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
;
21 protected long id
= 0;
23 private final String name
;
24 private final Object lock
= new Object();
25 private final Object counterLock
= new Object();
27 private ServerSocket ss
;
30 private boolean started
;
31 private boolean exiting
= false;
34 private long bytesReceived
;
35 private long bytesSent
;
37 private TraceHandler tracer
= new TraceHandler();
40 * Create a new {@link ConnectActionServer} to handle a request.
43 * the socket to service
47 abstract ConnectActionServer
createConnectActionServer(Socket s
);
50 * Create a new server that will start listening on the network when
51 * {@link Server#start()} is called.
54 * the port to listen on, or 0 to assign any unallocated port
55 * found (which can later on be queried via
56 * {@link Server#getPort()}
58 * an optional key to encrypt all the communications (if NULL,
59 * everything will be sent in clear text)
62 * in case of I/O error
63 * @throws UnknownHostException
64 * if the IP address of the host could not be determined
65 * @throws IllegalArgumentException
66 * if the port parameter is outside the specified range of valid
67 * port values, which is between 0 and 65535, inclusive
69 public Server(int port
, String key
) throws IOException
{
70 this((String
) null, port
, key
);
74 * Create a new server that will start listening on the network when
75 * {@link Server#start()} is called.
77 * All the communications will happen in plain text.
80 * the server name (only used for debug info and traces)
82 * the port to listen on
85 * in case of I/O error
86 * @throws UnknownHostException
87 * if the IP address of the host could not be determined
88 * @throws IllegalArgumentException
89 * if the port parameter is outside the specified range of valid
90 * port values, which is between 0 and 65535, inclusive
92 public Server(String name
, int port
) throws IOException
{
93 this(name
, port
, null);
97 * Create a new server that will start listening on the network when
98 * {@link Server#start()} is called.
101 * the server name (only used for debug info and traces)
103 * the port to listen on
105 * an optional key to encrypt all the communications (if NULL,
106 * everything will be sent in clear text)
108 * @throws IOException
109 * in case of I/O error
110 * @throws UnknownHostException
111 * if the IP address of the host could not be determined
112 * @throws IllegalArgumentException
113 * if the port parameter is outside the specified range of valid
114 * port values, which is between 0 and 65535, inclusive
116 public Server(String name
, int port
, String key
) throws IOException
{
120 this.ss
= new ServerSocket(port
);
122 if (this.port
== 0) {
123 this.port
= this.ss
.getLocalPort();
128 * The traces handler for this {@link Server}.
130 * @return the traces handler
132 public TraceHandler
getTraceHandler() {
137 * The traces handler for this {@link Server}.
140 * the new traces handler
142 public void setTraceHandler(TraceHandler tracer
) {
143 if (tracer
== null) {
144 tracer
= new TraceHandler(false, false, false);
147 this.tracer
= tracer
;
151 * The name of this {@link Server} if any.
153 * Used for traces and debug purposes only.
155 * @return the name or NULL
157 public String
getName() {
162 * Return the assigned port.
164 * @return the assigned port
166 public int getPort() {
171 * The total amount of bytes received.
173 * @return the amount of bytes received
175 public long getBytesReceived() {
176 return bytesReceived
;
180 * The total amount of bytes sent.
182 * @return the amount of bytes sent
184 public long getBytesSent() {
189 * Start the server (listen on the network for new connections).
191 * Can only be called once.
193 * This call is asynchronous, and will just start a new {@link Thread} on
194 * itself (see {@link Server#run()}).
196 public void start() {
197 new Thread(this).start();
201 * Start the server (listen on the network for new connections).
203 * Can only be called once.
205 * You may call it via {@link Server#start()} for an asynchronous call, too.
209 ServerSocket ss
= null;
210 boolean alreadyStarted
= false;
211 synchronized (lock
) {
213 if (!started
&& ss
!= null) {
216 alreadyStarted
= started
;
220 if (alreadyStarted
) {
221 tracer
.error(name
+ ": cannot start server on port " + port
222 + ", it is already started");
227 tracer
.error(name
+ ": cannot start server on port " + port
228 + ", it has already been used");
233 tracer
.trace(name
+ ": server starting on port " + port
+ " ("
234 + (key
!= null ?
"encrypted" : "plain text") + ")");
236 while (started
&& !exiting
) {
238 final Socket s
= ss
.accept();
239 new Thread(new Runnable() {
242 ConnectActionServer action
= null;
244 action
= createConnectActionServer(s
);
248 if (action
!= null) {
249 bytesReceived
+= action
.getBytesReceived();
250 bytesSent
+= action
.getBytesSent();
257 // Will be covered by @link{Server#stop(long)} for timeouts
258 while (counter
> 0) {
261 } catch (Exception e
) {
268 } catch (Exception e
) {
278 tracer
.trace(name
+ ": client terminated on port " + port
);
283 * Will stop the server, synchronously and without a timeout.
286 tracer
.trace(name
+ ": stopping server");
294 * the maximum timeout to wait for existing actions to complete,
295 * or 0 for "no timeout"
297 * wait for the server to be stopped before returning
298 * (synchronous) or not (asynchronous)
300 public void stop(final long timeout
, final boolean wait
) {
304 new Thread(new Runnable() {
314 * Stop the server (synchronous).
317 * the maximum timeout to wait for existing actions to complete,
318 * or 0 for "no timeout"
320 private void stop(long timeout
) {
321 tracer
.trace(name
+ ": server stopping on port " + port
);
322 synchronized (lock
) {
323 if (started
&& !exiting
) {
327 getConnectionToMe().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 * Return a connection to this server (used by the Exit code to send an exit
355 * @return the connection
357 * @throws UnknownHostException
358 * the host should always be NULL (localhost)
359 * @throws IOException
360 * in case of I/O error
362 abstract protected ConnectActionClient
getConnectionToMe()
363 throws UnknownHostException
, IOException
;
366 * Change the number of currently serviced actions.
369 * the number to increase or decrease
371 * @return the current number after this operation
373 private int count(int change
) {
374 synchronized (counterLock
) {
381 * This method will be called on errors.
383 * By default, it will only call the trace handler (so you may want to call
384 * super {@link Server#onError} if you override it).
389 protected void onError(Exception e
) {
394 * Return the next ID to use.
396 * @return the next ID
398 protected synchronized long getNextId() {
404 * {@link ServerObject#onRequest(ConnectActionServerObject, Object, long)}
405 * has successfully finished.
407 * Can be used to know how much data was transmitted.
410 * the ID used to identify the request
411 * @param bytesReceived
412 * the bytes received during the request
414 * the bytes sent during the request
416 @SuppressWarnings("unused")
417 protected void onRequestDone(long id
, long bytesReceived
, long bytesSent
) {