From 2b01e92e8d24e5083974257383905d89a4e106b4 Mon Sep 17 00:00:00 2001 From: schrom01 Date: Fri, 15 Apr 2022 15:34:03 +0200 Subject: [PATCH 1/8] sovled #42 --- .../client/ChatWindowController.java | 22 +++++++------------ .../client/ClientConnectionHandler.java | 9 +++++--- 2 files changed, 14 insertions(+), 17 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 5bde847..2f680ce 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 @@ -2,8 +2,6 @@ package ch.zhaw.pm2.multichat.client; 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; import javafx.beans.value.ObservableValue; @@ -38,8 +36,7 @@ public class ChatWindowController { @FXML public void initialize() { - serverAddressField.setText(NetworkHandler.DEFAULT_ADDRESS.getCanonicalHostName()); - serverPortField.setText(String.valueOf(NetworkHandler.DEFAULT_PORT)); + } public void setMessages(ClientMessageList messages) { @@ -49,6 +46,9 @@ public class ChatWindowController { public void setConnectionHandler(ClientConnectionHandler connectionHandler){ this.connectionHandler = connectionHandler; + startConnectionHandlerListener(); + serverAddressField.setText(connectionHandler.getServerAddressProperty().get()); + serverPortField.setText(String.valueOf(connectionHandler.getServerPortProperty().get())); } private void applicationClose() { @@ -104,7 +104,7 @@ public class ChatWindowController { @FXML private void applyFilter( ) { - this.redrawMessageList(); + Platform.runLater(() -> this.messageArea.setText(messages.getFilteredMessages(filterValue.getText().strip()))); } private void startConnectionHandler() throws IOException { @@ -116,7 +116,7 @@ public class ChatWindowController { new Thread(connectionHandler).start(); //register Listener - startListener(); + //startConnectionHandlerListener(); // register window close handler rootPane.getScene().getWindow().addEventHandler(WindowEvent.WINDOW_CLOSE_REQUEST, windowCloseHandler); @@ -169,19 +169,13 @@ public class ChatWindowController { messages.addMessage(new Message(Message.MessageType.ERROR, null, null, message)); } - private void redrawMessageList() { - this.messageArea.clear(); - Platform.runLater(() -> this.messageArea.setText(messages.getFilteredMessages(filterValue.getText().strip()))); - } - class WindowCloseHandler implements EventHandler { public void handle(WindowEvent event) { applicationClose(); } - } - public void startListener() { + public void startConnectionHandlerListener() { connectionHandler.getStateProperty().addListener(new ChangeListener() { @Override public void changed(ObservableValue observable, State oldValue, State newValue) { @@ -215,7 +209,7 @@ public class ChatWindowController { messages.getChangedProperty().addListener(new ChangeListener() { @Override public void changed(ObservableValue observable, Boolean oldValue, Boolean newValue) { - redrawMessageList(); + Platform.runLater(() -> messageArea.setText(messages.getFilteredMessages(filterValue.getText().strip()))); } }); } 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 983c34c..f67ceb0 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 @@ -29,12 +29,15 @@ public class ClientConnectionHandler extends ConnectionHandler implements Runnab super(); this.messages = messages; state = new SimpleObjectProperty<>(State.NEW); - serverAddress = new SimpleStringProperty(); - serverPort = new SimpleIntegerProperty(); + serverAddress = new SimpleStringProperty(NetworkHandler.DEFAULT_ADDRESS.getCanonicalHostName()); + serverPort = new SimpleIntegerProperty(NetworkHandler.DEFAULT_PORT); + this.userName = new SimpleStringProperty(null); } public void initialize(String serverAddress, int serverPort, String userName) throws IOException { - state = new SimpleObjectProperty<>(NEW); + state.set(NEW); + this.serverAddress.set(serverAddress); + this.serverPort.set(serverPort); setConnection(NetworkHandler.openConnection(serverAddress, serverPort)); this.userName = new SimpleStringProperty((userName == null || userName.isBlank())? USER_NONE : userName); } From 2b70299507e586519bfee6129ce19ed983621efa Mon Sep 17 00:00:00 2001 From: schrom01 Date: Fri, 15 Apr 2022 15:35:04 +0200 Subject: [PATCH 2/8] removed Method initialize because it's not used anymore. --- .../ch/zhaw/pm2/multichat/client/ChatWindowController.java | 5 ----- 1 file changed, 5 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 2f680ce..b780b49 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 @@ -34,11 +34,6 @@ public class ChatWindowController { @FXML private TextField filterValue; - @FXML - public void initialize() { - - } - public void setMessages(ClientMessageList messages) { this.messages = messages; messageListener(); From 37045ade15d444cc9be22c1ee89fedf885ebb1a1 Mon Sep 17 00:00:00 2001 From: Andrin Fassbind Date: Fri, 15 Apr 2022 16:25:45 +0200 Subject: [PATCH 3/8] Shrink code in the ConnectionHandlers --- .../client/ClientConnectionHandler.java | 81 ++++++------------ .../multichat/protocol/ConnectionHandler.java | 48 +++++++++++ .../server/ServerConnectionHandler.java | 82 +++++-------------- 3 files changed, 91 insertions(+), 120 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 983c34c..0dd7368 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 @@ -29,12 +29,15 @@ public class ClientConnectionHandler extends ConnectionHandler implements Runnab super(); this.messages = messages; state = new SimpleObjectProperty<>(State.NEW); - serverAddress = new SimpleStringProperty(); - serverPort = new SimpleIntegerProperty(); + serverAddress = new SimpleStringProperty(NetworkHandler.DEFAULT_ADDRESS.getCanonicalHostName()); + serverPort = new SimpleIntegerProperty(NetworkHandler.DEFAULT_PORT); + this.userName = new SimpleStringProperty(null); } public void initialize(String serverAddress, int serverPort, String userName) throws IOException { - state = new SimpleObjectProperty<>(NEW); + state.set(NEW); + this.serverAddress.set(serverAddress); + this.serverPort.set(serverPort); setConnection(NetworkHandler.openConnection(serverAddress, serverPort)); this.userName = new SimpleStringProperty((userName == null || userName.isBlank())? USER_NONE : userName); } @@ -96,65 +99,48 @@ public class ClientConnectionHandler extends ConnectionHandler implements Runnab private void processData(String data) { try { - // parse data content Scanner scanner = new Scanner(data); - String sender = null; - String reciever = null; - String type = null; - String payload = null; - if (scanner.hasNextLine()) { - sender = scanner.nextLine(); - } else { - throw new ChatProtocolException("No Sender found"); - } - if (scanner.hasNextLine()) { - reciever = scanner.nextLine(); - } else { - throw new ChatProtocolException("No Reciever found"); - } - if (scanner.hasNextLine()) { - type = scanner.nextLine(); - } else { - throw new ChatProtocolException("No Type found"); - } - if (scanner.hasNextLine()) { - payload = scanner.nextLine(); - } + StringBuilder sender = new StringBuilder(); + StringBuilder reciever = new StringBuilder(); + StringBuilder type = new StringBuilder(); + StringBuilder payload = new StringBuilder(); + super.processData(scanner,sender,reciever,type,payload); + // dispatch operation based on type parameter - if (type.equals(getDataTypeConnect())) { + if (type.toString().equals(getDataTypeConnect())) { System.err.println("Illegal connect request from server"); - } else if (type.equals(getDataTypeConfirm())) { + } else if (type.toString().equals(getDataTypeConfirm())) { if (state.get() == CONFIRM_CONNECT) { - this.userName.set(reciever); + this.userName.set(reciever.toString()); this.serverPort.set(getConnection().getRemotePort()); this.serverAddress.set(getConnection().getRemoteHost()); - messages.addMessage(new Message(Message.MessageType.INFO,sender,reciever,payload)); + messages.addMessage(new Message(Message.MessageType.INFO,sender.toString(),reciever.toString(),payload.toString())); System.out.println("CONFIRM: " + payload); this.setState(CONNECTED); } else if (state.get() == CONFIRM_DISCONNECT) { - messages.addMessage(new Message(Message.MessageType.INFO,sender,reciever,payload)); + messages.addMessage(new Message(Message.MessageType.INFO,sender.toString(),reciever.toString(),payload.toString())); System.out.println("CONFIRM: " + payload); this.setState(DISCONNECTED); } else { System.err.println("Got unexpected confirm message: " + payload); } - } else if (type.equals(getDataTypeDisconnect())) { + } else if (type.toString().equals(getDataTypeDisconnect())) { if (state.get() == DISCONNECTED) { System.out.println("DISCONNECT: Already in disconnected: " + payload); return; } - messages.addMessage(new Message(Message.MessageType.INFO,sender,reciever,payload)); + messages.addMessage(new Message(Message.MessageType.INFO,sender.toString(),reciever.toString(),payload.toString())); System.out.println("DISCONNECT: " + payload); this.setState(DISCONNECTED); - } else if (type.equals(getDataTypeMessage())) { + } else if (type.toString().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)); + messages.addMessage(new Message(Message.MessageType.MESSAGE,sender.toString(),reciever.toString(),payload.toString())); System.out.println("MESSAGE: From " + sender + " to " + reciever + ": "+ payload); - } else if (type.equals(getDataTypeError())) { - messages.addMessage(new Message(Message.MessageType.ERROR,sender,reciever,payload)); + } else if (type.toString().equals(getDataTypeError())) { + messages.addMessage(new Message(Message.MessageType.ERROR,sender.toString(),reciever.toString(),payload.toString())); System.out.println("ERROR: " + payload); } else { System.out.println("Unknown data type received: " + type); @@ -165,26 +151,6 @@ public class ClientConnectionHandler extends ConnectionHandler implements Runnab } } - private void sendData(String sender, String receiver, String type, String payload) { - if (getConnection().isAvailable()) { - new StringBuilder(); - String data = new StringBuilder() - .append(sender+"\n") - .append(receiver+"\n") - .append(type+"\n") - .append(payload+"\n") - .toString(); - try { - getConnection().send(data); - } catch (SocketException e) { - System.err.println("Connection closed: " + e.getMessage()); - } catch (EOFException e) { - System.out.println("Connection terminated by remote"); - } catch(IOException e) { - System.err.println("Communication error: " + e.getMessage()); - } - } - } public void connect() throws ChatProtocolException { if (state.get() != NEW) throw new ChatProtocolException("Illegal state for connect: " + state); @@ -215,5 +181,4 @@ public class ClientConnectionHandler extends ConnectionHandler implements Runnab 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 index f0e31bb..b0ed994 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,5 +1,10 @@ package ch.zhaw.pm2.multichat.protocol; +import java.io.EOFException; +import java.io.IOException; +import java.net.SocketException; +import java.util.Scanner; + public abstract class ConnectionHandler { private NetworkHandler.NetworkConnection connection; @@ -44,4 +49,47 @@ public abstract class ConnectionHandler { protected void setConnection(NetworkHandler.NetworkConnection connection) { this.connection = connection; } + + protected void processData(Scanner scanner, StringBuilder sender, StringBuilder reciever, StringBuilder type, StringBuilder payload) throws ChatProtocolException { + // parse data content + if (scanner.hasNextLine()) { + sender.append(scanner.nextLine()); + } else { + throw new ChatProtocolException("No Sender found"); + } + if (scanner.hasNextLine()) { + reciever.append(scanner.nextLine()); + } else { + throw new ChatProtocolException("No Reciever found"); + } + if (scanner.hasNextLine()) { + type.append(scanner.nextLine()); + } else { + throw new ChatProtocolException("No Type found"); + } + if (scanner.hasNextLine()) { + payload.append(scanner.nextLine()); + } + } + + protected void sendData(String sender, String receiver, String type, String payload) { + if (connection.isAvailable()) { + new StringBuilder(); + String data = new StringBuilder() + .append(sender+"\n") + .append(receiver+"\n") + .append(type+"\n") + .append(payload+"\n") + .toString(); + try { + getConnection().send(data); + } catch (SocketException e) { + System.err.println("Connection closed: " + e.getMessage()); + } catch (EOFException e) { + System.out.println("Connection terminated by remote"); + } catch(IOException e) { + System.err.println("Communication error: " + e.getMessage()); + } + } + } } 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 c45dfca..eac70e8 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 @@ -8,7 +8,6 @@ import ch.zhaw.pm2.multichat.protocol.NetworkHandler; import java.io.EOFException; import java.io.IOException; import java.net.SocketException; -import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.Scanner; @@ -91,43 +90,25 @@ public class ServerConnectionHandler extends ConnectionHandler implements Runnab getConnection().close(); System.out.println("Stopped receiving data."); } catch (IOException e) { - System.err.println("Failed to close connection." + e); + System.err.println("Failed to close connection." + e.getMessage()); } System.out.println("Closed Connection Handler for " + userName); } private void processData(String data) { try { - // parse data content Scanner scanner = new Scanner(data); - String sender = null; - String reciever = null; - String type = null; - String payload = null; - if (scanner.hasNextLine()) { - sender = scanner.nextLine(); - } else { - throw new ChatProtocolException("No Sender found"); - } - if (scanner.hasNextLine()) { - reciever = scanner.nextLine(); - } else { - throw new ChatProtocolException("No Reciever found"); - } - if (scanner.hasNextLine()) { - type = scanner.nextLine(); - } else { - throw new ChatProtocolException("No Type found"); - } - if (scanner.hasNextLine()) { - payload = scanner.nextLine(); - } + StringBuilder sender = new StringBuilder(); + StringBuilder reciever = new StringBuilder(); + StringBuilder type = new StringBuilder(); + StringBuilder payload = new StringBuilder(); + super.processData(scanner,sender,reciever,type,payload); // dispatch operation based on type parameter - if (type.equals(getDataTypeConnect())) { + if (type.toString().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)) { + if (sender.toString().isBlank()) sender.append(this.userName); + if (connectionRegistry.containsKey(sender.toString())) { mutex.lock(); try { state = ERROR; @@ -140,7 +121,7 @@ public class ServerConnectionHandler extends ConnectionHandler implements Runnab } mutex.lock(); try { - this.userName = sender; + this.userName = sender.toString(); nameComplete.signal(); } finally { @@ -149,9 +130,9 @@ public class ServerConnectionHandler extends ConnectionHandler implements Runnab connectionRegistry.put(userName, this); sendData(USER_NONE, userName, getDataTypeConfirm(), "Registration successfull for " + userName); this.state = CONNECTED; - } else if (type.equals(getDataTypeConfirm())) { + } else if (type.toString().equals(getDataTypeConfirm())) { System.out.println("Not expecting to receive a CONFIRM request from client"); - } else if (type.equals(getDataTypeDisconnect())) { + } else if (type.toString().equals(getDataTypeDisconnect())) { if (state == DISCONNECTED) throw new ChatProtocolException("Illegal state for disconnect request: " + state); if (state == CONNECTED) { @@ -160,24 +141,24 @@ public class ServerConnectionHandler extends ConnectionHandler implements Runnab sendData(USER_NONE, userName, getDataTypeConfirm(), "Confirm disconnect of " + userName); this.state = DISCONNECTED; this.stopReceiving(); - } else if (type.equals(getDataTypeMessage())) { + } else if (type.toString().equals(getDataTypeMessage())) { if (state != CONNECTED) throw new ChatProtocolException("Illegal state for message request: " + state); - if (USER_ALL.equals(reciever)) { + if (USER_ALL.equals(reciever.toString())) { for (ServerConnectionHandler handler : connectionRegistry.values()) { - handler.sendData(sender, reciever, type, payload); + handler.sendData(sender.toString(), reciever.toString(), type.toString(), payload.toString()); } } else { - ServerConnectionHandler handler = connectionRegistry.get(reciever); + ServerConnectionHandler handler = connectionRegistry.get(reciever.toString()); if (handler != null) { - handler.sendData(sender, reciever, type, payload); - if(!reciever.equals(sender)){ - sendData(sender, reciever, type, payload); //send message to sender if it's a direct message and sender is not receiver. + handler.sendData(sender.toString(), reciever.toString(), type.toString(), payload.toString()); + if(!reciever.toString().equals(sender.toString())){ + sendData(sender.toString(), reciever.toString(), type.toString(), payload.toString()); //send message to sender if it's a direct message and sender is not receiver. } } else { this.sendData(USER_NONE, userName, getDataTypeError(), "Unknown User: " + reciever); } } - } else if (type.equals(getDataTypeError())) { + } else if (type.toString().equals(getDataTypeError())) { System.err.println("Received error from client (" + sender + "): " + payload); } else { System.err.println("Unknown data type received: " + type); @@ -188,27 +169,4 @@ public class ServerConnectionHandler extends ConnectionHandler implements Runnab sendData(USER_NONE, userName, getDataTypeError(), e.getMessage()); } } - - - - private void sendData(String sender, String receiver, String type, String payload) { - if (getConnection().isAvailable()) { - new StringBuilder(); - String data = new StringBuilder() - .append(sender+"\n") - .append(receiver+"\n") - .append(type+"\n") - .append(payload+"\n") - .toString(); - try { - getConnection().send(data); - } catch (SocketException e) { - System.out.println("Connection closed: " + e.getMessage()); - } catch (EOFException e) { - System.out.println("Connection terminated by remote"); - } catch(IOException e) { - System.out.println("Communication error: " + e.getMessage()); - } - } - } } From 9a534b24653320d6dde6fc501162327e35855913 Mon Sep 17 00:00:00 2001 From: Andrin Fassbind Date: Fri, 15 Apr 2022 16:39:52 +0200 Subject: [PATCH 4/8] Shrink code in the ConnectionHandlers --- .../client/ClientConnectionHandler.java | 73 +++++++----- .../server/ServerConnectionHandler.java | 108 ++++++++++-------- 2 files changed, 104 insertions(+), 77 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 0dd7368..23fec7e 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 @@ -110,38 +110,13 @@ public class ClientConnectionHandler extends ConnectionHandler implements Runnab if (type.toString().equals(getDataTypeConnect())) { System.err.println("Illegal connect request from server"); } else if (type.toString().equals(getDataTypeConfirm())) { - if (state.get() == CONFIRM_CONNECT) { - this.userName.set(reciever.toString()); - this.serverPort.set(getConnection().getRemotePort()); - this.serverAddress.set(getConnection().getRemoteHost()); - messages.addMessage(new Message(Message.MessageType.INFO,sender.toString(),reciever.toString(),payload.toString())); - System.out.println("CONFIRM: " + payload); - this.setState(CONNECTED); - } else if (state.get() == CONFIRM_DISCONNECT) { - messages.addMessage(new Message(Message.MessageType.INFO,sender.toString(),reciever.toString(),payload.toString())); - System.out.println("CONFIRM: " + payload); - this.setState(DISCONNECTED); - } else { - System.err.println("Got unexpected confirm message: " + payload); - } + caseConfirm(sender.toString(), reciever.toString(), payload.toString()); } else if (type.toString().equals(getDataTypeDisconnect())) { - if (state.get() == DISCONNECTED) { - System.out.println("DISCONNECT: Already in disconnected: " + payload); - return; - } - messages.addMessage(new Message(Message.MessageType.INFO,sender.toString(),reciever.toString(),payload.toString())); - System.out.println("DISCONNECT: " + payload); - this.setState(DISCONNECTED); + caseDisconnect(sender.toString(),reciever.toString(),payload.toString()); } else if (type.toString().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.toString(),reciever.toString(),payload.toString())); - System.out.println("MESSAGE: From " + sender + " to " + reciever + ": "+ payload); + caseMessage(sender.toString(),reciever.toString(),payload.toString()); } else if (type.toString().equals(getDataTypeError())) { - messages.addMessage(new Message(Message.MessageType.ERROR,sender.toString(),reciever.toString(),payload.toString())); - System.out.println("ERROR: " + payload); + caseError(sender.toString(), reciever.toString(), payload.toString()); } else { System.out.println("Unknown data type received: " + type); } @@ -151,6 +126,46 @@ public class ClientConnectionHandler extends ConnectionHandler implements Runnab } } + private void caseConfirm(String sender, String reciever, String payload) { + if (state.get() == CONFIRM_CONNECT) { + this.userName.set(reciever); + 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); + } else if (state.get() == CONFIRM_DISCONNECT) { + messages.addMessage(new Message(Message.MessageType.INFO,sender,reciever,payload)); + System.out.println("CONFIRM: " + payload); + this.setState(DISCONNECTED); + } else { + System.err.println("Got unexpected confirm message: " + payload); + } + } + + private void caseDisconnect(String sender, String reciever, String payload) { + if (state.get() == DISCONNECTED) { + System.out.println("DISCONNECT: Already in disconnected: " + payload); + return; + } + messages.addMessage(new Message(Message.MessageType.INFO,sender,reciever,payload)); + System.out.println("DISCONNECT: " + payload); + this.setState(DISCONNECTED); + } + + private void caseMessage(String sender, String reciever, String payload) { + 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); + } + + private void caseError(String sender, String reciever, String payload) { + messages.addMessage(new Message(Message.MessageType.ERROR,sender,reciever,payload)); + System.out.println("ERROR: " + payload); + } public void connect() throws ChatProtocolException { if (state.get() != NEW) throw new ChatProtocolException("Illegal state for connect: " + state); 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 eac70e8..d7f45aa 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 @@ -106,58 +106,13 @@ public class ServerConnectionHandler extends ConnectionHandler implements Runnab // dispatch operation based on type parameter if (type.toString().equals(getDataTypeConnect())) { - if (this.state != NEW) throw new ChatProtocolException("Illegal state for connect request: " + state); - if (sender.toString().isBlank()) sender.append(this.userName); - if (connectionRegistry.containsKey(sender.toString())) { - mutex.lock(); - try { - state = ERROR; - nameComplete.signal(); - } - finally { - mutex.unlock(); - } - throw new ChatProtocolException("User name already taken: " + sender); - } - mutex.lock(); - try { - this.userName = sender.toString(); - nameComplete.signal(); - } - finally { - mutex.unlock(); - } - connectionRegistry.put(userName, this); - sendData(USER_NONE, userName, getDataTypeConfirm(), "Registration successfull for " + userName); - this.state = CONNECTED; + caseConnect(sender.toString()); } else if (type.toString().equals(getDataTypeConfirm())) { System.out.println("Not expecting to receive a CONFIRM request from client"); } else if (type.toString().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, getDataTypeConfirm(), "Confirm disconnect of " + userName); - this.state = DISCONNECTED; - this.stopReceiving(); + caseDisconnect(); } else if (type.toString().equals(getDataTypeMessage())) { - if (state != CONNECTED) throw new ChatProtocolException("Illegal state for message request: " + state); - if (USER_ALL.equals(reciever.toString())) { - for (ServerConnectionHandler handler : connectionRegistry.values()) { - handler.sendData(sender.toString(), reciever.toString(), type.toString(), payload.toString()); - } - } else { - ServerConnectionHandler handler = connectionRegistry.get(reciever.toString()); - if (handler != null) { - handler.sendData(sender.toString(), reciever.toString(), type.toString(), payload.toString()); - if(!reciever.toString().equals(sender.toString())){ - sendData(sender.toString(), reciever.toString(), type.toString(), payload.toString()); //send message to sender if it's a direct message and sender is not receiver. - } - } else { - this.sendData(USER_NONE, userName, getDataTypeError(), "Unknown User: " + reciever); - } - } + caseMessage(sender.toString(), reciever.toString(), type.toString(), payload.toString()); } else if (type.toString().equals(getDataTypeError())) { System.err.println("Received error from client (" + sender + "): " + payload); } else { @@ -169,4 +124,61 @@ public class ServerConnectionHandler extends ConnectionHandler implements Runnab sendData(USER_NONE, userName, getDataTypeError(), e.getMessage()); } } + + private void caseConnect(String sender) throws ChatProtocolException { + if (this.state != NEW) throw new ChatProtocolException("Illegal state for connect request: " + state); + if (sender.isBlank()) sender = this.userName; + if (connectionRegistry.containsKey(sender)) { + mutex.lock(); + try { + state = ERROR; + nameComplete.signal(); + } + finally { + mutex.unlock(); + } + throw new ChatProtocolException("User name already taken: " + sender); + } + mutex.lock(); + try { + this.userName = sender.toString(); + nameComplete.signal(); + } + finally { + mutex.unlock(); + } + connectionRegistry.put(userName, this); + sendData(USER_NONE, userName, getDataTypeConfirm(), "Registration successfull for " + userName); + this.state = CONNECTED; + } + + private void caseDisconnect() throws ChatProtocolException { + if (state == DISCONNECTED) + throw new ChatProtocolException("Illegal state for disconnect request: " + state); + if (state == CONNECTED) { + connectionRegistry.remove(this.userName); + } + sendData(USER_NONE, userName, getDataTypeConfirm(), "Confirm disconnect of " + userName); + this.state = DISCONNECTED; + this.stopReceiving(); + } + + private void caseMessage(String sender, String reciever, String type, String payload) throws ChatProtocolException{ + if (state != CONNECTED) throw new ChatProtocolException("Illegal state for message request: " + state); + if (USER_ALL.equals(reciever)) { + for (ServerConnectionHandler handler : connectionRegistry.values()) { + handler.sendData(sender, reciever, type, payload); + } + } else { + ServerConnectionHandler handler = connectionRegistry.get(reciever); + if (handler != null) { + handler.sendData(sender, reciever, type, payload); + if(!reciever.equals(sender)){ + 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, getDataTypeError(), "Unknown User: " + reciever); + } + } + } } From 73dcfb5058a0b90f0a45f9e74b5627af225496fb Mon Sep 17 00:00:00 2001 From: Andrin Fassbind Date: Fri, 15 Apr 2022 19:41:43 +0200 Subject: [PATCH 5/8] Java Doc Server && ServerConnectionHandler --- .../ch/zhaw/pm2/multichat/server/Server.java | 22 ++++- .../server/ServerConnectionHandler.java | 84 ++++++++++++++++++- 2 files changed, 103 insertions(+), 3 deletions(-) diff --git a/server/src/main/java/ch/zhaw/pm2/multichat/server/Server.java b/server/src/main/java/ch/zhaw/pm2/multichat/server/Server.java index 7c447d6..3167691 100644 --- a/server/src/main/java/ch/zhaw/pm2/multichat/server/Server.java +++ b/server/src/main/java/ch/zhaw/pm2/multichat/server/Server.java @@ -10,7 +10,15 @@ import java.util.Map; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; - +/** + * This Class represents a Server. The user can start the programm with the port number as a argument. + * If no argument has been set the {@link NetworkHandler#DEFAULT_PORT} is used as port number. + * After initialising the server: + * 1. Starts a Socketserver using the logic given in {@link NetworkHandler.NetworkServer#createServer()} + * 2. The server starts to listen for incoming connection using the Logic given in {@link NetworkHandler.NetworkServer#waitForConnection()} + * 3. New conections will be atached to a Connectionhandler: {@link ServerConnectionHandler} and placed in a Map containing all active connections {@link Server#connections} + * + */ public class Server { // Server connection @@ -57,6 +65,11 @@ public class Server { } } + /** + * The Constructor to create a new instance. + * @param serverPort to listen for incoming connections. + * @throws IOException thrown if an I/O error occurs when opening the socket. + */ public Server(int serverPort) throws IOException { // Open server connection System.out.println("Create server connection"); @@ -64,6 +77,10 @@ public class Server { System.out.println("Listening on " + networkServer.getHostAddress() + ":" + networkServer.getHostPort()); } + /** + * With this methode the instance waits for incoming connections. If a client tries to connect to the server. + * The connection will be registered in the connection registry if successful. + */ private void start() { ReentrantLock mutex = new ReentrantLock(); Condition nameComplete = mutex.newCondition(); @@ -104,6 +121,9 @@ public class Server { System.out.println("Server Stopped."); } + /** + * This method will stop the serversocket. + */ public void terminate() { try { System.out.println("Close server port."); 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 d7f45aa..1ab73ed 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 @@ -16,7 +16,31 @@ import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; - +/** + * This class represents the connection between the server and a client and offers the serverside logic. + * The ServerConnectionHandler receives data send from the client aswell as sends data to the client. + * + * The ServeConnectionHandler offers following functionality: + * + * Evaluating connection attempts from a client by: + * 1. Checks if Username is valid (Not used) + * 2. Saves Username in {@link ServerConnectionHandler#userName} + * 3. Saves the connection in the {@link ServerConnectionHandler#connectionRegistry} + * + * Processes disconnections from a client by: + * 1. Removing the connection from the {@link ServerConnectionHandler#connectionRegistry} + * 2. Terminates the socket by calling {@link NetworkHandler.NetworkConnection#close()} + * + * Processes Messages send from a client by: + * 1. Evaluating the reciever by differentiating between broadcast or unicast. + * 2. Sending the message accordingly. + * + * To use this class, start a new instance and start it in a thread. + * To constructor needs following parameter: + * 1. {@link ch.zhaw.pm2.multichat.protocol.NetworkHandler.NetworkConnection} representing the socket connection between server and client + * 2. {@link Map} registry to check for all active connections + * 3. {@link ReentrantLock @link Condition} to lock server thread to evaluate connection. + * */ public class ServerConnectionHandler extends ConnectionHandler implements Runnable{ private static final AtomicInteger connectionCounter = new AtomicInteger(0); private final int connectionId = connectionCounter.incrementAndGet(); @@ -29,11 +53,21 @@ public class ServerConnectionHandler extends ConnectionHandler implements Runnab private String userName = "Anonymous-"+connectionId; private State state = NEW; + /** + * Called when runnable gets started in a thread. + */ @Override public void run() { startReceiving(); } + /** + * Constructor to intitialize the connection + * @param connection representing the socket connection between server and clinet + * @param registry map containing all active connections between server and clients + * @param mutex to lock thread + * @param nameComplete condition to call threads + */ public ServerConnectionHandler(NetworkHandler.NetworkConnection connection, Map registry, ReentrantLock mutex, Condition nameComplete) { super(); @@ -45,14 +79,26 @@ public class ServerConnectionHandler extends ConnectionHandler implements Runnab this.nameComplete = nameComplete; } + /** + * + * @return the username of the connected client + */ public String getUserName() { return this.userName; } + /** + * + * @return state of the connection. Poosible states are see {@link ch.zhaw.pm2.multichat.protocol.ConnectionHandler.State} + */ public State getState() { return state; } + /** + * This methods runs in a whileloop as long as the socket between server and client is available + * and the connection State is not ERROR. + */ private void startReceiving() { System.out.println("Starting Connection Handler for new User"); try { @@ -83,6 +129,9 @@ public class ServerConnectionHandler extends ConnectionHandler implements Runnab } + /** + * This method will call {@link NetworkHandler.NetworkConnection#close()} to close the Socket. + */ private void stopReceiving() { System.out.println("Closing Connection Handler for " + userName); try { @@ -95,6 +144,17 @@ public class ServerConnectionHandler extends ConnectionHandler implements Runnab System.out.println("Closed Connection Handler for " + userName); } + /** + * This method gets called when socket recieves data. The method checks for the data type and reacts accordingly + * If data type: + * 1. Connect => checks if username is valid. if valid sends response to client with confirmation. + * If username not valid quits connection by changing status to ERROR. + * 2. Confirm => Server should not recieve this kind of message. STDOUT informs about it. + * 3. Disconnect => Disconnects connection by removing connection from registry and calling method to terminate socket. + * 4. Message => Checks if broadcast or unicast. Sends data accordingly + * 5. Error => STDERR message + * @param data recieved by the server + */ private void processData(String data) { try { Scanner scanner = new Scanner(data); @@ -125,6 +185,12 @@ public class ServerConnectionHandler extends ConnectionHandler implements Runnab } } + /** + * This method is called by method {@link ServerConnectionHandler#processData(String)} + * Checks if username is valid. if valid sends response to client with confirmation. + * @param sender of the payload + * @throws ChatProtocolException if username not valid + */ private void caseConnect(String sender) throws ChatProtocolException { if (this.state != NEW) throw new ChatProtocolException("Illegal state for connect request: " + state); if (sender.isBlank()) sender = this.userName; @@ -141,7 +207,7 @@ public class ServerConnectionHandler extends ConnectionHandler implements Runnab } mutex.lock(); try { - this.userName = sender.toString(); + this.userName = sender; nameComplete.signal(); } finally { @@ -152,6 +218,11 @@ public class ServerConnectionHandler extends ConnectionHandler implements Runnab this.state = CONNECTED; } + /** + * This method is called by method {@link ServerConnectionHandler#processData(String)} + * Disconnects connection by removing connection from registry and calling method {@link ServerConnectionHandler#stopReceiving()} to terminate socket. + * @throws ChatProtocolException if state allready DISCONNECTED. + */ private void caseDisconnect() throws ChatProtocolException { if (state == DISCONNECTED) throw new ChatProtocolException("Illegal state for disconnect request: " + state); @@ -163,6 +234,15 @@ public class ServerConnectionHandler extends ConnectionHandler implements Runnab this.stopReceiving(); } + /** + * This method is called by method {@link ServerConnectionHandler#processData(String)} + * Checks if broadcast or unicast. Sends data accordingly + * @param sender who sent data + * @param reciever to recieve data + * @param type of message + * @param payload data to transmit + * @throws ChatProtocolException if state not equal to CONNECT + */ private void caseMessage(String sender, String reciever, String type, String payload) throws ChatProtocolException{ if (state != CONNECTED) throw new ChatProtocolException("Illegal state for message request: " + state); if (USER_ALL.equals(reciever)) { From 5257195d8253547362e7d827b527334930c4b8e3 Mon Sep 17 00:00:00 2001 From: Andrin Fassbind Date: Fri, 15 Apr 2022 21:13:28 +0200 Subject: [PATCH 6/8] fixed issue #39 --- .../ch/zhaw/pm2/multichat/server/Server.java | 23 +--------------- .../server/ServerConnectionHandler.java | 27 ++++++++----------- 2 files changed, 12 insertions(+), 38 deletions(-) diff --git a/server/src/main/java/ch/zhaw/pm2/multichat/server/Server.java b/server/src/main/java/ch/zhaw/pm2/multichat/server/Server.java index 3167691..d7ef2cd 100644 --- a/server/src/main/java/ch/zhaw/pm2/multichat/server/Server.java +++ b/server/src/main/java/ch/zhaw/pm2/multichat/server/Server.java @@ -90,32 +90,11 @@ public class Server { NetworkHandler.NetworkConnection connection = networkServer.waitForConnection(); ServerConnectionHandler connectionHandler = new ServerConnectionHandler(connection, connections, mutex, nameComplete); new Thread(connectionHandler).start(); - mutex.lock(); - try { - nameComplete.await(); - if(connectionHandler.getState() == ConnectionHandler.State.ERROR) { - System.out.println(String.format("Connecting failed for new Client with IP:Port <%s:%d>.\nReason: Name already taken.", - connection.getRemoteHost(), - connection.getRemotePort())); - } - else { - System.out.println(String.format("Connected new Client %s with IP:Port <%s:%d>", - connectionHandler.getUserName(), - connection.getRemoteHost(), - connection.getRemotePort())); - } - } - finally { - mutex.unlock(); - } } } catch(SocketException e) { System.out.println("Server connection terminated"); - } - catch (IOException e) { + } catch (IOException e) { System.err.println("Communication error " + e); - } catch (InterruptedException e) { - throw new RuntimeException(e); } // close server System.out.println("Server Stopped."); 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 1ab73ed..9a7cb91 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 @@ -194,28 +194,23 @@ public class ServerConnectionHandler extends ConnectionHandler implements Runnab private void caseConnect(String sender) throws ChatProtocolException { if (this.state != NEW) throw new ChatProtocolException("Illegal state for connect request: " + state); if (sender.isBlank()) sender = this.userName; + //if username not valid if (connectionRegistry.containsKey(sender)) { - mutex.lock(); - try { - state = ERROR; - nameComplete.signal(); - } - finally { - mutex.unlock(); - } + state = ERROR; + System.out.println(String.format("Connecting failed for new Client with IP:Port <%s:%d>.\nReason: Name already taken.", + getConnection().getRemoteHost(), + getConnection().getRemotePort())); throw new ChatProtocolException("User name already taken: " + sender); } - mutex.lock(); - try { - this.userName = sender; - nameComplete.signal(); - } - finally { - mutex.unlock(); - } + //if username valid + this.userName = sender; connectionRegistry.put(userName, this); sendData(USER_NONE, userName, getDataTypeConfirm(), "Registration successfull for " + userName); this.state = CONNECTED; + System.out.println(String.format("Connected new Client %s with IP:Port <%s:%d>", + userName, + getConnection().getRemoteHost(), + getConnection().getRemotePort())); } /** From e53a751081476e8392b7c04bde54b146b1afa867 Mon Sep 17 00:00:00 2001 From: schrom01 Date: Fri, 15 Apr 2022 21:20:03 +0200 Subject: [PATCH 7/8] fixed Problem setting Username in ClientConnectionHandler #42 --- .../ch/zhaw/pm2/multichat/client/ClientConnectionHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 23fec7e..b5b72f9 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 @@ -39,7 +39,7 @@ public class ClientConnectionHandler extends ConnectionHandler implements Runnab this.serverAddress.set(serverAddress); this.serverPort.set(serverPort); setConnection(NetworkHandler.openConnection(serverAddress, serverPort)); - this.userName = new SimpleStringProperty((userName == null || userName.isBlank())? USER_NONE : userName); + this.userName.set((userName == null || userName.isBlank())? USER_NONE : userName); } public SimpleStringProperty getServerAddressProperty() { return serverAddress; } From a5069e938f825b4a005d1509a6571e3f5c3bb387 Mon Sep 17 00:00:00 2001 From: Andrin Fassbind Date: Fri, 15 Apr 2022 22:06:22 +0200 Subject: [PATCH 8/8] javadoc --- .../multichat/protocol/ConnectionHandler.java | 53 ++++++++++++++++++- 1 file changed, 52 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 b0ed994..21eacc8 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 @@ -5,6 +5,11 @@ import java.io.IOException; import java.net.SocketException; import java.util.Scanner; +/** + * This abstract class is the superclass for ClientConnectionHandler and ServerConnectionHandler + * It offers the DATA_TYPE Strings and a {@link State} enum for all valid connection states. + * Shared methods are implemented in this class aswell {@link ConnectionHandler#sendData(String, String, String, String)} {@link ConnectionHandler#processData(Scanner, StringBuilder, StringBuilder, StringBuilder, StringBuilder)} + */ public abstract class ConnectionHandler { private NetworkHandler.NetworkConnection connection; @@ -18,38 +23,77 @@ public abstract class ConnectionHandler { public static final String USER_NONE = ""; public static final String USER_ALL = "*"; + // State of the connection public enum State { NEW, CONFIRM_CONNECT, CONNECTED, CONFIRM_DISCONNECT, DISCONNECTED, ERROR; } + /** + * + * @return {@link ConnectionHandler#DATA_TYPE_CONNECT} + */ public static String getDataTypeConnect() { return DATA_TYPE_CONNECT; } + /** + * + * @return {@link ConnectionHandler#DATA_TYPE_CONFIRM} + */ public static String getDataTypeConfirm() { return DATA_TYPE_CONFIRM; } + /** + * + * @return {@link ConnectionHandler#DATA_TYPE_DISCONNECT} + */ public static String getDataTypeDisconnect() { return DATA_TYPE_DISCONNECT; } + /** + * + * @return {@link ConnectionHandler#DATA_TYPE_MESSAGE + */ public static String getDataTypeMessage() { return DATA_TYPE_MESSAGE; } + /** + * + * @return {@link ConnectionHandler#DATA_TYPE_ERROR} + */ public static String getDataTypeError() { return DATA_TYPE_ERROR; } + /** + * + * @return {@link NetworkHandler.NetworkConnection} + */ public NetworkHandler.NetworkConnection getConnection() { return connection; } + /** + * This method sets the NetworkConnection used for the server <-> client connection + * @param connection NetworkConnection used for the server <-> client connection + */ protected void setConnection(NetworkHandler.NetworkConnection connection) { this.connection = connection; } + /** + * This method reads the data when a ConnectionHandler recieves it. It tries to read out the sender, reciever, type and payload. + * If the data does not contain the expected number of lines, it throws a {@link ChatProtocolException} + * @param scanner to read data + * @param sender of the data + * @param reciever for the data + * @param type of data + * @param payload the data sent + * @throws ChatProtocolException if the data does not contain the expected number of lines + */ protected void processData(Scanner scanner, StringBuilder sender, StringBuilder reciever, StringBuilder type, StringBuilder payload) throws ChatProtocolException { // parse data content if (scanner.hasNextLine()) { @@ -72,6 +116,13 @@ public abstract class ConnectionHandler { } } + /** + * This method gets called to send data via the socket defined in the {@link NetworkHandler.NetworkConnection} + * @param sender of the data + * @param receiver of the data + * @param type of the data + * @param payload of the data + */ protected void sendData(String sender, String receiver, String type, String payload) { if (connection.isAvailable()) { new StringBuilder(); @@ -82,7 +133,7 @@ public abstract class ConnectionHandler { .append(payload+"\n") .toString(); try { - getConnection().send(data); + connection.send(data); } catch (SocketException e) { System.err.println("Connection closed: " + e.getMessage()); } catch (EOFException e) {