From 28376f12424f29460605e613a09f4e71a746f2ab Mon Sep 17 00:00:00 2001 From: Leonardo Brandenberger Date: Fri, 15 Apr 2022 04:57:46 +0200 Subject: [PATCH 01/22] trying to fix MVC --- .../client/ChatWindowController.java | 49 ++++++++++++------- .../pm2/multichat/client/ChatWindowModel.java | 29 +++++++++++ 2 files changed, 60 insertions(+), 18 deletions(-) create mode 100644 client/src/main/java/ch/zhaw/pm2/multichat/client/ChatWindowModel.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 5bde847..30cd50c 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 @@ -7,6 +7,7 @@ import ch.zhaw.pm2.multichat.protocol.NetworkHandler; import javafx.application.Platform; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; +import javafx.collections.FXCollections; import javafx.event.EventHandler; import javafx.fxml.FXML; import javafx.scene.control.Button; @@ -25,21 +26,33 @@ public class ChatWindowController { private final WindowCloseHandler windowCloseHandler = new WindowCloseHandler(); - @FXML private Pane rootPane; - @FXML private TextField serverAddressField; - @FXML private TextField serverPortField; - @FXML private TextField userNameField; - @FXML private TextField messageField; - @FXML private TextArea messageArea; - @FXML private Button connectButton; - @FXML private Button sendButton; - @FXML private TextField filterValue; + @FXML + private Pane rootPane; + @FXML + private TextField serverAddressField; + @FXML + private TextField serverPortField; + @FXML + private TextField userNameField; + @FXML + private TextField messageField; + @FXML + private TextArea messageArea; + @FXML + private Button connectButton; + @FXML + private Button sendButton; + @FXML + private TextField filterValue; + private ChatWindowModel model = new ChatWindowModel(); @FXML public void initialize() { - serverAddressField.setText(NetworkHandler.DEFAULT_ADDRESS.getCanonicalHostName()); - serverPortField.setText(String.valueOf(NetworkHandler.DEFAULT_PORT)); + serverAddressField.setText(model.getServerAddress()); // binding server address to view + model.setServerAddress(NetworkHandler.DEFAULT_ADDRESS.getCanonicalHostName()); // sets modell adress + serverPortField.setText(model.getServerPort()); + model.setServerPort(String.valueOf(NetworkHandler.DEFAULT_PORT)); } public void setMessages(ClientMessageList messages) { @@ -47,7 +60,7 @@ public class ChatWindowController { messageListener(); } - public void setConnectionHandler(ClientConnectionHandler connectionHandler){ + public void setConnectionHandler(ClientConnectionHandler connectionHandler) { this.connectionHandler = connectionHandler; } @@ -56,7 +69,7 @@ public class ChatWindowController { } @FXML - private void toggleConnection () { + private void toggleConnection() { if (connectionHandler == null || connectionHandler.getStateProperty().get() != CONNECTED) { connect(); } else { @@ -69,7 +82,7 @@ public class ChatWindowController { messages.clear(); // clear message list startConnectionHandler(); connectionHandler.connect(); - } catch(ChatProtocolException | IOException e) { + } catch (ChatProtocolException | IOException e) { addError(e.getMessage()); } } @@ -103,13 +116,13 @@ public class ChatWindowController { } @FXML - private void applyFilter( ) { - this.redrawMessageList(); + private void applyFilter() { + this.redrawMessageList(); } private void startConnectionHandler() throws IOException { String userName = userNameField.getText(); - if(!userName.contains(" ")) { + if (!userName.contains(" ")) { String serverAddress = serverAddressField.getText(); int serverPort = Integer.parseInt(serverPortField.getText()); connectionHandler.initialize(serverAddress, serverPort, userName); @@ -133,7 +146,7 @@ public class ChatWindowController { connectButton.setText((newState == CONNECTED || newState == CONFIRM_DISCONNECT) ? "Disconnect" : "Connect"); } }); - if(newState == DISCONNECTED){ + if (newState == DISCONNECTED) { connectionHandler.stopReceiving(); } } diff --git a/client/src/main/java/ch/zhaw/pm2/multichat/client/ChatWindowModel.java b/client/src/main/java/ch/zhaw/pm2/multichat/client/ChatWindowModel.java new file mode 100644 index 0000000..261283f --- /dev/null +++ b/client/src/main/java/ch/zhaw/pm2/multichat/client/ChatWindowModel.java @@ -0,0 +1,29 @@ +package ch.zhaw.pm2.multichat.client; + +import javafx.beans.value.ObservableStringValue; + +public class ChatWindowModel { + private ClientConnectionHandler connectionHandler; + private ClientMessageList messages; + ObservableStringValue serverAddress; + ObservableStringValue serverPort; + + + public String getServerAddress() { + return serverAddress.get(); + } + + public void setServerAddress(String address) { + setServerAddress(address); + } + + public String getServerPort() { + return getServerPort(); + } + + public void setServerPort(String address) { + setServerPort(address); + } + + +} From 9e027b5c7abd84f3cb679e1426f2811e26f893db Mon Sep 17 00:00:00 2001 From: schrom01 Date: Fri, 15 Apr 2022 15:34:03 +0200 Subject: [PATCH 02/22] sovled #42 --- .../client/ChatWindowController.java | 65 +++++++------------ .../client/ClientConnectionHandler.java | 9 ++- 2 files changed, 29 insertions(+), 45 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 30cd50c..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,12 +2,9 @@ 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; -import javafx.collections.FXCollections; import javafx.event.EventHandler; import javafx.fxml.FXML; import javafx.scene.control.Button; @@ -26,33 +23,20 @@ public class ChatWindowController { private final WindowCloseHandler windowCloseHandler = new WindowCloseHandler(); - @FXML - private Pane rootPane; - @FXML - private TextField serverAddressField; - @FXML - private TextField serverPortField; - @FXML - private TextField userNameField; - @FXML - private TextField messageField; - @FXML - private TextArea messageArea; - @FXML - private Button connectButton; - @FXML - private Button sendButton; - @FXML - private TextField filterValue; + @FXML private Pane rootPane; + @FXML private TextField serverAddressField; + @FXML private TextField serverPortField; + @FXML private TextField userNameField; + @FXML private TextField messageField; + @FXML private TextArea messageArea; + @FXML private Button connectButton; + @FXML private Button sendButton; + @FXML private TextField filterValue; - private ChatWindowModel model = new ChatWindowModel(); @FXML public void initialize() { - serverAddressField.setText(model.getServerAddress()); // binding server address to view - model.setServerAddress(NetworkHandler.DEFAULT_ADDRESS.getCanonicalHostName()); // sets modell adress - serverPortField.setText(model.getServerPort()); - model.setServerPort(String.valueOf(NetworkHandler.DEFAULT_PORT)); + } public void setMessages(ClientMessageList messages) { @@ -60,8 +44,11 @@ public class ChatWindowController { messageListener(); } - public void setConnectionHandler(ClientConnectionHandler connectionHandler) { + public void setConnectionHandler(ClientConnectionHandler connectionHandler){ this.connectionHandler = connectionHandler; + startConnectionHandlerListener(); + serverAddressField.setText(connectionHandler.getServerAddressProperty().get()); + serverPortField.setText(String.valueOf(connectionHandler.getServerPortProperty().get())); } private void applicationClose() { @@ -69,7 +56,7 @@ public class ChatWindowController { } @FXML - private void toggleConnection() { + private void toggleConnection () { if (connectionHandler == null || connectionHandler.getStateProperty().get() != CONNECTED) { connect(); } else { @@ -82,7 +69,7 @@ public class ChatWindowController { messages.clear(); // clear message list startConnectionHandler(); connectionHandler.connect(); - } catch (ChatProtocolException | IOException e) { + } catch(ChatProtocolException | IOException e) { addError(e.getMessage()); } } @@ -116,20 +103,20 @@ public class ChatWindowController { } @FXML - private void applyFilter() { - this.redrawMessageList(); + private void applyFilter( ) { + Platform.runLater(() -> this.messageArea.setText(messages.getFilteredMessages(filterValue.getText().strip()))); } private void startConnectionHandler() throws IOException { String userName = userNameField.getText(); - if (!userName.contains(" ")) { + if(!userName.contains(" ")) { String serverAddress = serverAddressField.getText(); int serverPort = Integer.parseInt(serverPortField.getText()); connectionHandler.initialize(serverAddress, serverPort, userName); new Thread(connectionHandler).start(); //register Listener - startListener(); + //startConnectionHandlerListener(); // register window close handler rootPane.getScene().getWindow().addEventHandler(WindowEvent.WINDOW_CLOSE_REQUEST, windowCloseHandler); @@ -146,7 +133,7 @@ public class ChatWindowController { connectButton.setText((newState == CONNECTED || newState == CONFIRM_DISCONNECT) ? "Disconnect" : "Connect"); } }); - if (newState == DISCONNECTED) { + if(newState == DISCONNECTED){ connectionHandler.stopReceiving(); } } @@ -182,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) { @@ -228,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 77b3057911ffdae80fd2bb390ed88b0ebd9ff7c7 Mon Sep 17 00:00:00 2001 From: schrom01 Date: Fri, 15 Apr 2022 15:35:04 +0200 Subject: [PATCH 03/22] 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 5f6266c01712d2b39398ed6aedcd1afc9fa0eca0 Mon Sep 17 00:00:00 2001 From: Leonardo Brandenberger Date: Fri, 15 Apr 2022 17:09:15 +0200 Subject: [PATCH 04/22] Java Doc and CodeStyle improvements --- .../client/ChatWindowController.java | 48 +++++++++---- .../client/ClientConnectionHandler.java | 70 +++++++++++-------- .../multichat/client/ClientMessageList.java | 31 ++++---- .../zhaw/pm2/multichat/client/ClientUI.java | 6 +- .../ch/zhaw/pm2/multichat/client/Message.java | 26 +++---- .../ch/zhaw/pm2/multichat/server/Server.java | 4 ++ 6 files changed, 110 insertions(+), 75 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 b780b49..a5ddbaa 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 @@ -17,21 +17,31 @@ import java.io.IOException; import static ch.zhaw.pm2.multichat.protocol.ConnectionHandler.State.*; + public class ChatWindowController { private ClientConnectionHandler connectionHandler; private ClientMessageList messages; private final WindowCloseHandler windowCloseHandler = new WindowCloseHandler(); - @FXML private Pane rootPane; - @FXML private TextField serverAddressField; - @FXML private TextField serverPortField; - @FXML private TextField userNameField; - @FXML private TextField messageField; - @FXML private TextArea messageArea; - @FXML private Button connectButton; - @FXML private Button sendButton; - @FXML private TextField filterValue; + @FXML + private Pane rootPane; + @FXML + private TextField serverAddressField; + @FXML + private TextField serverPortField; + @FXML + private TextField userNameField; + @FXML + private TextField messageField; + @FXML + private TextArea messageArea; + @FXML + private Button connectButton; + @FXML + private Button sendButton; + @FXML + private TextField filterValue; public void setMessages(ClientMessageList messages) { @@ -39,7 +49,7 @@ public class ChatWindowController { messageListener(); } - public void setConnectionHandler(ClientConnectionHandler connectionHandler){ + public void setConnectionHandler(ClientConnectionHandler connectionHandler) { this.connectionHandler = connectionHandler; startConnectionHandlerListener(); serverAddressField.setText(connectionHandler.getServerAddressProperty().get()); @@ -51,7 +61,7 @@ public class ChatWindowController { } @FXML - private void toggleConnection () { + private void toggleConnection() { if (connectionHandler == null || connectionHandler.getStateProperty().get() != CONNECTED) { connect(); } else { @@ -59,16 +69,20 @@ public class ChatWindowController { } } + private void connect() { try { messages.clear(); // clear message list startConnectionHandler(); connectionHandler.connect(); - } catch(ChatProtocolException | IOException e) { + } catch (ChatProtocolException | IOException e) { addError(e.getMessage()); } } + /** + * Initiates disconnecting of the connectionHandler, also checks if connectionHandler is avaible. + */ private void disconnect() { if (connectionHandler == null) { addError("No connection handler"); @@ -81,6 +95,9 @@ public class ChatWindowController { } } + /** + * + */ @FXML private void message() { String messageString = messageField.getText().strip(); @@ -98,13 +115,13 @@ public class ChatWindowController { } @FXML - private void applyFilter( ) { + private void applyFilter() { Platform.runLater(() -> this.messageArea.setText(messages.getFilteredMessages(filterValue.getText().strip()))); } private void startConnectionHandler() throws IOException { String userName = userNameField.getText(); - if(!userName.contains(" ")) { + if (!userName.contains(" ")) { String serverAddress = serverAddressField.getText(); int serverPort = Integer.parseInt(serverPortField.getText()); connectionHandler.initialize(serverAddress, serverPort, userName); @@ -128,7 +145,7 @@ public class ChatWindowController { connectButton.setText((newState == CONNECTED || newState == CONFIRM_DISCONNECT) ? "Disconnect" : "Connect"); } }); - if(newState == DISCONNECTED){ + if (newState == DISCONNECTED) { connectionHandler.stopReceiving(); } } @@ -200,6 +217,7 @@ public class ChatWindowController { }); } + private void messageListener() { messages.getChangedProperty().addListener(new ChangeListener() { @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 f67ceb0..da5c1a9 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 @@ -13,19 +13,20 @@ 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.*; public class ClientConnectionHandler extends ConnectionHandler implements Runnable { - private final Pattern messagePattern = Pattern.compile( "^(?:@(\\S*))?\\s*(.*)$" ); + private final Pattern messagePattern = Pattern.compile("^(?:@(\\S*))?\\s*(.*)$"); private SimpleStringProperty userName; - private SimpleObjectProperty state; - private ClientMessageList messages; - private SimpleStringProperty serverAddress; - private SimpleIntegerProperty serverPort; + private final SimpleObjectProperty state; + private final ClientMessageList messages; + private final SimpleStringProperty serverAddress; + private final SimpleIntegerProperty serverPort; - public ClientConnectionHandler(ClientMessageList messages) { + public ClientConnectionHandler(ClientMessageList messages) { super(); this.messages = messages; state = new SimpleObjectProperty<>(State.NEW); @@ -39,24 +40,30 @@ 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 = new SimpleStringProperty((userName == null || userName.isBlank()) ? USER_NONE : userName); } - public SimpleStringProperty getServerAddressProperty() { return serverAddress; } + public SimpleStringProperty getServerAddressProperty() { + return serverAddress; + } - public SimpleIntegerProperty getServerPortProperty() { return serverPort; } + public SimpleIntegerProperty getServerPortProperty() { + return serverPort; + } public SimpleObjectProperty getStateProperty() { return this.state; } - public SimpleStringProperty getUserNameProperty() { return userName; } + public SimpleStringProperty getUserNameProperty() { + return userName; + } - public void setState (State newState) { + public void setState(State newState) { state.set(newState); } - public void run () { + public void run() { startReceiving(); } @@ -77,9 +84,9 @@ public class ClientConnectionHandler extends ConnectionHandler implements Runnab System.out.println("Connection terminated by remote"); this.setState(DISCONNECTED); System.err.println("Unregistered because connection terminated" + e.getMessage()); - } catch(IOException e) { + } catch (IOException e) { System.err.println("Communication error" + e); - } catch(ClassNotFoundException e) { + } catch (ClassNotFoundException e) { System.err.println("Received object of unknown type" + e.getMessage()); } System.out.println("Stopped Connection Handler"); @@ -131,11 +138,11 @@ public class ClientConnectionHandler extends ConnectionHandler implements Runnab this.userName.set(reciever); 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, 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)); + messages.addMessage(new Message(Message.MessageType.INFO, sender, reciever, payload)); System.out.println("CONFIRM: " + payload); this.setState(DISCONNECTED); } else { @@ -146,7 +153,7 @@ public class ClientConnectionHandler extends ConnectionHandler implements Runnab 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, reciever, payload)); System.out.println("DISCONNECT: " + payload); this.setState(DISCONNECTED); } else if (type.equals(getDataTypeMessage())) { @@ -154,10 +161,10 @@ public class ClientConnectionHandler extends ConnectionHandler implements Runnab 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); + messages.addMessage(new Message(Message.MessageType.MESSAGE, sender, reciever, payload)); + System.out.println("MESSAGE: From " + sender + " to " + reciever + ": " + payload); } else if (type.equals(getDataTypeError())) { - messages.addMessage(new Message(Message.MessageType.ERROR,sender,reciever,payload)); + messages.addMessage(new Message(Message.MessageType.ERROR, sender, reciever, payload)); System.out.println("ERROR: " + payload); } else { System.out.println("Unknown data type received: " + type); @@ -172,18 +179,18 @@ public class ClientConnectionHandler extends ConnectionHandler implements Runnab if (getConnection().isAvailable()) { new StringBuilder(); String data = new StringBuilder() - .append(sender+"\n") - .append(receiver+"\n") - .append(type+"\n") - .append(payload+"\n") - .toString(); + .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) { + } catch (IOException e) { System.err.println("Communication error: " + e.getMessage()); } } @@ -191,13 +198,14 @@ public class ClientConnectionHandler extends ConnectionHandler implements Runnab public void connect() throws ChatProtocolException { if (state.get() != NEW) throw new ChatProtocolException("Illegal state for connect: " + state); - this.sendData(userName.get(), USER_NONE, getDataTypeConnect(),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, getDataTypeDisconnect(),null); + if (state.get() != NEW && state.get() != CONNECTED) + throw new ChatProtocolException("Illegal state for disconnect: " + state); + this.sendData(userName.get(), USER_NONE, getDataTypeDisconnect(), null); this.setState(CONFIRM_DISCONNECT); } @@ -208,11 +216,11 @@ public class ClientConnectionHandler extends ConnectionHandler implements Runnab if (matcher.find()) { String receiver = matcher.group(1); String message = matcher.group(2); - if(message.length() < 1){ + if (message.length() < 1) { return false; } if (receiver == null || receiver.isBlank()) receiver = ClientConnectionHandler.USER_ALL; - this.sendData(userName.get(), receiver, getDataTypeMessage(),message); + this.sendData(userName.get(), receiver, getDataTypeMessage(), message); return true; } else { return false; diff --git a/client/src/main/java/ch/zhaw/pm2/multichat/client/ClientMessageList.java b/client/src/main/java/ch/zhaw/pm2/multichat/client/ClientMessageList.java index 7105c5b..5f3f48f 100644 --- a/client/src/main/java/ch/zhaw/pm2/multichat/client/ClientMessageList.java +++ b/client/src/main/java/ch/zhaw/pm2/multichat/client/ClientMessageList.java @@ -7,35 +7,38 @@ import java.util.List; public class ClientMessageList { private List messages = new ArrayList<>(); - private SimpleBooleanProperty changed = new SimpleBooleanProperty(false); + private final SimpleBooleanProperty changed = new SimpleBooleanProperty(false); public void addMessage(Message message) { - messages.add(message); - changed.set(!changed.get()); + messages.add(message); + changed.set(!changed.get()); } - public String getFilteredMessages(String filter) { + public String getFilteredMessages(String filter) { StringBuilder result = new StringBuilder(); - boolean showAll = filter == null || filter.isBlank(); - for(Message message : messages) { - if(showAll || message.matchesFilter(filter)) - { + boolean showAll = filter == null || filter.isBlank(); + for (Message message : messages) { + if (showAll || message.matchesFilter(filter)) { switch (message.getType()) { - case MESSAGE -> result.append(String.format("[%s -> %s] %s\n", message.getSender(), message.getReceiver(), message.getText())); + case MESSAGE -> + result.append(String.format("[%s -> %s] %s\n", message.getSender(), message.getReceiver(), message.getText())); case ERROR -> result.append(String.format("[ERROR] %s\n", message.getText())); case INFO -> result.append(String.format("[INFO] %s\n", message.getText())); - default -> result.append(String.format("[ERROR] %s\n", "Unexpected message type: " + message.getType())); + default -> + result.append(String.format("[ERROR] %s\n", "Unexpected message type: " + message.getType())); } - } - } + } + } return result.toString(); - } + } public void clear() { messages = new ArrayList<>(); changed.set(!changed.get()); } - public SimpleBooleanProperty getChangedProperty() { return changed; } + public SimpleBooleanProperty getChangedProperty() { + return changed; + } } diff --git a/client/src/main/java/ch/zhaw/pm2/multichat/client/ClientUI.java b/client/src/main/java/ch/zhaw/pm2/multichat/client/ClientUI.java index 67a1d96..5fbdbf5 100644 --- a/client/src/main/java/ch/zhaw/pm2/multichat/client/ClientUI.java +++ b/client/src/main/java/ch/zhaw/pm2/multichat/client/ClientUI.java @@ -7,8 +7,8 @@ import javafx.scene.layout.Pane; import javafx.stage.Stage; public class ClientUI extends Application { - private ClientMessageList clientMessageList = new ClientMessageList(); - private ClientConnectionHandler connectionHandler = new ClientConnectionHandler(clientMessageList); + private final ClientMessageList clientMessageList = new ClientMessageList(); + private final ClientConnectionHandler connectionHandler = new ClientConnectionHandler(clientMessageList); @Override public void start(Stage primaryStage) { @@ -34,7 +34,7 @@ public class ClientUI extends Application { primaryStage.setTitle("Multichat Client"); primaryStage.show(); primaryStage.setMinWidth(primaryStage.getWidth()); //use automatically computed size as Minimum Size. - } catch(Exception e) { + } catch (Exception e) { System.err.println("Error starting up UI" + e.getMessage()); } } diff --git a/client/src/main/java/ch/zhaw/pm2/multichat/client/Message.java b/client/src/main/java/ch/zhaw/pm2/multichat/client/Message.java index d805f68..f30a445 100644 --- a/client/src/main/java/ch/zhaw/pm2/multichat/client/Message.java +++ b/client/src/main/java/ch/zhaw/pm2/multichat/client/Message.java @@ -1,20 +1,21 @@ package ch.zhaw.pm2.multichat.client; /** -A Message object represents one Message of a client. Can be stored in ClientMessageList. + * A Message object represents one Message of a client. Can be stored in ClientMessageList. */ public class Message { - private MessageType type; - private String sender; - private String receiver; - private String text; + private final MessageType type; + private final String sender; + private final String receiver; + private final String text; /** * Constructor of Message. Needs all Information about a Message to save them. - * @param type Message (if it's a message typed by a user), Error or Information (if it is generated automatically, in this case sender and reciever will be null) - * @param sender The User who has sent the message. + * + * @param type Message (if it's a message typed by a user), Error or Information (if it is generated automatically, in this case sender and reciever will be null) + * @param sender The User who has sent the message. * @param receiver The User who should recieve the message. - * @param text The Text of the message. + * @param text The Text of the message. */ public Message(MessageType type, String sender, String receiver, String text) { this.type = type; @@ -25,13 +26,14 @@ public class Message { /** * Checks if the Filter String is contained in one of the datafields sender, receiver or text + * * @param filter The Filter String * @return true if it the Filter String is contained in a datafield. */ - public boolean matchesFilter(String filter){ + public boolean matchesFilter(String filter) { return (sender != null && sender.contains(filter)) || - (receiver != null && receiver.contains(filter)) || - (text != null && text.contains(filter)); + (receiver != null && receiver.contains(filter)) || + (text != null && text.contains(filter)); } /** @@ -63,7 +65,7 @@ public class Message { } /** - * Enummeration of Message Types. + * Enumeration of Message Types. */ public enum MessageType { INFO, MESSAGE, ERROR; 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..01d9e32 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 @@ -19,6 +19,10 @@ public class Server { // Connection registry private Map connections = new HashMap<>(); + /** + * + * @param args + */ public static void main(String[] args) { // Parse arguments for server port. try { From 46b0e4386a7d805bda4ca46269028fab1c969b0d Mon Sep 17 00:00:00 2001 From: schrom01 Date: Fri, 15 Apr 2022 15:34:03 +0200 Subject: [PATCH 05/22] sovled #42 --- .../client/ChatWindowController.java | 53 +++++++------------ 1 file changed, 20 insertions(+), 33 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 a5ddbaa..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 @@ -17,39 +17,34 @@ import java.io.IOException; import static ch.zhaw.pm2.multichat.protocol.ConnectionHandler.State.*; - public class ChatWindowController { private ClientConnectionHandler connectionHandler; private ClientMessageList messages; private final WindowCloseHandler windowCloseHandler = new WindowCloseHandler(); - @FXML - private Pane rootPane; - @FXML - private TextField serverAddressField; - @FXML - private TextField serverPortField; - @FXML - private TextField userNameField; - @FXML - private TextField messageField; - @FXML - private TextArea messageArea; - @FXML - private Button connectButton; - @FXML - private Button sendButton; - @FXML - private TextField filterValue; + @FXML private Pane rootPane; + @FXML private TextField serverAddressField; + @FXML private TextField serverPortField; + @FXML private TextField userNameField; + @FXML private TextField messageField; + @FXML private TextArea messageArea; + @FXML private Button connectButton; + @FXML private Button sendButton; + @FXML private TextField filterValue; + @FXML + public void initialize() { + + } + public void setMessages(ClientMessageList messages) { this.messages = messages; messageListener(); } - public void setConnectionHandler(ClientConnectionHandler connectionHandler) { + public void setConnectionHandler(ClientConnectionHandler connectionHandler){ this.connectionHandler = connectionHandler; startConnectionHandlerListener(); serverAddressField.setText(connectionHandler.getServerAddressProperty().get()); @@ -61,7 +56,7 @@ public class ChatWindowController { } @FXML - private void toggleConnection() { + private void toggleConnection () { if (connectionHandler == null || connectionHandler.getStateProperty().get() != CONNECTED) { connect(); } else { @@ -69,20 +64,16 @@ public class ChatWindowController { } } - private void connect() { try { messages.clear(); // clear message list startConnectionHandler(); connectionHandler.connect(); - } catch (ChatProtocolException | IOException e) { + } catch(ChatProtocolException | IOException e) { addError(e.getMessage()); } } - /** - * Initiates disconnecting of the connectionHandler, also checks if connectionHandler is avaible. - */ private void disconnect() { if (connectionHandler == null) { addError("No connection handler"); @@ -95,9 +86,6 @@ public class ChatWindowController { } } - /** - * - */ @FXML private void message() { String messageString = messageField.getText().strip(); @@ -115,13 +103,13 @@ public class ChatWindowController { } @FXML - private void applyFilter() { + private void applyFilter( ) { Platform.runLater(() -> this.messageArea.setText(messages.getFilteredMessages(filterValue.getText().strip()))); } private void startConnectionHandler() throws IOException { String userName = userNameField.getText(); - if (!userName.contains(" ")) { + if(!userName.contains(" ")) { String serverAddress = serverAddressField.getText(); int serverPort = Integer.parseInt(serverPortField.getText()); connectionHandler.initialize(serverAddress, serverPort, userName); @@ -145,7 +133,7 @@ public class ChatWindowController { connectButton.setText((newState == CONNECTED || newState == CONFIRM_DISCONNECT) ? "Disconnect" : "Connect"); } }); - if (newState == DISCONNECTED) { + if(newState == DISCONNECTED){ connectionHandler.stopReceiving(); } } @@ -217,7 +205,6 @@ public class ChatWindowController { }); } - private void messageListener() { messages.getChangedProperty().addListener(new ChangeListener() { @Override From 502297442723d300d2dcbede3cc10cfdcbee8756 Mon Sep 17 00:00:00 2001 From: Andrin Fassbind Date: Fri, 15 Apr 2022 16:25:45 +0200 Subject: [PATCH 06/22] Shrink code in the ConnectionHandlers --- .../client/ClientConnectionHandler.java | 120 ++++++------------ .../multichat/protocol/ConnectionHandler.java | 48 +++++++ .../server/ServerConnectionHandler.java | 82 +++--------- 3 files changed, 105 insertions(+), 145 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 da5c1a9..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 @@ -13,20 +13,19 @@ 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.*; public class ClientConnectionHandler extends ConnectionHandler implements Runnable { - private final Pattern messagePattern = Pattern.compile("^(?:@(\\S*))?\\s*(.*)$"); + private final Pattern messagePattern = Pattern.compile( "^(?:@(\\S*))?\\s*(.*)$" ); private SimpleStringProperty userName; - private final SimpleObjectProperty state; - private final ClientMessageList messages; - private final SimpleStringProperty serverAddress; - private final SimpleIntegerProperty serverPort; + private SimpleObjectProperty state; + private ClientMessageList messages; + private SimpleStringProperty serverAddress; + private SimpleIntegerProperty serverPort; - public ClientConnectionHandler(ClientMessageList messages) { + public ClientConnectionHandler(ClientMessageList messages) { super(); this.messages = messages; state = new SimpleObjectProperty<>(State.NEW); @@ -40,30 +39,24 @@ 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 = new SimpleStringProperty((userName == null || userName.isBlank())? USER_NONE : userName); } - public SimpleStringProperty getServerAddressProperty() { - return serverAddress; - } + public SimpleStringProperty getServerAddressProperty() { return serverAddress; } - public SimpleIntegerProperty getServerPortProperty() { - return serverPort; - } + public SimpleIntegerProperty getServerPortProperty() { return serverPort; } public SimpleObjectProperty getStateProperty() { return this.state; } - public SimpleStringProperty getUserNameProperty() { - return userName; - } + public SimpleStringProperty getUserNameProperty() { return userName; } - public void setState(State newState) { + public void setState (State newState) { state.set(newState); } - public void run() { + public void run () { startReceiving(); } @@ -84,9 +77,9 @@ public class ClientConnectionHandler extends ConnectionHandler implements Runnab System.out.println("Connection terminated by remote"); this.setState(DISCONNECTED); System.err.println("Unregistered because connection terminated" + e.getMessage()); - } catch (IOException e) { + } catch(IOException e) { System.err.println("Communication error" + e); - } catch (ClassNotFoundException e) { + } catch(ClassNotFoundException e) { System.err.println("Received object of unknown type" + e.getMessage()); } System.out.println("Stopped Connection Handler"); @@ -106,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)); - System.out.println("MESSAGE: From " + sender + " to " + reciever + ": " + payload); - } else if (type.equals(getDataTypeError())) { - messages.addMessage(new Message(Message.MessageType.ERROR, 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.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); @@ -175,37 +151,16 @@ 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); - this.sendData(userName.get(), USER_NONE, getDataTypeConnect(), 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, getDataTypeDisconnect(), null); + if (state.get() != NEW && state.get() != CONNECTED) throw new ChatProtocolException("Illegal state for disconnect: " + state); + this.sendData(userName.get(), USER_NONE, getDataTypeDisconnect(),null); this.setState(CONFIRM_DISCONNECT); } @@ -216,15 +171,14 @@ public class ClientConnectionHandler extends ConnectionHandler implements Runnab if (matcher.find()) { String receiver = matcher.group(1); String message = matcher.group(2); - if (message.length() < 1) { + if(message.length() < 1){ return false; } if (receiver == null || receiver.isBlank()) receiver = ClientConnectionHandler.USER_ALL; - this.sendData(userName.get(), receiver, getDataTypeMessage(), 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 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 a94a4c81d833e1c89cf9841c54ebbd675b12e690 Mon Sep 17 00:00:00 2001 From: Andrin Fassbind Date: Fri, 15 Apr 2022 16:39:52 +0200 Subject: [PATCH 07/22] 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 5c0e55870d7041fc7383144e882f883e6fd95959 Mon Sep 17 00:00:00 2001 From: Andrin Fassbind Date: Fri, 15 Apr 2022 19:41:43 +0200 Subject: [PATCH 08/22] 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 01d9e32..891efe3 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 @@ -61,6 +69,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"); @@ -68,6 +81,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(); @@ -108,6 +125,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 add410250f776d3465f8861e9b1bc323dfaf5182 Mon Sep 17 00:00:00 2001 From: Andrin Fassbind Date: Fri, 15 Apr 2022 21:13:28 +0200 Subject: [PATCH 09/22] 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 891efe3..6b9667a 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 @@ -94,32 +94,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 1d9beb6b7d11bad00305dbfcee915d49caab50e4 Mon Sep 17 00:00:00 2001 From: schrom01 Date: Fri, 15 Apr 2022 21:20:03 +0200 Subject: [PATCH 10/22] 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 c9aed1affdab9ba3686490ca9b252b79a496754f Mon Sep 17 00:00:00 2001 From: Andrin Fassbind Date: Fri, 15 Apr 2022 22:06:22 +0200 Subject: [PATCH 11/22] 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) { From 6d6b9f156436ea0e5560fa368a7a66f177d88d39 Mon Sep 17 00:00:00 2001 From: Leonardo Brandenberger Date: Fri, 15 Apr 2022 17:09:15 +0200 Subject: [PATCH 12/22] Java Doc and CodeStyle improvements --- .../client/ChatWindowController.java | 48 +++++++++++++------ 1 file changed, 33 insertions(+), 15 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..b65daef 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 @@ -17,21 +17,31 @@ import java.io.IOException; import static ch.zhaw.pm2.multichat.protocol.ConnectionHandler.State.*; + public class ChatWindowController { private ClientConnectionHandler connectionHandler; private ClientMessageList messages; private final WindowCloseHandler windowCloseHandler = new WindowCloseHandler(); - @FXML private Pane rootPane; - @FXML private TextField serverAddressField; - @FXML private TextField serverPortField; - @FXML private TextField userNameField; - @FXML private TextField messageField; - @FXML private TextArea messageArea; - @FXML private Button connectButton; - @FXML private Button sendButton; - @FXML private TextField filterValue; + @FXML + private Pane rootPane; + @FXML + private TextField serverAddressField; + @FXML + private TextField serverPortField; + @FXML + private TextField userNameField; + @FXML + private TextField messageField; + @FXML + private TextArea messageArea; + @FXML + private Button connectButton; + @FXML + private Button sendButton; + @FXML + private TextField filterValue; @FXML @@ -44,7 +54,7 @@ public class ChatWindowController { messageListener(); } - public void setConnectionHandler(ClientConnectionHandler connectionHandler){ + public void setConnectionHandler(ClientConnectionHandler connectionHandler) { this.connectionHandler = connectionHandler; startConnectionHandlerListener(); serverAddressField.setText(connectionHandler.getServerAddressProperty().get()); @@ -56,7 +66,7 @@ public class ChatWindowController { } @FXML - private void toggleConnection () { + private void toggleConnection() { if (connectionHandler == null || connectionHandler.getStateProperty().get() != CONNECTED) { connect(); } else { @@ -64,16 +74,20 @@ public class ChatWindowController { } } + private void connect() { try { messages.clear(); // clear message list startConnectionHandler(); connectionHandler.connect(); - } catch(ChatProtocolException | IOException e) { + } catch (ChatProtocolException | IOException e) { addError(e.getMessage()); } } + /** + * Initiates disconnecting of the connectionHandler, also checks if connectionHandler is avaible. + */ private void disconnect() { if (connectionHandler == null) { addError("No connection handler"); @@ -86,6 +100,9 @@ public class ChatWindowController { } } + /** + * + */ @FXML private void message() { String messageString = messageField.getText().strip(); @@ -103,13 +120,13 @@ public class ChatWindowController { } @FXML - private void applyFilter( ) { + private void applyFilter() { Platform.runLater(() -> this.messageArea.setText(messages.getFilteredMessages(filterValue.getText().strip()))); } private void startConnectionHandler() throws IOException { String userName = userNameField.getText(); - if(!userName.contains(" ")) { + if (!userName.contains(" ")) { String serverAddress = serverAddressField.getText(); int serverPort = Integer.parseInt(serverPortField.getText()); connectionHandler.initialize(serverAddress, serverPort, userName); @@ -133,7 +150,7 @@ public class ChatWindowController { connectButton.setText((newState == CONNECTED || newState == CONFIRM_DISCONNECT) ? "Disconnect" : "Connect"); } }); - if(newState == DISCONNECTED){ + if (newState == DISCONNECTED) { connectionHandler.stopReceiving(); } } @@ -205,6 +222,7 @@ public class ChatWindowController { }); } + private void messageListener() { messages.getChangedProperty().addListener(new ChangeListener() { @Override From dcee52cc1b5cfdb1a041ccfc113377662adfba7e Mon Sep 17 00:00:00 2001 From: Leonardo Brandenberger Date: Sat, 16 Apr 2022 04:38:28 +0200 Subject: [PATCH 13/22] Started Java Doc in ChatWindowController --- .../client/ChatWindowController.java | 96 +++++++++++++++---- 1 file changed, 80 insertions(+), 16 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 b65daef..4aaf238 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 @@ -17,7 +17,11 @@ import java.io.IOException; import static ch.zhaw.pm2.multichat.protocol.ConnectionHandler.State.*; - +/** + * Class Representing the Controller Element of the Window, also Contains the Elements that contacts the View Elements via + * Listeners and Observable Objects. + * To Contact the Model Elements needed it also holds references to messages and the Connectionhandler. + */ public class ChatWindowController { private ClientConnectionHandler connectionHandler; private ClientMessageList messages; @@ -39,21 +43,24 @@ public class ChatWindowController { @FXML private Button connectButton; @FXML - private Button sendButton; - @FXML private TextField filterValue; - @FXML - public void initialize() { - - } - + /** + * Takes a message object and stores it used as Model and also starts message Listener via messageListener method. + * + * @param messages Object that will be set for use as Model + */ public void setMessages(ClientMessageList messages) { this.messages = messages; messageListener(); } + /** + * Takes a Connectionhandler object and stores it used as Model and also starts a Listener for it. + * + * @param connectionHandler that will be set and used as Model. + */ public void setConnectionHandler(ClientConnectionHandler connectionHandler) { this.connectionHandler = connectionHandler; startConnectionHandlerListener(); @@ -61,10 +68,17 @@ public class ChatWindowController { serverPortField.setText(String.valueOf(connectionHandler.getServerPortProperty().get())); } + /** + * Method which closes the Application via use of the disconnect Method. //TODO evtl Löschen? + */ private void applicationClose() { disconnect(); } + /** + * Method that handles the Connect Button and Initiates connect when connectionHandler is not Connected or + * a disconnect when it is connected. + */ @FXML private void toggleConnection() { if (connectionHandler == null || connectionHandler.getStateProperty().get() != CONNECTED) { @@ -74,19 +88,21 @@ public class ChatWindowController { } } - + /** + * Initiates a connection by starting Connection Handler and telling, the Handler to start a connection. + */ private void connect() { try { messages.clear(); // clear message list startConnectionHandler(); connectionHandler.connect(); } catch (ChatProtocolException | IOException e) { - addError(e.getMessage()); + addError("Error while starting Connection Handler and connect" + e); } } /** - * Initiates disconnecting of the connectionHandler, also checks if connectionHandler is avaible. + * Initiates disconnecting of the connectionHandler, also checks if connectionHandler is available. */ private void disconnect() { if (connectionHandler == null) { @@ -101,11 +117,11 @@ public class ChatWindowController { } /** - * + * Method which is used when the send button is pressed and handing over a message to the Connection Handler */ @FXML private void message() { - String messageString = messageField.getText().strip(); + String messageString = messageField.getText().strip(); //TODO MVC ok? try { if (connectionHandler == null) { addError("No connection handler"); @@ -119,11 +135,20 @@ public class ChatWindowController { } } + /** + * Method which is used when a Filter is applied + * Setting the Text in the message area after sending it through the filter. + */ @FXML private void applyFilter() { Platform.runLater(() -> this.messageArea.setText(messages.getFilteredMessages(filterValue.getText().strip()))); } + /** + * Starts the ConnectionHandler setting the username and Checking if the name follows the valid format of no spaces. + * + * @throws IOException for error that may occur during initialization of connectionHandler. + */ private void startConnectionHandler() throws IOException { String userName = userNameField.getText(); if (!userName.contains(" ")) { @@ -132,7 +157,7 @@ public class ChatWindowController { connectionHandler.initialize(serverAddress, serverPort, userName); new Thread(connectionHandler).start(); - //register Listener + //register Listener //TODO what todo with methods? //startConnectionHandlerListener(); // register window close handler @@ -142,6 +167,12 @@ public class ChatWindowController { } } + /** + * Sets the state shown according to the state the method receives, if state indicates disconnected it will also inform the + * Connection Handler and tell it to stop Receiving more messages. + * + * @param newState is the state that it should be set to. + */ public void stateChanged(State newState) { // update UI (need to be run in UI thread: see Platform.runLater()) Platform.runLater(new Runnable() { @@ -155,15 +186,25 @@ public class ChatWindowController { } } + /** + * Sets displayed username according to the String provided. + * + * @param userName provided String that is set as name. + */ public void setUserName(String userName) { Platform.runLater(new Runnable() { @Override - public void run() { + public void run() { //TODO MVC ok?? userNameField.setText(userName); } }); } + /** + * Sets displayed Server Address. + * + * @param serverAddress provided String that is set as server address. + */ public void setServerAddress(String serverAddress) { Platform.runLater(new Runnable() { @Override @@ -173,6 +214,11 @@ public class ChatWindowController { }); } + /** + * Sets displayed Server port. + * + * @param serverPort provided String that is set as server port. + */ public void setServerPort(int serverPort) { Platform.runLater(new Runnable() { @Override @@ -182,16 +228,32 @@ public class ChatWindowController { }); } + /** + * Method which adds an incoming String as an Error Message. + * + * @param message String to be added as Error + */ public void addError(String message) { messages.addMessage(new Message(Message.MessageType.ERROR, null, null, message)); } + /** + * Nested Class in charge of Closing the wind + */ class WindowCloseHandler implements EventHandler { + /** + * TODO + * @param event the event which occurred + */ public void handle(WindowEvent event) { applicationClose(); } } + /** + * TODO missing + */ + public void startConnectionHandlerListener() { connectionHandler.getStateProperty().addListener(new ChangeListener() { @Override @@ -222,7 +284,9 @@ public class ChatWindowController { }); } - + /** + * TODO + */ private void messageListener() { messages.getChangedProperty().addListener(new ChangeListener() { @Override From 4edcc9264991316935641e418c1b017cfc88e94b Mon Sep 17 00:00:00 2001 From: Leonardo Brandenberger Date: Sat, 16 Apr 2022 04:54:56 +0200 Subject: [PATCH 14/22] improved java doc in ChatWindowController and Client class finished --- .../client/ChatWindowController.java | 3 +- .../pm2/multichat/client/ChatWindowModel.java | 29 ------------------- .../ch/zhaw/pm2/multichat/client/Client.java | 8 +++++ 3 files changed, 10 insertions(+), 30 deletions(-) delete mode 100644 client/src/main/java/ch/zhaw/pm2/multichat/client/ChatWindowModel.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 4aaf238..8ee37fc 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 @@ -242,7 +242,8 @@ public class ChatWindowController { */ class WindowCloseHandler implements EventHandler { /** - * TODO + * TODO + * * @param event the event which occurred */ public void handle(WindowEvent event) { diff --git a/client/src/main/java/ch/zhaw/pm2/multichat/client/ChatWindowModel.java b/client/src/main/java/ch/zhaw/pm2/multichat/client/ChatWindowModel.java deleted file mode 100644 index 261283f..0000000 --- a/client/src/main/java/ch/zhaw/pm2/multichat/client/ChatWindowModel.java +++ /dev/null @@ -1,29 +0,0 @@ -package ch.zhaw.pm2.multichat.client; - -import javafx.beans.value.ObservableStringValue; - -public class ChatWindowModel { - private ClientConnectionHandler connectionHandler; - private ClientMessageList messages; - ObservableStringValue serverAddress; - ObservableStringValue serverPort; - - - public String getServerAddress() { - return serverAddress.get(); - } - - public void setServerAddress(String address) { - setServerAddress(address); - } - - public String getServerPort() { - return getServerPort(); - } - - public void setServerPort(String address) { - setServerPort(address); - } - - -} diff --git a/client/src/main/java/ch/zhaw/pm2/multichat/client/Client.java b/client/src/main/java/ch/zhaw/pm2/multichat/client/Client.java index 38caafc..fe2f9cc 100644 --- a/client/src/main/java/ch/zhaw/pm2/multichat/client/Client.java +++ b/client/src/main/java/ch/zhaw/pm2/multichat/client/Client.java @@ -2,8 +2,16 @@ package ch.zhaw.pm2.multichat.client; import javafx.application.Application; +/** + * Client Main Class in charge of starting the UI only contains main method. + */ public class Client { + /** + * Main method which launches the Client UI + * + * @param args no arguments needed + */ public static void main(String[] args) { // Start UI System.out.println("Starting Client Application"); From 7c8bdb3b33ea61fb6d4ee8da9de360fcb94f7556 Mon Sep 17 00:00:00 2001 From: Leonardo Brandenberger Date: Sat, 16 Apr 2022 06:35:43 +0200 Subject: [PATCH 15/22] improved java doc in ChatWindowController and Client class finished Client Connection Handler in Progress --- .../client/ChatWindowController.java | 1 - .../client/ClientConnectionHandler.java | 145 ++++++++++++++---- .../multichat/client/ClientMessageList.java | 3 + 3 files changed, 120 insertions(+), 29 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 8ee37fc..85e3916 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 @@ -25,7 +25,6 @@ import static ch.zhaw.pm2.multichat.protocol.ConnectionHandler.State.*; public class ChatWindowController { private ClientConnectionHandler connectionHandler; private ClientMessageList messages; - private final WindowCloseHandler windowCloseHandler = new WindowCloseHandler(); @FXML 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 b5b72f9..0e825fb 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 @@ -13,11 +13,17 @@ 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.*; +/** + * Client Connection Handler Class is used for the connection of the Client to a server + * it is used part as a Model storing properties like userName, state, serverAddress and Port. + * Also holds methods to + */ public class ClientConnectionHandler extends ConnectionHandler implements Runnable { - private final Pattern messagePattern = Pattern.compile( "^(?:@(\\S*))?\\s*(.*)$" ); + private final Pattern messagePattern = Pattern.compile("^(?:@(\\S*))?\\s*(.*)$"); private SimpleStringProperty userName; private SimpleObjectProperty state; @@ -25,7 +31,7 @@ public class ClientConnectionHandler extends ConnectionHandler implements Runnab private SimpleStringProperty serverAddress; private SimpleIntegerProperty serverPort; - public ClientConnectionHandler(ClientMessageList messages) { + public ClientConnectionHandler(ClientMessageList messages) { super(); this.messages = messages; state = new SimpleObjectProperty<>(State.NEW); @@ -39,27 +45,65 @@ public class ClientConnectionHandler extends ConnectionHandler implements Runnab this.serverAddress.set(serverAddress); this.serverPort.set(serverPort); setConnection(NetworkHandler.openConnection(serverAddress, serverPort)); - this.userName.set((userName == null || userName.isBlank())? USER_NONE : userName); + this.userName.set((userName == null || userName.isBlank()) ? USER_NONE : userName); } - public SimpleStringProperty getServerAddressProperty() { return serverAddress; } + /** + * Observable getter Method for the stored serverAddress + * + * @return the stored serverAddress + */ + public SimpleStringProperty getServerAddressProperty() { + return serverAddress; + } - public SimpleIntegerProperty getServerPortProperty() { return serverPort; } + /** + * Observable getter Method for the stored serverPort + * + * @return the stored serverPort + */ + public SimpleIntegerProperty getServerPortProperty() { + return serverPort; + } + /** + * Observable getter Method for the stored state + * + * @return the stored state + */ public SimpleObjectProperty getStateProperty() { return this.state; } - public SimpleStringProperty getUserNameProperty() { return userName; } + /** + * Observable getter Method for the stored userName + * + * @return the stored userName + */ + public SimpleStringProperty getUserNameProperty() { + return userName; + } - public void setState (State newState) { + /** + * Method which sets a new State. + * + * @param newState the state that will be set + */ + public void setState(State newState) { state.set(newState); } - public void run () { + /** + * Standard run method which will directly start the startReceiving method. + */ + public void run() { startReceiving(); } + /** + * Method that is started by the run method, starts a connection handler. + * Figures out if connection is avaible if not determines the error cause and gives an error acordingly. + */ private void startReceiving() { System.out.println("Starting Connection Handler"); try { @@ -77,14 +121,17 @@ public class ClientConnectionHandler extends ConnectionHandler implements Runnab System.out.println("Connection terminated by remote"); this.setState(DISCONNECTED); System.err.println("Unregistered because connection terminated" + e.getMessage()); - } catch(IOException e) { + } catch (IOException e) { System.err.println("Communication error" + e); - } catch(ClassNotFoundException e) { + } catch (ClassNotFoundException e) { System.err.println("Received object of unknown type" + e.getMessage()); } System.out.println("Stopped Connection Handler"); } + /** + * Method which is used to stop receiving data, gets the current connection and closes it. + */ public void stopReceiving() { System.out.println("Closing Connection Handler to Server"); try { @@ -94,9 +141,14 @@ public class ClientConnectionHandler extends ConnectionHandler implements Runnab } catch (IOException e) { System.err.println("Failed to close connection." + e.getMessage()); } - System.out.println("Closed Connection Handler to Server"); + System.out.println("Closed Connection Handler to Server"); //TODO should be shown also when failed to close ? } + /** + * Method which processes data and determines its type then uses the corresponding method to proccess it. + * + * @param data that is received in a form of a String and then used depending on its determined cause. + */ private void processData(String data) { try { Scanner scanner = new Scanner(data); @@ -104,7 +156,7 @@ public class ClientConnectionHandler extends ConnectionHandler implements Runnab StringBuilder reciever = new StringBuilder(); StringBuilder type = new StringBuilder(); StringBuilder payload = new StringBuilder(); - super.processData(scanner,sender,reciever,type,payload); + super.processData(scanner, sender, reciever, type, payload); // dispatch operation based on type parameter if (type.toString().equals(getDataTypeConnect())) { @@ -112,9 +164,9 @@ public class ClientConnectionHandler extends ConnectionHandler implements Runnab } else if (type.toString().equals(getDataTypeConfirm())) { caseConfirm(sender.toString(), reciever.toString(), payload.toString()); } else if (type.toString().equals(getDataTypeDisconnect())) { - caseDisconnect(sender.toString(),reciever.toString(),payload.toString()); + caseDisconnect(sender.toString(), reciever.toString(), payload.toString()); } else if (type.toString().equals(getDataTypeMessage())) { - caseMessage(sender.toString(),reciever.toString(),payload.toString()); + caseMessage(sender.toString(), reciever.toString(), payload.toString()); } else if (type.toString().equals(getDataTypeError())) { caseError(sender.toString(), reciever.toString(), payload.toString()); } else { @@ -131,11 +183,11 @@ public class ClientConnectionHandler extends ConnectionHandler implements Runnab this.userName.set(reciever); 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, 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)); + messages.addMessage(new Message(Message.MessageType.INFO, sender, reciever, payload)); System.out.println("CONFIRM: " + payload); this.setState(DISCONNECTED); } else { @@ -143,42 +195,79 @@ public class ClientConnectionHandler extends ConnectionHandler implements Runnab } } - private void caseDisconnect(String sender, String reciever, String payload) { + /** + * Initiates the disconnect sequence and sends the message with all its info. + * + * @param sender the sender name of the message + * @param receiver the receiver name of the message + * @param payload the added payload that corresponds to the message + */ + private void caseDisconnect(String sender, String receiver, 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)); + messages.addMessage(new Message(Message.MessageType.INFO, sender, receiver, payload)); System.out.println("DISCONNECT: " + payload); this.setState(DISCONNECTED); } - private void caseMessage(String sender, String reciever, String payload) { + /** + * Initiates the procedure to send a new message and sends one as such. + * + * @param sender the sender name of the message + * @param receiver the receiver name of the message + * @param payload the added payload that corresponds to the message + */ + private void caseMessage(String sender, String receiver, 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); + messages.addMessage(new Message(Message.MessageType.MESSAGE, sender, receiver, payload)); + System.out.println("MESSAGE: From " + sender + " to " + receiver + ": " + payload); } - private void caseError(String sender, String reciever, String payload) { - messages.addMessage(new Message(Message.MessageType.ERROR,sender,reciever,payload)); + /** + * Stores the message as an error message and displays it as such aswell. + * + * @param sender the sender name of the message + * @param receiver the receiver name of the message + * @param payload the added payload that corresponds to the message + */ + private void caseError(String sender, String receiver, String payload) { + messages.addMessage(new Message(Message.MessageType.ERROR, sender, receiver, payload)); System.out.println("ERROR: " + payload); } + /** + * Connects TODO + * + * @throws ChatProtocolException Error that is thrown if the state is not set to NEW + */ public void connect() throws ChatProtocolException { if (state.get() != NEW) throw new ChatProtocolException("Illegal state for connect: " + state); - this.sendData(userName.get(), USER_NONE, getDataTypeConnect(),null); + this.sendData(userName.get(), USER_NONE, getDataTypeConnect(), null); this.setState(CONFIRM_CONNECT); } + /** + * TODO + * @throws ChatProtocolException + */ 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, getDataTypeDisconnect(),null); + if (state.get() != NEW && state.get() != CONNECTED) + throw new ChatProtocolException("Illegal state for disconnect: " + state); + this.sendData(userName.get(), USER_NONE, getDataTypeDisconnect(), null); this.setState(CONFIRM_DISCONNECT); } + /** + * TODO + * @param messageString + * @return + * @throws ChatProtocolException + */ public boolean message(String messageString) throws ChatProtocolException { if (state.get() != CONNECTED) throw new ChatProtocolException("Illegal state for message: " + state); @@ -186,11 +275,11 @@ public class ClientConnectionHandler extends ConnectionHandler implements Runnab if (matcher.find()) { String receiver = matcher.group(1); String message = matcher.group(2); - if(message.length() < 1){ + if (message.length() < 1) { return false; } if (receiver == null || receiver.isBlank()) receiver = ClientConnectionHandler.USER_ALL; - this.sendData(userName.get(), receiver, getDataTypeMessage(),message); + this.sendData(userName.get(), receiver, getDataTypeMessage(), message); return true; } else { return false; diff --git a/client/src/main/java/ch/zhaw/pm2/multichat/client/ClientMessageList.java b/client/src/main/java/ch/zhaw/pm2/multichat/client/ClientMessageList.java index 5f3f48f..156d570 100644 --- a/client/src/main/java/ch/zhaw/pm2/multichat/client/ClientMessageList.java +++ b/client/src/main/java/ch/zhaw/pm2/multichat/client/ClientMessageList.java @@ -5,6 +5,9 @@ import javafx.beans.property.SimpleBooleanProperty; import java.util.ArrayList; import java.util.List; +/** + * Class that is use + */ public class ClientMessageList { private List messages = new ArrayList<>(); private final SimpleBooleanProperty changed = new SimpleBooleanProperty(false); From 2acfa708e9074afb970a941d988b4bec7c73df31 Mon Sep 17 00:00:00 2001 From: Leonardo Brandenberger Date: Sat, 16 Apr 2022 07:10:03 +0200 Subject: [PATCH 16/22] ClientUI JavaDocs finished --- .../ch/zhaw/pm2/multichat/client/ClientUI.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/client/src/main/java/ch/zhaw/pm2/multichat/client/ClientUI.java b/client/src/main/java/ch/zhaw/pm2/multichat/client/ClientUI.java index 5fbdbf5..e252877 100644 --- a/client/src/main/java/ch/zhaw/pm2/multichat/client/ClientUI.java +++ b/client/src/main/java/ch/zhaw/pm2/multichat/client/ClientUI.java @@ -6,15 +6,32 @@ import javafx.scene.Scene; import javafx.scene.layout.Pane; import javafx.stage.Stage; +/** + * Class in charge of initializing and loading elements needed for the GUI. + * Also Launching the chat window with pre-given parameters + */ public class ClientUI extends Application { private final ClientMessageList clientMessageList = new ClientMessageList(); private final ClientConnectionHandler connectionHandler = new ClientConnectionHandler(clientMessageList); + /** + * Start method will only run the chatWindow method. + * + * @param primaryStage the primary stage for this application, onto which + * the application scene can be set. + * Applications may create other stages, if needed, but they will not be + * primary stages. + */ @Override public void start(Stage primaryStage) { chatWindow(primaryStage); } + /** + * Method is in charge of loading the FXML file and then initializing the controllers and setting some parameters for the stage. + * + * @param primaryStage of the Application onto which the Scene is set. + */ private void chatWindow(Stage primaryStage) { try { FXMLLoader loader = new FXMLLoader(getClass().getResource("ChatWindow.fxml")); From c732e822601ee65004370e1d8945556a5a8cb955 Mon Sep 17 00:00:00 2001 From: Leonardo Brandenberger Date: Sat, 16 Apr 2022 08:19:44 +0200 Subject: [PATCH 17/22] General Codestyle and higlighted todos --- .../client/ChatWindowController.java | 10 +- .../client/ClientConnectionHandler.java | 60 +++++--- .../multichat/client/ClientMessageList.java | 22 ++- .../ch/zhaw/pm2/multichat/client/Message.java | 8 +- .../multichat/protocol/ConnectionHandler.java | 2 +- .../ch/zhaw/pm2/multichat/server/Server.java | 18 ++- .../server/ServerConnectionHandler.java | 131 +++++++++--------- 7 files changed, 145 insertions(+), 106 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 85e3916..06e6189 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 @@ -20,7 +20,7 @@ import static ch.zhaw.pm2.multichat.protocol.ConnectionHandler.State.*; /** * Class Representing the Controller Element of the Window, also Contains the Elements that contacts the View Elements via * Listeners and Observable Objects. - * To Contact the Model Elements needed it also holds references to messages and the Connectionhandler. + * To Contact the Model Elements needed it also holds references to message and the Connectionhandler. */ public class ChatWindowController { private ClientConnectionHandler connectionHandler; @@ -56,7 +56,7 @@ public class ChatWindowController { } /** - * Takes a Connectionhandler object and stores it used as Model and also starts a Listener for it. + * Takes a Connection handler object and stores it used as Model and also starts a Listener for it. * * @param connectionHandler that will be set and used as Model. */ @@ -241,7 +241,7 @@ public class ChatWindowController { */ class WindowCloseHandler implements EventHandler { /** - * TODO + * //TODO * * @param event the event which occurred */ @@ -251,7 +251,7 @@ public class ChatWindowController { } /** - * TODO missing + * //TODO missing */ public void startConnectionHandlerListener() { @@ -285,7 +285,7 @@ public class ChatWindowController { } /** - * TODO + * //TODO */ private void messageListener() { messages.getChangedProperty().addListener(new ChangeListener() { 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 0e825fb..0bae72f 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 @@ -25,12 +25,18 @@ public class ClientConnectionHandler extends ConnectionHandler implements Runnab private final Pattern messagePattern = Pattern.compile("^(?:@(\\S*))?\\s*(.*)$"); - private SimpleStringProperty userName; - private SimpleObjectProperty state; - private ClientMessageList messages; - private SimpleStringProperty serverAddress; - private SimpleIntegerProperty serverPort; + private final SimpleStringProperty userName; + private final SimpleObjectProperty state; + private final ClientMessageList + messages; + private final SimpleStringProperty serverAddress; + private final SimpleIntegerProperty serverPort; + /** + * Constructor initializes ConnectionHandler by Setting default values for the fields and stores the given messages itself. + * + * @param messages + */ public ClientConnectionHandler(ClientMessageList messages) { super(); this.messages = messages; @@ -40,6 +46,14 @@ public class ClientConnectionHandler extends ConnectionHandler implements Runnab this.userName = new SimpleStringProperty(null); } + /** + * //TODO complete javadoc + * + * @param serverAddress + * @param serverPort + * @param userName + * @throws IOException + */ public void initialize(String serverAddress, int serverPort, String userName) throws IOException { state.set(NEW); this.serverAddress.set(serverAddress); @@ -102,7 +116,7 @@ public class ClientConnectionHandler extends ConnectionHandler implements Runnab /** * Method that is started by the run method, starts a connection handler. - * Figures out if connection is avaible if not determines the error cause and gives an error acordingly. + * Figures out if connection is available if not determines the error cause and gives an error accordingly. */ private void startReceiving() { System.out.println("Starting Connection Handler"); @@ -112,7 +126,7 @@ public class ClientConnectionHandler extends ConnectionHandler implements Runnab String data = getConnection().receive(); processData(data); } - System.out.println("Stopped recieving data"); + System.out.println("Stopped receiving data"); } catch (SocketException e) { System.out.println("Connection terminated locally"); this.setState(DISCONNECTED); @@ -145,7 +159,7 @@ public class ClientConnectionHandler extends ConnectionHandler implements Runnab } /** - * Method which processes data and determines its type then uses the corresponding method to proccess it. + * Method which processes data and determines its type then uses the corresponding method to process it. * * @param data that is received in a form of a String and then used depending on its determined cause. */ @@ -153,22 +167,22 @@ public class ClientConnectionHandler extends ConnectionHandler implements Runnab try { Scanner scanner = new Scanner(data); StringBuilder sender = new StringBuilder(); - StringBuilder reciever = new StringBuilder(); + StringBuilder receiver = new StringBuilder(); StringBuilder type = new StringBuilder(); StringBuilder payload = new StringBuilder(); - super.processData(scanner, sender, reciever, type, payload); + super.processData(scanner, sender, receiver, type, payload); // dispatch operation based on type parameter if (type.toString().equals(getDataTypeConnect())) { System.err.println("Illegal connect request from server"); } else if (type.toString().equals(getDataTypeConfirm())) { - caseConfirm(sender.toString(), reciever.toString(), payload.toString()); + caseConfirm(sender.toString(), receiver.toString(), payload.toString()); } else if (type.toString().equals(getDataTypeDisconnect())) { - caseDisconnect(sender.toString(), reciever.toString(), payload.toString()); + caseDisconnect(sender.toString(), receiver.toString(), payload.toString()); } else if (type.toString().equals(getDataTypeMessage())) { - caseMessage(sender.toString(), reciever.toString(), payload.toString()); + caseMessage(sender.toString(), receiver.toString(), payload.toString()); } else if (type.toString().equals(getDataTypeError())) { - caseError(sender.toString(), reciever.toString(), payload.toString()); + caseError(sender.toString(), receiver.toString(), payload.toString()); } else { System.out.println("Unknown data type received: " + type); } @@ -178,16 +192,16 @@ public class ClientConnectionHandler extends ConnectionHandler implements Runnab } } - private void caseConfirm(String sender, String reciever, String payload) { + private void caseConfirm(String sender, String receiver, String payload) { if (state.get() == CONFIRM_CONNECT) { - this.userName.set(reciever); + this.userName.set(receiver); 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, receiver, 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)); + messages.addMessage(new Message(Message.MessageType.INFO, sender, receiver, payload)); System.out.println("CONFIRM: " + payload); this.setState(DISCONNECTED); } else { @@ -229,7 +243,7 @@ public class ClientConnectionHandler extends ConnectionHandler implements Runnab } /** - * Stores the message as an error message and displays it as such aswell. + * Stores the message as an error message and displays it as such as well. * * @param sender the sender name of the message * @param receiver the receiver name of the message @@ -241,7 +255,7 @@ public class ClientConnectionHandler extends ConnectionHandler implements Runnab } /** - * Connects TODO + * //Connects TODO * * @throws ChatProtocolException Error that is thrown if the state is not set to NEW */ @@ -252,7 +266,8 @@ public class ClientConnectionHandler extends ConnectionHandler implements Runnab } /** - * TODO + * //TODO + * * @throws ChatProtocolException */ public void disconnect() throws ChatProtocolException { @@ -263,7 +278,8 @@ public class ClientConnectionHandler extends ConnectionHandler implements Runnab } /** - * TODO + * //TODO + * * @param messageString * @return * @throws ChatProtocolException diff --git a/client/src/main/java/ch/zhaw/pm2/multichat/client/ClientMessageList.java b/client/src/main/java/ch/zhaw/pm2/multichat/client/ClientMessageList.java index 156d570..276f39e 100644 --- a/client/src/main/java/ch/zhaw/pm2/multichat/client/ClientMessageList.java +++ b/client/src/main/java/ch/zhaw/pm2/multichat/client/ClientMessageList.java @@ -6,17 +6,29 @@ import java.util.ArrayList; import java.util.List; /** - * Class that is use + * Class that is used to store the messages in an ArrayList + * And also as a Model that informs the controller via Listeners that can be initialized */ public class ClientMessageList { private List messages = new ArrayList<>(); private final SimpleBooleanProperty changed = new SimpleBooleanProperty(false); + /** + * Adds a new message to ArrayList and also informs Listener. + * @param message that should be added + */ public void addMessage(Message message) { messages.add(message); changed.set(!changed.get()); } + /** + * Applies a given filter over all messages and returns the result as a new String. + * Method is also in charge of applying the correct prefix to the message according to its type. + * + * @param filter is the applied filter on all the messages that are stored. + * @return String that matches the given filter. + */ public String getFilteredMessages(String filter) { StringBuilder result = new StringBuilder(); boolean showAll = filter == null || filter.isBlank(); @@ -35,11 +47,19 @@ public class ClientMessageList { return result.toString(); } + /** + * Overwrites the Arraylist of messages, clearing it, also informs all Listeners. + */ public void clear() { messages = new ArrayList<>(); changed.set(!changed.get()); } + /** + * Getter Method to check the current value of SimpleBooleanProperty changed. + * + * @return the current value of changed + */ public SimpleBooleanProperty getChangedProperty() { return changed; } diff --git a/client/src/main/java/ch/zhaw/pm2/multichat/client/Message.java b/client/src/main/java/ch/zhaw/pm2/multichat/client/Message.java index f30a445..036e486 100644 --- a/client/src/main/java/ch/zhaw/pm2/multichat/client/Message.java +++ b/client/src/main/java/ch/zhaw/pm2/multichat/client/Message.java @@ -14,7 +14,7 @@ public class Message { * * @param type Message (if it's a message typed by a user), Error or Information (if it is generated automatically, in this case sender and reciever will be null) * @param sender The User who has sent the message. - * @param receiver The User who should recieve the message. + * @param receiver The User who should receive the message. * @param text The Text of the message. */ public Message(MessageType type, String sender, String receiver, String text) { @@ -25,10 +25,10 @@ public class Message { } /** - * Checks if the Filter String is contained in one of the datafields sender, receiver or text + * Checks if the Filter String is contained in one of the data fields sender, receiver or text * * @param filter The Filter String - * @return true if it the Filter String is contained in a datafield. + * @return true if it is the Filter String is contained in a data field. */ public boolean matchesFilter(String filter) { return (sender != null && sender.contains(filter)) || @@ -51,7 +51,7 @@ public class Message { } /** - * @return The Reciever who recieves the Message. + * @return The Receiver who receives the Message. */ public String getReceiver() { return receiver; 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 21eacc8..a51fadc 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 @@ -8,7 +8,7 @@ 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)} + * Shared methods are implemented in this class as well {@link ConnectionHandler#sendData(String, String, String, String)} {@link ConnectionHandler#processData(Scanner, StringBuilder, StringBuilder, StringBuilder, StringBuilder)} */ public abstract class ConnectionHandler { private NetworkHandler.NetworkConnection connection; 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 6b9667a..f712540 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 @@ -1,6 +1,5 @@ package ch.zhaw.pm2.multichat.server; -import ch.zhaw.pm2.multichat.protocol.ConnectionHandler; import ch.zhaw.pm2.multichat.protocol.NetworkHandler; import java.io.IOException; @@ -16,19 +15,17 @@ import java.util.concurrent.locks.ReentrantLock; * 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} - * + * 3. New connections will be attached to a Connection handler: {@link ServerConnectionHandler} and placed in a Map containing all active connections {@link Server#connections} */ public class Server { // Server connection - private NetworkHandler.NetworkServer networkServer; + private final NetworkHandler.NetworkServer networkServer; // Connection registry - private Map connections = new HashMap<>(); + private final Map connections = new HashMap<>(); /** - * * @param args */ public static void main(String[] args) { @@ -71,6 +68,7 @@ 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. */ @@ -91,11 +89,11 @@ public class Server { System.out.println("Server started."); try { while (true) { - NetworkHandler.NetworkConnection connection = networkServer.waitForConnection(); - ServerConnectionHandler connectionHandler = new ServerConnectionHandler(connection, connections, mutex, nameComplete); - new Thread(connectionHandler).start(); + NetworkHandler.NetworkConnection connection = networkServer.waitForConnection(); + ServerConnectionHandler connectionHandler = new ServerConnectionHandler(connection, connections, mutex, nameComplete); + new Thread(connectionHandler).start(); } - } catch(SocketException e) { + } catch (SocketException e) { System.out.println("Server connection terminated"); } catch (IOException e) { System.err.println("Communication error " + e); 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 9a7cb91..13c460d 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 @@ -2,7 +2,9 @@ package ch.zhaw.pm2.multichat.server; import ch.zhaw.pm2.multichat.protocol.ChatProtocolException; import ch.zhaw.pm2.multichat.protocol.ConnectionHandler; + import static ch.zhaw.pm2.multichat.protocol.ConnectionHandler.State.*; + import ch.zhaw.pm2.multichat.protocol.NetworkHandler; import java.io.EOFException; @@ -19,38 +21,38 @@ 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{ + */ +public class ServerConnectionHandler extends ConnectionHandler implements Runnable { private static final AtomicInteger connectionCounter = new AtomicInteger(0); private final int connectionId = connectionCounter.incrementAndGet(); - private final Map connectionRegistry; + private final Map connectionRegistry; - private ReentrantLock mutex; + private final ReentrantLock mutex; - private Condition nameComplete; + private final Condition nameComplete; - private String userName = "Anonymous-"+connectionId; + private String userName = "Anonymous-" + connectionId; private State state = NEW; /** @@ -62,14 +64,15 @@ public class ServerConnectionHandler extends ConnectionHandler implements Runnab } /** - * 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 + * Constructor to initialize the connection + * + * @param connection representing the socket connection between server and client + * @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) { + Map registry, ReentrantLock mutex, Condition nameComplete) { super(); setConnection(connection); Objects.requireNonNull(connection, "Connection must not be null"); @@ -80,7 +83,6 @@ public class ServerConnectionHandler extends ConnectionHandler implements Runnab } /** - * * @return the username of the connected client */ public String getUserName() { @@ -88,39 +90,38 @@ public class ServerConnectionHandler extends ConnectionHandler implements Runnab } /** - * - * @return state of the connection. Poosible states are see {@link ch.zhaw.pm2.multichat.protocol.ConnectionHandler.State} + * @return state of the connection. Possible 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 + * This methods runs in a while-loop 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 { - System.out.println("Start receiving data..."); - while (getConnection().isAvailable() && !(state == ERROR)) { - String data = getConnection().receive(); - processData(data); - } - System.out.println("Stopped recieving data"); - } catch (SocketException e) { - System.out.println("Connection terminated locally"); - connectionRegistry.remove(userName); - System.out.println("Unregistered because client connection terminated: " + userName + " " + e.getMessage()); - } catch (EOFException e) { - System.out.println("Connection terminated by remote"); - connectionRegistry.remove(userName); - System.out.println("Unregistered because client connection terminated: " + userName + " " + e.getMessage()); - } catch (IOException e) { - System.err.println("Communication error: " + e); - } catch (ClassNotFoundException e) { - System.err.println("Received object of unknown type: " + e.getMessage()); + System.out.println("Starting Connection Handler for new User"); + try { + System.out.println("Start receiving data..."); + while (getConnection().isAvailable() && !(state == ERROR)) { + String data = getConnection().receive(); + processData(data); } + System.out.println("Stopped receiving data"); + } catch (SocketException e) { + System.out.println("Connection terminated locally"); + connectionRegistry.remove(userName); + System.out.println("Unregistered because client connection terminated: " + userName + " " + e.getMessage()); + } catch (EOFException e) { + System.out.println("Connection terminated by remote"); + connectionRegistry.remove(userName); + System.out.println("Unregistered because client connection terminated: " + userName + " " + e.getMessage()); + } catch (IOException e) { + System.err.println("Communication error: " + e); + } catch (ClassNotFoundException e) { + System.err.println("Received object of unknown type: " + e.getMessage()); + } if (state == ERROR) { System.out.println("Stopping Connection Handler for Rejected Client"); } else { @@ -145,24 +146,25 @@ public class ServerConnectionHandler extends ConnectionHandler implements Runnab } /** - * This method gets called when socket recieves data. The method checks for the data type and reacts accordingly + * This method gets called when socket receives 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. + * If username not valid quits connection by changing status to ERROR. + * 2. Confirm => Server should not receive 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 + * + * @param data received by the server */ - private void processData(String data) { + private void processData(String data) { try { Scanner scanner = new Scanner(data); StringBuilder sender = new StringBuilder(); - StringBuilder reciever = new StringBuilder(); + StringBuilder receiver = new StringBuilder(); StringBuilder type = new StringBuilder(); StringBuilder payload = new StringBuilder(); - super.processData(scanner,sender,reciever,type,payload); + super.processData(scanner, sender, receiver, type, payload); // dispatch operation based on type parameter if (type.toString().equals(getDataTypeConnect())) { @@ -172,14 +174,14 @@ public class ServerConnectionHandler extends ConnectionHandler implements Runnab } else if (type.toString().equals(getDataTypeDisconnect())) { caseDisconnect(); } else if (type.toString().equals(getDataTypeMessage())) { - caseMessage(sender.toString(), reciever.toString(), type.toString(), payload.toString()); + caseMessage(sender.toString(), receiver.toString(), type.toString(), payload.toString()); } else if (type.toString().equals(getDataTypeError())) { System.err.println("Received error from client (" + sender + "): " + payload); } else { System.err.println("Unknown data type received: " + type); } - } catch(ChatProtocolException e) { + } catch (ChatProtocolException e) { System.out.println("Error while processing data " + e.getMessage()); sendData(USER_NONE, userName, getDataTypeError(), e.getMessage()); } @@ -188,6 +190,7 @@ 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 */ @@ -205,7 +208,7 @@ public class ServerConnectionHandler extends ConnectionHandler implements Runnab //if username valid this.userName = sender; connectionRegistry.put(userName, this); - sendData(USER_NONE, userName, getDataTypeConfirm(), "Registration successfull for " + userName); + sendData(USER_NONE, userName, getDataTypeConfirm(), "Registration successful for " + userName); this.state = CONNECTED; System.out.println(String.format("Connected new Client %s with IP:Port <%s:%d>", userName, @@ -216,7 +219,8 @@ public class ServerConnectionHandler extends ConnectionHandler implements Runnab /** * 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. + * + * @throws ChatProtocolException if state already DISCONNECTED. */ private void caseDisconnect() throws ChatProtocolException { if (state == DISCONNECTED) @@ -232,27 +236,28 @@ public class ServerConnectionHandler extends ConnectionHandler implements Runnab /** * 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 + * + * @param sender who sent data + * @param receiver to receive 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{ + private void caseMessage(String sender, String receiver, String type, String payload) throws ChatProtocolException { if (state != CONNECTED) throw new ChatProtocolException("Illegal state for message request: " + state); - if (USER_ALL.equals(reciever)) { + if (USER_ALL.equals(receiver)) { for (ServerConnectionHandler handler : connectionRegistry.values()) { - handler.sendData(sender, reciever, type, payload); + handler.sendData(sender, receiver, type, payload); } } else { - ServerConnectionHandler handler = connectionRegistry.get(reciever); + ServerConnectionHandler handler = connectionRegistry.get(receiver); 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, receiver, type, payload); + if (!receiver.equals(sender)) { + sendData(sender, receiver, 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); + this.sendData(USER_NONE, userName, getDataTypeError(), "Unknown User: " + receiver); } } } From 154b9d435d91bae61c3ca5e80a3bc7f25b5668e9 Mon Sep 17 00:00:00 2001 From: Leonardo Brandenberger Date: Sat, 16 Apr 2022 16:23:25 +0200 Subject: [PATCH 18/22] Further improved Java Docs 10 missing todos. --- .../client/ChatWindowController.java | 4 +- .../ch/zhaw/pm2/multichat/client/Message.java | 2 +- .../multichat/protocol/ConnectionHandler.java | 48 ++++++++----------- .../ch/zhaw/pm2/multichat/server/Server.java | 26 +++++----- .../server/ServerConnectionHandler.java | 8 ++-- 5 files changed, 41 insertions(+), 47 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 06e6189..f46425d 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 @@ -251,7 +251,7 @@ public class ChatWindowController { } /** - * //TODO missing + * Starts several new Listener for Connection Handler changes by using several observable properties. */ public void startConnectionHandlerListener() { @@ -285,7 +285,7 @@ public class ChatWindowController { } /** - * //TODO + * Starts a new Listener for messages by using the observable Boolean. */ private void messageListener() { messages.getChangedProperty().addListener(new ChangeListener() { diff --git a/client/src/main/java/ch/zhaw/pm2/multichat/client/Message.java b/client/src/main/java/ch/zhaw/pm2/multichat/client/Message.java index 036e486..1745623 100644 --- a/client/src/main/java/ch/zhaw/pm2/multichat/client/Message.java +++ b/client/src/main/java/ch/zhaw/pm2/multichat/client/Message.java @@ -12,7 +12,7 @@ public class Message { /** * Constructor of Message. Needs all Information about a Message to save them. * - * @param type Message (if it's a message typed by a user), Error or Information (if it is generated automatically, in this case sender and reciever will be null) + * @param type Message (if it's a message typed by a user), Error or Information (if it is generated automatically, in this case sender and receiver will be null) * @param sender The User who has sent the message. * @param receiver The User who should receive the message. * @param text The Text of the message. 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 a51fadc..6762b0c 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 @@ -29,7 +29,6 @@ public abstract class ConnectionHandler { } /** - * * @return {@link ConnectionHandler#DATA_TYPE_CONNECT} */ public static String getDataTypeConnect() { @@ -37,7 +36,6 @@ public abstract class ConnectionHandler { } /** - * * @return {@link ConnectionHandler#DATA_TYPE_CONFIRM} */ public static String getDataTypeConfirm() { @@ -45,7 +43,6 @@ public abstract class ConnectionHandler { } /** - * * @return {@link ConnectionHandler#DATA_TYPE_DISCONNECT} */ public static String getDataTypeDisconnect() { @@ -53,7 +50,6 @@ public abstract class ConnectionHandler { } /** - * * @return {@link ConnectionHandler#DATA_TYPE_MESSAGE */ public static String getDataTypeMessage() { @@ -61,7 +57,6 @@ public abstract class ConnectionHandler { } /** - * * @return {@link ConnectionHandler#DATA_TYPE_ERROR} */ public static String getDataTypeError() { @@ -69,7 +64,6 @@ public abstract class ConnectionHandler { } /** - * * @return {@link NetworkHandler.NetworkConnection} */ public NetworkHandler.NetworkConnection getConnection() { @@ -78,6 +72,7 @@ public abstract class ConnectionHandler { /** * 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) { @@ -85,26 +80,24 @@ public abstract class ConnectionHandler { } /** - * This method reads the data when a ConnectionHandler recieves it. It tries to read out the sender, reciever, type and payload. + * This method reads the data when a ConnectionHandler receives it. It tries to read out the sender, receiver, 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 + * + * @param scanner to read data + * @param sender of the data + * @param receiver 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 { + protected void processData(Scanner scanner, StringBuilder sender, StringBuilder receiver, StringBuilder type, StringBuilder payload) throws ChatProtocolException { // parse data content if (scanner.hasNextLine()) { sender.append(scanner.nextLine()); + } else if (scanner.hasNextLine()) { + receiver.append(scanner.nextLine()); } else { - throw new ChatProtocolException("No Sender found"); - } - if (scanner.hasNextLine()) { - reciever.append(scanner.nextLine()); - } else { - throw new ChatProtocolException("No Reciever found"); + throw new ChatProtocolException("No Receiver found"); } if (scanner.hasNextLine()) { type.append(scanner.nextLine()); @@ -118,19 +111,20 @@ 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 sender of the data * @param receiver of the data - * @param type of the data - * @param payload 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(); String data = new StringBuilder() - .append(sender+"\n") - .append(receiver+"\n") - .append(type+"\n") - .append(payload+"\n") + .append(sender + "\n") + .append(receiver + "\n") + .append(type + "\n") + .append(payload + "\n") .toString(); try { connection.send(data); @@ -138,7 +132,7 @@ public abstract class ConnectionHandler { System.err.println("Connection closed: " + e.getMessage()); } catch (EOFException e) { System.out.println("Connection terminated by remote"); - } catch(IOException e) { + } catch (IOException e) { System.err.println("Communication error: " + e.getMessage()); } } 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 f712540..982c6bf 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 @@ -25,6 +25,19 @@ public class Server { // Connection registry private final Map connections = new HashMap<>(); + /** + * 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"); + networkServer = NetworkHandler.createServer(serverPort); + System.out.println("Listening on " + networkServer.getHostAddress() + ":" + networkServer.getHostPort()); + } + /** * @param args */ @@ -66,19 +79,6 @@ 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"); - networkServer = NetworkHandler.createServer(serverPort); - 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. 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 13c460d..45820bb 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 @@ -33,8 +33,8 @@ import java.util.concurrent.locks.ReentrantLock; * 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. + * Process Messages send from a client by: + * 1. Evaluating the receiver 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. @@ -89,7 +89,7 @@ public class ServerConnectionHandler extends ConnectionHandler implements Runnab return this.userName; } - /** + /** //TODO needed method? * @return state of the connection. Possible states are see {@link ch.zhaw.pm2.multichat.protocol.ConnectionHandler.State} */ public State getState() { @@ -97,7 +97,7 @@ public class ServerConnectionHandler extends ConnectionHandler implements Runnab } /** - * This methods runs in a while-loop as long as the socket between server and client is available + * These methods runs in a while-loop as long as the socket between server and client is available * and the connection State is not ERROR. */ private void startReceiving() { From 8cb83ef7efa141d987fef60d531bcfff0c66ab17 Mon Sep 17 00:00:00 2001 From: Leonardo Brandenberger Date: Sat, 16 Apr 2022 16:58:37 +0200 Subject: [PATCH 19/22] Fixed whitespaces ChatWindow.fxml other javadocs in other classes aswell deleted unneccessary declarations --- .../client/ChatWindowController.java | 8 +- .../zhaw/pm2/multichat/client/ChatWindow.fxml | 148 +++++++++--------- .../multichat/protocol/ConnectionHandler.java | 10 +- .../ch/zhaw/pm2/multichat/server/Server.java | 7 +- .../server/ServerConnectionHandler.java | 17 +- 5 files changed, 92 insertions(+), 98 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 f46425d..48ee896 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 @@ -23,6 +23,7 @@ import static ch.zhaw.pm2.multichat.protocol.ConnectionHandler.State.*; * To Contact the Model Elements needed it also holds references to message and the Connectionhandler. */ public class ChatWindowController { + public Button sendButton; //TODO necessary to have a attribute when not used or delete? private ClientConnectionHandler connectionHandler; private ClientMessageList messages; private final WindowCloseHandler windowCloseHandler = new WindowCloseHandler(); @@ -156,10 +157,6 @@ public class ChatWindowController { connectionHandler.initialize(serverAddress, serverPort, userName); new Thread(connectionHandler).start(); - //register Listener //TODO what todo with methods? - //startConnectionHandlerListener(); - - // register window close handler rootPane.getScene().getWindow().addEventHandler(WindowEvent.WINDOW_CLOSE_REQUEST, windowCloseHandler); } else { addError("It is not allowed to have spaces in username!"); @@ -240,6 +237,7 @@ public class ChatWindowController { * Nested Class in charge of Closing the wind */ class WindowCloseHandler implements EventHandler { + /** * //TODO * @@ -285,7 +283,7 @@ public class ChatWindowController { } /** - * Starts a new Listener for messages by using the observable Boolean. + * Starts a new Listener for messages by using the observable Boolean. */ private void messageListener() { messages.getChangedProperty().addListener(new ChangeListener() { diff --git a/client/src/main/resources/ch/zhaw/pm2/multichat/client/ChatWindow.fxml b/client/src/main/resources/ch/zhaw/pm2/multichat/client/ChatWindow.fxml index 263979e..7715a0a 100644 --- a/client/src/main/resources/ch/zhaw/pm2/multichat/client/ChatWindow.fxml +++ b/client/src/main/resources/ch/zhaw/pm2/multichat/client/ChatWindow.fxml @@ -12,74 +12,82 @@ - - - - - - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
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 6762b0c..604865f 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 @@ -120,12 +120,10 @@ public abstract class ConnectionHandler { 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(); + String data = sender + "\n" + + receiver + "\n" + + type + "\n" + + payload + "\n"; try { connection.send(data); } catch (SocketException e) { 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 982c6bf..be26302 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 @@ -6,8 +6,7 @@ import java.io.IOException; import java.net.SocketException; import java.util.HashMap; 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. @@ -84,13 +83,11 @@ public class Server { * The connection will be registered in the connection registry if successful. */ private void start() { - ReentrantLock mutex = new ReentrantLock(); - Condition nameComplete = mutex.newCondition(); System.out.println("Server started."); try { while (true) { NetworkHandler.NetworkConnection connection = networkServer.waitForConnection(); - ServerConnectionHandler connectionHandler = new ServerConnectionHandler(connection, connections, mutex, nameComplete); + ServerConnectionHandler connectionHandler = new ServerConnectionHandler(connection, connections); new Thread(connectionHandler).start(); } } catch (SocketException e) { 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 45820bb..25e7a49 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 @@ -14,13 +14,12 @@ import java.util.Map; import java.util.Objects; import java.util.Scanner; import java.util.concurrent.atomic.AtomicInteger; -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 ServerConnectionHandler receives data send from the client as well as sends data to the client. *

* The ServeConnectionHandler offers following functionality: *

@@ -48,10 +47,6 @@ public class ServerConnectionHandler extends ConnectionHandler implements Runnab private final int connectionId = connectionCounter.incrementAndGet(); private final Map connectionRegistry; - private final ReentrantLock mutex; - - private final Condition nameComplete; - private String userName = "Anonymous-" + connectionId; private State state = NEW; @@ -68,21 +63,19 @@ public class ServerConnectionHandler extends ConnectionHandler implements Runnab * * @param connection representing the socket connection between server and client * @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) { + Map registry) { super(); setConnection(connection); Objects.requireNonNull(connection, "Connection must not be null"); Objects.requireNonNull(registry, "Registry must not be null"); this.connectionRegistry = registry; - this.mutex = mutex; - this.nameComplete = nameComplete; } - /** + /** //TODO needed method? * @return the username of the connected client */ public String getUserName() { From dad15d52d21f29e9dc3a6d3bd17c9921d376c4bf Mon Sep 17 00:00:00 2001 From: Andrin Fassbind Date: Sat, 16 Apr 2022 19:40:51 +0200 Subject: [PATCH 20/22] javadoc --- .../client/ChatWindowController.java | 15 +++++------ .../client/ClientConnectionHandler.java | 26 +++++++++---------- .../server/ServerConnectionHandler.java | 14 ---------- 3 files changed, 20 insertions(+), 35 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 48ee896..c09d7ae 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 @@ -23,7 +23,6 @@ import static ch.zhaw.pm2.multichat.protocol.ConnectionHandler.State.*; * To Contact the Model Elements needed it also holds references to message and the Connectionhandler. */ public class ChatWindowController { - public Button sendButton; //TODO necessary to have a attribute when not used or delete? private ClientConnectionHandler connectionHandler; private ClientMessageList messages; private final WindowCloseHandler windowCloseHandler = new WindowCloseHandler(); @@ -44,6 +43,8 @@ public class ChatWindowController { private Button connectButton; @FXML private TextField filterValue; + @FXML + private Button sendButton; /** @@ -69,7 +70,7 @@ public class ChatWindowController { } /** - * Method which closes the Application via use of the disconnect Method. //TODO evtl Löschen? + * Method which closes the Application via use of the disconnect Method. */ private void applicationClose() { disconnect(); @@ -121,7 +122,7 @@ public class ChatWindowController { */ @FXML private void message() { - String messageString = messageField.getText().strip(); //TODO MVC ok? + String messageString = messageField.getText().strip(); try { if (connectionHandler == null) { addError("No connection handler"); @@ -190,7 +191,7 @@ public class ChatWindowController { public void setUserName(String userName) { Platform.runLater(new Runnable() { @Override - public void run() { //TODO MVC ok?? + public void run() { userNameField.setText(userName); } }); @@ -234,14 +235,13 @@ public class ChatWindowController { } /** - * Nested Class in charge of Closing the wind + * Nested Class in charge of Closing the window */ class WindowCloseHandler implements EventHandler { /** - * //TODO * - * @param event the event which occurred + * @param event the event which occurred when Windows is closed */ public void handle(WindowEvent event) { applicationClose(); @@ -251,7 +251,6 @@ public class ChatWindowController { /** * Starts several new Listener for Connection Handler changes by using several observable properties. */ - public void startConnectionHandlerListener() { connectionHandler.getStateProperty().addListener(new ChangeListener() { @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 0bae72f..769ff94 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 @@ -47,12 +47,12 @@ public class ClientConnectionHandler extends ConnectionHandler implements Runnab } /** - * //TODO complete javadoc + * Called to initialize the ClientConnectionHandler when trying to start a connection * - * @param serverAddress - * @param serverPort - * @param userName - * @throws IOException + * @param serverAddress to connect to + * @param serverPort to connect to + * @param userName to connect as + * @throws IOException if connection to Server not possible */ public void initialize(String serverAddress, int serverPort, String userName) throws IOException { state.set(NEW); @@ -155,7 +155,7 @@ public class ClientConnectionHandler extends ConnectionHandler implements Runnab } catch (IOException e) { System.err.println("Failed to close connection." + e.getMessage()); } - System.out.println("Closed Connection Handler to Server"); //TODO should be shown also when failed to close ? + System.out.println("Closed Connection Handler to Server"); } /** @@ -255,7 +255,7 @@ public class ClientConnectionHandler extends ConnectionHandler implements Runnab } /** - * //Connects TODO + * Send connect attempt to Server * * @throws ChatProtocolException Error that is thrown if the state is not set to NEW */ @@ -266,9 +266,9 @@ public class ClientConnectionHandler extends ConnectionHandler implements Runnab } /** - * //TODO + * Send Disconnect attempt to server * - * @throws ChatProtocolException + * @throws ChatProtocolException Error tha si thrown if state is invalid */ public void disconnect() throws ChatProtocolException { if (state.get() != NEW && state.get() != CONNECTED) @@ -278,11 +278,11 @@ public class ClientConnectionHandler extends ConnectionHandler implements Runnab } /** - * //TODO + * Sends message to Server * - * @param messageString - * @return - * @throws ChatProtocolException + * @param messageString The message the user wants to send + * @return true if message is valid else false + * @throws ChatProtocolException if illegal connection state */ public boolean message(String messageString) throws ChatProtocolException { if (state.get() != CONNECTED) throw new ChatProtocolException("Illegal state for message: " + 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 25e7a49..d246c66 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 @@ -75,20 +75,6 @@ public class ServerConnectionHandler extends ConnectionHandler implements Runnab this.connectionRegistry = registry; } - /** //TODO needed method? - * @return the username of the connected client - */ - public String getUserName() { - return this.userName; - } - - /** //TODO needed method? - * @return state of the connection. Possible states are see {@link ch.zhaw.pm2.multichat.protocol.ConnectionHandler.State} - */ - public State getState() { - return state; - } - /** * These methods runs in a while-loop as long as the socket between server and client is available * and the connection State is not ERROR. From b4b91ea5133ab1d457ca943f7995520a918d3376 Mon Sep 17 00:00:00 2001 From: Andrin Fassbind Date: Sat, 16 Apr 2022 19:53:15 +0200 Subject: [PATCH 21/22] klassendiagramm --- Klassendiagramm.svg | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 Klassendiagramm.svg diff --git a/Klassendiagramm.svg b/Klassendiagramm.svg new file mode 100644 index 0000000..8382ced --- /dev/null +++ b/Klassendiagramm.svg @@ -0,0 +1,4 @@ + + + +
%3CmxGraphModel%3E%3Croot%3E%3CmxCell%20id%3D%220%22%2F%3E%3CmxCell%20id%3D%221%22%20parent%3D%220%22%2F%3E%3CmxCell%20id%3D%222%22%20style%3D%22edgeStyle%3DorthogonalEdgeStyle%3Brounded%3D0%3BorthogonalLoop%3D1%3BjettySize%3Dauto%3Bhtml%3D1%3BentryX%3D0%3BentryY%3D0.25%3BentryDx%3D0%3BentryDy%3D0%3BendArrow%3Dclassic%3BendFill%3D1%3B%22%20edge%3D%221%22%20source%3D%223%22%20target%3D%2230%22%20parent%3D%221%22%3E%3CmxGeometry%20relative%3D%221%22%20as%3D%22geometry%22%3E%3CArray%20as%3D%22points%22%3E%3CmxPoint%20x%3D%22470%22%20y%3D%2260%22%2F%3E%3CmxPoint%20x%3D%22470%22%20y%3D%22178%22%2F%3E%3C%2FArray%3E%3C%2FmxGeometry%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%223%22%20value%3D%22Client%22%20style%3D%22swimlane%3BfontStyle%3D2%3Balign%3Dcenter%3BverticalAlign%3Dtop%3BchildLayout%3DstackLayout%3Bhorizontal%3D1%3BstartSize%3D26%3BhorizontalStack%3D0%3BresizeParent%3D1%3BresizeLast%3D0%3Bcollapsible%3D1%3BmarginBottom%3D0%3Brounded%3D0%3Bshadow%3D0%3BstrokeWidth%3D1%3B%22%20vertex%3D%221%22%20parent%3D%221%22%3E%3CmxGeometry%20x%3D%22220%22%20y%3D%2220%22%20width%3D%22160%22%20height%3D%2280%22%20as%3D%22geometry%22%3E%3CmxRectangle%20x%3D%22230%22%20y%3D%22140%22%20width%3D%22160%22%20height%3D%2226%22%20as%3D%22alternateBounds%22%2F%3E%3C%2FmxGeometry%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%224%22%20value%3D%22%22%20style%3D%22rounded%3D0%3BwhiteSpace%3Dwrap%3Bhtml%3D1%3B%22%20vertex%3D%221%22%20parent%3D%223%22%3E%3CmxGeometry%20y%3D%2226%22%20width%3D%22160%22%20height%3D%2224%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%225%22%20value%3D%22%2B%20main(args%3AString%5B%5D)%3A%20void%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3BfontStyle%3D4%22%20vertex%3D%221%22%20parent%3D%223%22%3E%3CmxGeometry%20y%3D%2250%22%20width%3D%22160%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%226%22%20value%3D%22ClientConnectionHandler%22%20style%3D%22swimlane%3BfontStyle%3D0%3Balign%3Dcenter%3BverticalAlign%3Dtop%3BchildLayout%3DstackLayout%3Bhorizontal%3D1%3BstartSize%3D26%3BhorizontalStack%3D0%3BresizeParent%3D1%3BresizeLast%3D0%3Bcollapsible%3D1%3BmarginBottom%3D0%3Brounded%3D0%3Bshadow%3D0%3BstrokeWidth%3D1%3B%22%20vertex%3D%221%22%20parent%3D%221%22%3E%3CmxGeometry%20x%3D%2220%22%20y%3D%22150%22%20width%3D%22430%22%20height%3D%22594%22%20as%3D%22geometry%22%3E%3CmxRectangle%20x%3D%22130%22%20y%3D%22380%22%20width%3D%22160%22%20height%3D%2226%22%20as%3D%22alternateBounds%22%2F%3E%3C%2FmxGeometry%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%227%22%20value%3D%22-%20connection%3A%C2%A0NetworkHandler.NetworkConnection%26lt%3BString%26gt%3B%20%7Bfinal%7D%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3B%22%20vertex%3D%221%22%20parent%3D%226%22%3E%3CmxGeometry%20y%3D%2226%22%20width%3D%22430%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%228%22%20value%3D%22-%20controller%3A%C2%A0ChatWindowController%20%7Bfinal%7D%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%226%22%3E%3CmxGeometry%20y%3D%2252%22%20width%3D%22430%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%229%22%20value%3D%22-%20DATA_TYPE_CONNECT%3A%20String%20%7Bfinal%7D%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%226%22%3E%3CmxGeometry%20y%3D%2278%22%20width%3D%22430%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2210%22%20value%3D%22-%20DATA_TYPE_CONFIRM%3A%20String%20%7Bfinal%7D%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%226%22%3E%3CmxGeometry%20y%3D%22104%22%20width%3D%22430%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2211%22%20value%3D%22-%20DATA_TYPE_DISCONNECT%3A%20String%20%7Bfinal%7D%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%226%22%3E%3CmxGeometry%20y%3D%22130%22%20width%3D%22430%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2212%22%20value%3D%22-%20DATA_TYPE_MESSAGE%3A%20String%20%7Bfinal%7D%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%226%22%3E%3CmxGeometry%20y%3D%22156%22%20width%3D%22430%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2213%22%20value%3D%22-%20DATA_TYPE_ERROR%3A%20String%20%7Bfinal%7D%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%226%22%3E%3CmxGeometry%20y%3D%22182%22%20width%3D%22430%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2214%22%20value%3D%22%2B%20USER_NONE%3A%20String%20%7Bfinal%7D%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%226%22%3E%3CmxGeometry%20y%3D%22208%22%20width%3D%22430%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2215%22%20value%3D%22%2B%20USER_ALL%3A%20String%20%7Bfinal%7D%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%226%22%3E%3CmxGeometry%20y%3D%22234%22%20width%3D%22430%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2216%22%20value%3D%22-%20userName%3A%20String%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%226%22%3E%3CmxGeometry%20y%3D%22260%22%20width%3D%22430%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2217%22%20value%3D%22-%20state%3A%20State%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%226%22%3E%3CmxGeometry%20y%3D%22286%22%20width%3D%22430%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2218%22%20value%3D%22%22%20style%3D%22line%3Bhtml%3D1%3BstrokeWidth%3D1%3Balign%3Dleft%3BverticalAlign%3Dmiddle%3BspacingTop%3D-1%3BspacingLeft%3D3%3BspacingRight%3D3%3Brotatable%3D0%3BlabelPosition%3Dright%3Bpoints%3D%5B%5D%3BportConstraint%3Deastwest%3B%22%20vertex%3D%221%22%20parent%3D%226%22%3E%3CmxGeometry%20y%3D%22312%22%20width%3D%22430%22%20height%3D%228%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2219%22%20value%3D%22%2B%20ClientEventHandler(NetworkHandler.NetworkConnection%26lt%3BString%26gt%3B%20connection%2C%26%2310%3B%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0String%20userName%2C%20ChatWindowController%20controller)%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3BfontStyle%3D0%22%20vertex%3D%221%22%20parent%3D%226%22%3E%3CmxGeometry%20y%3D%22320%22%20width%3D%22430%22%20height%3D%2240%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2220%22%20value%3D%22%2B%C2%A0getState()%3A%20State%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%226%22%3E%3CmxGeometry%20y%3D%22360%22%20width%3D%22430%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2221%22%20value%3D%22%2B%C2%A0setState%20(State%20newState)%3A%20void%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%226%22%3E%3CmxGeometry%20y%3D%22386%22%20width%3D%22430%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2222%22%20value%3D%22%2B%C2%A0run%20()%3A%20void%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%226%22%3E%3CmxGeometry%20y%3D%22412%22%20width%3D%22430%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2223%22%20value%3D%22%2B%C2%A0startReceiving()%3A%20void%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%226%22%3E%3CmxGeometry%20y%3D%22438%22%20width%3D%22430%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2224%22%20value%3D%22-%C2%A0processData(String%20data)%3A%20void%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%226%22%3E%3CmxGeometry%20y%3D%22464%22%20width%3D%22430%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2225%22%20value%3D%22%2B%C2%A0sendData(String%20sender%2C%20String%20receiver%2C%20String%20type%2C%20String%20payload)%3A%20void%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%226%22%3E%3CmxGeometry%20y%3D%22490%22%20width%3D%22430%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2226%22%20value%3D%22%2B%C2%A0connect()%3A%20void%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%226%22%3E%3CmxGeometry%20y%3D%22516%22%20width%3D%22430%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2227%22%20value%3D%22%2B%C2%A0disconnect()%3A%20void%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%226%22%3E%3CmxGeometry%20y%3D%22542%22%20width%3D%22430%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2228%22%20value%3D%22%2B%C2%A0message(String%20receiver%2C%20String%20message)%3A%20void%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%226%22%3E%3CmxGeometry%20y%3D%22568%22%20width%3D%22430%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2229%22%20style%3D%22edgeStyle%3DorthogonalEdgeStyle%3Brounded%3D0%3BorthogonalLoop%3D1%3BjettySize%3Dauto%3Bhtml%3D1%3BendArrow%3Dblock%3BendFill%3D0%3BentryX%3D0.5%3BentryY%3D1%3BentryDx%3D0%3BentryDy%3D0%3B%22%20edge%3D%221%22%20source%3D%2230%22%20target%3D%2234%22%20parent%3D%221%22%3E%3CmxGeometry%20relative%3D%221%22%20as%3D%22geometry%22%3E%3CmxPoint%20x%3D%22588%22%20y%3D%22120%22%20as%3D%22targetPoint%22%2F%3E%3C%2FmxGeometry%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2230%22%20value%3D%22ClientUI%22%20style%3D%22swimlane%3BfontStyle%3D0%3Balign%3Dcenter%3BverticalAlign%3Dtop%3BchildLayout%3DstackLayout%3Bhorizontal%3D1%3BstartSize%3D26%3BhorizontalStack%3D0%3BresizeParent%3D1%3BresizeLast%3D0%3Bcollapsible%3D1%3BmarginBottom%3D0%3Brounded%3D0%3Bshadow%3D0%3BstrokeWidth%3D1%3B%22%20vertex%3D%221%22%20parent%3D%221%22%3E%3CmxGeometry%20x%3D%22508%22%20y%3D%22150%22%20width%3D%22252%22%20height%3D%22110%22%20as%3D%22geometry%22%3E%3CmxRectangle%20x%3D%22550%22%20y%3D%22140%22%20width%3D%22160%22%20height%3D%2226%22%20as%3D%22alternateBounds%22%2F%3E%3C%2FmxGeometry%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2231%22%20value%3D%22%22%20style%3D%22rounded%3D0%3BwhiteSpace%3Dwrap%3Bhtml%3D1%3B%22%20vertex%3D%221%22%20parent%3D%2230%22%3E%3CmxGeometry%20y%3D%2226%22%20width%3D%22252%22%20height%3D%2224%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2232%22%20value%3D%22%2B%20start(primaryStage%3A%20Stage)%3A%20void%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3B%22%20vertex%3D%221%22%20parent%3D%2230%22%3E%3CmxGeometry%20y%3D%2250%22%20width%3D%22252%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2233%22%20value%3D%22-%20chatWindow(primaryStage%3A%20Stage)%3A%20void%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3B%22%20vertex%3D%221%22%20parent%3D%2230%22%3E%3CmxGeometry%20y%3D%2276%22%20width%3D%22252%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2234%22%20value%3D%22%26lt%3B%26lt%3Babstract%26gt%3B%26gt%3B%20Client%22%20style%3D%22swimlane%3BfontStyle%3D2%3Balign%3Dcenter%3BverticalAlign%3Dtop%3BchildLayout%3DstackLayout%3Bhorizontal%3D1%3BstartSize%3D26%3BhorizontalStack%3D0%3BresizeParent%3D1%3BresizeLast%3D0%3Bcollapsible%3D1%3BmarginBottom%3D0%3Brounded%3D0%3Bshadow%3D0%3BstrokeWidth%3D1%3B%22%20vertex%3D%221%22%20parent%3D%221%22%3E%3CmxGeometry%20x%3D%22508%22%20y%3D%2220%22%20width%3D%22252%22%20height%3D%2280%22%20as%3D%22geometry%22%3E%3CmxRectangle%20x%3D%22230%22%20y%3D%22140%22%20width%3D%22160%22%20height%3D%2226%22%20as%3D%22alternateBounds%22%2F%3E%3C%2FmxGeometry%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2235%22%20value%3D%22%22%20style%3D%22rounded%3D0%3BwhiteSpace%3Dwrap%3Bhtml%3D1%3B%22%20vertex%3D%221%22%20parent%3D%2234%22%3E%3CmxGeometry%20y%3D%2226%22%20width%3D%22252%22%20height%3D%2224%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2236%22%20value%3D%22%2B%20start(primaryStage%3A%20Stage)%3A%20void%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3BfontStyle%3D4%22%20vertex%3D%221%22%20parent%3D%2234%22%3E%3CmxGeometry%20y%3D%2250%22%20width%3D%22252%22%20height%3D%2220%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2237%22%20style%3D%22edgeStyle%3DorthogonalEdgeStyle%3Brounded%3D0%3BorthogonalLoop%3D1%3BjettySize%3Dauto%3Bhtml%3D1%3BendArrow%3Dblock%3BendFill%3D0%3BexitX%3D0.5%3BexitY%3D0%3BexitDx%3D0%3BexitDy%3D0%3B%22%20edge%3D%221%22%20source%3D%226%22%20target%3D%2238%22%20parent%3D%221%22%3E%3CmxGeometry%20relative%3D%221%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2238%22%20value%3D%22%26lt%3B%26lt%3Binterface%26gt%3B%26gt%3B%20Runnable%22%20style%3D%22swimlane%3BfontStyle%3D0%3BchildLayout%3DstackLayout%3Bhorizontal%3D1%3BstartSize%3D30%3BhorizontalStack%3D0%3BresizeParent%3D1%3BresizeParentMax%3D0%3BresizeLast%3D0%3Bcollapsible%3D1%3BmarginBottom%3D0%3B%22%20vertex%3D%221%22%20parent%3D%221%22%3E%3CmxGeometry%20x%3D%2220%22%20y%3D%2220%22%20width%3D%22140%22%20height%3D%2280%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2239%22%20value%3D%22%22%20style%3D%22rounded%3D0%3BwhiteSpace%3Dwrap%3Bhtml%3D1%3B%22%20vertex%3D%221%22%20parent%3D%2238%22%3E%3CmxGeometry%20y%3D%2230%22%20width%3D%22140%22%20height%3D%2220%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2240%22%20value%3D%22%2B%20run()%3A%20void%22%20style%3D%22text%3BstrokeColor%3Dnone%3BfillColor%3Dnone%3Balign%3Dleft%3BverticalAlign%3Dmiddle%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brotatable%3D0%3B%22%20vertex%3D%221%22%20parent%3D%2238%22%3E%3CmxGeometry%20y%3D%2250%22%20width%3D%22140%22%20height%3D%2230%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2241%22%20value%3D%22ClientMessageList%22%20style%3D%22swimlane%3BfontStyle%3D0%3Balign%3Dcenter%3BverticalAlign%3Dtop%3BchildLayout%3DstackLayout%3Bhorizontal%3D1%3BstartSize%3D26%3BhorizontalStack%3D0%3BresizeParent%3D1%3BresizeLast%3D0%3Bcollapsible%3D1%3BmarginBottom%3D0%3Brounded%3D0%3Bshadow%3D0%3BstrokeWidth%3D1%3B%22%20vertex%3D%221%22%20parent%3D%221%22%3E%3CmxGeometry%20x%3D%22508%22%20y%3D%22288%22%20width%3D%22272%22%20height%3D%22336%22%20as%3D%22geometry%22%3E%3CmxRectangle%20x%3D%22130%22%20y%3D%22380%22%20width%3D%22160%22%20height%3D%2226%22%20as%3D%22alternateBounds%22%2F%3E%3C%2FmxGeometry%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2242%22%20value%3D%22-%C2%A0typeList%3A%C2%A0List%26lt%3BMessageType%26gt%3B%20%7Bfinal%7D%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%2241%22%3E%3CmxGeometry%20y%3D%2226%22%20width%3D%22272%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2243%22%20value%3D%22-%C2%A0senderList%3A%C2%A0List%26lt%3BString%26gt%3B%20%7Bfinal%7D%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%2241%22%3E%3CmxGeometry%20y%3D%2252%22%20width%3D%22272%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2244%22%20value%3D%22-%C2%A0receiverList%3A%C2%A0List%26lt%3BString%26gt%3B%20%7Bfinal%7D%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%2241%22%3E%3CmxGeometry%20y%3D%2278%22%20width%3D%22272%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2245%22%20value%3D%22-%C2%A0messageList%3A%C2%A0List%26lt%3BString%26gt%3B%20%7Bfinal%7D%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%2241%22%3E%3CmxGeometry%20y%3D%22104%22%20width%3D%22272%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2246%22%20value%3D%22-%C2%A0gui%3A%20ChatWindowController%20%7Bfinal%7D%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%2241%22%3E%3CmxGeometry%20y%3D%22130%22%20width%3D%22272%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2247%22%20value%3D%22%22%20style%3D%22line%3Bhtml%3D1%3BstrokeWidth%3D1%3Balign%3Dleft%3BverticalAlign%3Dmiddle%3BspacingTop%3D-1%3BspacingLeft%3D3%3BspacingRight%3D3%3Brotatable%3D0%3BlabelPosition%3Dright%3Bpoints%3D%5B%5D%3BportConstraint%3Deastwest%3B%22%20vertex%3D%221%22%20parent%3D%2241%22%3E%3CmxGeometry%20y%3D%22156%22%20width%3D%22272%22%20height%3D%228%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2248%22%20value%3D%22%2B%C2%A0ClientMessageList(ChatWindowController%20gui)%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%2241%22%3E%3CmxGeometry%20y%3D%22164%22%20width%3D%22272%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2249%22%20value%3D%22%2B%C2%A0addMessage(MessageType%20type%2C%20String%20sender%2C%26%2310%3B%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0String%20receiver%2C%20String%20message)%3A%20void%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%2241%22%3E%3CmxGeometry%20y%3D%22190%22%20width%3D%22272%22%20height%3D%2250%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2250%22%20value%3D%22%2B%C2%A0writeFilteredMessages(String%20filter)%3A%20void%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%2241%22%3E%3CmxGeometry%20y%3D%22240%22%20width%3D%22272%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2251%22%20value%3D%22%26amp%3Blt%3B%26amp%3Blt%3BEnumeration%26amp%3Bgt%3B%26amp%3Bgt%3B%20MessageType%26lt%3Bbr%26gt%3B-%26amp%3Bnbsp%3BINFO%26lt%3Bbr%26gt%3B-%26amp%3Bnbsp%3BMESSAGE%26lt%3Bbr%26gt%3B-%26amp%3Bnbsp%3BERROR%22%20style%3D%22rounded%3D0%3BwhiteSpace%3Dwrap%3Bhtml%3D1%3Balign%3Dleft%3B%22%20vertex%3D%221%22%20parent%3D%2241%22%3E%3CmxGeometry%20y%3D%22266%22%20width%3D%22272%22%20height%3D%2270%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2252%22%20value%3D%22ChatWindowController%22%20style%3D%22swimlane%3BfontStyle%3D0%3Balign%3Dcenter%3BverticalAlign%3Dtop%3BchildLayout%3DstackLayout%3Bhorizontal%3D1%3BstartSize%3D26%3BhorizontalStack%3D0%3BresizeParent%3D1%3BresizeLast%3D0%3Bcollapsible%3D1%3BmarginBottom%3D0%3Brounded%3D0%3Bshadow%3D0%3BstrokeWidth%3D1%3B%22%20vertex%3D%221%22%20parent%3D%221%22%3E%3CmxGeometry%20x%3D%22480%22%20y%3D%22670%22%20width%3D%22390%22%20height%3D%22978%22%20as%3D%22geometry%22%3E%3CmxRectangle%20x%3D%22130%22%20y%3D%22380%22%20width%3D%22160%22%20height%3D%2226%22%20as%3D%22alternateBounds%22%2F%3E%3C%2FmxGeometry%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2253%22%20value%3D%22-%C2%A0messagePattern%3A%C2%A0Pattern%20%7Bfinal%7D%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%2252%22%3E%3CmxGeometry%20y%3D%2226%22%20width%3D%22390%22%20height%3D%2222%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2254%22%20value%3D%22-%C2%A0connectionHandler%3A%C2%A0ClientConnectionHandler%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%2252%22%3E%3CmxGeometry%20y%3D%2248%22%20width%3D%22390%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2255%22%20value%3D%22-%C2%A0messages%3A%C2%A0ClientMessageList%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%2252%22%3E%3CmxGeometry%20y%3D%2274%22%20width%3D%22390%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2256%22%20value%3D%22-%20windowCloseHandler%3A%C2%A0WindowCloseHandler%20%7Bfinal%7D%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%2252%22%3E%3CmxGeometry%20y%3D%22100%22%20width%3D%22390%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2257%22%20value%3D%22-%C2%A0rootPane%3A%C2%A0Pane%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%2252%22%3E%3CmxGeometry%20y%3D%22126%22%20width%3D%22390%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2258%22%20value%3D%22-%C2%A0serverAddressField%3A%C2%A0TextField%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%2252%22%3E%3CmxGeometry%20y%3D%22152%22%20width%3D%22390%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2259%22%20value%3D%22-%C2%A0serverPortField%3A%C2%A0TextField%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%2252%22%3E%3CmxGeometry%20y%3D%22178%22%20width%3D%22390%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2260%22%20value%3D%22-%C2%A0userNameField%3A%C2%A0TextField%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%2252%22%3E%3CmxGeometry%20y%3D%22204%22%20width%3D%22390%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2261%22%20value%3D%22-%C2%A0messageField%3A%C2%A0TextField%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%2252%22%3E%3CmxGeometry%20y%3D%22230%22%20width%3D%22390%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2262%22%20value%3D%22-%C2%A0messageArea%3A%C2%A0TextArea%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%2252%22%3E%3CmxGeometry%20y%3D%22256%22%20width%3D%22390%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2263%22%20value%3D%22-%C2%A0connectButton%3A%C2%A0Button%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%2252%22%3E%3CmxGeometry%20y%3D%22282%22%20width%3D%22390%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2264%22%20value%3D%22-%C2%A0sendButton%3A%C2%A0Button%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%2252%22%3E%3CmxGeometry%20y%3D%22308%22%20width%3D%22390%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2265%22%20value%3D%22-%C2%A0filterValue%3A%C2%A0TextField%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%2252%22%3E%3CmxGeometry%20y%3D%22334%22%20width%3D%22390%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2266%22%20value%3D%22%22%20style%3D%22line%3Bhtml%3D1%3BstrokeWidth%3D1%3Balign%3Dleft%3BverticalAlign%3Dmiddle%3BspacingTop%3D-1%3BspacingLeft%3D3%3BspacingRight%3D3%3Brotatable%3D0%3BlabelPosition%3Dright%3Bpoints%3D%5B%5D%3BportConstraint%3Deastwest%3B%22%20vertex%3D%221%22%20parent%3D%2252%22%3E%3CmxGeometry%20y%3D%22360%22%20width%3D%22390%22%20height%3D%228%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2267%22%20value%3D%22%2B%C2%A0initialize()%3A%20void%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%2252%22%3E%3CmxGeometry%20y%3D%22368%22%20width%3D%22390%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2268%22%20value%3D%22-%C2%A0applicationClose()%3A%20void%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%2252%22%3E%3CmxGeometry%20y%3D%22394%22%20width%3D%22390%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2269%22%20value%3D%22-%C2%A0toggleConnection()%3A%20void%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%2252%22%3E%3CmxGeometry%20y%3D%22420%22%20width%3D%22390%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2270%22%20value%3D%22-%C2%A0connect()%3A%20void%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%2252%22%3E%3CmxGeometry%20y%3D%22446%22%20width%3D%22390%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2271%22%20value%3D%22-%C2%A0disconnect()%3A%20void%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%2252%22%3E%3CmxGeometry%20y%3D%22472%22%20width%3D%22390%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2272%22%20value%3D%22-%C2%A0disconnect()%3A%20void%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%2252%22%3E%3CmxGeometry%20y%3D%22498%22%20width%3D%22390%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2273%22%20value%3D%22-%C2%A0message()%3A%20void%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%2252%22%3E%3CmxGeometry%20y%3D%22524%22%20width%3D%22390%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2274%22%20value%3D%22-%C2%A0applyFilter(%20)%3A%20void%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%2252%22%3E%3CmxGeometry%20y%3D%22550%22%20width%3D%22390%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2275%22%20value%3D%22-%C2%A0terminateConnectionHandler()%3A%20void%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%2252%22%3E%3CmxGeometry%20y%3D%22576%22%20width%3D%22390%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2276%22%20value%3D%22%2B%C2%A0stateChanged(State%20newState)%3A%20void%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%2252%22%3E%3CmxGeometry%20y%3D%22602%22%20width%3D%22390%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2277%22%20value%3D%22%2B%C2%A0setUserName(String%20userName)%3A%20void%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%2252%22%3E%3CmxGeometry%20y%3D%22628%22%20width%3D%22390%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2278%22%20value%3D%22%2B%C2%A0setServerAddress(String%20serverAddress)%3A%20void%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%2252%22%3E%3CmxGeometry%20y%3D%22654%22%20width%3D%22390%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2279%22%20value%3D%22%2B%C2%A0setServerPort(int%20serverPort)%3A%20void%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%2252%22%3E%3CmxGeometry%20y%3D%22680%22%20width%3D%22390%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2280%22%20value%3D%22%2B%C2%A0addMessage(String%20sender%2C%20String%20receiver%2C%20String%20message)%3A%20void%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%2252%22%3E%3CmxGeometry%20y%3D%22706%22%20width%3D%22390%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2281%22%20value%3D%22%2B%C2%A0addInfo(String%20message)%3A%20void%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%2252%22%3E%3CmxGeometry%20y%3D%22732%22%20width%3D%22390%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2282%22%20value%3D%22%2B%C2%A0addError(String%20message)%3A%20void%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%2252%22%3E%3CmxGeometry%20y%3D%22758%22%20width%3D%22390%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2283%22%20value%3D%22%2B%C2%A0clearMessageArea()%3A%20void%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%2252%22%3E%3CmxGeometry%20y%3D%22784%22%20width%3D%22390%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2284%22%20value%3D%22-%C2%A0redrawMessageList()%3A%20void%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%2252%22%3E%3CmxGeometry%20y%3D%22810%22%20width%3D%22390%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2285%22%20value%3D%22%2B%20writeError(String%20message)%3A%20void%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%2252%22%3E%3CmxGeometry%20y%3D%22836%22%20width%3D%22390%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2286%22%20value%3D%22%2B%20writeInfo(String%20message)%3A%20void%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%2252%22%3E%3CmxGeometry%20y%3D%22862%22%20width%3D%22390%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2287%22%20value%3D%22%2B%20writeMessage(String%20sender%2C%20String%20reciever%2C%20String%20message)%3A%20void%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3B%22%20vertex%3D%221%22%20parent%3D%2252%22%3E%3CmxGeometry%20y%3D%22888%22%20width%3D%22390%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2288%22%20value%3D%22%26amp%3Blt%3B%26amp%3Blt%3BClass%26amp%3Bgt%3B%26amp%3Bgt%3B%20WindowCloseHandler%26lt%3Bbr%26gt%3B%2B%20handle(WindowEvent%20event)%3A%20void%22%20style%3D%22rounded%3D0%3BwhiteSpace%3Dwrap%3Bhtml%3D1%3Balign%3Dleft%3B%22%20vertex%3D%221%22%20parent%3D%2252%22%3E%3CmxGeometry%20y%3D%22914%22%20width%3D%22390%22%20height%3D%2264%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3C%2Froot%3E%3C%2FmxGraphModel%3E
%3C...
Client+ main(args:String[]): voidClientConnectionHandler- messagePattern:Pattern{final}-userName:SimpleStringProperty- state:SimpleObjectProperty<State>- messages:ClientMessageList- serverAddress:SimpleStringProperty- serverPort:SimpleIntegerProperty+ ClientConnectionHandler(messages:ClientMessageList)+initialize(serverAddress:String,serverPort:int,userName:String):void+getServerAddressProperty():SimpleStringProperty+getServerPortProperty():SimpleIntegerProperty+getStateProperty():SimpleObjectProperty+getUsernameProperty():SimpleStringProperty+ setState (State newState): void+ run (): void+ startReceiving(): void+ stopReceiving(): void- processData(String data): void-caseConfirm(sender:String):void-caseMessage(sender:String,reciever:String,type:String,payload:String):void-caseDisconnect():void-caseError():void+ connect(): void+ disconnect(): void+ message(String receiver, String message): voidClientUI+ start(primaryStage: Stage): void- chatWindow(primaryStage: Stage): void<<abstract>> Client+ start(primaryStage: Stage): void<<interface>> Runnable+ run(): voidClientMessageList- messages: List<Message> {final}- changed:SimpleBooleanProperty+ addMessage(message:Message): void+ getFilteredMessages(filter:string):String+ clear():void+ getChangedProperty():SimpleBooleanPropertyChatWindowController- messagePattern: Pattern {final}- messages: ClientMessageList- windowCloseHandler: WindowCloseHandler {final}- rootPane: Pane- serverAddressField: TextField- serverPortField: TextField- connectionHandler: ClientConnectionHandler- userNameField: TextField- messageField: TextField- messageArea: TextArea- connectButton: Button- sendButton: Button- filterValue: TextField+ setMessages(messages:ClientMessageList): void+ setConnectionHandler(connectionHandler:ClientConnectionHandler): void- applicationClose(): void- toggleConnection(): void- connect(): void- disconnect(): void- message(): void- applyFilter( ): void- startConnectionHandler( ): void+ stateChanged(State newState): void+ setServerAddress(String serverAddress): void+ setServerPort(int serverPort): void+ setUserName(String userName): void+ addError(String message): void- startConnectionHandlerListener( ): void-messageListener():void
<<Class>> WindowCloseHandler
+ handle(WindowEvent event): void
<<Class>> WindowCloseHandler...
%3CmxGraphModel%3E%3Croot%3E%3CmxCell%20id%3D%220%22%2F%3E%3CmxCell%20id%3D%221%22%20parent%3D%220%22%2F%3E%3CmxCell%20id%3D%222%22%20value%3D%22NetworkHandler%22%20style%3D%22swimlane%3BfontStyle%3D2%3Balign%3Dcenter%3BverticalAlign%3Dtop%3BchildLayout%3DstackLayout%3Bhorizontal%3D1%3BstartSize%3D26%3BhorizontalStack%3D0%3BresizeParent%3D1%3BresizeLast%3D0%3Bcollapsible%3D1%3BmarginBottom%3D0%3Brounded%3D0%3Bshadow%3D0%3BstrokeWidth%3D1%3B%22%20vertex%3D%221%22%20parent%3D%221%22%3E%3CmxGeometry%20x%3D%2270%22%20y%3D%22320%22%20width%3D%22400%22%20height%3D%22216%22%20as%3D%22geometry%22%3E%3CmxRectangle%20x%3D%22230%22%20y%3D%22140%22%20width%3D%22160%22%20height%3D%2226%22%20as%3D%22alternateBounds%22%2F%3E%3C%2FmxGeometry%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%223%22%20value%3D%22%2BDEFAULT_ADDRESS%3AInetAddress%7Bfinal%7D%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3BfontStyle%3D4%22%20vertex%3D%221%22%20parent%3D%222%22%3E%3CmxGeometry%20y%3D%2226%22%20width%3D%22400%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%224%22%20value%3D%22%2BDEFAULT_PORT%3Aint%7Bfinal%7D%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3Brounded%3D0%3Bshadow%3D0%3Bhtml%3D0%3BfontStyle%3D4%22%20vertex%3D%221%22%20parent%3D%222%22%3E%3CmxGeometry%20y%3D%2252%22%20width%3D%22400%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%225%22%20value%3D%22%22%20style%3D%22line%3Bhtml%3D1%3BstrokeWidth%3D1%3Balign%3Dleft%3BverticalAlign%3Dmiddle%3BspacingTop%3D-1%3BspacingLeft%3D3%3BspacingRight%3D3%3Brotatable%3D0%3BlabelPosition%3Dright%3Bpoints%3D%5B%5D%3BportConstraint%3Deastwest%3B%22%20vertex%3D%221%22%20parent%3D%222%22%3E%3CmxGeometry%20y%3D%2278%22%20width%3D%22400%22%20height%3D%228%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%226%22%20value%3D%22%2BcreateServer(port%3Aint)%3ANetworkServer%26lt%3BT%26gt%3B%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3B%22%20vertex%3D%221%22%20parent%3D%222%22%3E%3CmxGeometry%20y%3D%2286%22%20width%3D%22400%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%227%22%20value%3D%22%2BcreatServer()%3ANetworkServer%26lt%3BT%26gt%3B%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3B%22%20vertex%3D%221%22%20parent%3D%222%22%3E%3CmxGeometry%20y%3D%22112%22%20width%3D%22400%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%228%22%20value%3D%22%2BopenConnection(address%3AInetAddress%2Cport%3Aint)%3ANetworkConnection%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3B%22%20vertex%3D%221%22%20parent%3D%222%22%3E%3CmxGeometry%20y%3D%22138%22%20width%3D%22400%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%229%22%20value%3D%22%2BopenConnection(hostname%3AString%2Cport%3Aint)%3ANetworkConnection%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3B%22%20vertex%3D%221%22%20parent%3D%222%22%3E%3CmxGeometry%20y%3D%22164%22%20width%3D%22400%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2210%22%20value%3D%22%2BopenConnection()%3ANetworkConnection%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3B%22%20vertex%3D%221%22%20parent%3D%222%22%3E%3CmxGeometry%20y%3D%22190%22%20width%3D%22400%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2211%22%20value%3D%22NetworkServer%22%20style%3D%22swimlane%3BfontStyle%3D0%3Balign%3Dcenter%3BverticalAlign%3Dtop%3BchildLayout%3DstackLayout%3Bhorizontal%3D1%3BstartSize%3D26%3BhorizontalStack%3D0%3BresizeParent%3D1%3BresizeLast%3D0%3Bcollapsible%3D1%3BmarginBottom%3D0%3Brounded%3D0%3Bshadow%3D0%3BstrokeWidth%3D1%3B%22%20vertex%3D%221%22%20parent%3D%221%22%3E%3CmxGeometry%20x%3D%22540%22%20y%3D%22320%22%20width%3D%22200%22%20height%3D%22270%22%20as%3D%22geometry%22%3E%3CmxRectangle%20x%3D%22550%22%20y%3D%22140%22%20width%3D%22160%22%20height%3D%2226%22%20as%3D%22alternateBounds%22%2F%3E%3C%2FmxGeometry%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2212%22%20value%3D%22-serverSocket%3AServerSocket%7Bfinal%7D%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3BfontStyle%3D4%22%20vertex%3D%221%22%20parent%3D%2211%22%3E%3CmxGeometry%20y%3D%2226%22%20width%3D%22200%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2213%22%20value%3D%22%22%20style%3D%22line%3Bhtml%3D1%3BstrokeWidth%3D1%3Balign%3Dleft%3BverticalAlign%3Dmiddle%3BspacingTop%3D-1%3BspacingLeft%3D3%3BspacingRight%3D3%3Brotatable%3D0%3BlabelPosition%3Dright%3Bpoints%3D%5B%5D%3BportConstraint%3Deastwest%3B%22%20vertex%3D%221%22%20parent%3D%2211%22%3E%3CmxGeometry%20y%3D%2252%22%20width%3D%22200%22%20height%3D%228%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2214%22%20value%3D%22-NetworkServer(port%3Aint)%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3B%22%20vertex%3D%221%22%20parent%3D%2211%22%3E%3CmxGeometry%20y%3D%2260%22%20width%3D%22200%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2215%22%20value%3D%22-NetworkServer()%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3B%22%20vertex%3D%221%22%20parent%3D%2211%22%3E%3CmxGeometry%20y%3D%2286%22%20width%3D%22200%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2216%22%20value%3D%22%2BwaitForConnection()%3ANetworkConnection%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3B%22%20vertex%3D%221%22%20parent%3D%2211%22%3E%3CmxGeometry%20y%3D%22112%22%20width%3D%22200%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2217%22%20value%3D%22%2BisAvailable()%3Aboolean%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3B%22%20vertex%3D%221%22%20parent%3D%2211%22%3E%3CmxGeometry%20y%3D%22138%22%20width%3D%22200%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2218%22%20value%3D%22%2BisClosed()%3Aboolean%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3B%22%20vertex%3D%221%22%20parent%3D%2211%22%3E%3CmxGeometry%20y%3D%22164%22%20width%3D%22200%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2219%22%20value%3D%22%2BgetHostPort()%3Aboolean%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3B%22%20vertex%3D%221%22%20parent%3D%2211%22%3E%3CmxGeometry%20y%3D%22190%22%20width%3D%22200%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2220%22%20value%3D%22%2BgetHostAddress()%3AString%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3B%22%20vertex%3D%221%22%20parent%3D%2211%22%3E%3CmxGeometry%20y%3D%22216%22%20width%3D%22200%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2221%22%20value%3D%22%2Bclose()%3Avoid%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3B%22%20vertex%3D%221%22%20parent%3D%2211%22%3E%3CmxGeometry%20y%3D%22242%22%20width%3D%22200%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2222%22%20value%3D%22NetworkConnection%22%20style%3D%22swimlane%3BfontStyle%3D0%3Balign%3Dcenter%3BverticalAlign%3Dtop%3BchildLayout%3DstackLayout%3Bhorizontal%3D1%3BstartSize%3D26%3BhorizontalStack%3D0%3BresizeParent%3D1%3BresizeLast%3D0%3Bcollapsible%3D1%3BmarginBottom%3D0%3Brounded%3D0%3Bshadow%3D0%3BstrokeWidth%3D1%3B%22%20vertex%3D%221%22%20parent%3D%221%22%3E%3CmxGeometry%20x%3D%22190%22%20y%3D%22580%22%20width%3D%22200%22%20height%3D%22270%22%20as%3D%22geometry%22%3E%3CmxRectangle%20x%3D%22550%22%20y%3D%22140%22%20width%3D%22160%22%20height%3D%2226%22%20as%3D%22alternateBounds%22%2F%3E%3C%2FmxGeometry%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2223%22%20value%3D%22-socket%3ASocket%7Bfinal%7D%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3BfontStyle%3D4%22%20vertex%3D%221%22%20parent%3D%2222%22%3E%3CmxGeometry%20y%3D%2226%22%20width%3D%22200%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2224%22%20value%3D%22%22%20style%3D%22line%3Bhtml%3D1%3BstrokeWidth%3D1%3Balign%3Dleft%3BverticalAlign%3Dmiddle%3BspacingTop%3D-1%3BspacingLeft%3D3%3BspacingRight%3D3%3Brotatable%3D0%3BlabelPosition%3Dright%3Bpoints%3D%5B%5D%3BportConstraint%3Deastwest%3B%22%20vertex%3D%221%22%20parent%3D%2222%22%3E%3CmxGeometry%20y%3D%2252%22%20width%3D%22200%22%20height%3D%228%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2225%22%20value%3D%22-NetworkConnection(socket%3ASocket)%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3B%22%20vertex%3D%221%22%20parent%3D%2222%22%3E%3CmxGeometry%20y%3D%2260%22%20width%3D%22200%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2226%22%20value%3D%22%2Bsend(data%3AT)%3Avoid%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3B%22%20vertex%3D%221%22%20parent%3D%2222%22%3E%3CmxGeometry%20y%3D%2286%22%20width%3D%22200%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2227%22%20value%3D%22%2Breceive()%3AT%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3B%22%20vertex%3D%221%22%20parent%3D%2222%22%3E%3CmxGeometry%20y%3D%22112%22%20width%3D%22200%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2228%22%20value%3D%22%2BisAvailable()%3Aboolean%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3B%22%20vertex%3D%221%22%20parent%3D%2222%22%3E%3CmxGeometry%20y%3D%22138%22%20width%3D%22200%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2229%22%20value%3D%22%2BisClosed()%3Aboolean%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3B%22%20vertex%3D%221%22%20parent%3D%2222%22%3E%3CmxGeometry%20y%3D%22164%22%20width%3D%22200%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2230%22%20value%3D%22%2BgetRemotePort()%3Aint%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3B%22%20vertex%3D%221%22%20parent%3D%2222%22%3E%3CmxGeometry%20y%3D%22190%22%20width%3D%22200%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2231%22%20value%3D%22%2BgetRemoteHost()%3AString%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3B%22%20vertex%3D%221%22%20parent%3D%2222%22%3E%3CmxGeometry%20y%3D%22216%22%20width%3D%22200%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2232%22%20value%3D%22%2Bclose()%3Avoid%22%20style%3D%22text%3Balign%3Dleft%3BverticalAlign%3Dtop%3BspacingLeft%3D4%3BspacingRight%3D4%3Boverflow%3Dhidden%3Brotatable%3D0%3Bpoints%3D%5B%5B0%2C0.5%5D%2C%5B1%2C0.5%5D%5D%3BportConstraint%3Deastwest%3B%22%20vertex%3D%221%22%20parent%3D%2222%22%3E%3CmxGeometry%20y%3D%22242%22%20width%3D%22200%22%20height%3D%2226%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3C%2Froot%3E%3C%2FmxGraphModel%3E
%3C...
NetworkHandler+DEFAULT_ADDRESS:InetAddress{final}+DEFAULT_PORT:int{final}+createServer(port:int):NetworkServer<T>+creatServer():NetworkServer<T>+openConnection(address:InetAddress,port:int):NetworkConnection+openConnection(hostname:String,port:int):NetworkConnection+openConnection():NetworkConnectionNetworkServer-serverSocket:ServerSocket{final}-NetworkServer(port:int)-NetworkServer()+waitForConnection():NetworkConnection+isAvailable():boolean+isClosed():boolean+getHostPort():boolean+getHostAddress():String+close():voidNetworkConnection-socket:Socket{final}-NetworkConnection(socket:Socket)+send(data:T):void+receive():T+isAvailable():boolean+isClosed():boolean+getRemotePort():int+getRemoteHost():String+close():voidServer-NetworkHandler.NetworkServer<String> networkServer{final}-Map<String,ServerConnectionHandler> connections{final}+main(args:String[]):void+Server(serverPort:int)-start():void-terminate():voidServerConnectionHandler-connectionCounter:AtomicInteger{final}-connectionId:int{final}-connectionRegistry:Map<String,ServerConnectionHandler>{final}-userName:String-state:State+ServerConnenctionHandler(connection:NetworkConnection,registry:Map<String,ServerConnectionHandler)+getUserName():String+getState():State+startReceiving():void+stopReceiving():void-processData(data:String):void-caseConnect(sender:String):void-caseDisconnect():void-caseMessage(sender:String,reciever:String,type:String,payload:String):void<<abstract>> ConnectionHandler-DATA_TYPE_MESSAGE:String{final}-DATA_TYPE_CONNECT:String{final}-DATA_TYPE_CONFIRM:String{final}-DATA_TYPE_DISCONNECT:String{final}-DATA_TYPE_ERROR:String{final}+USER_NONE:String{final}+USER_ALL:String{final}-connection:NetworkConnection<String>{final}+ getDataTypeConnect(): String+ getDataTypeConnfirm(): String+ getDataTypeDisconnect(): String+ getDataTypeMessage(): String+ getDataTypeError(): String+ getConnection(): NetworkConnection<String># setConnection(): NetworkConnection<String>#processData(scanner:Scanner,sender:StringBuilder,reciever:StringBuilder,type:StringBuilder,payload:StringBuilder)#sendData(sender:String,reciever:String,type:String,payload:String):void<<enumeration>> STATENEWCONFIRM_CONNECTCONNECTEDCONFIRM_DISCONNECTDISCONNECTEDERRORMessage- type:MessageType- sender:String- reciever:String- text:String+ Message(type:String,sender:String,reciever:String,text:String)+ matchesFiler(filter:String):boolean+ getType():MessageType+ getSender():String+ getReciever():String+ getText():String<<enumeration>> MessageTypeERRORMessageInfo
Text is not SVG - cannot display
\ No newline at end of file From 61feb48a86993b794f7f816d4e51ff15e8be19bd Mon Sep 17 00:00:00 2001 From: schrom01 Date: Sat, 16 Apr 2022 20:02:04 +0200 Subject: [PATCH 22/22] fixed Problem in processData in ConnectionHandler.java and Codecleanup --- .../pm2/multichat/client/ClientConnectionHandler.java | 3 +-- .../ch/zhaw/pm2/multichat/protocol/ConnectionHandler.java | 8 ++++++-- 2 files changed, 7 insertions(+), 4 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 769ff94..2ed34e5 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 @@ -27,8 +27,7 @@ public class ClientConnectionHandler extends ConnectionHandler implements Runnab private final SimpleStringProperty userName; private final SimpleObjectProperty state; - private final ClientMessageList - messages; + private final ClientMessageList messages; private final SimpleStringProperty serverAddress; private final SimpleIntegerProperty serverPort; 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 604865f..6cc5450 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 @@ -97,7 +97,12 @@ public abstract class ConnectionHandler { } else if (scanner.hasNextLine()) { receiver.append(scanner.nextLine()); } else { - throw new ChatProtocolException("No Receiver found"); + throw new ChatProtocolException("No Sender found"); + } + if (scanner.hasNextLine()) { + receiver.append(scanner.nextLine()); + } else { + throw new ChatProtocolException("No Reciever found"); } if (scanner.hasNextLine()) { type.append(scanner.nextLine()); @@ -119,7 +124,6 @@ public abstract class ConnectionHandler { */ protected void sendData(String sender, String receiver, String type, String payload) { if (connection.isAvailable()) { - new StringBuilder(); String data = sender + "\n" + receiver + "\n" + type + "\n" +