Merge remote-tracking branch 'origin/main'
This commit is contained in:
@@ -1,5 +1,11 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.server;
|
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.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.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;
|
||||||
@@ -7,6 +13,7 @@ import ch.unibas.dmi.dbis.cs108.casono.server.network.command.execution.CommandR
|
|||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.parsing.CommandParserDispatcher;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.parsing.CommandParserDispatcher;
|
||||||
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.protocol.response.dispatcher.ResponseDispatcher;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.SessionDisconnectJob;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.SessionDisconnectJob;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.SessionManager;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.SessionManager;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
@@ -58,6 +65,31 @@ public class ServerApp {
|
|||||||
SESSION_DISCONNECT_JOB_PERIOD,
|
SESSION_DISCONNECT_JOB_PERIOD,
|
||||||
TimeUnit.SECONDS);
|
TimeUnit.SECONDS);
|
||||||
|
|
||||||
|
ResponseDispatcher responseDispatcher = new ResponseDispatcher(sessionManager);
|
||||||
|
|
||||||
|
registerCommands(dispatcher, router, responseDispatcher, userRegistry);
|
||||||
|
|
||||||
networkManager.start();
|
networkManager.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers command parsers and handlers.
|
||||||
|
*
|
||||||
|
* @param parserDispatcher the dispatcher responsible for parsing incoming commands
|
||||||
|
* @param commandRouter the router that dispatches parsed commands to appropriate handlers
|
||||||
|
* @param responseDispatcher the dispatcher responsible for sending responses back to clients
|
||||||
|
*/
|
||||||
|
private static void registerCommands(
|
||||||
|
CommandParserDispatcher parserDispatcher,
|
||||||
|
CommandRouter commandRouter,
|
||||||
|
ResponseDispatcher responseDispatcher,
|
||||||
|
UserRegistry userRegistry) {
|
||||||
|
parserDispatcher.register("PING", new PingParser());
|
||||||
|
commandRouter.register(PingRequest.class, new PingHandler(responseDispatcher));
|
||||||
|
|
||||||
|
parserDispatcher.register("CHECK_USERNAME", new CheckUsernameParser());
|
||||||
|
commandRouter.register(
|
||||||
|
CheckUsernameRequest.class,
|
||||||
|
new CheckUsernameHandler(responseDispatcher, userRegistry));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+44
@@ -0,0 +1,44 @@
|
|||||||
|
package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.check_nick;
|
||||||
|
|
||||||
|
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.CommandHandler;
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.dispatcher.ResponseDispatcher;
|
||||||
|
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;
|
||||||
|
private final UserRegistry userRegistry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new handler for checking username availability.
|
||||||
|
*
|
||||||
|
* @param responseDispatcher the dispatcher used to send the response
|
||||||
|
* @param userRegistry the registry used to look up existing users
|
||||||
|
*/
|
||||||
|
public CheckUsernameHandler(ResponseDispatcher responseDispatcher, UserRegistry userRegistry) {
|
||||||
|
this.responseDispatcher = responseDispatcher;
|
||||||
|
this.userRegistry = userRegistry;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the username availability check for the given request.
|
||||||
|
*
|
||||||
|
* <p>If no user exists for the requested username, the username is reported as {@link
|
||||||
|
* UsernameAvailability#FREE}; otherwise, it is reported as {@link UsernameAvailability#TAKEN}.
|
||||||
|
*
|
||||||
|
* @param request the request to execute
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void execute(CheckUsernameRequest request) {
|
||||||
|
Optional<User> user = userRegistry.getByUsername(request.getUsername());
|
||||||
|
UsernameAvailability availability;
|
||||||
|
if (user.isEmpty()) {
|
||||||
|
availability = UsernameAvailability.FREE;
|
||||||
|
} else {
|
||||||
|
availability = UsernameAvailability.TAKEN;
|
||||||
|
}
|
||||||
|
responseDispatcher.dispatch(new CheckUsernameResponse(request.getContext(), availability));
|
||||||
|
}
|
||||||
|
}
|
||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.check_nick;
|
||||||
|
|
||||||
|
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 CheckUsernameRequest}. */
|
||||||
|
public class CheckUsernameParser implements CommandParser<CheckUsernameRequest> {
|
||||||
|
/**
|
||||||
|
* Extracts the required {@code USERNAME} parameter from the incoming request.
|
||||||
|
*
|
||||||
|
* @param primitiveRequest the request to parse
|
||||||
|
* @return {@link CheckUsernameRequest} containing the username
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public CheckUsernameRequest parse(PrimitiveRequest primitiveRequest) {
|
||||||
|
RequestParameterAccessor accessor =
|
||||||
|
new RequestParameterAccessor(primitiveRequest.parameters());
|
||||||
|
return new CheckUsernameRequest(primitiveRequest.context(), accessor.require("USERNAME"));
|
||||||
|
}
|
||||||
|
}
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.check_nick;
|
||||||
|
|
||||||
|
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 check whether a username is available or already taken */
|
||||||
|
public class CheckUsernameRequest extends Request {
|
||||||
|
private final String username;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new CheckUsernameRequest with the given context and username to check
|
||||||
|
*
|
||||||
|
* @param context the {@link RequestContext} containing information for responding to the
|
||||||
|
* request
|
||||||
|
* @param username the username to check for availability
|
||||||
|
*/
|
||||||
|
public CheckUsernameRequest(RequestContext context, String username) {
|
||||||
|
super(context);
|
||||||
|
this.username = username;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the provided username in the request
|
||||||
|
*
|
||||||
|
* @return username to check
|
||||||
|
*/
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
}
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.check_nick;
|
||||||
|
|
||||||
|
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 indicating the availability status of a username check. */
|
||||||
|
public class CheckUsernameResponse extends SuccessResponse {
|
||||||
|
/**
|
||||||
|
* Creates a new response to respond to the username availability check to
|
||||||
|
*
|
||||||
|
* @param context the {@link RequestContext} associated with the request
|
||||||
|
* @param availability the availability status of the requested username
|
||||||
|
*/
|
||||||
|
public CheckUsernameResponse(RequestContext context, UsernameAvailability availability) {
|
||||||
|
super(context, new ResponseBodyBuilder().param("STATUS", availability).build());
|
||||||
|
}
|
||||||
|
}
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.check_nick;
|
||||||
|
|
||||||
|
/** Represents the availability status of a username */
|
||||||
|
enum UsernameAvailability {
|
||||||
|
/** Username is available */
|
||||||
|
FREE,
|
||||||
|
|
||||||
|
/** Username is already in use */
|
||||||
|
TAKEN
|
||||||
|
}
|
||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.ping;
|
||||||
|
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.execution.CommandHandler;
|
||||||
|
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;
|
||||||
|
|
||||||
|
/** Handler for {@link PingRequest}. */
|
||||||
|
public class PingHandler implements CommandHandler<PingRequest> {
|
||||||
|
private final ResponseDispatcher responseDispatcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new PingHandler to execute {@link PingRequest}s
|
||||||
|
*
|
||||||
|
* @param responseDispatcher dispatcher used to send responses back to clients
|
||||||
|
*/
|
||||||
|
public PingHandler(ResponseDispatcher responseDispatcher) {
|
||||||
|
this.responseDispatcher = responseDispatcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the ping request.
|
||||||
|
*
|
||||||
|
* @param request the ping request to handle
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void execute(PingRequest request) {
|
||||||
|
responseDispatcher.dispatch(new OkResponse(request.getContext()));
|
||||||
|
}
|
||||||
|
}
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.ping;
|
||||||
|
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.parsing.CommandParser;
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.PrimitiveRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parser for the Ping command.
|
||||||
|
*
|
||||||
|
* <p>Converts a low-level {@link PrimitiveRequest} into a {@link PingRequest}.
|
||||||
|
*/
|
||||||
|
public class PingParser implements CommandParser<PingRequest> {
|
||||||
|
/**
|
||||||
|
* Parse the given primitive request into a {@link PingRequest}.
|
||||||
|
*
|
||||||
|
* @param primitiveRequest the raw request to parse
|
||||||
|
* @return {@link PingRequest}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public PingRequest parse(PrimitiveRequest primitiveRequest) {
|
||||||
|
return new PingRequest(primitiveRequest.context());
|
||||||
|
}
|
||||||
|
}
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.ping;
|
||||||
|
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.Request;
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.RequestContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a "PING" request sent by a client to check server availability and keep the connection
|
||||||
|
* alive.
|
||||||
|
*/
|
||||||
|
public class PingRequest extends Request {
|
||||||
|
/**
|
||||||
|
* Constructs a new PingRequest with the given context.
|
||||||
|
*
|
||||||
|
* @param context the request context associated with this request
|
||||||
|
*/
|
||||||
|
public PingRequest(RequestContext context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -86,15 +86,38 @@ public class UserRegistry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Looks up a user by their session ID.
|
* Looks up a user by their {@link SessionId}.
|
||||||
*
|
*
|
||||||
* @param sessionId the session ID to look up
|
* @param sessionId the SessionId to look up
|
||||||
* @return an Optional containing the user, or empty if no user is associated with this session
|
* @return an Optional containing the {@link User}, or empty if no user is associated with this
|
||||||
|
* SessionId
|
||||||
*/
|
*/
|
||||||
public Optional<User> findBySessionId(SessionId sessionId) {
|
public Optional<User> getBySessionId(SessionId sessionId) {
|
||||||
return Optional.ofNullable(bySessionId.get(sessionId));
|
return Optional.ofNullable(bySessionId.get(sessionId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Looks up a user by their {@link UserId}.
|
||||||
|
*
|
||||||
|
* @param userId the UserId to look up
|
||||||
|
* @return an Optional containing the {@link User}, or empty if no user is associated with this
|
||||||
|
* UserId
|
||||||
|
*/
|
||||||
|
public Optional<User> getByUserId(UserId userId) {
|
||||||
|
return Optional.ofNullable(byId.get(userId));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Looks up a user by their username.
|
||||||
|
*
|
||||||
|
* @param username the username to look up
|
||||||
|
* @return an Optional containing the {@link User}, or empty if no user is associated with this
|
||||||
|
* username
|
||||||
|
*/
|
||||||
|
public Optional<User> getByUsername(String username) {
|
||||||
|
return Optional.ofNullable(byName.get(username));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns all currently registered users.
|
* Returns all currently registered users.
|
||||||
*
|
*
|
||||||
|
|||||||
+9
@@ -12,6 +12,7 @@ import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.Primitive
|
|||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.RawRequest;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.RawRequest;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.Request;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.Request;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.RequestContext;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.RequestContext;
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.accessor.MissingParameterException;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.ErrorResponse;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.ErrorResponse;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.PrimitiveResponse;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.PrimitiveResponse;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.dispatcher.ResponseDispatchException;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.dispatcher.ResponseDispatchException;
|
||||||
@@ -94,6 +95,14 @@ public class SessionReader implements Runnable {
|
|||||||
"Unexpected ResponseDispatchException exception while dispatching request",
|
"Unexpected ResponseDispatchException exception while dispatching request",
|
||||||
e);
|
e);
|
||||||
|
|
||||||
|
} catch (MissingParameterException e) {
|
||||||
|
logger.error(
|
||||||
|
"Recieved request for command '{}' was missing the '{}' parameter",
|
||||||
|
rawRequest.command(),
|
||||||
|
e.getParameterKey());
|
||||||
|
sendErrorResponse(
|
||||||
|
new ErrorResponse(requestContext, "MISSING_PARAMETER", e.getMessage()));
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.error("Unexpected IO exception while reading from transport", e);
|
logger.error("Unexpected IO exception while reading from transport", e);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user