/* * This file is part of the Vamsas Client version 0.1. * Copyright 2009 by Jim Procter, Iain Milne, Pierre Marguerite, * Andrew Waterhouse and Dominik Lindner. * * Earlier versions have also been incorporated into Jalview version 2.4 * since 2008, and TOPALi version 2 since 2007. * * The Vamsas Client is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * The Vamsas Client is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the Vamsas Client. If not, see . */ package uk.ac.vamsas.client.picking; import java.net.*; import java.util.*; import java.util.logging.*; import org.apache.commons.logging.Log; /** * Concrete implementation of the IPickManager interface that uses sockets for * message communication. An instance of this class attempts to run the central * server for other clients; failing that, it attempts to connect to an existing * server instead. */ public class SocketManager implements IPickManager { private Log logger = org.apache.commons.logging.LogFactory .getLog(uk.ac.vamsas.client.picking.SocketManager.class); // Maintains a list of client communication objects - each object represents // a way of talking to either: // the server - if this is client side (and in which case, the list will only // contain one element // the other clients - if this is server side private LinkedList clients; private PickServer server; private IMessageHandler msgHandler; private boolean isRunning = true; /** * Constructs a new PickManager. This method will return immediately, while a * looping thread runs that attempts to run the server or connect to an * existing server. */ public SocketManager() { // logger.setLevel(Level.OFF); server = new PickServer(this); clients = new LinkedList(); new InitializeThread().start(); } /** * Registers a message handler with the manager that allows the manager to * perform a method callback on that object whenever a message is received. * * @param handler * the message handler to register */ public void registerMessageHandler(IMessageHandler handler) { msgHandler = handler; } /** * Attempts to establish a connection between two client endpoints. This * method is called in two ways: 1) by the server when it receives a remote * request (in which case the socket will already be established) and 2) by a * client that is attempting to connect *to* the server. * * @param socket * a socket endpoint for the connection * @return true if the connection is successfully, false otherwise */ synchronized boolean addEndPoint(Socket socket) { PickEndPoint client = new PickEndPoint(this, socket); if (client.openConnection()) { clients.add(client); // logger.info("List now contains " + clients.size() + " client(s)"); return true; } return false; } /** * Sends a message to other clients. * * @param message * the message to send */ public void sendMessage(Message message) { forwardMessage(null, message); } /** * Forwards (or sends) a message. When the server (client A) receives a * message from client B, it must also forward it to clients C and D (etc), * but mustn't forward it *back* to client B. * * @param origin * the client endpoint that received the message (will be null if the * message originates from this instance * @param message * the message to send */ private void forwardMessage(PickEndPoint origin, Message message) { for (int i = clients.size() - 1; i >= 0; i--) { try { PickEndPoint client = (PickEndPoint) clients.get(i); if (client != origin) client.send(message); } catch (Exception e) { System.out.println("FORWARD: " + e); } } } /** * Handles a received message. If the manager is running in server mode, then * it must ensure the message is also forwarded to the other clients. * * @param origin * the client endpoint that received the message * @message the message that was received */ void processMessage(PickEndPoint origin, Message message) { if (server.isServer()) forwardMessage(origin, message); if (msgHandler != null) msgHandler.handleMessage(message); // else // logger.info("No handler available to deal with incoming message"); } /** * Removes a client connection from the list when its connection is no longer * valid. * * @param client * the client endpoint to remove */ synchronized void removeEndPoint(PickEndPoint client) { clients.remove(client); // logger.info("List now contains " + clients.size() + " client(s)"); // If there's no endpoints left, then we've lost all connections and // need to reinitialize - but only if we've not been told to stop if (clients.size() == 0 && isRunning) new InitializeThread().start(); } /** * Thread extension class to handle the actual initialization */ private class InitializeThread extends Thread { public void run() { logger.debug("Initializing connection..."); boolean connected = false; // Loop until we can get a connection (one way or the other) while (!connected && isRunning) { // Sleep for a rnd time so we don't end up with all the VAMSAS // apps trying to initialize servers at the same time try { Thread.sleep((int) (10 * Math.random())); } catch (InterruptedException e) { } // Attempt to open the server port... if (server.isServer() || server.createServer()) connected = true; // If it fails, then attempt to make a client connection... else if (addEndPoint(null)) connected = true; } logger.debug("Completed initializing connection."); } } public void shutdown() { logger.debug("Shutting down socket manager."); if (server == null) throw new Error( "Client Implementation Error: shutdown() called on uninitialized SocketManager."); isRunning = false; if (server.isServer()) server.terminate(); while (clients.size() > 0) { logger.debug("Closing endpoint."); ((PickEndPoint) clients.getFirst()).terminate(); } logger.debug("Shutdown of socketmanager completed."); } }