From b47d98b9603d635b1462b0b2d20438a2c4c4acbb Mon Sep 17 00:00:00 2001 From: Andrin Fassbind Date: Thu, 14 Apr 2022 12:11:03 +0200 Subject: [PATCH 1/4] fixed Issue #19 by changing messagePattern --- .../ch/zhaw/pm2/multichat/client/ClientConnectionHandler.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/client/src/main/java/ch/zhaw/pm2/multichat/client/ClientConnectionHandler.java b/client/src/main/java/ch/zhaw/pm2/multichat/client/ClientConnectionHandler.java index 9da83da..9ff948a 100644 --- a/client/src/main/java/ch/zhaw/pm2/multichat/client/ClientConnectionHandler.java +++ b/client/src/main/java/ch/zhaw/pm2/multichat/client/ClientConnectionHandler.java @@ -28,7 +28,7 @@ public class ClientConnectionHandler implements Runnable { public static final String USER_NONE = ""; public static final String USER_ALL = "*"; - private final Pattern messagePattern = Pattern.compile( "^(?:@(\\w*))?\\s*(.*)$" ); + private final Pattern messagePattern = Pattern.compile( "^(?:@(\\S*))?\\s*(.*)$" ); private SimpleStringProperty userName; private SimpleObjectProperty state; @@ -63,7 +63,6 @@ public class ClientConnectionHandler implements Runnable { public void setState (State newState) { state.set(newState); - } public void run () { From b6fd5b569d08a2494f7afc13228647ab401c32c9 Mon Sep 17 00:00:00 2001 From: Andrin Fassbind Date: Thu, 14 Apr 2022 19:33:18 +0200 Subject: [PATCH 2/4] fixed Issue #28 by checking if username contains space before starting connection --- .../client/ChatWindowController.java | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/client/src/main/java/ch/zhaw/pm2/multichat/client/ChatWindowController.java b/client/src/main/java/ch/zhaw/pm2/multichat/client/ChatWindowController.java index 1a8a9c6..c657f44 100644 --- a/client/src/main/java/ch/zhaw/pm2/multichat/client/ChatWindowController.java +++ b/client/src/main/java/ch/zhaw/pm2/multichat/client/ChatWindowController.java @@ -106,18 +106,22 @@ public class ChatWindowController { private void startConnectionHandler() throws IOException { String userName = userNameField.getText(); - String serverAddress = serverAddressField.getText(); - int serverPort = Integer.parseInt(serverPortField.getText()); - connectionHandler = new ClientConnectionHandler( - NetworkHandler.openConnection(serverAddress, serverPort), userName, - messages); - new Thread(connectionHandler).start(); + if(!userName.contains(" ")) { + String serverAddress = serverAddressField.getText(); + int serverPort = Integer.parseInt(serverPortField.getText()); + connectionHandler = new ClientConnectionHandler( + NetworkHandler.openConnection(serverAddress, serverPort), userName, + messages); + new Thread(connectionHandler).start(); - //register Listener - startListener(); + //register Listener + startListener(); - // register window close handler - rootPane.getScene().getWindow().addEventHandler(WindowEvent.WINDOW_CLOSE_REQUEST, windowCloseHandler); + // register window close handler + rootPane.getScene().getWindow().addEventHandler(WindowEvent.WINDOW_CLOSE_REQUEST, windowCloseHandler); + } else { + addError("It is not allowed to have spaces in username!"); + } } private void terminateConnectionHandler() { From 5e6e9d58174d97b9fda7a334995dde108f9f9f9d Mon Sep 17 00:00:00 2001 From: Andrin Fassbind Date: Thu, 14 Apr 2022 21:11:58 +0200 Subject: [PATCH 3/4] Create ConnectionHandlerClass Remove Codeduplication --- .../client/ChatWindowController.java | 9 ++- .../client/ClientConnectionHandler.java | 61 +++++++------------ .../multichat/protocol/ConnectionHandler.java | 47 ++++++++++++++ .../server/ServerConnectionHandler.java | 50 ++++++--------- 4 files changed, 94 insertions(+), 73 deletions(-) create mode 100644 protocol/src/main/java/ch/zhaw/pm2/multichat/protocol/ConnectionHandler.java diff --git a/client/src/main/java/ch/zhaw/pm2/multichat/client/ChatWindowController.java b/client/src/main/java/ch/zhaw/pm2/multichat/client/ChatWindowController.java index 6a2f224..9204610 100644 --- a/client/src/main/java/ch/zhaw/pm2/multichat/client/ChatWindowController.java +++ b/client/src/main/java/ch/zhaw/pm2/multichat/client/ChatWindowController.java @@ -1,7 +1,8 @@ package ch.zhaw.pm2.multichat.client; -import ch.zhaw.pm2.multichat.client.ClientConnectionHandler.State; +import ch.zhaw.pm2.multichat.protocol.ConnectionHandler.State; import ch.zhaw.pm2.multichat.protocol.ChatProtocolException; +import ch.zhaw.pm2.multichat.protocol.ConnectionHandler; import ch.zhaw.pm2.multichat.protocol.NetworkHandler; import javafx.application.Platform; import javafx.beans.value.ChangeListener; @@ -15,10 +16,8 @@ import javafx.scene.layout.Pane; import javafx.stage.WindowEvent; import java.io.IOException; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import static ch.zhaw.pm2.multichat.client.ClientConnectionHandler.State.*; +import static ch.zhaw.pm2.multichat.protocol.ConnectionHandler.State.*; public class ChatWindowController { private ClientConnectionHandler connectionHandler; @@ -129,7 +128,7 @@ public class ChatWindowController { } } - public void stateChanged(State newState) { + public void stateChanged(ConnectionHandler.State newState) { // update UI (need to be run in UI thread: see Platform.runLater()) Platform.runLater(new Runnable() { @Override diff --git a/client/src/main/java/ch/zhaw/pm2/multichat/client/ClientConnectionHandler.java b/client/src/main/java/ch/zhaw/pm2/multichat/client/ClientConnectionHandler.java index 9da83da..4c47f6d 100644 --- a/client/src/main/java/ch/zhaw/pm2/multichat/client/ClientConnectionHandler.java +++ b/client/src/main/java/ch/zhaw/pm2/multichat/client/ClientConnectionHandler.java @@ -1,6 +1,7 @@ package ch.zhaw.pm2.multichat.client; import ch.zhaw.pm2.multichat.protocol.ChatProtocolException; +import ch.zhaw.pm2.multichat.protocol.ConnectionHandler; import ch.zhaw.pm2.multichat.protocol.NetworkHandler; import javafx.beans.property.SimpleIntegerProperty; import javafx.beans.property.SimpleObjectProperty; @@ -12,21 +13,9 @@ import java.net.SocketException; import java.util.Scanner; import java.util.regex.Matcher; import java.util.regex.Pattern; +import static ch.zhaw.pm2.multichat.protocol.ConnectionHandler.State.*; -import static ch.zhaw.pm2.multichat.client.ClientConnectionHandler.State.*; - -public class ClientConnectionHandler implements Runnable { - private final NetworkHandler.NetworkConnection connection; - - // Data types used for the Chat Protocol - private static final String DATA_TYPE_CONNECT = "CONNECT"; - private static final String DATA_TYPE_CONFIRM = "CONFIRM"; - private static final String DATA_TYPE_DISCONNECT = "DISCONNECT"; - private static final String DATA_TYPE_MESSAGE = "MESSAGE"; - private static final String DATA_TYPE_ERROR = "ERROR"; - - public static final String USER_NONE = ""; - public static final String USER_ALL = "*"; +public class ClientConnectionHandler extends ConnectionHandler implements Runnable { private final Pattern messagePattern = Pattern.compile( "^(?:@(\\w*))?\\s*(.*)$" ); @@ -36,17 +25,13 @@ public class ClientConnectionHandler implements Runnable { private SimpleStringProperty serverAddress; private SimpleIntegerProperty serverPort; - enum State { - NEW, CONFIRM_CONNECT, CONNECTED, CONFIRM_DISCONNECT, DISCONNECTED; - } - public ClientConnectionHandler(NetworkHandler.NetworkConnection connection, String userName, ClientMessageList messages) { - this.connection = connection; + super(connection); this.userName = new SimpleStringProperty((userName == null || userName.isBlank())? USER_NONE : userName); this.messages = messages; - state = new SimpleObjectProperty<>(NEW); + state = new SimpleObjectProperty<>(State.NEW); serverAddress = new SimpleStringProperty(); serverPort = new SimpleIntegerProperty(); } @@ -70,12 +55,12 @@ public class ClientConnectionHandler implements Runnable { startReceiving(); } - public void startReceiving() { + private void startReceiving() { System.out.println("Starting Connection Handler"); try { System.out.println("Start receiving data..."); - while (connection.isAvailable()) { - String data = connection.receive(); + while (getConnection().isAvailable()) { + String data = getConnection().receive(); processData(data); } System.out.println("Stopped recieving data"); @@ -99,7 +84,7 @@ public class ClientConnectionHandler implements Runnable { System.out.println("Closing Connection Handler to Server"); try { System.out.println("Stop receiving data..."); - connection.close(); + getConnection().close(); System.out.println("Stopped receiving data."); } catch (IOException e) { System.err.println("Failed to close connection." + e.getMessage()); @@ -134,13 +119,13 @@ public class ClientConnectionHandler implements Runnable { payload = scanner.nextLine(); } // dispatch operation based on type parameter - if (type.equals(DATA_TYPE_CONNECT)) { + if (type.equals(getDataTypeConnect())) { System.err.println("Illegal connect request from server"); - } else if (type.equals(DATA_TYPE_CONFIRM)) { + } else if (type.equals(getDataTypeConfirm())) { if (state.get() == CONFIRM_CONNECT) { this.userName.set(reciever); - this.serverPort.set(connection.getRemotePort()); - this.serverAddress.set(connection.getRemoteHost()); + this.serverPort.set(getConnection().getRemotePort()); + this.serverAddress.set(getConnection().getRemoteHost()); messages.addMessage(new Message(Message.MessageType.INFO,sender,reciever,payload)); System.out.println("CONFIRM: " + payload); this.setState(CONNECTED); @@ -151,7 +136,7 @@ public class ClientConnectionHandler implements Runnable { } else { System.err.println("Got unexpected confirm message: " + payload); } - } else if (type.equals(DATA_TYPE_DISCONNECT)) { + } else if (type.equals(getDataTypeDisconnect())) { if (state.get() == DISCONNECTED) { System.out.println("DISCONNECT: Already in disconnected: " + payload); return; @@ -159,14 +144,14 @@ public class ClientConnectionHandler implements Runnable { messages.addMessage(new Message(Message.MessageType.INFO,sender,reciever,payload)); System.out.println("DISCONNECT: " + payload); this.setState(DISCONNECTED); - } else if (type.equals(DATA_TYPE_MESSAGE)) { + } else if (type.equals(getDataTypeMessage())) { if (state.get() != CONNECTED) { System.out.println("MESSAGE: Illegal state " + state + " for message: " + payload); return; } messages.addMessage(new Message(Message.MessageType.MESSAGE,sender,reciever,payload)); System.out.println("MESSAGE: From " + sender + " to " + reciever + ": "+ payload); - } else if (type.equals(DATA_TYPE_ERROR)) { + } else if (type.equals(getDataTypeError())) { messages.addMessage(new Message(Message.MessageType.ERROR,sender,reciever,payload)); System.out.println("ERROR: " + payload); } else { @@ -174,12 +159,12 @@ public class ClientConnectionHandler implements Runnable { } } catch (ChatProtocolException e) { System.err.println("Error while processing data: " + e.getMessage()); - sendData(USER_NONE, userName.get(), DATA_TYPE_ERROR, e.getMessage()); + sendData(USER_NONE, userName.get(), getDataTypeError(), e.getMessage()); } } - public void sendData(String sender, String receiver, String type, String payload) { - if (connection.isAvailable()) { + private void sendData(String sender, String receiver, String type, String payload) { + if (getConnection().isAvailable()) { new StringBuilder(); String data = new StringBuilder() .append(sender+"\n") @@ -188,7 +173,7 @@ public class ClientConnectionHandler implements Runnable { .append(payload+"\n") .toString(); try { - connection.send(data); + getConnection().send(data); } catch (SocketException e) { System.err.println("Connection closed: " + e.getMessage()); } catch (EOFException e) { @@ -201,13 +186,13 @@ public class ClientConnectionHandler implements Runnable { public void connect() throws ChatProtocolException { if (state.get() != NEW) throw new ChatProtocolException("Illegal state for connect: " + state); - this.sendData(userName.get(), USER_NONE, DATA_TYPE_CONNECT,null); + this.sendData(userName.get(), USER_NONE, getDataTypeConnect(),null); this.setState(CONFIRM_CONNECT); } public void disconnect() throws ChatProtocolException { if (state.get() != NEW && state.get() != CONNECTED) throw new ChatProtocolException("Illegal state for disconnect: " + state); - this.sendData(userName.get(), USER_NONE, DATA_TYPE_DISCONNECT,null); + this.sendData(userName.get(), USER_NONE, getDataTypeDisconnect(),null); this.setState(CONFIRM_DISCONNECT); } @@ -222,7 +207,7 @@ public class ClientConnectionHandler implements Runnable { return false; } if (receiver == null || receiver.isBlank()) receiver = ClientConnectionHandler.USER_ALL; - this.sendData(userName.get(), receiver, DATA_TYPE_MESSAGE,message); + this.sendData(userName.get(), receiver, getDataTypeMessage(),message); return true; } else { return false; diff --git a/protocol/src/main/java/ch/zhaw/pm2/multichat/protocol/ConnectionHandler.java b/protocol/src/main/java/ch/zhaw/pm2/multichat/protocol/ConnectionHandler.java new file mode 100644 index 0000000..73008d9 --- /dev/null +++ b/protocol/src/main/java/ch/zhaw/pm2/multichat/protocol/ConnectionHandler.java @@ -0,0 +1,47 @@ +package ch.zhaw.pm2.multichat.protocol; + +public abstract class ConnectionHandler { + private final NetworkHandler.NetworkConnection connection; + + // Data types used for the Chat Protocol + private static final String DATA_TYPE_CONNECT = "CONNECT"; + private static final String DATA_TYPE_CONFIRM = "CONFIRM"; + private static final String DATA_TYPE_DISCONNECT = "DISCONNECT"; + private static final String DATA_TYPE_MESSAGE = "MESSAGE"; + private static final String DATA_TYPE_ERROR = "ERROR"; + + public static final String USER_NONE = ""; + public static final String USER_ALL = "*"; + + public enum State { + NEW, CONFIRM_CONNECT, CONNECTED, CONFIRM_DISCONNECT, DISCONNECTED; + } + + public ConnectionHandler(NetworkHandler.NetworkConnection connection) { + this.connection = connection; + } + + public static String getDataTypeConnect() { + return DATA_TYPE_CONNECT; + } + + public static String getDataTypeConfirm() { + return DATA_TYPE_CONFIRM; + } + + public static String getDataTypeDisconnect() { + return DATA_TYPE_DISCONNECT; + } + + public static String getDataTypeMessage() { + return DATA_TYPE_MESSAGE; + } + + public static String getDataTypeError() { + return DATA_TYPE_ERROR; + } + + public NetworkHandler.NetworkConnection getConnection() { + return connection; + } +} diff --git a/server/src/main/java/ch/zhaw/pm2/multichat/server/ServerConnectionHandler.java b/server/src/main/java/ch/zhaw/pm2/multichat/server/ServerConnectionHandler.java index fa6686e..4e3db14 100644 --- a/server/src/main/java/ch/zhaw/pm2/multichat/server/ServerConnectionHandler.java +++ b/server/src/main/java/ch/zhaw/pm2/multichat/server/ServerConnectionHandler.java @@ -1,6 +1,7 @@ package ch.zhaw.pm2.multichat.server; import ch.zhaw.pm2.multichat.protocol.ChatProtocolException; +import ch.zhaw.pm2.multichat.protocol.ConnectionHandler; import ch.zhaw.pm2.multichat.protocol.NetworkHandler; import java.io.EOFException; @@ -16,22 +17,11 @@ import java.util.concurrent.locks.ReentrantLock; import static ch.zhaw.pm2.multichat.server.ServerConnectionHandler.State.*; -public class ServerConnectionHandler implements Runnable{ +public class ServerConnectionHandler extends ConnectionHandler implements Runnable{ private static final AtomicInteger connectionCounter = new AtomicInteger(0); private final int connectionId = connectionCounter.incrementAndGet(); - private final NetworkHandler.NetworkConnection connection; private final Map connectionRegistry; - // Data types used for the Chat Protocol - private static final String DATA_TYPE_CONNECT = "CONNECT"; - private static final String DATA_TYPE_CONFIRM = "CONFIRM"; - private static final String DATA_TYPE_DISCONNECT = "DISCONNECT"; - private static final String DATA_TYPE_MESSAGE = "MESSAGE"; - private static final String DATA_TYPE_ERROR = "ERROR"; - - private static final String USER_NONE = ""; - private static final String USER_ALL = "*"; - private ReentrantLock mutex; private Condition nameComplete; @@ -50,9 +40,9 @@ public class ServerConnectionHandler implements Runnable{ public ServerConnectionHandler(NetworkHandler.NetworkConnection connection, Map registry, ReentrantLock mutex, Condition nameComplete) { + super(connection); Objects.requireNonNull(connection, "Connection must not be null"); Objects.requireNonNull(registry, "Registry must not be null"); - this.connection = connection; this.connectionRegistry = registry; this.mutex = mutex; this.nameComplete = nameComplete; @@ -62,12 +52,12 @@ public class ServerConnectionHandler implements Runnable{ return this.userName; } - public void startReceiving() { + private void startReceiving() { System.out.println("Starting Connection Handler for new User"); try { System.out.println("Start receiving data..."); - while (connection.isAvailable()) { - String data = connection.receive(); + while (getConnection().isAvailable()) { + String data = getConnection().receive(); processData(data); } System.out.println("Stopped recieving data"); @@ -88,11 +78,11 @@ public class ServerConnectionHandler implements Runnable{ } - public void stopReceiving() { + private void stopReceiving() { System.out.println("Closing Connection Handler for " + userName); try { System.out.println("Stop receiving data..."); - connection.close(); + getConnection().close(); System.out.println("Stopped receiving data."); } catch (IOException e) { System.err.println("Failed to close connection." + e); @@ -128,7 +118,7 @@ public class ServerConnectionHandler implements Runnable{ } // dispatch operation based on type parameter - if (type.equals(DATA_TYPE_CONNECT)) { + if (type.equals(getDataTypeConnect())) { if (this.state != NEW) throw new ChatProtocolException("Illegal state for connect request: " + state); if (sender == null || sender.isBlank()) sender = this.userName; if (connectionRegistry.containsKey(sender)) @@ -142,20 +132,20 @@ public class ServerConnectionHandler implements Runnable{ mutex.unlock(); } connectionRegistry.put(userName, this); - sendData(USER_NONE, userName, DATA_TYPE_CONFIRM, "Registration successfull for " + userName); + sendData(USER_NONE, userName, getDataTypeConfirm(), "Registration successfull for " + userName); this.state = CONNECTED; - } else if (type.equals(DATA_TYPE_CONFIRM)) { + } else if (type.equals(getDataTypeConfirm())) { System.out.println("Not expecting to receive a CONFIRM request from client"); - } else if (type.equals(DATA_TYPE_DISCONNECT)) { + } else if (type.equals(getDataTypeDisconnect())) { if (state == DISCONNECTED) throw new ChatProtocolException("Illegal state for disconnect request: " + state); if (state == CONNECTED) { connectionRegistry.remove(this.userName); } - sendData(USER_NONE, userName, DATA_TYPE_CONFIRM, "Confirm disconnect of " + userName); + sendData(USER_NONE, userName, getDataTypeConfirm(), "Confirm disconnect of " + userName); this.state = DISCONNECTED; this.stopReceiving(); - } else if (type.equals(DATA_TYPE_MESSAGE)) { + } else if (type.equals(getDataTypeMessage())) { if (state != CONNECTED) throw new ChatProtocolException("Illegal state for message request: " + state); if (USER_ALL.equals(reciever)) { for (ServerConnectionHandler handler : connectionRegistry.values()) { @@ -169,10 +159,10 @@ public class ServerConnectionHandler implements Runnable{ sendData(sender, reciever, type, payload); //send message to sender if it's a direct message and sender is not receiver. } } else { - this.sendData(USER_NONE, userName, DATA_TYPE_ERROR, "Unknown User: " + reciever); + this.sendData(USER_NONE, userName, getDataTypeError(), "Unknown User: " + reciever); } } - } else if (type.equals(DATA_TYPE_ERROR)) { + } else if (type.equals(getDataTypeError())) { System.err.println("Received error from client (" + sender + "): " + payload); } else { System.err.println("Unknown data type received: " + type); @@ -180,12 +170,12 @@ public class ServerConnectionHandler implements Runnable{ } } catch(ChatProtocolException e) { System.out.println("Error while processing data" + e.getMessage()); - sendData(USER_NONE, userName, DATA_TYPE_ERROR, e.getMessage()); + sendData(USER_NONE, userName, getDataTypeError(), e.getMessage()); } } - public void sendData(String sender, String receiver, String type, String payload) { - if (connection.isAvailable()) { + private void sendData(String sender, String receiver, String type, String payload) { + if (getConnection().isAvailable()) { new StringBuilder(); String data = new StringBuilder() .append(sender+"\n") @@ -194,7 +184,7 @@ public class ServerConnectionHandler implements Runnable{ .append(payload+"\n") .toString(); try { - connection.send(data); + getConnection().send(data); } catch (SocketException e) { System.out.println("Connection closed: " + e.getMessage()); } catch (EOFException e) { From 5a26c6b127ba45e46408dcc28d61c793ba32e15f Mon Sep 17 00:00:00 2001 From: Andrin Fassbind Date: Thu, 14 Apr 2022 21:37:06 +0200 Subject: [PATCH 4/4] create setConnection in ConnectionHandlerClass --- .../ch/zhaw/pm2/multichat/protocol/ConnectionHandler.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/protocol/src/main/java/ch/zhaw/pm2/multichat/protocol/ConnectionHandler.java b/protocol/src/main/java/ch/zhaw/pm2/multichat/protocol/ConnectionHandler.java index 73008d9..a0ddd57 100644 --- a/protocol/src/main/java/ch/zhaw/pm2/multichat/protocol/ConnectionHandler.java +++ b/protocol/src/main/java/ch/zhaw/pm2/multichat/protocol/ConnectionHandler.java @@ -1,7 +1,7 @@ package ch.zhaw.pm2.multichat.protocol; public abstract class ConnectionHandler { - private final NetworkHandler.NetworkConnection connection; + private NetworkHandler.NetworkConnection connection; // Data types used for the Chat Protocol private static final String DATA_TYPE_CONNECT = "CONNECT"; @@ -44,4 +44,8 @@ public abstract class ConnectionHandler { public NetworkHandler.NetworkConnection getConnection() { return connection; } + + protected void setConnection() { + this.connection = connection; + } }