Author: mturk Date: Fri Oct 9 14:48:22 2009 New Revision: 823572 URL: http://svn.apache.org/viewvc?rev=823572&view=rev Log: Add FileLock class similar to NIO FileLock
Added: commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/exception/OverlappingFileLockException.java (with props) commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/io/FileLock.java (with props) Modified: commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/io/File.java commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/io/FileInstance.java commons/sandbox/runtime/trunk/src/main/native/os/unix/fsysio.c Added: commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/exception/OverlappingFileLockException.java URL: http://svn.apache.org/viewvc/commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/exception/OverlappingFileLockException.java?rev=823572&view=auto ============================================================================== --- commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/exception/OverlappingFileLockException.java (added) +++ commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/exception/OverlappingFileLockException.java Fri Oct 9 14:48:22 2009 @@ -0,0 +1,42 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.runtime.exception; + +/** + * An {...@code OverlappingFileLockException} is thrown when attempting to acquire + * a lock that overlaps an existing or pending lock held by this process. + */ +public class OverlappingFileLockException extends IllegalStateException +{ + + /** + * Constructs a {...@code OverlappingFileLockException}. + */ + public OverlappingFileLockException() + { + super(); + } + + /** + * Constructs a {...@code OverlappingFileLockException}. + */ + public OverlappingFileLockException(String msg) + { + super(msg); + } + +} Propchange: commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/exception/OverlappingFileLockException.java ------------------------------------------------------------------------------ svn:eol-style = native Modified: commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/io/File.java URL: http://svn.apache.org/viewvc/commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/io/File.java?rev=823572&r1=823571&r2=823572&view=diff ============================================================================== --- commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/io/File.java (original) +++ commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/io/File.java Fri Oct 9 14:48:22 2009 @@ -16,6 +16,7 @@ package org.apache.commons.runtime.io; +import java.io.FileNotFoundException; import java.io.IOException; import java.net.URI; import java.util.EnumSet; @@ -527,6 +528,47 @@ } /** + * Create new temporary {...@code FileInstance} inide {...@code path}. + * + * @param path directory abstract path. + * @param prefix temporary file prefix. + * @param sufix temporary file sufix. + * @param preserve if {...@code true} do not delete file on {...@code close}. + * + * @return new temporary {...@code FileInstance}. + */ + public static FileInstance createTempFile(File path, String prefix, + String sufix, boolean preserve) + throws FileNotFoundException, IOException, IllegalArgumentException, + SecurityException + { + if (prefix == null) { + throw new IllegalArgumentException(); + } + Descriptor fd = FileWrapper.mktemp(path, prefix, sufix, preserve); + return new FileInstance(fd); + } + + /** + * Create new temporary {...@code FileInstance} inside current directory. + * + * @param prefix file prefix. + * + * @return new temporary {...@code FileInstance}. + */ + public static FileInstance createTempFile(String prefix, String sufix, + boolean preserve) + throws FileNotFoundException, IOException, IllegalArgumentException, + SecurityException + { + if (prefix == null) { + throw new IllegalArgumentException(); + } + Descriptor fd = FileWrapper.mktemp(prefix, sufix, preserve); + return new FileInstance(fd); + } + + /** * Return target pathname of this abstract {...@code File} instance. * * @return Pathname this {...@code File} points to. Modified: commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/io/FileInstance.java URL: http://svn.apache.org/viewvc/commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/io/FileInstance.java?rev=823572&r1=823571&r2=823572&view=diff ============================================================================== --- commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/io/FileInstance.java (original) +++ commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/io/FileInstance.java Fri Oct 9 14:48:22 2009 @@ -21,6 +21,7 @@ import org.apache.commons.runtime.exception.ClosedDescriptorException; import org.apache.commons.runtime.exception.AsyncClosedDescriptorException; import org.apache.commons.runtime.exception.InvalidDescriptorException; +import org.apache.commons.runtime.exception.OverlappingFileLockException; import org.apache.commons.runtime.exception.TimeoutException; import org.apache.commons.runtime.util.StringManager; import java.io.Closeable; @@ -29,7 +30,9 @@ import java.io.IOException; import java.io.SyncFailedException; import java.nio.ByteBuffer; +import java.util.Enumeration; import java.util.EnumSet; +import java.util.Vector; /** * Allows reading from and writing to a file in a random-access manner. @@ -47,6 +50,10 @@ /* File's object lock */ private Object sync = new Object(); + /* + * List of all lock regions to this file. + */ + private Vector<FileLock> locks = new Vector<FileLock>(2); /** * Return the {...@link Descriptor} accosicated with this file. @@ -364,8 +371,34 @@ return FileWrapper.name(fd); } + private boolean regionOverlaps(long offset, long length) + { + for (Enumeration<FileLock> e = locks.elements(); e.hasMoreElements();) { + try { + FileLock lck = e.nextElement(); + /* Check if we already have the valid lock + * for the requested region. + */ + if (lck.isValid()) { + if (lck.overlaps(offset, length)) { + return true; + } + } + else { + locks.remove(lck); + } + } catch (Throwable te) { + } + } + return false; + } + /** * Lock the entire file. + * <p> + * To simulate the {...@link java.nio.channels.FileChannel#tryLock} use + * the {...@link FileLockType#NONBLOCK} type. + * </p> * * @param type * {...@code FileLockType} to acquire. @@ -379,12 +412,24 @@ * If lock operation times out. * @throws IOException * If some other I/O error occurs. - * @see #unlock + * @see FileLock#release */ - public void lock(EnumSet<FileLockType> type) - throws IOException + public FileLock lock(EnumSet<FileLockType> type) + throws OverlappingFileLockException, IOException { + if (regionOverlaps(0L, -1L)) { + /* We already have at least one lock, so we cannot + * lock the entire file. + */ + throw new OverlappingFileLockException(); + } + FileWrapper.lock(fd, type); + FileLock lock = new FileLock(fd, type); + /* Add the FileLock to the list of locks + */ + locks.add(lock); + return lock; } /** @@ -418,67 +463,26 @@ * If read operation times out. * @throws IOException * If some other I/O error occurs. - * @see #unlock + * @see FileLock#release */ - public void lock(EnumSet<FileLockType> type, long offset, long length) - throws IllegalArgumentException, IOException + public FileLock lock(EnumSet<FileLockType> type, long offset, long length) + throws OverlappingFileLockException, IllegalArgumentException, + IOException { if (offset < 0 || length < 0 || offset < length) { // unlock position is negative throw new IllegalArgumentException(); } - FileWrapper.lock(fd, type, offset, length); - } - - /** - * Unlock the file. - * - * @throws ClosedDescriptorException - * If this file is closed. - * @throws AsyncClosedDescriptorException - * If another thread closes this file while the unlock - * operation is in progress. - * @throws TimeoutException - * If unlock operation times out. - * @throws IOException - * If some other I/O error occurs. - * @see #lock - */ - public void unlock() - throws IOException - { - FileWrapper.unlock(fd); - } - - /** - * Unlock the file region. - * - * @param offset - * The offset in bytes where the locked region begins. - * @param length - * The length of the byte region to be unlocked. - * - * @throws IllegalArgumentException - * If {...@code offset < 0} or {...@code length < 0}. - * @throws ClosedDescriptorException - * If this file is closed. - * @throws AsyncClosedDescriptorException - * If another thread closes this file while the unlock - * operation is in progress. - * @throws TimeoutException - * If read operation times out. - * @throws IOException - * If some other I/O error occurs. - * @see #lock - */ - public void unlock(long offset, long length) - throws IllegalArgumentException, IOException - { - if (offset < 0 || length < 0 || offset < length) { - // unlock position is negative - throw new IllegalArgumentException(); + if (regionOverlaps(offset, length)) { + /* We already have locked overlapping region. + */ + throw new OverlappingFileLockException(); } - FileWrapper.unlock(fd, offset, length); + FileLock lock = new FileLock(fd, type, offset, length); + /* Add the FileLock to the list of locks + */ + locks.add(lock); + return lock; } /** Added: commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/io/FileLock.java URL: http://svn.apache.org/viewvc/commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/io/FileLock.java?rev=823572&view=auto ============================================================================== --- commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/io/FileLock.java (added) +++ commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/io/FileLock.java Fri Oct 9 14:48:22 2009 @@ -0,0 +1,246 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.runtime.io; + +import org.apache.commons.runtime.Descriptor; +import org.apache.commons.runtime.exception.ClosedDescriptorException; +import org.apache.commons.runtime.exception.AsyncClosedDescriptorException; +import org.apache.commons.runtime.exception.InvalidDescriptorException; +import org.apache.commons.runtime.exception.TimeoutException; +import java.io.IOException; +import java.util.EnumSet; + +/** + * A {...@code FileLock} represents a locked region of a file. + * <p> + * Locks have certain properties that enable collaborating processes to avoid + * the lost update problem or reading inconsistent data. Logically, a file lock + * can be <em>exclusive</em> or <em>shared</em>. Multiple processes can hold + * shared locks on the same region of a file, but only a single process can hold + * an exclusive lock on a given region of a file and no other process can + * simultaneously hold a shared lock overlapping the exclusive lock. An + * application can determine whether a {...@code FileLock} is shared or exclusive + * via the {...@code isShared()} method. + * <p> + * Locks held by a particular process cannot overlap one another. Applications + * can determine whether a proposed lock will overlap by using the {...@code + * overlaps(long, long)}) method. Locks held in other processes may overlap + * locks held in this process. Locks are shared amongst all threads in the + * acquiring process, and are therefore unsuitable for intra-process + * synchronization. + * <p> + * Once a lock is acquired, it is immutable in all its state except {...@code + * isValid()}. The lock will initially be valid, but may be rendered invalid by + * explicit removal of the lock, using {...@code release()}, or implicitly by + * closing the channel or exiting the process (terminating the virtual machine). + * <h3>Platform dependencies</h3> + * <p> + * Locks are intended to be true platform operating system file locks, and + * therefore locks held by the virtual machine process will be visible to other + * operating system processes. + * <p> + * The characteristics of the underlying operating system locks will show + * through in the Java implementation. For example, some platforms' locks are + * 'mandatory' -- meaning the operating system enforces the locks on processes + * that attempt to access locked regions of files; whereas other platforms' + * locks are only 'advisory' -- meaning that processes are required to + * collaborate to ensure locks are acquired and there is a potential for + * processes to not play well. To be on the safe side, it is best to assume that + * the platform is adopting advisory locks and always acquire shared locks when + * reading a region of a file. + * <p> + * On some platforms, the presence of a lock will prevent the file from being + * memory-mapped. On some platforms, closing a channel on a given file handle + * will release all the locks held on that file -- even if there are other + * channels open on the same file; their locks will also be released. The safe + * option here is to ensure that you only acquire locks on a single channel for + * a particular file and that becomes the synchronization point. + * <p> + * Further care should be exercised when locking files maintained on network + * file systems, since they often have further limitations. + */ +public class FileLock +{ + + /* + * The lock starting position. + */ + private final long offset; + /* + * The lock length in bytes. + */ + private final long length; + /* + * File Descriptor for this lock. + */ + private Descriptor fd; + /* + * FileLockType flags. + */ + private EnumSet<FileLockType> mode; + + /** + * Create new {...@code FileLock} that locks the entire file. + * + * @param fd + * the file {...@code Descriptor} this lock is attached to. + * @param mode + * file lock mode. + */ + protected FileLock(Descriptor fd, EnumSet<FileLockType> mode) + { + this.fd = fd; + this.mode = mode; + offset = 0; + length = -1; + } + + /** + * Create new {...@code FileLock} that locks the region of file. + * + * @param fd + * the file {...@code Descriptor} this lock is attached to. + * @param mode + * file lock mode. + * @param start + * the starting position for the lock. + * @param len + * the length of the lock. + */ + protected FileLock(Descriptor fd, EnumSet<FileLockType> mode, + long start, long len) + { + this.fd = fd; + this.mode = mode; + offset = start; + length = len; + } + + /** + * Indicates if the receiver's lock region overlaps the region described + * in the parameter list. + * + * @param start + * the starting position for the comparative lock. + * @param len + * the length of the comparative lock. + * @return {...@code true} if there is an overlap, {...@code false} otherwise. + */ + public final boolean overlaps(long start, long len) + { + /* + * Negative length means the entire file + * so it will always overlap with another lock + */ + if (length < 0 || len < 0) + return true; + final long end = offset + length - 1; + final long siz = start + len - 1; + if (end < start || offset > siz) { + return false; + } + return true; + } + + /** + * Indicates whether this lock is a valid file lock. The lock is + * valid unless the underlying Descriptor has been closed or it has been + * explicitly released. + * + * @return {...@code true} if the lock is valid, {...@code false} otherwise. + */ + public boolean isValid() + { + if (fd == null) + return false; + else + return fd.valid(); + } + + /** + * Returns the lock's starting position in the file. + * + * @return the lock position. + */ + public final long position() + { + return offset; + } + + /** + * Returns the length of the file lock in bytes. + * + * @return the size of the file lock in bytes. + */ + public final long size() + { + return length; + } + + /** + * Indicates if the file lock is shared with other processes + * or if it is exclusive. + * + * @return {...@code true} if the lock is a shared lock, + * {...@code false} if it is exclusive. + */ + public boolean isShared() + { + return mode.contains(FileLockType.SHARED); + } + + /** + * Indicates if the file lock is blocking or non-blocking. + * + * @return {...@code true} if the lock is blocking lock, + * {...@code false} if it is non-blocking. + */ + public boolean isBlocking() + { + return !mode.contains(FileLockType.NONBLOCK); + } + + /** + * Releases this particular lock on the file. + * If the lock is invalid then this method has no effect. + * Once released, the lock becomes invalid. + * + * @throws ClosedDescriptorException + * If this file is closed. + * @throws AsyncClosedDescriptorException + * If another thread closes this file while the unlock + * operation is in progress. + * @throws TimeoutException + * If unlock operation times out. + * @throws IOException + * If some other I/O error occurs. + */ + public void release() + throws IOException + { + if (fd == null) { + // Already released + return; + } + if (length < 0) + FileWrapper.unlock(fd); + else + FileWrapper.unlock(fd, offset, length); + fd = null; + } + +} Propchange: commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/io/FileLock.java ------------------------------------------------------------------------------ svn:eol-style = native Modified: commons/sandbox/runtime/trunk/src/main/native/os/unix/fsysio.c URL: http://svn.apache.org/viewvc/commons/sandbox/runtime/trunk/src/main/native/os/unix/fsysio.c?rev=823572&r1=823571&r2=823572&view=diff ============================================================================== --- commons/sandbox/runtime/trunk/src/main/native/os/unix/fsysio.c (original) +++ commons/sandbox/runtime/trunk/src/main/native/os/unix/fsysio.c Fri Oct 9 14:48:22 2009 @@ -760,7 +760,7 @@ rc = ACR_TIMEUP; } if (rd == 0) { - ACR_DescriptorSetErrEx(_E, f->descriptor, 0); + ACR_DescriptorSetErrEx(_E, f->descriptor, ACR_EOF); f->eof = 1; return -1; }