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