From 5a32c865123e6aa09079b4928346ffc6bd269258 Mon Sep 17 00:00:00 2001 From: jprocter Date: Thu, 14 Dec 2006 17:50:39 +0000 Subject: [PATCH] refactored org to uk git-svn-id: https://svn.lifesci.dundee.ac.uk/svn/repository/trunk@267 be28352e-c001-0410-b1a7-c7978e42abec --- src/uk/ac/vamsas/client/picking/PickEndPoint.java | 101 ++++++++++++++ src/uk/ac/vamsas/client/picking/PickManager.java | 147 +++++++++++++++++++++ src/uk/ac/vamsas/client/picking/PickServer.java | 83 ++++++++++++ src/uk/ac/vamsas/client/picking/TestApp.java | 33 +++++ 4 files changed, 364 insertions(+) create mode 100644 src/uk/ac/vamsas/client/picking/PickEndPoint.java create mode 100644 src/uk/ac/vamsas/client/picking/PickManager.java create mode 100644 src/uk/ac/vamsas/client/picking/PickServer.java create mode 100644 src/uk/ac/vamsas/client/picking/TestApp.java diff --git a/src/uk/ac/vamsas/client/picking/PickEndPoint.java b/src/uk/ac/vamsas/client/picking/PickEndPoint.java new file mode 100644 index 0000000..951e8d4 --- /dev/null +++ b/src/uk/ac/vamsas/client/picking/PickEndPoint.java @@ -0,0 +1,101 @@ +package uk.ac.vamsas.client.picking; + +import java.io.*; +import java.net.*; + +class PickEndPoint extends Thread +{ + private Socket socket; + private int rPort; + private BufferedWriter os; + private BufferedReader in; + + private PickManager manager; + + PickEndPoint(PickManager manager, Socket s) + { + this.manager = manager; + socket = s; + } + + boolean openConnection() + { + try + { + // Create the socket if it doesn't already exist + if (socket == null) + socket = new Socket(InetAddress.getLocalHost(), PickServer.PORT); + + rPort = socket.getPort(); + socket.setKeepAlive(true); + + // Open the streams for reading/writing + os = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); + in = new BufferedReader(new InputStreamReader(socket.getInputStream())); + + // Start the thread to listen for incoming messages + System.out.println("CLIENT: connection successful to port " + + socket.getPort() + " via port " + socket.getLocalPort()); + start(); + + return true; + } + catch (Exception e) + { + System.out.println("CLIENT: connection failed: " + e); + return false; + } + } + + void send(String str) + { + try + { + System.out.println("CLIENT: send " + str + " to " + rPort); + os.write(str); + + // We use a newline to terminate the message + os.newLine(); + os.flush(); + } + catch (Exception e) + { + System.out.println("CLIENT: failed to send"); + + // TODO: terminate the connection on a failed send or retry? + terminate(); + } + } + + // void receive() (threaded) + public void run() + { + try + { + while (true) + { + String str = in.readLine(); + System.out.println("CLIENT: recv " + str + " from " + rPort); + + manager.handleMessage(this, str); + } + } + catch (Exception e) + { + // Means the other end of the connection has (probably died) so we need + // terminate this endpoint (if this is server side) + System.out.println("CLIENT: read failed: " + e); + + terminate(); + } + } + + private void terminate() + { + try { socket.close(); } + catch (IOException e) {} + + System.out.println("CLIENT: closing connection to port " + socket.getPort()); + manager.removeEndPoint(this); + } +} \ No newline at end of file diff --git a/src/uk/ac/vamsas/client/picking/PickManager.java b/src/uk/ac/vamsas/client/picking/PickManager.java new file mode 100644 index 0000000..ee03f8a --- /dev/null +++ b/src/uk/ac/vamsas/client/picking/PickManager.java @@ -0,0 +1,147 @@ +package uk.ac.vamsas.client.picking; + +import java.net.*; +import java.util.*; +import java.util.logging.*; + +/** + * Manager class that maintains a list of connected clients in addition to + * attempting to run a server to listen for client connections. If the server + * initialization fails, then an attempt to connect (as a client) to another JVM + * that is already a server happens instead. + */ +public class PickManager +{ + private static Logger logger = Logger.getLogger("uk.ac.vamsas.client.picking"); + + // 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; + + /** + * 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 PickManager() + { + server = new PickServer(this); + clients = new LinkedList(); + + new InitializeThread().start(); + } + + /** + * 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 + */ + boolean addEndPoint(Socket socket) + { + PickEndPoint client = new PickEndPoint(this, socket); + + if (client.openConnection()) + { + clients.add(client); + logger.fine("List now contains " + clients.size() + " client(s)"); + return true; + } + + return false; + } + + /** + * Sends a message to other clients. + * @param str the message to send + */ + public void sendMessage(String str) + { + forwardMessage(null, str); + } + + /** + * 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 str the message to send + */ + private void forwardMessage(PickEndPoint origin, String str) + { + ListIterator itor = clients.listIterator(); + while (itor.hasNext()) + { + PickEndPoint client = (PickEndPoint) itor.next(); + + if (client != origin) + client.send(str); + } + } + + /** + * 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 + * @str the message that was received + */ + void handleMessage(PickEndPoint origin, String str) + { + if (server.isServer()) + forwardMessage(origin, str); + + // TODO: pass message to VAMSAS API + } + + /** + * Removes a client connection from the list when its connection is no + * longer valid. + * @param client the client endpoint to remove + */ + void removeEndPoint(PickEndPoint client) + { + clients.remove(client); + logger.fine("List now contains " + clients.size() + " client(s)"); + + // If there's no endpoints left, then we've lost all connections and + // need to reinitialize + if (clients.size() == 0) + new InitializeThread().start(); + } + + /** + * Thread extension class to handle the actual initialization + */ + private class InitializeThread extends Thread + { + public void run() + { + logger.fine("Initializing connection..."); + boolean connected = false; + + // Loop until we can get a connection (one way or the other) + while (!connected) + { + // 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)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; + } + } + } +} \ No newline at end of file diff --git a/src/uk/ac/vamsas/client/picking/PickServer.java b/src/uk/ac/vamsas/client/picking/PickServer.java new file mode 100644 index 0000000..61884a3 --- /dev/null +++ b/src/uk/ac/vamsas/client/picking/PickServer.java @@ -0,0 +1,83 @@ +package uk.ac.vamsas.client.picking; + +import java.io.*; +import java.net.*; +import java.util.logging.*; + +/** + * Server class that listens for incoming connections on a predefined port. + */ +class PickServer extends Thread +{ + private static Logger logger = Logger.getLogger("uk.ac.vamsas.client.picking"); + + // The port number we'll listen on + static final int PORT = 53782; + + private ServerSocket serverSocket; + // Set to true once the server is established + private boolean isServer = false; + + private PickManager manager; + + /** + * Constructs a new instance of the server (but doesn't start it). + * @param manager a reference to the pick manager that owns this server + */ + PickServer(PickManager manager) + { + this.manager = manager; + } + + /** + * Returns true if this server instance is running. + * Return true if this server instance is running + */ + boolean isServer() + { return isServer; } + + /** + * Attempts to create the server by opening a server socket on the port. + * @return true if the server was created; false otherwise + */ + boolean createServer() + { + try + { + serverSocket = new ServerSocket(PORT); + start(); + + return isServer = true; + } + catch (IOException e) + { + logger.info("SERVER: " + e); + return false; + } + } + + /** + * Thread listening method - loops indefinitely listening for connections. + * When one is received, the socket object is passed to the manager so it + * can make a full client connection for further comms. + */ + public void run() + { + logger.fine("SERVER: listening on " + PORT + " - SERVER"); + + // Loop forever, accepting connectons from other clients + // TODO: add in the ability to terminate the server if a VAMSAS session + // is ended + while (true) + { + try + { + Socket socket = serverSocket.accept(); + logger.fine("SERVER: connection detected"); + + manager.addEndPoint(socket); + } + catch (IOException e) {} + } + } +} \ No newline at end of file diff --git a/src/uk/ac/vamsas/client/picking/TestApp.java b/src/uk/ac/vamsas/client/picking/TestApp.java new file mode 100644 index 0000000..988939b --- /dev/null +++ b/src/uk/ac/vamsas/client/picking/TestApp.java @@ -0,0 +1,33 @@ +package uk.ac.vamsas.client.picking; + +import java.util.logging.*; + +public class TestApp +{ + private static Logger logger = Logger.getLogger("uk.ac.vamsas.client.picking"); + + public static void main(String[] args) + throws Exception + { +// logger.setLevel(Level.INFO); + + TestApp app = new TestApp(); + + PickManager manager = new PickManager(); + + // Send 5 test messages... +// for (int i = 0; i < 5; i++) + while (true) + { + try { Thread.sleep((int) (Math.random()*20000)); } + catch (InterruptedException e) {} + + int rnd = (int) (Math.random()*100); + manager.sendMessage("" + rnd); + } + } + + public TestApp() + { + } +} \ No newline at end of file -- 1.7.10.2