import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.net.ssl.SSLServerSocket;
-import javax.net.ssl.SSLServerSocketFactory;
-import javax.net.ssl.SSLSocket;
-import javax.net.ssl.SSLSocketFactory;
+import java.net.UnknownHostException;
import be.nikiroo.utils.TraceHandler;
* @author niki
*/
abstract class Server implements Runnable {
- static private final String[] ANON_CIPHERS = getAnonCiphers();
+ protected final String key;
+ protected long id = 0;
private final String name;
- private final boolean ssl;
private final Object lock = new Object();
private final Object counterLock = new Object();
private boolean exiting = false;
private int counter;
+ private long bytesReceived;
+ private long bytesSent;
+
private TraceHandler tracer = new TraceHandler();
/**
* 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)
+ * @param key
+ * an optional key to encrypt all the communications (if NULL,
+ * everything will be sent in clear text)
*
* @throws IOException
* in case of I/O error
+ * @throws UnknownHostException
+ * if the IP address of the host could not be determined
+ * @throws IllegalArgumentException
+ * if the port parameter is outside the specified range of valid
+ * port values, which is between 0 and 65535, inclusive
*/
- public Server(int port, boolean ssl) throws IOException {
- this((String) null, port, ssl);
+ public Server(int port, String key) throws IOException {
+ this((String) null, port, key);
}
/**
* Create a new server that will start listening on the network when
* {@link Server#start()} is called.
+ * <p>
+ * All the communications will happen in plain text.
*
* @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
+ * @throws UnknownHostException
+ * if the IP address of the host could not be determined
+ * @throws IllegalArgumentException
+ * if the port parameter is outside the specified range of valid
+ * port values, which is between 0 and 65535, inclusive
*/
- public Server(String name, int port, boolean ssl) throws IOException {
+ public Server(String name, int port) throws IOException {
+ this(name, port, null);
+ }
+
+ /**
+ * 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 key
+ * an optional key to encrypt all the communications (if NULL,
+ * everything will be sent in clear text)
+ *
+ * @throws IOException
+ * in case of I/O error
+ * @throws UnknownHostException
+ * if the IP address of the host could not be determined
+ * @throws IllegalArgumentException
+ * if the port parameter is outside the specified range of valid
+ * port values, which is between 0 and 65535, inclusive
+ */
+ public Server(String name, int port, String key) throws IOException {
this.name = name;
this.port = port;
- this.ssl = ssl;
- this.ss = createSocketServer(port, ssl);
+ this.key = key;
+ this.ss = new ServerSocket(port);
if (this.port == 0) {
this.port = this.ss.getLocalPort();
return port;
}
+ /**
+ * The total amount of bytes received.
+ *
+ * @return the amount of bytes received
+ */
+ public long getBytesReceived() {
+ return bytesReceived;
+ }
+
+ /**
+ * The total amount of bytes sent.
+ *
+ * @return the amount of bytes sent
+ */
+ public long getBytesSent() {
+ return bytesSent;
+ }
+
/**
* Start the server (listen on the network for new connections).
* <p>
}
try {
- tracer.trace(name + ": server starting on port " + port);
+ tracer.trace(name + ": server starting on port " + port + " ("
+ + (key != null ? "encrypted" : "plain text") + ")");
while (started && !exiting) {
count(1);
- Socket s = ss.accept();
- createConnectActionServer(s).connectAsync();
+ final Socket s = ss.accept();
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ ConnectActionServer action = null;
+ try {
+ action = createConnectActionServer(s);
+ action.connect();
+ } finally {
+ count(-1);
+ if (action != null) {
+ bytesReceived += action.getBytesReceived();
+ bytesSent += action.getBytesSent();
+ }
+ }
+ }
+ }).start();
}
// Will be covered by @link{Server#stop(long)} for timeouts
exiting = true;
try {
- new ConnectActionClientObject(createSocket(null, port, ssl))
- .connect();
+ getConnectionToMe().connect();
long time = 0;
while (ss != null && timeout > 0 && timeout > time) {
Thread.sleep(10);
}
}
}
+ }
- // only return when stopped
- while (started || exiting) {
- try {
- Thread.sleep(10);
- } catch (InterruptedException e) {
- }
+ // return only when stopped
+ while (started || exiting) {
+ try {
+ Thread.sleep(10);
+ } catch (InterruptedException e) {
}
}
}
/**
- * This method will be called on errors.
- * <p>
- * By default, it will only call the trace handler (so you may want to call
- * super {@link Server#onError} if you override it).
+ * Return a connection to this server (used by the Exit code to send an exit
+ * message).
*
- * @param e
- * the error
+ * @return the connection
+ *
+ * @throws UnknownHostException
+ * the host should always be NULL (localhost)
+ * @throws IOException
+ * in case of I/O error
*/
- protected void onError(Exception e) {
- tracer.error(e);
- }
+ abstract protected ConnectActionClient getConnectionToMe()
+ throws UnknownHostException, IOException;
/**
* Change the number of currently serviced actions.
*
* @return the current number after this operation
*/
- int count(int change) {
+ private int count(int change) {
synchronized (counterLock) {
counter += change;
return counter;
}
/**
- * 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}
+ * This method will be called on errors.
+ * <p>
+ * By default, it will only call the trace handler (so you may want to call
+ * super {@link Server#onError} if you override it).
*
- * @throws IOException
- * in case of I/O error
+ * @param e
+ * the error
*/
- static Socket createSocket(String host, int port, boolean ssl)
- throws IOException {
- Socket s;
- if (ssl) {
- s = SSLSocketFactory.getDefault().createSocket(host, port);
- ((SSLSocket) s).setEnabledCipherSuites(ANON_CIPHERS);
- } else {
- s = new Socket(host, port);
- }
-
- return s;
+ protected void onError(Exception e) {
+ tracer.error(e);
}
/**
- * 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}
+ * Return the next ID to use.
*
- * @throws IOException
- * in case of I/O error
+ * @return the next ID
*/
- static ServerSocket createSocketServer(int port, boolean ssl)
- throws IOException {
- ServerSocket ss;
- if (ssl) {
- ss = SSLServerSocketFactory.getDefault().createServerSocket(port);
- ((SSLServerSocket) ss).setEnabledCipherSuites(ANON_CIPHERS);
- } else {
- ss = new ServerSocket(port);
- }
-
- return ss;
+ protected synchronized long getNextId() {
+ return id++;
}
/**
- * Return all the supported ciphers that do not use authentication.
+ * Method called when
+ * {@link ServerObject#onRequest(ConnectActionServerObject, Object, long)}
+ * has successfully finished.
+ * <p>
+ * Can be used to know how much data was transmitted.
*
- * @return the list of such supported ciphers
+ * @param id
+ * the ID used to identify the request
+ * @param bytesReceived
+ * the bytes received during the request
+ * @param bytesSent
+ * the bytes sent during the request
*/
- private static String[] getAnonCiphers() {
- List<String> anonCiphers = new ArrayList<String>();
- for (String cipher : ((SSLSocketFactory) SSLSocketFactory.getDefault())
- .getSupportedCipherSuites()) {
- if (cipher.contains("_anon_")) {
- anonCiphers.add(cipher);
- }
- }
-
- return anonCiphers.toArray(new String[] {});
+ @SuppressWarnings("unused")
+ protected void onRequestDone(long id, long bytesReceived, long bytesSent) {
}
}