diff --git a/Test Cases.zip b/Test Cases.zip
new file mode 100644
index 0000000..bc42918
Binary files /dev/null and b/Test Cases.zip differ
diff --git a/code/simplechat1/ClientConsole.java b/code/simplechat1/ClientConsole.java
index c9bb4e9..09bfb96 100644
--- a/code/simplechat1/ClientConsole.java
+++ b/code/simplechat1/ClientConsole.java
@@ -41,21 +41,20 @@ public class ClientConsole implements ChatIF
* @param host The host to connect to.
* @param port The port to connect on.
*/
- public ClientConsole(String host, int port)
+ public ClientConsole(String loginID, String host, int port)
{
try
{
- client= new ChatClient(host, port, this);
+ client= new ChatClient(loginID, host, port, this);
}
catch(IOException exception)
{
- System.out.println("Error: Can't setup connection!"
- + " Terminating client.");
- System.exit(1);
+ System.out.println("Cannot open connection. Awaiting command.");
+ System.exit(1); // Added because commands don't work..
}
}
-
+
//Instance methods ************************************************
/**
@@ -78,8 +77,7 @@ public void accept()
}
catch (Exception ex)
{
- System.out.println
- ("Unexpected error while reading from console!");
+ System.out.println("Unexpected error while reading from console!");
}
}
@@ -100,22 +98,36 @@ public void display(String message)
/**
* This method is responsible for the creation of the Client UI.
*
- * @param args[0] The host to connect to.
+ * @param args[0] The loginID to connect to.
+ * @param args[1] The host to connect to.
+ * @param args[2] The port to connect to.
*/
public static void main(String[] args)
{
+ String loginID = "";
String host = "";
- int port = 0; //The port number
-
- try
- {
- host = args[0];
+ int port = 5555; //The port number
+ try{
+ loginID = args[0];
}
- catch(ArrayIndexOutOfBoundsException e)
- {
+ catch(ArrayIndexOutOfBoundsException ex){
+ System.out.println("You need to enter a login ID. Terminating client..");
+ System.exit(1);
+ }
+ try{
+ host = args[1];
+ }
+ catch(ArrayIndexOutOfBoundsException ex){
host = "localhost";
}
- ClientConsole chat= new ClientConsole(host, DEFAULT_PORT);
+ try{
+ port = Integer.parseInt(args[2]);
+ }
+ catch(ArrayIndexOutOfBoundsException ex)
+ {
+ port = DEFAULT_PORT;
+ }
+ ClientConsole chat= new ClientConsole(loginID, host, port);
chat.accept(); //Wait for console data
}
}
diff --git a/code/simplechat1/EchoServer.java b/code/simplechat1/EchoServer.java
index d4f3a1a..fe415ca 100644
--- a/code/simplechat1/EchoServer.java
+++ b/code/simplechat1/EchoServer.java
@@ -4,6 +4,7 @@
import java.io.*;
import ocsf.server.*;
+import common.*;
/**
* This class overrides some of the methods in the abstract
@@ -25,7 +26,7 @@ public class EchoServer extends AbstractServer
final public static int DEFAULT_PORT = 5555;
//Constructors ****************************************************
-
+ ChatIF serverUI;
/**
* Constructs an instance of the echo server.
*
@@ -36,6 +37,11 @@ public EchoServer(int port)
super(port);
}
+ public EchoServer(int port, ChatIF serverUI) throws IOException {
+ super(port);
+ this.serverUI = serverUI;
+ }
+
//Instance methods ************************************************
@@ -45,21 +51,108 @@ public EchoServer(int port)
* @param msg The message received from the client.
* @param client The connection from which the message originated.
*/
- public void handleMessageFromClient
- (Object msg, ConnectionToClient client)
- {
- System.out.println("Message received: " + msg + " from " + client);
- this.sendToAllClients(msg);
+ public void handleMessageFromClient(Object msg, ConnectionToClient client){
+ if(msg.toString().startsWith("#login")){
+ if(client.getInfo("loginID") != null){
+ try{
+ System.out.println("You are already logged in.");
+ }
+ catch(Exception e){}
+ }
+ client.setInfo("loginID",msg.toString().substring(8));
+ try{
+ client.sendToClient(client.getInfo("loginID") + " has logged on.");
+ }
+ catch(IOException g){}
+ }
+ else{
+ try{
+ if(client.getInfo("loginID") == null){
+ System.out.println("Could not login.. use #login has first argument!");
+ client.close();
+ }
+ }
+ catch(IOException e){
+ e.printStackTrace();
+ }
+ }
+ System.out.println("Message received: " + msg + " from " + client.getInfo("loginID") + ", Users IP: " + client);
+ if(msg.toString().startsWith("#login")){
+ if(client.getInfo("loginID") != null){
+ try{
+ System.out.println(client.getInfo("loginID") + " has logged on.");
+ }
+ catch(Exception gg){}
+ }
+ }
+ this.sendToAllClients(client.getInfo("loginID") + " > " + msg);
+ }
+
+ public void handleMessageFromServerUI(String message){
+ try{
+ if(message.charAt(0) == '#'){
+ if(message.equals("#quit")){
+ stopListening();
+ close();
+ System.out.println("Server is quitting..");
+ System.exit(1);
+ }
+ if(message.equals("#stop")){
+ stopListening();
+ this.sendToAllClients("WARNING - Server has stopped listening for connections.");
+ }
+ if(message.equals("#close")){
+ close();
+ }
+ if(message.startsWith("#setport")){
+ if(!isListening()){
+ try{
+ String p = message.substring(9);
+ setPort(Integer.parseInt(p));
+ System.out.println("The server port has been changed to: " + getPort());
+ }
+ catch(Exception ex){
+ System.out.println("Could not set the server port!");
+ }
+ }
+ else{
+ System.out.println("Server must be stopped.");
+ }
+ }
+ if(message.equals("#start")){
+ if(!isListening()){
+ try{
+ listen();
+ }
+ catch(Exception el){
+ System.out.println("Could not listen for new connections.");
+ }
+ }
+ else{
+ System.out.println("Server must be stopped.");
+ }
+ }
+ if(message.equals("#getport")){
+ System.out.println("Port: " + getPort());
+ }
+ }
+ else{
+ serverUI.display(message);
+ this.sendToAllClients("SERVER MSG> " + message);
+ }
+ }
+ catch(IOException e){
+ System.out.println("Could not send message from server. Terminating server.");
+ System.exit(1);
+ }
}
-
/**
* This method overrides the one in the superclass. Called
* when the server starts listening for connections.
*/
protected void serverStarted()
{
- System.out.println
- ("Server listening for connections on port " + getPort());
+ System.out.println("Server listening for connections on port " + getPort());
}
/**
@@ -68,9 +161,22 @@ protected void serverStarted()
*/
protected void serverStopped()
{
- System.out.println
- ("Server has stopped listening for connections.");
+ System.out.println("Server has stopped listening for connections.");
+ }
+
+ protected void clientConnected(ConnectionToClient client) {
+ System.out.println("A new client is attempting to connect to the server.");
}
+ synchronized protected void clientDisconnected(ConnectionToClient client) {
+ System.out.println(client.getInfo("loginID") + " has disconnected from the server.");
+ this.sendToAllClients(client.getInfo("loginID") + " has disconnected from the server.");
+ }
+ synchronized protected void clientException(ConnectionToClient client, Throwable exception) {
+ System.out.println(client.getInfo("loginID") + " has disconnected from the server.");
+ this.sendToAllClients(client.getInfo("loginID") + " has disconnected from the server.");
+ }
+
+
//Class methods ***************************************************
diff --git a/code/simplechat1/ServerConsole.java b/code/simplechat1/ServerConsole.java
new file mode 100644
index 0000000..01f92dc
--- /dev/null
+++ b/code/simplechat1/ServerConsole.java
@@ -0,0 +1,128 @@
+// This file contains material supporting section 3.7 of the textbook:
+// "Object Oriented Software Engineering" and is issued under the open-source
+// license found at www.lloseng.com
+
+import java.io.*;
+import client.*;
+import common.*;
+
+/**
+ * This class constructs the UI for a server chat client. It implements the
+ * chat interface in order to activate the display() method.
+ * Warning: Some of the code here is cloned in ServerConsole
+ *
+ * @author François Bélanger
+ * @author Dr Timothy C. Lethbridge
+ * @author Dr Robert Laganière
+ * @version July 2000
+ */
+public class ServerConsole implements ChatIF
+{
+ //Class variables *************************************************
+
+ /**
+ * The default port to connect on.
+ */
+ final public static int DEFAULT_PORT = 5555;
+
+ //Instance variables **********************************************
+
+ /**
+ * The instance of the EchoServer class.
+ */
+ EchoServer server;
+
+
+ //Constructors ****************************************************
+
+ /**
+ * Constructs an instance of the ServerConsole UI.
+ *
+ * @param port The port to connect on.
+ */
+ public ServerConsole(int port)
+ {
+ try
+ {
+ server = new EchoServer(port, this);
+ }
+ catch(IOException exception)
+ {
+ System.out.println("Error: Can't setup connection!"
+ + " Terminating client.");
+ System.exit(1);
+ }
+ try
+ {
+ server.listen();
+ }
+ catch (Exception ex)
+ {
+ System.out.println("Error when attempting to listen.");
+ }
+ }
+
+
+ //Instance methods ************************************************
+
+ /**
+ * This method waits for input from the console. Once it is
+ * received, it sends it to the client's message handler.
+ */
+ public void accept()
+ {
+ try
+ {
+ BufferedReader fromConsole =
+ new BufferedReader(new InputStreamReader(System.in));
+ String message;
+
+ while (true)
+ {
+ message = fromConsole.readLine();
+ server.handleMessageFromServerUI(message);
+ }
+ }
+ catch (Exception ex)
+ {
+ System.out.println
+ ("Unexpected error while reading from console!");
+ }
+ }
+
+ /**
+ * This method overrides the method in the ChatIF interface. It
+ * displays a message onto the screen.
+ *
+ * @param message The string to be displayed.
+ */
+ public void display(String message)
+ {
+ System.out.println("> " + message);
+ }
+
+
+ //Class methods ***************************************************
+
+ /**
+ * This method is responsible for the creation of the Client UI.
+ *
+ * @param args[0] The host to connect to.
+ */
+ public static void main(String[] args)
+ {
+ int port = 5555; //The port number
+
+ try
+ {
+ port = Integer.parseInt(args[0]);
+ }
+ catch(ArrayIndexOutOfBoundsException e)
+ {
+ port = DEFAULT_PORT;
+ }
+ ServerConsole chat = new ServerConsole(port);
+ chat.accept(); //Wait for console data
+ }
+}
+//End of ConsoleChat class
diff --git a/code/simplechat1/client/ChatClient.java b/code/simplechat1/client/ChatClient.java
index fe1401e..8085e35 100644
--- a/code/simplechat1/client/ChatClient.java
+++ b/code/simplechat1/client/ChatClient.java
@@ -27,7 +27,8 @@ public class ChatClient extends AbstractClient
*/
ChatIF clientUI;
-
+ private String loginID;
+
//Constructors ****************************************************
/**
@@ -37,13 +38,23 @@ public class ChatClient extends AbstractClient
* @param port The port number to connect on.
* @param clientUI The interface type variable.
*/
-
public ChatClient(String host, int port, ChatIF clientUI)
throws IOException
{
super(host, port); //Call the superclass constructor
this.clientUI = clientUI;
+ this.loginID = "Guest";
openConnection();
+ sendToServer("#loginID " + loginID);
+ }
+ public ChatClient(String loginID, String host, int port, ChatIF clientUI)
+ throws IOException
+ {
+ super(host, port); //Call the superclass constructor
+ this.clientUI = clientUI;
+ this.loginID = loginID;
+ openConnection();
+ sendToServer("#loginID " + loginID);
}
@@ -59,21 +70,99 @@ public void handleMessageFromServer(Object msg)
clientUI.display(msg.toString());
}
+ // protected void connectionClosed()
+ // {
+ // clientUI.display("Connection closed! Exiting..");
+ // }
+
+ protected void connectionException(Exception exception)
+ {
+ clientUI.display("Connection to server closed.");
+ }
/**
* This method handles all data coming from the UI
*
* @param message The message from the UI.
*/
+
public void handleMessageFromClientUI(String message)
{
try
{
- sendToServer(message);
+ if(message.charAt(0) == '#'){
+ if(message.equals("#quit")){
+ closeConnection();
+ clientUI.display("Connection closed.");
+ System.exit(0);
+ }
+ if(message.equals("#logoff")){
+ try{
+ closeConnection();
+ clientUI.display("Connection closed.");
+ }
+ catch(Exception ex){
+ clientUI.display("You have disconnected.");
+ }
+ }
+ if(message.startsWith("#sethost")){
+ if(!isConnected()){
+ try{
+ String h = message.substring(9); // hostname begins from index 9 to end (includes space)
+ setHost(h);
+ clientUI.display("New host set to " + getHost());
+ }
+ catch(Exception e){
+ clientUI.display("Couldn't set host.. error occurred.");
+ }
+ }
+ else{
+ clientUI.display("Error: You must disconnect from the server.");
+ }
+ }
+ if(message.startsWith("#setport")){
+ if(!isConnected()){
+ try{
+ String p = message.substring(9); // same as above (for port however)
+ setPort(Integer.parseInt(p));
+ clientUI.display("New port set to " + getPort());
+ }
+ catch(Exception e){
+ clientUI.display("Couldn't set port.. error occurred.");
+ }
+ }
+ else{
+ clientUI.display("Error: You must disconnect from the server.");
+ }
+ }
+ if(message.startsWith("#login")){
+ if(!isConnected()){
+ loginID = message.substring(7);
+ try{
+ openConnection();
+ sendToServer("#loginID " + loginID);
+ }
+ catch(Exception ex){
+ clientUI.display("Error: Could not connect to server.");
+ }
+ }
+ else{
+ clientUI.display("Error: You must disconnect from the server.");
+ }
+ }
+ if(message.equals("#gethost")){
+ clientUI.display("Host: " + getHost());
+ }
+ if(message.equals("#getport")){
+ clientUI.display("Port: " + getPort());
+ }
+ }
+ else{
+ sendToServer(message);
+ }
}
catch(IOException e)
{
- clientUI.display
- ("Could not send message to server. Terminating client.");
+ clientUI.display("Could not send message to server. Terminating client.");
quit();
}
}
diff --git a/code/simplechat1/ocsf/client/AbstractClient.java b/code/simplechat1/ocsf/client/AbstractClient.java
new file mode 100644
index 0000000..e905636
--- /dev/null
+++ b/code/simplechat1/ocsf/client/AbstractClient.java
@@ -0,0 +1,333 @@
+// This file contains material supporting section 3.7 of the textbook:
+// "Object Oriented Software Engineering" and is issued under the open-source
+// license found at www.lloseng.com
+
+package ocsf.client;
+
+import java.io.*;
+import java.net.*;
+
+/**
+ * The AbstractClient contains all the methods necessary to set
+ * up the client side of a client-server architecture. When a client is thus
+ * connected to the server, the two programs can then exchange
+ * Object instances.
+ *
+ * Method handleMessageFromServer must be defined by a concrete
+ * subclass. Several other hook methods may also be overriden.
+ *
+ * Several public service methods are provided to application that use this + * framework. + *
+ * Project Name: OCSF (Object Client-Server Framework) + *
+ *
+ * @author Dr. Robert Laganière
+ * @author Dr. Timothy C. Lethbridge
+ * @author François Bél;langer
+ * @author Paul Holden
+ * @version February 2001 (2.12)
+ */
+public abstract class AbstractClient implements Runnable {
+
+ // INSTANCE VARIABLES ***********************************************
+
+ /**
+ * Sockets are used in the operating system as channels of communication
+ * between two processes.
+ *
+ * @see java.net.Socket
+ */
+ private Socket clientSocket;
+
+ /**
+ * The stream to handle data going to the server.
+ */
+ private ObjectOutputStream output;
+
+ /**
+ * The stream to handle data from the server.
+ */
+ private ObjectInputStream input;
+
+ /**
+ * The thread created to read data from the server.
+ */
+ private Thread clientReader;
+
+ /**
+ * Indicates if the thread is ready to stop. Needed so that the loop in the
+ * run method knows when to stop waiting for incoming messages.
+ */
+ private boolean readyToStop = false;
+
+ /**
+ * The server's host name.
+ */
+ private String host;
+
+ /**
+ * The port number.
+ */
+ private int port;
+
+ // CONSTRUCTORS *****************************************************
+
+ /**
+ * Constructs the client.
+ *
+ * @param host
+ * the server's host name.
+ * @param port
+ * the port number.
+ */
+ public AbstractClient(String host, int port) {
+ // Initialize variables
+ this.host = host;
+ this.port = port;
+ }
+
+ // INSTANCE METHODS *************************************************
+
+ /**
+ * Opens the connection with the server. If the connection is already
+ * opened, this call has no effect.
+ *
+ * @exception IOException
+ * if an I/O error occurs when opening.
+ */
+ final public void openConnection() throws IOException {
+ // Do not do anything if the connection is already open
+ if (isConnected())
+ return;
+
+ // Create the sockets and the data streams
+ try {
+ clientSocket = new Socket(host, port);
+ output = new ObjectOutputStream(clientSocket.getOutputStream());
+ input = new ObjectInputStream(clientSocket.getInputStream());
+ } catch (IOException ex)
+ // All three of the above must be closed when there is a failure
+ // to create any of them
+ {
+ try {
+ closeAll();
+ } catch (Exception exc) {
+ }
+
+ throw ex; // Rethrow the exception.
+ }
+
+ clientReader = new Thread(this); // Create the data reader thread
+ readyToStop = false;
+ clientReader.start(); // Start the thread
+ }
+
+ /**
+ * Sends an object to the server. This is the only way that methods should
+ * communicate with the server.
+ *
+ * @param msg
+ * The message to be sent.
+ * @exception IOException
+ * if an I/O error occurs when sending
+ */
+ final public void sendToServer(Object msg) throws IOException {
+ if (clientSocket == null || output == null)
+ throw new SocketException("socket does not exist");
+
+ output.writeObject(msg);
+ }
+
+ /**
+ * Reset the object output stream so we can use the same
+ * buffer repeatedly. This would not normally be used, but is necessary
+ * in some circumstances when Java refuses to send data that it thinks has been sent.
+ */
+ final public void forceResetAfterSend() throws IOException {
+ output.reset();
+ }
+
+ /**
+ * Closes the connection to the server.
+ *
+ * @exception IOException
+ * if an I/O error occurs when closing.
+ */
+ final public void closeConnection() throws IOException {
+ // Prevent the thread from looping any more
+ readyToStop = true;
+
+ try {
+ closeAll();
+ } finally {
+ // Call the hook method
+ connectionClosed();
+ }
+ }
+
+ // ACCESSING METHODS ------------------------------------------------
+
+ /**
+ * @return true if the client is connnected.
+ */
+ final public boolean isConnected() {
+ return clientReader != null && clientReader.isAlive();
+ }
+
+ /**
+ * @return the port number.
+ */
+ final public int getPort() {
+ return port;
+ }
+
+ /**
+ * Sets the server port number for the next connection. The change in port
+ * only takes effect at the time of the next call to openConnection().
+ *
+ * @param port
+ * the port number.
+ */
+ final public void setPort(int port) {
+ this.port = port;
+ }
+
+ /**
+ * @return the host name.
+ */
+ final public String getHost() {
+ return host;
+ }
+
+ /**
+ * Sets the server host for the next connection. The change in host only
+ * takes effect at the time of the next call to openConnection().
+ *
+ * @param host
+ * the host name.
+ */
+ final public void setHost(String host) {
+ this.host = host;
+ }
+
+ /**
+ * returns the client's description.
+ *
+ * @return the client's Inet address.
+ */
+ final public InetAddress getInetAddress() {
+ return clientSocket.getInetAddress();
+ }
+
+ // RUN METHOD -------------------------------------------------------
+
+ /**
+ * Waits for messages from the server. When each arrives, a call is made to
+ * handleMessageFromServer(). Not to be explicitly called.
+ */
+ final public void run() {
+ connectionEstablished();
+
+ // The message from the server
+ Object msg;
+
+ // Loop waiting for data
+
+ try {
+ while (!readyToStop) {
+ // Get data from Server and send it to the handler
+ // The thread waits indefinitely at the following
+ // statement until something is received from the server
+ msg = input.readObject();
+
+ // Concrete subclasses do what they want with the
+ // msg by implementing the following method
+ handleMessageFromServer(msg);
+ }
+ } catch (Exception exception) {
+ if (!readyToStop) {
+ try {
+ closeAll();
+ } catch (Exception ex) {
+ }
+
+ connectionException(exception);
+ }
+ } finally {
+ clientReader = null;
+ }
+ }
+
+ // METHODS DESIGNED TO BE OVERRIDDEN BY CONCRETE SUBCLASSES ---------
+
+ /**
+ * Hook method called after the connection has been closed. The default
+ * implementation does nothing. The method may be overriden by subclasses to
+ * perform special processing such as cleaning up and terminating, or
+ * attempting to reconnect.
+ */
+ protected void connectionClosed() {
+ }
+
+ /**
+ * Hook method called each time an exception is thrown by the client's
+ * thread that is waiting for messages from the server. The method may be
+ * overridden by subclasses.
+ *
+ * @param exception
+ * the exception raised.
+ */
+ protected void connectionException(Exception exception) {
+ }
+
+ /**
+ * Hook method called after a connection has been established. The default
+ * implementation does nothing. It may be overridden by subclasses to do
+ * anything they wish.
+ */
+ protected void connectionEstablished() {
+ }
+
+ /**
+ * Handles a message sent from the server to this client. This MUST be
+ * implemented by subclasses, who should respond to messages.
+ *
+ * @param msg
+ * the message sent.
+ */
+ protected abstract void handleMessageFromServer(Object msg);
+
+ // METHODS TO BE USED FROM WITHIN THE FRAMEWORK ONLY ----------------
+
+ /**
+ * Closes all aspects of the connection to the server.
+ *
+ * @exception IOException
+ * if an I/O error occurs when closing.
+ */
+ private void closeAll() throws IOException {
+ try {
+ // Close the socket
+ if (clientSocket != null)
+ clientSocket.close();
+
+ // Close the output stream
+ if (output != null)
+ output.close();
+
+ // Close the input stream
+ if (input != null)
+ input.close();
+ } finally {
+ // Set the streams and the sockets to NULL no matter what
+ // Doing so allows, but does not require, any finalizers
+ // of these objects to reclaim system resources if and
+ // when they are garbage collected.
+ output = null;
+ input = null;
+ clientSocket = null;
+ }
+ }
+}
+// end of AbstractClient class
\ No newline at end of file
diff --git a/code/simplechat1/ocsf/client/AdaptableClient.java b/code/simplechat1/ocsf/client/AdaptableClient.java
new file mode 100644
index 0000000..143592e
--- /dev/null
+++ b/code/simplechat1/ocsf/client/AdaptableClient.java
@@ -0,0 +1,82 @@
+// 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.client;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+/**
+* The AdaptableClient is a class
+* that extends the AbstractClient in place of
+* the ObservableClient .
+* +* Project Name: OCSF (Object Client-Server Framework)
+*
+* @author Dr. Robert Laganière
+* @version Febuary 2001
+*/
+class AdaptableClient extends AbstractClient
+{
+ //Instance variables **********************************************
+
+ /**
+ * The proxy used to simulate multiple class inheritance.
+ */
+ private ObservableClient client;
+
+// CONSTRUCTORS *****************************************************
+
+ /**
+ * Constructs the client adapter.
+ *
+ * @param host the server's host name.
+ * @param port the port number.
+ */
+ public AdaptableClient(String host, int port, ObservableClient client)
+ {
+ super(host, port);
+ this.client = client;
+ }
+
+// OVERRIDDEN METHODS *************************************************
+
+ /**
+ * Hook method called after the connection has been closed.
+ */
+ final protected void connectionClosed()
+ {
+ client.connectionClosed();
+ }
+
+ /**
+ * Hook method called after an exception
+ * is raised by the client listening thread.
+ *
+ * @param exception the exception raised.
+ */
+ final protected void connectionException(Exception exception)
+ {
+ client.connectionException(exception);
+ }
+
+ /**
+ * Hook method called after a connection has been established.
+ */
+ final protected void connectionEstablished()
+ {
+ client.connectionEstablished();
+ }
+
+ /**
+ * Handles a message sent from the server to this client.
+ *
+ * @param msg the message sent.
+ */
+ final protected void handleMessageFromServer(Object msg)
+ {
+ client.handleMessageFromServer(msg);
+ }
+}
diff --git a/code/simplechat1/ocsf/client/ObservableClient.java b/code/simplechat1/ocsf/client/ObservableClient.java
new file mode 100644
index 0000000..a816488
--- /dev/null
+++ b/code/simplechat1/ocsf/client/ObservableClient.java
@@ -0,0 +1,181 @@
+// 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.client;
+
+import java.util.*;
+import java.io.*;
+import java.net.*;
+
+/**
+ * This class acts as a subclass of AbstractClient
+ * and is also an Observable class.
+ * Each time a new message is received, observers are notified.
+ *
+ * @author Dr Robert Laganière
+ * @author Dr Timothy C. Lethbridge
+ * @author François Bélange
+ * @version Febuary 2001
+ */
+public class ObservableClient extends Observable
+{
+ // Class variables ***************************************************
+
+ /**
+ * Indicates occurence of a connection exception.
+ */
+ public static final String CONNECTION_EXCEPTION = "#OC:Connection error.";
+
+ /**
+ * Indicates a close of the connection to server.
+ */
+ public static final String CONNECTION_CLOSED = "#OC:Connection closed.";
+
+ /**
+ * Indicates establishment of a connection to server.
+ */
+ public static final String CONNECTION_ESTABLISHED = "#OC:Connection established.";
+
+ //Instance variables **********************************************
+
+ /**
+ * The service instance used to simulate multiple class inheritance.
+ */
+ private AdaptableClient service;
+
+ //Constructor *****************************************************
+
+ public ObservableClient(String host, int port)
+ {
+ service = new AdaptableClient(host, port, this);
+ }
+
+ //Instance methods ************************************************
+
+ /**
+ * Opens the connections with the server.
+ */
+ final public void openConnection() throws IOException
+ {
+ service.openConnection();
+ }
+
+ /**
+ * Closes the connection to the server.
+ */
+ final public void closeConnection() throws IOException
+ {
+ service.closeConnection();
+ }
+
+ /**
+ * Sends an object to the server. This is the only way that
+ * methods should communicate with the server.
+ *
+ * @param msg The message to be sent.
+ */
+ final public void sendToServer(Object msg) throws IOException
+ {
+ service.sendToServer(msg);
+ }
+
+// ACCESSING METHODS ------------------------------------------------
+
+ /**
+ * @used to find out if the client is connnected.
+ */
+ final public boolean isConnected()
+ {
+ return service.isConnected();
+ }
+
+ /**
+ * @return the port number.
+ */
+ final public int getPort()
+ {
+ return service.getPort();
+ }
+
+ /**
+ * Sets the server port number for the next connection.
+ * Only has effect if the client is not currently connected.
+ *
+ * @param port the port number.
+ */
+ final public void setPort(int port)
+ {
+ service.setPort(port);
+ }
+
+ /**
+ * @return the host name.
+ */
+ final public String getHost()
+ {
+ return service.getHost();
+ }
+
+ /**
+ * Sets the server host for the next connection.
+ * Only has effect if the client is not currently connected.
+ *
+ * @param host the host name.
+ */
+ final public void setHost(String host)
+ {
+ service.setHost(host);
+ }
+
+ /**
+ * @return the client's Inet address.
+ */
+ final public InetAddress getInetAddress()
+ {
+ return service.getInetAddress();
+ }
+
+
+ /**
+ * This method is used to handle messages from the server. This method
+ * can be overriden but should always call notifyObservers().
+ *
+ * @param message The message received from the client.
+ */
+ protected void handleMessageFromServer(Object message)
+ {
+ setChanged();
+ notifyObservers(message);
+ }
+
+ /**
+ * Hook method called after the connection has been closed.
+ */
+ protected void connectionClosed()
+ {
+ setChanged();
+ notifyObservers(CONNECTION_CLOSED);
+ }
+
+ /**
+ * Hook method called each time an exception
+ * is raised by the client listening thread.
+ *
+ * @param exception the exception raised.
+ */
+ protected void connectionException(Exception exception)
+ {
+ setChanged();
+ notifyObservers(CONNECTION_EXCEPTION);
+ }
+
+ /**
+ * Hook method called after a connection has been established.
+ */
+ protected void connectionEstablished()
+ {
+ setChanged();
+ notifyObservers(CONNECTION_ESTABLISHED);
+ }
+}
diff --git a/code/simplechat1/ocsf/index.html b/code/simplechat1/ocsf/index.html
new file mode 100644
index 0000000..13025a0
--- /dev/null
+++ b/code/simplechat1/ocsf/index.html
@@ -0,0 +1,22 @@
+
+
To install OCSF, simply compile all the .java files in the client and +server directories.
+ +OCSF is a Framework, so this directory contains no main program. To +learn how it works, consult the book +"Object-Oriented Software Engineering: Practical Software Development +using UML and Java" by Lethbridge and Laganière.
+ +To use OCSF, import the "ocsf.client" or "ocsf.server" package in your +application code. Make sure that the ocsf directory is in your classpath +when you compile your application.
+ + + + + diff --git a/code/simplechat1/ocsf/server/AbstractServer.java b/code/simplechat1/ocsf/server/AbstractServer.java new file mode 100644 index 0000000..0134b22 --- /dev/null +++ b/code/simplechat1/ocsf/server/AbstractServer.java @@ -0,0 +1,459 @@ +// This file contains material supporting section 3.8 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.net.*; +import java.util.*; +import java.io.*; + +/** +* The AbstractServer class maintains a thread that waits
+* for connection attempts from clients. When a connection attempt occurs
+* it creates a new ConnectionToClient instance which
+* runs as a thread. When a client is thus connected to the
+* server, the two programs can then exchange Object
+* instances.
+*
+* Method handleMessageFromClient must be defined by
+* a concrete subclass. Several other hook methods may also be
+* overriden.
+* +* Several public service methods are provided to applications that use +* this framework, and several hook methods are also available
+* +* Project Name: OCSF (Object Client-Server Framework)
+*
+* @author Dr Robert Laganière
+* @author Dr Timothy C. Lethbridge
+* @author François Bélanger
+* @author Paul Holden
+* @version February 2001 (2.12)
+* @see ocsf.server.ConnectionToClient
+*/
+public abstract class AbstractServer implements Runnable
+{
+ // INSTANCE VARIABLES *********************************************
+
+ /**
+ * The server socket: listens for clients who want to connect.
+ */
+ private ServerSocket serverSocket = null;
+
+ /**
+ * The connection listener thread.
+ */
+ private Thread connectionListener;
+
+ /**
+ * The port number
+ */
+ private int port;
+
+ /**
+ * The server timeout while for accepting connections.
+ * After timing out, the server will check to see if a command to
+ * stop the server has been issued; it not it will resume accepting
+ * connections.
+ * Set to half a second by default.
+ */
+ private int timeout = 500;
+
+ /**
+ * The maximum queue length; i.e. the maximum number of clients that
+ * can be waiting to connect.
+ * Set to 10 by default.
+ */
+ private int backlog = 10;
+
+ /**
+ * The thread group associated with client threads. Each member of the
+ * thread group is a
+*
+* Project Name: OCSF (Object Client-Server Framework)
+*
+* @author Dr. Robert Laganière
+* @version Febuary 2001
+*/
+class AdaptableServer extends AbstractServer
+{
+ //Instance variables **********************************************
+
+ /**
+ * The adapter used to simulate multiple class inheritance.
+ */
+ private ObservableServer server;
+
+// CONSTRUCTORS *****************************************************
+
+ /**
+ * Constructs the server adapter.
+ *
+ * @param host the server's host name.
+ * @param port the port number.
+ */
+ public AdaptableServer(int port, ObservableServer server)
+ {
+ super(port);
+ this.server = server;
+ }
+
+// OVERRIDDEN METHODS ---------
+
+ /**
+ * Hook method called each time a new client connection is
+ * accepted.
+ *
+ * @param client the connection connected to the client.
+ */
+ final protected void clientConnected(ConnectionToClient client)
+ {
+ server.clientConnected(client);
+ }
+
+ /**
+ * Hook method called each time a client disconnects.
+ *
+ * @param client the connection with the client.
+ */
+ final protected void clientDisconnected(ConnectionToClient client)
+ {
+ server.clientDisconnected(client);
+ }
+
+ /**
+ * Hook method called each time an exception
+ * is raised in a client thread.
+ *
+ * @param client the client that raised the exception.
+ * @param exception the exception raised.
+ */
+ final protected void clientException(ConnectionToClient client,
+ Throwable exception)
+ {
+ server.clientException(client, exception);
+ }
+
+ /**
+ * Hook method called when the server stops accepting
+ * connections because an exception has been raised.
+ *
+ * @param exception the exception raised.
+ */
+ final protected void listeningException(Throwable exception)
+ {
+ server.listeningException(exception);
+ }
+
+ /**
+ * Hook method called when the server stops accepting
+ * connections.
+ */
+ final protected void serverStopped()
+ {
+ server.serverStopped();
+ }
+
+ /**
+ * Hook method called when the server starts listening for
+ * connections.
+ */
+ final protected void serverStarted()
+ {
+ server.serverStarted();
+ }
+
+ /**
+ * Hook method called when the server is closed.
+ */
+ final protected void serverClosed()
+ {
+ server.serverClosed();
+ }
+
+ /**
+ * Handles a command sent from the client to the server.
+ *
+ * @param msg the message sent.
+ * @param client the connection connected to the client that
+ * sent the message.
+ */
+ final protected void handleMessageFromClient(Object msg,
+ ConnectionToClient client)
+ {
+ server.handleMessageFromClient(msg, client);
+ }
+}
diff --git a/code/simplechat1/ocsf/server/ConnectionToClient.java b/code/simplechat1/ocsf/server/ConnectionToClient.java
new file mode 100644
index 0000000..4394b37
--- /dev/null
+++ b/code/simplechat1/ocsf/server/ConnectionToClient.java
@@ -0,0 +1,270 @@
+// This file contains material supporting section 3.8 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.io.*;
+import java.net.*;
+import java.util.HashMap;
+
+/**
+ * An instance of this class is created by the server when a client connects. It
+ * accepts messages coming from the client and is responsible for sending data
+ * to the client since the socket is private to this class. The AbstractServer
+ * contains a set of instances of this class and is responsible for adding and
+ * deleting them.
+ *
+ * Project Name: OCSF (Object Client-Server Framework)
+ *
+ *
+ * @author Dr Robert Laganière
+ * @author Dr Timothy C. Lethbridge
+ * @author François Bélanger
+ * @author Paul Holden
+ * @version February 2001 (2.12)
+ */
+public class ConnectionToClient extends Thread {
+ // INSTANCE VARIABLES ***********************************************
+
+ /**
+ * A reference to the Server that created this instance.
+ */
+ private AbstractServer server;
+
+ /**
+ * Sockets are used in the operating system as channels of communication
+ * between two processes.
+ *
+ * @see java.net.Socket
+ */
+ private Socket clientSocket;
+
+ /**
+ * Stream used to read from the client.
+ */
+ private ObjectInputStream input;
+
+ /**
+ * Stream used to write to the client.
+ */
+ private ObjectOutputStream output;
+
+ /**
+ * Indicates if the thread is ready to stop. Set to true when closing of the
+ * connection is initiated.
+ */
+ private boolean readyToStop;
+
+ /**
+ * Map to save information about the client such as its login ID. The
+ * initial size of the map is small since it is not expected that concrete
+ * servers will want to store many different types of information about each
+ * client. Used by the setInfo and getInfo methods.
+ */
+ private HashMap savedInfo = new HashMap(10);
+
+ // CONSTRUCTORS *****************************************************
+
+ /**
+ * Constructs a new connection to a client.
+ *
+ * @param group
+ * the thread groupSystem.out.println("Client at "+ client +
+ * "connected"); that contains the connections.
+ * @param clientSocket
+ * contains the client's socket.
+ * @param server
+ * a reference to the server that created this instance
+ * @exception IOException
+ * if an I/O error occur when creating the connection.
+ */
+ ConnectionToClient(ThreadGroup group, Socket clientSocket, AbstractServer server) throws IOException {
+ super(group, (Runnable) null);
+ // Initialize variables
+ this.clientSocket = clientSocket;
+ this.server = server;
+
+ clientSocket.setSoTimeout(0); // make sure timeout is infinite
+
+ // Initialize the objects streams
+ try {
+ input = new ObjectInputStream(clientSocket.getInputStream());
+ output = new ObjectOutputStream(clientSocket.getOutputStream());
+ } catch (IOException ex) {
+ try {
+ closeAll();
+ } catch (Exception exc) {
+ }
+
+ throw ex; // Rethrow the exception.
+ }
+
+ readyToStop = false;
+ start(); // Start the thread waits for data from the socket
+ }
+
+ // INSTANCE METHODS *************************************************
+
+ /**
+ * Sends an object to the client.
+ *
+ * @param msg
+ * the message to be sent.
+ * @exception IOException
+ * if an I/O error occur when sending the message.
+ */
+ final public void sendToClient(Object msg) throws IOException {
+ if (clientSocket == null || output == null)
+ throw new SocketException("socket does not exist");
+
+ output.writeObject(msg);
+ }
+
+ /**
+ * Reset the output stream so we can use the same
+ * buffer repeatedly. This would not normally be used, but is necessary
+ * in some circumstances when Java refuses to send data that it thinks has been sent.
+ */
+ final public void forceResetAfterSend() throws IOException {
+ output.reset();
+ }
+
+ /**
+ * Closes the client. If the connection is already closed, this call has no
+ * effect.
+ *
+ * @exception IOException
+ * if an error occurs when closing the socket.
+ */
+ final public void close() throws IOException {
+ readyToStop = true; // Set the flag that tells the thread to stop
+
+ try {
+ closeAll();
+ } finally {
+ server.clientDisconnected(this);
+ }
+ }
+
+ // ACCESSING METHODS ------------------------------------------------
+
+ /**
+ * Returns the address of the client.
+ *
+ * @return the client's Internet address.
+ */
+ final public InetAddress getInetAddress() {
+ return clientSocket == null ? null : clientSocket.getInetAddress();
+ }
+
+ /**
+ * Returns a string representation of the client.
+ *
+ * @return the client's description.
+ */
+ public String toString() {
+ return clientSocket == null ? null : clientSocket.getInetAddress().getHostName() + " ("
+ + clientSocket.getInetAddress().getHostAddress() + ")";
+ }
+
+ /**
+ * Saves arbitrary information about this client. Designed to be used by
+ * concrete subclasses of AbstractServer. Based on a hash map.
+ *
+ * @param infoType
+ * identifies the type of information
+ * @param info
+ * the information itself.
+ */
+ public void setInfo(String infoType, Object info) {
+ savedInfo.put(infoType, info);
+ }
+
+ /**
+ * Returns information about the client saved using setInfo. Based on a hash
+ * map.
+ *
+ * @param infoType
+ * identifies the type of information
+ */
+ public Object getInfo(String infoType) {
+ return savedInfo.get(infoType);
+ }
+
+ // RUN METHOD -------------------------------------------------------
+
+ /**
+ * Constantly reads the client's input stream. Sends all objects that are
+ * read to the server. Not to be called.
+ */
+ final public void run() {
+ server.clientConnected(this);
+
+ // This loop reads the input stream and responds to messages
+ // from clients
+ try {
+ // The message from the client
+ Object msg;
+
+ while (!readyToStop) {
+ // This block waits until it reads a message from the client
+ // and then sends it for handling by the server
+ msg = input.readObject();
+ server.receiveMessageFromClient(msg, this);
+ }
+ } catch (Exception exception) {
+ if (!readyToStop) {
+ try {
+ closeAll();
+ } catch (Exception ex) {
+ }
+
+ server.clientException(this, exception);
+ }
+ }
+ }
+
+ // METHODS TO BE USED FROM WITHIN THE FRAMEWORK ONLY ----------------
+
+ /**
+ * Closes all connection to the server.
+ *
+ * @exception IOException
+ * if an I/O error occur when closing the connection.
+ */
+ private void closeAll() throws IOException {
+ try {
+ // Close the socket
+ if (clientSocket != null)
+ clientSocket.close();
+
+ // Close the output stream
+ if (output != null)
+ output.close();
+
+ // Close the input stream
+ if (input != null)
+ input.close();
+ } finally {
+ // Set the streams and the sockets to NULL no matter what
+ // Doing so allows, but does not require, any finalizers
+ // of these objects to reclaim system resources if and
+ // when they are garbage collected.
+ output = null;
+ input = null;
+ clientSocket = null;
+ }
+ }
+
+ /**
+ * This method is called by garbage collection.
+ */
+ protected void finalize() {
+ try {
+ closeAll();
+ } catch (IOException e) {
+ }
+ }
+}
+// End of ConnectionToClient class
\ No newline at end of file
diff --git a/code/simplechat1/ocsf/server/ObservableOriginatorServer.java b/code/simplechat1/ocsf/server/ObservableOriginatorServer.java
new file mode 100644
index 0000000..4333694
--- /dev/null
+++ b/code/simplechat1/ocsf/server/ObservableOriginatorServer.java
@@ -0,0 +1,165 @@
+// This file contains material supporting the textbook:
+// "Object Oriented Software Engineering" and is issued under the open-source
+// license found at www.lloseng.com
+
+package ocsf.server;
+
+/**
+* The
+*
+* @author Dr Robert Laganière
+* @author Dr Timothy C. Lethbridge
+* @author François Bélanger
+* @author Paul Holden
+* @version February 2001 (2.12)
+* @see ocsf.server.OriginatorMessage
+*/
+public class ObservableOriginatorServer extends ObservableServer
+{
+ // Constructor ******************************************************
+
+ /**
+ * Constructs a new server.
+ *
+ * @param port the port on which to listen.
+ */
+ public ObservableOriginatorServer(int port)
+ {
+ super(port);
+ }
+
+ // Instance methods ************************************************
+
+ /**
+ * This method is used to handle messages coming from the client.
+ * Observers are notfied by receiveing an instance of OriginatorMessage
+ * that contains both the message received and a reference to the
+ * client who sent the message.
+ *
+ * @param message The message received from the client.
+ * @param client The connection to the client.
+ */
+ protected synchronized void handleMessageFromClient
+ (Object message, ConnectionToClient client)
+ {
+ setChanged();
+ notifyObservers(new OriginatorMessage(client, message));
+ }
+
+ /**
+ * Method called each time a new client connection is
+ * accepted. It notifies observers by sending an
+ * ConnectionToClient .
+ */
+ private ThreadGroup clientThreadGroup;
+
+ /**
+ * Indicates if the listening thread is ready to stop. Set to
+ * false by default.
+ */
+ private boolean readyToStop = false;
+
+
+// CONSTRUCTOR ******************************************************
+
+ /**
+ * Constructs a new server.
+ *
+ * @param port the port number on which to listen.
+ */
+ public AbstractServer(int port)
+ {
+ this.port = port;
+
+ this.clientThreadGroup =
+ new ThreadGroup("ConnectionToClient threads")
+ {
+ // All uncaught exceptions in connection threads will
+ // be sent to the clientException callback method.
+ public void uncaughtException(
+ Thread thread, Throwable exception)
+ {
+ clientException((ConnectionToClient)thread, exception);
+ }
+ };
+ }
+
+
+// INSTANCE METHODS *************************************************
+
+ /**
+ * Begins the thread that waits for new clients.
+ * If the server is already in listening mode, this
+ * call has no effect.
+ *
+ * @exception IOException if an I/O error occurs
+ * when creating the server socket.
+ */
+ final public void listen() throws IOException
+ {
+ if (!isListening())
+ {
+ if (serverSocket == null)
+ {
+ serverSocket = new ServerSocket(getPort(), backlog);
+ }
+
+ serverSocket.setSoTimeout(timeout);
+ readyToStop = false;
+ connectionListener = new Thread(this);
+ connectionListener.start();
+ }
+ }
+
+ /**
+ * Causes the server to stop accepting new connections.
+ */
+ final public void stopListening()
+ {
+ readyToStop = true;
+ }
+
+ /**
+ * Closes the server socket and the connections with all clients.
+ * Any exception thrown while closing a client is ignored.
+ * If one wishes to catch these exceptions, then clients
+ * should be individually closed before calling this method.
+ * The method also stops listening if this thread is running.
+ * If the server is already closed, this
+ * call has no effect.
+ *
+ * @exception IOException if an I/O error occurs while
+ * closing the server socket.
+ */
+ final synchronized public void close() throws IOException
+ {
+ if (serverSocket == null)
+ return;
+ stopListening();
+ try
+ {
+ serverSocket.close();
+ }
+ finally
+ {
+ // Close the client sockets of the already connected clients
+ Thread[] clientThreadList = getClientConnections();
+ for (int i=0; iConnectionToClient instances.
+ */
+ synchronized final public Thread[] getClientConnections()
+ {
+ Thread[] clientThreadList = new
+ Thread[clientThreadGroup.activeCount()];
+
+ clientThreadGroup.enumerate(clientThreadList);
+
+ return clientThreadList;
+ }
+
+ /**
+ * Counts the number of clients currently connected.
+ *
+ * @return the number of clients currently connected.
+ */
+ final public int getNumberOfClients()
+ {
+ return clientThreadGroup.activeCount();
+ }
+
+ /**
+ * Returns the port number.
+ *
+ * @return the port number.
+ */
+ final public int getPort()
+ {
+ return port;
+ }
+
+ /**
+ * Sets the port number for the next connection.
+ * The server must be closed and restarted for the port
+ * change to be in effect.
+ *
+ * @param port the port number.
+ */
+ final public void setPort(int port)
+ {
+ this.port = port;
+ }
+
+ /**
+ * Sets the timeout time when accepting connections.
+ * The default is half a second. This means that stopping the
+ * server may take up to timeout duration to actually stop.
+ * The server must be stopped and restarted for the timeout
+ * change to be effective.
+ *
+ * @param timeout the timeout time in ms.
+ */
+ final public void setTimeout(int timeout)
+ {
+ this.timeout = timeout;
+ }
+
+ /**
+ * Sets the maximum number of waiting connections accepted by the
+ * operating system. The default is 20.
+ * The server must be closed and restarted for the backlog
+ * change to be in effect.
+ *
+ * @param backlog the maximum number of connections.
+ */
+ final public void setBacklog(int backlog)
+ {
+ this.backlog = backlog;
+ }
+
+// RUN METHOD -------------------------------------------------------
+
+ /**
+ * Runs the listening thread that allows clients to connect.
+ * Not to be called.
+ */
+ final public void run()
+ {
+ // call the hook method to notify that the server is starting
+ serverStarted();
+
+ try
+ {
+ // Repeatedly waits for a new client connection, accepts it, and
+ // starts a new thread to handle data exchange.
+ while(!readyToStop)
+ {
+ try
+ {
+ // Wait here for new connection attempts, or a timeout
+ Socket clientSocket = serverSocket.accept();
+
+ // When a client is accepted, create a thread to handle
+ // the data exchange, then add it to thread group
+
+ synchronized(this)
+ {
+ ConnectionToClient c = new ConnectionToClient(
+ this.clientThreadGroup, clientSocket, this);
+ }
+ }
+ catch (InterruptedIOException exception)
+ {
+ // This will be thrown when a timeout occurs.
+ // The server will continue to listen if not ready to stop.
+ }
+ }
+
+ // call the hook method to notify that the server has stopped
+ serverStopped();
+ }
+ catch (IOException exception)
+ {
+ if (!readyToStop)
+ {
+ // Closing the socket must have thrown a SocketException
+ listeningException(exception);
+ }
+ else
+ {
+ serverStopped();
+ }
+ }
+ finally
+ {
+ readyToStop = true;
+ connectionListener = null;
+ }
+ }
+
+
+// METHODS DESIGNED TO BE OVERRIDDEN BY CONCRETE SUBCLASSES ---------
+
+ /**
+ * Hook method called each time a new client connection is
+ * accepted. The default implementation does nothing.
+ * @param client the connection connected to the client.
+ */
+ protected void clientConnected(ConnectionToClient client) {}
+
+ /**
+ * Hook method called each time a client disconnects.
+ * The default implementation does nothing. The method
+ * may be overridden by subclasses but should remains synchronized.
+ *
+ * @param client the connection with the client.
+ */
+ synchronized protected void clientDisconnected(
+ ConnectionToClient client) {}
+
+ /**
+ * Hook method called each time an exception is thrown in a
+ * ConnectionToClient thread.
+ * The method may be overridden by subclasses but should remains
+ * synchronized.
+ *
+ * @param client the client that raised the exception.
+ * @param Throwable the exception thrown.
+ */
+ synchronized protected void clientException(
+ ConnectionToClient client, Throwable exception) {}
+
+ /**
+ * Hook method called when the server stops accepting
+ * connections because an exception has been raised.
+ * The default implementation does nothing.
+ * This method may be overriden by subclasses.
+ *
+ * @param exception the exception raised.
+ */
+ protected void listeningException(Throwable exception) {}
+
+ /**
+ * Hook method called when the server starts listening for
+ * connections. The default implementation does nothing.
+ * The method may be overridden by subclasses.
+ */
+ protected void serverStarted() {}
+
+ /**
+ * Hook method called when the server stops accepting
+ * connections. The default implementation
+ * does nothing. This method may be overriden by subclasses.
+ */
+ protected void serverStopped() {}
+
+ /**
+ * Hook method called when the server is clased.
+ * The default implementation does nothing. This method may be
+ * overriden by subclasses. When the server is closed while still
+ * listening, serverStopped() will also be called.
+ */
+ protected void serverClosed() {}
+
+ /**
+ * Handles a command sent from one client to the server.
+ * This MUST be implemented by subclasses, who should respond to
+ * messages.
+ * This method is called by a synchronized method so it is also
+ * implcitly synchronized.
+ *
+ * @param msg the message sent.
+ * @param client the connection connected to the client that
+ * sent the message.
+ */
+ protected abstract void handleMessageFromClient(
+ Object msg, ConnectionToClient client);
+
+
+// METHODS TO BE USED FROM WITHIN THE FRAMEWORK ONLY ----------------
+
+ /**
+ * Receives a command sent from the client to the server.
+ * Called by the run method of ConnectionToClient
+ * instances that are watching for messages coming from the server
+ * This method is synchronized to ensure that whatever effects it has
+ * do not conflict with work being done by other threads. The method
+ * simply calls the handleMessageFromClient slot method.
+ *
+ * @param msg the message sent.
+ * @param client the connection connected to the client that
+ * sent the message.
+ */
+ final synchronized void receiveMessageFromClient(
+ Object msg, ConnectionToClient client)
+ {
+ this.handleMessageFromClient(msg, client);
+ }
+}
+// End of AbstractServer Class
diff --git a/code/simplechat1/ocsf/server/AdaptableServer.java b/code/simplechat1/ocsf/server/AdaptableServer.java
new file mode 100644
index 0000000..24462d4
--- /dev/null
+++ b/code/simplechat1/ocsf/server/AdaptableServer.java
@@ -0,0 +1,129 @@
+// 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.io.*;
+import java.net.*;
+import java.util.*;
+
+/**
+* The AdaptableServer is an adapter class
+* that extends the AbstractServer class in place of
+* the AbstractObservableServer . ObservableOriginatorServer is a subclass
+* of ObservableServer that sends
+* OriginatorMessage instances to its observers.
+* This class should be used when the observers need to know
+* the orginator of the messages received. The originator
+* is null when the message sent concerns the server.
+*
+* Project Name: OCSF (Object Client-Server Framework) OriginatorMessage instance
+ * containing a reference to that client and
+ * the message defined by the static variable CLIENT_CONNECTED.
+ *
+ * @param client the connection connected to the client.
+ */
+ protected synchronized void clientConnected(ConnectionToClient client)
+ {
+ setChanged();
+ notifyObservers(new OriginatorMessage(client, CLIENT_CONNECTED));
+ }
+
+ /**
+ * Method called each time a client connection is
+ * disconnected. It notifies observers by sending an
+ * OriginatorMessage instance
+ * containing a reference to that client and
+ * the message defined by the static variable CLIENT_DISCONNECTED.
+ *
+ * @param client the connection connected to the client.
+ */
+ synchronized protected void clientDisconnected(ConnectionToClient client)
+ {
+ setChanged();
+ notifyObservers(new OriginatorMessage(client, CLIENT_DISCONNECTED));
+ }
+
+
+ /**
+ * Method called each time an exception is raised
+ * by a client connection.
+ * It notifies observers by sending an
+ * OriginatorMessage instance
+ * containing a reference to that client and
+ * the message defined by the static variable CLIENT_EXCEPTION
+ * to which is appended the exception message.
+ *
+ * @param client the client that raised the exception.
+ * @param Throwable the exception thrown.
+ */
+ synchronized protected void clientException(
+ ConnectionToClient client, Throwable exception)
+ {
+ setChanged();
+ notifyObservers(
+ new OriginatorMessage(client,
+ CLIENT_EXCEPTION + exception.getMessage()));
+ }
+
+ /**
+ * Method called each time an exception is raised
+ * while listening.
+ * It notifies observers by sending an
+ * OriginatorMessage instance
+ * containing the message defined by the static variable LISTENING_EXCEPTION
+ * to which is appended the exception message.
+ * The originator is set to null.
+ *
+ * @param exception the exception raised.
+ */
+ protected synchronized void listeningException(Throwable exception)
+ {
+ setChanged();
+ notifyObservers(
+ new OriginatorMessage(null,
+ LISTENING_EXCEPTION + exception.getMessage()));
+ }
+
+ /**
+ * Method called each time the server is started.
+ * It notifies observers by sending an
+ * OriginatorMessage instance
+ * containing the message defined by the static variable SERVER_STARTED.
+ * The originator is set to null.
+ */
+ protected synchronized void serverStarted()
+ {
+ setChanged();
+ notifyObservers(new OriginatorMessage(null, SERVER_STARTED));
+ }
+
+ /**
+ * Method called each time the server is stopped.
+ * It notifies observers by sending an
+ * OriginatorMessage instance
+ * containing the message defined by the static variable SERVER_STOPPED.
+ * The originator is set to null.
+ */
+ synchronized protected void serverStopped()
+ {
+ setChanged();
+ notifyObservers(new OriginatorMessage(null, SERVER_STOPPED));
+ }
+
+ /**
+ * Method called each time the server is closed.
+ * It notifies observers by sending an
+ * OriginatorMessage instance
+ * containing the message defined by the static variable SERVER_CLOSED.
+ * The originator is set to null.
+ */
+ synchronized protected void serverClosed()
+ {
+ setChanged();
+ notifyObservers(new OriginatorMessage(null, SERVER_CLOSED));
+ }
+}
diff --git a/code/simplechat1/ocsf/server/ObservableServer.java b/code/simplechat1/ocsf/server/ObservableServer.java
new file mode 100644
index 0000000..5b16752
--- /dev/null
+++ b/code/simplechat1/ocsf/server/ObservableServer.java
@@ -0,0 +1,309 @@
+// 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);
+ }
+}
diff --git a/code/simplechat1/ocsf/server/OriginatorMessage.java b/code/simplechat1/ocsf/server/OriginatorMessage.java
new file mode 100644
index 0000000..a25d2b4
--- /dev/null
+++ b/code/simplechat1/ocsf/server/OriginatorMessage.java
@@ -0,0 +1,61 @@
+// This file contains material supporting the textbook:
+// "Object Oriented Software Engineering" and is issued under the open-source
+// license found at www.lloseng.com
+
+package ocsf.server;
+
+/**
+ * A message class used by the Observable layer of the OCSF in order to conserve
+ * information about the originator of a message.
+ *
+ * @author Dr. Robert Laganière
+ * @version July 2001
+ */
+public class OriginatorMessage
+{
+ /**
+ * The connection that originated the message
+ */
+ private ConnectionToClient originator;
+
+ /**
+ * The message.
+ */
+ private Object message;
+
+// Constructor ***************************************************************
+
+ /**
+ * Constructs an instance of an OriginatorMessage
+ *
+ * @param originator The client who created this message
+ * @param message The contents of the message
+ */
+ public OriginatorMessage(ConnectionToClient originator, Object message)
+ {
+ this.originator = originator;
+ this.message = message;
+ }
+
+// Accessor methods *********************************************************
+
+ /**
+ * Returns the originating connection.
+ *
+ * @return The connection from which the message originated.
+ */
+ public ConnectionToClient getOriginator()
+ {
+ return originator;
+ }
+
+ /**
+ * Returns the message's contents.
+ *
+ * @return The content of the message.
+ */
+ public Object getMessage()
+ {
+ return message;
+ }
+}