📄 lockfile.java
字号:
* limitations/capabilites, as well as under usability considerations and * external security or operating constraints that may need to be imposed.<p> * * Alternate approaches that have been considered and rejected for now * include: <p> * * <ul> * <li>Socket-based locks (with/without broadcast protocol) * <li>Pure NIO locking * <li>Simple lock file (no heartbeat or polling) * <li>JNI and native configuration alternatives * </ul> * * Of course, discussions involving and patches implementing improvements * or better alternatives are always welcome. <p> * * As a final note and sign post for developers starting to work with * Java NIO: <p> * * A separate <tt>NIOLockFile</tt> descendent exists specifically * because it was determined though experimenatation that * <tt>java.nio.channels.FileLock</tt> does not always exhibit the correct * or desired behaviour under reflective method invocation. That is, it was * discovered that under some operating system/JVM combinations, after calling * <tt>FileLock.release()</tt> via a reflective method invocation, the lock is * not released properly, deletion of the lock file is not possible even from * the owning object (this) and it is impossible for other <tt>LockFile</tt> * instances, other in-process objects or other processes to successfully obtain * a lock condition on the lock file, despite the fact that the * <tt>FileLock</tt> object reports that its lock is invalid (was released * successfully). Frustratingly, this condition appears to persist until full * exit of the process hosting the JVM in which the <tt>FileLock.tryLock()</tt> * method was reflectively invoked. <p> * * To solve this, the original <tt>LockFile</tt> class was split in two and * instead of reflective method invocation, subclass instantiation is now * performed at the level of the <tt>newLockFile()</tt> factory method. * Similarly, the HSQLDB ANT build script now detects the presence or abscence * of JDK 1.4+ features such as java.nio and only attempts to build and deploy * <tt>NIOLockFile</tt> to the hsqldb.jar if such features are reported * present. <p> * * @author boucherb@users * @version 1.8.0.10 * @since 1.7.2 */public class LockFile { /** * Arbitary period, in milliseconds, at which heartbeat timestamps are * written to this object's lock file. <p> * * This value was selected to be very conservative, just in case timing * jitters are experienced on the order introduced by brief network * partitions, accidentally removed media and transient high load * CPU bursts. */ public static final long HEARTBEAT_INTERVAL = 10000; /** * {@link #HEARTBEAT_INTERVAL} + 100. <p> * * Interval used by {@link #checkHeartbeat(boolean) checkHeartbeat} to * test whether the timestamp in the underlying lock file is live or stale. * Padding added in the hope of reducing potential timing jitter issues * under the polling scheme introduced in 1.8.0.3 */ public static final long HEARTBEAT_INTERVAL_PADDED = 10100; /** * Value written at the beginning of an HSQLDB lock file to distinguish it * from other file types. <p> * * The value is the octet sequence: {0x48, 0x53, 0x51, 0x4c, 0x4c, 0x4f, * 0x43, 0x4b}, which is the ASCII sequence {'H', 'S', 'Q', 'L', 'L', 'O', * 'C', 'K'}. <p> * * <b>Design Note</b>: <p> * * "HSQLLOCK".getBytes() is no longer used because it is dependent on the * underlying platform's default character set. */ protected static final byte[] MAGIC = { 0x48, 0x53, 0x51, 0x4c, 0x4c, 0x4f, 0x43, 0x4b }; /** * Size, in bytes, of the region at the beginning of a lock file that is * actually used to record lock information. <p> * * Value is currently MAGIC.length + sizeof(long) = (8 + 8) = 16 */ public static final int USED_REGION = 16; /** * Number of retries used by default in {@link #pollHeartbeat() * pollHeartbeat}. */ public static final int POLL_RETRIES_DEFAULT = 10; /** * System property that can be used to override the default number of * heartbeat poll retries. */ public static final String POLL_RETRIES_PROPERTY = "hsqldb.lockfile.poll.retries"; /** * System property that can be used to override the default number of * milliseconds between each heartbeat poll retry. */ public static final String POLL_INTERVAL_PROPERTY = "hsqldb.lockfile.poll.interval"; /** Whether <tt>java.nio</tt> file locking is attempted by default. */ public static final boolean USE_NIO_FILELOCK_DEFAULT = false; /** * System property that can be used to control whether nio file locking is * attempted. */ public static final String USE_NIO_FILELOCK_PROPERTY = "hsqldb.lockfile.nio.filelock"; /** * Statically computed indication of <tt>java.nio.channels.FileLock</tt> * runtime availability. <p> * * <b>Design Note</b>:<p> * * Computed in a static initializer block. Will be <tt>false</tt> if * <tt>USE_NIO_FILELOCK_PROPERTY</tt> is <tt>false</tt> at static * initialization, regardless of actual availability. */ public static final boolean NIO_FILELOCK_AVAILABLE; /** * Statically computed reference to the <tt>NIOLockFile</tt> class. <p> * * <b>Design Note</b>:<p> * * Computed in a static initializer block. Will be <tt>null</tt> if * <tt>USE_NIO_FILELOCK_PROPERTY</tt> is <tt>false</tt> at static * initialization, regardless of actual availability. */ public static final Class NIO_LOCKFILE_CLASS; /** * The timed scheduler with which to register this object's * heartbeat task. */ protected static final HsqlTimer timer = DatabaseManager.getTimer(); // This static initializer comes last, since it references a subclass // // That is, it is best practice to ensure the static fields of this class // are all initialized before referecing a subclass whose static // field initializtion may in turn reference static fields in this class. static { synchronized (LockFile.class) { boolean use = USE_NIO_FILELOCK_DEFAULT; try { use = "true".equalsIgnoreCase( System.getProperty(USE_NIO_FILELOCK_PROPERTY, use ? "true" : "false")); } catch (Exception e) {} boolean avail = false; Class clazz = null; if (use) { try { Class.forName("java.nio.channels.FileLock"); clazz = Class.forName("org.hsqldb.persist.NIOLockFile"); avail = true; } catch (Exception e) {} } NIO_FILELOCK_AVAILABLE = avail; NIO_LOCKFILE_CLASS = clazz; } } /** * Canonical reference to this object's lock file. <p> * * <b>Design Note</b>:<p> * * Should really be final, but finality makes reflective construction * and adherence to desirable <tt>LockFile</tt> factory method event * sequence more complicated. */ protected File file; /** * Cached value of the lock file's canonical path * * <b>Design Note</b>:<p> * * Should really be final, but finality makes reflective construction * and adherence to desirable <tt>LockFile</tt> factory method event * sequence much more complicated. */ private String cpath; /** * A <tt>RandomAccessFile</tt> constructed from this object's canonical file * reference. <p> * * This <tt>RandomAccessFile</tt> is used to periodically write out the * heartbeat timestamp to this object's lock file. */ protected volatile RandomAccessFile raf; /** Indicates presence or absence of the cooperative lock condition. */ protected volatile boolean locked; /** Opaque reference to this object's heatbeat task. */ private volatile Object timerTask; /** * Retrieves a new <tt>NIOLockFile</tt>, or <tt>null</tt> if not available * under the current runtime environment. * * @return a new <tt>NIOLockFile</tt>, or <tt>null</tt> if not available * under the current runtime environment */ private static final LockFile newNIOLockFile() { if (NIO_FILELOCK_AVAILABLE && NIO_LOCKFILE_CLASS != null) { try { return (LockFile) NIO_LOCKFILE_CLASS.newInstance(); } catch (Exception e) { // e.printStackTrace() } } return null; } /** * To allow subclassing without exposing a public constructor. */ protected LockFile() {} /** * Retrieves a <tt>LockFile</tt> instance, initialized with a <tt>File</tt> * object whose path is the canonical form of the one specified by the * given <tt>path</tt> argument. <p> * * The resulting <tt>LockFile</tt> instance does not yet hold a lock * condition on the file with the given path, nor does it guarantee that the * file pre-exists or is created. * * However, upon successful execution, it is guaranteed that all required * parent directories have been created and that the underlying platform has * verified the specified path is legal on the file system of the underlying * storage partition. * * @return a <tt>LockFile</tt> instance initialized with a <tt>File</tt> * object whose path is the one specified by the given <tt>path</tt> * argument. * @param path the path of the <tt>File</tt> object with which the retrieved * <tt>LockFile</tt> object is to be initialized * @throws FileCanonicalizationException if an I/O error occurs upon * canonicalization of the given path, which is possible because * it may be illegal on the runtime file system or because * construction of the canonical path name may require native file * system queries * @throws FileSecurityException if a required system property value cannot * be accessed, or if a security manager exists and its <tt>{@link * java.lang.SecurityManager#checkRead}</tt> method denies read * access to the file; or if its <tt>{@link * java.lang.SecurityManager#checkRead(java.lang.String)}</tt> * method does not permit verification of the existence of all * necessary parent directories; or if the <tt>{@link * java.lang.SecurityManager#checkWrite(java.lang.String)}</tt> * method does not permit all necessary parent directories to be * created */ public static final LockFile newLockFile(final String path) throws FileCanonicalizationException, FileSecurityException { LockFile lockFile = newNIOLockFile(); if (lockFile == null) { lockFile = new LockFile(); } lockFile.setPath(path); return lockFile; } /** * {@link org.hsqldb.persist.Logger#acquireLock(java.lang.String)} * delegate.<p> * * Retrieves a new <tt>LockFile</tt> object holding a cooperative lock * condition upon the file with the given path, appended with the * extension '.lck'. <p> * * @param path of the lock file, to which will be appended '.lck' * @throws org.hsqldb.HsqlException if the lock condition cannot * be obtained for any reason. * @return a new <tt>LockFile</tt> object holding a cooperative lock * condition upon the file with the given path, appended with the * extension '.lck' */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -