KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > hsqldb > persist > LockFile


1 /* Copyright (c) 2001-2005, The HSQL Development Group
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  * Redistributions of source code must retain the above copyright notice, this
8  * list of conditions and the following disclaimer.
9  *
10  * Redistributions in binary form must reproduce the above copyright notice,
11  * this list of conditions and the following disclaimer in the documentation
12  * and/or other materials provided with the distribution.
13  *
14  * Neither the name of the HSQL Development Group nor the names of its
15  * contributors may be used to endorse or promote products derived from this
16  * software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
22  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */

30
31
32 package org.hsqldb.persist;
33
34 import java.io.DataInputStream JavaDoc;
35 import java.io.File JavaDoc;
36 import java.io.FileInputStream JavaDoc;
37 import java.io.RandomAccessFile JavaDoc;
38
39 import org.hsqldb.DatabaseManager;
40 import org.hsqldb.HsqlDateTime;
41 import org.hsqldb.HsqlException;
42 import org.hsqldb.Trace;
43 import org.hsqldb.lib.FileUtil;
44 import org.hsqldb.lib.HsqlTimer;
45 import org.hsqldb.lib.java.JavaSystem;
46
47 /**
48  * The base HSQLDB cooperative file locking implementation and factory. <p>
49  *
50  * <hr>
51  *
52  * Here is the way this class operates: <p>
53  *
54  * <ol>
55  * <li>A file with a well-known path relative to each database instance
56  * is used to implement cooperative locking of database files across
57  * process boundaries (database instances running in different JVM
58  * host processes) and class loader contexts (databases whose classes
59  * have been loaded by distinct class loaders such that their open
60  * database repositories are distinct and are inaccessible across
61  * the class loader context boundaries).<p>
62  *
63  * <li>A background thread periodically writes a timestamp to this object's
64  * lock file at {@link #HEARTBEAT_INTERVAL} millisecond intervals,
65  * acting as a heartbeat to indicate that a lock is still held.<p>
66  *
67  * <li>The generic lock attempt rules are: <p>
68  * <ul>
69  * <li>If a lock condition is already held by this object, do nothing and
70  * signify that the lock attempt was successful, else...<p>
71  *
72  * <li>If no lock file exists, go ahead and create one, silently issue the
73  * {@link java.io.File#deleteOnExit File.deleteOnExit()} directive via
74  * refelective method invocation (in order to stay JDK 1.1 compliant),
75  * schedule a periodic heartbeat task and signify that the lock attempt
76  * was successful, else...<p>
77  *
78  * <li>The lock file must already exist, so try to read its heartbeat
79  * timestamp. If the read fails, assume that a lock condition is held by
80  * another process or a database in an inaccessible class loader context
81  * and signify that the attempt failed, else if the read value
82  * is less than <code>HEARTBEAT_INTERVAL</code> milliseconds into the
83  * past or futuer, assume that a lock condition is held by another
84  * process or a database in an inaccessible class loader context and
85  * signify that the lock attempt failed, else assume that the file is
86  * not in use, schedule a periodic heartbeat task and signify that the
87  * lock attempt was successful.<p>
88  *
89  * </ul>
90  * <li>The generic release attempt rules are:<p>
91  * <ul>
92  * <li>If a lock condition is not currently held, do nothing and signify
93  * that the release attempt was successful, else...<p>
94  *
95  * <li>A lock condition is currently held, so try to release it. By
96  * default, releasing the lock condition consists of closing and
97  * nullifying any objects that have a file descriptor open on the
98  * lock file. If the release is successful, cancel the periodic
99  * heartbeat task and signify that the release succeeded, else signify
100  * that the release attempt failed.<p>
101  * </ul>
102  * </ol> <p>
103  *
104  * In addition to the generic lock and release rules, the protected methods
105  * {@link #lockImpl() lockImpl()} and {@link #releaseImpl() releaseImpl()}
106  * are called during lock and release attempts, respectively. This allows
107  * transparent, JDK 1.1 compliant integration of extended strategies for
108  * locking and releasing, based on subclassing and reflective construction
109  * of such specializations in the factory method
110  * {@link #newLockFile newLockFile()}, determined by information gathered
111  * at run-time. <p>
112  *
113  * In particular, if it is available at runtime, then newLockFile() retrieves
114  * instances of {@link org.hsqldb.NIOLockFile NIOLockFile} to capitalize,
115  * when possible, on the existence of the {@link java.nio.channels.FileLock
116  * FileLock} class. If the <code>NIOLockFile</code> class does not exist at
117  * run-time or the java.nio classes it uses are not supported under the
118  * run-time JVM, then newLockFile() produces vanilla LockFile instances,
119  * meaning that only purely cooperative locking takes place, as opposed to
120  * possibly O/S-enforced file locking which, at least in theory, is made
121  * available through the {@link java.nio.channels} package). However, it
122  * must be noted that even if a JVM implementation provides the full
123  * java.nio.channels package, it is not absolutely required to guarantee
124  * that the underlying platform (the current operating system) provides
125  * true process-wide file locking. <p>
126  *
127  * <b>Note:</b> <p>
128  *
129  * The <code>NIOLockFile</code> descendent exists because it has been determined
130  * though experimenatation that <code>java.nio.channels.FileLock</code>
131  * does not always exhibit the correct/desired behaviour under reflective
132  * method invocation. That is, it has been discovered that under some operating
133  * system/JVM combinations, after calling <code>FileLock.release()</code>
134  * via a reflective method invocation, the lock is not released properly,
135  * deletion of the lock file is not possible even from the owning object
136  * (this) and it is impossible for other <code>LockFile</code> instances
137  * or any other objects or processes to successfully obtain a lock
138  * condition on the lock file, despite the fact that the <code>FileLock</code>
139  * object reports that its lock is invalid (was released successfully).
140  * Frustratingly, this condition appears to persist until full exit of the
141  * JVM process in which the <code>FileLock.tryLock()</code> method was
142  * reflectively invoked. <p>
143  *
144  * To solve this, the original <code>LockFile</code> class was split in two and
145  * instead of reflective method invocation, reflection-based class
146  * instantiation is now performed at the level of the <code>newLockFile()</code>
147  * factory method. Similarly, the HSQLDB ANT build script detects the presence
148  * or abscence of JDK 1.4 features such as java.nio and only attempts to build
149  * and deploy <code>NIOLockFile</code> to the hsqldb.jar if such features are
150  * reported present. </p>
151  *
152  * @author boucherb@users
153  * @version 1.7.2
154  * @since 1.7.2
155  */

156 public class LockFile {
157
158     /** Canonical reference to this object's lock file. */
159     protected File JavaDoc f;
160
161     /** Cached value of the lock file's canonical path. */
162     private String JavaDoc cpath = null;
163
164     /**
165      * A RandomAccessFile constructed from this object's reference, f, to its
166      * lock file. <p>
167      *
168      * This RandomAccessFile is used to periodically write out the heartbeat
169      * timestamp to this object's lock file.
170      */

171     protected RandomAccessFile JavaDoc raf;
172
173     /**
174      * The period, in milliseconds, at which heartbeat timestamps are written
175      * to this object's lock file.
176      */

177     public static final long HEARTBEAT_INTERVAL = 10000;
178
179     /**
180      * A magic value to place at the beginning of the lock file to
181      * differentiate it from other files. The value is "HSQLLOCK".getBytes().
182      */

183     public static final byte[] MAGIC = "HSQLLOCK".getBytes();
184
185     /** Indicates whether this object has a lock condition on its lock file. */
186     protected boolean locked;
187
188     /**
189      * The timed scheduler with which to register this object's
190      * heartbeat task.
191      */

192     protected static final HsqlTimer timer = DatabaseManager.getTimer();
193
194     /**
195      * An opaque reference to this object's heatbeat task.
196      */

197     private Object JavaDoc timerTask;
198
199     /**
200      * Attempts to read the hearbeat timestamp from this object's lock file
201      * and compare it with the current time. <p>
202      *
203      * An exception is thrown if it must be presumned that another process has
204      * locked the file, using the following rules: <p>
205      *
206      * <ol>
207      * <li>If the file does not exist, this method returns immediately.
208      *
209      * <li>If an exception is raised reading the file, then an exeption is
210      * thrown.
211      *
212      * <li>If the read is successful and the timestamp read in is less than
213      * <code>HEARTBEAT_INTERVAL</code> milliseconds into the past or
214      * future, then an exception is thrown.
215      *
216      * <li>If no exception is thrown in 2.) or 3.), this method simply returns.
217      * </ol>
218      * @throws Exception if it must be presumed that another process
219      * or isolated class loader context currently has a
220      * lock condition on this object's lock file
221      */

222     private void checkHeartbeat() throws Exception JavaDoc {
223
224         long lastHeartbeat;
225         String JavaDoc mn;
226         String JavaDoc path;
227
228         mn = "checkHeartbeat(): ";
229         path = "lock file [" + cpath + "]";
230
231         trace(mn + "entered.");
232
233         if (!f.exists()) {
234             trace(mn + path + " does not exist. Check OK.");
235
236             return;
237         }
238
239         if (f.length() != 16) {
240             trace(mn + path + " length != 16; Check OK.");
241
242             return;
243         }
244
245         try {
246             lastHeartbeat = System.currentTimeMillis() - readHeartbeat();
247         } catch (Exception JavaDoc e) {
248
249             // e.printStackTrace();
250
throw new Exception JavaDoc(
251                 Trace.getMessage(
252                     Trace.LockFile_checkHeartbeat, true, new Object JavaDoc[] {
253                 e.toString(), cpath
254             }));
255         }
256
257         trace(mn + path + " last heartbeat " + lastHeartbeat + " ms ago.");
258
259         if (Math.abs(lastHeartbeat) < HEARTBEAT_INTERVAL) {
260             throw new Exception JavaDoc(
261                 Trace.getMessage(
262                     Trace.LockFile_checkHeartbeat2, true, new Object JavaDoc[] {
263                 mn, path
264             }));
265         }
266     }
267
268     /**
269      * Closes this object's {@link #raf RandomAccessFile}.
270      *
271      * @throws Exception if an IOException occurs
272      */

273     private void closeRAF() throws Exception JavaDoc {
274
275         String JavaDoc mn;
276
277         mn = "closeRAF(): ";
278
279         trace(mn + "entered.");
280
281         if (raf == null) {
282             trace(mn + "raf was null upon entry. Exiting immediately.");
283         } else {
284             trace(mn + "closing " + raf);
285             raf.close();
286             trace(mn + raf + " closed successfully. Setting raf null");
287
288             raf = null;
289         }
290     }
291
292     /**
293      * Initializes this object with the specified <code>File</code>
294      * object. <p>
295      *
296      * The file argument is a reference to this object's lock file. <p>
297      *
298      * This action has the effect of attempting to release any existing
299      * lock condition and reinitializing all lock-related member attributes
300      * @param file a reference to the file this object is to use as its
301      * lock file
302      */

303     private void setFile(File JavaDoc file) throws Exception JavaDoc {
304
305         if (isLocked()) {
306             try {
307                 tryRelease();
308             } catch (Exception JavaDoc e) {
309                 trace(e);
310             }
311         }
312
313         f = FileUtil.canonicalFile(file);
314         cpath = f.getPath();
315         raf = null;
316         locked = false;
317     }
318
319     /**
320      * Provides any specialized locking actions for the
321      * {@link #tryLock() tryLock()} method. <p>
322      *
323      * Descendents are free to provide additional functionality here,
324      * using the following rules:
325      *
326      * <pre>
327      * PRE:
328      *
329      * This method is only called if tryLock() thinks it needs to get a lock
330      * condition, so it can be assumed the locked == false upon entry, raf is
331      * a non-null instance that can be used to get a FileChannel if desired,
332      * and the lock file is, at the very least, readable. Further, this
333      * object's heatbeat task is definitely cancelled and/or has not yet been
334      * scheduled, so whatever timestamp is recorded in the lock file, if it
335      * exists, is what was written by a previous locker, if any. A timestamp
336      * value in a preexisting file is only considered valid if the file is
337      * of the correct length and its first eight bytes are
338      * the value {@link #MAGIC MAGIC}.
339      *
340      * POST:
341      *
342      * This method must return false if any additional locking work fails,
343      * else true.
344      * </pre>
345      *
346      * The default implementation of this method reflectively (for JDK1.1
347      * compliance) invokes f.deleteOnExit() in a silent manner and always
348      * returns true. <p>
349      *
350      * @throws Exception if a situation is encountered that absolutely
351      * prevents the status of the lock condtion
352      * to be determined. (e.g. an IO exception
353      * occurs here)
354      * @return <code>true</code> if no extended locking
355      * actions are taken or the actions succeed,
356      * else <code>false</code>.
357      */

358     protected boolean lockImpl() throws Exception JavaDoc {
359
360         String JavaDoc mn;
361
362         mn = "lockImpl(): ";
363
364         trace(mn + "entered.");
365         FileUtil.deleteOnExit(f);
366
367         return true;
368     }
369
370     /**
371      * Opens this object's {@link #raf RandomAccessFile}. <p>
372      *
373      * @throws Exception if an IOException occurs
374      */

375     private void openRAF() throws Exception JavaDoc {
376
377         trace("openRAF(): entered.");
378
379         raf = new RandomAccessFile JavaDoc(f, "rw");
380
381         trace("openRAF(): got new 'rw' mode " + raf);
382     }
383
384     /**
385      * Retrieves the last written hearbeat timestamp from
386      * this object's lock file. If this object's lock file
387      * does not exist, <code>Long.MIN_VALUE</code> (the earliest
388      * time representable as a long in Java) is retrieved. <p>
389      *
390      * @throws Exception if an error occurs while reading the hearbeat
391      * timestamp from this object's lock file.
392      * @return the hearbeat timestamp from this object's lock file,
393      * as a <code>long</code> value or, if this object's lock
394      * file does not exist, Long.MIN_VALUE, the earliest time
395      * representable as a long in Java,
396      */

397     private long readHeartbeat() throws Exception JavaDoc {
398
399         DataInputStream JavaDoc dis;
400         long heartbeat;
401
402         heartbeat = Long.MIN_VALUE;
403
404         String JavaDoc mn = "readHeartbeat(): ";
405         String JavaDoc path = "lock file [" + cpath + "]";
406
407         trace(mn + "entered.");
408
409         if (!f.exists()) {
410             trace(mn + path + " does not exist. Return '" + heartbeat + "'");
411
412             return heartbeat;
413         }
414
415         dis = new DataInputStream JavaDoc(new FileInputStream JavaDoc(f));
416
417         trace(mn + " got new " + dis);
418
419         for (int i = 0; i < MAGIC.length; i++) {
420             if (MAGIC[i] != dis.readByte()) {
421                 trace(mn + path + " is not lock file. Return '" + heartbeat
422                       + "'");
423
424                 return heartbeat;
425             }
426         }
427
428         heartbeat = dis.readLong();
429
430         trace(mn + " read: [" + HsqlDateTime.getTimestampString(heartbeat)
431               + "]");
432         dis.close();
433         trace(mn + " closed " + dis);
434
435         return heartbeat;
436     }
437
438     /**
439      * Provides any specialized release actions for the tryRelease()
440      * method. <p>
441      *
442      * @return true if there are no specialized release
443      * actions performed or they succeed,
444      * else false
445      * @throws Exception if a situation is encountered that absolutely
446      * prevents the status of the lock condtion
447      * to be determined. (e.g. an IO exception
448      * occurs here).
449      */

450     protected boolean releaseImpl() throws Exception JavaDoc {
451
452         trace("releaseImpl(): no action: returning true");
453
454         return true;
455     }
456
457     /** Schedules the lock heartbeat task. */
458     private void startHeartbeat() {
459
460         Runnable JavaDoc r;
461
462         trace("startHeartbeat(): entered.");
463
464         if (timerTask == null || HsqlTimer.isCancelled(timerTask)) {
465             r = new HeartbeatRunner();
466
467             // now, periodic at HEARTBEAT_INTERVAL, running this, fixed rate
468
timerTask = timer.schedulePeriodicallyAfter(0,
469                     HEARTBEAT_INTERVAL, r, true);
470
471             trace("startHeartbeat(): heartbeat task scheduled.");
472         }
473
474         trace("startHeartbeat(): exited.");
475     }
476
477     /** Cancels the lock heartbeat task. */
478     private void stopHeartbeat() {
479
480         String JavaDoc mn = "stopHeartbeat(): ";
481
482         trace(mn + "entered");
483
484         if (timerTask != null &&!HsqlTimer.isCancelled(timerTask)) {
485             HsqlTimer.cancel(timerTask);
486
487             timerTask = null;
488         }
489
490         trace(mn + "exited");
491     }
492
493     /**
494      * Writes a magic value to this object's lock file that distiguishes
495      * it as an HSQLDB lock file.
496      *
497      * @throws Exception if the magic value cannot be written to
498      * the lock file
499      */

500     private void writeMagic() throws Exception JavaDoc {
501
502         String JavaDoc mn = "writeMagic(): ";
503         String JavaDoc path = "lock file [" + cpath + "]";
504
505         trace(mn + "entered.");
506         trace(mn + "raf.seek(0)");
507         raf.seek(0);
508         trace(mn + "raf.write(byte[])");
509         raf.write(MAGIC);
510         trace(mn + "wrote [\"HSQLLOCK\".getBytes()] to " + path);
511     }
512
513     /**
514      * Writes the current hearbeat timestamp value to this
515      * object's lock file. <p>
516      *
517      * @throws Exception if the current heartbeat timestamp value
518      * cannot be written
519      */

520     private void writeHeartbeat() throws Exception JavaDoc {
521
522         long time;
523         String JavaDoc mn = "writeHeartbeat(): ";
524         String JavaDoc path = "lock file [" + cpath + "]";
525
526         trace(mn + "entered.");
527
528         time = System.currentTimeMillis();
529
530         trace(mn + "raf.seek(" + MAGIC.length + ")");
531         raf.seek(MAGIC.length);
532         trace(mn + "raf.writeLong(" + time + ")");
533         raf.writeLong(time);
534         trace(mn + "wrote [" + time + "] to " + path);
535     }
536
537     /**
538      * Retrieves a <code>LockFile</code> instance, initialized with a
539      * <code>File</code> object whose path is the one specified by
540      * the <code>path</code> argument. <p>
541      *
542      * @return a <code>LockFile</code> instance initialized with a
543      * <code>File</code> object whose path is the one specified
544      * by the <code>path</code> argument.
545      * @param path the path of the <code>File</code> object with
546      * which the retrieved <code>LockFile</code>
547      * object is to be initialized
548      */

549     public static LockFile newLockFile(String JavaDoc path) throws Exception JavaDoc {
550
551         File JavaDoc f;
552         LockFile lf;
553         Class JavaDoc c;
554
555         c = null;
556
557         try {
558             Class.forName("java.nio.channels.FileLock");
559
560             c = Class.forName("org.hsqldb.persist.NIOLockFile");
561             lf = (LockFile) c.newInstance();
562         } catch (Exception JavaDoc e) {
563             lf = new LockFile();
564         }
565
566         f = new File JavaDoc(path);
567
568         FileUtil.makeParentDirectories(f);
569         lf.setFile(f);
570
571         return lf;
572     }
573
574     public static LockFile newLockFileLock(String JavaDoc path) throws HsqlException {
575
576         LockFile lf = null;
577
578         try {
579             lf = LockFile.newLockFile(path + ".lck");
580         } catch (Exception JavaDoc e) {
581             throw Trace.error(Trace.FILE_IO_ERROR, e.toString());
582         }
583
584         boolean locked = false;
585         String JavaDoc msg = "";
586
587         try {
588             locked = lf.tryLock();
589         } catch (Exception JavaDoc e) {
590
591             // e.printStackTrace();
592
msg = e.toString();
593         }
594
595         if (!locked) {
596             throw Trace.error(Trace.DATABASE_ALREADY_IN_USE, lf + ": " + msg);
597         }
598
599         return lf;
600     }
601
602     /**
603      * Tests whether some other object is "equal to" this one.
604      *
605      * An object is considered equal to a <code>LockFile</code> object iff it
606      * is not null, it is an instance of <code>LockFile</code> and either it's
607      * the identical instance or it has the same lock file. More formally,
608      * is is considered equal iff it is not null, it is an instance of
609      * <code>LockFile</code>, and the expression: <p>
610      *
611      * <pre>
612      * this == other ||
613      * this.f == null ? other.f == null : this.f.equals(other.f);
614      * </pre>
615      *
616      * yeilds true. <p>
617      *
618      * @param obj the reference object with which to compare.
619      * @return <code>true</code> if this object is equal to
620      * the <code>obj</code> argument;
621      * <code>false</code> otherwise.
622      * @see #hashCode
623      */

624     public boolean equals(Object JavaDoc obj) {
625
626         // do faster tests first
627
if (this == obj) {
628             return true;
629         } else if (obj instanceof LockFile) {
630             LockFile that = (LockFile) obj;
631
632             return (f == null) ? that.f == null
633                                : f.equals(that.f);
634         } else {
635             return false;
636         }
637     }
638
639     /**
640      * Retrieves, as a String, the canonical path of this object's lock file.
641      *
642      * @return the canonical path of this object's lock file.
643      */

644     public String JavaDoc getCanonicalPath() {
645         return cpath;
646     }
647
648     /**
649      * Retrieves the hash code value for this object.
650      *
651      * The value is zero if the <code>File</code> object attribute
652      * <code>f</code> is <code>null</code>, else it is the <code>hashCode</code>
653      * of <code>f</code>. That is, two <code>LockFile</code>
654      * objects have the same <code>hashCode</code> value if they have the
655      * same lock file. <p>
656      *
657      * @return a hash code value for this object.
658      * @see #equals(java.lang.Object)
659      */

660     public int hashCode() {
661         return f == null ? 0
662                          : f.hashCode();
663     }
664
665     /**
666      * Retrieves whether this object has successfully obtained and is
667      * still currently holding (has not yet released) a cooperative
668      * lock condition on its lock file. <p>
669      *
670      * <b>Note:</b> Due to the retrictions placed on the JVM by
671      * platform-independence, it is very possible to successfully
672      * obtain and hold a cooperative lock on a lock file and yet for
673      * the lock to become invalid while held. <p>
674      *
675      * For instance, under JVMs with no <code>java.nio</code> package or
676      * operating systems that cannot live up to the contracts set forth for
677      * {@link java.nio.channels.FileLock FileLock}, it is quite possible
678      * for another process or even an uncooperative bit of code running
679      * in the same JVM to delete or overwrite the lock file while
680      * this object holds a lock on it. <p>
681      *
682      * Because of this, the isValid() method is provided in the public
683      * interface in order to allow clients to detect such situations. <p>
684      *
685      * @return true iff this object has successfully obtained
686      * and is currently holding (has not yet released)
687      * a lock on its lock file
688      * @see #isValid
689      */

690     public boolean isLocked() {
691         return locked;
692     }
693
694     /**
695      * Retrieves whether there is potentially already a cooperative lock,
696      * operating system lock or some other situation preventing
697      * a cooperative lock condition from being aquired, relative to the
698      * specified path.
699      *
700      * @param path the path to test
701      */

702     public static boolean isLocked(String JavaDoc path) {
703
704         FileInputStream JavaDoc fis = null;
705
706         try {
707             LockFile lf = LockFile.newLockFile(path);
708
709             lf.checkHeartbeat();
710
711             if (lf.f.exists() && lf.f.isFile()) {
712                 fis = new FileInputStream JavaDoc(lf.f);
713
714                 fis.read();
715             }
716
717             return false;
718         } catch (Exception JavaDoc e) {}
719         finally {
720             if (fis != null) {
721                 try {
722                     fis.close();
723                 } catch (java.io.IOException JavaDoc e) {}
724             }
725         }
726
727         return true;
728     }
729
730     /**
731      * Retrieves whether this object holds a valid lock on its lock file. <p>
732      *
733      * More formally, this method retrieves true iff: <p>
734      *
735      * <pre>
736      * isLocked() &&
737      * f != null &&
738      * f.exists() &&
739      * raf != null
740      * </pre>
741      *
742      * @return true iff this object holds a valid lock on its
743      * lock file.
744      */

745     public boolean isValid() {
746         return isLocked() && f != null && f.exists() && raf != null;
747     }
748
749     /**
750      * For internal use only. <p>
751      *
752      * This Runnable class provides the implementation for the timed task
753      * that periodically writes out a heartbeat timestamp to the lock file.<p>
754      */

755     protected class HeartbeatRunner implements Runnable JavaDoc {
756
757         public void run() {
758
759             try {
760                 trace("HeartbeatRunner.run(): writeHeartbeat()");
761                 writeHeartbeat();
762             } catch (Throwable JavaDoc t) {
763                 trace("HeartbeatRunner.run(): caught Throwable: " + t);
764             }
765         }
766     }
767
768     /**
769      * Retrieves a String representation of this object. <p>
770      *
771      * The String is of the form: <p>
772      *
773      * <pre>
774      * super.toString() +
775      * "[file=" + getAbsolutePath() +
776      * ", exists=" + f.exists() +
777      * ", locked=" + isLocked() +
778      * ", valid=" + isValid() +
779      * ", " + toStringImpl() +
780      * "]";
781      * </pre>
782      * @return a String representation of this object.
783      * @see #toStringImpl
784      */

785     public String JavaDoc toString() {
786
787         return super.toString() + "[file =" + cpath + ", exists="
788                + f.exists() + ", locked=" + isLocked() + ", valid="
789                + isValid() + ", " + toStringImpl() + "]";
790     }
791
792     /**
793      * Retrieves an implementation-specific tail value for the
794      * toString() method. <p>
795      *
796      * The default implementation returns the empty string.
797      * @return an implementation-specific tail value for the toString() method
798      * @see #toString
799      */

800     protected String JavaDoc toStringImpl() {
801         return "";
802     }
803
804     /**
805      * Attempts, if not already held, to obtain a cooperative lock condition
806      * on this object's lock file. <p>
807      *
808      * @throws Exception if an error occurs that absolutely prevents the lock
809      * status of the lock condition from being determined
810      * (e.g. an unhandled file I/O error).
811      * @return <code>true</code> if this object already holds a lock or
812      * the lock was obtained successfully, else
813      * <code>false</code>
814      */

815     public boolean tryLock() throws Exception JavaDoc {
816
817         String JavaDoc mn = "tryLock(): ";
818
819         trace(mn + "entered.");
820
821         if (locked) {
822             trace(mn + " lock already held. Returning true immediately.");
823
824             return true;
825         }
826
827         checkHeartbeat();
828
829 // Alternatively, we could give ourselves a second try,
830
// raising our chances of success in the rare case that the
831
// last locker terminiated abruptly just less than
832
// HEARTBEAT_INTERVAL ago.
833
//
834
// try {
835
// checkHeartbeat();
836
// } catch (Exception e) {
837
// try {
838
// Thread.sleep(HEARTBEAT_INTERVAL);
839
// } catch (Exception e2) {}
840
// checkHeartbeat();
841
// }
842
openRAF();
843
844         locked = lockImpl();
845
846         if (locked) {
847             writeMagic();
848             startHeartbeat();
849
850             try {
851
852                 // attempt to ensure that tryRelease() gets called if/when
853
// the VM shuts down, just in case this object has not yet
854
// been garbage-collected or explicitly released.
855
JavaSystem.runFinalizers();
856                 trace(mn + "success for System.runFinalizersOnExit(true)");
857             } catch (Exception JavaDoc e) {
858                 trace(mn + e.toString());
859             }
860         } else {
861             try {
862                 releaseImpl();
863                 closeRAF();
864             } catch (Exception JavaDoc e) {
865                 trace(mn + e.toString());
866             }
867         }
868
869         trace(mn + "ran to completion. Returning " + locked);
870
871         return locked;
872     }
873
874     /**
875      * Attempts to release any cooperative lock condition this object
876      * may have on its lock file. <p>
877      *
878      * @throws Exception if an error occurs that absolutely prevents
879      * the status of the lock condition from
880      * being determined (e.g. an unhandled file
881      * I/O exception).
882      * @return <code>true</code> if this object does not hold a
883      * lock or the lock is released successfully,
884      * else <code>false</code>.
885      */

886     public boolean tryRelease() throws Exception JavaDoc {
887
888         String JavaDoc mn = "tryRelease(): ";
889         String JavaDoc path;
890
891         trace(mn + "entered.");
892
893         boolean released = !locked;
894
895         if (released) {
896             trace(mn + "No lock held. Returning true immediately");
897
898             return true;
899         }
900
901         try {
902             released = releaseImpl();
903         } catch (Exception JavaDoc e) {
904             trace(mn + e);
905         }
906
907         if (!released) {
908             trace(mn + "releaseImpl() failed. Returning false immediately.");
909
910             return false;
911         }
912
913         trace(mn + "releaseImpl() succeeded.");
914         stopHeartbeat();
915         closeRAF();
916
917         // without a small delay, the following delete may occasionally fail
918
// and return false on some systems, when really it should succeed
919
// and return true.
920
trace(mn + "Starting Thread.sleep(100).");
921
922         try {
923             Thread.sleep(100);
924         } catch (Exception JavaDoc e) {
925             trace(mn + e.toString());
926         }
927
928         trace(mn + "Finished Thread.sleep(100).");
929
930         path = "[" + cpath + "]";
931
932         if (f.exists()) {
933             trace(mn + path + " exists.");
934
935             released = f.delete();
936
937             trace(mn + path + (released ? ""
938                                         : "not") + " deleted.");
939
940             if (f.exists()) {
941                 trace(mn + " WARNING!: " + path + "still exists.");
942             }
943         }
944
945         locked = !released;
946
947         trace(mn + "ran to completion. Returning " + released);
948
949         return released;
950     }
951
952     /**
953      * Prints tracing information and the value of the specified object
954      *
955      * @param o the value to print
956      */

957     protected void trace(Object JavaDoc o) {
958
959         if (Trace.TRACE) {
960             Trace.printSystemOut("[" + super.toString() + "]: " + o);
961         }
962     }
963
964     /**
965      * Attempts to release any lock condition this object may have on its
966      * lock file. <p>
967      *
968      * @throws Throwable if this object encounters an unhandled exception
969      * trying to release the lock condition,
970      * if any, that it has on its lock file.
971      */

972     protected void finalize() throws Throwable JavaDoc {
973         trace("finalize(): calling tryRelease()");
974         tryRelease();
975     }
976 }
977
Popular Tags