summaryrefslogtreecommitdiff
path: root/java/JACE/netsvcs/Token/TokenAcceptor.java
blob: 53adf08753b8b4f60279f2cd7e1ff262160ed359 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
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_;
}