1 package be
.nikiroo
.utils
.serial
.server
;
3 import java
.io
.IOException
;
4 import java
.net
.ServerSocket
;
5 import java
.net
.Socket
;
6 import java
.util
.ArrayList
;
9 import javax
.net
.ssl
.SSLServerSocket
;
10 import javax
.net
.ssl
.SSLServerSocketFactory
;
11 import javax
.net
.ssl
.SSLSocket
;
12 import javax
.net
.ssl
.SSLSocketFactory
;
14 import be
.nikiroo
.utils
.TraceHandler
;
17 * This class implements a simple server that can listen for connections and
18 * send/receive objects.
20 * Note: this {@link Server} has to be discarded after use (cannot be started
25 abstract class Server
implements Runnable
{
26 static private final String
[] ANON_CIPHERS
= getAnonCiphers();
28 private final String name
;
29 private final boolean ssl
;
30 private final Object lock
= new Object();
31 private final Object counterLock
= new Object();
33 private ServerSocket ss
;
36 private boolean started
;
37 private boolean exiting
= false;
40 private TraceHandler tracer
= new TraceHandler();
43 * Create a new {@link ConnectActionServer} to handle a request.
46 * the socket to service
50 abstract ConnectActionServer
createConnectActionServer(Socket s
);
53 * Create a new server that will start listening on the network when
54 * {@link Server#start()} is called.
57 * the port to listen on, or 0 to assign any unallocated port
58 * found (which can later on be queried via
59 * {@link Server#getPort()}
61 * use a SSL connection (or not)
64 * in case of I/O error
66 public Server(int port
, boolean ssl
) throws IOException
{
67 this((String
) null, port
, ssl
);
71 * Create a new server that will start listening on the network when
72 * {@link Server#start()} is called.
75 * the server name (only used for debug info and traces)
77 * the port to listen on
79 * use a SSL connection (or not)
82 * in case of I/O error
84 public Server(String name
, int port
, boolean ssl
) throws IOException
{
88 this.ss
= createSocketServer(port
, ssl
);
91 this.port
= this.ss
.getLocalPort();
96 * The traces handler for this {@link Server}.
98 * @return the traces handler
100 public TraceHandler
getTraceHandler() {
105 * The traces handler for this {@link Server}.
108 * the new traces handler
110 public void setTraceHandler(TraceHandler tracer
) {
111 if (tracer
== null) {
112 tracer
= new TraceHandler(false, false, false);
115 this.tracer
= tracer
;
119 * The name of this {@link Server} if any.
121 * Used for traces and debug purposes only.
123 * @return the name or NULL
125 public String
getName() {
130 * Return the assigned port.
132 * @return the assigned port
134 public int getPort() {
139 * Start the server (listen on the network for new connections).
141 * Can only be called once.
143 * This call is asynchronous, and will just start a new {@link Thread} on
144 * itself (see {@link Server#run()}).
146 public void start() {
147 new Thread(this).start();
151 * Start the server (listen on the network for new connections).
153 * Can only be called once.
155 * You may call it via {@link Server#start()} for an asynchronous call, too.
159 ServerSocket ss
= null;
160 boolean alreadyStarted
= false;
161 synchronized (lock
) {
163 if (!started
&& ss
!= null) {
166 alreadyStarted
= started
;
170 if (alreadyStarted
) {
171 tracer
.error(name
+ ": cannot start server on port " + port
172 + ", it is already started");
177 tracer
.error(name
+ ": cannot start server on port " + port
178 + ", it has already been used");
183 tracer
.trace(name
+ ": server starting on port " + port
);
185 while (started
&& !exiting
) {
187 final Socket s
= ss
.accept();
188 new Thread(new Runnable() {
192 createConnectActionServer(s
).connect();
200 // Will be covered by @link{Server#stop(long)} for timeouts
201 while (counter
> 0) {
204 } catch (Exception e
) {
211 } catch (Exception e
) {
221 tracer
.trace(name
+ ": client terminated on port " + port
);
226 * Will stop the server, synchronously and without a timeout.
229 tracer
.trace(name
+ ": stopping server");
237 * the maximum timeout to wait for existing actions to complete,
238 * or 0 for "no timeout"
240 * wait for the server to be stopped before returning
241 * (synchronous) or not (asynchronous)
243 public void stop(final long timeout
, final boolean wait
) {
247 new Thread(new Runnable() {
257 * Stop the server (synchronous).
260 * the maximum timeout to wait for existing actions to complete,
261 * or 0 for "no timeout"
263 private void stop(long timeout
) {
264 tracer
.trace(name
+ ": server stopping on port " + port
);
265 synchronized (lock
) {
266 if (started
&& !exiting
) {
270 new ConnectActionClientObject(createSocket(null, port
, ssl
))
273 while (ss
!= null && timeout
> 0 && timeout
> time
) {
277 } catch (Exception e
) {
279 counter
= 0; // will stop the main thread
285 // only return when stopped
286 while (started
|| exiting
) {
289 } catch (InterruptedException e
) {
296 * Change the number of currently serviced actions.
299 * the number to increase or decrease
301 * @return the current number after this operation
303 private int count(int change
) {
304 synchronized (counterLock
) {
311 * This method will be called on errors.
313 * By default, it will only call the trace handler (so you may want to call
314 * super {@link Server#onError} if you override it).
319 protected void onError(Exception e
) {
324 * Create a {@link Socket}.
327 * the host to connect to
329 * the port to connect to
331 * TRUE for SSL mode (or FALSE for plain text mode)
333 * @return the {@link Socket}
335 * @throws IOException
336 * in case of I/O error
338 static Socket
createSocket(String host
, int port
, boolean ssl
)
342 s
= SSLSocketFactory
.getDefault().createSocket(host
, port
);
343 ((SSLSocket
) s
).setEnabledCipherSuites(ANON_CIPHERS
);
345 s
= new Socket(host
, port
);
352 * Create a {@link ServerSocket}.
355 * the port to accept connections on
357 * TRUE for SSL mode (or FALSE for plain text mode)
359 * @return the {@link ServerSocket}
361 * @throws IOException
362 * in case of I/O error
364 static ServerSocket
createSocketServer(int port
, boolean ssl
)
368 ss
= SSLServerSocketFactory
.getDefault().createServerSocket(port
);
369 ((SSLServerSocket
) ss
).setEnabledCipherSuites(ANON_CIPHERS
);
371 ss
= new ServerSocket(port
);
378 * Return all the supported ciphers that do not use authentication.
380 * @return the list of such supported ciphers
382 private static String
[] getAnonCiphers() {
383 List
<String
> anonCiphers
= new ArrayList
<String
>();
384 for (String cipher
: ((SSLSocketFactory
) SSLSocketFactory
.getDefault())
385 .getSupportedCipherSuites()) {
386 if (cipher
.contains("_anon_")) {
387 anonCiphers
.add(cipher
);
391 return anonCiphers
.toArray(new String
[] {});