<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-4805886270579815438</id><updated>2011-04-21T23:40:47.393+03:00</updated><title type='text'>Javalanche</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://javalanche.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4805886270579815438/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://javalanche.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Val</name><uri>http://www.blogger.com/profile/13961008360676791961</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_azYG6DDyITU/SYtouJ51WkI/AAAAAAAAC0Q/D_V-hcgg1Gw/S220/Me4Google.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>1</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-4805886270579815438.post-6670680183942624252</id><published>2007-10-12T14:21:00.000+03:00</published><updated>2007-10-22T15:20:17.056+03:00</updated><title type='text'>Broken locks</title><content type='html'>&lt;span style="font-family:georgia;"&gt;&lt;/span&gt;&lt;span style="font-family:georgia;"&gt;In one of my recent projects I came across this scenario:&lt;br /&gt;On one *nix machine there are two processes: a non-Java &lt;/span&gt;&lt;span style="font-family:georgia;"&gt;process and a Java application.&lt;/span&gt;&lt;span style="font-family:georgia;"&gt; The (external) process writes several text files to "well-known" locations (typically every file is put  in its own directory). Also the file names are &lt;span style="font-style: italic;"&gt;known in advance&lt;/span&gt; to a certain degree (there can be a name pattern specified instead of complete name).&lt;br /&gt;The files are written on a regular basis (daily or hourly) but the exact timing is not known.&lt;br /&gt;The Java application  has to:&lt;br /&gt;&lt;/span&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-family:georgia;"&gt;continually poll these directories and look for any &lt;span style="font-style: italic;"&gt;new file&lt;/span&gt;(s)  using the provided name or file pattern; &lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:georgia;"&gt;as soon as it &lt;span style="font-style: italic;"&gt;detects&lt;/span&gt; a &lt;span style="font-style: italic;"&gt;new file&lt;/span&gt;, reads and "process" it (the exact nature of process is unimportant);&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:georgia;"&gt;after the file processing is done, move the file to another directory -- mainly for archival purposes.&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;A number of questions appeared:&lt;br /&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;What exactly does it mean "new file [version]"?&lt;br /&gt;&lt;/li&gt;&lt;li&gt;How do we know when the external process finished writing the input file? Suppose external process starts writing the file. Should the Java process wait for the writer process to finish before it starts reading? If yes, for how long? [perhaps one should ask: is &lt;span style="font-style: italic;"&gt;required &lt;/span&gt;that reader process has to wait for writer to finish?]&lt;br /&gt;&lt;/li&gt;&lt;li&gt;How do we prevent external process to start a new write of the input file while our Java process is in the middle of reading it?&lt;br /&gt;&lt;/li&gt;&lt;li&gt;How do we make sure the Java process won't move the file (once it's done with it) while the writer process writes a new version of the file?&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;span style="font-family:georgia;"&gt;Question 1 is a matter of comparing file creation dates; it should be enough to compare the creation/modification date with a previous value. If the date is greater then we have a new file.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:georgia;"&gt;The question 2 was answered by comparing two successive takes of the file length; if they match then one can assume that writing ended. The time interval between those two measurements was established empirically by running the program on various input files. If there are still detected differences between the two successive length values the Java process (thread) sleeps and then tries again.&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:georgia;"&gt;To answer questions 3 and 4 successfully some sort of &lt;span style="font-style: italic;"&gt;access protocol&lt;/span&gt; must be established between the involved parties, because the &lt;span style="font-style: italic;"&gt;file moving&lt;/span&gt; requirement makes the Java process a "writer" too. Therefore the processes must synchronize on the shared resource. This -I think- is a typical  problem that &lt;a href="http://en.wikipedia.org/wiki/Inter-process_communication"&gt;IPC&lt;/a&gt; mechanisms ([FIFO]&lt;span style="font-style: italic;"&gt;pipes&lt;/span&gt;, shared memory, &lt;span style="font-weight: bold;"&gt;sockets&lt;/span&gt;) solve.&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:georgia;"&gt;Unfortunately none of the above IPC mechanisms was adopted (possibly because of lack of time or knowledge). Instead the team chose &lt;/span&gt;&lt;span style="font-style: italic;font-family:georgia;" &gt;file locks&lt;/span&gt;&lt;span style="font-family:georgia;"&gt; as they would prevent -they thought- input file(s) being clobbered by multiple processes. Moreover, because nobody knew at that time what would be the writer process(es), only the Java program was considered. So, the Java program had to do the following:&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Tries to o&lt;span style="font-family:georgia;"&gt;btain &lt;/span&gt;&lt;span style="font-style: italic;font-family:georgia;" &gt;exclusive access (exclusive file lock)&lt;/span&gt;&lt;span style="font-family:georgia;"&gt; to the in&lt;/span&gt;put file;&lt;/li&gt;&lt;li&gt;IF step 1 succeeds, reads the file and do something with the data (unimportant for this discussion) ELSE try later (sleeps a certain amount of time) ;&lt;/li&gt;&lt;li&gt;AFTER step 2 completes, tries to &lt;span style="font-style: italic;"&gt;move&lt;/span&gt; the input file to a back-up directory.&lt;/li&gt;&lt;li&gt;Relinquish the exclusive lock on file, allowing other process(es) to write to it.&lt;/li&gt;&lt;/ol&gt;[IMPORTANT ASSUMPTION: &lt;span style="font-style: italic; color: rgb(51, 51, 255);"&gt;no other process can write to the file while the Java process is holding an exclusive lock&lt;/span&gt;]&lt;p&gt;&lt;/p&gt;The above scheme was implemented and tested on a &lt;span style="font-style: italic;"&gt;Windows machine&lt;/span&gt;. The tests were successful; everything seemed to work well. BUT as soon as the program was deployed on the *nix machine, weird things started to happen. The "writer" process happily wrote to the file while the "reader" read it - even though the &lt;span style="font-weight: bold;"&gt;file lock was held by the Java process&lt;/span&gt;!&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Reality check: The assumption does not hold on *nix machines!&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Read what &lt;a href="http://java.sun.com/javase/6/docs/api/java/nio/channels/FileLock.html"&gt;FileLock javadocs&lt;/a&gt; has to say:&lt;br /&gt;&lt;blockquote&gt;"This file-locking API is intended to map directly to the native locking  facility of the underlying operating system.  Thus the locks held on a file  should be visible to all programs that have access to the file, regardless  of the language in which those programs are written.&lt;p&gt;&lt;span style="font-weight: bold;"&gt; Whether or not a lock actually prevents another program from accessing  the content of the locked region is system-dependent and therefore  unspecified.  The native file-locking facilities of some systems are merely  &lt;/span&gt;&lt;i style="font-weight: bold;"&gt;advisory&lt;/i&gt;&lt;span style="font-weight: bold;"&gt;, meaning that programs must cooperatively observe a known  locking protocol in order to guarantee data integrity&lt;/span&gt; [my emphasis - this is the case (of some?) of Unix platforms].  On other systems  native file locks are &lt;i&gt;mandatory&lt;/i&gt;, meaning that if one program locks a  region of a file then other programs are actually prevented from accessing  that region in a way that would violate the lock.  On yet other systems,  whether native file locks are advisory or mandatory is configurable on a  per-file basis.  To ensure consistent and correct behavior across platforms,  it is strongly recommended that the locks provided by this API be used as if  they were advisory locks.   &lt;/p&gt;&lt;p&gt;On some systems, acquiring a mandatory lock on a region of a file  prevents that region from being &lt;a href="http://java.sun.com/j2se/1.4.2/docs/api/java/nio/channels/FileChannel.html#map%28java.nio.channels.FileChannel.MapMode,%20long,%20long%29"&gt;mapped into memory&lt;/a&gt;, and vice versa.  Programs that combine  locking and mapping should be prepared for this combination to fail.&lt;/p&gt;&lt;p&gt; On some systems, closing a channel releases all locks held by the Java  virtual machine on the underlying file regardless of whether the locks were  acquired via that channel or via another channel open on the same file.  &lt;span style="font-style: italic;"&gt;It  is strongly recommended that, within a program, a unique channel be used to  acquire all locks on any given file&lt;/span&gt;."&lt;br /&gt;&lt;/p&gt;&lt;/blockquote&gt;I wrote a small Java program that uses file locks and tested it on an AIX machine. The program reads a text file line by line and then echoes each line to the console. To make things more interesting (to be read: to allow the tester to alter the file using an editor), I added a delay of 1.5 seconds between every two reads. Here it is:&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;import java.io.File;&lt;br /&gt;import java.io.IOException;&lt;br /&gt;import java.io.InputStream;&lt;br /&gt;import java.io.OutputStream;&lt;br /&gt;import java.io.RandomAccessFile;&lt;br /&gt;import java.nio.channels.Channels;&lt;br /&gt;import java.nio.channels.FileChannel;&lt;br /&gt;import java.nio.channels.FileLock;&lt;br /&gt;/**&lt;br /&gt;*&lt;br /&gt;* This class ``slow reads'' a file (whose path was given as argument) and echoes it to console.&lt;br /&gt;* Demonstrates that on a UNIX system the exclusive file locking semantics cannot be preserved.&lt;br /&gt;* Use it with a reasonably medium-size file (~8000 chars).&lt;br /&gt;* Run it on a UNIX system with the command &lt;pre&gt;java -cp . SlowReader testinput.txt&lt;/pre&gt;&lt;br /&gt;* While the program runs, open a navigator/editor and alter the file (towards the end).&lt;br /&gt;* (You should be able to save the changes performed in the editor)&lt;br /&gt;* You will see that the program's output changes accordingly.&lt;br /&gt;* @author Val&lt;br /&gt;*/&lt;br /&gt;public class SlowReader {&lt;br /&gt;&lt;br /&gt;private static final int TIME_TO_WAIT_BEFORE_WRITE = 1500;&lt;br /&gt;private static final int BUFFER_SIZE = 1024;&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;* @param args&lt;br /&gt;* @throws IOException&lt;br /&gt;*/&lt;br /&gt;public static void main(String[] args) throws IOException {&lt;br /&gt;&lt;br /&gt;   if(args.length == 0 || args[0] == null) {&lt;br /&gt;       return;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   File f = new File(args[0]);&lt;br /&gt;   if(f.exists() &amp;amp;&amp;amp; f.canRead()) {&lt;br /&gt;       FileChannel fileChannel = null;&lt;br /&gt;       FileLock lock = null;&lt;br /&gt;       RandomAccessFile raf = new RandomAccessFile(f, "rw");&lt;br /&gt;       fileChannel = raf.getChannel();&lt;br /&gt;       lock = fileChannel.tryLock(0L, Long.MAX_VALUE, false);&lt;br /&gt;&lt;br /&gt;       if (lock == null) {&lt;br /&gt;           //couldn't lock the file :( Try later&lt;br /&gt;           System.err.println("Cannot obtain exclusive access to file");&lt;br /&gt;           return;&lt;br /&gt;       }&lt;br /&gt;       InputStream in = Channels.newInputStream(fileChannel);&lt;br /&gt;       OutputStream out = System.out;&lt;br /&gt;       try {&lt;br /&gt;            // Transfer bytes from in to out&lt;br /&gt;            byte[] buf = new byte[BUFFER_SIZE];&lt;br /&gt;            int len;&lt;br /&gt;            while ((len = in.read(buf)) &gt; 0) {&lt;br /&gt;                Thread.sleep(TIME_TO_WAIT_BEFORE_WRITE);&lt;br /&gt;                out.write(buf, 0, len);&lt;br /&gt;            }&lt;br /&gt;       &lt;br /&gt;        } catch (IOException e) {&lt;br /&gt;            System.err.println(e);&lt;br /&gt;        } catch (InterruptedException e) {&lt;br /&gt;           // ignored&lt;br /&gt;       } finally {&lt;br /&gt;            try {&lt;br /&gt;                if (in != null) {&lt;br /&gt;                    in.close(); //this will close the channel too&lt;br /&gt;                }&lt;br /&gt;            } catch (IOException ioex) {&lt;br /&gt;                System.err.println(ioex);&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;p  style="font-family:georgia;"&gt;On the AIX machine where I tested the above program the input file gets easily overwritten by another process. Needless to say, on Windows platforms the file cannot be written once the Java process has got the lock..&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Conclusion&lt;/span&gt;&lt;/p&gt;&lt;p style="font-family: georgia;"&gt;The FileLock is deceiving! The "write once, run everywhere" slogan has been proved once again to be just marketing bullshit.&lt;/p&gt;&lt;p style="font-family: georgia;"&gt;P.S. I wonder what happens on Linux platforms. One of these days I will test it on my Ubuntu box. &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4805886270579815438-6670680183942624252?l=javalanche.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javalanche.blogspot.com/feeds/6670680183942624252/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4805886270579815438&amp;postID=6670680183942624252' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4805886270579815438/posts/default/6670680183942624252'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4805886270579815438/posts/default/6670680183942624252'/><link rel='alternate' type='text/html' href='http://javalanche.blogspot.com/2007/10/broken-locks.html' title='Broken locks'/><author><name>Val</name><uri>http://www.blogger.com/profile/13961008360676791961</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_azYG6DDyITU/SYtouJ51WkI/AAAAAAAAC0Q/D_V-hcgg1Gw/S220/Me4Google.jpg'/></author><thr:total>0</thr:total></entry></feed>
