Merge branch 'feat/14-commandrouter-and-commandhandler' into 'main'

Create CommandRouter and CommandHandler to execute request

Closes #17

See merge request cs108-fs26/Gruppe-13!50
This commit was merged in pull request #206.
This commit is contained in:
Lars Simon Winzer
2026-03-26 18:23:18 +01:00
7 changed files with 84 additions and 10 deletions
@@ -3,6 +3,7 @@ package ch.unibas.dmi.dbis.cs108.casono.server;
import ch.unibas.dmi.dbis.cs108.casono.server.domain.user.UserCleanupJob; import ch.unibas.dmi.dbis.cs108.casono.server.domain.user.UserCleanupJob;
import ch.unibas.dmi.dbis.cs108.casono.server.domain.user.UserRegistry; import ch.unibas.dmi.dbis.cs108.casono.server.domain.user.UserRegistry;
import ch.unibas.dmi.dbis.cs108.casono.server.network.NetworkManager; import ch.unibas.dmi.dbis.cs108.casono.server.network.NetworkManager;
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.CommandRouter;
import ch.unibas.dmi.dbis.cs108.casono.server.network.events.DisconnectEvent; 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.events.EventBus;
import ch.unibas.dmi.dbis.cs108.casono.server.network.parser.CommandParserDispatcher; import ch.unibas.dmi.dbis.cs108.casono.server.network.parser.CommandParserDispatcher;
@@ -28,7 +29,9 @@ public class ServerApp {
EventBus eventBus = new EventBus(); EventBus eventBus = new EventBus();
CommandParserDispatcher dispatcher = new CommandParserDispatcher(); CommandParserDispatcher dispatcher = new CommandParserDispatcher();
SessionManager sessionManager = new SessionManager(eventBus, dispatcher); CommandRouter router = new CommandRouter();
SessionManager sessionManager = new SessionManager(eventBus, dispatcher, router);
eventBus.subscribe(DisconnectEvent.class, event -> sessionManager.onDisconnect(event)); eventBus.subscribe(DisconnectEvent.class, event -> sessionManager.onDisconnect(event));
NetworkManager networkManager = new NetworkManager(port, sessionManager); NetworkManager networkManager = new NetworkManager(port, sessionManager);
@@ -0,0 +1,7 @@
package ch.unibas.dmi.dbis.cs108.casono.server.network.command;
import ch.unibas.dmi.dbis.cs108.casono.server.network.parser.Request;
public interface CommandHandler<T extends Request> {
void execute(T request);
}
@@ -0,0 +1,28 @@
package ch.unibas.dmi.dbis.cs108.casono.server.network.command;
import ch.unibas.dmi.dbis.cs108.casono.server.network.parser.Request;
import java.util.HashMap;
import java.util.Map;
public class CommandRouter {
private final Map<Class<? extends Request>, CommandHandler<?>> handlers = new HashMap<>();
public <T extends Request> void register(Class<T> request, CommandHandler<T> handler) {
handlers.put(request, handler);
}
// Safe, because during registration, it's ensured that the provided CommandHandler only
// receives requests it can handle.
@SuppressWarnings("unchecked")
public void execute(Request request) {
CommandHandler<Request> handler =
(CommandHandler<Request>) handlers.get(request.getClass());
if (handler == null) {
String requestName = request.getClass().toString();
throw new UnknownRequestException(
"Unable to execute request " + requestName + ". Type unknown", requestName);
}
handler.execute(request);
}
}
@@ -0,0 +1,14 @@
package ch.unibas.dmi.dbis.cs108.casono.server.network.command;
public class UnknownRequestException extends RuntimeException {
private final String requestName;
public UnknownRequestException(String message, String requestName) {
super(message);
this.requestName = requestName;
}
public String getRequestName() {
return requestName;
}
}
@@ -1,5 +1,6 @@
package ch.unibas.dmi.dbis.cs108.casono.server.network.sessions; package ch.unibas.dmi.dbis.cs108.casono.server.network.sessions;
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.CommandRouter;
import ch.unibas.dmi.dbis.cs108.casono.server.network.events.EventBus; import ch.unibas.dmi.dbis.cs108.casono.server.network.events.EventBus;
import ch.unibas.dmi.dbis.cs108.casono.server.network.parser.CommandParserDispatcher; import ch.unibas.dmi.dbis.cs108.casono.server.network.parser.CommandParserDispatcher;
import ch.unibas.dmi.dbis.cs108.casono.server.network.response.PrimitiveResponse; import ch.unibas.dmi.dbis.cs108.casono.server.network.response.PrimitiveResponse;
@@ -14,6 +15,7 @@ public class Session {
private final TransportLayer transport; private final TransportLayer transport;
private final BlockingQueue<PrimitiveResponse> responseQueue; private final BlockingQueue<PrimitiveResponse> responseQueue;
private final CommandParserDispatcher dispatcher; private final CommandParserDispatcher dispatcher;
private final CommandRouter router;
private static final int RESPOND_QUEUE_SIZE = 32; private static final int RESPOND_QUEUE_SIZE = 32;
/** /**
@@ -24,10 +26,14 @@ public class Session {
* @throws IOException if an I/O error occurs during initialization * @throws IOException if an I/O error occurs during initialization
*/ */
public Session( public Session(
TransportLayer transport, EventBus eventBus, CommandParserDispatcher dispatcher) { TransportLayer transport,
EventBus eventBus,
CommandParserDispatcher dispatcher,
CommandRouter router) {
this.id = new SessionId(); this.id = new SessionId();
this.transport = transport; this.transport = transport;
this.dispatcher = dispatcher; this.dispatcher = dispatcher;
this.router = router;
this.responseQueue = new ArrayBlockingQueue<>(RESPOND_QUEUE_SIZE); this.responseQueue = new ArrayBlockingQueue<>(RESPOND_QUEUE_SIZE);
} }
@@ -66,4 +72,8 @@ public class Session {
public CommandParserDispatcher getDispatcher() { public CommandParserDispatcher getDispatcher() {
return dispatcher; return dispatcher;
} }
public CommandRouter getRouter() {
return router;
}
} }
@@ -1,5 +1,6 @@
package ch.unibas.dmi.dbis.cs108.casono.server.network.sessions; package ch.unibas.dmi.dbis.cs108.casono.server.network.sessions;
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.CommandRouter;
import ch.unibas.dmi.dbis.cs108.casono.server.network.events.DisconnectEvent; 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.events.EventBus;
import ch.unibas.dmi.dbis.cs108.casono.server.network.parser.CommandParserDispatcher; import ch.unibas.dmi.dbis.cs108.casono.server.network.parser.CommandParserDispatcher;
@@ -16,13 +17,16 @@ public class SessionManager {
private final EventBus eventBus; private final EventBus eventBus;
private final Logger logger; private final Logger logger;
private final CommandParserDispatcher dispatcher; private final CommandParserDispatcher dispatcher;
private final CommandRouter router;
/** Constructs a new SessionManager. */ /** Constructs a new SessionManager. */
public SessionManager(EventBus eventBus, CommandParserDispatcher dispatcher) { public SessionManager(
EventBus eventBus, CommandParserDispatcher dispatcher, CommandRouter router) {
this.sessions = new ConcurrentHashMap<>(); this.sessions = new ConcurrentHashMap<>();
this.eventBus = eventBus; this.eventBus = eventBus;
this.logger = LogManager.getLogger(SessionManager.class); this.logger = LogManager.getLogger(SessionManager.class);
this.dispatcher = dispatcher; this.dispatcher = dispatcher;
this.router = router;
} }
/** /**
@@ -34,16 +38,18 @@ public class SessionManager {
* @return newly created session * @return newly created session
*/ */
public Session create(TransportLayer transport) { public Session create(TransportLayer transport) {
Session session = new Session(transport, eventBus, dispatcher); Session session = new Session(transport, eventBus, dispatcher, router);
SessionReader reader = new SessionReader(session, eventBus); SessionReader reader = new SessionReader(session, eventBus);
SessionWriter writer = new SessionWriter(session); SessionWriter writer = new SessionWriter(session);
Thread readerThread = Thread.ofVirtual() Thread readerThread =
.name("session-" + session.getId().value() + "-reader") Thread.ofVirtual()
.unstarted(reader); .name("session-" + session.getId().value() + "-reader")
Thread writerThread = Thread.ofVirtual() .unstarted(reader);
.name("session-" + session.getId().value() + "-writer") Thread writerThread =
.unstarted(writer); Thread.ofVirtual()
.name("session-" + session.getId().value() + "-writer")
.unstarted(writer);
sessions.put(session.getId(), new SessionHandle(session, readerThread, writerThread)); sessions.put(session.getId(), new SessionHandle(session, readerThread, writerThread));
readerThread.start(); readerThread.start();
@@ -1,5 +1,6 @@
package ch.unibas.dmi.dbis.cs108.casono.server.network.sessions; package ch.unibas.dmi.dbis.cs108.casono.server.network.sessions;
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.CommandRouter;
import ch.unibas.dmi.dbis.cs108.casono.server.network.events.DisconnectEvent; 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.events.EventBus;
import ch.unibas.dmi.dbis.cs108.casono.server.network.parser.CommandParserDispatcher; import ch.unibas.dmi.dbis.cs108.casono.server.network.parser.CommandParserDispatcher;
@@ -22,6 +23,7 @@ public class SessionReader implements Runnable {
private final TransportLayer transport; private final TransportLayer transport;
private final EventBus eventBus; private final EventBus eventBus;
private final CommandParserDispatcher dispatcher; private final CommandParserDispatcher dispatcher;
private final CommandRouter router;
private final Logger logger; private final Logger logger;
public SessionReader(Session session, EventBus eventBus) { public SessionReader(Session session, EventBus eventBus) {
@@ -29,6 +31,7 @@ public class SessionReader implements Runnable {
this.transport = session.getTransport(); this.transport = session.getTransport();
this.eventBus = eventBus; this.eventBus = eventBus;
this.dispatcher = session.getDispatcher(); this.dispatcher = session.getDispatcher();
this.router = session.getRouter();
this.logger = this.logger =
LogManager.getLogger( LogManager.getLogger(
SessionReader.class.toString() + "-" + session.getId().value()); SessionReader.class.toString() + "-" + session.getId().value());
@@ -50,8 +53,11 @@ public class SessionReader implements Runnable {
PrimitiveRequest primitiveRequest = PrimitiveRequest primitiveRequest =
new PrimitiveRequest( new PrimitiveRequest(
requestContext, rawRequest.command(), rawRequest.parameters()); requestContext, rawRequest.command(), rawRequest.parameters());
logger.debug("Converted to {}", primitiveRequest);
Request request = dispatcher.parse(primitiveRequest); Request request = dispatcher.parse(primitiveRequest);
router.execute(request);
} catch (EOFException e) { } catch (EOFException e) {
logger.info("Client disconnected"); logger.info("Client disconnected");
eventBus.publish(new DisconnectEvent(session.getId())); eventBus.publish(new DisconnectEvent(session.getId()));