// 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);
}
}