X-Git-Url: http://git.nikiroo.be/?p=nikiroo-utils.git;a=blobdiff_plain;f=src%2Fbe%2Fnikiroo%2Futils%2Fserial%2FConnectAction.java;h=dfc1a2bb422138ea8acbf930a831d0c6d7a7a0da;hp=2a25c5b1c62e9f6a15778e200b04603637fa747a;hb=f157aed840bdd5b8ef04902d2326d916f71139da;hpb=08a58812f12617289463b00161c98d7c59490bf2 diff --git a/src/be/nikiroo/utils/serial/ConnectAction.java b/src/be/nikiroo/utils/serial/ConnectAction.java index 2a25c5b..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; + + /** + * 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); - // server = version NULL + /** + * 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,18 +82,22 @@ abstract class ConnectAction { } else { this.version = version; } + + clientVersion = new Version(); } - public void connectAsync() { - new Thread(new Runnable() { - @Override - public void run() { - connect(); - } - }).start(); + /** + * The version of this client-or-server. + * + * @return the version + */ + public Version getVersion() { + return version; } - // connect, do the action (sync) + /** + * Actually start the process (this is synchronous). + */ public void connect() { try { in = new BufferedReader(new InputStreamReader(s.getInputStream(), @@ -51,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 ")) { @@ -77,9 +132,30 @@ 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)); if (rep != null) { @@ -90,37 +166,55 @@ abstract class ConnectAction { } } - 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(); } /** - * Handler called when the client {@link Version} is received. + * Send the given string to the counter part (and, only for client, return + * the answer -- the server will always receive NULL). * - * @param clientVersion - * the client {@link Version} - */ - protected void onClientVersionReceived( - @SuppressWarnings("unused") Version clientVersion) { - } - - /** - * Handler called when an unexpected error occurs in the code. + * @param line + * the data to send (we will add a line feed) * - * @param e - * the exception that occurred + * @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 void onError(@SuppressWarnings("unused") Exception e) { - } - - // \n included in line, but not in rep (also, server never get anything) - private String sendString(String line) throws IOException { + protected String sendString(String line) throws IOException { synchronized (lock) { out.write(line); out.write("\n"); @@ -135,8 +229,21 @@ abstract class ConnectAction { } } - // 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) { @@ -149,8 +256,12 @@ 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(); }