summaryrefslogtreecommitdiff
path: root/java/JACE/netsvcs/Server.java
blob: 199a830d0c52cd9d505bbea14358e07f336ea01b (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
354
355
356
package JACE.netsvcs;

import java.io.*;
import java.net.*;
import java.util.*;
import JACE.OS.*;
import JACE.Connection.*;
import JACE.Misc.GetOpt;
import JACE.ServiceConfigurator.Service;

/**
 * Abstract class providing default implementations for several
 * Service methods.  Currently, all the network services
 * descend from this class.  The real work for a service is done
 * by a Handler.  
 * <P>
 * Inner classes are provided for thread per connection
 * and single threaded server activation strategies.  Currently,
 * specifying a single threaded strategy means that the server will
 * disconnect the client after handling one request.  Acceptor and
 * EventHandler may be changed later to incorporate handleInput to
 * address this.  Thus, the default activation strategy is thread
 * per connection.
 *
 *@see Handler
 *@see JACE.Connection.ActivateStrategy
 *@author Everett Anderson
 */
public abstract class Server extends Acceptor implements Runnable
{
  /**
   * Safely shuts down all the handlers as well as the accepting socket.
   *
   *@return -1 on failure, 0 on success
   */
  public synchronized int fini ()
  {
    if (!done ()) {
      ACE.DEBUG ("Shutting down " + name ());
      try {
	this.done_ = true;
	for (int i = handlers_.size () - 1; i >= 0; i--)
	  ((Handler)handlers_.elementAt (i)).close ();

	this.sockAcceptor_.close();
      } catch (IOException e) {
	ACE.ERROR(e);
	return -1;
      }
    }      
    
    return 0;
  }

  /**
   * Returns information about the state of the service such as
   * suspended, not running, or running.
   */
  public String info ()
  {
    if (suspended ())
      return "suspended";
    else
    if (done ())
      return "not running";
    else
      return "running on port " + port_;
  }
  
  /**
   * Provided for extra initialization in subclasses after the
   * command line arguments have been parsed but before starting the
   * service.  This is a good place to set the default ActivateStrategy
   * since you can make sure it wasn't set in parseArgs.  The default
   * implementation sets the strategy to Server.ThreadPerConnection.
   *
   *@return -1 on error, 0 on success
   */
  protected int initialize ()
  {
    if (activateStrategy_ == null)
      activateStrategy (new Server.ThreadPerConnection ());

    return 0;
  }

  /**
   * Template method for initialization.  Calls parseArgs, initialize,
   * sets the done() state to false, and starts this Server in its own
   * thread.  If parseArgs fails, this calls printUsage.
   *
   *@param args command line arguments
   *@return -1 on failure, 0 on success
   */
  public int init (String [] args)
  {
    // Parse arguments
    if (this.parseArgs (args) == -1) {
      printUsage ();
      return -1;
    }

    if (initialize () < 0) {
      ACE.ERROR (name () + " failed initializing");
      return -1;
    }

    ACE.DEBUG ("Using " + activateStrategy_.getClass().getName ());
    ACE.DEBUG ("Starting " + name () + " on port: " + this.port_);

    done_ = false;

    // Run in own thread of control so that we don't block the caller
    new Thread (this).start();
    return 0;
  }

  /**
   * Called by the JVM when this Server starts running in its own
   * thread.
   */
  public void run ()
  {
    try {
      this.open (this.port_);
      while (!this.done ()) {
	this.accept ();
      }
    } catch (InstantiationException e) {
      ACE.ERROR (e);
    } catch (IllegalAccessException e) {
      ACE.ERROR (e);
    } catch (IOException e) {
      if (!done ())
	ACE.ERROR (e);
    } finally {
      fini ();
    }
  }

  /**
   * Calls the appropriate activation strategy with the given
   * service handler.  This assumes the SvcHandler is an instance
   * of type Handler, and sets its parent accordingly.
   *
   *@param sh SvcHandler (assumed to be a Handler) to activate
   *@return -1 on failure, 0 on success
   */
  protected int activateSvcHandler (SvcHandler sh)
  {
    if (done ())
      return -1;
    
    addHandler (sh);
    ((Handler)sh).parent (this);

    while (suspended () && !done ())
      Thread.yield ();

    if (activateStrategy_.activateSvcHandler (sh) != 0) {
      removeHandler (sh);
      return -1;
    }

    return 0;
  }
  
  /**
   * Add the given SvcHandler to this Servers list of handlers.
   * @param sh service handler to add (assumed to be a Handler)
   */
  protected void addHandler (SvcHandler sh)
  {
    handlers_.addElement (sh);
  }

  /**
   * Called by Handler instances during their close () method.
   *@param sh service handler to remove
   */
  public void removeHandler (SvcHandler sh)
  {
    handlers_.removeElement (sh);
  }
  
  /**
   * Parses the command line arguments.  Subclasses must override
   * this.
   *
   *@param args command line arguments
   *@return -1 on failure, 0 on success
   */
  protected abstract int parseArgs (String [] args);

  /**
   * Create the appropriate Handler.  Subclasses must override this,
   * returning a new instance of the proper subclass of Handler.
   *
   *@return new Handler instance
   */
  protected abstract SvcHandler makeSvcHandler ();

  /**
   * Print out the correct syntax and meaning of the command line
   * arguments.
   */
  protected abstract void printUsage ();

  /**
   * Set the ActivateStrategy for handlers.
   *
   *@param strategy new ActivateStrategy to use
   *@see JACE.Connection.ActivateStrategy
   */
  protected void activateStrategy (ActivateStrategy strategy)
  {
    activateStrategy_ = strategy;
  }

  /**
   * Return the current ActivateStrategy for handlers.
   *
   *@return current ActivateStrategy instance
   */
  protected ActivateStrategy activateStrategy ()
  {
    return activateStrategy_;
  }

  /**
   * Check to see if this Server has been shut down.
   */
  protected synchronized boolean done ()
  {
    return done_;
  }

  /**
   * Useful method for subclasses when parsing the port command
   * line option.  
   *
   *@param port String gathered from the command line representing the port
   *@return false if there was an error, true if successful
   */
  protected boolean port (String port)
  {
    try {
      
      this.port_ = Integer.parseInt (port);

    } catch (NumberFormatException e) {
      ACE.ERROR("Invalid port specified: " + e.getMessage ());
      return false;
    } catch (ArrayIndexOutOfBoundsException e) {
      ACE.ERROR("Port option requires an argument");
      return false;
    }

    return true;
  }

  /**
   * Useful method for subclasses when trying to load and instantiate
   * a certain class from a command line argument.  This can be used
   * when a possible command line argument is what kind of activation
   * strategy is used for handlers.
   *
   *@param classname name of the class to load and create an instance of
   *@param descrption descrption of what type of class it is
   *@return null if failed loading, a new instance of the class on success
   */
  protected Object newStrategyInstance (String classname,
					String description)
  {
    try {
      Class factory = Class.forName (classname);

      return factory.newInstance ();
    
    } catch (ClassNotFoundException e) {
      ACE.ERROR("Unable to find " + description + ": " 
		+ e.getMessage ());
    } catch (InstantiationException e) {
      ACE.ERROR ("Instantiating " + description + ": " 
		 + e.getMessage ());
    } catch (IllegalAccessException e) {
      ACE.ERROR ("Illegal access on " + description + ": " 
		 + e.getMessage ());
    }    

    return null;
  }

  /**
   * Shuts down the Server if it wasn't already done
   */
  protected void finalize () throws Throwable
  {
    fini ();
  }

  private boolean done_ = true;

  /**
   * List of currently active Handlers
   */
  protected Vector handlers_ = new Vector ();
  private ActivateStrategy activateStrategy_ = null;

  /**
   * Activation strategy in which each Handler is run in its own
   * Thread.
   */
  public static class ThreadPerConnection extends ActivateStrategy
  {
    /**
     * Opens the given service handler, and runs it in its own
     * Thread.
     *@param sh service handler to activate
     *@return -1 on failure, 0 on success
     */
    public int activateSvcHandler (SvcHandler sh)
    {
      if (sh.open (null) < 0)
	return -1;

      new Thread (sh).start ();
      return 0;
    }
  }

  /**
   * Activation strategy in which all Handlers are run in the
   * Server Thread in sequence.  This assumes that the given
   * SvcHandler is a Handler instance. 
   */
  public static class SingleThreaded extends ActivateStrategy
  {
    /**
     * Opens the given service handler, calls Handler.handleRequest, and
     * then Handler.close before returning.
     *@param sh service handler to activate (assumed to be a Handler)
     *@return -1 on failure, 0 on success
     */
    public int activateSvcHandler (SvcHandler sh)
    {
      if (sh.open (null) < 0)
	return -1;

      ((Handler)sh).handleRequest ();
      ((Handler)sh).close ();

      return 0;
    }
  }
}