summaryrefslogtreecommitdiff
path: root/java
diff options
context:
space:
mode:
authoreea1 <eea1@ae88bc3d-4319-0410-8dbf-d08b4c9d3795>1999-08-24 23:12:46 +0000
committereea1 <eea1@ae88bc3d-4319-0410-8dbf-d08b4c9d3795>1999-08-24 23:12:46 +0000
commitb63497639790e8470a84e5f622f1ba6ce09d5c48 (patch)
tree149fc9d2627fda46d537166a749076cffbc4f7c1 /java
parent2277d3e8270d04092497d70fa24250bae88211a4 (diff)
downloadATCD-b63497639790e8470a84e5f622f1ba6ce09d5c48.tar.gz
Updated source files for netsvcs/Token.
Diffstat (limited to 'java')
-rw-r--r--java/JACE/netsvcs/Token/LockHandler.java38
-rw-r--r--java/JACE/netsvcs/Token/LockHandlerAdapter.java380
-rw-r--r--java/JACE/netsvcs/Token/LockOperations.java16
-rw-r--r--java/JACE/netsvcs/Token/LockTypes.java19
-rw-r--r--java/JACE/netsvcs/Token/MutexHandler.java51
-rw-r--r--java/JACE/netsvcs/Token/RWMutexHandler.java54
-rw-r--r--java/JACE/netsvcs/Token/RemoteLock.java543
-rw-r--r--java/JACE/netsvcs/Token/RemoteMutex.java28
-rw-r--r--java/JACE/netsvcs/Token/RemoteRWMutex.java29
-rw-r--r--java/JACE/netsvcs/Token/TokenAcceptor.java353
-rw-r--r--java/JACE/netsvcs/Token/TokenReply.java171
-rw-r--r--java/JACE/netsvcs/Token/TokenRequest.java426
-rw-r--r--java/JACE/netsvcs/Token/TokenRequestHandler.java180
-rw-r--r--java/JACE/netsvcs/Token/package.html16
14 files changed, 2304 insertions, 0 deletions
diff --git a/java/JACE/netsvcs/Token/LockHandler.java b/java/JACE/netsvcs/Token/LockHandler.java
new file mode 100644
index 00000000000..8e3612efb42
--- /dev/null
+++ b/java/JACE/netsvcs/Token/LockHandler.java
@@ -0,0 +1,38 @@
+package JACE.netsvcs.Token;
+
+/**
+ * Defines a handler for a certain type of lock. This allows new types
+ * of synchronization mechanisms to be added to the Token service without
+ * any modification of existing code. Implementing class instances that
+ * are registered (via the command line or another way)
+ * with the token service can be created as requests for that type of
+ * lock come into the service.
+ *
+ *@see LockHandlerAdapter
+ *@see MutexHandler
+ *@author Everett Anderson
+ */
+public interface LockHandler
+{
+ /**
+ * Process a given TokenRequest and construct the appropriate
+ * reply. The request has already been read from the connection,
+ * and the reply will be sent without the LockHandler having to
+ * worry about the details.
+ *
+ *@param caller TokenRequestHandler which is accessing this LockHandler
+ *@param request request read from the connection
+ *@return appropriate TokenReply (success, failure, etc)
+ */
+ TokenReply handleRequest(TokenRequestHandler caller,
+ TokenRequest request);
+
+ /**
+ * Release any claim the client represented with the given ID
+ * has on this handler's lock. This is used when a client
+ * disconnects.
+ *
+ *@param clientID ID of the client whose claims to abandon
+ */
+ void abandonLock (String clientID);
+}
diff --git a/java/JACE/netsvcs/Token/LockHandlerAdapter.java b/java/JACE/netsvcs/Token/LockHandlerAdapter.java
new file mode 100644
index 00000000000..e240000161c
--- /dev/null
+++ b/java/JACE/netsvcs/Token/LockHandlerAdapter.java
@@ -0,0 +1,380 @@
+package JACE.netsvcs.Token;
+
+import java.util.*;
+import JACE.ASX.*;
+import JACE.OS.*;
+import JACE.Concurrency.*;
+
+/**
+ * LockHandler implementation for any AbstractLock.
+ * <P>
+ * Provides the dispatching to appropriate methods on an AbstractLock
+ * as requests come in.
+ */
+public class LockHandlerAdapter implements LockHandler
+{
+ /**
+ * Constructor taking an AbstractLock to use as the locking
+ * mechanism the requests work on.
+ */
+ public LockHandlerAdapter (AbstractLock lock)
+ {
+ lock_ = lock;
+ }
+
+ /**
+ * Default constructor.
+ */
+ public LockHandlerAdapter ()
+ {
+ lock_ = null;
+ }
+
+ /**
+ * Dispatch the request according to its type, calling the
+ * appropriate methods on the AbstractLock member.
+ *
+ *@param caller TokenRequestHandler which called handleRequest (unused)
+ *@param request request to process
+ *@return appropriate reply to send to the client
+ */
+ public TokenReply handleRequest (TokenRequestHandler caller,
+ TokenRequest request)
+ {
+ String client = request.clientID ();
+ String token = request.tokenName ();
+ TokenReply result = null;
+
+ // Dispatch according to operation type
+ switch (request.operationType ())
+ {
+ case LockOperations.ACQUIRE:
+ ACE.DEBUG (client + " begins ACQUIRE for " + token);
+ result = acquireDispatcher (request);
+ break;
+ case LockOperations.RELEASE:
+ ACE.DEBUG (client + " begins RELEASE for " + token);
+ result = release (request);
+ break;
+ case LockOperations.RENEW:
+ ACE.DEBUG (client + " begins RENEW for " + token);
+ result = renew (request);
+ break;
+ case LockOperations.REMOVE:
+ ACE.DEBUG (client + " begins REMOVE for " + token);
+ result = remove (request);
+ break;
+ case LockOperations.TRY_ACQUIRE:
+ ACE.DEBUG (client + " begins TRY_ACQUIRE for " + token);
+ result = tryAcquireDispatcher (request);
+ break;
+ default:
+ ACE.ERROR ("Unknown operation: " + request.operationType ());
+ break;
+ }
+
+ ACE.DEBUG (client + " result: " + result);
+
+ return result;
+ }
+
+ /**
+ * Create a TimeValue from the given request's timeout information. Note
+ * that the time in the request is an absolute time timeout.
+ *
+ *@param request request to obtain the timeout info from
+ *@return null if useTimeout is false, otherwise a TimeValue
+ * representing the appropriate time period
+ */
+ protected TimeValue getTimeout (TokenRequest request)
+ {
+ if (request.useTimeout ())
+ return new TimeValue (request.sec (),
+ request.usec () * 1000);
+ else
+ return null;
+ }
+
+ /**
+ * Call acquireWrite on the lock, returning its return value.
+ *
+ *@see AbstractLock#acquireWrite
+ *@return value from the lock's operation
+ */
+ protected int acquireWrite (TokenRequest request, TimeValue timeout)
+ throws LockException, TimeoutException, InterruptedException
+ {
+ int result;
+
+ if (timeout != null)
+ result = lock_.acquireWrite (timeout);
+ else
+ result = lock_.acquireWrite ();
+
+ return result;
+ }
+
+ /**
+ * Call acquireRead on the lock, returning its return value.
+ *
+ *@see AbstractLock#acquireRead
+ *@return value from the lock's operation
+ */
+ protected int acquireRead (TokenRequest request, TimeValue timeout)
+ throws LockException, TimeoutException, InterruptedException
+ {
+ int result;
+
+ if (timeout != null)
+ result = lock_.acquireRead (timeout);
+ else
+ result = lock_.acquireRead ();
+
+ return result;
+ }
+
+ /**
+ * Call acquire on the lock, returning its return value.
+ *
+ *@see AbstractLock#acquire
+ *@return value from the lock's operation
+ */
+ protected int acquire (TokenRequest request, TimeValue timeout)
+ throws LockException, TimeoutException, InterruptedException
+ {
+ int result;
+
+ if (timeout != null)
+ result = lock_.acquire (timeout);
+ else
+ result = lock_.acquire ();
+
+ return result;
+ }
+
+ /**
+ * Dispatch to the appropriate acquire method. In C++ ACE, when
+ * the type is LockTypes.RWLOCK and the proxy type is
+ * LockTypes.WRITE_LOCK_PROXY, then this calls acquireWrite.
+ * If it's RWLOCK and the proxy is READ_LOCK_PROXY, it calls
+ * acquireRead. In the normal case, it just calls acquire.
+ *
+ *@return reply to be sent back to the client (values for errno
+ * include constants in TokenReply such as EFAULT, ETIME,
+ * EINTR, or NO_ERRORS)
+ */
+ protected TokenReply acquireDispatcher (TokenRequest request)
+ {
+ int result;
+ TimeValue timeout = getTimeout (request);
+
+ try {
+
+ /*
+ ACE specifies that when requesting a reader lock, the
+ token type will be RWLOCK and the proxy type is 0.
+ When it's a writer lock, the proxy type is 1.
+ */
+ if (request.tokenType () == LockTypes.RWLOCK) {
+ if (request.proxyType () == LockTypes.READ_LOCK_PROXY)
+ result = acquireRead (request, timeout);
+ else
+ result = acquireWrite (request, timeout);
+ } else
+ result = acquire (request, timeout);
+
+ } catch (LockException e) {
+ return new TokenReply (TokenReply.EFAULT,
+ request.arg ());
+ } catch (TimeoutException e) {
+ return new TokenReply (TokenReply.ETIME,
+ request.arg ());
+ } catch (InterruptedException e) {
+ return new TokenReply (TokenReply.EINTR,
+ request.arg ());
+ }
+
+ if (result == AbstractLock.FAILURE) {
+ return new TokenReply (TokenReply.EFAULT,
+ request.arg ());
+ } else {
+ return new TokenReply (TokenReply.NO_ERRORS,
+ request.arg ());
+ }
+ }
+
+ /**
+ * Process a release request and construct a reply. The values
+ * for errno include TokenReply constants EFAULT, EACCES, or
+ * NO_ERRORS.
+ */
+ protected TokenReply release (TokenRequest request)
+ {
+ int result;
+
+ try {
+ result = lock_.release ();
+ } catch (LockException e) {
+ return new TokenReply (TokenReply.EFAULT,
+ request.arg ());
+ }
+
+ if (result == AbstractLock.FAILURE) {
+ return new TokenReply (TokenReply.EACCES,
+ request.arg ());
+ } else {
+ return new TokenReply (TokenReply.NO_ERRORS,
+ request.arg ());
+ }
+ }
+
+ /**
+ * Process a renew request and construct a reply. The values for
+ * errno include TokenReply constants EFAULT, ETIME, EINTR, EACCES,
+ * or NO_ERRORS.
+ */
+ protected TokenReply renew (TokenRequest request)
+ {
+ int result = AbstractLock.FAILURE;
+ TimeValue timeout = getTimeout (request);
+
+ try {
+
+ if (timeout != null) {
+ result = lock_.renew (request.requeuePosition (),
+ timeout);
+ } else {
+ result = lock_.renew (request.requeuePosition ());
+ }
+
+ } catch (LockException e) {
+ return new TokenReply (TokenReply.EFAULT,
+ request.arg ());
+ } catch (TimeoutException e) {
+ return new TokenReply (TokenReply.ETIME,
+ request.arg ());
+ } catch (InterruptedException e) {
+ return new TokenReply (TokenReply.EINTR,
+ request.arg ());
+ }
+
+ if (result == AbstractLock.FAILURE) {
+ return new TokenReply (TokenReply.EACCES,
+ request.arg ());
+ } else {
+ return new TokenReply (TokenReply.NO_ERRORS,
+ request.arg ());
+ }
+ }
+
+ /**
+ * Process a remove request and construct a reply. This currently
+ * is not supported in the normal AbstractLock interface, so the
+ * default implementation returns a reply with errno set to
+ * TokenReply.ENOTSUP.
+ */
+ protected TokenReply remove (TokenRequest request)
+ {
+ ACE.ERROR ("Remove is unimplemented");
+ return new TokenReply (TokenReply.ENOTSUP,
+ request.arg ());
+ }
+
+ /**
+ * Call tryAcquireWrite on the lock, returning the result.
+ */
+ protected int tryAcquireWrite (TokenRequest request)
+ throws LockException
+ {
+ return lock_.tryAcquireWrite ();
+ }
+
+ /**
+ * Call tryAcquireRead on the lock, returning the result.
+ */
+ protected int tryAcquireRead (TokenRequest request)
+ throws LockException
+ {
+ return lock_.tryAcquireRead ();
+ }
+
+ /**
+ * Call tryAcquire on the lock, returning the result.
+ */
+ protected int tryAcquire (TokenRequest request) throws LockException
+ {
+ return lock_.tryAcquire ();
+ }
+
+ /**
+ * Dispatch to the appropriate tryAcquire method. In C++ ACE, when
+ * the type is LockTypes.RWLOCK and the proxy type is
+ * LockTypes.WRITE_LOCK_PROXY, then this calls acquireWrite.
+ * If it's RWLOCK and the proxy is READ_LOCK_PROXY, it calls
+ * acquireRead. In the normal case, it just calls acquire.
+ *
+ *@return reply to be sent back to the client (values for errno
+ * include constants in TokenReply such as EFAULT,
+ * EWOULDBLOCK, or NO_ERRORS).
+ */
+ protected TokenReply tryAcquireDispatcher (TokenRequest request)
+ {
+ int result;
+
+ try {
+
+ /*
+ ACE specifies that when requesting a reader lock, the
+ token type will be RWLOCK and the proxy type is 0.
+ When it's a writer lock, the proxy type is 1.
+ */
+ if (request.tokenType () == LockTypes.RWLOCK) {
+ if (request.proxyType () == LockTypes.READ_LOCK_PROXY)
+ result = tryAcquireRead (request);
+ else
+ result = tryAcquireWrite (request);
+ } else
+ result = tryAcquire (request);
+
+ } catch (LockException e) {
+ return new TokenReply (TokenReply.EFAULT,
+ request.arg ());
+ }
+
+ if (result == AbstractLock.FAILURE) {
+ return new TokenReply (TokenReply.EWOULDBLOCK,
+ request.arg ());
+ } else {
+ return new TokenReply (TokenReply.NO_ERRORS,
+ request.arg ());
+ }
+ }
+
+ /**
+ * Abandon any claim the specified client has on the lock.
+ *
+ *@param clientID identification of the client
+ */
+ public void abandonLock (String clientID)
+ {
+ ACE.DEBUG (clientID + " abandoning lock");
+ try {
+ int nesting_level = 0;
+ while (lock_.release () != AbstractLock.FAILURE)
+ {
+ nesting_level++;
+ // Loop until not the owner in case the lock
+ // supports nested acquires
+ }
+ if (nesting_level == 0)
+ ACE.DEBUG (clientID + " was not the owner");
+ else
+ ACE.DEBUG (clientID + " had " + nesting_level + " locks");
+ } catch (LockException e) {
+ ACE.ERROR ("While abandoning lock: " + e.getMessage ());
+ // Don't need to send a reply to the client
+ }
+ }
+
+ protected AbstractLock lock_;
+}
diff --git a/java/JACE/netsvcs/Token/LockOperations.java b/java/JACE/netsvcs/Token/LockOperations.java
new file mode 100644
index 00000000000..f5dfa5bc486
--- /dev/null
+++ b/java/JACE/netsvcs/Token/LockOperations.java
@@ -0,0 +1,16 @@
+package JACE.netsvcs.Token;
+
+/**
+ * Constants defining the operation types available on a lock.
+ * For information on specifying a read/write style lock, see LockTypes.
+ *
+ *@see LockTypes
+ */
+public interface LockOperations
+{
+ int ACQUIRE = 0;
+ int RELEASE = 1;
+ int RENEW = 2;
+ int REMOVE = 3;
+ int TRY_ACQUIRE = 4;
+}
diff --git a/java/JACE/netsvcs/Token/LockTypes.java b/java/JACE/netsvcs/Token/LockTypes.java
new file mode 100644
index 00000000000..f377529367a
--- /dev/null
+++ b/java/JACE/netsvcs/Token/LockTypes.java
@@ -0,0 +1,19 @@
+package JACE.netsvcs.Token;
+
+/**
+ * Constants related to the default lock and proxy types. New types
+ * of LockHandlers and lock types can be created and registered with
+ * the token service on the command line without modifying this.
+ * <P>
+ * C++ ACE handles RWLOCK in this way:
+ * When a request comes through for a RWLOCK, the proxy type is
+ * 0 for a read lock request and 1 for a write lock request.
+ */
+public interface LockTypes
+{
+ int MUTEX = 0;
+ int RWLOCK = 1;
+
+ int READ_LOCK_PROXY = 0;
+ int WRITE_LOCK_PROXY = 1;
+}
diff --git a/java/JACE/netsvcs/Token/MutexHandler.java b/java/JACE/netsvcs/Token/MutexHandler.java
new file mode 100644
index 00000000000..82f79fe5a76
--- /dev/null
+++ b/java/JACE/netsvcs/Token/MutexHandler.java
@@ -0,0 +1,51 @@
+package JACE.netsvcs.Token;
+
+import JACE.Concurrency.*;
+
+/**
+ * LockHandler implementation for a mutex lock.
+ * <P>
+ * Currently, this uses JACE.Concurrency.Token as the actual lock since
+ * it supports nested acquires.
+ *
+ *@see LockHandler
+ */
+public class MutexHandler extends LockHandlerAdapter
+{
+ // Uses token since it supports nested acquires.
+ static class ExtendedMutex extends Token
+ {
+ // This is so that we don't make any assumptions about previous
+ // implementations of LockAdapter, and enable owner checking with
+ // the client ID from TokenRequest. The thread name is set in
+ // handleRequest.
+ protected Object accessorID ()
+ {
+ return Thread.currentThread().getName();
+ }
+ }
+
+ /**
+ * Default constructor.
+ */
+ public MutexHandler ()
+ {
+ super (new ExtendedMutex ());
+ }
+
+ public TokenReply handleRequest (TokenRequestHandler caller,
+ TokenRequest request)
+ {
+ Thread.currentThread().setName (request.clientID ());
+
+ return super.handleRequest (caller, request);
+ }
+
+ public void abandonLock (String clientID)
+ {
+ Thread.currentThread().setName (clientID);
+
+ super.abandonLock (clientID);
+ }
+}
+
diff --git a/java/JACE/netsvcs/Token/RWMutexHandler.java b/java/JACE/netsvcs/Token/RWMutexHandler.java
new file mode 100644
index 00000000000..89dc679dd8d
--- /dev/null
+++ b/java/JACE/netsvcs/Token/RWMutexHandler.java
@@ -0,0 +1,54 @@
+package JACE.netsvcs.Token;
+
+import JACE.Concurrency.*;
+
+/**
+ * LockHandler implementation for a reader/writer mutex lock.
+ * <P>
+ * Since it uses RWMutex as the actual lock, it doesn't support
+ * nested acquires.
+ *
+ *@see LockHandler
+ */
+public class RWMutexHandler extends LockHandlerAdapter
+{
+ static class ExtendedRWMutex extends RWMutex
+ {
+ // This is so that we don't make any assumptions about previous
+ // implementations of LockAdapter, and enable owner checking with
+ // the client ID from TokenRequest. The thread name is set in
+ // handleRequest.
+ protected Object accessorID ()
+ {
+ return Thread.currentThread().getName();
+ }
+ }
+
+ /**
+ * Default constructor.
+ */
+ public RWMutexHandler ()
+ {
+ super (new ExtendedRWMutex ());
+ }
+
+ public TokenReply handleRequest (TokenRequestHandler caller,
+ TokenRequest request)
+ {
+ // Set the name of this thread to the client ID to perform
+ // proper owner checking.
+ Thread.currentThread().setName (request.clientID ());
+
+ // process the request
+ return super.handleRequest (caller, request);
+ }
+
+ public void abandonLock (String clientID)
+ {
+ // Set the name of this thread to the client ID to perform
+ // proper owner checking.
+ Thread.currentThread().setName (clientID);
+
+ super.abandonLock (clientID);
+ }
+}
diff --git a/java/JACE/netsvcs/Token/RemoteLock.java b/java/JACE/netsvcs/Token/RemoteLock.java
new file mode 100644
index 00000000000..824e05a31f0
--- /dev/null
+++ b/java/JACE/netsvcs/Token/RemoteLock.java
@@ -0,0 +1,543 @@
+package JACE.netsvcs.Token;
+
+import java.io.*;
+import JACE.Concurrency.*;
+import JACE.ASX.*;
+import JACE.Connection.*;
+import JACE.OS.*;
+
+/**
+ * Proxy used by clients to connect to the token service. This
+ * implements the AbstractLock interface, so can be used like any
+ * other synchronization mechanism. The user can either use this
+ * class directly, or use a proxy which already inputs its type.
+ * <P>
+ * Currently, a separate instance (and thus a separate socket connection)
+ * must be used for each thread which accesses the service. The token
+ * service itself could handle multiple client IDs and token names per
+ * connection with the following requirement -- since the service blocks
+ * in its operations, a shadow mutex would have to be used in the proxy.
+ * <P>
+ * It would be best if the user called the close () method after finishing
+ * up with a RemoteLock, but that is not absolutely necessary. The socket
+ * will be closed when the JVM exits or finalize is called. (That will also
+ * free the actual token in the token service in case release was never
+ * called.)
+ * <P>
+ * The SLEEPHOOK result is never returned, only SUCCESS or FAILURE. (C++
+ * version doesn't seem to indicate the sleep hook result.)
+ *
+ *@see MutexHandler
+ *@see RWMutexHandler
+ *@see JACE.Concurrency.AbstractLock
+ *
+ *@author Everett Anderson
+ */
+public class RemoteLock extends SvcHandler implements AbstractLock
+{
+ /**
+ * Accessor for the token name.
+ *
+ *@return name of the token
+ */
+ public String tokenName ()
+ {
+ return request_.tokenName ();
+ }
+
+ /**
+ * Set the name of the token.
+ */
+ public void tokenName (String name)
+ {
+ request_.tokenName (name);
+ }
+
+ /**
+ * Accessor for the client ID.
+ */
+ public String clientID ()
+ {
+ return request_.clientID ();
+ }
+
+ /**
+ * Set the client ID.
+ */
+ public void clientID (String clientID)
+ {
+ request_.clientID (clientID);
+ }
+
+ /**
+ * Constructor.
+ *
+ *@see LockTypes
+ *@param tokenType type of token to create in the token service
+ *@param proxyType type of proxy to define this RemoteLock as
+ *@param tokenName name of the token to connect to in the token service
+ *@param clientID clientID to use to refer to this client
+ *@param host host name of the token service
+ *@param port port to connect to for the token service
+ */
+ public RemoteLock (int tokenType,
+ int proxyType,
+ String tokenName,
+ String clientID,
+ String host,
+ int port)
+ {
+ host_ = host;
+ port_ = port;
+
+ // Only allocates one reply and one request
+ reply_ = new TokenReply ();
+
+ request_ = new TokenRequest (tokenType,
+ proxyType,
+ 0,
+ tokenName,
+ clientID);
+ }
+
+ /**
+ * Reconnect this proxy to the token service.
+ *
+ *@exception LockException problem occured in reconnecting
+ */
+ protected void reconnect () throws LockException
+ {
+ Connector c = new Connector ();
+ c.open (host_, port_);
+
+ try {
+ c.connect (this);
+ } catch (InstantiationException e) {
+ throw new LockException (e.getMessage());
+ } catch (IllegalAccessException e) {
+ throw new LockException (e.getMessage());
+ } catch (IOException e) {
+ throw new LockException (e.getMessage());
+ }
+ }
+
+ /**
+ * Check to see if this RemoteLock is connected.
+ */
+ public boolean connected ()
+ {
+ return connected_;
+ }
+
+ /**
+ * Initialize this RemoteLock. Called by Connector.
+ */
+ public int open (Object obj)
+ {
+ connected_ = true;
+ return 0;
+ }
+
+ /**
+ * Shut down the connection to the server. Current implementation
+ * calls close ().
+ */
+ public int close (long flags)
+ {
+ return close ();
+ }
+
+ /**
+ * Shut down the connection to the server and mark this lock
+ * as disconnected.
+ */
+ public int close ()
+ {
+ if (connected ()) {
+ try {
+ connected_ = false;
+ peer ().close ();
+ } catch (IOException e) {
+ return -1;
+ }
+ }
+
+ return 0;
+ }
+
+ /**
+ * Send the given request to the token service, throwing a
+ * LockException on error.
+ */
+ protected void sendRequest (TokenRequest request) throws LockException
+ {
+ try {
+ if (!connected ())
+ reconnect ();
+
+ request.streamOutTo (peer ().dataOutputStream ());
+
+ } catch (IOException e) {
+ close ();
+ throw new LockException (e.getMessage ());
+ }
+ }
+
+ /**
+ * Receive a reply from the token service, throwing a LockException
+ * on error.
+ */
+ protected void receiveReply (TokenReply reply) throws LockException
+ {
+ if (!connected ())
+ throw new LockException ("Proxy wasn't connected, any replies lost");
+
+ try {
+
+ reply.streamInFrom (peer ().dataInputStream ());
+
+ } catch (IOException e) {
+ close ();
+ throw new LockException (e.getMessage ());
+ }
+ }
+
+ /**
+ * For errors that shouldn't generate exceptions, return the
+ * appropriate result code as defined in AbstractLock.
+ *
+ *@return AbstractLock.SUCCESS or AbstractLock.FAILURE
+ */
+ protected int processErrno (TokenReply reply)
+ {
+ switch (reply.errno ())
+ {
+ case TokenReply.NO_ERRORS:
+ return AbstractLock.SUCCESS;
+ case TokenReply.EIO:
+ close ();
+ return AbstractLock.FAILURE;
+ default:
+ return AbstractLock.FAILURE;
+ }
+ }
+
+ /**
+ * Make a request to the token service with the given operation
+ * type and arguments.
+ *
+ *@see LockOperations
+ *@see LockTypes
+ *@param operationType type of operation to perform
+ *@param proxyType type of proxy this is
+ *@param requeuePosition put this owner at this position in the
+ * waiting queue (only makes sense if the
+ * operation is renew)
+ *@return AbstractLock.SUCCESS or AbstractLock.FAILURE
+ *@exception LockException remote access error occured
+ */
+ protected int makeRequest (int operationType,
+ int proxyType,
+ int requeuePosition)
+ throws LockException
+ {
+ request_.operationType (operationType);
+ request_.proxyType (proxyType);
+ request_.requeuePosition (requeuePosition);
+ request_.useTimeout (false);
+
+ sendRequest (request_);
+ receiveReply (reply_);
+
+ // make sure that if someone does send a magic cookie arg back,
+ // to keep it going
+ request_.arg (reply_.arg ());
+
+ return processErrno (reply_);
+ }
+
+ /**
+ * Make a request to the token service with the given arguments
+ * that must be performed by the given absolute time timeout.
+ * Currently, the timeout is managed by the remote service.
+ *
+ *@see LockOperations
+ *@see LockTypes
+ *@param operationType type of operation to perform
+ *@param proxyType type of proxy this is
+ *@param requeuePosition put this owner at this position in the
+ * waiting queue (only makes sense if the
+ * operation is renew)
+ *@param timeout absolute time timeout to accomplish the operation by
+ *@return AbstractLock.SUCCESS or AbstractLock.FAILURE
+ *@exception LockException remote access error occured
+ */
+ protected int makeRequest (int operationType,
+ int proxyType,
+ int requeuePosition,
+ TimeValue timeout)
+ throws LockException, TimeoutException
+ {
+ request_.operationType (operationType);
+ request_.proxyType (proxyType);
+ request_.requeuePosition (requeuePosition);
+ request_.useTimeout (timeout);
+
+ sendRequest (request_);
+ receiveReply (reply_);
+
+ request_.arg (reply_.arg ());
+
+ if (reply_.errno () == TokenReply.ETIME)
+ throw new TimeoutException (timeout, "Remote Lock");
+
+ return processErrno (reply_);
+ }
+
+ /**
+ * Acquire ownership of the lock, blocking indefinitely if necessary.
+ * <P>
+ *@return AbstractLock.FAILURE or AbstractLock.SUCCESS
+ *@exception LockException a remote error occured
+ */
+ public int acquire () throws LockException
+ {
+ return makeRequest (LockOperations.ACQUIRE, 0, 0);
+ }
+
+ /**
+ * Acquire ownership of the lock by the given absolute time time-out.
+ * A value of null for the timeout parameter results in a blocking
+ * acquire.
+ * A value of TimeValue.zero throws a TimeoutException if the
+ * acquire would block.
+ * <P>
+ *@param timeout absolute time by which the lock must be acquired
+ *@return appropriate Lock return value (AbstractLock.FAILURE,
+ * AbstractLock.SUCCESS or AbstractLock.SLEEPHOOK)
+ *@exception LockException a remote error occured
+ *@exception JACE.ASX.TimeoutException thrown when the lock is not
+ * obtained by the desired time
+ *@see #tryAcquire
+ */
+ public int acquire (TimeValue timeout)
+ throws LockException, TimeoutException
+ {
+ return makeRequest (LockOperations.ACQUIRE, 0, 0, timeout);
+ }
+
+ /**
+ * Acquire a read lock, blocking indefinitely if necessary.
+ *
+ *@return AbstractLock.FAILURE or AbstractLock.SUCCESS
+ *@exception LockException a remote error occured
+ */
+ public int acquireRead () throws LockException
+ {
+ return makeRequest (LockOperations.ACQUIRE,
+ LockTypes.READ_LOCK_PROXY,
+ 0);
+ }
+
+ /**
+ * Acquire a read lock by the given absolute time time-out.
+ *
+ *@param timeout absolute time by which the lock must be acquired
+ *@return appropriate lock return value (AbstractLock.FAILURE,
+ * AbstractLock.SUCCESS or AbstractLock.SLEEPHOOK)
+ *@exception LockException a remote error occured
+ *@exception JACE.ASX.TimeoutException thrown when the lock is not
+ * obtained by the desired time
+ *@see #tryAcquireRead
+ */
+ public int acquireRead (TimeValue timeout)
+ throws LockException, TimeoutException
+ {
+ return makeRequest (LockOperations.ACQUIRE,
+ LockTypes.READ_LOCK_PROXY,
+ 0,
+ timeout);
+ }
+
+ /**
+ * Acquire a write lock, blocking indefinitely if necessary.
+ *
+ *@return AbstractLock.FAILURE or AbstractLock.SUCCESS
+ *@exception LockException a remote error occured
+ */
+ public int acquireWrite ()
+ throws LockException
+ {
+ return makeRequest (LockOperations.ACQUIRE,
+ LockTypes.WRITE_LOCK_PROXY,
+ 0);
+ }
+
+ /**
+ * Acquire a write lock by the given absolute time time-out.
+ *
+ *@param timeout absolute time by which the lock must be acquired
+ *@return appropriate lock return value (AbstractLock.FAILURE,
+ * AbstractLock.SUCCESS or AbstractLock.SLEEPHOOK)
+ *@exception LockException a remote error occured
+ *@exception JACE.ASX.TimeoutException thrown when the lock is not
+ * obtained by the desired time
+ *@see #tryAcquireWrite
+ */
+ public int acquireWrite (TimeValue timeout)
+ throws LockException, TimeoutException
+ {
+ return makeRequest (LockOperations.ACQUIRE,
+ LockTypes.WRITE_LOCK_PROXY,
+ 0,
+ timeout);
+ }
+
+
+ /**
+ * Give up the lock to some number of waiting threads (if any), then
+ * reacquire, blocking indefinitely if necessary.
+ * <P>
+ * An optimized method that efficiently reacquires the token if no
+ * other threads are waiting. This is useful for situations where
+ * you don't want to degrade the quality of service if there are
+ * other threads waiting to get the token.
+ * <P>
+ *@param requeuePosition position in the waiters queue to insert
+ * this thread. If this value is -1 and there are other
+ * threads waiting to obtain the token, this thread is queued
+ * at the end. If this value is greater than -1, then it
+ * indicates how many entries to skip over before inserting
+ * our thread into the queue. (For example, if it is 0,
+ * this thread is put at the front of the queue.) If this
+ * value is greater than the number of waiters, this thread is
+ * simply put at the end of the current waiters queue.
+ *@return AbstractLock.FAILURE or AbstractLock.SUCCESS
+ *@exception LockException a remote error occured
+ */
+ public int renew (int requeuePosition)
+ throws LockException
+ {
+ return makeRequest (LockOperations.RENEW,
+ 0,
+ requeuePosition);
+ }
+
+ /**
+ * Give up the lock to some waiting threads (if any), then reacquire
+ * by the given absolute time time-out.
+ * <P>
+ * An optimized method that efficiently reacquires the token if no
+ * other threads are waiting. This is useful for situations where
+ * you don't want to degrade the quality of service if there are
+ * other threads waiting to get the token.
+ * <P>
+ * A value of null for the timeout indicates a blocking renew.
+ * <P>
+ *@param requeuePosition position in the waiters queue to insert
+ * this thread. If this value is -1 and there are other
+ * threads waiting to obtain the token, this thread is queued
+ * at the end. If this value is greater than -1, then it
+ * indicates how many entries to skip over before inserting
+ * our thread into the queue. (For example, if it is 0,
+ * this thread is put at the front of the queue.) If this
+ * value is greater than the number of waiters, this thread is
+ * simply put at the end of the current waiters queue.
+ *
+ *@param timeout absolute time by which the lock must be reacquired
+ *
+ *@return appropriate AbstractLock return value
+ * (AbstractLock.FAILURE or AbstractLock.SUCCESS)
+ *@exception LockException a remote error occured
+ *@exception JACE.ASX.TimeoutException thrown when the lock is not
+ * obtained by the desired time
+ */
+ public int renew (int requeuePosition, TimeValue timeout)
+ throws LockException, TimeoutException
+ {
+ return makeRequest (LockOperations.RENEW,
+ 0,
+ requeuePosition,
+ timeout);
+ }
+
+ /**
+ * Try to acquire the lock without blocking.
+ * <P>
+ *@return appropriate AbstractLock return value
+ * (AbstractLock.FAILURE or AbstractLock.SUCCESS)
+ *@exception LockException a remote error occured
+ */
+ public int tryAcquire () throws LockException
+ {
+ return makeRequest (LockOperations.TRY_ACQUIRE, 0, 0);
+ }
+
+ /**
+ * Try to acquire a read lock without blocking.
+ * <P>
+ *@return appropriate AbstractLock return value
+ * (AbstractLock.FAILURE or AbstractLock.SUCCESS)
+ *@exception LockException a remote error occured
+ */
+ public int tryAcquireRead () throws LockException
+ {
+ return makeRequest (LockOperations.TRY_ACQUIRE,
+ LockTypes.READ_LOCK_PROXY,
+ 0);
+ }
+
+ /**
+ * Try to acquire a write lock without blocking.
+ *
+ *@return appropriate AbstractLock return value
+ * (AbstractLock.FAILURE or AbstractLock.SUCCESS)
+ *@exception LockException a remote error occured
+ */
+ public int tryAcquireWrite () throws LockException
+ {
+ return makeRequest (LockOperations.TRY_ACQUIRE,
+ LockTypes.WRITE_LOCK_PROXY,
+ 0);
+ }
+
+ /**
+ * Release ownership of this lock.
+ *
+ *@return appropriate AbstractLock return value
+ * (AbstractLock.FAILURE or AbstractLock.SUCCESS)
+ *@exception LockException a remote error occured
+ */
+ public int release () throws LockException
+ {
+ return makeRequest (LockOperations.RELEASE, 0, 0);
+ }
+
+ /**
+ * Closes the connection to the server (if it is still open).
+ */
+ protected void finalize () throws Throwable
+ {
+ close ();
+ }
+
+ /**
+ * No-op implementation for the sleep hook (unused).
+ */
+ public void sleepHook () {}
+
+ /** Status of whether this RemoteLock is connected to the server or not */
+ protected boolean connected_ = false;
+
+ /** Request object for transmissions to the server */
+ protected TokenRequest request_;
+
+ /** Reply object for receiving transmissions from the server */
+ protected TokenReply reply_;
+
+ /** Host name of the token service */
+ protected String host_;
+
+ /** Port number of the token service */
+ protected int port_;
+}
diff --git a/java/JACE/netsvcs/Token/RemoteMutex.java b/java/JACE/netsvcs/Token/RemoteMutex.java
new file mode 100644
index 00000000000..7f2a4311116
--- /dev/null
+++ b/java/JACE/netsvcs/Token/RemoteMutex.java
@@ -0,0 +1,28 @@
+package JACE.netsvcs.Token;
+
+/**
+ * Proxy used by clients for accessing a mutex at the token service.
+ */
+public class RemoteMutex extends RemoteLock
+{
+ /**
+ * Constructor.
+ *
+ *@param tokenName name of the mutex to access
+ *@param clientID identification of this client
+ *@param host host of the token service
+ *@param port port number of the token service
+ */
+ public RemoteMutex (String tokenName,
+ String clientID,
+ String host,
+ int port)
+ {
+ super (LockTypes.MUTEX,
+ 0,
+ tokenName,
+ clientID,
+ host,
+ port);
+ }
+}
diff --git a/java/JACE/netsvcs/Token/RemoteRWMutex.java b/java/JACE/netsvcs/Token/RemoteRWMutex.java
new file mode 100644
index 00000000000..cc666bfd70f
--- /dev/null
+++ b/java/JACE/netsvcs/Token/RemoteRWMutex.java
@@ -0,0 +1,29 @@
+package JACE.netsvcs.Token;
+
+/**
+ * Proxy used by clients for accessing a reader/writer mutex
+ * at the token service.
+ */
+public class RemoteRWMutex extends RemoteLock
+{
+ /**
+ * Constructor.
+ *
+ *@param tokenName name of the reader/writer lock to access
+ *@param clientID identification of this client
+ *@param host host of the token service
+ *@param port port number of the token service
+ */
+ public RemoteRWMutex (String tokenName,
+ String clientID,
+ String host,
+ int port)
+ {
+ super (LockTypes.RWLOCK,
+ 0,
+ tokenName,
+ clientID,
+ host,
+ port);
+ }
+}
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_;
+}
+
diff --git a/java/JACE/netsvcs/Token/TokenReply.java b/java/JACE/netsvcs/Token/TokenReply.java
new file mode 100644
index 00000000000..35f50901610
--- /dev/null
+++ b/java/JACE/netsvcs/Token/TokenReply.java
@@ -0,0 +1,171 @@
+package JACE.netsvcs.Token;
+
+import java.io.*;
+import JACE.ASX.*;
+import JACE.OS.*;
+
+/**
+ * Reply from a lock operation, and constants involved in it.
+ * This is compatible with the C++ ACE version. The user probably
+ * never deals directly with the constant errno values in Java ACE since
+ * the proxy (RemoteLock) should hide those details.
+ */
+public class TokenReply
+{
+
+ /** indicates success */
+ public static final int NO_ERRORS = 0;
+
+ /** indicates a timeout */
+ public static final int ETIME = 62;
+
+ /** indicates the operation was interrupted */
+ public static final int EINTR = 4;
+
+ /** deadlock indication errno (JACE currently doesn't implement a
+ * deadlock detection system, but C++ ACE does and JACE proxies
+ * act appropriately).
+ */
+ public static final int EDEADLK = 45;
+
+ /** indicates the operation would block, used in tryAcquire */
+ public static final int EWOULDBLOCK = 11;
+
+ /** indicates a token name or client ID was too long */
+ public static final int ENAMETOOLONG = 78;
+
+ /** indicates an operation type was not supported */
+ public static final int ENOTSUP = 48;
+
+ /** indicates that this client was not the owner of the lock,
+ * so couldn't perform the desired operation */
+ public static final int EACCES = 13;
+
+ /** indicates an IO error occured during transmission of the request */
+ public static final int EIO = 5;
+
+ /** indicates a generic failure to complete the request */
+ public static final int EFAULT = 14;
+
+ /** indicates an operation was requested on an unknown type of token */
+ public static final int EINVAL = 22;
+
+ /** constant length of a valid token reply */
+ private final static int LENGTH = 12;
+
+ /** error code */
+ private int errno_;
+
+ /** argument (unused in JACE) */
+ private int arg_;
+
+ /** Dump the state of this TokenReply to a String */
+ public String toString ()
+ {
+ return "TokenReply(" + this.length() + ", " + this.errno_
+ + ", " + this.arg_ + ")";
+ }
+
+ /** Default constructor (NO_ERRORS) */
+ public TokenReply ()
+ {
+ errno_ = NO_ERRORS;
+ arg_ = 0;
+ }
+
+ /** Constructor which takes the error code and argument */
+ public TokenReply (int errno, int arg)
+ {
+ errno_ = errno;
+ arg_ = arg;
+ }
+
+ /**
+ * Accessor for the length of this TokenReply.
+ */
+ public int length ()
+ {
+ return LENGTH;
+ }
+
+ /** Accessor for the error code of this TokenReply. */
+ public int errno ()
+ {
+ return errno_;
+ }
+
+ /**
+ * Set the error code of this TokenReply.
+ */
+ public void errno (int value)
+ {
+ errno_ = value;
+ }
+
+ /**
+ * Accessor of the argument of this TokenReply. (Unused in JACE)
+ */
+ public int arg ()
+ {
+ return arg_;
+ }
+
+ /**
+ * Set the argument of this TokenReply. (Unused in JACE)
+ */
+ public void arg (int value)
+ {
+ arg_ = value;
+ }
+
+ /**
+ * Read this TokenReply in from the given InputStream.
+ */
+ public void streamInFrom (InputStream is)
+ throws IOException, EOFException
+ {
+ BufferedInputStream bis = new BufferedInputStream (is, LENGTH);
+ DataInputStream dis = new DataInputStream (bis);
+
+ streamInFrom (dis);
+ }
+
+ /**
+ * Read this TokenReply in from the given DataInputStream.
+ */
+ public void streamInFrom (DataInputStream dis)
+ throws IOException, EOFException
+ {
+ int length = dis.readInt ();
+ if (length != LENGTH)
+ throw new IOException ("Invalid TokenReply length " + length);
+
+ this.errno_ = dis.readInt ();
+ this.arg_ = dis.readInt ();
+ }
+
+ /**
+ * Write this TokenReply out to the given OutputStream.
+ */
+ public void streamOutTo (OutputStream os)
+ throws IOException
+ {
+ BufferedOutputStream bos = new BufferedOutputStream (os, LENGTH);
+ DataOutputStream dos = new DataOutputStream (bos);
+
+ streamOutTo (dos);
+ }
+
+ /**
+ * Write this TokenReply out to the given DataOutputStream.
+ */
+ public void streamOutTo (DataOutputStream dos)
+ throws IOException
+ {
+ dos.writeInt (LENGTH);
+ dos.writeInt (this.errno_);
+ dos.writeInt (this.arg_);
+
+ dos.flush ();
+ }
+}
diff --git a/java/JACE/netsvcs/Token/TokenRequest.java b/java/JACE/netsvcs/Token/TokenRequest.java
new file mode 100644
index 00000000000..eb1113428c5
--- /dev/null
+++ b/java/JACE/netsvcs/Token/TokenRequest.java
@@ -0,0 +1,426 @@
+package JACE.netsvcs.Token;
+
+import java.io.*;
+import JACE.ASX.*;
+import JACE.OS.*;
+
+/**
+ * Request for an operation on a lock. This is compatible with the
+ * C++ ACE version. The US-ASCII character encoding is used for
+ * String to byte conversions (and vice versa). If
+ * that encoding isn't supported, it attempts to use the default
+ * encoding (but prints a message). Users probably never need to
+ * deal with this class directly. The notify field isn't used
+ * in JACE (or in C++ ACE as far as I can tell).
+ *
+ *@author Everett Anderson
+ */
+public class TokenRequest
+{
+ /** Maximum length for a token name */
+ public final static int MAX_TOKEN_NAME_LEN = 40;
+
+ /** Maximum length for a client ID */
+ public final static int MAX_CLIENT_ID_LEN = 276;
+
+ /** Length of the fixed size header */
+ protected final static int HEADER_LEN = 40;
+
+ /** Maximum length of any TokenRequest (total) */
+ protected final static int MAX_LEN = 359;
+
+ /**
+ * Dump this TokenRequest's state out to a String.
+ */
+ public String toString()
+ {
+ return "TokenRequest(" + this.length() + ", " +
+ this.tokenType_ + ", " +
+ this.proxyType_ + ", " +
+ this.operationType_ + ", " +
+ this.requeuePosition_ + ", " +
+ this.notify_ + ", " +
+ this.useTimeout_ + ", " +
+ this.sec_ + ", " +
+ this.usec_ + ", " +
+ this.arg_ + ", " +
+ this.tokenName_ + ", " +
+ this.clientID_ + ")";
+ }
+
+ /** Default constructor. */
+ public TokenRequest()
+ {
+ // Remember that the length is transmitted first
+ tokenType_ = 0;
+ proxyType_ = 0;
+ operationType_ = 0;
+ requeuePosition_ = 0;
+ notify_ = 0;
+ useTimeout_ = 0;
+ sec_ = 0;
+ usec_ = 0;
+ arg_ = 0;
+ tokenName_ = "";
+ clientID_ = "";
+ // Transmission is "<10 ints><token name>(null):<clientID>(null)"
+
+ charEncoding_ = "US-ASCII";
+
+ buffer_ = new byte[MAX_LEN];
+ }
+
+ /**
+ * Create a request which doesn't use timeouts.
+ *
+ *@param tokenType type of token (usually a constant in LockTypes)
+ *@param proxyType type of proxy (usually a constant in LockTypes)
+ *@param operationType type of operation (usually a constant in
+ * LockOperations)
+ *@param tokenName name of the token to operate on
+ *@param clientID name of the client requesting an operation
+ *
+ *@see LockTypes
+ *@see LockOperations
+ */
+ public TokenRequest(int tokenType,
+ int proxyType,
+ int operationType,
+ String tokenName,
+ String clientID)
+ {
+ this();
+
+ this.tokenType_ = tokenType;
+ this.proxyType_ = proxyType;
+ this.operationType_ = operationType;
+ this.tokenName_ = tokenName;
+ this.clientID_ = clientID;
+ }
+
+ /**
+ * Create a request which uses the given absolute time timeout.
+ *
+ *@param tokenType type of token (usually a constant in LockTypes)
+ *@param proxyType type of proxy (usually a constant in LockTypes)
+ *@param operationType type of operation (usually a constant in
+ * LockOperations)
+ *@param tokenName name of the token to operate on
+ *@param clientID name of the client requesting an operation
+ *@param tv absolute time timeout to process the request by
+ *
+ *@see LockTypes
+ *@see LockOperations
+ */
+ public TokenRequest(int tokenType,
+ int proxyType,
+ int operationType,
+ String tokenName,
+ String clientID,
+ TimeValue tv)
+ {
+ this(tokenType,
+ proxyType,
+ operationType,
+ tokenName,
+ clientID);
+
+ this.useTimeout_ = 1;
+ this.sec_ = (int)tv.sec();
+ this.usec_ = tv.nanos() / 1000;
+ }
+
+ /**
+ * Return the length of this TokenRequest.
+ * <P>
+ * Details:
+ * <PRE>
+ * Fixed size header of length HEADER_LEN
+ * token name
+ * null
+ * :
+ * client ID
+ * null
+ * </PRE>
+ */
+ public int length()
+ {
+ return (HEADER_LEN +
+ this.tokenName_.length() +
+ this.clientID_.length() + 3);
+ }
+
+ /** Accessor for the token type. */
+ public int tokenType()
+ {
+ return this.tokenType_;
+ }
+ /** Set the token type. */
+ public void tokenType(int type)
+ {
+ this.tokenType_ = type;
+ }
+ /** Accessor for the proxy type. */
+ public int proxyType()
+ {
+ return this.proxyType_;
+ }
+ /** Set the proxy type. */
+ public void proxyType(int type)
+ {
+ this.proxyType_ = type;
+ }
+ /** Accessor for the operation type. */
+ public int operationType()
+ {
+ return this.operationType_;
+ }
+ /** Set the operation type. */
+ public void operationType(int type)
+ {
+ this.operationType_ = type;
+ }
+ /** Accessor for the requeue position. This only makes
+ * sense for a renew operation. */
+ public int requeuePosition()
+ {
+ return this.requeuePosition_;
+ }
+ /** Set the requeue position. This only makes sense for
+ * a renew operation. */
+ public void requeuePosition(int position)
+ {
+ this.requeuePosition_ = position;
+ }
+ /** Accessor for the flag to determine if a timeout should be used. */
+ public boolean useTimeout()
+ {
+ return (this.useTimeout_ == 1 ? true : false);
+ }
+ /** Set the flag to enable or disable use of timeouts.
+ */
+ public void useTimeout(boolean useIt)
+ {
+ this.useTimeout_ = (useIt == true ? 1 : 0);
+ }
+
+ /**
+ * Set the absolute time timeout to the given TimeValue's value, and
+ * enable the useTimeout flag.
+ */
+ public void useTimeout (TimeValue timeout)
+ {
+ this.useTimeout_ = 1;
+ this.sec_ = (int)timeout.sec ();
+ this.usec_ = timeout.nanos () / 1000;
+ }
+
+ /**
+ * Accessor for the seconds component of the absolute time timeout.
+ */
+ public int sec()
+ {
+ return this.sec_;
+ }
+ /** Set the seconds component of the timeout. */
+ public void sec(int sec)
+ {
+ this.sec_ = sec;
+ }
+ /** Accessor for the usec component of the timeout. */
+ public int usec()
+ {
+ return this.usec_;
+ }
+ /** Set the usec component of the timeout. */
+ public void usec(int usec)
+ {
+ this.usec_ = usec;
+ }
+ /** Accessor for the arg value. (unused in JACE) */
+ public int arg()
+ {
+ return this.arg_;
+ }
+ /** Set the arg value. (unused in JACE) */
+ public void arg(int arg)
+ {
+ this.arg_ = arg;
+ }
+
+ /** Accessor for the name of the token. */
+ public String tokenName()
+ {
+ return this.tokenName_;
+ }
+
+ /** Set the name of the token. */
+ public void tokenName(String name)
+ {
+ this.tokenName_ = name;
+ }
+
+ /** Accessor for the client identification string. */
+ public String clientID()
+ {
+ return this.clientID_;
+ }
+
+ /** Set the client identification string. */
+ public void clientID(String ID)
+ {
+ this.clientID_ = ID;
+ }
+
+ /**
+ * Construct a String from a specific subset of the byte array.
+ * The string begins at the start index and ends at either the
+ * end of the buffer or the first byte with value 0 (null).
+ * Tries to use the specified encoding, but uses the default
+ * platform encoding if it isn't available.
+ *
+ *@param start beginning index in the buffer
+ *@param bufferLength total length of the buffer
+ */
+ protected String stringFromBuffer(int start, int bufferLength)
+ {
+ int end = start;
+ String result = null;
+
+ while (end < bufferLength && this.buffer_[end] != 0)
+ end++;
+
+ try {
+ result = new String(this.buffer_,
+ start,
+ end - start,
+ this.charEncoding_);
+ } catch (UnsupportedEncodingException e) {
+ ACE.ERROR(this.charEncoding_ + " is not supported!");
+ result = new String (this.buffer_, start, end - start);
+ }
+
+ return result;
+ }
+
+ /**
+ * Read the request in from the given InputStream.
+ */
+ public void streamInFrom(InputStream is)
+ throws IOException, EOFException
+ {
+ BufferedInputStream bis = new BufferedInputStream(is,
+ MAX_LEN);
+ DataInputStream dis = new DataInputStream(bis);
+
+ streamInFrom (dis);
+ }
+
+ /**
+ * Read the request in from the given DataInputStream.
+ */
+ public void streamInFrom (DataInputStream dis)
+ throws IOException, EOFException
+ {
+ int length = dis.readInt();
+ if (length > MAX_LEN || length < HEADER_LEN + 5)
+ throw new IOException ("Invalid TokenRequest length: " + length);
+
+ this.tokenType_ = dis.readInt();
+ this.proxyType_ = dis.readInt();
+ this.operationType_ = dis.readInt();
+ this.requeuePosition_ = dis.readInt();
+ this.notify_ = dis.readInt();
+ this.useTimeout_ = dis.readInt();
+ this.sec_ = dis.readInt();
+ this.usec_ = dis.readInt();
+ this.arg_ = dis.readInt();
+
+ int total = dis.read(this.buffer_, 0, length - HEADER_LEN);
+
+ this.tokenName_ = this.stringFromBuffer(0, total);
+
+ this.clientID_ = this.stringFromBuffer(this.tokenName_.length() + 2,
+ total);
+
+ if (this.tokenName_.length() > MAX_TOKEN_NAME_LEN ||
+ this.clientID_.length() > MAX_CLIENT_ID_LEN)
+ throw new IOException("Exceeds maximum token name or client ID");
+ }
+
+ /**
+ * Write the request out to the given OutputStream.
+ */
+ public void streamOutTo (OutputStream os)
+ throws IOException
+ {
+ BufferedOutputStream bos = new BufferedOutputStream(os);
+ DataOutputStream dos = new DataOutputStream(bos);
+
+ streamOutTo (dos);
+ }
+
+ /**
+ * Write the request out to the given DataOutputStream. Tries to use
+ * the specified encoding to the convert the token name and client ID
+ * to bytes, but uses the platform default encoding if necessary.
+ */
+ public void streamOutTo (DataOutputStream dos)
+ throws IOException
+ {
+ dos.writeInt(this.length());
+ dos.writeInt(this.tokenType_);
+ dos.writeInt(this.proxyType_);
+ dos.writeInt(this.operationType_);
+ dos.writeInt(this.requeuePosition_);
+
+ dos.writeInt(this.notify_);
+ dos.writeInt(this.useTimeout_);
+ dos.writeInt(this.sec_);
+ dos.writeInt(this.usec_);
+ dos.writeInt(this.arg_);
+
+ StringBuffer data = new StringBuffer(this.tokenName_.length() +
+ this.clientID_.length() +
+ 3);
+
+ data.append(this.tokenName_);
+ data.append('\0');
+ data.append(':');
+ data.append(this.clientID_);
+ data.append('\0');
+
+ byte buf [] = null;
+ String dataString = data.toString ();
+ try {
+ buf = dataString.getBytes (this.charEncoding_);
+ } catch (UnsupportedEncodingException e) {
+ ACE.ERROR (charEncoding_ + " is unsupported, trying to use default");
+ buf = dataString.getBytes ();
+ }
+
+ dos.write(buf, 0, buf.length);
+ dos.flush();
+ }
+
+ private int tokenType_;
+ private int proxyType_;
+ private int operationType_;
+ private int requeuePosition_;
+ private int notify_;
+ private int useTimeout_;
+ private int sec_;
+ private int usec_;
+ private int arg_;
+
+ private String tokenName_;
+ private String clientID_;
+
+ private byte buffer_[];
+
+ /**
+ * Character encoding to use for converting the token name and
+ * client ID to/from bytes.
+ */
+ protected String charEncoding_;
+}
diff --git a/java/JACE/netsvcs/Token/TokenRequestHandler.java b/java/JACE/netsvcs/Token/TokenRequestHandler.java
new file mode 100644
index 00000000000..cb6d729f3bd
--- /dev/null
+++ b/java/JACE/netsvcs/Token/TokenRequestHandler.java
@@ -0,0 +1,180 @@
+package JACE.netsvcs.Token;
+
+import java.io.*;
+import java.net.SocketException;
+import java.util.*;
+import JACE.Connection.*;
+import JACE.OS.*;
+import JACE.netsvcs.Handler;
+
+/**
+ * Created by TokenAcceptor to handle token requests. Delegates to
+ * the appropriate LockHandler. This is fairly robust, and can handle
+ * multiple clients and locks (meaning requests can come in to this
+ * handle with varying client IDs and token names and still be processed
+ * and released appropriately.) Compatible with the C++ ACE token service.
+ *
+ *@author Everett Anderson
+ */
+class TokenRequestHandler extends Handler
+{
+ /**
+ * Default constructor.
+ */
+ public TokenRequestHandler() {
+ this.clients_ = new Vector (10);
+ }
+
+ /**
+ * Creates a new TokenRequest instance.
+ */
+ public Object newRequest ()
+ {
+ return new TokenRequest ();
+ }
+
+ /**
+ * Sends an error message to a client with the TokenReply.EIO
+ * errno before abandoning the connection. This is used when an IO
+ * error occured while receiving the request.
+ *
+ *@param lastRequest request object to get the arg from
+ */
+ protected void sendAbortMessage (TokenRequest lastRequest)
+ {
+ TokenReply reply = new TokenReply (TokenReply.EIO,
+ lastRequest.arg ());
+ try {
+ reply.streamOutTo (this.peer ().dataOutputStream ());
+ } catch (Exception e) {
+ // Doesn't matter if there is an error here, we've abandoned
+ // the connection.
+ }
+ }
+
+ /**
+ * Safely shuts down this handler, making sure to release any locks
+ * that were touched by clients from this TokenRequestHandler.
+ *
+ *@return -1 on failure, 0 on success
+ */
+ public synchronized int close ()
+ {
+ // For every client X that has used this handler
+ // for every LockHandler that X has used
+ // release the lock until it fails because X isn't the owner
+ // remove the client entries
+ // Call Handler.close ()
+ if (!done ()) {
+
+ TokenAcceptor parent = (TokenAcceptor) parent ();
+ Enumeration clientEnum = clients_.elements ();
+
+ while (clientEnum.hasMoreElements ()) {
+ String clientID = (String)clientEnum.nextElement ();
+
+ Enumeration handlers = parent.getClientLockHandlers (clientID);
+ if (handlers == null)
+ continue;
+
+ int num = 0;
+
+ while (handlers.hasMoreElements ()) {
+ LockHandler handler = (LockHandler)handlers.nextElement ();
+
+ handler.abandonLock (clientID);
+
+ num++;
+ }
+
+ parent.removeClient (clientID);
+ }
+
+ return super.close ();
+ }
+
+ return 0;
+ }
+
+ /**
+ * Read in the given TokenRequest and delegates to the appropriate
+ * LockHandler.
+ *
+ *@see JACE.netsvcs.Handler
+ *@param requestObject TokenRequest object to use
+ */
+ public void processRequest (Object requestObject)
+ throws SocketException, EOFException, IOException
+ {
+ TokenRequest request = (TokenRequest)requestObject;
+ TokenAcceptor parent = (TokenAcceptor) parent ();
+
+ try {
+ request.streamInFrom (this.peer ().dataInputStream ());
+
+ if (!request.tokenName ().equals (lastTokenName_)) {
+ // Switched tokens:
+ //
+ // Either find a handler that's already been made (which would
+ // mean this token has been accessed before), or create a new
+ // one with a new token
+ handler_ = parent.getLockHandler(request.tokenName(),
+ request.tokenType());
+
+ if (handler_ == null) {
+ // The client asked for an operation on a type of token
+ // that we don't know about.
+ ACE.ERROR ("Unknown lock type: " + request.tokenType ());
+ TokenReply error = new TokenReply (TokenReply.EINVAL,
+ request.arg ());
+ error.streamOutTo(this.peer ().dataOutputStream ());
+ return;
+ }
+
+ // Add this LockHandler to the list of those accessed by
+ // this clientID
+ parent.addClientLockHandler (request.clientID (),
+ handler_);
+ }
+
+ if (!request.clientID ().equals (lastClientID_)) {
+ // Switched clients
+
+ if (!clients_.contains (request.clientID ()))
+ clients_.addElement (request.clientID ());
+
+ parent.addClientLockHandler (request.clientID (),
+ handler_);
+ }
+
+ lastClientID_ = request.clientID ();
+ lastTokenName_ = request.tokenName ();
+
+ TokenReply reply = handler_.handleRequest(this, request);
+
+ reply.streamOutTo(this.peer ().dataOutputStream ());
+
+ } catch (NullPointerException e) {
+ sendAbortMessage (request);
+ throw e;
+ } catch (IOException e) {
+ sendAbortMessage (request);
+ throw e;
+ }
+ }
+
+ // List of clientIDs that have been processed by this instance
+ // of TokenRequestHandler. This is useful when abandoning the
+ // locks of any clients that have been using this socket.
+ private Vector clients_;
+
+ // Name of the last token accessed
+ private String lastTokenName_ = null;
+
+ // Last client ID which accessed a token from this handler
+ private String lastClientID_ = null;
+
+ // Current LockHandler
+ private LockHandler handler_ = null;
+}
+
diff --git a/java/JACE/netsvcs/Token/package.html b/java/JACE/netsvcs/Token/package.html
new file mode 100644
index 00000000000..80777aecc0a
--- /dev/null
+++ b/java/JACE/netsvcs/Token/package.html
@@ -0,0 +1,16 @@
+<!-- $Id$ -->
+<HTML>
+<BODY>
+Token Service for remote mutexes and reader/writer locks.
+<P>
+New types of lock can be easily added on the command line to the service
+without changing existing code by implementing a LockHandler for the new
+type.
+<P>
+A simple test client is available in the tests directory under
+netsvcs\Token.
+
+@see JACE.netsvcs.Token.LockHandler
+@see <a href="http://www.cs.wustl.edu/~schmidt/ACE-netsvcs.html">ACE Network Services</a>
+</BODY>
+</HTML>