forked from Cockadoodle/KOProject
257 lines
9.5 KiB
Java
257 lines
9.5 KiB
Java
|
package com.collibra.server;
|
|||
|
|
|||
|
import java.io.PrintWriter;
|
|||
|
|
|||
|
/**
|
|||
|
* A simple protocol for exchanging messages between a server and clients.
|
|||
|
*
|
|||
|
* @version 1.0
|
|||
|
* @author Yordan Kirov
|
|||
|
* @since 21/11/2018
|
|||
|
*
|
|||
|
*/
|
|||
|
class SimpleProtocol {
|
|||
|
/**
|
|||
|
* Sends a first message to every client that connects to the server.
|
|||
|
* @param out the PrintWriter with which the server sends messages to the client.
|
|||
|
* @param sessionId identification of the session with the current client.
|
|||
|
*/
|
|||
|
public static void sendFirstMessage(PrintWriter out, String sessionId) {
|
|||
|
out.println("HI, I'M " + sessionId);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* Extracts and validates the name of the connected client from its first message.
|
|||
|
*
|
|||
|
* The server expects a message starting with "HI, I'M " and followed by the name of the client.
|
|||
|
* If the client doesn't fulfil the server expectations, by sending the wrong command or sending
|
|||
|
* a clientName that is non alphanumeric plus the character "-", the server is awaiting for the client to give a
|
|||
|
* proper name and doesn't accept other commands before that.
|
|||
|
*
|
|||
|
* @param out the PrintWriter with which the sever sends messages to the client.
|
|||
|
* @param message received from a client.
|
|||
|
*
|
|||
|
* @return clientName - the name of the client if it is valid, otherwise returns null;
|
|||
|
*/
|
|||
|
public static String retrieveClientName(PrintWriter out, String message) {
|
|||
|
String clientName;
|
|||
|
if (message.contains("HI, I'M ")) {
|
|||
|
String unvalidatedClientName = message.replace("HI, I'M ", "");
|
|||
|
|
|||
|
if (isValidName(unvalidatedClientName)) {
|
|||
|
clientName = unvalidatedClientName;
|
|||
|
System.out.println("Connected client: " + clientName);
|
|||
|
|
|||
|
out.println("HI " + clientName);
|
|||
|
return clientName;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
out.println(unsupportedCommand());
|
|||
|
return null;
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* Sends a message to the client before disconnecting.
|
|||
|
* The message includes the name of the client and the duration of the connection.
|
|||
|
*
|
|||
|
* @param out the PrintWriter with which the sever sends messages to the client.
|
|||
|
* @param clientName the name of the connected client.
|
|||
|
* @param startTime the time in milliseconds when the client connected.
|
|||
|
*/
|
|||
|
public static void sendGoodbyeMessage(PrintWriter out, String clientName, long startTime) {
|
|||
|
if (out != null) {
|
|||
|
System.out.println("GOODBYE MESSAGE!" + clientName);
|
|||
|
long stopTime = System.currentTimeMillis();
|
|||
|
long duration = stopTime - startTime;
|
|||
|
out.println("BYE " + clientName + ", WE SPOKE FOR " + duration + " MS");
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* Adds a node (as a string) to the GRAPH.
|
|||
|
* When the name of the node is only including alphanumeric character,
|
|||
|
* plus the character "-" and the node doesn't already exist.
|
|||
|
*
|
|||
|
* @param message received from a client.
|
|||
|
* @return returns an error message if the node already exists
|
|||
|
* or success message if the node has been successfully added.
|
|||
|
*/
|
|||
|
public static String addNode(String message) {
|
|||
|
String nodeName = message.replace("ADD NODE ", "");
|
|||
|
|
|||
|
if (!isValidName(nodeName)) {
|
|||
|
return unsupportedCommand();
|
|||
|
}
|
|||
|
|
|||
|
if (!Server.GRAPH.containsNode(nodeName)) {
|
|||
|
Server.GRAPH.addNode(nodeName);
|
|||
|
return "NODE ADDED";
|
|||
|
} else {
|
|||
|
return "ERROR: NODE ALREADY EXISTS";
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* Adds a directed edge to the GRAPH.
|
|||
|
* A directed edge has three parts, node from which it points, node to which it points
|
|||
|
* and weight which is a positive integer.
|
|||
|
* When the names of the nodes between the edge is are already existing and
|
|||
|
* the format of the command is valid and the weight is a positive integer.
|
|||
|
*
|
|||
|
* @param message received from a client.
|
|||
|
* @return returns an error message if the the edge can't be added
|
|||
|
* or success message if the edge has been successfully added.
|
|||
|
*/
|
|||
|
public static String addEdge(String message) {
|
|||
|
String edgeString = message.replace("ADD EDGE ", "");
|
|||
|
String[] edgeParts = edgeString.split(" ");
|
|||
|
|
|||
|
if (!isValidEdge(edgeParts)) {
|
|||
|
return unsupportedCommand();
|
|||
|
}
|
|||
|
|
|||
|
String fromName = edgeParts[0];
|
|||
|
String toName = edgeParts[1];
|
|||
|
|
|||
|
if (Server.GRAPH.containsNode(fromName) && Server.GRAPH.containsNode(toName)) {
|
|||
|
int weight = Integer.valueOf(edgeParts[2]);
|
|||
|
|
|||
|
Server.GRAPH.addEdge(fromName, toName, weight);
|
|||
|
return "EDGE ADDED";
|
|||
|
}
|
|||
|
|
|||
|
return "ERROR: NODE NOT FOUND";
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* Removes a node from the GRAPH, only when the node is already existing.
|
|||
|
* All edges that are linked to the removed node will also be removed.
|
|||
|
* @param message received from a client.
|
|||
|
* @return returns an error message if the node doesn't exist
|
|||
|
* or success message if the node has been successfully removed.
|
|||
|
*/
|
|||
|
public static String removeNode(String message) {
|
|||
|
String nodeName = message.replace("REMOVE NODE ", "");
|
|||
|
|
|||
|
if (Server.GRAPH.containsNode(nodeName)) {
|
|||
|
Server.GRAPH.removeNode(nodeName);
|
|||
|
return "NODE REMOVED";
|
|||
|
} else {
|
|||
|
return "ERROR: NODE NOT FOUND";
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* Removes all directed edges from the GRAPH when the command is valid
|
|||
|
* and the both nodes that form the graph are existing.
|
|||
|
* @param message received from a client.
|
|||
|
* @return returns an error message if the command is invalid or the nodes between the graph aren't existing.
|
|||
|
* Returns a success message if the edges have been successfully removed.
|
|||
|
*/
|
|||
|
public static String removeEdge(String message) {
|
|||
|
String edgeString = message.replace("REMOVE EDGE ", "");
|
|||
|
String[] edgeParts = edgeString.split(" ");
|
|||
|
|
|||
|
if (edgeParts.length != 2) {
|
|||
|
return unsupportedCommand();
|
|||
|
}
|
|||
|
|
|||
|
String fromName = edgeParts[0];
|
|||
|
String toName = edgeParts[1];
|
|||
|
|
|||
|
if (Server.GRAPH.containsNode(fromName) && Server.GRAPH.containsNode(toName)) {
|
|||
|
Server.GRAPH.removeEdge(fromName, toName);
|
|||
|
return "EDGE REMOVED";
|
|||
|
}
|
|||
|
|
|||
|
return "ERROR: NODE NOT FOUND";
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* Calculates the sum of the weights of the shortest path between two nodes in the GRAPH,
|
|||
|
* when the input of the command is valid and the nodes exist in the GRAPH.
|
|||
|
* @param message received from a client.
|
|||
|
* @return the sum of the weights of the shortest path between two nodes.
|
|||
|
* Returns an error message if the command is invalid or the nodes of the GRAPH aren't existing.
|
|||
|
*/
|
|||
|
public static String getShortestPath(String message) {
|
|||
|
String fromAndTo = message.replace("SHORTEST PATH ", "");
|
|||
|
String[] fromTo = fromAndTo.split(" ");
|
|||
|
if (fromTo.length != 2) {
|
|||
|
return unsupportedCommand();
|
|||
|
}
|
|||
|
|
|||
|
String from = fromTo[0];
|
|||
|
String to = fromTo[1];
|
|||
|
|
|||
|
if (Server.GRAPH.containsNode(from) && Server.GRAPH.containsNode(to)) {
|
|||
|
return String.valueOf(Server.GRAPH.getShortestPathDistance(from, to));
|
|||
|
}
|
|||
|
|
|||
|
return "ERROR: NODE NOT FOUND";
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* Finds all the nodes that are closer to a node from the client message than the given weight.
|
|||
|
*
|
|||
|
* For example:
|
|||
|
* Simple graph: Mark - 5 -> Michael - 2 -> Madeleine - 8 -> Mufasa
|
|||
|
* CLOSER THAN 8 Mark
|
|||
|
* Would return: Madeleine,Michael
|
|||
|
* Because Michael is at weight 5 from Mark and Madeleine is at weight 7 (5+2) from Mark.
|
|||
|
*
|
|||
|
* @param message received from a client.
|
|||
|
* @return a comma separated list(no spaces) of found nodes, that are closer to a node from the client message,
|
|||
|
* sorted alphabetically by name, not including the starting node.
|
|||
|
* Returns an error message if the command is invalid or the node of the GRAPH isn't existing.
|
|||
|
*/
|
|||
|
public static String getCloserThan(String message) {
|
|||
|
String weightAndNode = message.replace("CLOSER THAN ", "");
|
|||
|
String[] weightNode = weightAndNode.split(" ");
|
|||
|
|
|||
|
if (weightNode.length != 2 || !isValidWeightString(weightNode[0])) {
|
|||
|
return unsupportedCommand();
|
|||
|
}
|
|||
|
int weight = Integer.valueOf(weightNode[0]);
|
|||
|
String nodeName = weightNode[1];
|
|||
|
|
|||
|
if (Server.GRAPH.containsNode(nodeName)) {
|
|||
|
String path = Server.GRAPH.getNodesCloserThan(weight, nodeName);
|
|||
|
return path;
|
|||
|
}
|
|||
|
|
|||
|
return "ERROR: NODE NOT FOUND";
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* Returns string for unsupported command.
|
|||
|
* @return string for unsupported command.
|
|||
|
*/
|
|||
|
public static String unsupportedCommand() {
|
|||
|
return "SORRY, I DIDN'T UNDERSTAND THAT";
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
private static boolean isValidEdge(String[] input) {
|
|||
|
if (input.length != 3) {
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
String weightString = input[2];
|
|||
|
|
|||
|
boolean isValidWeightString = isValidWeightString(weightString);
|
|||
|
|
|||
|
return isValidWeightString;
|
|||
|
}
|
|||
|
|
|||
|
private static boolean isValidWeightString(String weightString) {
|
|||
|
return weightString.length() > 0 &&
|
|||
|
weightString.chars().allMatch(Character::isDigit);
|
|||
|
}
|
|||
|
|
|||
|
private static boolean isValidName(String input) {
|
|||
|
return input.matches("[A-Za-z0-9-]+");
|
|||
|
}
|
|||
|
}
|