New Server class to send/rec objects via network
[nikiroo-utils.git] / src / be / nikiroo / utils / serial / Server.java
1 package be.nikiroo.utils.serial;
2
3 import java.io.IOException;
4 import java.net.ServerSocket;
5 import java.net.Socket;
6 import java.util.ArrayList;
7 import java.util.List;
8
9 import javax.net.ssl.SSLServerSocket;
10 import javax.net.ssl.SSLServerSocketFactory;
11 import javax.net.ssl.SSLSocket;
12 import javax.net.ssl.SSLSocketFactory;
13
14 import be.nikiroo.utils.Version;
15
16 abstract public class Server implements Runnable {
17 static private final String[] ANON_CIPHERS = getAnonCiphers();
18
19 private Version serverVersion = new Version();
20 private int port;
21 private boolean ssl;
22 private ServerSocket ss;
23 private boolean started;
24 private boolean exiting = false;
25 private int counter;
26 private Object lock = new Object();
27 private Object counterLock = new Object();
28
29 public Server(Version version, int port, boolean ssl) throws IOException {
30 this.serverVersion = version;
31 this.port = port;
32 this.ssl = ssl;
33 this.ss = createSocketServer(port, ssl);
34 }
35
36 public void start() {
37 synchronized (lock) {
38 if (!started) {
39 started = true;
40 new Thread(this).start();
41 }
42 }
43 }
44
45 public void stop() {
46 stop(0, true);
47 }
48
49 // wait = wait before returning (sync VS async) timeout in ms, 0 or -1 for
50 // never
51 public void stop(final long timeout, final boolean wait) {
52 if (wait) {
53 stop(timeout);
54 } else {
55 new Thread(new Runnable() {
56 public void run() {
57 stop(timeout);
58 }
59 }).start();
60 }
61 }
62
63 // timeout in ms, 0 or -1 or never
64 private void stop(long timeout) {
65 synchronized (lock) {
66 if (started && !exiting) {
67 exiting = true;
68
69 try {
70 new ConnectActionClient(createSocket(null, port, ssl)) {
71 @Override
72 public void action(Version serverVersion)
73 throws Exception {
74 }
75 }.connect();
76
77 long time = 0;
78 while (ss != null && timeout > 0 && timeout > time) {
79 Thread.sleep(10);
80 time += 10;
81 }
82 } catch (Exception e) {
83 if (ss != null) {
84 counter = 0; // will stop the main thread
85 onError(e);
86 }
87 }
88 }
89
90 // only return when stopped
91 while (started || exiting) {
92 try {
93 Thread.sleep(10);
94 } catch (InterruptedException e) {
95 }
96 }
97 }
98 }
99
100 public void run() {
101 try {
102 while (started && !exiting) {
103 count(1);
104 Socket s = ss.accept();
105 new ConnectActionServer(s) {
106 private Version clientVersion = new Version();
107
108 @Override
109 public void action(Version dummy) throws Exception {
110 try {
111 for (Object data = flush(); true; data = flush()) {
112 Object rep = null;
113 try {
114 rep = onRequest(this, clientVersion, data);
115 } catch (Exception e) {
116 onError(e);
117 }
118 send(rep);
119 }
120 } catch (NullPointerException e) {
121 // Client has no data any more, we quit
122 }
123 }
124
125 @Override
126 public void connect() {
127 try {
128 super.connect();
129 } finally {
130 count(-1);
131 }
132 };
133
134 @Override
135 protected void onClientVersionReceived(Version clientVersion) {
136 this.clientVersion = clientVersion;
137 };
138 }.connectAsync();
139 }
140
141 // Will be covered by @link{Server#stop(long)} for timeouts
142 while (counter > 0) {
143 Thread.sleep(10);
144 }
145 } catch (Exception e) {
146 if (counter > 0) {
147 onError(e);
148 }
149 } finally {
150 try {
151 ss.close();
152 } catch (Exception e) {
153 onError(e);
154 }
155
156 ss = null;
157
158 started = false;
159 exiting = false;
160 counter = 0;
161 }
162 }
163
164 abstract protected Object onRequest(ConnectActionServer action,
165 Version clientVersion, Object data) throws Exception;
166
167 protected void onError(Exception e) {
168 if (e == null) {
169 e = new Exception("Unknown error");
170 }
171
172 e.printStackTrace();
173 }
174
175 private int count(int change) {
176 synchronized (counterLock) {
177 counter += change;
178 return counter;
179 }
180 }
181
182 static Socket createSocket(String host, int port, boolean ssl)
183 throws IOException {
184 Socket s;
185 if (ssl) {
186 s = SSLSocketFactory.getDefault().createSocket(host, port);
187 ((SSLSocket) s).setEnabledCipherSuites(ANON_CIPHERS);
188 } else {
189 s = new Socket(host, port);
190 }
191
192 return s;
193 }
194
195 static ServerSocket createSocketServer(int port, boolean ssl)
196 throws IOException {
197 ServerSocket ss;
198 if (ssl) {
199 ss = SSLServerSocketFactory.getDefault().createServerSocket(port);
200 ((SSLServerSocket) ss).setEnabledCipherSuites(ANON_CIPHERS);
201 } else {
202 ss = new ServerSocket(port);
203 }
204
205 return ss;
206 }
207
208 private static String[] getAnonCiphers() {
209 List<String> anonCiphers = new ArrayList<String>();
210 for (String cipher : ((SSLSocketFactory) SSLSocketFactory.getDefault())
211 .getSupportedCipherSuites()) {
212 if (cipher.contains("_anon_")) {
213 anonCiphers.add(cipher);
214 }
215 }
216
217 return anonCiphers.toArray(new String[] {});
218 }
219 }