FileDocCategorySizeDatePackage
Server.javaAPI DocExample27413Sat Jan 24 10:44:28 GMT 2004je3.net

Server

public 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 voidaddConnection(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 voidaddService(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 voiddisplayStatus(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 voidendConnection(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 voidlog(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 voidlog(java.lang.Object o)
Write the specified object to the log

 log(o.toString()); 
public static voidmain(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 voidremoveService(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 voidsetLogStream(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 voidsetLogger(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 voidsetMaxConnections(int max)
Change the current connection limit

	maxConnections = max;