image
 
image
FileLockingProtocol.java


/*::.
==================================================================================================================================
=================================================¦ Copyright © 2002 Allen Baker ¦=================================================
                                                 +------------------------------+
File:       FileLockingProtocol.java
Originator: Allen Baker (2002.03.02)
LayoutRev:  5
================================================================================================================================== */



/*.
==========================================================================================
Package
------------------------------------------------------------------------------------------ */
package cosmicabyss.com.lib;



/*.
==========================================================================================
Imports
------------------------------------------------------------------------------------------ */
import java.io.*;
import java.util.*;



/*::
======================================================================================================================== *//**
Instances of this class implement a cooperative file-locking protocol. A cooperative file-locking protocol is used to
control access to a shared resource. In a cooperative file-locking protocol, the participating programs use a lock file
as a token that gives permission to the holder of the token to access the shared resource. The holder of the token is
the program that creates the lock file. When the holder of the token is done, it deletes the lock file.<P>

When a program wants to access the shared resource, it checks to see if the lock file exists. If it does, the program
must wait to access the shared resource. If the lock file does not exist, the program creates it and accesses the shared
resource. While the lock file exists, other programs are prevented from accessing the shared resource. When the program
is done accessing the shared resource, it deletes the lock file and in so doing, allows another program to access the
shared resource.<P>

<I> Note: Using the Standard Java SDK method createNewFile() from within getLock() is the key to making this whole
FileLockingProtocol class work because it is an <B>atomic operation</B>. In other words, two processes cannot both call
createNewFile() at the same time and both succeed. CreateNewFile() atomically creates a new, empty file if and only if
the file does not yet exist. <B>The check for the existence of the file and the creation of the file if it does not
exist are a single operation that is atomic with respect to all other filesystem activities that might affect the
file</B>. </I>

<P>
   <DL>
      <DT>
         <B>
            Example usage:
         </B>
         <DD>
            <BLOCKQUOTE>
               <PRE id="unindent">
                  this snippet will try for up to 5 seconds to obtain a
                  lock by creating the lock file "/lock.file". Then, after
                  accessing the shared resource, it releases the lock by
                  deleting the lock file.

                  FileLockingProtocol  locker = new FileLockingProtocol("/lock.file",5);

                  if (locker.getLock())
                     {
                     System.out.println("obtained lock; accessing shared resource");
                     locker.releaseLock();
                     }
                  else
                     {
                     System.out.println("cannot obtain lock; cannot access shared resource");
                     }
               </PRE>
            </BLOCKQUOTE>
         </DD>
      </DT>
      <DT>
         <B>
            View Source:
         </B>
         <DD>
            <A href="FileLockingProtocol.java.html">
               FileLockingProtocol.java
            </A>
         </DD>
      </DT>
      <DT>
         <B>
            Author:
         </B>
         <DD>
            <A href="mailto:sourcecode.v01@cosmicabyss.com">
               Allen Baker
            </A>
         </DD>
      </DT>
   </DL>
*//*
======================================================================================================================== */
public class FileLockingProtocol
   {



   /*:                                    :METHOD:000:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method create a object that can be used to get and release a lock file.

   <P><B>Implementation: </B><A HREF="FileLockingProtocol.java.html#000">View source</A>

   @param
      pLockFileName is the name of the file that will be used as a lock file.
   @param
      pMaxSecondsToWait is the maximum amount of time to wait (in seconds) for the lock file to become
      available for creation before giving up on a getLock() attempt.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type> FileLockingProtocol(Type pLockFileName, int pMaxSecondsToWait)
      {
      mLockFileName     = XString.toXString(pLockFileName);
      mMaxSecondsToWait = pMaxSecondsToWait;
      }



   /*:                                    :METHOD:001:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method gets the lock file that controls access to a shared resource.<P>

   This method will poll for an opportunity to create the lock file. It will continue polling up to the
   pMaxSecondsToWait that was passed into the constructor.

   Its polling interval is a function of pMaxSecondsToWait. The minimum polling interval is 1 second
   and the maximum polling interval is 5 minutes.

   At pMaxSecondsToWait above 1min, the polling interval is 1/60th of pMaxSecondsToWait in
   milliseconds. The "inc" column of this table gives the polling interval for some sample
   pMaxSecondsToWait.

      <BLOCKQUOTE>
         <PRE id="unindent">
            wait  pMaxSecondsToWait wait in ms  wait/60  inc in ms   inc
            -----+-----------------+-----------+--------+-----------+------
            1sec  1                 1000        16       1000        1sec
            30sec 30                30000       500      1000        1sec
            1min  60                60000       1000     1000        1sec

            2min  120               120000      2000     2000        2sec
            30min 1800              1800000     30000    30000       30sec
            1hr   3600              3600000     60000    60000       1min
            2hr   7200              7200000     120000   120000      2min
            4hr   14400             14400000    240000   240000      4min

            5hr   18000             18000000    300000   300000      5min

            8hr   28800             28800000    480000   300000      5min
            16hr  57600             57600000    960000   300000      5min
            32hr  115200            115200000   1920000  300000      5min
         </PRE>
      </BLOCKQUOTE>

   <P><B>Implementation: </B><A HREF="FileLockingProtocol.java.html#001">View source</A>

   @return
      True if the lock was obtained, false otherwise.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public boolean getLock() throws IOException
      {
      /*.
      ==========================================================================================
      If this object already owns the lock file, then just return true.
      ------------------------------------------------------------------------------------------ */
      if (mLockFileOwner)
         {
         return true;
         }
      /*.
      ==========================================================================================
      Convert to milliseconds: the amount of time to poll (total polling time) before giving up
      ------------------------------------------------------------------------------------------ */
      int  maxMillisecondsToWait;
      maxMillisecondsToWait = mMaxSecondsToWait * 1000;
      /*.
      ==========================================================================================
      Compute the frequency of the polls. Polls will occur every 1/60th of the total polling
      time. However, the most frequently that polls can occur is once every 1000ms or every 1
      second (no faster than once every 1 sec). The least frequently is once every 300000ms or
      once every 5 minutes (no slower than once every 5min).
      ------------------------------------------------------------------------------------------ */
      int  millisecondsBetweenPolls;
      millisecondsBetweenPolls = maxMillisecondsToWait / 60;
      millisecondsBetweenPolls = UMath.max(1000,  millisecondsBetweenPolls);
      millisecondsBetweenPolls = UMath.min(300000,millisecondsBetweenPolls);
      /*.
      ==========================================================================================
      Go through the process of trying to create the lock file.
      ------------------------------------------------------------------------------------------ */
      long  startTime = System.currentTimeMillis();
      mLockFile = new File(mLockFileName.string());
      while (true)
         {
         /*.
         ==========================================================================================
         Using createNewFile() is the key to making this whole FileLockingProtocol class work
         because it is an atomic operation. In other words, two processes cannot both call
         createNewFile() at the same time and both succeed. CreateNewFile() atomically creates a
         new, empty file named by this abstract pathname if and only if a file with this name does
         not yet exist. The check for the existence of the file and the creation of the file if it
         does not exist are a single operation that is atomic with respect to all other filesystem
         activities that might affect the file.
         ------------------------------------------------------------------------------------------ */
         if (mLockFile.createNewFile())
            {
            mLockFileOwner = true;
            return true;
            }
         if ((System.currentTimeMillis() - startTime) > maxMillisecondsToWait)
            {
            return false;
            }
         Util.sleep(millisecondsBetweenPolls);
         }
      }



   /*:                                    :METHOD:002:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method deletes the lock file and in so doing, allows another program to access the shared
   resource.

   <P><B>Implementation: </B><A HREF="FileLockingProtocol.java.html#002">View source</A>

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void releaseLock()
      {
      /*.
      ==========================================================================================
      Only delete the file if we actually own it.
      ------------------------------------------------------------------------------------------ */
      if (mLockFileOwner)
         {
         mLockFile.delete();
         mLockFileOwner = false;
         }
      }



   /*:                                    :METHOD:003:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method forcably deletes the lock file and in so doing, allows another program to access the
   shared resource. This method violates the protocol and should be used only to delete the file after
   a program that created it crashed without deleting it.

   <P><B>Implementation: </B><A HREF="FileLockingProtocol.java.html#003">View source</A>

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void forceReleaseLock() throws Exception
      {
      mLockFile = new File(mLockFileName.string());
      mLockFile.delete();
      mLockFileOwner = false;
      }



   /*:.
   ==============================================================================================================
   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@[  Protected  ]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
   ============================================================================================================== */



   /*:.
   ==============================================================================================================
   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@[  Private  ]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
   ============================================================================================================== */



   /*.
   ==========================================================================================
   ------------------------------------------------------------------------------------------ */
   private boolean  mLockFileOwner = false;
   private XString  mLockFileName;
   private File     mLockFile;
   private int      mMaxSecondsToWait;



   /*:.
   ==============================================================================================================
   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@[  Inner Classes  ]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
   ============================================================================================================== */



   /*:.
   ==============================================================================================================
   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@[  Public Static Methods  ]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
   ============================================================================================================== */



   /*:                                    :METHOD:004:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method allows the FileLockingProtocol class file to be unit tested as a stand-alone
   application. It's the method that's called when the class is invoked from the command line by using
   the java application launcher - "java". Main() is not a required method, but the practice of putting
   one in each class and wrapping class test code within it allows easy unit testing of the class; and
   main does not need to be removed when testing is complete.

   <P>
      <DL>
         <DT>
            <B>
               Command line usage:
            </B>
            <DD>
               Java com.hp.ces.provision.FileLockingProtocol
            </DD>
         </DT>
      </DL>

   <P><B>Implementation: </B><A HREF="FileLockingProtocol.java.html#004">View source</A>

   @param
      pArgs contains the command line arguments with which the class was invoked as an application.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public static void main(String[] pArgs) throws Exception
      {
      FileLockingProtocol  locker = new FileLockingProtocol(new XString("/lock.file"),5);

      if (locker.getLock())
         {
         System.out.println("obtained lock; accessing shared resource");
         locker.releaseLock();
         }
      else
         {
         System.out.println("cannot obtain lock; cannot access shared resource");
         }
      }



   }  // class FileLockingProtocol