/* **************************************************************************

  $Archive: /njcl/src/com/novell/service/file/nw/naming/ExtendedAttributeLock.java $
  $Revision: 3 $
  $Modtime: 5/08/98 11:41a $
 
  Copyright (c) 1998 Novell, Inc.  All Rights Reserved.

  THIS WORK IS  SUBJECT  TO  U.S.  AND  INTERNATIONAL  COPYRIGHT  LAWS  AND
  TREATIES.   NO  PART  OF  THIS  WORK MAY BE  USED,  PRACTICED,  PERFORMED
  COPIED, DISTRIBUTED, REVISED, MODIFIED, TRANSLATED,  ABRIDGED, CONDENSED,
  EXPANDED,  COLLECTED,  COMPILED,  LINKED,  RECAST, TRANSFORMED OR ADAPTED
  WITHOUT THE PRIOR WRITTEN CONSENT OF NOVELL, INC. ANY USE OR EXPLOITATION
  OF THIS WORK WITHOUT AUTHORIZATION COULD SUBJECT THE PERPETRATOR TO
  CRIMINAL AND CIVIL LIABILITY.

****************************************************************************/

package com.novell.service.file.nw.naming;

import java.util.Hashtable;

/** @internal
 * Anybody using any extended attribute calls must be synchonized on the
 * singleton object obtained by calling the getInstance method of this class.
 * when the caller has closed the extended attribute, they must call the 
 * releaseInstance method of this class to free up the resource.
 */

public class ExtendedAttributeLock
{
   /*
      instances is a hashtable of hashtables, first level is context name
      second level is ea name
   */
   private static Hashtable instances = new Hashtable(5, 0.75f);
   private int count;
   private String contextName;
   private String eaName;

   /**
    * Constructs a new ExtendedAttributeLock for a given context name
    * 
    * <p>A singleton object needs to exist for any unique context node of the
    * file system.  Only the public static getInstance method should ever
    * call this constructor.
    * </p>
    *
    * @param contextName         The full context name that contains the ea
    *                            to be locked.
    * @param eaName              The name of the ea to create a lock for.
    */
    
   private ExtendedAttributeLock(String contextName, String eaName)
   {
      count = 0;
      this.contextName = contextName;
      this.eaName = eaName;
   }
 
   /**
    * Obtain an Object on which to be synchronized on when doing any EA work.
    * 
    * <p>The extended attributes are very touching in the Netware environment.
    * once a EA has been opened, and a read started, the whole EA must be 
    * read without interruption.  The same is true for writting.  This method
    * gaurentees that for any given EA in a given context name, a singleton 
    * object will be returned that the user can be synchronized on, thereby 
    * locking any given EA on a given context (Directory or File) to a single 
    * thread of access at any given time.
    * <p>
    * After the user closes the Extended Attribute, the releaseInstance method
    * must be called to free up resources.
    * </p>
    *
    * @param contextName         The full context name containing the EA to 
    *                            be locked.
    * @param eaName              EA name to be locked. 
    * @return                    The object the user should synchronize on.
    */
      
   public static Object getInstance(
      String contextName, 
      String eaName)
   {
      synchronized(instances)
      {
         ExtendedAttributeLock lock = null;
         Hashtable eaHash = (Hashtable)instances.get(contextName);
         if (eaHash == null)
         {
            eaHash = new Hashtable(5, 0.75f);
            instances.put(contextName, eaHash);
         }
         else
            lock = (ExtendedAttributeLock)eaHash.get(eaName);

         if (lock == null)
         {
            lock = new ExtendedAttributeLock(contextName, eaName);
            eaHash.put(eaName, lock);
            lock.incrementCount();
            return lock;
         }
         lock.incrementCount();
         return lock;
      }
   }

   /**
    * Called after the synchronized block is finished to free up resources.
    * 
    * @param lock                The object obtained from the getInstance
    *                            method.
    */
    
   public static void releaseInstance(Object lock)
   {
      ((ExtendedAttributeLock)lock).decrementCount();
   }

   /**
    * Debug toString
    *
    * <p>returns "contextName: " + contextName + " eaName: " + eaName +
    * " count: " + count;
    * </p>
    */

   public String toString()
   {
      return   "contextName: " + contextName + 
               " eaName: " + eaName +
               " count: " + count;
   }
       
   /**
    * increments the usage count of this instance.
    * 
    */
    
   void incrementCount()
   {
      ++count;
   }

   /**
    * decrements the usage count of this instance.  If usage count is 0, the
    * object is removed from the Hashtable.
    */
    
   void decrementCount()
   {
      synchronized(instances)
      {
         --count;
         if (count == 0)
         {
            Hashtable eaHash = (Hashtable)instances.get(contextName);
            // take the lock out of the eaHash table
            eaHash.remove(eaName); 
            if (eaHash.isEmpty())
               //  take the eaHash out of the context
               instances.remove(contextName);
         }
      }
   }
   public static Hashtable getHashtable()
   {
      return instances;
   }

}   