Serverpublic class Server extends Object This class is a generic framework for a flexible, multi-threaded server.
It listens on any number of specified ports, and, when it receives a
connection on a port, passes input and output streams to a specified Service
object which provides the actual service. It can limit the number of
concurrent connections, and logs activity to a specified stream. |
Fields Summary |
---|
Map | services | Set | connections | int | maxConnections | ThreadGroup | threadGroup | PrintWriter | logStream | Logger | logger | Level | logLevel |
Constructors Summary |
---|
public Server(OutputStream logStream, int maxConnections)This is the Server() constructor. It must be passed a stream
to send log output to (may be null), and the limit on the number of
concurrent connections.
this(maxConnections);
setLogStream(logStream);
log("Starting server");
| public Server(Logger logger, Level logLevel, int maxConnections)This constructor added to support logging with the Java 1.4 Logger class
this(maxConnections);
setLogger(logger, logLevel);
log("Starting server");
| public Server(int maxConnections)This constructor supports no logging
threadGroup = new ThreadGroup(Server.class.getName());
this.maxConnections = maxConnections;
services = new HashMap();
connections = new HashSet(maxConnections);
|
Methods Summary |
---|
protected synchronized void | addConnection(java.net.Socket s, je3.net.Server$Service service)This is the method that Listener objects call when they accept a
connection from a client. It either creates a Connection object
for the connection and adds it to the list of current connections,
or, if the limit on connections has been reached, it closes the
connection.
// If the connection limit has been reached
if (connections.size() >= maxConnections) {
try {
// Then tell the client it is being rejected.
PrintWriter out = new PrintWriter(s.getOutputStream());
out.print("Connection refused; " +
"the server is busy; please try again later.\r\n");
out.flush();
// And close the connection to the rejected client.
s.close();
// And log it, of course
log("Connection refused to " +
s.getInetAddress().getHostAddress() +
":" + s.getPort() + ": max connections reached.");
} catch (IOException e) {log(e);}
}
else { // Otherwise, if the limit has not been reached
// Create a Connection thread to handle this connection
Connection c = new Connection(s, service);
// Add it to the list of current connections
connections.add(c);
// Log this new connection
log("Connected to " + s.getInetAddress().getHostAddress() +
":" + s.getPort() + " on port " + s.getLocalPort() +
" for service " + service.getClass().getName());
// And start the Connection thread to provide the service
c.start();
}
| public synchronized void | addService(je3.net.Server$Service service, int port)This method makes the server start providing a new service.
It runs the specified Service object on the specified port.
Integer key = new Integer(port); // the hashtable key
// Check whether a service is already on that port
if (services.get(key) != null)
throw new IllegalArgumentException("Port " + port +
" already in use.");
// Create a Listener object to listen for connections on the port
Listener listener = new Listener(threadGroup, port, service);
// Store it in the hashtable
services.put(key, listener);
// Log it
log("Starting service " + service.getClass().getName() +
" on port " + port);
// Start the listener running.
listener.start();
| public synchronized void | displayStatus(java.io.PrintWriter out)This method displays status information about the server on the
specified stream. It can be used for debugging, and is used by the
Control service later in this example.
// Display a list of all Services that are being provided
Iterator keys = services.keySet().iterator();
while(keys.hasNext()) {
Integer port = (Integer) keys.next();
Listener listener = (Listener) services.get(port);
out.print("SERVICE " + listener.service.getClass().getName()
+ " ON PORT " + port + "\r\n");
}
// Display the current connection limit
out.print("MAX CONNECTIONS: " + maxConnections + "\r\n");
// Display a list of all current connections
Iterator conns = connections.iterator();
while(conns.hasNext()) {
Connection c = (Connection)conns.next();
out.print("CONNECTED TO " +
c.client.getInetAddress().getHostAddress() +
":" + c.client.getPort() + " ON PORT " +
c.client.getLocalPort() + " FOR SERVICE " +
c.service.getClass().getName() + "\r\n");
}
| protected synchronized void | endConnection(je3.net.Server$Connection c)A Connection thread calls this method just before it exits. It removes
the specified Connection from the set of connections.
connections.remove(c);
log("Connection to " + c.client.getInetAddress().getHostAddress() +
":" + c.client.getPort() + " closed.");
| protected synchronized void | log(java.lang.String s)Write the specified string to the log
if (logger != null) logger.log(logLevel, s);
if (logStream != null) {
logStream.println("[" + new Date() + "] " + s);
logStream.flush();
}
| protected void | log(java.lang.Object o)Write the specified object to the log log(o.toString());
| public static void | main(java.lang.String[] args)A main() method for running the server as a standalone program. The
command-line arguments to the program should be pairs of servicenames
and port numbers. For each pair, the program will dynamically load the
named Service class, instantiate it, and tell the server to provide
that Service on the specified port. The special -control argument
should be followed by a password and port, and will start special
server control service running on the specified port, protected by the
specified password.
try {
if (args.length < 2) // Check number of arguments
throw new IllegalArgumentException("Must specify a service");
// Create a server object that has a limit of 10 concurrent
// connections, and logs to a Logger at the Level.INFO level
// Prior to Java 1.4 we did this: new Server(System.out, 10);
Server s = new Server(Logger.getLogger(Server.class.getName()),
Level.INFO, 10);
// Parse the argument list
int i = 0;
while(i < args.length) {
if (args[i].equals("-control")) { // Handle the -control arg
i++;
String password = args[i++];
int port = Integer.parseInt(args[i++]);
// add control service
s.addService(new Control(s, password), port);
}
else {
// Otherwise start a named service on the specified port.
// Dynamically load and instantiate a Service class
String serviceName = args[i++];
Class serviceClass = Class.forName(serviceName);
Service service = (Service)serviceClass.newInstance();
int port = Integer.parseInt(args[i++]);
s.addService(service, port);
}
}
}
catch (Exception e) { // Display a message if anything goes wrong
System.err.println("Server: " + e);
System.err.println("Usage: java Server " +
"[-control <password> <port>] " +
"[<servicename> <port> ... ]");
System.exit(1);
}
| public synchronized void | removeService(int port)This method makes the server stop providing a service on a port.
It does not terminate any pending connections to that service, merely
causes the server to stop accepting new connections
Integer key = new Integer(port); // hashtable key
// Look up the Listener object for the port in the hashtable
final Listener listener = (Listener) services.get(key);
if (listener == null) return;
// Ask the listener to stop
listener.pleaseStop();
// Remove it from the hashtable
services.remove(key);
// And log it.
log("Stopping service " + listener.service.getClass().getName() +
" on port " + port);
| public synchronized void | setLogStream(java.io.OutputStream out)A public method to set the current logging stream. Pass null
to turn logging off.
if (out != null) logStream = new PrintWriter(out);
else logStream = null;
| public synchronized void | setLogger(java.util.logging.Logger logger, java.util.logging.Level level)Set the current Logger and logging level. Pass null to turn logging off.
this.logger = logger;
this.logLevel = level;
| public synchronized void | setMaxConnections(int max)Change the current connection limit
maxConnections = max;
|
|