edfd5731ad41c2341fd93dd82bf2c069d89b996c
[vamsas.git] / src / uk / ac / vamsas / client / picking / SocketManager.java
1 package uk.ac.vamsas.client.picking;\r
2 \r
3 import java.net.*;\r
4 import java.util.*;\r
5 import java.util.logging.*;\r
6 \r
7 /**\r
8  * Concrete implementation of the IPickManager interface that uses sockets for\r
9  * message communication. An instance of this class attempts to run the central\r
10  * server for other clients; failing that, it attempts to connect to an existing\r
11  * server instead.\r
12  */\r
13 public class SocketManager implements IPickManager\r
14 {\r
15         private Logger logger = Logger.getLogger("uk.ac.vamsas.client.picking");\r
16         \r
17         // Maintains a list of client communication objects - each object represents\r
18         // a way of talking to either:\r
19         //  the server - if this is client side (and in which case, the list will only contain one element\r
20         //  the other clients - if this is server side\r
21         private LinkedList clients;\r
22         \r
23         private PickServer server;\r
24         \r
25         private IMessageHandler msgHandler;\r
26         \r
27         /**\r
28          * Constructs a new PickManager. This method will return immediately, while\r
29          * a looping thread runs that attempts to run the server or connect to an\r
30          * existing server.\r
31          */\r
32         public SocketManager()\r
33         {\r
34 //              logger.setLevel(Level.OFF);\r
35                 \r
36                 server = new PickServer(this);\r
37                 clients = new LinkedList();\r
38                 \r
39                 new InitializeThread().start();\r
40         }\r
41         \r
42         /**\r
43          * Registers a message handler with the manager that allows the manager to\r
44          * perform a method callback on that object whenever a message is received.\r
45          * @param handler the message handler to register\r
46          */\r
47         public void registerMessageHandler(IMessageHandler handler)\r
48         {\r
49                 msgHandler = handler;\r
50         }\r
51         \r
52         /**\r
53          * Attempts to establish a connection between two client endpoints. This\r
54          * method is called in two ways: 1) by the server when it receives a remote\r
55          * request (in which case the socket will already be established) and 2) by\r
56          * a client that is attempting to connect *to* the server.\r
57          * @param socket a socket endpoint for the connection\r
58          * @return true if the connection is successfully, false otherwise\r
59          */\r
60         synchronized boolean addEndPoint(Socket socket)\r
61         {\r
62                 PickEndPoint client = new PickEndPoint(this, socket);\r
63                 \r
64                 if (client.openConnection())\r
65                 {\r
66                         clients.add(client);\r
67                         //logger.info("List now contains " + clients.size() + " client(s)");\r
68                         return true;\r
69                 }\r
70                 \r
71                 return false;\r
72         }\r
73         \r
74         /**\r
75          * Sends a message to other clients.\r
76          * @param message the message to send\r
77          */\r
78         public void sendMessage(Message message)\r
79         {\r
80                 forwardMessage(null, message);\r
81         }\r
82         \r
83         /**\r
84          * Forwards (or sends) a message. When the server (client A) receives a\r
85          * message from client B, it must also forward it to clients C and D (etc),\r
86          * but mustn't forward it *back* to client B.\r
87          * @param origin the client endpoint that received the message (will be null\r
88          * if the message originates from this instance\r
89          * @param message the message to send\r
90          */\r
91         private void forwardMessage(PickEndPoint origin, Message message)\r
92         {\r
93                 ListIterator itor = clients.listIterator();\r
94                 while (itor.hasNext())\r
95                 {\r
96                         PickEndPoint client = (PickEndPoint) itor.next();\r
97                         \r
98                         if (client != origin)\r
99                                 client.send(message);\r
100                 }\r
101         }\r
102         \r
103         /**\r
104          * Handles a received message. If the manager is running in server mode,\r
105          * then it must ensure the message is also forwarded to the other clients.\r
106          * @param origin the client endpoint that received the message\r
107          * @message the message that was received\r
108          */\r
109         void processMessage(PickEndPoint origin, Message message)\r
110         {       \r
111                 if (server.isServer())\r
112                         forwardMessage(origin, message);\r
113                         \r
114                 if (msgHandler != null)\r
115                         msgHandler.handleMessage(message);\r
116 //              else\r
117 //                      logger.info("No handler available to deal with incoming message");\r
118         }\r
119         \r
120         /**\r
121          * Removes a client connection from the list when its connection is no\r
122          * longer valid.\r
123          * @param client the client endpoint to remove\r
124          */\r
125         synchronized void removeEndPoint(PickEndPoint client)\r
126         {\r
127                 clients.remove(client);\r
128                 //logger.info("List now contains " + clients.size() + " client(s)");\r
129                 \r
130                 // If there's no endpoints left, then we've lost all connections and\r
131                 // need to reinitialize\r
132                 if (clients.size() == 0)\r
133                         new InitializeThread().start();\r
134         }\r
135         \r
136         /**\r
137          * Thread extension class to handle the actual initialization\r
138          */\r
139         private class InitializeThread extends Thread\r
140         {\r
141                 public void run()\r
142                 {\r
143                         //logger.info("Initializing connection...");\r
144                         boolean connected = false;\r
145                         \r
146                         // Loop until we can get a connection (one way or the other)\r
147                         while (!connected)\r
148                         {\r
149                                 // Sleep for a rnd time so we don't end up with all the VAMSAS\r
150                                 // apps trying to initialize servers at the same time\r
151                                 try { Thread.sleep((int)Math.random()); }\r
152                                 catch (InterruptedException e) {}\r
153                                         \r
154                                 // Attempt to open the server port...\r
155                                 if (server.isServer() || server.createServer())\r
156                                         connected = true;\r
157 \r
158                                 // If it fails, then attempt to make a client connection...\r
159                                 else if (addEndPoint(null))\r
160                                         connected = true;\r
161                         }\r
162                 }\r
163         }\r
164 }