// This file contains material supporting section 6.13 of the textbook: // "Object Oriented Software Engineering" and is issued under the open-source // license found at www.lloseng.com package ocsf.server; import java.util.*; import java.io.*; import java.net.*; /** * This class acts as a subclass of AbstractServer * and is also an Observable class. * This means that when a message is received, all observers * are notified. * * @author François Bélange * @author Dr Timothy C. Lethbridge * @author Dr Robert Laganière * @version August 2000 */ public class ObservableServer extends Observable { // Class variables ************************************************ /** * The string sent to the observers when a client has connected. */ public static final String CLIENT_CONNECTED= "#OS:Client connected."; /** * The string sent to the observers when a client has disconnected. */ public static final String CLIENT_DISCONNECTED= "#OS:Client disconnected."; /** * The string sent to the observers when an exception occurred with a client. * The error message of that exception will be appended to this string. */ public static final String CLIENT_EXCEPTION= "#OS:Client exception."; /** * The string sent to the observers when a listening exception occurred. * The error message of that exception will be appended to this string. */ public static final String LISTENING_EXCEPTION= "#OS:Listening exception."; /** * The string sent to the observers when the server has closed. */ public static final String SERVER_CLOSED= "#OS:Server closed."; /** * The string sent to the observers when the server has started. */ public static final String SERVER_STARTED= "#OS:Server started."; /** * The string sent to the observers when the server has stopped. */ public static final String SERVER_STOPPED= "#OS:Server stopped."; //Instance variables ********************************************** /** * The service used to simulate multiple class inheritance. */ private AdaptableServer service; //Constructor ***************************************************** /** * Constructs a new server. * * @param port the port on which to listen. */ public ObservableServer(int port) { service = new AdaptableServer(port, this); } //Instance methods ************************************************ /** * Begins the thread that waits for new clients */ final public void listen() throws IOException { service.listen(); } /** * Causes the server to stop accepting new connections. */ final public void stopListening() { service.stopListening(); } /** * Closes the server's connections with all clients. */ final public void close() throws IOException { service.close(); } /** * Sends a message to every client connected to the server. * * @param msg The message to be sent */ public void sendToAllClients(Object msg) { service.sendToAllClients(msg); } // ACCESSING METHODS ------------------------------------------------ /** * Used to find out if the server is accepting new clients. */ final public boolean isListening() { return service.isListening(); } /** * Returns an array of containing the existing * client connections. This can be used by * concrete subclasses to implement messages that do something with * each connection (e.g. kill it, send a message to it etc.) * * @return an array of Thread containing * ConnectionToClient instances. */ final public Thread[] getClientConnections() { return service.getClientConnections(); } /** * @return the number of clients currently connected. */ final public int getNumberOfClients() { return service.getNumberOfClients(); } /** * @return the port number. */ final public int getPort() { return service.getPort(); } /** * Sets the port number for the next connection. * Only has effect if the server is not currently listening. * * @param port the port number. */ final public void setPort(int port) { service.setPort(port); } /** * Sets the timeout time when accepting connection. * The default is half a second. * The server must be stopped and restarted for the timeout * change be in effect. * * @param timeout the timeout time in ms. */ final public void setTimeout(int timeout) { service.setTimeout(timeout); } /** * Sets the maximum number of * waiting connections accepted by the operating system. * The default is 20. * The server must be closed and restart for the backlog * change be in effect. * * @param backlog the maximum number of connections. */ final public void setBacklog(int backlog) { service.setBacklog(backlog); } /** * Hook method called each time a new client connection is * accepted. The method may be overridden by subclasses. * * @param client the connection connected to the client. */ protected synchronized void clientConnected(ConnectionToClient client) { setChanged(); notifyObservers(CLIENT_CONNECTED); } /** * Hook method called each time a client disconnects. * The method may be overridden by subclasses. * * @param client the connection with the client. */ protected synchronized void clientDisconnected(ConnectionToClient client) { setChanged(); notifyObservers(CLIENT_DISCONNECTED); } /** * Hook method called each time an exception * is raised in a client thread. * This implementation simply closes the * client connection, ignoring any exception. * The method may be overridden by subclasses. * * @param client the client that raised the exception. * @param exception the exception raised. */ protected synchronized void clientException(ConnectionToClient client, Throwable exception) { setChanged(); notifyObservers(CLIENT_EXCEPTION); try { client.close(); } catch (Exception e) {} } /** * This method is called when the server stops accepting * connections because an exception has been raised. * This implementation * simply calls stopListening. * This method may be overriden by subclasses. * * @param exception the exception raised. */ protected synchronized void listeningException(Throwable exception) { setChanged(); notifyObservers(LISTENING_EXCEPTION); stopListening(); } /** * This method is called when the server stops accepting * connections for any reason. This method may be overriden by * subclasses. */ synchronized protected void serverStopped() { setChanged(); notifyObservers(SERVER_STOPPED); } /** * This method is called when the server is closed. * This method may be overriden by subclasses. */ synchronized protected void serverClosed() { setChanged(); notifyObservers(SERVER_CLOSED); } /** * This method is called when the server starts listening for * connections. The method may be overridden by subclasses. */ protected synchronized void serverStarted() { setChanged(); notifyObservers(SERVER_STARTED); } /** * This method is used to handle messages coming from the client. * Observers are notfied by receiveing the transmitted message. * Note that, in this implementation, the information concerning * the client that sent the message is lost. * It can be overriden, but is still expected to call notifyObservers(). * * @param message The message received from the client. * @param client The connection to the client. * @see ocsf.server.ObservableOriginatorServer */ protected synchronized void handleMessageFromClient (Object message, ConnectionToClient client) { setChanged(); notifyObservers(message); } }