diff options
Diffstat (limited to 'java/JACE/netsvcs/Token/TokenAcceptor.java')
-rw-r--r-- | java/JACE/netsvcs/Token/TokenAcceptor.java | 353 |
1 files changed, 353 insertions, 0 deletions
diff --git a/java/JACE/netsvcs/Token/TokenAcceptor.java b/java/JACE/netsvcs/Token/TokenAcceptor.java new file mode 100644 index 00000000000..53adf08753b --- /dev/null +++ b/java/JACE/netsvcs/Token/TokenAcceptor.java @@ -0,0 +1,353 @@ +package JACE.netsvcs.Token; + +import java.io.*; +import java.net.*; +import java.util.*; +import JACE.OS.*; +import JACE.Misc.*; +import JACE.Connection.*; +import JACE.netsvcs.Server; + +/** + * Server for the token service. Launches TokenRequestHandlers as + * connections are made. Currently, the activation strategy must be + * thread per connection since the operations are allowed to block + * during acquires, etc. + * <P> + * Two types of locks are supported by default -- Mutex and RWMutex. + * New lock types can be added from the command line without changing + * any code in the service. To do this, just create a class which + * implements the LockHandler interface. + * <P> + * When a request for a new lock comes in, a LockHandler of the corresponding + * type is created and a mapping is created between the lock name and the + * handler. Later requests reuse that mapping. + * <P> + * <B>Valid command line arguments:</B> + * <PRE> + * -f (class name):(type) Specify a LockHandler for a type of lock"); + * -p (port number) Port to listen on for clients"); + * -d Enable debugging messages"); + * </PRE> + * + *@see JACE.netsvcs.Server + *@see TokenRequestHandler + *@see LockHandler + *@see LockTypes + */ +public class TokenAcceptor extends Server +{ + protected void addDefaultFactories() throws ClassNotFoundException { + addHandlerFactory(LockTypes.MUTEX, + Class.forName("JACE.netsvcs.Token.MutexHandler")); + addHandlerFactory(LockTypes.RWLOCK, + Class.forName("JACE.netsvcs.Token.RWMutexHandler")); + } + + /** + * Default constructor. + */ + public TokenAcceptor() { + + // Set the name in case we aren't using the service configurator. + name ("Token Service"); + + lockHandlerMap_ = new Hashtable(); + handlerFactoryMap_ = new Hashtable(); + clientHandlerMap_ = new Hashtable (); + } + + /** + * Add a map between a type of lock and the factory which + * creates LockHandler instances that handle it. + * + *@see LockTypes + *@param type number representing the type of lock + *@param factory Class object for a LockHandler class + */ + public void addHandlerFactory(Integer type, Class factory) { + handlerFactoryMap_.put(type, factory); + } + + /** + * Add a map between a type of lock and the factory which + * creates LockHandler instances that handle it. + * + *@see LockTypes + *@param type number representing the type of lock + *@param factory Class object for a LockHandler class + */ + public void addHandlerFactory(int type, Class factory) { + addHandlerFactory(new Integer(type), factory); + } + + /** + * Remove the LockHandler factory which handles locks + * of the specified type. + * + *@param type type of LockHandler to cease supporting + *@return the LockHandler instance (or null if not found) + */ + public Object removeHandlerFactory(Integer type) { + return handlerFactoryMap_.remove(type); + } + + /** + * Remove the LockHandler factory which handles locks + * of the specified type. + * + *@param type type of LockHandler to cease supporting + *@return the LockHandler instance (or null if not found) + */ + public Object removeHandlerFactory(int type) { + return handlerFactoryMap_.remove(new Integer(type)); + } + + /** + * Retrieve the LockHandler corresponding to the given name + * or create a new one if it doesn't exist. This is called by + * TokenRequestHandlers. + * + *@param lockName name of the lock to retrieve or create a LockHandler for + *@param lockType type of the lock + *@return LockHandler which handles the lock with that name + */ + public LockHandler getLockHandler (String lockName, + int lockType) { + synchronized (lockHandlerMap_) { + + Object obj = lockHandlerMap_.get(lockName); + + if (obj != null) + return (LockHandler)obj; + else { + LockHandler handler = newHandler (lockType); + lockHandlerMap_.put (lockName, handler); + return handler; + } + } + } + + /** + * Create a new LockHandler of the specified type. + * + *@param type type of LockHandler to create + *@return a new LockHandler instance + */ + protected LockHandler newHandler (int type) { + ACE.DEBUG ("Creating new handler of type " + type); + Object factoryObj = handlerFactoryMap_.get(new Integer(type)); + if (factoryObj == null) + return null; + + Class factory = (Class)factoryObj; + LockHandler handler = null; + + try { + handler = (LockHandler)factory.newInstance(); + } catch (InstantiationException e) { + ACE.ERROR("Can't create a handler of type " + type); + } catch (IllegalAccessException e) { + ACE.ERROR("Handler of type " + type + + " must have a default constructor"); + } + return handler; + } + + /** + * Simple main program. See the class description for more + * information about command line arguments. + */ + public static void main(String args[]) { + TokenAcceptor ta = new TokenAcceptor(); + + ta.init(args); + } + + /** + * Create a new TokenRequestHandler instance. + */ + protected SvcHandler makeSvcHandler() + { + return new TokenRequestHandler(); + } + + /** + * Sets up the default factories so the user can override them on + * the command line, then delegates back to Server.init (String[]). + * + *@see JACE.netsvcs.Server#init + *@param args command line arguments + *@return -1 on failure, 0 on success + */ + public int init(String [] args) { + try { + addDefaultFactories (); + } catch (ClassNotFoundException e) { + ACE.ERROR ("Can't find default factory " + e.getMessage ()); + return -1; + } + + return super.init (args); + } + + /** + * Prints out the valid command line arguments. See the class + * description for more information. Called by Server.init when + * parseArgs returns -1. + */ + protected void printUsage () + { + ACE.ERROR ("Valid options:\n"); + ACE.ERROR ("-f <class name>:<type> Specify a handler for a type of lock"); + ACE.ERROR ("-p <port number> Port to listen on for clients"); + ACE.ERROR ("-d Enable debugging messages"); + } + + /** + * Parses the command line arguments. See the class description + * for more information. + * + *@param args command line arguments + *@return -1 on failure, 0 on success + */ + protected int parseArgs(String [] args) + { + int c = 0; + GetOpt opt = new GetOpt(args, "p:f:d", true); + + try { + + while ((c = opt.next ()) != -1) { + switch (c) + { + case 'd': + ACE.enableDebugging (); + ACE.DEBUG ("Debugging is enabled"); + break; + case 'p': + if (!port (opt.optarg ())) + return -1; + break; + case 'f': + if (newHandlerFactory (opt.optarg ()) < 0) + return -1; + break; + default: + ACE.ERROR("Unknown argument: " + (char)c); + return -1; + } + } + } catch (ArrayIndexOutOfBoundsException e) { + ACE.ERROR ("Option -" + (char)c + " requires an argument"); + return -1; + } + + return 0; + } + + /** + * Load the Class for the specified LockHandler and create a mapping + * from its type to the Class instance. Used to parse the command + * line pair of (class name):(type). + * + *@param nameAndType (class name):(type) pair from the command line + *@return -1 on failure, 0 on success + */ + protected int newHandlerFactory (String nameAndType) + { + int colon = nameAndType.lastIndexOf (':'); + + if (colon < 1) { + ACE.ERROR ("Invalid -f <class name>:<type num> for handler: " + + nameAndType); + return -1; + } + + int type = 0; + try { + type = Integer.parseInt (nameAndType.substring (colon + 1)); + } catch (NumberFormatException e) { + ACE.ERROR ("Invalid token type: " + e.getMessage ()); + return -1; + } + + String name = nameAndType.substring (0, colon); + + Class factory; + try { + factory = Class.forName (name); + } catch (ClassNotFoundException e) { + ACE.ERROR (e.getMessage ()); + return -1; + } + + addHandlerFactory (type, factory); + ACE.DEBUG ("New handler " + name + " with type " + type); + + return 0; + } + + /** + * Create a mapping between a client ID and a LockHandler. This is + * only used by TokenRequestHandlers in order to keep track of which + * locks a client touches. That way, if/when a client disconnects, + * all its locks can be abandoned successfully. + * + *@param clientID client identification (key in the mapping) + *@param handler LockHandler to map to (value in the mapping) + * + */ + void addClientLockHandler (String clientID, + LockHandler handler) + { + Object obj = clientHandlerMap_.get (clientID); + if (obj == null) { + // Probably won't have more than 10 locks per client ID, and the Vector + // should resize automatically even if someone does. + Vector handlerList = new Vector (10); + handlerList.addElement (handler); + clientHandlerMap_.put (clientID, handlerList); + } else { + Vector handlerList = (Vector)obj; + int alreadyThereIndex = handlerList.indexOf (handler); + if (alreadyThereIndex == -1) + handlerList.addElement (handler); + } + } + + /** + * Called by TokenRequestHandlers to remove a specified client ID + * from the client ID to LockHandler mapping. + */ + void removeClient (String clientID) + { + clientHandlerMap_.remove (clientID); + } + + /** + * Called by TokenRequestHandlers to obtain a list of all LockHandlers + * accessed by a particular client. Useful for abandoning the locks. + */ + Enumeration getClientLockHandlers (String clientID) + { + Object obj = clientHandlerMap_.get (clientID); + if (obj == null) + return null; + else + return ((Vector)obj).elements (); + } + + // These should be replaced by weak hash maps when available + + // Map consisting of (token name) to (LockHandler instance) pairs + private Hashtable lockHandlerMap_; + + // Map consisting of (Integer token type) to (Class instance for + // corresponding LockHandler class) + private Hashtable handlerFactoryMap_; + + // Map consisting of (client ID) to (Vector of LockHandler) pairs + private Hashtable clientHandlerMap_; +} + |