Merge branch 'main' into feat/gameui

This commit is contained in:
Julian Kropff
2026-04-09 10:47:28 +02:00
25 changed files with 583 additions and 27 deletions
@@ -3,12 +3,19 @@ package ch.unibas.dmi.dbis.cs108.casono.server;
import ch.unibas.dmi.dbis.cs108.casono.server.app.commands.check_nick.CheckUsernameHandler;
import ch.unibas.dmi.dbis.cs108.casono.server.app.commands.check_nick.CheckUsernameParser;
import ch.unibas.dmi.dbis.cs108.casono.server.app.commands.check_nick.CheckUsernameRequest;
import ch.unibas.dmi.dbis.cs108.casono.server.app.commands.login.LoginHandler;
import ch.unibas.dmi.dbis.cs108.casono.server.app.commands.login.LoginParser;
import ch.unibas.dmi.dbis.cs108.casono.server.app.commands.login.LoginRequest;
import ch.unibas.dmi.dbis.cs108.casono.server.app.commands.logout.LogoutHandler;
import ch.unibas.dmi.dbis.cs108.casono.server.app.commands.logout.LogoutParser;
import ch.unibas.dmi.dbis.cs108.casono.server.app.commands.logout.LogoutRequest;
import ch.unibas.dmi.dbis.cs108.casono.server.app.commands.ping.PingHandler;
import ch.unibas.dmi.dbis.cs108.casono.server.app.commands.ping.PingParser;
import ch.unibas.dmi.dbis.cs108.casono.server.app.commands.ping.PingRequest;
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.network.NetworkManager;
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.execution.CommandHandlerExecutor;
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.execution.CommandRouter;
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.parsing.CommandParserDispatcher;
import ch.unibas.dmi.dbis.cs108.casono.server.network.events.DisconnectEvent;
@@ -40,11 +47,12 @@ public class ServerApp {
EventBus eventBus = new EventBus();
CommandParserDispatcher dispatcher = new CommandParserDispatcher();
CommandRouter router = new CommandRouter();
SessionManager sessionManager = new SessionManager(eventBus, dispatcher);
ResponseDispatcher responseDispatcher = new ResponseDispatcher(sessionManager);
CommandHandlerExecutor handlerExecutor = new CommandHandlerExecutor(responseDispatcher);
CommandRouter router = new CommandRouter(handlerExecutor);
SessionManager sessionManager = new SessionManager(eventBus, dispatcher, router);
eventBus.subscribe(DisconnectEvent.class, event -> sessionManager.onDisconnect(event));
NetworkManager networkManager = new NetworkManager(port, sessionManager);
UserRegistry userRegistry = new UserRegistry();
eventBus.subscribe(
@@ -65,10 +73,9 @@ public class ServerApp {
SESSION_DISCONNECT_JOB_PERIOD,
TimeUnit.SECONDS);
ResponseDispatcher responseDispatcher = new ResponseDispatcher(sessionManager);
registerCommands(dispatcher, router, responseDispatcher, userRegistry);
NetworkManager networkManager = new NetworkManager(port, sessionManager, router);
networkManager.start();
}
@@ -91,5 +98,13 @@ public class ServerApp {
commandRouter.register(
CheckUsernameRequest.class,
new CheckUsernameHandler(responseDispatcher, userRegistry));
parserDispatcher.register("LOGIN", new LoginParser());
commandRouter.register(
LoginRequest.class, new LoginHandler(responseDispatcher, userRegistry));
parserDispatcher.register("LOGOUT", new LogoutParser());
commandRouter.register(
LogoutRequest.class, new LogoutHandler(responseDispatcher, userRegistry));
}
}
@@ -0,0 +1,30 @@
package ch.unibas.dmi.dbis.cs108.casono.server.app.checks;
import ch.unibas.dmi.dbis.cs108.casono.server.domain.user.User;
import ch.unibas.dmi.dbis.cs108.casono.server.domain.user.UserRegistry;
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.execution.checks.HandlerCheck;
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.Request;
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.ErrorResponse;
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.Response;
import java.util.Optional;
public class UserLoggedInCheck implements HandlerCheck {
private final UserRegistry userRegistry;
public UserLoggedInCheck(UserRegistry userRegistry) {
this.userRegistry = userRegistry;
}
@Override
public Optional<Response> check(Request request) {
Optional<User> user = userRegistry.getBySessionId(request.getSessionId());
if (user.isPresent()) {
return Optional.empty();
}
return Optional.of(
new ErrorResponse(
request.getContext(),
"USER_NOT_LOGGED_IN",
"The execution of this command requires the user to be logged in"));
}
}
@@ -7,8 +7,7 @@ import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.dispatch
import java.util.Optional;
/** Handles {@link CheckUsernameRequest}s to check whether a username is available. */
public class CheckUsernameHandler implements CommandHandler<CheckUsernameRequest> {
private final ResponseDispatcher responseDispatcher;
public class CheckUsernameHandler extends CommandHandler<CheckUsernameRequest> {
private final UserRegistry userRegistry;
/**
@@ -18,7 +17,7 @@ public class CheckUsernameHandler implements CommandHandler<CheckUsernameRequest
* @param userRegistry the registry used to look up existing users
*/
public CheckUsernameHandler(ResponseDispatcher responseDispatcher, UserRegistry userRegistry) {
this.responseDispatcher = responseDispatcher;
super(responseDispatcher);
this.userRegistry = userRegistry;
}
@@ -0,0 +1,57 @@
package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.login;
import ch.unibas.dmi.dbis.cs108.casono.server.domain.user.User;
import ch.unibas.dmi.dbis.cs108.casono.server.domain.user.UserFactory;
import ch.unibas.dmi.dbis.cs108.casono.server.domain.user.UserId;
import ch.unibas.dmi.dbis.cs108.casono.server.domain.user.UserRegistry;
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.execution.CommandHandler;
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.ErrorResponse;
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.dispatcher.ResponseDispatcher;
import java.util.Optional;
/**
* Handles {@link LoginRequest}s to create a user for a session, if the session has not assigned one
* already
*/
public class LoginHandler extends CommandHandler<LoginRequest> {
private final UserRegistry userRegistry;
private final UserFactory userFactory;
/**
* Creates a new handler for checking for existing user and creating a new one
*
* @param responseDispatcher the dispatcher used to send the response
* @param userRegistry the registry used to look up existing users and create the new one
*/
public LoginHandler(ResponseDispatcher responseDispatcher, UserRegistry userRegistry) {
super(responseDispatcher);
this.userRegistry = userRegistry;
this.userFactory = new UserFactory(userRegistry);
}
/**
* Executes the login request
*
* <p>If no user is already assigned to the session, a new user is created and its name and
* {@link UserId} returned in the response. If the session has already a user assigned, a {@code
* ALREADY_LOGGED_IN} error is responded with.
*
* @param request the request to execute
*/
@Override
public void execute(LoginRequest request) {
Optional<User> existingUser = userRegistry.getBySessionId(request.getSessionId());
if (existingUser.isPresent()) {
responseDispatcher.dispatch(
new ErrorResponse(
request.getContext(),
"ALREADY_LOGGED_IN",
"This session is already associated with an active user."));
return;
}
User user = userFactory.create(request.getUsername(), request.getSessionId());
responseDispatcher.dispatch(
new LoginResponse(request.getContext(), user.getName(), user.getId()));
}
}
@@ -0,0 +1,21 @@
package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.login;
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.parsing.CommandParser;
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.PrimitiveRequest;
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.accessor.RequestParameterAccessor;
/** Parses a primitive request into a {@link LoginRequest}. */
public class LoginParser implements CommandParser<LoginRequest> {
/**
* Extracts the required {@code USERNAME} parameter from the incoming request.
*
* @param primitiveRequest the request to parse
* @return {@link LoginRequest} containing the username
*/
@Override
public LoginRequest parse(PrimitiveRequest primitiveRequest) {
RequestParameterAccessor accessor =
new RequestParameterAccessor(primitiveRequest.parameters());
return new LoginRequest(primitiveRequest.context(), accessor.require("USERNAME"));
}
}
@@ -0,0 +1,30 @@
package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.login;
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.Request;
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.RequestContext;
/** Request implementation used to create server-side user instance based on provided username */
public class LoginRequest extends Request {
private final String username;
/**
* Constructs a new LoginRequest with the given context and desired username
*
* @param context the {@link RequestContext} containing information for responding to the
* request
* @param username the desired username when creating the user
*/
public LoginRequest(RequestContext context, String username) {
super(context);
this.username = username;
}
/**
* Returns the desired username requested for login
*
* @return the desired username
*/
public String getUsername() {
return username;
}
}
@@ -0,0 +1,23 @@
package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.login;
import ch.unibas.dmi.dbis.cs108.casono.server.domain.user.UserId;
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.RequestContext;
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.SuccessResponse;
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.builder.ResponseBodyBuilder;
/** Response containing the assigned username and id of said user */
public class LoginResponse extends SuccessResponse {
/**
* @param context the {@link RequestContext} associated with the request
* @param assignedUsername the assigned username to this user
* @param id of the created user
*/
public LoginResponse(RequestContext context, String assignedUsername, UserId id) {
super(
context,
new ResponseBodyBuilder()
.param("USERNAME", assignedUsername)
.param("ID", id.value())
.build());
}
}
@@ -0,0 +1,44 @@
package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.logout;
import ch.unibas.dmi.dbis.cs108.casono.server.domain.user.UserRegistry;
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.execution.CommandHandler;
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.ErrorResponse;
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.OkResponse;
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.dispatcher.ResponseDispatcher;
/** Handles {@link LogoutRequest} to logout connected user. */
public class LogoutHandler extends CommandHandler<LogoutRequest> {
private final UserRegistry userRegistry;
/**
* Creates a new logout handler to logout user.
*
* @param responseDispatcher dispatcher used to send the logout result back to the client
* @param userRegistry registry responsible for tracking connected user sessions
*/
public LogoutHandler(ResponseDispatcher responseDispatcher, UserRegistry userRegistry) {
super(responseDispatcher);
this.userRegistry = userRegistry;
}
/**
* Executes the logout request for the given request.
*
* <p>The user is removed from the {@link UserRegistry}.
*
* @param request the request to execute
*/
@Override
public void execute(LogoutRequest request) {
boolean wasRemoved = userRegistry.removeBySessionId(request.getSessionId());
if (wasRemoved) {
responseDispatcher.dispatch(new OkResponse(request.getContext()));
} else {
responseDispatcher.dispatch(
new ErrorResponse(
request.getContext(),
"NO_USER_ASSOCIATED",
"No user is associated with your session. Did you login before?"));
}
}
}
@@ -0,0 +1,19 @@
package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.logout;
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.parsing.CommandParser;
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.PrimitiveRequest;
/** Parses a primitive request into a {@link LogoutRequest}. */
public class LogoutParser implements CommandParser<LogoutRequest> {
/**
* Parses a primitive request into a LogoutRequest.
*
* @param primitiveRequest the request to parse
* @return the created {@link LogoutRequest}
*/
@Override
public LogoutRequest parse(PrimitiveRequest primitiveRequest) {
return new LogoutRequest(primitiveRequest.context());
}
}
@@ -0,0 +1,18 @@
package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.logout;
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.Request;
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.RequestContext;
/** Request implementation used to logout a currently logged in user */
public class LogoutRequest extends Request {
/**
* Constructs a new LogoutRequest with the given context.
*
* @param context the {@link RequestContext} containing information for responding to the
* request
*/
public LogoutRequest(RequestContext context) {
super(context);
}
}
@@ -5,8 +5,7 @@ import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.OkRespon
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.dispatcher.ResponseDispatcher;
/** Handler for {@link PingRequest}. */
public class PingHandler implements CommandHandler<PingRequest> {
private final ResponseDispatcher responseDispatcher;
public class PingHandler extends CommandHandler<PingRequest> {
/**
* Create a new PingHandler to execute {@link PingRequest}s
@@ -14,7 +13,7 @@ public class PingHandler implements CommandHandler<PingRequest> {
* @param responseDispatcher dispatcher used to send responses back to clients
*/
public PingHandler(ResponseDispatcher responseDispatcher) {
this.responseDispatcher = responseDispatcher;
super(responseDispatcher);
}
/**
@@ -31,6 +31,66 @@ public class UserRegistry {
return Optional.of(user);
}
/**
* Removes a user from the registry by user ID.
*
* @param userId the ID of the user to remove
* @return true if a user was removed, false if no user with that ID exists
*/
public synchronized boolean removeByUserId(UserId userId) {
User user = byId.get(userId);
if (user == null) {
return false;
}
byName.remove(user.getName());
user.getSessionId().ifPresent(bySessionId::remove);
return true;
}
/**
* Removes a user from the registry by session ID.
*
* @param sessionId the session ID associated with the user to remove
* @return true if a user was removed, false if no user with that session exists
*/
public synchronized boolean removeBySessionId(SessionId sessionId) {
User user = bySessionId.get(sessionId);
if (user == null) {
return false;
}
remove(user);
return true;
}
/**
* Removes a user from the registry by username.
*
* @param username the username associated with the user to remove
* @return true if a user was removed, false if no user with that session exists
*/
public synchronized boolean removeByUsername(String username) {
User user = byName.get(username);
if (user == null) {
return false;
}
remove(user);
return true;
}
/**
* Removes a user from the internals of the registry.
*
* @param user the user to remove
*/
private synchronized void remove(User user) {
byId.remove(user.getId());
byName.remove(user.getName());
user.getSessionId().ifPresent(bySessionId::remove);
}
/**
* Removes the user with the given ID, but only if they are still disconnected. This prevents
* removing a user who has reconnected between the cleanup job's check and its removal call.
@@ -47,8 +107,7 @@ public class UserRegistry {
return false;
}
byId.remove(user.getId());
byName.remove(user.getName());
remove(user);
return true;
}
@@ -1,5 +1,6 @@
package ch.unibas.dmi.dbis.cs108.casono.server.network;
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.execution.CommandRouter;
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;
@@ -15,6 +16,7 @@ public class NetworkManager implements Runnable {
private Thread thread;
private Boolean running;
private SessionManager sessionManager;
private CommandRouter router;
/**
* Creates a new NetworkManager with the given port, session manager, and event bus.
@@ -22,12 +24,13 @@ public class NetworkManager implements Runnable {
* @param port the port to listen on
* @param sessionManager the session manager to use
*/
public NetworkManager(Integer port, SessionManager sessionManager) {
public NetworkManager(Integer port, SessionManager sessionManager, CommandRouter router) {
this.port = port;
this.logger = LogManager.getLogger(NetworkManager.class);
this.thread = new Thread(this, "networkManager");
this.running = true;
this.sessionManager = sessionManager;
this.router = router;
}
/** Starts the internal thread to accept new connections. */
@@ -45,7 +48,7 @@ public class NetworkManager implements Runnable {
logger.debug("Accepted connection from {}", clientSocket.getRemoteSocketAddress());
sessionManager.create(new TcpTransport(clientSocket));
sessionManager.create(new TcpTransport(clientSocket), router);
}
} catch (IOException e) {
@@ -1,7 +1,56 @@
package ch.unibas.dmi.dbis.cs108.casono.server.network.command.execution;
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.execution.checks.HandlerCheck;
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.Request;
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.dispatcher.ResponseDispatcher;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public interface CommandHandler<T extends Request> {
void execute(T request);
/**
* Abstract base class for handling requests of a specific type.
*
* @param <T> the type of request this handler processes, must extend {@link Request}
*/
public abstract class CommandHandler<T extends Request> {
private final List<HandlerCheck> checks = new ArrayList<>();
protected final ResponseDispatcher responseDispatcher;
/**
* Constructs a CommandHandler with the specified ResponseDispatcher.
*
* @param responseDispatcher the ResponseDispatcher instance used to send responses to clients.
*/
public CommandHandler(ResponseDispatcher responseDispatcher) {
this.responseDispatcher = responseDispatcher;
}
/**
* Adds a handler check to be performed before request execution.
*
* <p>The {@code execute} Method is only invoked, if all specified checks passed.
*
* @param check the {@link HandlerCheck} to add
*/
protected final void addCheck(HandlerCheck check) {
checks.add(check);
}
/**
* Returns an unmodifiable list of all registered handler checks.
*
* @return an unmodifiable list of {@link HandlerCheck} objects
*/
public final List<HandlerCheck> getChecks() {
return Collections.unmodifiableList(checks);
}
/**
* Executes the request.
*
* <p>Subclasses should override this method to implement their specific request handling logic.
*
* @param request the request to execute, of type T
*/
public void execute(T request) {}
}
@@ -0,0 +1,46 @@
package ch.unibas.dmi.dbis.cs108.casono.server.network.command.execution;
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.execution.checks.HandlerCheck;
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.Request;
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.Response;
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.dispatcher.ResponseDispatcher;
import java.util.Optional;
/**
* Invokes execution of the {@link CommandHandler} if all defined pre-execution checks pass.
*
* <p>If any check fails, the response returned by the check is dispatched immediately and execution
* is aborted. Otherwise, the handler will invoke execution normally.
*/
public class CommandHandlerExecutor {
private final ResponseDispatcher responseDispatcher;
/**
* Creates a new CommandHandlerExecutor to execute pre-execution checks and invoke the {@link
* CommandHandler} if all checks pass.
*
* @param responseDispatcher to dispatch the response with if an check fails
*/
public CommandHandlerExecutor(ResponseDispatcher responseDispatcher) {
this.responseDispatcher = responseDispatcher;
}
/**
* Executes a command handler if all defined pre-execution checks pass.
*
* @param handler the command handler containing checks and execution logic
* @param request the incoming request to validate and process
* @throws NullPointerException if handler or request is null
*/
public void execute(CommandHandler<Request> handler, Request request) {
for (HandlerCheck check : handler.getChecks()) {
Optional<Response> result = check.check(request);
if (result.isPresent()) {
responseDispatcher.dispatch(result.get());
return;
}
}
handler.execute(request);
}
}
@@ -4,13 +4,46 @@ import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.Request;
import java.util.HashMap;
import java.util.Map;
/** Routes incoming requests to their corresponding {@link CommandHandler} implementations. */
public class CommandRouter {
private final Map<Class<? extends Request>, CommandHandler<?>> handlers = new HashMap<>();
private final CommandHandlerExecutor handlerExecutor;
/**
* Creates a new CommandRouter with the specified handler executor.
*
* @param commandHandlerExecutor to delegate execution of checks and invocation of {@link
* CommandHandler} to
*/
public CommandRouter(CommandHandlerExecutor commandHandlerExecutor) {
this.handlerExecutor = commandHandlerExecutor;
}
/**
* Registers a {@link CommandHandler} for a specific request type.
*
* <p>This method establishes a type-safe mapping between a request class and its handler.
*
* <p>Only one handler can be registered per request type; subsequent registrations will
* overwrite previous ones.
*
* @param <T> the type of request handled by the provided handler
* @param request the request class to associate with the given command handler
* @param handler the command handler for the given request type
*/
public <T extends Request> void register(Class<T> request, CommandHandler<T> handler) {
handlers.put(request, handler);
}
/**
* Executes the appropriate handler for the given request.
*
* <p>Looks up the handler registered for the request's class and executes it through the
* configured {@link CommandHandlerExecutor}.
*
* @param request the request to be executed
* @throws UnknownRequestException if no handler is registered for the request type
*/
// Safe, because during registration, it's ensured that the provided CommandHandler only
// receives requests it can handle.
@SuppressWarnings("unchecked")
@@ -23,6 +56,7 @@ public class CommandRouter {
throw new UnknownRequestException(
"Unable to execute request " + requestName + ". Type unknown", requestName);
}
handler.execute(request);
handlerExecutor.execute(handler, request);
}
}
@@ -0,0 +1,23 @@
package ch.unibas.dmi.dbis.cs108.casono.server.network.command.execution.checks;
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.execution.CommandHandler;
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.Request;
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.ErrorResponse;
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.Response;
import java.util.Optional;
/**
* Functional interface used by pre-execution checks. Executed before the execute method of the
* {@link CommandHandler} is called
*/
@FunctionalInterface
public interface HandlerCheck {
/**
* Checks that the requirements outlined by this HandlerCheck are fulfilled.
*
* @param request to execute the check with
* @return Optional containing no value if the check was successful. And a {@link ErrorResponse}
* if the check failed, the response will be dispatched to the client.
*/
Optional<Response> check(Request request);
}
@@ -19,16 +19,13 @@ public class SessionManager {
private final EventBus eventBus;
private final Logger logger;
private final CommandParserDispatcher dispatcher;
private final CommandRouter router;
/** Constructs a new SessionManager. */
public SessionManager(
EventBus eventBus, CommandParserDispatcher dispatcher, CommandRouter router) {
public SessionManager(EventBus eventBus, CommandParserDispatcher dispatcher) {
this.sessions = new ConcurrentHashMap<>();
this.eventBus = eventBus;
this.logger = LogManager.getLogger(SessionManager.class);
this.dispatcher = dispatcher;
this.router = router;
}
/**
@@ -37,9 +34,10 @@ public class SessionManager {
* <p>Will create both worker threads and start them.
*
* @param transport to create session from
* @param router the command router used by the created session
* @return newly created session
*/
public Session create(TransportLayer transport) {
public Session create(TransportLayer transport, CommandRouter router) {
Session session = new Session(transport, eventBus, dispatcher, router);
SessionReader reader = new SessionReader(session, eventBus);
SessionWriter writer = new SessionWriter(session);