alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

HSQLDB example source code file (NIOLockFile.java)

This example HSQLDB source code file (NIOLockFile.java) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - HSQLDB tags/keywords

exception, exception, filelock, io, lockfile, max_nfs_lock_region, min_lock_region, min_lock_region, niolockfile, posix_manditory_filelock_default, posix_manditory_filelock_default, posix_manditory_filelock_property, randomaccessfile, string, string

The HSQLDB NIOLockFile.java source code

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


package org.hsqldb.persist;

import java.io.RandomAccessFile;
import java.nio.channels.FileLock;

/**
 * LockFile variant that capitalizes upon the availability of
 * {@link java.nio.channels.FileLock FileLock}. <p>
 *
 * <b>Configuration:

* * To enable POSIX mandatory file lock tagging, set the system property * {@link #POSIX_MANDITORY_FILELOCK_PROPERTY} true. <p> * * <hr> * * <b>Windowstm File Locking Notes:

* * There are two major differences between Unix and Windows locking.<p> * * First, <em>Windowstm supports a share reservation programming * interface. Share reservations apply to the entire file and are specified at * the time a file is created or opened. A share reservation consists of a pair * of modes. The first is the access mode, which is how the application will * access the file: read, write, or read/write. The second is the access * that the application will deny to other applications: none, read, write, * or read/write. When the application attempts to open a file, the operating * system checks to see if there are any other open requests on the file. If so, * it first compares the application's access mode with the deny mode of the * other openers. If there is a match, then the open is denied. If not, then * the operating system compares the application's deny mode with the access * mode of the other openers. Again, if there is a match, the open is denied.<p> * * Second, there is no advisory locking under <em>Windows. Whole file * locking, byte range locking, and share reservation locking are all * mandatory. <p> * * As a side note, research indicates that very few <em>Windows programs * actually rely on byte range mandatory locking. <p> * * <b>POSIX File Locking Notes:

* * There are three commonly found locking functions under POSIX - flock, lockf, * and fcntl.<p> * * These functions, while managed by the kernel, are advisory locking mechanisms * by default. That is, the kernel does not actually stop programs from reading, * writing or deleting locked files. Instead, each program must check to see if * a lock is in place and act accordingly (be cooperative).<p> * * This might be problematic when some programs are cooperative and others are * not, especially if you do not have the ability to recompile the uncooperative * code, yet it must be allowed to run.<p> * * As POSIX has evolved, provisions have been made to allow locks to be enforced * at the kernel level with mandatory locking semantics. For historical reasons * <sup>[1] and for practical reasons * <a href="#note2">[2], POSIX mandatory locking is generally * implemented such that it must be configured on a file-by-file basis. When a * program attempts to lock a file with lockf or fcntl, the kernel will prevent * other programs from accessing the file if and only if the file has mandatory * locking set (note: processes which use flock never trigger a mandatory * lock).<p> * * In addition, to enable mandatory locking under Linux, the target filesystem * device must be mounted with the <tt>mand option, for example:

* * <pre> * # mount -omand /dev/hdb3 /mnt/data * * # mount | grep /mnt/data * /dev/hdb3 on /mnt/data type ext3 (rw,mand) *</pre> * * To automate this across reboots, <tt>mand must be added to the * appropriate <tt>/etc/fstab entry.

* * Although Linux, Solaris and HP/UX are known to support kernel-enforced * mandatory locking semantics, some POSIX systems may provide no support at all, * while others may have system-specific configuration prerequisites. Some may * even enforce manditory locking semantics by default under certain conditions * while others may silently ignore mandatory locking directives or consistently * raise exception conditions in response to such directives. <p> * * Regardless of any wider scoped prerequisites, restrictions and behavioural * variations, it is generally true under POSIX that only specifically tagged * files will exhibit mandatory locks. <p> * * And it is generally accepted that the way to designate a file to be governed * by mandatory locking semantics is to set its sgid (setgroupid) bit and unset * its group execute bit. This has become the commonly accepted practice * specifically because the combination is devoid of meaning in the regular * sense. <p> * * As a specific illustration, if mandatory locking is required on all * pre-existing files in a certain directory, then the corresponding * <tt>chmod invocation would be:

* * <pre> * $ chmod g+s,g-x /path/to/directory/* * </pre> * * <hr> * * <a name="note1"/>[1] * The earliest versions of Unix had no way to lock files except to create * lock files. The idea is that two or more processes would more or less * simultaneously try to create a lock file in exclusive mode, via the * O_EXCL flag of the open( ) system call. The operating system would * return success to the process that won the race, and a "file exists" * error to losing processes. One problem with this scheme is that it * relies on the winning process to remove the lock file before it exits. * If the process is running buggy software, this might not happen. * Some applications mitigate this problem by recording the process ID of * the winner into the contents of the lock file. A process that finds that * it gets a "file exists" error can then read the lock file to see if the * owning process is still running. <p> * * Still, lock files can be clumsy. In the 1980s, Unix versions were * released with file locking support built into the operating system. * The System V branch of Unix offered file locking via the fcntl( ) system * call, whereas the BSD branch provided the flock( ) system call. In both * cases, when the process that creates the lock dies, the lock will be * automatically released. <p> * * In a perfect world all processes would use and honour a cooperative, or * "advisory" locking scheme. However, the world isn't perfect, and there's * a lot of poorly written code out there. <p> * * In trying to address this problem, the designers of System V UNIX came up * with a "mandatory" locking scheme, whereby the operating system kernel * would block attempts by a process to write to a file upon which another * process holds a "read" -or- "shared" lock, and block attempts both to * read and to write a file upon which a process holds a "write " -or- * "exclusive" lock. <p> * * The System V mandatory locking scheme was intended to have as little * impact as possible on existing user code. The scheme is based on marking * individual files as candidates for mandatory locking, and using the * existing fcntl()/lockf() interface for applying locks, just as if they * were normal, advisory locks. <p> * * <a name="note2"/>[2] * Even with mandatory locks, conflicts can occur. If program A reads in a * file, program B locks, edits, and unlocks the file, and program A then * writes out what it originally read, this may still be less than * desirable. <p> * * As well, it is generally true that nothing can override a mandatory * lock, not even root-owned processes. In this situation, the best root * can do is kill the process that has the lock upon the file. <p> * * And this can be especially problematic if a file upon which a mandatory * lock exists is also available via NFS or some other remotely-accessible * filesystem, specifically because the entire fileserver process may be * forced to block until the lock is released. <p> * * Indeed, these effects of mandatory locking policy are commonly encountered in * the <em>Windowstm environment, where all locks are of the * mandatory style. <p> * * <hr> * * <b>Research Results:

* * After some experimentation under JDK 1.5/6 and Linux (at least Fedora * Core 4), research results indicate that, after mounting the target file * system device using the described <tt>mand option and doing a * <tt>chmod g+s,g-x on the lock file before issuing a * <tt>FileChannel.tryLock(...), it is still possible to delete the lock * file from another process while the resulting <tt>FileLock is held, * although mandatory locking does appear to be in effect for read/write * operations. <p> * * This result was actually the one anticipated, because deletion of open files * is generally possible under POSIX. In turn, this is because POSIX file * deletion simply removes the iNode entry from the file's parent directory, * while any processes with open file handles continue to have access to the * deleted file. Only when all file handles have been released does the space * occupied by the file become elligible for reuse.<p> * * In other words, under both <em>Windowstm and Linux (at lease * FC 4), it appears this class is a practically useless extension to the base. * Under Java for <em>Windows, the act of holding a file handle open is * enough to produce a mandatory lock on the underlying file and also prevents * the file's deletion (i.e. the ultimately desired behavior occurs with or * without <tt>NIOLockFile), while under Java for Linux it appears * impossible to use NIO to produce a lock that prevents file deletion, yeilding * protection against inadvertent modification of the lock file practically * useless. <p> * * To put it another way, even after the much-heralded introduction of Java * NIO, without further resorting to platform-specific JNI alternatives, we * might as well still be back in the early 80's, because we cannot guarantee, * in a cross-platform manner, conditions better than offered by the earliest * known lock file approach, even on systems that do support mandatory file * locking semantics. Instead, we must simply still trust that all software is * well written and cooperative in nature. <p> * * @author boucherb@users * @version 1.8.0.3 * @since 1.7.2 */ final class NIOLockFile extends LockFile { /** The largest lock region that can be specified with <tt>java.nio. */ public static final long MAX_LOCK_REGION = Long.MAX_VALUE; /** * Generally, the largest lock region that can be specified for files on * network file systems. <p> * * From the java.nio.channels.FileLock API JavaDocs: <p> * * Some network filesystems do not implement file locks on regions * that extend past a certain position, often 2**30 or 2**31. * In general, great care should be taken when locking files that * reside on network filesystems. */ public static final long MAX_NFS_LOCK_REGION = (1L << 30); /** * The smallest lock region that still protects the area actually used to * record lock information. */ public static final long MIN_LOCK_REGION = LockFile.USED_REGION; /** * Whether POSIX mandatory file lock tagging is performed by default. <p> * * Under the default build, this is <tt>false, but custom * distributions are free to rebuild with a default <tt>true * value. <p> * * For <em>Windows targets, distributions should build with this set * false. */ public static final boolean POSIX_MANDITORY_FILELOCK_DEFAULT = false; /** * System property that can be used to control whether POSIX mandatory * file lock tagging is performed. */ public static final String POSIX_MANDITORY_FILELOCK_PROPERTY = "hsqldb.lockfile.posix.manditory.filelock"; /** * Represents an OS-enforced lock region upon this object's lock file. */ private volatile FileLock fileLock; /** * Retrieves whether POSIX mandatory file lock tagging is performed. <p> * * The value is obtained in the following manner: <p> * * <ol> * <li>manditory is assigned POSIX_MANDITORY_FILELOCK_DEFAULT * <li>manditory is assigned "true".equalsIgnoreCase( * System.getProperty(POSIX_MANDITORY_FILELOCK_PROPERTY, * manditory ? "true" : "false")); * </tt>, inside a try-catch block, to silently ignore any security * exception. * </ol> * @return <tt>true if POSIX mandatory file lock tagging is performed */ public boolean isPosixManditoryFileLock() { boolean manditory = POSIX_MANDITORY_FILELOCK_DEFAULT; try { manditory = "true".equalsIgnoreCase( System.getProperty( POSIX_MANDITORY_FILELOCK_PROPERTY, manditory ? "true" : "false")); } catch (Exception e) {} return manditory; } /** * Does work toward ensuring that a {@link #fileLock FileLock} exists upon * this object's lock file. <p> * * <b>POST:

* * Upon exit, if a valid <tt>fileLock could not be aquired for any * reason, then a best effort has also been expended toward releasing and * nullifying any resources obtained in the process. * * @return true if there is valid FileLock on exit, else false. */ protected boolean doOptionalLockActions() { // override return this.aquireFileLock(); } /** * Peforms best effort work toward releasing this object's * {@link #fileLock FileLock}, nullifying it in the process. * * @return true if <tt>fileLock is released successfully, else false */ protected boolean doOptionalReleaseActions() { // override return this.releaseFileLock(); } /** * Retrieves whether this object's {@link #fileLock FileLock} * represents a valid lock region upon this object's lock file. * * @return true if this object's {@link #fileLock FileLock} attribute is * valid, else false * @throws SecurityException 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 lock file; */ public boolean isValid() { // override try { return super.isValid() && (this.fileLock != null && this.fileLock.isValid()); } catch (Exception e) { return false; } } /** * Retrieves the String value: "fileLock = " + {@link #fileLock FileLock}.<p> * * @return the String value: "fileLock = " + fileLock */ protected String toStringImpl() { // override return "fileLock = " + this.fileLock; } // ------------------------- Internal Implementation------------------------ // does the real work of aquiring the FileLock private boolean aquireFileLock() { // PRE: // // raf is never null and is never closed upon entry. // // Rhetorical question to self: How does one tell if a RandomAccessFile // is closed, short of invoking an operation and getting an IOException // the says its closed (assuming you can control the Locale of the error // message)? // final RandomAccessFile lraf = super.raf; // In an ideal world, we would use a lock region back off approach, // starting with region MAX_LOCK_REGION, then MAX_NFS_LOCK_REGION, // then MIN_LOCK_REGION. // // In practice, however, it is just generally unwise to mount network // file system database instances. Be warned. // // In general, it is probably also unwise to mount removable media // database instances that are not read-only. boolean success = false; try { if (this.fileLock != null) { // API says never throws exception, but I suspect // it's quite possible some research / FOSS JVMs might // still throw unsupported operation exceptions on certain // NIO classes...better to be safe than sorry. if (this.fileLock.isValid()) { return true; } else { // It's not valid, so releasing it is a no-op. // // However, we should still clean up the referenceand hope // no previous complications exist (a hung FileLock in a // flaky JVM) or that gc kicks in and saves the day... // (unlikely, though). this.releaseFileLock(); } } if (isPosixManditoryFileLock()) { try { Runtime.getRuntime().exec(new String[] { "chmod", "g+s,g-x", file.getPath() }); } catch (Exception ex) { //ex.printStackTrace(); } } // Note: from FileChannel.tryLock(...) JavaDoc: // // @return A lock object representing the newly-acquired lock, // or <tt>null if the lock could not be acquired // because another program holds an overlapping lock this.fileLock = lraf.getChannel().tryLock(0, MIN_LOCK_REGION, false); // According to the API, if it's non-null, it must be valid. // This may not actually yet be the full truth of the matter under // all commonly available JVM implementations. // fileLock.isValid() API says it never throws, though, so // with fingers crossed... success = (this.fileLock != null && this.fileLock.isValid()); } catch (Exception e) {} if (!success) { this.releaseFileLock(); } return success; } // does the real work of releasing the FileLock private boolean releaseFileLock() { // Note: Closing the super class RandomAccessFile has the // side-effect of closing the file lock's FileChannel, // so we do not deal with this here. boolean success = false; if (this.fileLock == null) { success = true; } else { try { this.fileLock.release(); success = true; } catch (Exception e) {} finally { this.fileLock = null; } } return success; } }

Other HSQLDB examples (source code examples)

Here is a short list of links related to this HSQLDB NIOLockFile.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2024 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.