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
.net
.UnknownHostException
;
8 import be
.nikiroo
.utils
.TraceHandler
;
11 * This class implements a simple server that can listen for connections and
12 * send/receive objects.
14 * Note: this {@link Server} has to be discarded after use (cannot be started
19 abstract class Server
implements Runnable
{
20 protected final String key
;
22 private final String name
;
23 private final Object lock
= new Object();
24 private final Object counterLock
= new Object();
26 private ServerSocket ss
;
29 private boolean started
;
30 private boolean exiting
= false;
33 private long bytesReceived
;
34 private long bytesSent
;
36 private TraceHandler tracer
= new TraceHandler();
39 * Create a new {@link ConnectActionServer} to handle a request.
42 * the socket to service
46 abstract ConnectActionServer
createConnectActionServer(Socket s
);
49 * Create a new server that will start listening on the network when
50 * {@link Server#start()} is called.
53 * the port to listen on, or 0 to assign any unallocated port
54 * found (which can later on be queried via
55 * {@link Server#getPort()}
57 * an optional key to encrypt all the communications (if NULL,
58 * everything will be sent in clear text)
61 * in case of I/O error
62 * @throws UnknownHostException
63 * if the IP address of the host could not be determined
64 * @throws IllegalArgumentException
65 * if the port parameter is outside the specified range of valid
66 * port values, which is between 0 and 65535, inclusive
68 public Server(int port
, String key
) throws IOException
{
69 this((String
) null, port
, key
);
73 * Create a new server that will start listening on the network when
74 * {@link Server#start()} is called.
76 * All the communications will happen in plain text.
79 * the server name (only used for debug info and traces)
81 * the port to listen on
84 * in case of I/O error
85 * @throws UnknownHostException
86 * if the IP address of the host could not be determined
87 * @throws IllegalArgumentException
88 * if the port parameter is outside the specified range of valid
89 * port values, which is between 0 and 65535, inclusive
91 public Server(String name
, int port
) throws IOException
{
92 this(name
, port
, null);
96 * Create a new server that will start listening on the network when
97 * {@link Server#start()} is called.
100 * the server name (only used for debug info and traces)
102 * the port to listen on
104 * an optional key to encrypt all the communications (if NULL,
105 * everything will be sent in clear text)
107 * @throws IOException
108 * in case of I/O error
109 * @throws UnknownHostException
110 * if the IP address of the host could not be determined
111 * @throws IllegalArgumentException
112 * if the port parameter is outside the specified range of valid
113 * port values, which is between 0 and 65535, inclusive
115 public Server(String name
, int port
, String key
) throws IOException
{
119 this.ss
= new ServerSocket(port
);
121 if (this.port
== 0) {
122 this.port
= this.ss
.getLocalPort();
127 * The traces handler for this {@link Server}.
129 * @return the traces handler
131 public TraceHandler
getTraceHandler() {
136 * The traces handler for this {@link Server}.
139 * the new traces handler
141 public void setTraceHandler(TraceHandler tracer
) {
142 if (tracer
== null) {
143 tracer
= new TraceHandler(false, false, false);
146 this.tracer
= tracer
;
150 * The name of this {@link Server} if any.
152 * Used for traces and debug purposes only.
154 * @return the name or NULL
156 public String
getName() {
161 * Return the assigned port.
163 * @return the assigned port
165 public int getPort() {
170 * The total amount of bytes received.
172 * @return the amount of bytes received
174 public long getBytesReceived() {
175 return bytesReceived
;
179 * The total amount of bytes sent.
181 * @return the amount of bytes sent
183 public long getBytesSent() {
188 * Start the server (listen on the network for new connections).
190 * Can only be called once.
192 * This call is asynchronous, and will just start a new {@link Thread} on
193 * itself (see {@link Server#run()}).
195 public void start() {
196 new Thread(this).start();
200 * Start the server (listen on the network for new connections).
202 * Can only be called once.
204 * You may call it via {@link Server#start()} for an asynchronous call, too.
208 ServerSocket ss
= null;
209 boolean alreadyStarted
= false;
210 synchronized (lock
) {
212 if (!started
&& ss
!= null) {
215 alreadyStarted
= started
;
219 if (alreadyStarted
) {
220 tracer
.error(name
+ ": cannot start server on port " + port
221 + ", it is already started");
226 tracer
.error(name
+ ": cannot start server on port " + port
227 + ", it has already been used");
232 tracer
.trace(name
+ ": server starting on port " + port
+ " ("
233 + (key
!= null ?
"encrypted" : "plain text") + ")");
235 while (started
&& !exiting
) {
237 final Socket s
= ss
.accept();
238 new Thread(new Runnable() {
241 ConnectActionServer action
= null;
243 action
= createConnectActionServer(s
);
247 if (action
!= null) {
248 bytesReceived
+= action
.getBytesReceived();
249 bytesSent
+= action
.getBytesSent();
256 // Will be covered by @link{Server#stop(long)} for timeouts
257 while (counter
> 0) {
260 } catch (Exception e
) {
267 } catch (Exception e
) {
277 tracer
.trace(name
+ ": client terminated on port " + port
);
282 * Will stop the server, synchronously and without a timeout.
285 tracer
.trace(name
+ ": stopping server");
293 * the maximum timeout to wait for existing actions to complete,
294 * or 0 for "no timeout"
296 * wait for the server to be stopped before returning
297 * (synchronous) or not (asynchronous)
299 public void stop(final long timeout
, final boolean wait
) {
303 new Thread(new Runnable() {
313 * Stop the server (synchronous).
316 * the maximum timeout to wait for existing actions to complete,
317 * or 0 for "no timeout"
319 private void stop(long timeout
) {
320 tracer
.trace(name
+ ": server stopping on port " + port
);
321 synchronized (lock
) {
322 if (started
&& !exiting
) {
326 new ConnectActionClientObject(new Socket((String
) null,
327 port
), key
).connect();
329 while (ss
!= null && timeout
> 0 && timeout
> time
) {
333 } catch (Exception e
) {
335 counter
= 0; // will stop the main thread
342 // return only when stopped
343 while (started
|| exiting
) {
346 } catch (InterruptedException e
) {
352 * Change the number of currently serviced actions.
355 * the number to increase or decrease
357 * @return the current number after this operation
359 private int count(int change
) {
360 synchronized (counterLock
) {
367 * This method will be called on errors.
369 * By default, it will only call the trace handler (so you may want to call
370 * super {@link Server#onError} if you override it).
375 protected void onError(Exception e
) {
380 * Create a {@link Socket}.
383 * the host to connect to
385 * the port to connect to
387 * @return the {@link Socket}
389 * @throws IOException
390 * in case of I/O error
391 * @throws UnknownHostException
392 * if the host is not known
393 * @throws IllegalArgumentException
394 * if the port parameter is outside the specified range of valid
395 * port values, which is between 0 and 65535, inclusive
398 static Socket
createSocket(String host
, int port
) throws IOException
{
399 return new Socket(host
, port
);
403 * Create a {@link ServerSocket}.
406 * the port to accept connections on
408 * @return the {@link ServerSocket}
410 * @throws IOException
411 * in case of I/O error
412 * @throws UnknownHostException
413 * if the IP address of the host could not be determined
414 * @throws IllegalArgumentException
415 * if the port parameter is outside the specified range of valid
416 * port values, which is between 0 and 65535, inclusive
419 static ServerSocket
createSocketServer(int port
) throws IOException
{
420 return new ServerSocket(port
);