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