From aef4a5421908df2511280a9817567030f04ab38d Mon Sep 17 00:00:00 2001 From: jksza042 <55514862+jksza042@users.noreply.github.com> Date: Fri, 22 May 2020 19:19:25 -0400 Subject: [PATCH 01/13] Created Initial Files --- .../ocsf/client/AbstractClient.java | 333 +++++++++++++ .../ocsf/client/AdaptableClient.java | 82 ++++ .../ocsf/client/ObservableClient.java | 181 +++++++ code/simplechat1/ocsf/index.html | 22 + .../ocsf/server/AbstractServer.java | 459 ++++++++++++++++++ .../ocsf/server/AdaptableServer.java | 129 +++++ .../ocsf/server/ConnectionToClient.java | 270 +++++++++++ .../server/ObservableOriginatorServer.java | 165 +++++++ .../ocsf/server/ObservableServer.java | 309 ++++++++++++ .../ocsf/server/OriginatorMessage.java | 61 +++ 10 files changed, 2011 insertions(+) create mode 100644 code/simplechat1/ocsf/client/AbstractClient.java create mode 100644 code/simplechat1/ocsf/client/AdaptableClient.java create mode 100644 code/simplechat1/ocsf/client/ObservableClient.java create mode 100644 code/simplechat1/ocsf/index.html create mode 100644 code/simplechat1/ocsf/server/AbstractServer.java create mode 100644 code/simplechat1/ocsf/server/AdaptableServer.java create mode 100644 code/simplechat1/ocsf/server/ConnectionToClient.java create mode 100644 code/simplechat1/ocsf/server/ObservableOriginatorServer.java create mode 100644 code/simplechat1/ocsf/server/ObservableServer.java create mode 100644 code/simplechat1/ocsf/server/OriginatorMessage.java 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 @@ + + +Installing and Running OCSF + + + +

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.

+ +

Back to the source code page.

+ + + 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 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; iThread containing + * ConnectionToClient 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 .

+* +* 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 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)

+* +* @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 + * 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; + } +} From 455400b7d9415bcd1bec0ec2be60cb0ca2ce3619 Mon Sep 17 00:00:00 2001 From: jksza042 <55514862+jksza042@users.noreply.github.com> Date: Sat, 23 May 2020 01:40:09 -0400 Subject: [PATCH 02/13] Performed Phase-1 Testing Performed Phase-1 Testing and included the documentation --- code/simplechat1/TestLogs.txt | 86 +++++++++++++++++++++++++++++++++++ code/simplechat1/Testcase | 0 code/simplechat1/java | 0 3 files changed, 86 insertions(+) create mode 100644 code/simplechat1/TestLogs.txt create mode 100644 code/simplechat1/Testcase create mode 100644 code/simplechat1/java diff --git a/code/simplechat1/TestLogs.txt b/code/simplechat1/TestLogs.txt new file mode 100644 index 0000000..2e4bd2b --- /dev/null +++ b/code/simplechat1/TestLogs.txt @@ -0,0 +1,86 @@ +Client 3 + +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat1>java ClientConsole +Testcase 1007! +> Testcase 1007! +> Could not send message to server. Terminating client. + +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat1> + +Client 2 + +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat1>java ClientConsole +Test> Testcase 1007! + +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat1> + +Client 1 + +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat1>java ClientConsole +Currently on Testcase 1004 +> Currently on Testcase 1004 +Testcase 1005 +> Could not send message to server. Terminating client. + +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat1>java ClientConsole + +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat1>java ClientConsole +This is Testcase 1006 +> This is Testcase 1006 +> Testcase 1007! +> Could not send message to server. Terminating client. + +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat1>C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat1>java ClientConsole +'C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat1' is not recognized as an internal or external command, +operable program or batch file. + +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat1>Test> Testcase 1007! +'Test' is not recognized as an internal or external command, +operable program or batch file. + +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat1> +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat1>java ClientConsole +FinFin + + +FIniewfnoewf> FinFin +> +> +Done +> FIniewfnoewfDone +d +> d +> Final FFFinTestcase 1013, final test +e +> Could not send message to server. Terminating client. + +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat1> + +Server: + +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat1>java EchoServer +Server listening for connections on port 5555 + +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat1>java ClientConsole +Error: Can't setup connection! Terminating client. + +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat1>java EchoServer +Server listening for connections on port 5555 +Message received: Currently on Testcase 1004 from 127.0.0.1 (127.0.0.1) + +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat1>java EchoServer +Server listening for connections on port 5555 +Message received: null from 127.0.0.1 (127.0.0.1) +Message received: This is Testcase 1006 from 127.0.0.1 (127.0.0.1) +Message received: Testcase 1007! from 127.0.0.1 (127.0.0.1) +Message received: null from 127.0.0.1 (127.0.0.1) +Message received: This message is from my Desktop to my Laptop! from Nev-PC (192.168.2.29) +Message received: null from Nev-PC (192.168.2.29) +Message received: FinFin from 127.0.0.1 (127.0.0.1) +Message received: from 127.0.0.1 (127.0.0.1) +Message received: from 127.0.0.1 (127.0.0.1) +Message received: FIniewfnoewfDone from 127.0.0.1 (127.0.0.1) +Message received: d from 127.0.0.1 (127.0.0.1) +Message received: Final FFFinTestcase 1013, final test from 127.0.0.1 (127.0.0.1) + +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat1> diff --git a/code/simplechat1/Testcase b/code/simplechat1/Testcase new file mode 100644 index 0000000..e69de29 diff --git a/code/simplechat1/java b/code/simplechat1/java new file mode 100644 index 0000000..e69de29 From 189fcb78a71bfd27ca2b8817bdb8b5072a73e839 Mon Sep 17 00:00:00 2001 From: jksza042 <55514862+jksza042@users.noreply.github.com> Date: Sat, 23 May 2020 15:39:01 -0400 Subject: [PATCH 03/13] Initial Files Added + Modified Client to Shut Down on Server Shut Down and use a Specified Port Started Phase 2 of Simplechat and completed the requirements of 5 a) and b) --- code/simplechat2/ClientConsole.java | 132 +++++ code/simplechat2/EchoServer.java | 109 +++++ code/simplechat2/client/ChatClient.java | 107 ++++ code/simplechat2/common/ChatIF.java | 22 + code/simplechat2/index.html | 34 ++ code/simplechat2/java | 0 .../ocsf/client/AbstractClient.java | 333 +++++++++++++ .../ocsf/client/AdaptableClient.java | 82 ++++ .../ocsf/client/ObservableClient.java | 181 +++++++ code/simplechat2/ocsf/index.html | 22 + .../ocsf/server/AbstractServer.java | 459 ++++++++++++++++++ .../ocsf/server/AdaptableServer.java | 129 +++++ .../ocsf/server/ConnectionToClient.java | 270 +++++++++++ .../server/ObservableOriginatorServer.java | 165 +++++++ .../ocsf/server/ObservableServer.java | 309 ++++++++++++ .../ocsf/server/OriginatorMessage.java | 61 +++ 16 files changed, 2415 insertions(+) create mode 100644 code/simplechat2/ClientConsole.java create mode 100644 code/simplechat2/EchoServer.java create mode 100644 code/simplechat2/client/ChatClient.java create mode 100644 code/simplechat2/common/ChatIF.java create mode 100644 code/simplechat2/index.html create mode 100644 code/simplechat2/java create mode 100644 code/simplechat2/ocsf/client/AbstractClient.java create mode 100644 code/simplechat2/ocsf/client/AdaptableClient.java create mode 100644 code/simplechat2/ocsf/client/ObservableClient.java create mode 100644 code/simplechat2/ocsf/index.html create mode 100644 code/simplechat2/ocsf/server/AbstractServer.java create mode 100644 code/simplechat2/ocsf/server/AdaptableServer.java create mode 100644 code/simplechat2/ocsf/server/ConnectionToClient.java create mode 100644 code/simplechat2/ocsf/server/ObservableOriginatorServer.java create mode 100644 code/simplechat2/ocsf/server/ObservableServer.java create mode 100644 code/simplechat2/ocsf/server/OriginatorMessage.java diff --git a/code/simplechat2/ClientConsole.java b/code/simplechat2/ClientConsole.java new file mode 100644 index 0000000..7d7c2b6 --- /dev/null +++ b/code/simplechat2/ClientConsole.java @@ -0,0 +1,132 @@ +// 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 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 ClientConsole implements ChatIF +{ + //Class variables ************************************************* + + /** + * The default port to connect on. + */ + final public static int DEFAULT_PORT = 5555; + + //Instance variables ********************************************** + + /** + * The instance of the client that created this ConsoleChat. + */ + ChatClient client; + + + //Constructors **************************************************** + + /** + * Constructs an instance of the ClientConsole UI. + * + * @param host The host to connect to. + * @param port The port to connect on. + */ + public ClientConsole(String host, int port) + { + try + { + client= new ChatClient(host, port, this); + } + catch(IOException exception) + { + System.out.println("Error: Can't setup connection!" + + " Terminating client."); + System.exit(1); + } + } + + + //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(); + client.handleMessageFromClientUI(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) + { + String host = ""; + int port = 0; //The port number + + try + { + host = args[0]; + } + catch(ArrayIndexOutOfBoundsException e) + { + host = "localhost"; + + } + + try + { + port = Integer.parseInt(args[1]); + } + catch(ArrayIndexOutOfBoundsException e){ + port = DEFAULT_PORT; + } + + ClientConsole chat= new ClientConsole(host, port); + chat.accept(); //Wait for console data + } +} +//End of ConsoleChat class diff --git a/code/simplechat2/EchoServer.java b/code/simplechat2/EchoServer.java new file mode 100644 index 0000000..d4f3a1a --- /dev/null +++ b/code/simplechat2/EchoServer.java @@ -0,0 +1,109 @@ +// 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 ocsf.server.*; + +/** + * This class overrides some of the methods in the abstract + * superclass in order to give more functionality to the server. + * + * @author Dr Timothy C. Lethbridge + * @author Dr Robert Laganière + * @author François Bélanger + * @author Paul Holden + * @version July 2000 + */ +public class EchoServer extends AbstractServer +{ + //Class variables ************************************************* + + /** + * The default port to listen on. + */ + final public static int DEFAULT_PORT = 5555; + + //Constructors **************************************************** + + /** + * Constructs an instance of the echo server. + * + * @param port The port number to connect on. + */ + public EchoServer(int port) + { + super(port); + } + + + //Instance methods ************************************************ + + /** + * This method handles any messages received from the client. + * + * @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); + } + + /** + * 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()); + } + + /** + * This method overrides the one in the superclass. Called + * when the server stops listening for connections. + */ + protected void serverStopped() + { + System.out.println + ("Server has stopped listening for connections."); + } + + //Class methods *************************************************** + + /** + * This method is responsible for the creation of + * the server instance (there is no UI in this phase). + * + * @param args[0] The port number to listen on. Defaults to 5555 + * if no argument is entered. + */ + public static void main(String[] args) + { + int port = 0; //Port to listen on + + try + { + port = Integer.parseInt(args[0]); //Get port from command line + } + catch(Throwable t) + { + port = DEFAULT_PORT; //Set port to 5555 + } + + EchoServer sv = new EchoServer(port); + + try + { + sv.listen(); //Start listening for connections + } + catch (Exception ex) + { + System.out.println("ERROR - Could not listen for clients!"); + } + } +} +//End of EchoServer class diff --git a/code/simplechat2/client/ChatClient.java b/code/simplechat2/client/ChatClient.java new file mode 100644 index 0000000..fb0e0ea --- /dev/null +++ b/code/simplechat2/client/ChatClient.java @@ -0,0 +1,107 @@ +// 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 client; + +import ocsf.client.*; +import common.*; +import java.io.*; + +/** + * This class overrides some of the methods defined in the abstract + * superclass in order to give more functionality to the client. + * + * @author Dr Timothy C. Lethbridge + * @author Dr Robert Laganiè + * @author François Bélanger + * @version July 2000 + */ +public class ChatClient extends AbstractClient +{ + //Instance variables ********************************************** + + /** + * The interface type variable. It allows the implementation of + * the display method in the client. + */ + ChatIF clientUI; + + + //Constructors **************************************************** + + /** + * Constructs an instance of the chat client. + * + * @param host The server to connect to. + * @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; + openConnection(); + } + + + //Instance methods ************************************************ + + /** + * This method handles all data that comes in from the server. + * + * @param msg The message from the server. + */ + public void handleMessageFromServer(Object msg) + { + clientUI.display(msg.toString()); + } + + /** + * This method handles all data coming from the UI + * + * @param message The message from the UI. + */ + public void handleMessageFromClientUI(String message) + { + try + { + sendToServer(message); + } + catch(IOException e) + { + clientUI.display + ("Could not send message to server. Terminating client."); + quit(); + } + } + + /** + * This method terminates the client. + */ + public void quit() + { + try + { + closeConnection(); + } + catch(IOException e) {} + System.exit(0); + } + + //Terminates Client when server terminates + public void connectionException(Exception Error){ + + quit(); + + } + + //Displays Message upon client termination + public void connectionClosed(){ + System.out.println("The server has closed"); + } + +} +//End of ChatClient class diff --git a/code/simplechat2/common/ChatIF.java b/code/simplechat2/common/ChatIF.java new file mode 100644 index 0000000..45b04d4 --- /dev/null +++ b/code/simplechat2/common/ChatIF.java @@ -0,0 +1,22 @@ +// 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 common; + +/** + * This interface implements the abstract method used to display + * objects onto the client or server UIs. + * + * @author Dr Robert Laganière + * @author Dr Timothy C. Lethbridge + * @version July 2000 + */ +public interface ChatIF +{ + /** + * Method that when overriden is used to display objects onto + * a UI. + */ + public abstract void display(String message); +} diff --git a/code/simplechat2/index.html b/code/simplechat2/index.html new file mode 100644 index 0000000..f88583d --- /dev/null +++ b/code/simplechat2/index.html @@ -0,0 +1,34 @@ + + +Installing and Running SimpleChat, Phase 1 + + + +

To run the SimpleChat, you must first install ocsf, and make sure ocsf is in your classpath (or is a +subdirectory of simplechat1).

+ +

Then you must compile the .java files, including those in the +subdirectories.

+ +

To run the SimpleChat you must first start a server: "java EchoServer". +Then you start one or more clients: "java ClientConsole". To run a client +on a different machine, you enter "java ClientConsole serveraddress", where +serveraddress is the IP address or host name of the machine where the +server is runing. Once a client is running, you can type some text; the +text will be echoed to any other clients that are running.

+ +

To learn how this code works, consult the book "Object-Oriented Software Engineering: +Practical Software Development using UML and Java" by Lethbridge and +Laganière. The book contains many exercises where you will add +features to SimpleChat.

+ +

If the server won't start, it might be because the default 'port' number +of 5555 is already in use. Follow the exercises in the book to improve the +code and solve this problem.

+ +

Back to the source code page.

+ + + diff --git a/code/simplechat2/java b/code/simplechat2/java new file mode 100644 index 0000000..e69de29 diff --git a/code/simplechat2/ocsf/client/AbstractClient.java b/code/simplechat2/ocsf/client/AbstractClient.java new file mode 100644 index 0000000..e905636 --- /dev/null +++ b/code/simplechat2/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/simplechat2/ocsf/client/AdaptableClient.java b/code/simplechat2/ocsf/client/AdaptableClient.java new file mode 100644 index 0000000..143592e --- /dev/null +++ b/code/simplechat2/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/simplechat2/ocsf/client/ObservableClient.java b/code/simplechat2/ocsf/client/ObservableClient.java new file mode 100644 index 0000000..a816488 --- /dev/null +++ b/code/simplechat2/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/simplechat2/ocsf/index.html b/code/simplechat2/ocsf/index.html new file mode 100644 index 0000000..13025a0 --- /dev/null +++ b/code/simplechat2/ocsf/index.html @@ -0,0 +1,22 @@ + + +Installing and Running OCSF + + + +

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.

+ +

Back to the source code page.

+ + + diff --git a/code/simplechat2/ocsf/server/AbstractServer.java b/code/simplechat2/ocsf/server/AbstractServer.java new file mode 100644 index 0000000..0134b22 --- /dev/null +++ b/code/simplechat2/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 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; iThread containing + * ConnectionToClient 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/simplechat2/ocsf/server/AdaptableServer.java b/code/simplechat2/ocsf/server/AdaptableServer.java new file mode 100644 index 0000000..24462d4 --- /dev/null +++ b/code/simplechat2/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 .

+* +* 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/simplechat2/ocsf/server/ConnectionToClient.java b/code/simplechat2/ocsf/server/ConnectionToClient.java new file mode 100644 index 0000000..4394b37 --- /dev/null +++ b/code/simplechat2/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/simplechat2/ocsf/server/ObservableOriginatorServer.java b/code/simplechat2/ocsf/server/ObservableOriginatorServer.java new file mode 100644 index 0000000..4333694 --- /dev/null +++ b/code/simplechat2/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 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)

+* +* @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 + * 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/simplechat2/ocsf/server/ObservableServer.java b/code/simplechat2/ocsf/server/ObservableServer.java new file mode 100644 index 0000000..5b16752 --- /dev/null +++ b/code/simplechat2/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/simplechat2/ocsf/server/OriginatorMessage.java b/code/simplechat2/ocsf/server/OriginatorMessage.java new file mode 100644 index 0000000..a25d2b4 --- /dev/null +++ b/code/simplechat2/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; + } +} From bda6e28e0c38b19f23df1264d87ebbf7e6e35ae0 Mon Sep 17 00:00:00 2001 From: jksza042 <55514862+jksza042@users.noreply.github.com> Date: Sat, 23 May 2020 15:46:30 -0400 Subject: [PATCH 04/13] Implemented clientConnected and clientDisconnected Fulfilled the requirements of 5 c) --- code/simplechat2/EchoServer.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/code/simplechat2/EchoServer.java b/code/simplechat2/EchoServer.java index d4f3a1a..8b58f39 100644 --- a/code/simplechat2/EchoServer.java +++ b/code/simplechat2/EchoServer.java @@ -71,6 +71,18 @@ protected void serverStopped() System.out.println ("Server has stopped listening for connections."); } + + //Displays a message to all clients when a new client connects + public void clientConnected(ConnectionToClient client){ + String message = client + " has connected!"; + sendToAllClients(message); + } + + //Displays a message to all clients when a client disconnects + public void clientDisconnected(ConnectionToClient client){ + String message = client + "has disconnected"; + sendToAllClients(message); + } //Class methods *************************************************** From 1deaffb4a7a304eeb42a834c8397c2f07f0983fb Mon Sep 17 00:00:00 2001 From: jksza042 <55514862+jksza042@users.noreply.github.com> Date: Sat, 23 May 2020 16:39:51 -0400 Subject: [PATCH 05/13] Implemented Client Commands Satisfied 6 a) of the assignment --- code/simplechat2/client/ChatClient.java | 107 ++++++++++++++++++++++-- 1 file changed, 98 insertions(+), 9 deletions(-) diff --git a/code/simplechat2/client/ChatClient.java b/code/simplechat2/client/ChatClient.java index fb0e0ea..5065727 100644 --- a/code/simplechat2/client/ChatClient.java +++ b/code/simplechat2/client/ChatClient.java @@ -66,16 +66,104 @@ public void handleMessageFromServer(Object msg) */ public void handleMessageFromClientUI(String message) { - try - { - sendToServer(message); + + char commandKey = "#".charAt(0); + + if(message != null && message.charAt(0) == commandKey){ + + parseClientCommand(message); + } - catch(IOException e) - { - clientUI.display - ("Could not send message to server. Terminating client."); - quit(); + else{ + + try + { + sendToServer(message); + } + catch(IOException e) + { + clientUI.display + ("Could not send message to server. Terminating client."); + quit(); + } } + } + + private void parseClientCommand(String message){ + + String[] messageList = message.split(" "); + + switch(messageList[0]){ + case "#quit": + quit(); + break; + + case "#logoff": + if(!isConnected()){ + System.out.println("Error: Client Not Connected to a Server"); + } + else{ + try{ + closeConnection(); + } + catch(IOException e){ + System.out.println(e.toString()); + } + } + break; + + case "#sethost": + if(!isConnected()){ + setHost(messageList[1]); + } + else{ + System.out.println("Error: Cannot Change Host While Connected to a Server"); + } + break; + + case "#setport": + + if(!isConnected()){ + setPort(Integer.parseInt(messageList[1])); + } + else{ + System.out.println("Error: Cannot Change Port While Connected to a Server"); + } + + break; + + case "#login": + + if(!isConnected()){ + try{ + openConnection(); + } + catch(IOException e){ + System.out.println(e.toString()); + } + } + else{ + System.out.println("Error: Cannot login with an open connection"); + } + + break; + + case "#gethost": + System.out.println("Host: " + this.getHost()); + break; + + case "#getport": + System.out.println("Port: " + this.getPort()); + break; + + default: + System.out.println("Error: Command Not Recognized"); + + } + + + + } /** @@ -94,13 +182,14 @@ public void quit() //Terminates Client when server terminates public void connectionException(Exception Error){ + System.out.println("The server has closed"); quit(); } //Displays Message upon client termination public void connectionClosed(){ - System.out.println("The server has closed"); + } } From 10b5b29dbc843a97cd477639d2d8fb771e67ea95 Mon Sep 17 00:00:00 2001 From: jksza042 <55514862+jksza042@users.noreply.github.com> Date: Fri, 29 May 2020 04:16:47 -0400 Subject: [PATCH 06/13] Added Server-Side Chat Functionality Completed 6 b) --- code/simplechat2/EchoServer.java | 29 ++-------- code/simplechat2/ServerConsole.java | 82 +++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 25 deletions(-) create mode 100644 code/simplechat2/ServerConsole.java diff --git a/code/simplechat2/EchoServer.java b/code/simplechat2/EchoServer.java index 8b58f39..5b497c0 100644 --- a/code/simplechat2/EchoServer.java +++ b/code/simplechat2/EchoServer.java @@ -3,6 +3,7 @@ // license found at www.lloseng.com import java.io.*; +import common.*; import ocsf.server.*; /** @@ -23,6 +24,7 @@ public class EchoServer extends AbstractServer * The default port to listen on. */ final public static int DEFAULT_PORT = 5555; + ChatIF serverChat; //Constructors **************************************************** @@ -31,9 +33,10 @@ public class EchoServer extends AbstractServer * * @param port The port number to connect on. */ - public EchoServer(int port) + public EchoServer(int port, ChatIF serverChat) { super(port); + this.serverChat = serverChat; } @@ -93,29 +96,5 @@ public void clientDisconnected(ConnectionToClient client){ * @param args[0] The port number to listen on. Defaults to 5555 * if no argument is entered. */ - public static void main(String[] args) - { - int port = 0; //Port to listen on - - try - { - port = Integer.parseInt(args[0]); //Get port from command line - } - catch(Throwable t) - { - port = DEFAULT_PORT; //Set port to 5555 - } - - EchoServer sv = new EchoServer(port); - - try - { - sv.listen(); //Start listening for connections - } - catch (Exception ex) - { - System.out.println("ERROR - Could not listen for clients!"); - } - } } //End of EchoServer class diff --git a/code/simplechat2/ServerConsole.java b/code/simplechat2/ServerConsole.java new file mode 100644 index 0000000..e16789a --- /dev/null +++ b/code/simplechat2/ServerConsole.java @@ -0,0 +1,82 @@ +import java.io.*; +import common.ChatIF; + +public class ServerConsole implements ChatIF { + + //Instance Variables + EchoServer server; + int port; + + + //Constructors + + public ServerConsole(int port){ + + this.port = port; + + try{ + server = new EchoServer(port, this); + server.listen(); + } + catch(IOException e){ + System.out.println("Error: Cannot setup server"); + System.exit(1); + } + + } + + //Instance Methods + + public void display(String message){ + System.out.println(message); + } + + public void accept() + { + try + { + BufferedReader fromServer = + new BufferedReader(new InputStreamReader(System.in)); + String message; + + while (true) + { + message = fromServer.readLine(); + message = "SERVER MSG> " + message; + display(message); + server.sendToAllClients(message); + } + } + catch (Exception ex) + { + System.out.println("Unexpected error while reading from server!"); + } + } + + + //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 DEFAULT_PORT = 5555; //Default Port + int port = 0; //Port to listen on + + try + { + port = Integer.parseInt(args[0]); //Get port from command line + } + catch(Throwable t) + { + port = DEFAULT_PORT; //Set port to 5555 + } + + ServerConsole chat = new ServerConsole(port); + chat.accept(); //Wait for server data + } + } + From f812a5e13e7a691ca02bd609923bee07127e4260 Mon Sep 17 00:00:00 2001 From: jksza042 <55514862+jksza042@users.noreply.github.com> Date: Fri, 29 May 2020 05:36:31 -0400 Subject: [PATCH 07/13] Implemented Server Side Commands Completed 7 c) and changed the implementation of 7 b) to be more efficient and closer reflect the client solution --- code/simplechat2/EchoServer.java | 87 +++++++++++++++++++++++++++++ code/simplechat2/ServerConsole.java | 6 +- 2 files changed, 89 insertions(+), 4 deletions(-) diff --git a/code/simplechat2/EchoServer.java b/code/simplechat2/EchoServer.java index 5b497c0..d6d1976 100644 --- a/code/simplechat2/EchoServer.java +++ b/code/simplechat2/EchoServer.java @@ -25,6 +25,7 @@ public class EchoServer extends AbstractServer */ final public static int DEFAULT_PORT = 5555; ChatIF serverChat; + boolean isClosed; //Constructors **************************************************** @@ -37,6 +38,7 @@ public EchoServer(int port, ChatIF serverChat) { super(port); this.serverChat = serverChat; + isClosed = false; } @@ -63,6 +65,7 @@ protected void serverStarted() { System.out.println ("Server listening for connections on port " + getPort()); + isClosed = false; } /** @@ -86,6 +89,90 @@ public void clientDisconnected(ConnectionToClient client){ String message = client + "has disconnected"; sendToAllClients(message); } + + public void handleMessageFromServerUI(String message){ + + char commandKey = "#".charAt(0); + + if(message != null && message.charAt(0) == commandKey){ + + parseServerCommand(message); + + } + else{ + message = "SERVER MSG> " + message; + System.out.println(message); + sendToAllClients(message); + } + } + + private void parseServerCommand(String command){ + + String[] commandList = command.split(" "); + + switch(commandList[0]){ + + case "#quit": + System.exit(0); + break; + + case "#close": + try{ + close(); + } + catch(IOException e){ + System.out.println("Error: Unknown IOException"); + } + break; + + case "#setport": + + if(isClosed){ + + try{ + setPort(Integer.parseInt(commandList[1])); + } + catch(IndexOutOfBoundsException e){ + System.out.println("Error: Port Not Specified"); + } + catch(NumberFormatException ex){ + System.out.println("Error: Port Must be a Number"); + } + + } + else{ + System.out.println("Error: Server Not Closed"); + } + + break; + + case "#getport": + System.out.println("Port: " + getPort()); + break; + + case "#start": + try{ + listen(); + } + catch(IOException e){ + System.out.println("Error: Unknown IOException"); + } + break; + + case "#stop": + stopListening(); + break; + + default: + System.out.println("Error: Command not Recognized"); + + } + + } + + public void serverClosed(){ + isClosed = true; + } //Class methods *************************************************** diff --git a/code/simplechat2/ServerConsole.java b/code/simplechat2/ServerConsole.java index e16789a..645cd8d 100644 --- a/code/simplechat2/ServerConsole.java +++ b/code/simplechat2/ServerConsole.java @@ -40,11 +40,9 @@ public void accept() String message; while (true) - { + { message = fromServer.readLine(); - message = "SERVER MSG> " + message; - display(message); - server.sendToAllClients(message); + server.handleMessageFromServerUI(message); } } catch (Exception ex) From 144dff1645709646f14311775469b4ee916a1707 Mon Sep 17 00:00:00 2001 From: jksza042 <55514862+jksza042@users.noreply.github.com> Date: Fri, 29 May 2020 05:49:06 -0400 Subject: [PATCH 08/13] LoginID added Completed 7 a) and b) --- code/simplechat2/ClientConsole.java | 20 +++++++++++++++----- code/simplechat2/client/ChatClient.java | 6 +++++- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/code/simplechat2/ClientConsole.java b/code/simplechat2/ClientConsole.java index 7d7c2b6..5dd5ec1 100644 --- a/code/simplechat2/ClientConsole.java +++ b/code/simplechat2/ClientConsole.java @@ -41,11 +41,11 @@ 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 host, int port, String loginID) { try { - client= new ChatClient(host, port, this); + client= new ChatClient(loginID, host, port, this); } catch(IOException exception) { @@ -106,10 +106,20 @@ public static void main(String[] args) { String host = ""; int port = 0; //The port number + String loginID = ""; try { - host = args[0]; + loginID = args[0]; + } + catch(ArrayIndexOutOfBoundsException e){ + System.out.println("Error: No Login ID Provided"); + System.exit(0); + } + + try + { + host = args[1]; } catch(ArrayIndexOutOfBoundsException e) { @@ -119,13 +129,13 @@ public static void main(String[] args) try { - port = Integer.parseInt(args[1]); + port = Integer.parseInt(args[2]); } catch(ArrayIndexOutOfBoundsException e){ port = DEFAULT_PORT; } - ClientConsole chat= new ClientConsole(host, port); + ClientConsole chat= new ClientConsole(host, port, loginID); chat.accept(); //Wait for console data } } diff --git a/code/simplechat2/client/ChatClient.java b/code/simplechat2/client/ChatClient.java index 5065727..8e255e1 100644 --- a/code/simplechat2/client/ChatClient.java +++ b/code/simplechat2/client/ChatClient.java @@ -26,6 +26,7 @@ public class ChatClient extends AbstractClient * the display method in the client. */ ChatIF clientUI; + public String loginID; //Constructors **************************************************** @@ -38,12 +39,14 @@ public class ChatClient extends AbstractClient * @param clientUI The interface type variable. */ - public ChatClient(String host, int port, ChatIF clientUI) + 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("#login " + loginID); } @@ -137,6 +140,7 @@ private void parseClientCommand(String message){ if(!isConnected()){ try{ openConnection(); + sendToServer("#login " + loginID); } catch(IOException e){ System.out.println(e.toString()); From 9d42aa75917dc8a750f44e2e98bd03effb19b2ab Mon Sep 17 00:00:00 2001 From: jksza042 <55514862+jksza042@users.noreply.github.com> Date: Fri, 29 May 2020 06:26:39 -0400 Subject: [PATCH 09/13] Implemented LoginID on the Server-Side Completed 7 c) --- code/simplechat2/EchoServer.java | 44 +++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/code/simplechat2/EchoServer.java b/code/simplechat2/EchoServer.java index d6d1976..704455c 100644 --- a/code/simplechat2/EchoServer.java +++ b/code/simplechat2/EchoServer.java @@ -3,6 +3,8 @@ // license found at www.lloseng.com import java.io.*; +import java.sql.Connection; + import common.*; import ocsf.server.*; @@ -54,7 +56,47 @@ public EchoServer(int port, ChatIF serverChat) (Object msg, ConnectionToClient client) { System.out.println("Message received: " + msg + " from " + client); - this.sendToAllClients(msg); + + char commandKey = "#".charAt(0); + String msgString = msg.toString(); + + if(msg!= null && msgString.charAt(0) == commandKey){ + handleClientLogin(msgString, client); + } + else{ + msg = client.getInfo("loginID") + ": " + msg; + this.sendToAllClients(msg); + } + } + + private void handleClientLogin(String msg, ConnectionToClient client){ + + String[] loginList = msg.split(" "); + + switch(loginList[0]){ + case "#login": + if(client.getInfo("loginID") == null){ + client.setInfo("loginID", loginList[1]); + } + else{ + System.out.println("Error: Client Already Logged In"); + + try{ + client.sendToClient("Error: Client Already Logged In"); + client.close(); + } + catch(IOException e){ + System.out.println("Error: IOException Thrown on Client Invalid Login"); + } + + } + break; + + default: + System.out.println("Error: Unknown Client-Sent Command"); + } + + } /** From c98bf941aab82191de55b0c00935a3eb4a4f7f2b Mon Sep 17 00:00:00 2001 From: jksza042 <55514862+jksza042@users.noreply.github.com> Date: Fri, 29 May 2020 12:07:33 -0400 Subject: [PATCH 10/13] Made some changes to fix testcases --- code/simplechat2/ClientConsole.java | 18 ++++------- code/simplechat2/EchoServer.java | 40 ++++++++++++++++++++----- code/simplechat2/ServerConsole.java | 10 +++++-- code/simplechat2/client/ChatClient.java | 16 ++++++---- 4 files changed, 56 insertions(+), 28 deletions(-) diff --git a/code/simplechat2/ClientConsole.java b/code/simplechat2/ClientConsole.java index 5dd5ec1..3188c39 100644 --- a/code/simplechat2/ClientConsole.java +++ b/code/simplechat2/ClientConsole.java @@ -43,16 +43,9 @@ public class ClientConsole implements ChatIF */ public ClientConsole(String host, int port, String loginID) { - try - { - client= new ChatClient(loginID, host, port, this); - } - catch(IOException exception) - { - System.out.println("Error: Can't setup connection!" - + " Terminating client."); - System.exit(1); - } + + client= new ChatClient(loginID, host, port, this); + } @@ -78,8 +71,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!"); } } @@ -113,7 +105,7 @@ public static void main(String[] args) loginID = args[0]; } catch(ArrayIndexOutOfBoundsException e){ - System.out.println("Error: No Login ID Provided"); + System.out.println("ERROR - No login ID specified. Connection aborted."); System.exit(0); } diff --git a/code/simplechat2/EchoServer.java b/code/simplechat2/EchoServer.java index 704455c..c5b6c35 100644 --- a/code/simplechat2/EchoServer.java +++ b/code/simplechat2/EchoServer.java @@ -55,7 +55,7 @@ public EchoServer(int port, ChatIF serverChat) public void handleMessageFromClient (Object msg, ConnectionToClient client) { - System.out.println("Message received: " + msg + " from " + client); + System.out.println("Message received: " + msg + " from " + client.getInfo("loginID")); char commandKey = "#".charAt(0); String msgString = msg.toString(); @@ -64,7 +64,7 @@ public EchoServer(int port, ChatIF serverChat) handleClientLogin(msgString, client); } else{ - msg = client.getInfo("loginID") + ": " + msg; + msg = client.getInfo("loginID") + "> " + msg; this.sendToAllClients(msg); } } @@ -77,6 +77,10 @@ private void handleClientLogin(String msg, ConnectionToClient client){ case "#login": if(client.getInfo("loginID") == null){ client.setInfo("loginID", loginList[1]); + + String message = client.getInfo("loginID") + " has logged on."; + System.out.println(message); + sendToAllClients(message); } else{ System.out.println("Error: Client Already Logged In"); @@ -119,16 +123,17 @@ protected void serverStopped() System.out.println ("Server has stopped listening for connections."); } - - //Displays a message to all clients when a new client connects + public void clientConnected(ConnectionToClient client){ - String message = client + " has connected!"; - sendToAllClients(message); + + System.out.println("A new client is attempting to connect to the server."); + } //Displays a message to all clients when a client disconnects public void clientDisconnected(ConnectionToClient client){ - String message = client + "has disconnected"; + String message = client.getInfo("loginID") + " has disconnected"; + System.out.println(message); sendToAllClients(message); } @@ -160,6 +165,7 @@ private void parseServerCommand(String command){ case "#close": try{ + sendToAllClients("SERVER SHUTTING DOWN! DISCONNECTING!"); close(); } catch(IOException e){ @@ -203,6 +209,7 @@ private void parseServerCommand(String command){ case "#stop": stopListening(); + sendToAllClients("WARNING - The server has stopped listening for connections"); break; default: @@ -225,5 +232,24 @@ public void serverClosed(){ * @param args[0] The port number to listen on. Defaults to 5555 * if no argument is entered. */ + + public static void main(String[] args) + { + int DEFAULT_PORT = 5555; //Default Port + int port = 0; //Port to listen on + + try + { + port = Integer.parseInt(args[0]); //Get port from command line + } + catch(Throwable t) + { + port = DEFAULT_PORT; //Set port to 5555 + } + + ServerConsole chat = new ServerConsole(port); + chat.accept(); //Wait for client data + } + } //End of EchoServer class diff --git a/code/simplechat2/ServerConsole.java b/code/simplechat2/ServerConsole.java index 645cd8d..9bd71b0 100644 --- a/code/simplechat2/ServerConsole.java +++ b/code/simplechat2/ServerConsole.java @@ -59,11 +59,12 @@ public void accept() * * @param args[0] The host to connect to. */ + public static void main(String[] args) { int DEFAULT_PORT = 5555; //Default Port int port = 0; //Port to listen on - + try { port = Integer.parseInt(args[0]); //Get port from command line @@ -72,9 +73,12 @@ public static void main(String[] args) { port = DEFAULT_PORT; //Set port to 5555 } - + ServerConsole chat = new ServerConsole(port); - chat.accept(); //Wait for server data + chat.accept(); //Wait for client data } + } + + diff --git a/code/simplechat2/client/ChatClient.java b/code/simplechat2/client/ChatClient.java index 8e255e1..ca8f8dd 100644 --- a/code/simplechat2/client/ChatClient.java +++ b/code/simplechat2/client/ChatClient.java @@ -39,14 +39,19 @@ public class ChatClient extends AbstractClient * @param clientUI The interface type variable. */ - public ChatClient(String loginID, String host, int port, ChatIF clientUI) - throws IOException + public ChatClient(String loginID, String host, int port, ChatIF clientUI) { super(host, port); //Call the superclass constructor this.clientUI = clientUI; this.loginID = loginID; + + try{ openConnection(); sendToServer("#login " + loginID); + } + catch(IOException e){ + System.out.println("Cannot open connection. Awaiting command."); + } } @@ -118,6 +123,7 @@ private void parseClientCommand(String message){ case "#sethost": if(!isConnected()){ setHost(messageList[1]); + System.out.println("Host set to: " + getHost() + "."); } else{ System.out.println("Error: Cannot Change Host While Connected to a Server"); @@ -128,6 +134,7 @@ private void parseClientCommand(String message){ if(!isConnected()){ setPort(Integer.parseInt(messageList[1])); + System.out.println("Port set to: " + getPort() + "."); } else{ System.out.println("Error: Cannot Change Port While Connected to a Server"); @@ -186,14 +193,13 @@ public void quit() //Terminates Client when server terminates public void connectionException(Exception Error){ - System.out.println("The server has closed"); - quit(); + System.out.println("Abnormal termination of connection."); } //Displays Message upon client termination public void connectionClosed(){ - + System.out.println("Connection Closed."); } } From 52f1efe7ede4d8d1534ae6f370536dfe87bd7ed4 Mon Sep 17 00:00:00 2001 From: jksza042 <55514862+jksza042@users.noreply.github.com> Date: Fri, 29 May 2020 13:21:37 -0400 Subject: [PATCH 11/13] Temp Push, Troubleshooting Issues --- code/simplechat2/EchoServer.java | 3 ++- code/simplechat2/client/ChatClient.java | 4 ---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/code/simplechat2/EchoServer.java b/code/simplechat2/EchoServer.java index c5b6c35..36c16fe 100644 --- a/code/simplechat2/EchoServer.java +++ b/code/simplechat2/EchoServer.java @@ -83,7 +83,6 @@ private void handleClientLogin(String msg, ConnectionToClient client){ sendToAllClients(message); } else{ - System.out.println("Error: Client Already Logged In"); try{ client.sendToClient("Error: Client Already Logged In"); @@ -137,6 +136,7 @@ public void clientDisconnected(ConnectionToClient client){ sendToAllClients(message); } + public void handleMessageFromServerUI(String message){ char commandKey = "#".charAt(0); @@ -179,6 +179,7 @@ private void parseServerCommand(String command){ try{ setPort(Integer.parseInt(commandList[1])); + System.out.println("Port Set to: " + getPort()); } catch(IndexOutOfBoundsException e){ System.out.println("Error: Port Not Specified"); diff --git a/code/simplechat2/client/ChatClient.java b/code/simplechat2/client/ChatClient.java index ca8f8dd..1ac8ac9 100644 --- a/code/simplechat2/client/ChatClient.java +++ b/code/simplechat2/client/ChatClient.java @@ -171,10 +171,6 @@ private void parseClientCommand(String message){ System.out.println("Error: Command Not Recognized"); } - - - - } /** From 88293d18f363ba976eedf7407da7b21497025f92 Mon Sep 17 00:00:00 2001 From: jksza042 <55514862+jksza042@users.noreply.github.com> Date: Fri, 29 May 2020 13:45:36 -0400 Subject: [PATCH 12/13] Fixed Disconnect Messages --- code/simplechat2/EchoServer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/simplechat2/EchoServer.java b/code/simplechat2/EchoServer.java index 36c16fe..294b425 100644 --- a/code/simplechat2/EchoServer.java +++ b/code/simplechat2/EchoServer.java @@ -130,7 +130,7 @@ public void clientConnected(ConnectionToClient client){ } //Displays a message to all clients when a client disconnects - public void clientDisconnected(ConnectionToClient client){ + public void clientException(ConnectionToClient client, Throwable e){ String message = client.getInfo("loginID") + " has disconnected"; System.out.println(message); sendToAllClients(message); From 0cc834483055affc0b56463b64632bca84491008 Mon Sep 17 00:00:00 2001 From: jksza042 <55514862+jksza042@users.noreply.github.com> Date: Fri, 29 May 2020 13:55:06 -0400 Subject: [PATCH 13/13] Added Final Test Logs --- .../simplechat2/TestCases - Phase 1 and 2.txt | 428 ++++++++++++++++++ 1 file changed, 428 insertions(+) create mode 100644 code/simplechat2/TestCases - Phase 1 and 2.txt diff --git a/code/simplechat2/TestCases - Phase 1 and 2.txt b/code/simplechat2/TestCases - Phase 1 and 2.txt new file mode 100644 index 0000000..805393e --- /dev/null +++ b/code/simplechat2/TestCases - Phase 1 and 2.txt @@ -0,0 +1,428 @@ +Name: Jacob Kszan +Student Number: 300132837 +Email: jksza042@uottawa.ca + +PHASE 1 TESTING (Sorry if it's a bit unorganized, phase 2 is better) + +Client 3 + +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat1>java ClientConsole +Testcase 1007! +> Testcase 1007! +> Could not send message to server. Terminating client. + +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat1> + +Client 2 + +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat1>java ClientConsole +Test> Testcase 1007! + +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat1> + +Client 1 + +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat1>java ClientConsole +Currently on Testcase 1004 +> Currently on Testcase 1004 +Testcase 1005 +> Could not send message to server. Terminating client. + +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat1>java ClientConsole + +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat1>java ClientConsole +This is Testcase 1006 +> This is Testcase 1006 +> Testcase 1007! +> Could not send message to server. Terminating client. + +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat1>C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat1>java ClientConsole +'C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat1' is not recognized as an internal or external command, +operable program or batch file. + +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat1>Test> Testcase 1007! +'Test' is not recognized as an internal or external command, +operable program or batch file. + +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat1> +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat1>java ClientConsole +FinFin + + +FIniewfnoewf> FinFin +> +> +Done +> FIniewfnoewfDone +d +> d +> Final FFFinTestcase 1013, final test +e +> Could not send message to server. Terminating client. + +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat1> + +Server: + +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat1>java EchoServer +Server listening for connections on port 5555 + +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat1>java ClientConsole +Error: Can't setup connection! Terminating client. + +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat1>java EchoServer +Server listening for connections on port 5555 +Message received: Currently on Testcase 1004 from 127.0.0.1 (127.0.0.1) + +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat1>java EchoServer +Server listening for connections on port 5555 +Message received: null from 127.0.0.1 (127.0.0.1) +Message received: This is Testcase 1006 from 127.0.0.1 (127.0.0.1) +Message received: Testcase 1007! from 127.0.0.1 (127.0.0.1) +Message received: null from 127.0.0.1 (127.0.0.1) +Message received: This message is from my Desktop to my Laptop! from Nev-PC (192.168.2.29) +Message received: null from Nev-PC (192.168.2.29) +Message received: FinFin from 127.0.0.1 (127.0.0.1) +Message received: from 127.0.0.1 (127.0.0.1) +Message received: from 127.0.0.1 (127.0.0.1) +Message received: FIniewfnoewfDone from 127.0.0.1 (127.0.0.1) +Message received: d from 127.0.0.1 (127.0.0.1) +Message received: Final FFFinTestcase 1013, final test from 127.0.0.1 (127.0.0.1) + +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat1> + + + +------------------------------------------------------------------------------------------------------------------------------- + +PHASE 2 TESTING + +Testcase 2001: + +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat2>java EchoServer +Server listening for connections on port 5555 + +Testcase 2002: + +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat2>java ClientConsole +ERROR - No login ID specified. Connection aborted. + +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat2> + +Testcase 2003: + +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat2>java ClientConsole Testcase2003 +Cannot open connection. Awaiting command. + +Testcase 2004: + +Client - +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat2>java ClientConsole Testcase2004 +> Testcase2004 has logged on. + +Server - +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat2>java EchoServer +Server listening for connections on port 5555 +A new client is attempting to connect to the server. +Message received: #login Testcase2004 from null +Testcase2004 has logged on. + +Testcase 2005: + +Client - +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat2>java ClientConsole Testcase2004 +> Testcase2004 has logged on. +This is Testcase 2005! +> Testcase2004> This is Testcase 2005! + +Server - +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat2>java EchoServer +Server listening for connections on port 5555 +A new client is attempting to connect to the server. +Message received: #login Testcase2004 from null +Testcase2004 has logged on. +Message received: This is Testcase 2005! from Testcase2004 + +Testcase 2006: + +Server - +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat2>java EchoServer +Server listening for connections on port 5555 +A new client is attempting to connect to the server. +Message received: #login YipOne from null +YipOne has logged on. +A new client is attempting to connect to the server. +Message received: #login YipTwo from null +YipTwo has logged on. +Message received: YipTwo here! from YipTwo +Message received: YipOne Here! from YipOne +YipServer also present +SERVER MSG> YipServer also present + +Client 1 - +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat2>java ClientConsole YipOne +> YipOne has logged on. +> YipTwo has logged on. +> YipTwo> YipTwo here! +YipOne Here! +> YipOne> YipOne Here! +> SERVER MSG> YipServer also present + +Client 2 - +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat2>java ClientConsole YipTwo +> YipTwo has logged on. +YipTwo here! +> YipTwo> YipTwo here! +> YipOne> YipOne Here! +> SERVER MSG> YipServer also present + +Testcase 2007: + +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat2>java EchoServer +Server listening for connections on port 5555 +#quit + +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat2> + +Testcase 2008: + +Server - +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat2>java EchoServer +Server listening for connections on port 5555 +A new client is attempting to connect to the server. +Message received: #login First from null +First has logged on. +#stop +Server has stopped listening for connections. +Message received: Still here from First +#start +Server listening for connections on port 5555 +A new client is attempting to connect to the server. +Message received: #login Second from null +Second has logged on. + +Client 1 - +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat2>java ClientConsole First +> First has logged on. +> WARNING - The server has stopped listening for connections +Still here +> First> Still here +> Second has logged on. + +Client 2 - +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat2>java ClientConsole Second +> Second has logged on. + +Testcase 2009: + +Server - +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat2>java EchoServer +Server listening for connections on port 5555 +A new client is attempting to connect to the server. +Message received: #login Connector from null +Connector has logged on. +#stop +Server has stopped listening for connections. +#close +Connector has disconnected + +Client - +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat2>java ClientConsole Connector +> Connector has logged on. +> WARNING - The server has stopped listening for connections +> SERVER SHUTTING DOWN! DISCONNECTING! +Abnormal termination of connection. + + + +Testcase 2009 (There are two Testcase 2009): + +Server - +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat2>java EchoServer +Server listening for connections on port 5555 +A new client is attempting to connect to the server. +Message received: #login One from null +One has logged on. +#close +Server has stopped listening for connections. +One has disconnected +#start +Server listening for connections on port 5555 +A new client is attempting to connect to the server. +Message received: #login Two from null +Two has logged on. + +Client - +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat2>java ClientConsole One +> One has logged on. +> SERVER SHUTTING DOWN! DISCONNECTING! +Abnormal termination of connection. +#quit + +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat2>java ClientConsole Two +> Two has logged on. + + + +Testcase 2010: + +Client - +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat2>java ClientConsole Test +Cannot open connection. Awaiting command. +#quit + +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat2> + + + +Testcase 2011: + +Client - +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat2>java ClientConsole Yipper +> Yipper has logged on. +#logoff +Connection Closed. + +Testcase 2012: + +Client - +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat2>java ClientConsole Testcase +Cannot open connection. Awaiting command. +#sethost newhost +Host set to: newhost. +#setport 1234 +Port set to: 1234. + +Testcase 2013: + +Server - +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat2>java ServerConsole 1234 +Server listening for connections on port 1234 + +Testcase 2014: + +Server - +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat2>java ServerConsole 1234 +Server listening for connections on port 1234 +A new client is attempting to connect to the server. +Message received: #login Tester from null +Tester has logged on. + +Client - +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat2>java ClientConsole Tester localhost 1234 +> Tester has logged on. + +Testcase 2015: + +Server - +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat2>java EchoServer +Server listening for connections on port 5555 +A new client is attempting to connect to the server. +Message received: #login Desktop from null +Desktop has logged on. +A new client is attempting to connect to the server. +Message received: #login Laptop from null +Laptop has logged on. +Message received: Laptop here! from Laptop +Message received: Desktop here! from Desktop +#close +Server has stopped listening for connections. +Laptop has disconnected +Desktop has disconnected +#setport 1234 +Port Set to: 1234 +#start +Server listening for connections on port 1234 +A new client is attempting to connect to the server. +Message received: #login Desktop from null +Desktop has logged on. +Message received: Desktop Back! from Desktop +A new client is attempting to connect to the server. +Message received: #login Laptop from null +Laptop has logged on. +Message received: Laptop back! from Laptop + +Client 1 - + +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat2>java ClientConsole Laptop +> Laptop has logged on. +Laptop here! +> Laptop> Laptop here! +> Desktop> Desktop here! +> SERVER SHUTTING DOWN! DISCONNECTING! +Abnormal termination of connection. +#setport 1234 +Port set to: 1234. +#login Laptop +> Laptop has logged on. +Laptop back! +> Laptop> Laptop back! + +Client 2 - + +C:\Users\Nev\Documents\GitHub\Lloseng\code\simplechat2>java ClientConsole Deskto +p JKTOP +> Desktop has logged on. +> Laptop has logged on. +> Laptop> Laptop here! +Desktop here! +> Desktop> Desktop here! +> SERVER SHUTTING DOWN! DISCONNECTING! +> Laptop has disconnected +Abnormal termination of connection. +#setport 1234 +Port set to: 1234. +#login Desktop +> Desktop has logged on. +Desktop Back! +> Desktop> Desktop Back! +> Laptop has logged on. +> Laptop> Laptop back! + +Testcase 2016: + +Server - +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat2>java EchoServer +Server listening for connections on port 5555 +A new client is attempting to connect to the server. +Message received: #login UserOne from null +UserOne has logged on. +A new client is attempting to connect to the server. +Message received: #login UserTwo from null +UserTwo has logged on. +A new client is attempting to connect to the server. +Message received: #login UserThree from null +UserThree has logged on. +UserOne has disconnected +UserTwo has disconnected + +Client 1 - +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat2>java ClientConsole UserOne +> UserOne has logged on. +> UserTwo has logged on. +> UserThree has logged on. +#quit +Connection Closed. + +Client 2 - +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat2>java ClientConsole UserTwo +> UserTwo has logged on. +> UserThree has logged on. +> UserOne has disconnected +#logoff +Connection Closed. + + +Client 3 - +C:\Users\Nevada\Documents\GitHub\Lloseng\code\simplechat2>java ClientConsole UserThree +> UserThree has logged on. +> UserOne has disconnected +> UserTwo has disconnected + + + +I do not have the computers and operating systems to do some of the tests, so some are omitted + + + +