From 98df1dc227ff21a1a44cac1d8d6be9a60ef30979 Mon Sep 17 00:00:00 2001 From: Mathis Ginkel Date: Sun, 15 Mar 2026 22:13:50 +0100 Subject: [PATCH] add: first implementation of login --- .../casono/client/network/ClientService.java | 62 +++++++++++++++++-- .../casono/client/network/sendRequest.java | 47 -------------- .../server/network/handlers/ChatHandler.java | 23 ++++++- .../server/network/handlers/UserHandler.java | 57 +++++++++++++++++ .../server/network/sessions/Session.java | 27 +++++--- .../client/network/ClientServiceTest.java | 7 ++- 6 files changed, 163 insertions(+), 60 deletions(-) delete mode 100644 src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/network/sendRequest.java create mode 100644 src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/handlers/UserHandler.java diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/network/ClientService.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/network/ClientService.java index 3ad46ca..386c146 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/network/ClientService.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/network/ClientService.java @@ -59,7 +59,28 @@ public class ClientService { } /** - * Sends a Request to get all the messages, the other clients sent, and that the client is not currently aware of. + * Sends a ping to the server + * Returns nothing, the method processMessage() already handles the cases "+OK" or "-ERROR" + */ + + public void ping() { + processMessage("PING"); + } + + /** + * Sends a login request to the server to create a new user on the server + * @param username + * @return - a new username, if the same username is already used by someone else + */ + + public String login(String username) { + String msg = "LOGIN USERNAME=" + username; + return processMessage(msg); + } + + /** + * Send a Request to get the number of Messages currently in the Queue for the client. + * Then proceeds, if needed, to get the messages by sending */ public List getMessages() { @@ -77,11 +98,26 @@ public class ClientService { return messages; } - public String sendMessage(Message message) { + /** + * Sends a Request to the Server containing all relevant information of the message the client wrote. + * @param message + * @return + */ + + public void sendMessage(Message message) { String request = "SEND_MESSAGE " + message.toArgsString(); - return processMessage(request); + processMessage(request); } + /** + * Sends the Requests to the server and waits for the response + * If the response is "+OK" it proceeds normal + * If the response "-ERROR" it throws a runtime exception + * @param message + * @return - The response as a string, if it has to be returned + * (+OK will not be returned) + */ + private String processMessage(String message) { AtomicReference response = new AtomicReference<>(); sendRequest(() -> { @@ -93,7 +129,7 @@ public class ClientService { System.out.println("Raw message '" + responseLine + "'"); if ("+OK".equals(responseLine)) { return; - } else if ("-FAIL".equals(responseLine)) { + } else if (("-ERROR").equals(responseLine)) { throw new RuntimeException(responseLine); } response.set(responseLine); @@ -105,6 +141,11 @@ public class ClientService { return response.get(); } + /** + * + * @param request + */ + private void sendRequest(Runnable request) { Future future = executor.submit(request); try { @@ -116,6 +157,12 @@ public class ClientService { } } + /** + * Returns a Runtime Exceptions thrown by the sendRequest method + * @param e - an Exception + * @return - a Runtime Exception + */ + private static RuntimeException getRuntimeException(Exception e) { Throwable reason = e.getCause(); RuntimeException re; @@ -127,6 +174,7 @@ public class ClientService { re = new RuntimeException(reason); return re; } + /** * Closes the Socket and shuts down the Threadpool associated with that Socket-Connection. */ @@ -142,6 +190,12 @@ public class ClientService { } + /** + * Method to write with the tcp transport to the server + * @param s - Message to be sent + * @throws IOException + */ + private void writeToTransport(String s) throws IOException { int id = this.idGenerator.incrementAndGet(); this.clienttcptransport.write(new RawPacket(id, s)); diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/network/sendRequest.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/network/sendRequest.java deleted file mode 100644 index 1c94f95..0000000 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/network/sendRequest.java +++ /dev/null @@ -1,47 +0,0 @@ -package ch.unibas.dmi.dbis.cs108.casono.client.network; - -import java.io.BufferedReader; -import java.util.concurrent.Callable; - -import ch.unibas.dmi.dbis.cs108.casono.client.network.ClientService; - -import java.util.ArrayList; - -import java.io.*; - -public class sendRequest implements Runnable { - - final private String request; - final private BufferedReader input; - final private BufferedWriter output; - - public sendRequest(String request, BufferedReader input, BufferedWriter output) { - this.request = request; - this.input = input; - this.output = output; - } - - @Override - public void run() { - try { - output.write(request + "\r\n"); - output.flush(); - //System.out.println("Writing following request: " + request); - } catch (Exception e) { - System.out.println(e); - } - - try { - while (true) { - String line; - line = input.readLine(); - if (line.toLowerCase().startsWith("+ok")) { - break; - } - ClientService.response.add(line); - } - } catch (Exception e) { - System.out.println(e); - } - } -} \ No newline at end of file diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/handlers/ChatHandler.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/handlers/ChatHandler.java index d409401..2d23852 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/handlers/ChatHandler.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/handlers/ChatHandler.java @@ -9,6 +9,11 @@ import java.util.ArrayDeque; public class ChatHandler { private final EventBus eventBus; private final ArrayDeque receivedMessages = new ArrayDeque<>(); + + /** + * Creates a ChatHandler for a Session + * @param eventBus + */ public ChatHandler(EventBus eventBus) { eventBus.subscribe(ChatMessageEvent.class, (event) -> { processMessageEvent(event.getMessage()); @@ -16,18 +21,34 @@ public class ChatHandler { this.eventBus = eventBus; } + /** + * Sends the Chat Message that the server received to the queue + * @param message + */ + private synchronized void processMessageEvent(Message message) { - // TODO: filter + // TODO: filter for Messages that were sent by the Client this.receivedMessages.offer(message); } + + /** + * Returns the current size of the queue (Used for Command GET_MESSAGE_COUNT) + */ public synchronized int getMessageCount() { return this.receivedMessages.size(); } + + /** + * Takes the next message out of the queue, to be sent to the Client + */ public synchronized Message getNextMessage() { return this.receivedMessages.poll(); } + /** + * Message is converted into a String and sent to the Client + */ public void sendMessage(String messageString) { Message msg = Message.toMessage(messageString); this.eventBus.publish(new ChatMessageEvent(msg)); diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/handlers/UserHandler.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/handlers/UserHandler.java new file mode 100644 index 0000000..6c171e0 --- /dev/null +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/handlers/UserHandler.java @@ -0,0 +1,57 @@ +package ch.unibas.dmi.dbis.cs108.casono.server.network.handlers; + +import jdk.jshell.spi.ExecutionControl; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class UserHandler { + + private final List usernames; + + public UserHandler() { + this.usernames = new ArrayList(); + } + + public Boolean checkUsername(String username) { + for (int i = 0; i < this.usernames.size(); i++) { + if (this.usernames.get(i).equals(username)) { + return false; + } + } + return true; + } + + public Pattern usrRex = Pattern.compile("USERNAME=(?\\w+)"); + + public String addUsername(String username) { + Matcher matcher = usrRex.matcher(username); + if (! matcher.find()) { + throw new RuntimeException("Cannot parse " + username); + } + String usr = matcher.group("user"); + if (!this.checkUsername(usr)) { + this.usernames.add(usr); + } else { + this.usernames.add(getAltUsername(usr)); + } + return String.format("USERNAME=%s", usr); + } + + public void removeUsername(String username) { + this.usernames.remove(username); + } + + public String getAltUsername(String username) { + int count = 0; + while (true) { + username = String.format("%s%d", username, ++count); + if (this.checkUsername(username)) { + return username; + } + } + } + +} diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/Session.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/Session.java index ffe5d6e..8e33248 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/Session.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/Session.java @@ -5,6 +5,7 @@ import java.io.IOException; import java.util.concurrent.atomic.AtomicInteger; import ch.unibas.dmi.dbis.cs108.casono.server.network.handlers.ChatHandler; +import ch.unibas.dmi.dbis.cs108.casono.server.network.handlers.UserHandler; import ch.unibas.dmi.dbis.cs108.casono.server.network.transport.RawPacket; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -18,6 +19,7 @@ import ch.unibas.dmi.dbis.cs108.casono.server.network.transport.TransportLayer; */ public class Session implements Runnable { private final ChatHandler chatHandler; + private final UserHandler userHandler; private SessionId id; private Thread thread; private TransportLayer transport; @@ -44,6 +46,7 @@ public class Session implements Runnable { this.logger.info("Created new session"); this.idGenerator = new AtomicInteger(); this.chatHandler = new ChatHandler(eventBus); + this.userHandler = new UserHandler(); } /** @@ -84,11 +87,11 @@ public class Session implements Runnable { String commandWord = commandAndArgs[0]; System.out.println("Session "+id.value()+" Received: " + commandAndArgs[0]); switch (commandWord) { - case "SEND_MESSAGE": - this.chatHandler.sendMessage(commandAndArgs[1]); - writeToTransport("+OK"); - writeToTransport("+OK"); - break; + case "SEND_MESSAGE": + this.chatHandler.sendMessage(commandAndArgs[1]); + writeToTransport("+OK"); + writeToTransport("+OK"); + break; case "GET_MESSAGE_COUNT": int count = chatHandler.getMessageCount(); writeToTransport(""+count); @@ -97,7 +100,7 @@ public class Session implements Runnable { case "GET_NEXT_MESSAGE": if (chatHandler.getMessageCount() == 0) { logger.warn("FAIL No more messages!"); - writeToTransport("-FAIL"); + writeToTransport("-ERROR"); } else { String msgString = chatHandler.getNextMessage().toArgsString(); logger.debug("Session "+id.value()+" Write next message "+msgString); @@ -105,9 +108,19 @@ public class Session implements Runnable { writeToTransport("+OK"); } break; + case "LOGIN": + String usr = userHandler.addUsername(commandAndArgs[1]); + writeToTransport(usr); + writeToTransport("+OK"); + + case "LOGOUT": + userHandler.removeUsername(commandAndArgs[1]); + writeToTransport("+OK"); + case "PING": + default: this.logger.warn("Unknown command: " + commandWord); - writeToTransport("-FAIL"); + writeToTransport("-ERROR"); } } catch (EOFException e) { logger.info("Client disconnected"); diff --git a/src/test/java/ch/unibas/dmi/dbis/cs108/casono/client/network/ClientServiceTest.java b/src/test/java/ch/unibas/dmi/dbis/cs108/casono/client/network/ClientServiceTest.java index 55ea07e..36c2bb9 100644 --- a/src/test/java/ch/unibas/dmi/dbis/cs108/casono/client/network/ClientServiceTest.java +++ b/src/test/java/ch/unibas/dmi/dbis/cs108/casono/client/network/ClientServiceTest.java @@ -33,15 +33,20 @@ public class ClientServiceTest { @Test public void testClientService() throws ExecutionException, InterruptedException { ClientService client1 = new ClientService("localhost", 5000); + /* ClientService client2 = new ClientService("localhost", 5000); Thread.sleep(2000); - String response = client1.sendMessage(new Message(Message.MessageType.GLOBAL, 0, "Mathis", null, "blahh")); + client1.sendMessage(new Message(Message.MessageType.GLOBAL, 0, "Mathis", null, "blahh")); Thread.sleep(5000); List received = client2.getMessages(); assertEquals(1, received.size()); Message s = received.get(0); assertEquals(Message.MessageType.GLOBAL, s.getMessageType()); + */ + + String username = client1.login("Mathis"); + assertEquals("USERNAME=Mathis", username); } @Test