X-Git-Url: http://git.nikiroo.be/?a=blobdiff_plain;f=src%2Fbe%2Fnikiroo%2Futils%2Fserial%2FServer.java;h=e15680e5862d799291a732c52e7877b5fcad6c7a;hb=08a58812f12617289463b00161c98d7c59490bf2;hp=bc4e637a7d65d0e8bbdc154c6b45c32b0197a80f;hpb=530d4062471346d6ececf76d74a0358c91323998;p=fanfix.git diff --git a/src/be/nikiroo/utils/serial/Server.java b/src/be/nikiroo/utils/serial/Server.java index bc4e637..e15680e 100644 --- a/src/be/nikiroo/utils/serial/Server.java +++ b/src/be/nikiroo/utils/serial/Server.java @@ -11,19 +11,34 @@ import javax.net.ssl.SSLServerSocketFactory; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; +import be.nikiroo.utils.TraceHandler; import be.nikiroo.utils.Version; +/** + * This class implements a simple server that can listen for connections and + * send/receive objects. + *

+ * Note: this {@link Server} has to be discarded after use (cannot be started + * twice). + * + * @author niki + */ abstract public class Server implements Runnable { static private final String[] ANON_CIPHERS = getAnonCiphers(); - private int port; - private boolean ssl; + private final String name; + private final boolean ssl; + private final Object lock = new Object(); + private final Object counterLock = new Object(); + private ServerSocket ss; + private int port; + private boolean started; private boolean exiting = false; private int counter; - private Object lock = new Object(); - private Object counterLock = new Object(); + + private TraceHandler tracer = new TraceHandler(); @Deprecated public Server(@SuppressWarnings("unused") Version notUsed, int port, @@ -31,27 +46,123 @@ abstract public class Server implements Runnable { this(port, ssl); } + /** + * Create a new server that will start listening on the network when + * {@link Server#start()} is called. + * + * @param port + * the port to listen on, or 0 to assign any unallocated port + * found (which can later on be queried via + * {@link Server#getPort()} + * @param ssl + * use a SSL connection (or not) + * + * @throws IOException + * in case of I/O error + */ public Server(int port, boolean ssl) throws IOException { + this((String) null, port, ssl); + } + + /** + * Create a new server that will start listening on the network when + * {@link Server#start()} is called. + * + * @param name + * the server name (only used for debug info and traces) + * @param port + * the port to listen on + * @param ssl + * use a SSL connection (or not) + * + * @throws IOException + * in case of I/O error + */ + public Server(String name, int port, boolean ssl) throws IOException { + this.name = name; this.port = port; this.ssl = ssl; this.ss = createSocketServer(port, ssl); + + if (this.port == 0) { + this.port = this.ss.getLocalPort(); + } + } + + /** + * The traces handler for this {@link Server}. + * + * @return the traces handler + */ + public TraceHandler getTraceHandler() { + return tracer; } + /** + * The traces handler for this {@link Server}. + * + * @param tracer + * the new traces handler + */ + public void setTraceHandler(TraceHandler tracer) { + if (tracer == null) { + tracer = new TraceHandler(false, false, false); + } + + this.tracer = tracer; + } + + /** + * Return the assigned port. + */ + public int getPort() { + return port; + } + + /** + * Start the server (listen on the network for new connections). + *

+ * Can only be called once. + */ public void start() { + boolean ok = false; synchronized (lock) { - if (!started) { + if (!started && ss != null) { started = true; new Thread(this).start(); + ok = true; } } + + if (ok) { + tracer.trace(name + ": server started on port " + port); + } else if (ss == null) { + tracer.error(name + ": cannot start server on port " + port + + ", it has already been used"); + } else { + tracer.error(name + ": cannot start server on port " + port + + ", it is already started"); + } } + /** + * Will stop the server, synchronously and without a timeout. + */ public void stop() { + tracer.trace(name + ": stopping server"); stop(0, true); } - // wait = wait before returning (sync VS async) timeout in ms, 0 or -1 for - // never + /** + * Stop the server. + * + * @param timeout + * the maximum timeout to wait for existing actions to complete, + * or 0 for "no timeout" + * @param wait + * wait for the server to be stopped before returning + * (synchronous) or not (asynchronous) + */ public void stop(final long timeout, final boolean wait) { if (wait) { stop(timeout); @@ -65,7 +176,13 @@ abstract public class Server implements Runnable { } } - // timeout in ms, 0 or -1 or never + /** + * Stop the server (synchronous). + * + * @param timeout + * the maximum timeout to wait for existing actions to complete, + * or 0 for "no timeout" + */ private void stop(long timeout) { synchronized (lock) { if (started && !exiting) { @@ -105,6 +222,7 @@ abstract public class Server implements Runnable { @Override public void run() { try { + tracer.trace(name + ": server starting on port " + port); while (started && !exiting) { count(1); Socket s = ss.accept(); @@ -167,17 +285,47 @@ abstract public class Server implements Runnable { } } + /** + * This is the method that is called on each client request. + *

+ * You are expected to react to it and return an answer (which can be NULL). + * + * @param action + * the client action + * @param clientVersion + * the client version + * @param data + * the data sent by the client (which can be NULL) + * + * @return the answer to return to the client (which can be NULL) + * + * @throws Exception + * in case of an exception, the error will only be logged + */ abstract protected Object onRequest(ConnectActionServer action, Version clientVersion, Object data) throws Exception; + /** + * This method will be called on errors. + *

+ * By default, it will only call the trace handler (so you may want to call + * super {@link Server#onError} if you override it). + * + * @param e + * the error + */ protected void onError(Exception e) { - if (e == null) { - e = new Exception("Unknown error"); - } - - e.printStackTrace(); + tracer.error(e); } + /** + * Change the number of currently serviced actions. + * + * @param change + * the number to increase or decrease + * + * @return the current number after this operation + */ private int count(int change) { synchronized (counterLock) { counter += change; @@ -185,6 +333,21 @@ abstract public class Server implements Runnable { } } + /** + * Create a {@link Socket}. + * + * @param host + * the host to connect to + * @param port + * the port to connect to + * @param ssl + * TRUE for SSL mode (or FALSE for plain text mode) + * + * @return the {@link Socket} + * + * @throws IOException + * in case of I/O error + */ static Socket createSocket(String host, int port, boolean ssl) throws IOException { Socket s; @@ -198,6 +361,19 @@ abstract public class Server implements Runnable { return s; } + /** + * Create a {@link ServerSocket}. + * + * @param port + * the port to accept connections on + * @param ssl + * TRUE for SSL mode (or FALSE for plain text mode) + * + * @return the {@link ServerSocket} + * + * @throws IOException + * in case of I/O error + */ static ServerSocket createSocketServer(int port, boolean ssl) throws IOException { ServerSocket ss; @@ -211,6 +387,11 @@ abstract public class Server implements Runnable { return ss; } + /** + * Return all the supported ciphers that do not use authentication. + * + * @return the list of such supported ciphers + */ private static String[] getAnonCiphers() { List anonCiphers = new ArrayList(); for (String cipher : ((SSLSocketFactory) SSLSocketFactory.getDefault())