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 f2e9563..5bde847 100644 --- a/client/src/main/java/ch/zhaw/pm2/multichat/client/ChatWindowController.java +++ b/client/src/main/java/ch/zhaw/pm2/multichat/client/ChatWindowController.java @@ -1,7 +1,8 @@ package ch.zhaw.pm2.multichat.client; -import ch.zhaw.pm2.multichat.client.ClientConnectionHandler.State; +import ch.zhaw.pm2.multichat.protocol.ConnectionHandler.State; import ch.zhaw.pm2.multichat.protocol.ChatProtocolException; +import ch.zhaw.pm2.multichat.protocol.ConnectionHandler; import ch.zhaw.pm2.multichat.protocol.NetworkHandler; import javafx.application.Platform; import javafx.beans.value.ChangeListener; @@ -15,10 +16,8 @@ import javafx.scene.layout.Pane; import javafx.stage.WindowEvent; import java.io.IOException; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import static ch.zhaw.pm2.multichat.client.ClientConnectionHandler.State.*; +import static ch.zhaw.pm2.multichat.protocol.ConnectionHandler.State.*; public class ChatWindowController { private ClientConnectionHandler connectionHandler; @@ -110,16 +109,20 @@ public class ChatWindowController { private void startConnectionHandler() throws IOException { String userName = userNameField.getText(); - String serverAddress = serverAddressField.getText(); - int serverPort = Integer.parseInt(serverPortField.getText()); - connectionHandler.initialize(serverAddress, serverPort, userName); - new Thread(connectionHandler).start(); + 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(); + //register Listener + startListener(); - // register window close handler - rootPane.getScene().getWindow().addEventHandler(WindowEvent.WINDOW_CLOSE_REQUEST, windowCloseHandler); + // register window close handler + rootPane.getScene().getWindow().addEventHandler(WindowEvent.WINDOW_CLOSE_REQUEST, windowCloseHandler); + } else { + addError("It is not allowed to have spaces in username!"); + } } public void stateChanged(State newState) { 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 b79195f..983c34c 100644 --- a/client/src/main/java/ch/zhaw/pm2/multichat/client/ClientConnectionHandler.java +++ b/client/src/main/java/ch/zhaw/pm2/multichat/client/ClientConnectionHandler.java @@ -1,6 +1,7 @@ package ch.zhaw.pm2.multichat.client; import ch.zhaw.pm2.multichat.protocol.ChatProtocolException; +import ch.zhaw.pm2.multichat.protocol.ConnectionHandler; import ch.zhaw.pm2.multichat.protocol.NetworkHandler; import javafx.beans.property.SimpleIntegerProperty; import javafx.beans.property.SimpleObjectProperty; @@ -12,23 +13,11 @@ import java.net.SocketException; import java.util.Scanner; import java.util.regex.Matcher; import java.util.regex.Pattern; +import static ch.zhaw.pm2.multichat.protocol.ConnectionHandler.State.*; -import static ch.zhaw.pm2.multichat.client.ClientConnectionHandler.State.*; +public class ClientConnectionHandler extends ConnectionHandler implements Runnable { -public class ClientConnectionHandler implements Runnable { - private NetworkHandler.NetworkConnection connection; - - // Data types used for the Chat Protocol - private static final String DATA_TYPE_CONNECT = "CONNECT"; - private static final String DATA_TYPE_CONFIRM = "CONFIRM"; - private static final String DATA_TYPE_DISCONNECT = "DISCONNECT"; - private static final String DATA_TYPE_MESSAGE = "MESSAGE"; - private static final String DATA_TYPE_ERROR = "ERROR"; - - public static final String USER_NONE = ""; - public static final String USER_ALL = "*"; - - private final Pattern messagePattern = Pattern.compile( "^(?:@(\\w*))?\\s*(.*)$" ); + private final Pattern messagePattern = Pattern.compile( "^(?:@(\\S*))?\\s*(.*)$" ); private SimpleStringProperty userName; private SimpleObjectProperty state; @@ -36,20 +25,17 @@ public class ClientConnectionHandler implements Runnable { private SimpleStringProperty serverAddress; private SimpleIntegerProperty serverPort; - enum State { - NEW, CONFIRM_CONNECT, CONNECTED, CONFIRM_DISCONNECT, DISCONNECTED; - } - public ClientConnectionHandler(ClientMessageList messages) { + super(); this.messages = messages; - state = new SimpleObjectProperty<>(NEW); + state = new SimpleObjectProperty<>(State.NEW); serverAddress = new SimpleStringProperty(); serverPort = new SimpleIntegerProperty(); } public void initialize(String serverAddress, int serverPort, String userName) throws IOException { state = new SimpleObjectProperty<>(NEW); - this.connection = NetworkHandler.openConnection(serverAddress, serverPort); + setConnection(NetworkHandler.openConnection(serverAddress, serverPort)); this.userName = new SimpleStringProperty((userName == null || userName.isBlank())? USER_NONE : userName); } @@ -65,19 +51,18 @@ public class ClientConnectionHandler implements Runnable { public void setState (State newState) { state.set(newState); - } public void run () { startReceiving(); } - public void startReceiving() { + private void startReceiving() { System.out.println("Starting Connection Handler"); try { System.out.println("Start receiving data..."); - while (connection.isAvailable()) { - String data = connection.receive(); + while (getConnection().isAvailable()) { + String data = getConnection().receive(); processData(data); } System.out.println("Stopped recieving data"); @@ -101,7 +86,7 @@ public class ClientConnectionHandler implements Runnable { System.out.println("Closing Connection Handler to Server"); try { System.out.println("Stop receiving data..."); - connection.close(); + getConnection().close(); System.out.println("Stopped receiving data."); } catch (IOException e) { System.err.println("Failed to close connection." + e.getMessage()); @@ -136,13 +121,13 @@ public class ClientConnectionHandler implements Runnable { payload = scanner.nextLine(); } // dispatch operation based on type parameter - if (type.equals(DATA_TYPE_CONNECT)) { + if (type.equals(getDataTypeConnect())) { System.err.println("Illegal connect request from server"); - } else if (type.equals(DATA_TYPE_CONFIRM)) { + } else if (type.equals(getDataTypeConfirm())) { if (state.get() == CONFIRM_CONNECT) { this.userName.set(reciever); - this.serverPort.set(connection.getRemotePort()); - this.serverAddress.set(connection.getRemoteHost()); + this.serverPort.set(getConnection().getRemotePort()); + this.serverAddress.set(getConnection().getRemoteHost()); messages.addMessage(new Message(Message.MessageType.INFO,sender,reciever,payload)); System.out.println("CONFIRM: " + payload); this.setState(CONNECTED); @@ -153,7 +138,7 @@ public class ClientConnectionHandler implements Runnable { } else { System.err.println("Got unexpected confirm message: " + payload); } - } else if (type.equals(DATA_TYPE_DISCONNECT)) { + } else if (type.equals(getDataTypeDisconnect())) { if (state.get() == DISCONNECTED) { System.out.println("DISCONNECT: Already in disconnected: " + payload); return; @@ -161,14 +146,14 @@ public class ClientConnectionHandler implements Runnable { messages.addMessage(new Message(Message.MessageType.INFO,sender,reciever,payload)); System.out.println("DISCONNECT: " + payload); this.setState(DISCONNECTED); - } else if (type.equals(DATA_TYPE_MESSAGE)) { + } else if (type.equals(getDataTypeMessage())) { if (state.get() != CONNECTED) { System.out.println("MESSAGE: Illegal state " + state + " for message: " + payload); return; } messages.addMessage(new Message(Message.MessageType.MESSAGE,sender,reciever,payload)); System.out.println("MESSAGE: From " + sender + " to " + reciever + ": "+ payload); - } else if (type.equals(DATA_TYPE_ERROR)) { + } else if (type.equals(getDataTypeError())) { messages.addMessage(new Message(Message.MessageType.ERROR,sender,reciever,payload)); System.out.println("ERROR: " + payload); } else { @@ -176,12 +161,12 @@ public class ClientConnectionHandler implements Runnable { } } catch (ChatProtocolException e) { System.err.println("Error while processing data: " + e.getMessage()); - sendData(USER_NONE, userName.get(), DATA_TYPE_ERROR, e.getMessage()); + sendData(USER_NONE, userName.get(), getDataTypeError(), e.getMessage()); } } - public void sendData(String sender, String receiver, String type, String payload) { - if (connection.isAvailable()) { + private void sendData(String sender, String receiver, String type, String payload) { + if (getConnection().isAvailable()) { new StringBuilder(); String data = new StringBuilder() .append(sender+"\n") @@ -190,7 +175,7 @@ public class ClientConnectionHandler implements Runnable { .append(payload+"\n") .toString(); try { - connection.send(data); + getConnection().send(data); } catch (SocketException e) { System.err.println("Connection closed: " + e.getMessage()); } catch (EOFException e) { @@ -203,13 +188,13 @@ public class ClientConnectionHandler implements Runnable { public void connect() throws ChatProtocolException { if (state.get() != NEW) throw new ChatProtocolException("Illegal state for connect: " + state); - this.sendData(userName.get(), USER_NONE, DATA_TYPE_CONNECT,null); + this.sendData(userName.get(), USER_NONE, getDataTypeConnect(),null); this.setState(CONFIRM_CONNECT); } public void disconnect() throws ChatProtocolException { if (state.get() != NEW && state.get() != CONNECTED) throw new ChatProtocolException("Illegal state for disconnect: " + state); - this.sendData(userName.get(), USER_NONE, DATA_TYPE_DISCONNECT,null); + this.sendData(userName.get(), USER_NONE, getDataTypeDisconnect(),null); this.setState(CONFIRM_DISCONNECT); } @@ -224,7 +209,7 @@ public class ClientConnectionHandler implements Runnable { return false; } if (receiver == null || receiver.isBlank()) receiver = ClientConnectionHandler.USER_ALL; - this.sendData(userName.get(), receiver, DATA_TYPE_MESSAGE,message); + this.sendData(userName.get(), receiver, getDataTypeMessage(),message); return true; } else { return false; diff --git a/protocol/src/main/java/ch/zhaw/pm2/multichat/protocol/ConnectionHandler.java b/protocol/src/main/java/ch/zhaw/pm2/multichat/protocol/ConnectionHandler.java new file mode 100644 index 0000000..a0ddd57 --- /dev/null +++ b/protocol/src/main/java/ch/zhaw/pm2/multichat/protocol/ConnectionHandler.java @@ -0,0 +1,51 @@ +package ch.zhaw.pm2.multichat.protocol; + +public abstract class ConnectionHandler { + private NetworkHandler.NetworkConnection connection; + + // Data types used for the Chat Protocol + private static final String DATA_TYPE_CONNECT = "CONNECT"; + private static final String DATA_TYPE_CONFIRM = "CONFIRM"; + private static final String DATA_TYPE_DISCONNECT = "DISCONNECT"; + private static final String DATA_TYPE_MESSAGE = "MESSAGE"; + private static final String DATA_TYPE_ERROR = "ERROR"; + + public static final String USER_NONE = ""; + public static final String USER_ALL = "*"; + + public enum State { + NEW, CONFIRM_CONNECT, CONNECTED, CONFIRM_DISCONNECT, DISCONNECTED; + } + + public ConnectionHandler(NetworkHandler.NetworkConnection connection) { + this.connection = connection; + } + + public static String getDataTypeConnect() { + return DATA_TYPE_CONNECT; + } + + public static String getDataTypeConfirm() { + return DATA_TYPE_CONFIRM; + } + + public static String getDataTypeDisconnect() { + return DATA_TYPE_DISCONNECT; + } + + public static String getDataTypeMessage() { + return DATA_TYPE_MESSAGE; + } + + public static String getDataTypeError() { + return DATA_TYPE_ERROR; + } + + public NetworkHandler.NetworkConnection getConnection() { + return connection; + } + + protected void setConnection() { + this.connection = connection; + } +} diff --git a/server/src/main/java/ch/zhaw/pm2/multichat/server/ServerConnectionHandler.java b/server/src/main/java/ch/zhaw/pm2/multichat/server/ServerConnectionHandler.java index fa6686e..4e3db14 100644 --- a/server/src/main/java/ch/zhaw/pm2/multichat/server/ServerConnectionHandler.java +++ b/server/src/main/java/ch/zhaw/pm2/multichat/server/ServerConnectionHandler.java @@ -1,6 +1,7 @@ package ch.zhaw.pm2.multichat.server; import ch.zhaw.pm2.multichat.protocol.ChatProtocolException; +import ch.zhaw.pm2.multichat.protocol.ConnectionHandler; import ch.zhaw.pm2.multichat.protocol.NetworkHandler; import java.io.EOFException; @@ -16,22 +17,11 @@ import java.util.concurrent.locks.ReentrantLock; import static ch.zhaw.pm2.multichat.server.ServerConnectionHandler.State.*; -public class ServerConnectionHandler implements Runnable{ +public class ServerConnectionHandler extends ConnectionHandler implements Runnable{ private static final AtomicInteger connectionCounter = new AtomicInteger(0); private final int connectionId = connectionCounter.incrementAndGet(); - private final NetworkHandler.NetworkConnection connection; private final Map connectionRegistry; - // Data types used for the Chat Protocol - private static final String DATA_TYPE_CONNECT = "CONNECT"; - private static final String DATA_TYPE_CONFIRM = "CONFIRM"; - private static final String DATA_TYPE_DISCONNECT = "DISCONNECT"; - private static final String DATA_TYPE_MESSAGE = "MESSAGE"; - private static final String DATA_TYPE_ERROR = "ERROR"; - - private static final String USER_NONE = ""; - private static final String USER_ALL = "*"; - private ReentrantLock mutex; private Condition nameComplete; @@ -50,9 +40,9 @@ public class ServerConnectionHandler implements Runnable{ public ServerConnectionHandler(NetworkHandler.NetworkConnection connection, Map registry, ReentrantLock mutex, Condition nameComplete) { + super(connection); Objects.requireNonNull(connection, "Connection must not be null"); Objects.requireNonNull(registry, "Registry must not be null"); - this.connection = connection; this.connectionRegistry = registry; this.mutex = mutex; this.nameComplete = nameComplete; @@ -62,12 +52,12 @@ public class ServerConnectionHandler implements Runnable{ return this.userName; } - public void startReceiving() { + private void startReceiving() { System.out.println("Starting Connection Handler for new User"); try { System.out.println("Start receiving data..."); - while (connection.isAvailable()) { - String data = connection.receive(); + while (getConnection().isAvailable()) { + String data = getConnection().receive(); processData(data); } System.out.println("Stopped recieving data"); @@ -88,11 +78,11 @@ public class ServerConnectionHandler implements Runnable{ } - public void stopReceiving() { + private void stopReceiving() { System.out.println("Closing Connection Handler for " + userName); try { System.out.println("Stop receiving data..."); - connection.close(); + getConnection().close(); System.out.println("Stopped receiving data."); } catch (IOException e) { System.err.println("Failed to close connection." + e); @@ -128,7 +118,7 @@ public class ServerConnectionHandler implements Runnable{ } // dispatch operation based on type parameter - if (type.equals(DATA_TYPE_CONNECT)) { + if (type.equals(getDataTypeConnect())) { if (this.state != NEW) throw new ChatProtocolException("Illegal state for connect request: " + state); if (sender == null || sender.isBlank()) sender = this.userName; if (connectionRegistry.containsKey(sender)) @@ -142,20 +132,20 @@ public class ServerConnectionHandler implements Runnable{ mutex.unlock(); } connectionRegistry.put(userName, this); - sendData(USER_NONE, userName, DATA_TYPE_CONFIRM, "Registration successfull for " + userName); + sendData(USER_NONE, userName, getDataTypeConfirm(), "Registration successfull for " + userName); this.state = CONNECTED; - } else if (type.equals(DATA_TYPE_CONFIRM)) { + } else if (type.equals(getDataTypeConfirm())) { System.out.println("Not expecting to receive a CONFIRM request from client"); - } else if (type.equals(DATA_TYPE_DISCONNECT)) { + } else if (type.equals(getDataTypeDisconnect())) { if (state == DISCONNECTED) throw new ChatProtocolException("Illegal state for disconnect request: " + state); if (state == CONNECTED) { connectionRegistry.remove(this.userName); } - sendData(USER_NONE, userName, DATA_TYPE_CONFIRM, "Confirm disconnect of " + userName); + sendData(USER_NONE, userName, getDataTypeConfirm(), "Confirm disconnect of " + userName); this.state = DISCONNECTED; this.stopReceiving(); - } else if (type.equals(DATA_TYPE_MESSAGE)) { + } else if (type.equals(getDataTypeMessage())) { if (state != CONNECTED) throw new ChatProtocolException("Illegal state for message request: " + state); if (USER_ALL.equals(reciever)) { for (ServerConnectionHandler handler : connectionRegistry.values()) { @@ -169,10 +159,10 @@ public class ServerConnectionHandler implements Runnable{ sendData(sender, reciever, type, payload); //send message to sender if it's a direct message and sender is not receiver. } } else { - this.sendData(USER_NONE, userName, DATA_TYPE_ERROR, "Unknown User: " + reciever); + this.sendData(USER_NONE, userName, getDataTypeError(), "Unknown User: " + reciever); } } - } else if (type.equals(DATA_TYPE_ERROR)) { + } else if (type.equals(getDataTypeError())) { System.err.println("Received error from client (" + sender + "): " + payload); } else { System.err.println("Unknown data type received: " + type); @@ -180,12 +170,12 @@ public class ServerConnectionHandler implements Runnable{ } } catch(ChatProtocolException e) { System.out.println("Error while processing data" + e.getMessage()); - sendData(USER_NONE, userName, DATA_TYPE_ERROR, e.getMessage()); + sendData(USER_NONE, userName, getDataTypeError(), e.getMessage()); } } - public void sendData(String sender, String receiver, String type, String payload) { - if (connection.isAvailable()) { + private void sendData(String sender, String receiver, String type, String payload) { + if (getConnection().isAvailable()) { new StringBuilder(); String data = new StringBuilder() .append(sender+"\n") @@ -194,7 +184,7 @@ public class ServerConnectionHandler implements Runnable{ .append(payload+"\n") .toString(); try { - connection.send(data); + getConnection().send(data); } catch (SocketException e) { System.out.println("Connection closed: " + e.getMessage()); } catch (EOFException e) {