/*
* This file or a portion of this file is licensed under the terms of
* the Globus Toolkit Public License, found in file ../GTPL, or at
* http://www.globus.org/toolkit/download/license.html. This notice must
* appear in redistributions of this file, with or without modification.
*
* Redistributions of this Software, with or without modification, must
* reproduce the GTPL in: (1) the Software, or (2) the Documentation or
* some other similar material which is provided with the Software (if
* any).
*
* Copyright 1999-2004 University of Chicago and The University of
* Southern California. All rights reserved.
*/
package org.griphyn.vdl.util;
import org.griphyn.vdl.util.*;
import java.util.*;
import java.io.*;
/**
* @author Jens-S. Vöckler
* @author Yong Zhao
* @version $Revision$
*
* @see java.io.File
*/
public class LockHelper {
/**
* The suffix to use for the regular lock file.
*/
public static final String LOCK_SUFFIX = ".lock";
/**
* The initial timeout to use when retrying with exponential backoff.
*/
private long m_timeout;
/**
* The number of retries to attempt obtaining a lock.
*/
private int m_retries;
/**
* default ctor.
*/
public LockHelper()
{
this.m_timeout = 250;
this.m_retries = 10;
}
/**
* ctor.
*/
public LockHelper( long timeout, int retries )
{
this.m_timeout = timeout;
this.m_retries = retries;
}
/**
* Creates a file-based lock file in an NFS secure fashion. Do not use
* this function for locking, use {@link #lockFile(String)} instead.
* One exception is to use this method for test-and-set locking.
*
* @param filename is the file to create a lockfile for
* @return the representation of the lock file, or <code>null</code>
* in case of error.
*/
public File createLock( String filename )
{
// create local names from basename
File result = null;
File lock = new File( filename + LockHelper.LOCK_SUFFIX );
// exclusively create new file
boolean created = false;
try {
created = lock.createNewFile();
} catch ( IOException ioe ) {
Logging.instance().log( "lock", 0, "while creating lock " +
lock.getPath() + ": " + ioe.getMessage() );
created = false; // make extra sure
}
// if the lock was created, continue
if ( created ) {
// postcondition: file was created
Logging.instance().log( "lock", 2, "created lock " + lock.getPath() );
LockFileSet.instance().add( lock );
lock.setLastModified( System.currentTimeMillis() ); // force NFS
result = lock;
} else {
// unable to rename file to lock file: lock exists
Logging.instance().log( "lock", 1, "lock " + lock.getPath() +
" already exists" );
}
// may be null
return result;
}
/**
* Locks a file using an empty lockfile. This method repeatedly retries
* to lock a file, and gives up after a few seconds.
*
* @param filename is the basename of the file to lock.
* @return true, if the file was locked successfully, false otherwise.
*/
public boolean lockFile( String filename )
{
long timeout = this.m_timeout;
// do five retries
for ( int i=0; i<this.m_retries; ++i ) {
File lock = this.createLock(filename);
if ( lock == null ) {
// lock is busy, try again later
Logging.instance().log( "lock", 1, "file " + filename + " is busy, " +
"sleeping " + Long.toString(timeout) + " ms " +
"(retry " + Integer.toString(i) + ')' );
try {
Thread.sleep(timeout);
} catch ( InterruptedException ie ) {
// ignore, as usual
}
timeout <<= 1;
} else {
// we got the lock, go ahead, and write our pid into it
// FIXME: we don't have a pid in Java (in Windows)
return true;
}
}
// postcondition: timeout out after retries
Logging.instance().log( "default", 0, "unable to lock " + filename +
", still busy, giving up!" );
return false;
}
/**
* Releases a lock file from its basename.
* @param filename is the name of the basename of the file to unlock.
* The locking extension will be added internally.
* @return true, if the lock was removed successfully, false otherwise.
*/
public boolean unlockFile( String filename )
{
File lock = new File( filename + LockHelper.LOCK_SUFFIX );
LockFileSet.instance().remove( lock );
boolean result = lock.delete();
if ( result )
Logging.instance().log( "lock", 2, "removed lock " + lock.getPath() );
return result;
}
}