1 package uk.ac.vamsas.client.picking;
\r
5 import java.util.logging.*;
\r
7 import org.apache.commons.logging.Log;
\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
15 public class SocketManager implements IPickManager
\r
17 private Log logger = org.apache.commons.logging.LogFactory.getLog(uk.ac.vamsas.client.picking.SocketManager.class);
\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
25 private PickServer server;
\r
27 private IMessageHandler msgHandler;
\r
29 private boolean isRunning = true;
\r
32 * Constructs a new PickManager. This method will return immediately, while
\r
33 * a looping thread runs that attempts to run the server or connect to an
\r
36 public SocketManager()
\r
38 //logger.setLevel(Level.OFF);
\r
40 server = new PickServer(this);
\r
41 clients = new LinkedList();
\r
43 new InitializeThread().start();
\r
47 * Registers a message handler with the manager that allows the manager to
\r
48 * perform a method callback on that object whenever a message is received.
\r
49 * @param handler the message handler to register
\r
51 public void registerMessageHandler(IMessageHandler handler)
\r
53 msgHandler = handler;
\r
57 * Attempts to establish a connection between two client endpoints. This
\r
58 * method is called in two ways: 1) by the server when it receives a remote
\r
59 * request (in which case the socket will already be established) and 2) by
\r
60 * a client that is attempting to connect *to* the server.
\r
61 * @param socket a socket endpoint for the connection
\r
62 * @return true if the connection is successfully, false otherwise
\r
64 synchronized boolean addEndPoint(Socket socket)
\r
66 PickEndPoint client = new PickEndPoint(this, socket);
\r
68 if (client.openConnection())
\r
70 clients.add(client);
\r
71 //logger.info("List now contains " + clients.size() + " client(s)");
\r
79 * Sends a message to other clients.
\r
80 * @param message the message to send
\r
82 public void sendMessage(Message message)
\r
84 forwardMessage(null, message);
\r
88 * Forwards (or sends) a message. When the server (client A) receives a
\r
89 * message from client B, it must also forward it to clients C and D (etc),
\r
90 * but mustn't forward it *back* to client B.
\r
91 * @param origin the client endpoint that received the message (will be null
\r
92 * if the message originates from this instance
\r
93 * @param message the message to send
\r
95 private void forwardMessage(PickEndPoint origin, Message message)
\r
97 for (int i = clients.size()-1; i >= 0; i--)
\r
101 PickEndPoint client = (PickEndPoint) clients.get(i);
\r
102 if (client != origin)
\r
103 client.send(message);
\r
105 catch (Exception e)
\r
107 System.out.println("FORWARD: " + e);
\r
113 * Handles a received message. If the manager is running in server mode,
\r
114 * then it must ensure the message is also forwarded to the other clients.
\r
115 * @param origin the client endpoint that received the message
\r
116 * @message the message that was received
\r
118 void processMessage(PickEndPoint origin, Message message)
\r
120 if (server.isServer())
\r
121 forwardMessage(origin, message);
\r
123 if (msgHandler != null)
\r
124 msgHandler.handleMessage(message);
\r
126 // logger.info("No handler available to deal with incoming message");
\r
130 * Removes a client connection from the list when its connection is no
\r
132 * @param client the client endpoint to remove
\r
134 synchronized void removeEndPoint(PickEndPoint client)
\r
136 clients.remove(client);
\r
137 //logger.info("List now contains " + clients.size() + " client(s)");
\r
139 // If there's no endpoints left, then we've lost all connections and
\r
140 // need to reinitialize - but only if we've not been told to stop
\r
141 if (clients.size() == 0 && isRunning)
\r
142 new InitializeThread().start();
\r
146 * Thread extension class to handle the actual initialization
\r
148 private class InitializeThread extends Thread
\r
152 logger.debug("Initializing connection...");
\r
153 boolean connected = false;
\r
155 // Loop until we can get a connection (one way or the other)
\r
156 while (!connected && isRunning)
\r
158 // Sleep for a rnd time so we don't end up with all the VAMSAS
\r
159 // apps trying to initialize servers at the same time
\r
160 try { Thread.sleep((int)(10*Math.random())); }
\r
161 catch (InterruptedException e) {}
\r
163 // Attempt to open the server port...
\r
164 if (server.isServer() || server.createServer())
\r
167 // If it fails, then attempt to make a client connection...
\r
168 else if (addEndPoint(null))
\r
171 logger.debug("Completed initializing connection.");
\r
175 public void shutdown()
\r
177 logger.debug("Shutting down socket manager.");
\r
178 if (server == null)
\r
179 throw new Error("Client Implementation Error: shutdown() called on uninitialized SocketManager.");
\r
183 if (server.isServer())
\r
184 server.terminate();
\r
186 while (clients.size() > 0) {
\r
187 logger.debug("Closing endpoint.");
\r
188 ((PickEndPoint)clients.getFirst()).terminate();
\r
190 logger.debug("Shutdown of socketmanager completed.");
\r