X-Git-Url: http://git.nikiroo.be/?a=blobdiff_plain;f=src%2Fbe%2Fnikiroo%2Futils%2Fserial%2FConnectAction.java;h=dfc1a2bb422138ea8acbf930a831d0c6d7a7a0da;hb=f157aed840bdd5b8ef04902d2326d916f71139da;hp=ec49a1dd7f3c235c32614c0c04bee9db902e1166;hpb=ce0974c4b695f842fa7ec81f3c53d016d1959854;p=nikiroo-utils.git diff --git a/src/be/nikiroo/utils/serial/ConnectAction.java b/src/be/nikiroo/utils/serial/ConnectAction.java index ec49a1d..dfc1a2b 100644 --- a/src/be/nikiroo/utils/serial/ConnectAction.java +++ b/src/be/nikiroo/utils/serial/ConnectAction.java @@ -8,20 +8,71 @@ import java.net.Socket; import be.nikiroo.utils.Version; +/** + * Base class used for the client/server basic handling. + *

+ * It represents a single action: a client is expected to only execute one + * action, while a server is expected to execute one action for each client + * action. + * + * @author niki + */ abstract class ConnectAction { private Socket s; private boolean server; private Version version; + private Version clientVersion; private Object lock = new Object(); private BufferedReader in; private OutputStreamWriter out; private boolean contentToSend; - // serverVersion = null on server (or bad clients) - abstract public void action(Version serverVersion) throws Exception; + /** + * Method that will be called when an action is performed on either the + * client or server this {@link ConnectAction} represent. + * + * @param version + * the counter part version + * + * @throws Exception + * in case of I/O error + */ + abstract protected void action(Version version) throws Exception; - // server = version NULL + /** + * Method called when we negotiate the version with the client. + *

+ * Thus, it is only called on the server. + *

+ * Will return the actual server version by default. + * + * @param clientVersion + * the client version + * + * @return the version to send to the client + */ + abstract protected Version negotiateVersion(Version clientVersion); + + /** + * Handler called when an unexpected error occurs in the code. + * + * @param e + * the exception that occurred + */ + abstract protected void onError(Exception e); + + /** + * Create a new {@link ConnectAction}. + * + * @param s + * the socket to bind to + * @param server + * TRUE for a server action, FALSE for a client action (will + * impact the process) + * @param version + * the version of this client-or-server + */ protected ConnectAction(Socket s, boolean server, Version version) { this.s = s; this.server = server; @@ -31,16 +82,22 @@ abstract class ConnectAction { } else { this.version = version; } + + clientVersion = new Version(); } - public void connectAsync() { - new Thread(new Runnable() { - public void run() { - connect(); - } - }).start(); + /** + * The version of this client-or-server. + * + * @return the version + */ + public Version getVersion() { + return version; } + /** + * Actually start the process (this is synchronous). + */ public void connect() { try { in = new BufferedReader(new InputStreamReader(s.getInputStream(), @@ -49,7 +106,7 @@ abstract class ConnectAction { out = new OutputStreamWriter(s.getOutputStream(), "UTF-8"); try { if (server) { - action(version); + action(clientVersion); } else { String v = sendString("VERSION " + version.toString()); if (v != null && v.startsWith("VERSION ")) { @@ -75,35 +132,89 @@ abstract class ConnectAction { } } - // (also, server never get anything) - public Object send(Object data) throws IOException, NoSuchFieldException, - NoSuchMethodException, ClassNotFoundException { + /** + * Serialise and send the given object to the counter part (and, only for + * client, return the deserialised answer -- the server will always receive + * NULL). + * + * @param data + * the data to send + * + * @return the answer (which can be NULL) if this action is a client, always + * NULL if it is a server + * + * @throws IOException + * in case of I/O error + * @throws NoSuchFieldException + * if the serialised data contains information about a field + * which does actually not exist in the class we know of + * @throws NoSuchMethodException + * if a class described in the serialised data cannot be created + * because it is not compatible with this code + * @throws ClassNotFoundException + * if a class described in the serialised data cannot be found + */ + protected Object send(Object data) throws IOException, + NoSuchFieldException, NoSuchMethodException, ClassNotFoundException { synchronized (lock) { String rep = sendString(new Exporter().append(data).toString(true)); - return new Importer().read(rep).getValue(); + if (rep != null) { + return new Importer().read(rep).getValue(); + } + + return null; } } - public Object flush() throws NoSuchFieldException, NoSuchMethodException, - ClassNotFoundException, IOException, java.lang.NullPointerException { + /** + * Reserved for the server: flush the data to the client and retrieve its + * answer. + *

+ * Also used internally for the client (only do something if there is + * contentToSend). + *

+ * Will only flush the data if there is contentToSend. + * + * @return the deserialised answer (which can actually be NULL) + * + * @throws IOException + * in case of I/O error + * @throws NoSuchFieldException + * if the serialised data contains information about a field + * which does actually not exist in the class we know of + * @throws NoSuchMethodException + * if a class described in the serialised data cannot be created + * because it is not compatible with this code + * @throws ClassNotFoundException + * if a class described in the serialised data cannot be found + * @throws java.lang.NullPointerException + * if the counter part has no data to send + */ + protected Object rec() throws IOException, NoSuchFieldException, + NoSuchMethodException, ClassNotFoundException, + java.lang.NullPointerException { String str = flushString(); if (str == null) { - throw new NullPointerException("No more data from client"); + throw new NullPointerException("No more data available"); } return new Importer().read(str).getValue(); } - protected void onClientVersionReceived(Version clientVersion) { - - } - - protected void onError(Exception e) { - - } - - // \n included in line, but not in rep (also, server never get anything) - private String sendString(String line) throws IOException { + /** + * Send the given string to the counter part (and, only for client, return + * the answer -- the server will always receive NULL). + * + * @param line + * the data to send (we will add a line feed) + * + * @return the answer if this action is a client (without the added line + * feed), NULL if it is a server + * + * @throws IOException + * in case of I/O error + */ + protected String sendString(String line) throws IOException { synchronized (lock) { out.write(line); out.write("\n"); @@ -111,15 +222,28 @@ abstract class ConnectAction { if (server) { out.flush(); return null; - } else { - contentToSend = true; - return flushString(); } + + contentToSend = true; + return flushString(); } } - // server can receive something even without pending content - private String flushString() throws IOException { + /** + * Reserved for the server (externally): flush the data to the client and + * retrieve its answer. + *

+ * Also used internally for the client (only do something if there is + * contentToSend). + *

+ * Will only flush the data if there is contentToSend. + * + * @return the answer (which can be NULL) + * + * @throws IOException + * in case of I/O error + */ + protected String flushString() throws IOException { synchronized (lock) { if (server || contentToSend) { if (contentToSend) { @@ -132,16 +256,20 @@ abstract class ConnectAction { // "VERSION client-version" (VERSION 1.0.0) Version clientVersion = new Version( line.substring("VERSION ".length())); - onClientVersionReceived(clientVersion); - sendString("VERSION " + version.toString()); + this.clientVersion = clientVersion; + Version v = negotiateVersion(clientVersion); + if (v == null) { + v = new Version(); + } + sendString("VERSION " + v.toString()); line = in.readLine(); } return line; - } else { - return null; } + + return null; } } } \ No newline at end of file