2 * This file is part of the Vamsas Client version 0.1.
\r
3 * Copyright 2009 by Jim Procter, Iain Milne, Pierre Marguerite,
\r
4 * Andrew Waterhouse and Dominik Lindner.
\r
6 * Earlier versions have also been incorporated into Jalview version 2.4
\r
7 * since 2008, and TOPALi version 2 since 2007.
\r
9 * The Vamsas Client is free software: you can redistribute it and/or modify
\r
10 * it under the terms of the GNU Lesser General Public License as published by
\r
11 * the Free Software Foundation, either version 3 of the License, or
\r
12 * (at your option) any later version.
\r
14 * The Vamsas Client is distributed in the hope that it will be useful,
\r
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
17 * GNU Lesser General Public License for more details.
\r
19 * You should have received a copy of the GNU Lesser General Public License
\r
20 * along with the Vamsas Client. If not, see <http://www.gnu.org/licenses/>.
\r
22 package uk.ac.vamsas.client.picking;
\r
26 import java.util.logging.*;
\r
28 import org.apache.commons.logging.Log;
\r
31 * Concrete implementation of the IPickManager interface that uses sockets for
\r
32 * message communication. An instance of this class attempts to run the central
\r
33 * server for other clients; failing that, it attempts to connect to an existing
\r
36 public class SocketManager implements IPickManager {
\r
37 private Log logger = org.apache.commons.logging.LogFactory
\r
38 .getLog(uk.ac.vamsas.client.picking.SocketManager.class);
\r
40 // Maintains a list of client communication objects - each object represents
\r
41 // a way of talking to either:
\r
42 // the server - if this is client side (and in which case, the list will only
\r
43 // contain one element
\r
44 // the other clients - if this is server side
\r
45 private LinkedList clients;
\r
47 private PickServer server;
\r
49 private IMessageHandler msgHandler;
\r
51 private boolean isRunning = true;
\r
54 * Constructs a new PickManager. This method will return immediately, while a
\r
55 * looping thread runs that attempts to run the server or connect to an
\r
58 public SocketManager() {
\r
59 // logger.setLevel(Level.OFF);
\r
61 server = new PickServer(this);
\r
62 clients = new LinkedList();
\r
64 new InitializeThread().start();
\r
68 * Registers a message handler with the manager that allows the manager to
\r
69 * perform a method callback on that object whenever a message is received.
\r
72 * the message handler to register
\r
74 public void registerMessageHandler(IMessageHandler handler) {
\r
75 msgHandler = handler;
\r
79 * Attempts to establish a connection between two client endpoints. This
\r
80 * method is called in two ways: 1) by the server when it receives a remote
\r
81 * request (in which case the socket will already be established) and 2) by a
\r
82 * client that is attempting to connect *to* the server.
\r
85 * a socket endpoint for the connection
\r
86 * @return true if the connection is successfully, false otherwise
\r
88 synchronized boolean addEndPoint(Socket socket) {
\r
89 PickEndPoint client = new PickEndPoint(this, socket);
\r
91 if (client.openConnection()) {
\r
92 clients.add(client);
\r
93 // logger.info("List now contains " + clients.size() + " client(s)");
\r
101 * Sends a message to other clients.
\r
104 * the message to send
\r
106 public void sendMessage(Message message) {
\r
107 forwardMessage(null, message);
\r
111 * Forwards (or sends) a message. When the server (client A) receives a
\r
112 * message from client B, it must also forward it to clients C and D (etc),
\r
113 * but mustn't forward it *back* to client B.
\r
116 * the client endpoint that received the message (will be null if the
\r
117 * message originates from this instance
\r
119 * the message to send
\r
121 private void forwardMessage(PickEndPoint origin, Message message) {
\r
122 for (int i = clients.size() - 1; i >= 0; i--) {
\r
124 PickEndPoint client = (PickEndPoint) clients.get(i);
\r
125 if (client != origin)
\r
126 client.send(message);
\r
127 } catch (Exception e) {
\r
128 System.out.println("FORWARD: " + e);
\r
134 * Handles a received message. If the manager is running in server mode, then
\r
135 * it must ensure the message is also forwarded to the other clients.
\r
138 * the client endpoint that received the message
\r
139 * @message the message that was received
\r
141 void processMessage(PickEndPoint origin, Message message) {
\r
142 if (server.isServer())
\r
143 forwardMessage(origin, message);
\r
145 if (msgHandler != null)
\r
146 msgHandler.handleMessage(message);
\r
148 // logger.info("No handler available to deal with incoming message");
\r
152 * Removes a client connection from the list when its connection is no longer
\r
156 * the client endpoint to remove
\r
158 synchronized void removeEndPoint(PickEndPoint client) {
\r
159 clients.remove(client);
\r
160 // logger.info("List now contains " + clients.size() + " client(s)");
\r
162 // If there's no endpoints left, then we've lost all connections and
\r
163 // need to reinitialize - but only if we've not been told to stop
\r
164 if (clients.size() == 0 && isRunning)
\r
165 new InitializeThread().start();
\r
169 * Thread extension class to handle the actual initialization
\r
171 private class InitializeThread extends Thread {
\r
172 public void run() {
\r
173 logger.debug("Initializing connection...");
\r
174 boolean connected = false;
\r
176 // Loop until we can get a connection (one way or the other)
\r
177 while (!connected && isRunning) {
\r
178 // Sleep for a rnd time so we don't end up with all the VAMSAS
\r
179 // apps trying to initialize servers at the same time
\r
181 Thread.sleep((int) (10 * Math.random()));
\r
182 } catch (InterruptedException e) {
\r
185 // Attempt to open the server port...
\r
186 if (server.isServer() || server.createServer())
\r
189 // If it fails, then attempt to make a client connection...
\r
190 else if (addEndPoint(null))
\r
193 logger.debug("Completed initializing connection.");
\r
197 public void shutdown() {
\r
198 logger.debug("Shutting down socket manager.");
\r
199 if (server == null)
\r
201 "Client Implementation Error: shutdown() called on uninitialized SocketManager.");
\r
205 if (server.isServer())
\r
206 server.terminate();
\r
208 while (clients.size() > 0) {
\r
209 logger.debug("Closing endpoint.");
\r
210 ((PickEndPoint) clients.getFirst()).terminate();
\r
212 logger.debug("Shutdown of socketmanager completed.");
\r