From f46a9401190f34549d6c1bef0c2ce8a920cab83d Mon Sep 17 00:00:00 2001 From: imilne Date: Thu, 14 Dec 2006 19:29:20 +0000 Subject: [PATCH] Picking API now has message classes and basic implementation of interfaces (for its own implementation and client-app message handling implementation). git-svn-id: https://svn.lifesci.dundee.ac.uk/svn/repository/trunk@302 be28352e-c001-0410-b1a7-c7978e42abec --- src/uk/ac/vamsas/client/picking/CustomMessage.java | 17 +++++ .../ac/vamsas/client/picking/IMessageHandler.java | 15 +++++ src/uk/ac/vamsas/client/picking/IPickManager.java | 20 ++++++ src/uk/ac/vamsas/client/picking/Message.java | 24 ++++++++ .../ac/vamsas/client/picking/MouseOverMessage.java | 38 ++++++++++++ src/uk/ac/vamsas/client/picking/PickEndPoint.java | 52 ++++++++++++---- src/uk/ac/vamsas/client/picking/PickServer.java | 10 +-- .../{PickManager.java => SocketManager.java} | 65 ++++++++++++-------- src/uk/ac/vamsas/client/picking/TestApp.java | 26 ++++---- 9 files changed, 216 insertions(+), 51 deletions(-) create mode 100644 src/uk/ac/vamsas/client/picking/CustomMessage.java create mode 100644 src/uk/ac/vamsas/client/picking/IMessageHandler.java create mode 100644 src/uk/ac/vamsas/client/picking/IPickManager.java create mode 100644 src/uk/ac/vamsas/client/picking/Message.java create mode 100644 src/uk/ac/vamsas/client/picking/MouseOverMessage.java rename src/uk/ac/vamsas/client/picking/{PickManager.java => SocketManager.java} (64%) diff --git a/src/uk/ac/vamsas/client/picking/CustomMessage.java b/src/uk/ac/vamsas/client/picking/CustomMessage.java new file mode 100644 index 0000000..3f07cfc --- /dev/null +++ b/src/uk/ac/vamsas/client/picking/CustomMessage.java @@ -0,0 +1,17 @@ +package uk.ac.vamsas.client.picking; + +/** + * Defines a custom message that applications can use to send message types that + * haven't been predefined in the API. + */ +public class CustomMessage extends Message +{ + /** + * Constructs a new custom message. + * @param message the message to be sent + */ + public CustomMessage(String message) + { + this.message = "CUSTOM_" + message; + } +} \ No newline at end of file diff --git a/src/uk/ac/vamsas/client/picking/IMessageHandler.java b/src/uk/ac/vamsas/client/picking/IMessageHandler.java new file mode 100644 index 0000000..e09b81d --- /dev/null +++ b/src/uk/ac/vamsas/client/picking/IMessageHandler.java @@ -0,0 +1,15 @@ +package uk.ac.vamsas.client.picking; + +/** + * Interface that defines the methods required for a message handler. This + * interface must be implemented by the client application (TOPALi, Jalview or + * AstexViewer). + */ +public interface IMessageHandler +{ + /** + * Responds to a message received event. + * @param message the message that was received + */ + public void handleMessage(Message message); +} \ No newline at end of file diff --git a/src/uk/ac/vamsas/client/picking/IPickManager.java b/src/uk/ac/vamsas/client/picking/IPickManager.java new file mode 100644 index 0000000..6c9615d --- /dev/null +++ b/src/uk/ac/vamsas/client/picking/IPickManager.java @@ -0,0 +1,20 @@ +package uk.ac.vamsas.client.picking; + +/** + * Interface that defines the methods required for a pick manager. + */ +public interface IPickManager +{ + /** + * Sends a message. + * @param message the message to send + */ + public void sendMessage(Message message); + + /** + * 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); +} \ No newline at end of file diff --git a/src/uk/ac/vamsas/client/picking/Message.java b/src/uk/ac/vamsas/client/picking/Message.java new file mode 100644 index 0000000..1a0165d --- /dev/null +++ b/src/uk/ac/vamsas/client/picking/Message.java @@ -0,0 +1,24 @@ +package uk.ac.vamsas.client.picking; + +/** + * Abstract base class for all message types supported by the picking API. + */ +public abstract class Message +{ + protected String message; + + /** + * Constructs a new message. + */ + protected Message() + { + + } + + /** + * Returns the raw message content as a string. + * @return the raw message content as a string + */ + public String getRawMessage() + { return message; } +} \ No newline at end of file diff --git a/src/uk/ac/vamsas/client/picking/MouseOverMessage.java b/src/uk/ac/vamsas/client/picking/MouseOverMessage.java new file mode 100644 index 0000000..d14dc22 --- /dev/null +++ b/src/uk/ac/vamsas/client/picking/MouseOverMessage.java @@ -0,0 +1,38 @@ +package uk.ac.vamsas.client.picking; + +/** + * Message class that can be used to send mouse over events. + */ +public class MouseOverMessage extends Message +{ + private String vorbaID; + private int position; + + /** + * Constructs a new mouse over message. + * @param vorbaID the VAMSAS object ID of the event's source (usually an + * alignment or alignment sequence) + * @param position a position on the source in its coordinate system (ie a + * column or nucleotide/residue position) + */ + public MouseOverMessage(String vorbaID, int position) + { + this.vorbaID = vorbaID; + this.position = position; + + message = "MOUSEOVER_" + + "vorbaID=" + vorbaID + "_" + "position=" + position; + } + + /** + * Constructs a new mouse over message from its underlying string format. + * @param str the string representation of an instance of this object + * @throws java.lang.Exception if the message cannot be reconstructed + */ + MouseOverMessage(String str) + throws Exception + { + String[] elements = str.split("_"); + + } +} \ No newline at end of file diff --git a/src/uk/ac/vamsas/client/picking/PickEndPoint.java b/src/uk/ac/vamsas/client/picking/PickEndPoint.java index 951e8d4..e62c79e 100644 --- a/src/uk/ac/vamsas/client/picking/PickEndPoint.java +++ b/src/uk/ac/vamsas/client/picking/PickEndPoint.java @@ -2,17 +2,20 @@ package uk.ac.vamsas.client.picking; import java.io.*; import java.net.*; +import java.util.logging.*; class PickEndPoint extends Thread { + private Logger logger = Logger.getLogger("uk.ac.vamsas.client.picking"); + private Socket socket; private int rPort; private BufferedWriter os; private BufferedReader in; - private PickManager manager; + private SocketManager manager; - PickEndPoint(PickManager manager, Socket s) + PickEndPoint(SocketManager manager, Socket s) { this.manager = manager; socket = s; @@ -34,7 +37,7 @@ class PickEndPoint extends Thread in = new BufferedReader(new InputStreamReader(socket.getInputStream())); // Start the thread to listen for incoming messages - System.out.println("CLIENT: connection successful to port " + logger.info("CLIENT: connection successful to port " + socket.getPort() + " via port " + socket.getLocalPort()); start(); @@ -42,16 +45,18 @@ class PickEndPoint extends Thread } catch (Exception e) { - System.out.println("CLIENT: connection failed: " + e); + logger.info("CLIENT: connection failed: " + e); return false; } } - void send(String str) + void send(Message message) { try { - System.out.println("CLIENT: send " + str + " to " + rPort); + String str = message.getRawMessage(); + + logger.info("CLIENT: send " + str + " to " + rPort); os.write(str); // We use a newline to terminate the message @@ -60,7 +65,7 @@ class PickEndPoint extends Thread } catch (Exception e) { - System.out.println("CLIENT: failed to send"); + logger.info("CLIENT: failed to send"); // TODO: terminate the connection on a failed send or retry? terminate(); @@ -75,16 +80,23 @@ class PickEndPoint extends Thread while (true) { String str = in.readLine(); - System.out.println("CLIENT: recv " + str + " from " + rPort); + logger.info("CLIENT: recv " + str + " from " + rPort); + + // TODO: Spawn this off into the GUI Event-Dispatch thread... + + // Convert the string back into something API friendly + Message message = strToMessage(str); - manager.handleMessage(this, str); + // Ignore corrupted or unknown message types + if (message != null) + manager.processMessage(this, message); } } 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); + logger.info("CLIENT: read failed: " + e); terminate(); } @@ -95,7 +107,25 @@ class PickEndPoint extends Thread try { socket.close(); } catch (IOException e) {} - System.out.println("CLIENT: closing connection to port " + socket.getPort()); + logger.info("CLIENT: closing connection to port " + socket.getPort()); manager.removeEndPoint(this); } + + private Message strToMessage(String str) + { + try + { + if (str.startsWith("CUSTOM_")) + return new CustomMessage(str.substring(7)); + + if (str.startsWith("MOUSEOVER_")) + return new MouseOverMessage(str); + } + catch (Exception e) + { + logger.info("Unable to reconstruct message: " + e); + } + + return null; + } } \ 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 index 61884a3..2247049 100644 --- a/src/uk/ac/vamsas/client/picking/PickServer.java +++ b/src/uk/ac/vamsas/client/picking/PickServer.java @@ -9,7 +9,7 @@ import java.util.logging.*; */ class PickServer extends Thread { - private static Logger logger = Logger.getLogger("uk.ac.vamsas.client.picking"); + private Logger logger = Logger.getLogger("uk.ac.vamsas.client.picking"); // The port number we'll listen on static final int PORT = 53782; @@ -18,13 +18,13 @@ class PickServer extends Thread // Set to true once the server is established private boolean isServer = false; - private PickManager manager; + private SocketManager 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) + PickServer(SocketManager manager) { this.manager = manager; } @@ -63,7 +63,7 @@ class PickServer extends Thread */ public void run() { - logger.fine("SERVER: listening on " + PORT + " - SERVER"); + logger.info("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 @@ -73,7 +73,7 @@ class PickServer extends Thread try { Socket socket = serverSocket.accept(); - logger.fine("SERVER: connection detected"); + logger.info("SERVER: connection detected"); manager.addEndPoint(socket); } diff --git a/src/uk/ac/vamsas/client/picking/PickManager.java b/src/uk/ac/vamsas/client/picking/SocketManager.java similarity index 64% rename from src/uk/ac/vamsas/client/picking/PickManager.java rename to src/uk/ac/vamsas/client/picking/SocketManager.java index ee03f8a..f4f4ab7 100644 --- a/src/uk/ac/vamsas/client/picking/PickManager.java +++ b/src/uk/ac/vamsas/client/picking/SocketManager.java @@ -5,14 +5,14 @@ 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. + * 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 PickManager +public class SocketManager implements IPickManager { - private static Logger logger = Logger.getLogger("uk.ac.vamsas.client.picking"); + private 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: @@ -22,13 +22,17 @@ public class PickManager private PickServer server; + private IMessageHandler msgHandler; + /** * 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() + public SocketManager() { +// logger.setLevel(Level.OFF); + server = new PickServer(this); clients = new LinkedList(); @@ -36,6 +40,16 @@ public class PickManager } /** + * 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 @@ -43,14 +57,14 @@ public class PickManager * @param socket a socket endpoint for the connection * @return true if the connection is successfully, false otherwise */ - boolean addEndPoint(Socket socket) + synchronized 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)"); + logger.info("List now contains " + clients.size() + " client(s)"); return true; } @@ -59,11 +73,11 @@ public class PickManager /** * Sends a message to other clients. - * @param str the message to send + * @param message the message to send */ - public void sendMessage(String str) + public void sendMessage(Message message) { - forwardMessage(null, str); + forwardMessage(null, message); } /** @@ -72,9 +86,9 @@ public class PickManager * 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 + * @param message the message to send */ - private void forwardMessage(PickEndPoint origin, String str) + private void forwardMessage(PickEndPoint origin, Message message) { ListIterator itor = clients.listIterator(); while (itor.hasNext()) @@ -82,7 +96,7 @@ public class PickManager PickEndPoint client = (PickEndPoint) itor.next(); if (client != origin) - client.send(str); + client.send(message); } } @@ -90,14 +104,17 @@ public class PickManager * 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 + * @message the message that was received */ - void handleMessage(PickEndPoint origin, String str) - { + void processMessage(PickEndPoint origin, Message message) + { if (server.isServer()) - forwardMessage(origin, str); - - // TODO: pass message to VAMSAS API + forwardMessage(origin, message); + + if (msgHandler != null) + msgHandler.handleMessage(message); + else + logger.info("No handler available to deal with incoming message"); } /** @@ -105,10 +122,10 @@ public class PickManager * longer valid. * @param client the client endpoint to remove */ - void removeEndPoint(PickEndPoint client) + synchronized void removeEndPoint(PickEndPoint client) { clients.remove(client); - logger.fine("List now contains " + clients.size() + " client(s)"); + 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 @@ -123,7 +140,7 @@ public class PickManager { public void run() { - logger.fine("Initializing connection..."); + logger.info("Initializing connection..."); boolean connected = false; // Loop until we can get a connection (one way or the other) diff --git a/src/uk/ac/vamsas/client/picking/TestApp.java b/src/uk/ac/vamsas/client/picking/TestApp.java index 988939b..95b23dc 100644 --- a/src/uk/ac/vamsas/client/picking/TestApp.java +++ b/src/uk/ac/vamsas/client/picking/TestApp.java @@ -2,32 +2,36 @@ package uk.ac.vamsas.client.picking; import java.util.logging.*; -public class TestApp +/** + * Simple example of a (runnable) class that shows how to use the picking API. + */ +public class TestApp implements IMessageHandler { - 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(); + } + + public TestApp() + { + IPickManager manager = new SocketManager(); + manager.registerMessageHandler(this); - 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); + CustomMessage msg = new CustomMessage("" + rnd); + + manager.sendMessage(msg); } } - public TestApp() + public void handleMessage(Message message) { +// System.out.println("Handler received " + message.getRawMessage()); } } \ No newline at end of file -- 1.7.10.2