+package uk.ac.vamsas.client.picking;\r
+\r
+import java.net.*;\r
+import java.util.*;\r
+import java.util.logging.*;\r
+\r
+/**\r
+ * Manager class that maintains a list of connected clients in addition to\r
+ * attempting to run a server to listen for client connections. If the server\r
+ * initialization fails, then an attempt to connect (as a client) to another JVM\r
+ * that is already a server happens instead.\r
+ */\r
+public class PickManager\r
+{\r
+ private static Logger logger = Logger.getLogger("uk.ac.vamsas.client.picking");\r
+ \r
+ // Maintains a list of client communication objects - each object represents\r
+ // a way of talking to either:\r
+ // the server - if this is client side (and in which case, the list will only contain one element\r
+ // the other clients - if this is server side\r
+ private LinkedList clients;\r
+ \r
+ private PickServer server;\r
+ \r
+ /**\r
+ * Constructs a new PickManager. This method will return immediately, while\r
+ * a looping thread runs that attempts to run the server or connect to an\r
+ * existing server.\r
+ */\r
+ public PickManager()\r
+ {\r
+ server = new PickServer(this);\r
+ clients = new LinkedList();\r
+ \r
+ new InitializeThread().start();\r
+ }\r
+ \r
+ /**\r
+ * Attempts to establish a connection between two client endpoints. This\r
+ * method is called in two ways: 1) by the server when it receives a remote\r
+ * request (in which case the socket will already be established) and 2) by\r
+ * a client that is attempting to connect *to* the server.\r
+ * @param socket a socket endpoint for the connection\r
+ * @return true if the connection is successfully, false otherwise\r
+ */\r
+ boolean addEndPoint(Socket socket)\r
+ {\r
+ PickEndPoint client = new PickEndPoint(this, socket);\r
+ \r
+ if (client.openConnection())\r
+ {\r
+ clients.add(client);\r
+ logger.fine("List now contains " + clients.size() + " client(s)");\r
+ return true;\r
+ }\r
+ \r
+ return false;\r
+ }\r
+ \r
+ /**\r
+ * Sends a message to other clients.\r
+ * @param str the message to send\r
+ */\r
+ public void sendMessage(String str)\r
+ {\r
+ forwardMessage(null, str);\r
+ }\r
+ \r
+ /**\r
+ * Forwards (or sends) a message. When the server (client A) receives a\r
+ * message from client B, it must also forward it to clients C and D (etc),\r
+ * but mustn't forward it *back* to client B.\r
+ * @param origin the client endpoint that received the message (will be null\r
+ * if the message originates from this instance\r
+ * @param str the message to send\r
+ */\r
+ private void forwardMessage(PickEndPoint origin, String str)\r
+ {\r
+ ListIterator itor = clients.listIterator();\r
+ while (itor.hasNext())\r
+ {\r
+ PickEndPoint client = (PickEndPoint) itor.next();\r
+ \r
+ if (client != origin)\r
+ client.send(str);\r
+ }\r
+ }\r
+ \r
+ /**\r
+ * Handles a received message. If the manager is running in server mode,\r
+ * then it must ensure the message is also forwarded to the other clients.\r
+ * @param origin the client endpoint that received the message\r
+ * @str the message that was received\r
+ */\r
+ void handleMessage(PickEndPoint origin, String str)\r
+ {\r
+ if (server.isServer())\r
+ forwardMessage(origin, str);\r
+ \r
+ // TODO: pass message to VAMSAS API\r
+ }\r
+ \r
+ /**\r
+ * Removes a client connection from the list when its connection is no\r
+ * longer valid.\r
+ * @param client the client endpoint to remove\r
+ */\r
+ void removeEndPoint(PickEndPoint client)\r
+ {\r
+ clients.remove(client);\r
+ logger.fine("List now contains " + clients.size() + " client(s)");\r
+ \r
+ // If there's no endpoints left, then we've lost all connections and\r
+ // need to reinitialize\r
+ if (clients.size() == 0)\r
+ new InitializeThread().start();\r
+ }\r
+ \r
+ /**\r
+ * Thread extension class to handle the actual initialization\r
+ */\r
+ private class InitializeThread extends Thread\r
+ {\r
+ public void run()\r
+ {\r
+ logger.fine("Initializing connection...");\r
+ boolean connected = false;\r
+ \r
+ // Loop until we can get a connection (one way or the other)\r
+ while (!connected)\r
+ {\r
+ // Sleep for a rnd time so we don't end up with all the VAMSAS\r
+ // apps trying to initialize servers at the same time\r
+ try { Thread.sleep((int)Math.random()); }\r
+ catch (InterruptedException e) {}\r
+ \r
+ // Attempt to open the server port...\r
+ if (server.isServer() || server.createServer())\r
+ connected = true;\r
+\r
+ // If it fails, then attempt to make a client connection...\r
+ else if (addEndPoint(null))\r
+ connected = true;\r
+ }\r
+ }\r
+ }\r
+}
\ No newline at end of file