private final void checkHeartbeat(boolean withCreateNewFile)
    throws LockFile.FileSecurityException,
           LockFile.LockHeldExternallyException,
           LockFile.UnexpectedEndOfFileException,
           LockFile.UnexpectedFileIOException,
           LockFile.UnexpectedFileNotFoundException,
           LockFile.WrongLengthException, LockFile.WrongMagicException {

        long now;
        long lastHeartbeat;
        long length = 0;

        try {
            if (withCreateNewFile) {
                try {
                    file.createNewFile();

                    return;
                } catch (IOException ioe) {}
            }

            if (!file.exists()) {
                return;
            }

            length = file.length();
        } catch (SecurityException se) {
            throw new FileSecurityException(this, "checkHeartbeat", se);
        }

        if (length != USED_REGION) {
            throw new WrongLengthException(this, "checkHeartbeat", length);
        }

        // Compute the current wall clock time *first* to reduce possibility
        // of unwanted time dilation effects introduced, for example,
        // by intervening thread or process context switches under CPU
        // bursts.
        //
        // Example:
        //
        // Say currentTimeMillis is actually somewhere in (-0.5 and 0.5]
        // and another LockFile concurrently writes a 0-valued heartbeat
        // timestamp.
        //
        // Then, if readHeartbeat comes first here, happens to 'win the race
        // condition' (reads the previous heartbeat: -10,000) and an intervening
        // switch causes greater than ~0.5 millisecond elapsed time to
        // be experienced between readHeartbeat and currentTimeMillis, then
        // currentTimeMillis will be computed as n (n > 0), and (now -
        // lastHearbeat) will be HEARTBEAT_INTERVAL + n, instead of
        // HEARTBEAT_INTERVAL.
        //
        // Now, let n be greater than (HEARTBEAT_INTERVAL_PADDED -
        // HEARTBEAT_INTERVAL).
        //
        // Then the check will succeed, although it should fail.
        //
        // On the other hand, if currentTimeMillis is computed first, the
        // worst than can happen is a false positive indication that
        // the read heartbeat timestamp value was written by a live LockFile
        // instance.
        //
        now           = System.currentTimeMillis();
        lastHeartbeat = readHeartbeat();

        // Using padded interval to further reduce corner case effects,
        // now that heartbeat polling is in effect.
        //
        // Basically, it is absolutely essential to fail when a lock really is
        // still held elsewhere, so it is OK to fail on corner cases where
        // the last written heartbeat is very close to HEARTBEAT_INTERVAL
        // in the past and it is possible that timing jitters make it uncertain
        // whether the lock really is still held.
        if (Math.abs(now - lastHeartbeat) <= (HEARTBEAT_INTERVAL_PADDED)) {
            throw new LockHeldExternallyException(this, "checkHeartbeat", now,
                                                  lastHeartbeat);
        }
    }
private final void checkHeartbeat ( boolean withCreateNewFile ) throws LockFile . FileSecurityException , LockFile . LockHeldExternallyException , LockFile . UnexpectedEndOfFileException , LockFile . UnexpectedFileIOException , LockFile . UnexpectedFileNotFoundException , LockFile . WrongLengthException , LockFile . WrongMagicException { long now ; long lastHeartbeat ; long length = 0 ; try { if ( withCreateNewFile ) { try { file . createNewFile ( ) ; return ; } catch ( IOException ioe ) { } } if ( ! file . exists ( ) ) { return ; } length = file . length ( ) ; } catch ( SecurityException se ) { throw new FileSecurityException ( this , "checkHeartbeat" , se ) ; } if ( length != USED_REGION ) { throw new WrongLengthException ( this , "checkHeartbeat" , length ) ; } now = System . currentTimeMillis ( ) ; lastHeartbeat = readHeartbeat ( ) ; if ( Math . abs ( now - lastHeartbeat ) <= ( HEARTBEAT_INTERVAL_PADDED ) ) { throw new LockHeldExternallyException ( this , "checkHeartbeat" , now , lastHeartbeat ) ; } }
Checks whether the underlying file is an HSQLDB lock file and, if so,
whether its heartbeat timestamp is live (is, as far as can be known,
presumably in use by another <tt>LockFile</tt> instance) or stale. <p>

The check conforms to the following rules: <p>

<ol>
<li>If the parameter <tt>withCreateNewFile</tt> is true, {@link
java.io.File#createNewFile()} is available and its invocation
upon this object's <tt>file</tt> object indicates the underlying
file was atomically created if and only if it did not yet exist,
then return immediately (we have won the <em>race</em> to establish
a lock file). <p>

<li>Test again if the file exists, returning immediately if it does not
(there's no file and hence no heartbeat to check). <p>

An immediate return can occur here only under pre-JDK 1.2 runtimes;
or when the underlying platform does not correctly support
<tt>File.createNewFile()</tt>; or when the underlying file is deleted
within a very short time after i.), above (typically on the order of
microseconds). <p>

If the underlying platform employs a kernel-enforced mandatory file
locking blanket policy for open files (e.g. <em>Windows</em><sup>tm
</sup>), then this is likely a non-issue. And if this case makes
possible a race condition with another <tt>LockFile</tt> object
(because the test for existence yeilds false and subsequent file
creation is not atomic relative to all other file system actions), it
is still <em>very</em> unlikely that so unfortunate a timing will
occur as to allow simultaneous lock conditions to be established.
Finally, if some non-<tt>LockFile</tt> entity deleted the file, then
there are much worse things to worry about, in particular that the
files this object is supposed to protect are in reality subject to
arbitrary external modification and deletion by some uncooperative
process.<p>

<li>If a Java security exception is thrown while testing for existence,
it is rethrown as a <tt>FileSecurityException</tt>.

<li>Read the file's length.

<li>If a Java security exception is thrown reading length, it is rethrown
as a <tt>FileSecurityException</tt> (it <em>is</em> possible somebody
concurrently refreshed the system Policy in the interim).

<li>If the file does not have the expected length, a
<tt>WrongLengthException</tt> is thrown (we're trying to check
something that is not an HSQLDB lock file).

<li>Open an input steam to read the file's <tt>MAGIC</tt> and heartbeat
timestamp values.

<li>If a file not found exception is thrown above, it is rethrown as an
<tt>UnexpectedFileNotFoundException</tt> (we've already tested for
existence).

<li>If a Java security exception is thrown above, it is rethrown as a
<tt>FileSecurityException</tt> (it <em>is</em> possible somebody
concurrently refreshed the system Policy in the interim).

<li>Read the <tt>MAGIC</tt> value.

<li>If an end of file exepction is thrown above, it is rethrown as an
<tt>UnexpectedEndOfFileException</tt> (we've already tested the
length... did someone truncate the file in the interim?).

<li>If an I/O exception is thrown, it is rethrown as an
<tt>UnexpectedFileIOException</tt> (we've already tested for
existence, length and successfully opened a stream...did someone,
for example, force unmount or physically remove the underlying device
in the interim?)

<li>If the value read in does not match the expected <tt>MAGIC</tt> value,
a <tt>WrongMagicException</tt> is thrown (we're trying to check
something that is not an HSQLDB lock file).

<li>Read the heartbeat timestamp.

<li>If a Java security exception is thrown above, it is rethrown as a
<tt>FileSecurityException</tt> (it <em>is</em> possible somebody
concurrently refreshed the system Policy in the interim).

<li>If an end of file exection is thrown above, it is rethrown as an
<tt>UnexpectedEndOfFileException</tt> (we've already tested the
length... did someone truncate the file in the interim?).

<li>If an I/O exception is thrown, it is rethrown as an
<tt>UnexpectedFileIOException</tt> (we've already tested for
existence, length and successfully opened a stream...did someone,
for example, force unmount or physically remove the underlying device
in the interim?)

<li>If the timestamp read in is less than or equal to
{@link #HEARTBEAT_INTERVAL_PADDED} milliseconds into the past or
future, then a <tt>LockHeldExternallyException</tt> is thrown.

<li>Otherwise, this method simply returns.
</ol>

@param withCreateNewFile if <tt>true</tt>, attempt to employ
<tt>File.createNewFile()</tt> as part of the check so as to
eliminate potential race conditions when establising a new
lock file
@throws FileSecurityException if the check fails due to a Java
security permission check failure
@throws LockHeldExternallyException if it is determined that the
file's heartbeat timestamp is less than
<tt>HEARTBEAT_INTERVAL_PADDED</tt> into the past (or future)
@throws UnexpectedEndOfFileException if an <tt>EOFExceoption</tt> is
thrown while reading either the magic or heartbeat timestamp values
@throws UnexpectedFileIOException if an <tt>IOException</tt> other than
<tt>EOFException</tt> is thrown while reading either the magic or
heartbeat timestamp values
@throws UnexpectedFileNotFoundException if a
<tt>FileNotFoundException</tt> is thrown while attempting to open a
stream to read the underlying file's magic and heartbeat timestamp
values
@throws WrongLengthException if it is determined that the length
of the file does not equal {@link #USED_REGION}
@throws WrongMagicException if it is determined that the file's
content does not start with {@link #MAGIC}.