From 7b4fd4aeae1e6b80b9b65f235c5c3b423d693be3 Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer Date: Thu, 19 Mar 2026 17:41:45 +0100 Subject: [PATCH 01/22] Add: Make TransportLayer accessible through get-method --- .../cs108/casono/server/network/sessions/Session.java | 9 +++++++++ 1 file changed, 9 insertions(+) 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 97e3943..c76ecce 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 @@ -47,6 +47,15 @@ public class Session implements Runnable { return this.id; } + /** + * Returns the TransportLayer of this session + * + * @return the transport layer of the session + */ + public TransportLayer getTransport() { + return transport; + } + /** Starts the session thread. */ public void start() { thread.start(); From c10aaf15b538cf774879a4f3fa66858bc7725973 Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer Date: Thu, 19 Mar 2026 18:09:31 +0100 Subject: [PATCH 02/22] Add: Move read logic to SessionReader runnable --- .../server/network/sessions/Session.java | 59 +------------------ .../network/sessions/SessionReader.java | 53 +++++++++++++++++ 2 files changed, 56 insertions(+), 56 deletions(-) create mode 100644 src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionReader.java 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 c76ecce..67a24fd 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 @@ -1,24 +1,13 @@ package ch.unibas.dmi.dbis.cs108.casono.server.network.sessions; -import ch.unibas.dmi.dbis.cs108.casono.server.network.events.DisconnectEvent; import ch.unibas.dmi.dbis.cs108.casono.server.network.events.EventBus; -import ch.unibas.dmi.dbis.cs108.casono.server.network.parser.PrimitiveRequest; -import ch.unibas.dmi.dbis.cs108.casono.server.network.parser.ProtocolParser; -import ch.unibas.dmi.dbis.cs108.casono.server.network.transport.RawPacket; import ch.unibas.dmi.dbis.cs108.casono.server.network.transport.TransportLayer; -import java.io.EOFException; import java.io.IOException; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; /** Represents a client session in the network server. */ -public class Session implements Runnable { +public class Session { private SessionId id; - private Thread thread; private TransportLayer transport; - private Logger logger; - private Boolean running; - private EventBus eventBus; /** * Creates a new Session with the given transport and event bus. @@ -27,15 +16,9 @@ public class Session implements Runnable { * @param eventBus the event bus for publishing events * @throws IOException if an I/O error occurs during initialization */ - public Session(TransportLayer transport, EventBus eventBus) throws IOException { + public Session(TransportLayer transport, EventBus eventBus) { this.id = new SessionId(); - this.thread = new Thread(this, "session-" + this.id.value()); this.transport = transport; - this.running = true; - this.eventBus = eventBus; - - this.logger = LogManager.getLogger(Session.class.toString() + id.value()); - this.logger.info("Created new session"); } /** @@ -49,46 +32,10 @@ public class Session implements Runnable { /** * Returns the TransportLayer of this session - * + * * @return the transport layer of the session */ public TransportLayer getTransport() { return transport; } - - /** Starts the session thread. */ - public void start() { - thread.start(); - } - - /** - * Closes the session and its transport. - * - * @throws IOException if an I/O error occurs - */ - public void close() throws IOException { - transport.close(); - this.running = false; - } - - /** Runs the session loop, reading from the transport. */ - @Override - public void run() { - while (running) { - try { - RawPacket rawPacket = transport.read(); - logger.debug("Recieved: {}", rawPacket); - - PrimitiveRequest primitiveRequest = ProtocolParser.parse(rawPacket); - logger.debug("Parsed request to {}", primitiveRequest); - } catch (EOFException e) { - logger.info("Client disconnected"); - eventBus.publish(new DisconnectEvent(id)); - break; - } catch (IOException e) { - e.printStackTrace(); - break; - } - } - } } diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionReader.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionReader.java new file mode 100644 index 0000000..2f8e5fc --- /dev/null +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionReader.java @@ -0,0 +1,53 @@ +package ch.unibas.dmi.dbis.cs108.casono.server.network.sessions; + +import ch.unibas.dmi.dbis.cs108.casono.server.network.events.DisconnectEvent; +import ch.unibas.dmi.dbis.cs108.casono.server.network.events.EventBus; +import ch.unibas.dmi.dbis.cs108.casono.server.network.parser.PrimitiveRequest; +import ch.unibas.dmi.dbis.cs108.casono.server.network.parser.ProtocolParser; +import ch.unibas.dmi.dbis.cs108.casono.server.network.parser.ProtocolParserException; +import ch.unibas.dmi.dbis.cs108.casono.server.network.transport.RawPacket; +import ch.unibas.dmi.dbis.cs108.casono.server.network.transport.TransportLayer; +import ch.unibas.dmi.dbis.cs108.casono.server.tokenizer.TokenizerException; +import java.io.EOFException; +import java.io.IOException; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class SessionReader implements Runnable { + private final Session session; + private final TransportLayer transport; + private final EventBus eventBus; + private final Logger logger; + + public SessionReader(Session session, EventBus eventBus) { + this.session = session; + this.transport = session.getTransport(); + this.eventBus = eventBus; + this.logger = + LogManager.getLogger( + SessionReader.class.toString() + "-" + session.getId().value()); + } + + @Override + public void run() { + while (!Thread.currentThread().isInterrupted()) { + try { + RawPacket rawPacket = transport.read(); + logger.debug("Recieved: {}", rawPacket); + + PrimitiveRequest primitiveRequest = ProtocolParser.parse(rawPacket); + logger.debug("Parsed request to {}", primitiveRequest); + } catch (EOFException e) { + logger.info("Client disconnected"); + eventBus.publish(new DisconnectEvent(session.getId())); + break; + } catch (TokenizerException | ProtocolParserException e) { + logger.trace("Error occured while parsing request", e); + + // TODO: Send error response to client + } catch (IOException e) { + logger.trace("Unexpected exception while reading from transport", e); + } + } + } +} From 355b1cdbd4af258d5a9708389256299a02a5c142 Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer Date: Thu, 19 Mar 2026 18:32:57 +0100 Subject: [PATCH 03/22] Add: Queue to Session --- .../server/network/sessions/Session.java | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) 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 67a24fd..9837127 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 @@ -1,13 +1,17 @@ package ch.unibas.dmi.dbis.cs108.casono.server.network.sessions; import ch.unibas.dmi.dbis.cs108.casono.server.network.events.EventBus; +import ch.unibas.dmi.dbis.cs108.casono.server.network.response.PrimitiveResponse; import ch.unibas.dmi.dbis.cs108.casono.server.network.transport.TransportLayer; import java.io.IOException; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; /** Represents a client session in the network server. */ public class Session { - private SessionId id; - private TransportLayer transport; + private final SessionId id; + private final TransportLayer transport; + private final BlockingQueue responseQueue; /** * Creates a new Session with the given transport and event bus. @@ -19,6 +23,7 @@ public class Session { public Session(TransportLayer transport, EventBus eventBus) { this.id = new SessionId(); this.transport = transport; + this.responseQueue = new ArrayBlockingQueue<>(32); } /** @@ -38,4 +43,13 @@ public class Session { public TransportLayer getTransport() { return transport; } + + /** + * Returns the BlockingQueue of this session + * + * @return the queue containing outgoing responses + */ + public BlockingQueue getResponseQueue() { + return responseQueue; + } } From 8db7986131d392940f8b0157668859e72e092db6 Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer Date: Thu, 19 Mar 2026 18:42:10 +0100 Subject: [PATCH 04/22] Add: PrimitiveResponse as pendant to PrimitiveRequest --- .../casono/server/network/response/PrimitiveResponse.java | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/response/PrimitiveResponse.java diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/response/PrimitiveResponse.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/response/PrimitiveResponse.java new file mode 100644 index 0000000..38a7422 --- /dev/null +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/response/PrimitiveResponse.java @@ -0,0 +1,5 @@ +package ch.unibas.dmi.dbis.cs108.casono.server.network.response; + +import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.SessionId; + +public record PrimitiveResponse(SessionId sessionId, int requestId, String payload) {} From ec5ebe5d42cb19f409632e5a85991f80a5e3d2e3 Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer Date: Thu, 19 Mar 2026 18:42:34 +0100 Subject: [PATCH 05/22] Add: SessionWriter to use queue of session and write to transport --- .../network/sessions/SessionWriter.java | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionWriter.java diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionWriter.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionWriter.java new file mode 100644 index 0000000..d886ec8 --- /dev/null +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionWriter.java @@ -0,0 +1,38 @@ +package ch.unibas.dmi.dbis.cs108.casono.server.network.sessions; + +import ch.unibas.dmi.dbis.cs108.casono.server.network.response.PrimitiveResponse; +import ch.unibas.dmi.dbis.cs108.casono.server.network.transport.RawPacket; +import ch.unibas.dmi.dbis.cs108.casono.server.network.transport.TransportLayer; +import java.io.IOException; +import java.util.concurrent.BlockingQueue; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class SessionWriter implements Runnable { + private final TransportLayer transport; + private final BlockingQueue queue; + private final Logger logger; + + public SessionWriter(Session session) { + this.transport = session.getTransport(); + this.queue = session.getResponseQueue(); + this.logger = + LogManager.getLogger( + SessionReader.class.toString() + "-" + session.getId().value()); + } + + public void run() { + while (!Thread.currentThread().isInterrupted()) { + try { + PrimitiveResponse response = queue.take(); + RawPacket packet = new RawPacket(response.requestId(), response.payload()); + transport.write(packet); + } catch (IOException e) { + logger.trace("Unexpected exception while writing to transport", e); + } catch (InterruptedException e) { + logger.trace("Thread got interrupted", e); + break; + } + } + } +} From 247f16e26e1051d2ea44152ff4ec03c630516ffb Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer Date: Thu, 19 Mar 2026 19:01:33 +0100 Subject: [PATCH 06/22] Add: SessionHandle to store Session and two worker threads --- .../cs108/casono/server/network/sessions/SessionHandle.java | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionHandle.java diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionHandle.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionHandle.java new file mode 100644 index 0000000..a603a8a --- /dev/null +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionHandle.java @@ -0,0 +1,4 @@ +package ch.unibas.dmi.dbis.cs108.casono.server.network.sessions; + +/** The SessionHandle stores the session and the two worker threads associated with the session */ +record SessionHandle(Session session, Thread reader, Thread writer) {} From 72da952e5a9baf302f77022f35aac68793bba11a Mon Sep 17 00:00:00 2001 From: Jona Walpert Date: Fri, 20 Mar 2026 11:44:26 +0100 Subject: [PATCH 07/22] Add: Gradle properties to gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index c29286e..56624a0 100644 --- a/.gitignore +++ b/.gitignore @@ -121,4 +121,5 @@ $RECYCLE.BIN/ .idea ## bin -bin/ \ No newline at end of file +bin/ +gradle.properties \ No newline at end of file From 7713069496195efb1bf365d16ee4445c9eebf852 Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer Date: Fri, 20 Mar 2026 16:22:47 +0100 Subject: [PATCH 08/22] Add: Make SessionManager use SessionHandle --- .../network/sessions/SessionManager.java | 30 +++++++++++++++---- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionManager.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionManager.java index 6702543..17f06bb 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionManager.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionManager.java @@ -5,7 +5,7 @@ import java.util.concurrent.ConcurrentHashMap; /** Manages active sessions in the server. */ public class SessionManager { - private Map sessions; + private Map sessions; /** Constructs a new SessionManager. */ public SessionManager() { @@ -17,8 +17,8 @@ public class SessionManager { * * @param session the session to add */ - public void addSession(Session session) { - sessions.put(session.getId(), session); + public void addSession(SessionHandle handle) { + sessions.put(handle.session().getId(), handle); } /** @@ -28,7 +28,13 @@ public class SessionManager { * @return the removed session, or null if not found */ public Session removeSession(SessionId id) { - return sessions.remove(id); + SessionHandle handle = sessions.remove(id); + + if (handle == null) { + return null; + } + + return handle.session(); } /** @@ -38,7 +44,13 @@ public class SessionManager { * @return the removed session, or null if not found */ public Session removeSession(Session session) { - return sessions.remove(session.getId()); + SessionHandle handle = sessions.remove(session.getId()); + + if (handle == null) { + return null; + } + + return handle.session(); } /** @@ -48,6 +60,12 @@ public class SessionManager { * @return the session with the specified ID, or null if not found */ public Session getSessionById(SessionId id) { - return sessions.get(id); + SessionHandle handle = sessions.get(id); + + if (handle == null) { + return null; + } + + return handle.session(); } } From 11a7f0036c1699f0a2d0f079be95ed5b095ab783 Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer Date: Fri, 20 Mar 2026 16:28:33 +0100 Subject: [PATCH 09/22] Add: create and disconnect methods to SessionManager --- .../network/sessions/SessionManager.java | 61 +++++++++++++++++-- 1 file changed, 55 insertions(+), 6 deletions(-) diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionManager.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionManager.java index 17f06bb..6dfe245 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionManager.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionManager.java @@ -1,24 +1,73 @@ package ch.unibas.dmi.dbis.cs108.casono.server.network.sessions; +import java.io.IOException; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import ch.unibas.dmi.dbis.cs108.casono.server.network.events.EventBus; +import ch.unibas.dmi.dbis.cs108.casono.server.network.transport.TransportLayer; /** Manages active sessions in the server. */ public class SessionManager { private Map sessions; + private final EventBus eventBus; + private final Logger logger; /** Constructs a new SessionManager. */ - public SessionManager() { + public SessionManager(EventBus eventBus) { this.sessions = new ConcurrentHashMap<>(); + this.eventBus = eventBus; + this.logger = LogManager.getLogger(SessionManager.class); } /** - * Adds a session to the manager. - * - * @param session the session to add + * Create new Session from provided transport. + * + *

Will create both worker threads and start them. + * + * @param transport to create session from + * @return newly created session */ - public void addSession(SessionHandle handle) { - sessions.put(handle.session().getId(), handle); + public Session create(TransportLayer transport) { + Session session = new Session(transport, eventBus); + + SessionReader reader = new SessionReader(session, eventBus); + Thread readerThread = new Thread(reader, "session-" + session.getId().value() + "-reader"); + readerThread.start(); + + SessionWriter writer = new SessionWriter(session); + Thread writerThread = new Thread(writer, "session-" + session.getId().value() + "-writer"); + writerThread.start(); + + SessionHandle handle = new SessionHandle(session, readerThread, writerThread); + sessions.put(session.getId(), handle); + logger.debug("Created new session {}", session.getId().value()); + return session; + } + + /** + * Disconnect specified client + * + *

WARNING: Client will be uninformed about disconnect. Use with caution. + * + * @param id of the client to disconnect + */ + public void disconnect(SessionId id) { + SessionHandle handle = sessions.get(id); + if (handle == null) { + logger.warn("Requested to disconnect client with id {}. Failed as client is not found", id.value()); + return; + } + logger.debug("Disconnecting session {}", id.value()); + + handle.reader().interrupt(); + handle.writer().interrupt(); + try { + handle.session().getTransport().close(); + } catch (IOException e) { + logger.trace("Unexpected exception while closing transport", e); + } } /** From 761cf4a760e08cd171a2f5794b090dfcecef0be7 Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer Date: Fri, 20 Mar 2026 16:30:06 +0100 Subject: [PATCH 10/22] Add: DisconnectEvent handler function to SessionManager --- .../server/network/sessions/SessionManager.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionManager.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionManager.java index 6dfe245..013680b 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionManager.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionManager.java @@ -5,6 +5,8 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; + +import ch.unibas.dmi.dbis.cs108.casono.server.network.events.DisconnectEvent; import ch.unibas.dmi.dbis.cs108.casono.server.network.events.EventBus; import ch.unibas.dmi.dbis.cs108.casono.server.network.transport.TransportLayer; @@ -70,6 +72,17 @@ public class SessionManager { } } + /** + * Handler for the DisconnectEvent + * + * @param id of the session that disconnected + */ + public void onDisconnect(DisconnectEvent event) { + logger.debug("Recieved DisconnectEvent event for session {}", event.sessionId().value()); + + disconnect(event.sessionId()); + } + /** * Removes a session by its ID. * From 35da4d27700ab5b000118ccd111625106655b028 Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer Date: Fri, 20 Mar 2026 16:30:38 +0100 Subject: [PATCH 11/22] Add: Make NetworkManager use SessionManager to create session --- .../dmi/dbis/cs108/casono/server/network/NetworkManager.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/NetworkManager.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/NetworkManager.java index ad2e5b3..4833d61 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/NetworkManager.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/NetworkManager.java @@ -2,7 +2,6 @@ package ch.unibas.dmi.dbis.cs108.casono.server.network; import ch.unibas.dmi.dbis.cs108.casono.server.network.events.DisconnectEvent; import ch.unibas.dmi.dbis.cs108.casono.server.network.events.EventBus; -import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.Session; import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.SessionManager; import ch.unibas.dmi.dbis.cs108.casono.server.network.transport.TcpTransport; import java.io.IOException; @@ -61,9 +60,7 @@ public class NetworkManager implements Runnable { logger.debug("Accepted connection from {}", clientSocket.getRemoteSocketAddress()); - Session session = new Session(new TcpTransport(clientSocket), eventBus); - sessionManager.addSession(session); - session.start(); + sessionManager.create(new TcpTransport(clientSocket)); } } catch (IOException e) { From 699b9053eb1c203a99b4bcefec08ba93b39df8b2 Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer Date: Fri, 20 Mar 2026 16:31:26 +0100 Subject: [PATCH 12/22] Add: Instantiate SessionManager with provided eventManager and correct DisconnectEvent handler --- .../ch/unibas/dmi/dbis/cs108/casono/server/ServerApp.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/ServerApp.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/ServerApp.java index d44df55..ddb1138 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/ServerApp.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/ServerApp.java @@ -26,9 +26,9 @@ public class ServerApp { logger.info("Starting server at port {}", port); EventBus eventBus = new EventBus(); - SessionManager sessionManager = new SessionManager(); + SessionManager sessionManager = new SessionManager(eventBus); eventBus.subscribe( - DisconnectEvent.class, event -> sessionManager.removeSession(event.sessionId())); + DisconnectEvent.class, event -> sessionManager.onDisconnect(event)); NetworkManager networkManager = new NetworkManager(port, sessionManager, eventBus); UserRegistry userRegistry = new UserRegistry(); From 9e8ea37e882ef9487a85e7478491f75cfb2014ad Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer Date: Fri, 20 Mar 2026 16:37:46 +0100 Subject: [PATCH 13/22] Remove: DisconnectEvent handler from NetworkManager --- .../casono/server/network/NetworkManager.java | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/NetworkManager.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/NetworkManager.java index 4833d61..d9382a4 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/NetworkManager.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/NetworkManager.java @@ -1,7 +1,5 @@ package ch.unibas.dmi.dbis.cs108.casono.server.network; -import ch.unibas.dmi.dbis.cs108.casono.server.network.events.DisconnectEvent; -import ch.unibas.dmi.dbis.cs108.casono.server.network.events.EventBus; import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.SessionManager; import ch.unibas.dmi.dbis.cs108.casono.server.network.transport.TcpTransport; import java.io.IOException; @@ -17,23 +15,19 @@ public class NetworkManager implements Runnable { private Thread thread; private Boolean running; private SessionManager sessionManager; - private EventBus eventBus; /** * Creates a new NetworkManager with the given port, session manager, and event bus. * * @param port the port to listen on * @param sessionManager the session manager to use - * @param eventBus the event bus for events */ - public NetworkManager(Integer port, SessionManager sessionManager, EventBus eventBus) { + public NetworkManager(Integer port, SessionManager sessionManager) { this.port = port; this.logger = LogManager.getLogger(NetworkManager.class); this.thread = new Thread(this, "networkManager"); this.running = true; this.sessionManager = sessionManager; - this.eventBus = eventBus; - this.eventBus.subscribe(DisconnectEvent.class, event -> clientDisconnected(event)); } /** Starts the internal thread to accept new connections. */ @@ -42,15 +36,6 @@ public class NetworkManager implements Runnable { thread.start(); } - /** - * Handles client disconnection events. - * - * @param event the disconnect event - */ - public void clientDisconnected(DisconnectEvent event) { - logger.info("Session {} disconnected", event.sessionId().value()); - } - /** Runs the network manager loop, accepting connections. */ @Override public void run() { From 107870ff78128f8525a0c47bb17b9587d483fab7 Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer Date: Fri, 20 Mar 2026 16:46:26 +0100 Subject: [PATCH 14/22] Remove: eventBus argument from NetworkManager creation --- .../java/ch/unibas/dmi/dbis/cs108/casono/server/ServerApp.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/ServerApp.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/ServerApp.java index ddb1138..985b60a 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/ServerApp.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/ServerApp.java @@ -29,7 +29,7 @@ public class ServerApp { SessionManager sessionManager = new SessionManager(eventBus); eventBus.subscribe( DisconnectEvent.class, event -> sessionManager.onDisconnect(event)); - NetworkManager networkManager = new NetworkManager(port, sessionManager, eventBus); + NetworkManager networkManager = new NetworkManager(port, sessionManager); UserRegistry userRegistry = new UserRegistry(); eventBus.subscribe( From 2ed7ae4e5d0fb5b8490dc6c43fdf7f7f1909dbb3 Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer Date: Fri, 20 Mar 2026 16:47:01 +0100 Subject: [PATCH 15/22] Fix: Replace logging level 'trace' with 'error' and 'warn' --- .../cs108/casono/server/network/sessions/SessionManager.java | 2 +- .../cs108/casono/server/network/sessions/SessionReader.java | 4 ++-- .../cs108/casono/server/network/sessions/SessionWriter.java | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionManager.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionManager.java index 013680b..efe43ca 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionManager.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionManager.java @@ -68,7 +68,7 @@ public class SessionManager { try { handle.session().getTransport().close(); } catch (IOException e) { - logger.trace("Unexpected exception while closing transport", e); + logger.error("Unexpected exception while closing transport", e); } } diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionReader.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionReader.java index 2f8e5fc..45e83a4 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionReader.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionReader.java @@ -42,11 +42,11 @@ public class SessionReader implements Runnable { eventBus.publish(new DisconnectEvent(session.getId())); break; } catch (TokenizerException | ProtocolParserException e) { - logger.trace("Error occured while parsing request", e); + logger.error("Error occured while parsing request", e); // TODO: Send error response to client } catch (IOException e) { - logger.trace("Unexpected exception while reading from transport", e); + logger.error("Unexpected exception while reading from transport", e); } } } diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionWriter.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionWriter.java index d886ec8..714745c 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionWriter.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionWriter.java @@ -28,9 +28,9 @@ public class SessionWriter implements Runnable { RawPacket packet = new RawPacket(response.requestId(), response.payload()); transport.write(packet); } catch (IOException e) { - logger.trace("Unexpected exception while writing to transport", e); + logger.error("Unexpected exception while writing to transport", e); } catch (InterruptedException e) { - logger.trace("Thread got interrupted", e); + logger.warn("Thread got interrupted", e); break; } } From f79f7f0ae2aeae24335348d71dc24139c8cb04b9 Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer Date: Fri, 20 Mar 2026 16:56:03 +0100 Subject: [PATCH 16/22] Add: Useful information to log statement to ease debugging --- .../cs108/casono/server/network/sessions/SessionReader.java | 5 +++-- .../cs108/casono/server/network/sessions/SessionWriter.java | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionReader.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionReader.java index 45e83a4..7c2a6c7 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionReader.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionReader.java @@ -31,8 +31,9 @@ public class SessionReader implements Runnable { @Override public void run() { while (!Thread.currentThread().isInterrupted()) { + RawPacket rawPacket = null; try { - RawPacket rawPacket = transport.read(); + rawPacket = transport.read(); logger.debug("Recieved: {}", rawPacket); PrimitiveRequest primitiveRequest = ProtocolParser.parse(rawPacket); @@ -42,7 +43,7 @@ public class SessionReader implements Runnable { eventBus.publish(new DisconnectEvent(session.getId())); break; } catch (TokenizerException | ProtocolParserException e) { - logger.error("Error occured while parsing request", e); + logger.error("Error occured while parsing request. RawPacket: {}", rawPacket, e); // TODO: Send error response to client } catch (IOException e) { diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionWriter.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionWriter.java index 714745c..7d85fec 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionWriter.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionWriter.java @@ -23,12 +23,13 @@ public class SessionWriter implements Runnable { public void run() { while (!Thread.currentThread().isInterrupted()) { + RawPacket packet = null; try { PrimitiveResponse response = queue.take(); - RawPacket packet = new RawPacket(response.requestId(), response.payload()); + packet = new RawPacket(response.requestId(), response.payload()); transport.write(packet); } catch (IOException e) { - logger.error("Unexpected exception while writing to transport", e); + logger.error("Unexpected exception while writing to transport. RawPacket: {}", packet, e); } catch (InterruptedException e) { logger.warn("Thread got interrupted", e); break; From b19e923356fbb96dad3f985e334a3723269e5ae6 Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer Date: Fri, 20 Mar 2026 17:09:59 +0100 Subject: [PATCH 17/22] Style: Magic number, linelength and trailing whitespace --- .../dbis/cs108/casono/server/ServerApp.java | 3 +-- .../server/network/sessions/Session.java | 4 +++- .../network/sessions/SessionManager.java | 23 ++++++++++--------- .../network/sessions/SessionWriter.java | 5 +++- 4 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/ServerApp.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/ServerApp.java index 985b60a..126b341 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/ServerApp.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/ServerApp.java @@ -27,8 +27,7 @@ public class ServerApp { EventBus eventBus = new EventBus(); SessionManager sessionManager = new SessionManager(eventBus); - eventBus.subscribe( - DisconnectEvent.class, event -> sessionManager.onDisconnect(event)); + eventBus.subscribe(DisconnectEvent.class, event -> sessionManager.onDisconnect(event)); NetworkManager networkManager = new NetworkManager(port, sessionManager); UserRegistry userRegistry = new UserRegistry(); 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 9837127..84f4489 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 @@ -13,6 +13,8 @@ public class Session { private final TransportLayer transport; private final BlockingQueue responseQueue; + private final int RESPOND_QUEUE_SIZE = 32; + /** * Creates a new Session with the given transport and event bus. * @@ -23,7 +25,7 @@ public class Session { public Session(TransportLayer transport, EventBus eventBus) { this.id = new SessionId(); this.transport = transport; - this.responseQueue = new ArrayBlockingQueue<>(32); + this.responseQueue = new ArrayBlockingQueue<>(RESPOND_QUEUE_SIZE); } /** diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionManager.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionManager.java index efe43ca..c81c119 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionManager.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionManager.java @@ -1,15 +1,14 @@ package ch.unibas.dmi.dbis.cs108.casono.server.network.sessions; +import ch.unibas.dmi.dbis.cs108.casono.server.network.events.DisconnectEvent; +import ch.unibas.dmi.dbis.cs108.casono.server.network.events.EventBus; +import ch.unibas.dmi.dbis.cs108.casono.server.network.transport.TransportLayer; import java.io.IOException; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import ch.unibas.dmi.dbis.cs108.casono.server.network.events.DisconnectEvent; -import ch.unibas.dmi.dbis.cs108.casono.server.network.events.EventBus; -import ch.unibas.dmi.dbis.cs108.casono.server.network.transport.TransportLayer; - /** Manages active sessions in the server. */ public class SessionManager { private Map sessions; @@ -25,9 +24,9 @@ public class SessionManager { /** * Create new Session from provided transport. - * - *

Will create both worker threads and start them. - * + * + *

Will create both worker threads and start them. + * * @param transport to create session from * @return newly created session */ @@ -50,15 +49,17 @@ public class SessionManager { /** * Disconnect specified client - * - *

WARNING: Client will be uninformed about disconnect. Use with caution. - * + * + *

WARNING: Client will be uninformed about disconnect. Use with caution. + * * @param id of the client to disconnect */ public void disconnect(SessionId id) { SessionHandle handle = sessions.get(id); if (handle == null) { - logger.warn("Requested to disconnect client with id {}. Failed as client is not found", id.value()); + logger.warn( + "Requested to disconnect client with id {}. Failed as client is not found", + id.value()); return; } logger.debug("Disconnecting session {}", id.value()); diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionWriter.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionWriter.java index 7d85fec..e5c3e78 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionWriter.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionWriter.java @@ -29,7 +29,10 @@ public class SessionWriter implements Runnable { packet = new RawPacket(response.requestId(), response.payload()); transport.write(packet); } catch (IOException e) { - logger.error("Unexpected exception while writing to transport. RawPacket: {}", packet, e); + logger.error( + "Unexpected exception while writing to transport. RawPacket: {}", + packet, + e); } catch (InterruptedException e) { logger.warn("Thread got interrupted", e); break; From c6833b71e84d10227bdc375bd8f5e1367715bff8 Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer Date: Fri, 20 Mar 2026 17:12:55 +0100 Subject: [PATCH 18/22] Style: Add missing 'static' keyword to static variable --- .../dmi/dbis/cs108/casono/server/network/sessions/Session.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 84f4489..0a9be66 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 @@ -12,8 +12,7 @@ public class Session { private final SessionId id; private final TransportLayer transport; private final BlockingQueue responseQueue; - - private final int RESPOND_QUEUE_SIZE = 32; + private static final int RESPOND_QUEUE_SIZE = 32; /** * Creates a new Session with the given transport and event bus. From 0bb5a1e940c86a85bd5f330b5197362f9d2c450f Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer Date: Fri, 20 Mar 2026 17:21:36 +0100 Subject: [PATCH 19/22] Fix: Remove session requested to be disconnected from sessions map --- .../cs108/casono/server/network/sessions/SessionManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionManager.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionManager.java index c81c119..9ad41cd 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionManager.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionManager.java @@ -55,7 +55,7 @@ public class SessionManager { * @param id of the client to disconnect */ public void disconnect(SessionId id) { - SessionHandle handle = sessions.get(id); + SessionHandle handle = sessions.remove(id); if (handle == null) { logger.warn( "Requested to disconnect client with id {}. Failed as client is not found", From 94fcd882231f3100d4e04ead1986ed822b174e6d Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer Date: Fri, 20 Mar 2026 17:31:44 +0100 Subject: [PATCH 20/22] Fix: Avoid Race condition in create method of SessionManager --- .../network/sessions/SessionManager.java | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionManager.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionManager.java index 9ad41cd..b78439b 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionManager.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionManager.java @@ -32,18 +32,19 @@ public class SessionManager { */ public Session create(TransportLayer transport) { Session session = new Session(transport, eventBus); - SessionReader reader = new SessionReader(session, eventBus); - Thread readerThread = new Thread(reader, "session-" + session.getId().value() + "-reader"); - readerThread.start(); - SessionWriter writer = new SessionWriter(session); - Thread writerThread = new Thread(writer, "session-" + session.getId().value() + "-writer"); - writerThread.start(); - SessionHandle handle = new SessionHandle(session, readerThread, writerThread); - sessions.put(session.getId(), handle); - logger.debug("Created new session {}", session.getId().value()); + Thread readerThread = Thread.ofVirtual() + .name("session-" + session.getId().value() + "-reader") + .unstarted(reader); + Thread writerThread = Thread.ofVirtual() + .name("session-" + session.getId().value() + "-writer") + .unstarted(writer); + + sessions.put(session.getId(), new SessionHandle(session, readerThread, writerThread)); + readerThread.start(); + writerThread.start(); return session; } From e2e01addaa262c5b4ce33b0c16775e0d7780876b Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer Date: Fri, 20 Mar 2026 17:32:16 +0100 Subject: [PATCH 21/22] Fix: Leak of SessionManager by removing both removeSession method --- .../network/sessions/SessionManager.java | 32 ------------------- 1 file changed, 32 deletions(-) diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionManager.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionManager.java index b78439b..2ff6e38 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionManager.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionManager.java @@ -85,38 +85,6 @@ public class SessionManager { disconnect(event.sessionId()); } - /** - * Removes a session by its ID. - * - * @param id the ID of the session to remove - * @return the removed session, or null if not found - */ - public Session removeSession(SessionId id) { - SessionHandle handle = sessions.remove(id); - - if (handle == null) { - return null; - } - - return handle.session(); - } - - /** - * Removes the specified session. - * - * @param session the session to remove - * @return the removed session, or null if not found - */ - public Session removeSession(Session session) { - SessionHandle handle = sessions.remove(session.getId()); - - if (handle == null) { - return null; - } - - return handle.session(); - } - /** * Retrieves a session by its ID. * From f3703e4dbdcbb7c453273b594d070c5ccffe30c7 Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer Date: Mon, 23 Mar 2026 10:28:28 +0100 Subject: [PATCH 22/22] Build: Bumb google-java-format version from '1.25.2' to '1.35.0' --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index e126d6d..a7f3f9e 100644 --- a/build.gradle +++ b/build.gradle @@ -55,7 +55,7 @@ checkstyle { spotless { java { - googleJavaFormat('1.25.2').aosp() + googleJavaFormat('1.35.0').aosp() importOrder() removeUnusedImports() trimTrailingWhitespace()