|
1 import java.util.*; |
|
2 import java.io.*; |
|
3 import java.net.*; |
|
4 import javax.net.ssl.*; |
|
5 import java.security.*; |
|
6 import java.nio.*; |
|
7 import java.nio.channels.*; |
|
8 import java.awt.*; |
|
9 import java.awt.image.*; |
|
10 import java.awt.event.*; |
|
11 import javax.swing.*; |
|
12 import javax.swing.border.*; |
|
13 |
|
14 class Debug { |
|
15 static int debug; |
|
16 String debid = ""; |
|
17 Debug(Debug d) { debid = d.debid; } |
|
18 Debug(String debid) { this.debid = debid; } |
|
19 Debug() {} |
|
20 static void pRe(Object o) { |
|
21 System.err.println((new Date().getTime()) + " " + o); |
|
22 System.err.flush(); |
|
23 } |
|
24 void log(int level, Object o) { |
|
25 if(debug == 7) { if(level == 7) pRe(debid + ": " + o); } |
|
26 else if(debug >= level) pRe(debid + ": " + o); |
|
27 } |
|
28 boolean abendMsg(String msg, Exception x) { |
|
29 log(0, "ABEND: " + msg + (x == null ? "" : (": " + x.getClass().getSimpleName() + ": " + x.getMessage()))); |
|
30 return false; |
|
31 } |
|
32 String prBuf(ByteBuffer b) { return b.position() + "/" + b.remaining() + "/" + b.limit(); } |
|
33 } |
|
34 class Data extends Debug implements Serializable { |
|
35 static final long serialVersionUID = 42; |
|
36 class DataObj implements Serializable { |
|
37 static final long serialVersionUID = 42; |
|
38 String text; |
|
39 int ttl; |
|
40 int lport; |
|
41 int rport; |
|
42 DataObj(String s, int ttl) { |
|
43 this.text = s; |
|
44 this.ttl = ttl; |
|
45 this.lport = 0; |
|
46 this.rport = 0; |
|
47 } |
|
48 } |
|
49 ByteBuffer buf; |
|
50 DataObj data; |
|
51 Data(Debug deb, String s, int ttl) throws Exception { |
|
52 super(deb.debid + " DATA"); |
|
53 data = new DataObj(s, ttl); |
|
54 if(CS.fake != 0 && debid.equals("DEMO SSL MASH DATA")) throw new Exception("faked error"); // <----------------------------------------------- |
|
55 load(); |
|
56 } |
|
57 Data(Debug deb, ByteBuffer b) throws Exception { |
|
58 super(deb); |
|
59 debid += " DATA"; |
|
60 log(5, "unloading data objects from buffer..."); |
|
61 unload(); |
|
62 } |
|
63 Data(Debug deb) { |
|
64 super(deb); |
|
65 debid += " DATA"; |
|
66 } |
|
67 void load() throws IOException { |
|
68 log(5, "loading data objects to buffer..."); |
|
69 ByteArrayOutputStream bas = new ByteArrayOutputStream(); |
|
70 try { |
|
71 new ObjectOutputStream(bas).writeObject(data); |
|
72 buf = ByteBuffer.wrap(bas.toByteArray()); |
|
73 } catch(IOException x) { abendMsg("data buffer " + data + " load error", x); throw new IOException(x); } |
|
74 } |
|
75 void unload() throws Exception { |
|
76 buf.rewind(); |
|
77 data = (DataObj) new ObjectInputStream(new ByteArrayInputStream(buf.array())).readObject(); |
|
78 } |
|
79 int dttl() { |
|
80 return --data.ttl; |
|
81 } |
|
82 int ttl() { |
|
83 return data.ttl; |
|
84 } |
|
85 boolean equals(Data data) { |
|
86 return this.data.text.equals(data.data.text); |
|
87 } |
|
88 public String toString() { |
|
89 return "data: ttl=" + data.ttl + ", text=" + |
|
90 (data.text.length() < 24 ? data.text : data.text.substring(0, 8) + "-------" + data.text.substring(data.text.length() - 8)); |
|
91 } |
|
92 } |
|
93 class Node extends Debug implements Runnable { |
|
94 boolean kicker, closing = false; |
|
95 int thisNode, nextNode, closingThreshHold = 0; |
|
96 Data data; |
|
97 Selector selector; |
|
98 ServerSocketChannel ssc; |
|
99 SelectionKey ssk; |
|
100 SSLContext sslC = null; |
|
101 HashMap<Integer, SockIO> cliSide = new HashMap<Integer, SockIO>(); |
|
102 HashMap<SelectionKey, SockIO> srvSide = new HashMap<SelectionKey, SockIO>(); |
|
103 Constellation con; |
|
104 Node(Constellation con, int port) { |
|
105 super(con); |
|
106 debid = (con.ssl ? "" : "non") + "SSL " + (con.mash ? "mash" : "ring") + " node " + port; |
|
107 log(4, "initalizing..."); |
|
108 try { |
|
109 this.con = con; |
|
110 thisNode = port; |
|
111 kicker = (thisNode == con.first); |
|
112 if(con.ssl) try { prepareSSL(); } catch(Exception x) { abend("SSL preparation", x); throw new Exception(); } |
|
113 try { bind(); } catch(Exception x) { abend("bind", x); throw new Exception(); } |
|
114 data = new Data(this); |
|
115 data.buf = ByteBuffer.allocate(con.data.buf.limit()); |
|
116 log(5, "initalized"); |
|
117 } catch(Exception x) { abend("initialization", x); } |
|
118 } |
|
119 public void run() { |
|
120 runLoop(); |
|
121 log(2, "ended"); |
|
122 synchronized(con.glob) { con.glob.spawned--; con.glob.notify(); } |
|
123 } |
|
124 public void runLoop() { |
|
125 if(con.glob.doForward && kicker) { // kick off |
|
126 log(1, "ready to initial send (" + con.data.buf.limit() + " bytes) " + con.data.toString()); |
|
127 data.buf.put(con.data.buf); data.buf.flip(); |
|
128 try { data.unload(); data.load(); } catch(Exception x) {}; |
|
129 doForward(); |
|
130 } |
|
131 //if(con.glob.doForward && !con.glob.abend) do { // main loop |
|
132 if(con.glob.doForward) do { // main loop |
|
133 int k = 0; |
|
134 log(5, "main loop, waiting for " + (con.glob.doForward ? "data" : "close") + |
|
135 " from " + srvSide.size() + " open sockets, timeout=" + CS.selTO + " msecs, closingThreshHold=" + closingThreshHold); |
|
136 if(!con.glob.doForward && closingThreshHold == 0) closingThreshHold = 5000 / CS.selTO; |
|
137 try { |
|
138 try { while((k = selector.select(CS.selTO)) == 0 && con.glob.doForward) {} |
|
139 } catch(Exception x) { abend("socket channel select", x); throw new Exception(x); } |
|
140 if(k > 0) { |
|
141 for(Iterator<SelectionKey> ski = selector.selectedKeys().iterator(); ski.hasNext();) { |
|
142 SelectionKey sk = ski.next(); |
|
143 if(sk.isAcceptable()) |
|
144 try { acc(); } catch(Exception x) { abend("main loop accept error", x); throw new Exception(x); } |
|
145 if(sk.isReadable()) |
|
146 try { forward(sk); } catch(Exception x) { abend("main loop forward error", x); throw new Exception(x); } |
|
147 ski.remove(); |
|
148 } |
|
149 } |
|
150 } catch (Exception x) { abend("main loop", x); } |
|
151 if(con.glob.stop) { |
|
152 log(1, "constellation stop, closing all connections"); |
|
153 stop(); return; |
|
154 } |
|
155 } while(con.glob.doForward || ((srvSide.size() > 0) && (closingThreshHold-- > 0))); |
|
156 //} while((con.glob.doForward || (srvSide.size() > 0)) && !con.glob.abend); |
|
157 log(5, "closing ssc..."); |
|
158 try { ssc.close(); } catch (Exception x) { abend("ssc close", x); } |
|
159 if(kicker && !con.glob.stop && !con.glob.abend) con.glob.abend = !data.equals(con.data); |
|
160 } |
|
161 private void forward(SelectionKey sk) { |
|
162 log(5, "entering 'forward'..."); |
|
163 SockIO si = srvSide.get(sk); |
|
164 if(!si.get()) { |
|
165 stop(); |
|
166 si.close(); |
|
167 srvSide.remove(sk); |
|
168 } |
|
169 else { |
|
170 log(5, "received " + prBuf(data.buf)); |
|
171 try { data.unload(); } catch(Exception x) { abend("data unload at forwarding", x); return; } |
|
172 if(kicker) { |
|
173 log(3, "received from " + (con.mash ? "mash: " : "ring: ") + data.toString()); |
|
174 if(data.dttl() <= 0) { |
|
175 if(CS.isGui) con.cBox.ttl = data.ttl(); |
|
176 log(1, "TTL 0 reached, received " + data.toString() + ", leaving 'forward' closing all connections"); |
|
177 stop(); return; |
|
178 } |
|
179 try { data.load(); } catch(Exception x) { abend("data load at forwarding", x); return; } |
|
180 } |
|
181 if(con.glob.doForward) doForward(); |
|
182 } |
|
183 log(4, "leaving 'forward'"); |
|
184 } |
|
185 private void doForward() { |
|
186 if((nextNode = chooseNextNode()) == 0) { stop(); return; } |
|
187 log(3, "forwarding data to " + nextNode); |
|
188 if(CS.pacing) pacing(nextNode); |
|
189 if(CS.pace > 0) try { Thread.sleep(CS.pace); } catch(InterruptedException i) {}; |
|
190 if(!cliSide.get(nextNode).put()) stop(); |
|
191 } |
|
192 private int chooseNextNode() { |
|
193 int next; |
|
194 if(con.mash) while((next = (con.first + CS.r.nextInt(con.nodes))) == thisNode); |
|
195 else { next = thisNode + 1; if(next > con.last) next = con.first; } |
|
196 if(cliSide.containsKey(next)) return next; |
|
197 else return (conn(next) ? next : 0); |
|
198 } |
|
199 private void pacing(int nextNode) { |
|
200 log(4, "constls.pacing"); |
|
201 synchronized(CS.constls) { |
|
202 if(!con.glob.pacingGo) try { CS.constls.wait(); } catch(InterruptedException x) {} |
|
203 if(nextNode > 0) { |
|
204 con.cBox.currLink[0] = thisNode - con.first; |
|
205 con.cBox.currLink[1] = nextNode - con.first; |
|
206 } |
|
207 con.cBox.ttl = data.ttl(); |
|
208 log(4, "constls.notify"); CS.constls.notify(); |
|
209 con.glob.pacingGo = false; |
|
210 } |
|
211 } |
|
212 public void stop() { |
|
213 con.glob.doForward = false; |
|
214 synchronized(con) { con.notify(); } |
|
215 closeNode(); |
|
216 } |
|
217 private void abend(String msg, Exception x) { |
|
218 abendMsg(msg, x); |
|
219 con.glob.abend = true; |
|
220 CS.isRun = false; |
|
221 stop(); |
|
222 } |
|
223 private void closeNode() { |
|
224 if(!closing) new Thread(new closeClients(this)).start(); |
|
225 closing = true; |
|
226 } |
|
227 private class closeClients extends Debug implements Runnable { |
|
228 closeClients(Debug deb) { this.debid = deb.debid + " clients CLOSE"; } |
|
229 public void run() { |
|
230 log(4, "starting..."); |
|
231 for(Iterator<Integer> i = cliSide.keySet().iterator(); i.hasNext();) { |
|
232 int port = i.next(); |
|
233 log(4, "closing conn to " + port); |
|
234 cliSide.get(port).close(); |
|
235 } |
|
236 log(4, "end"); |
|
237 } |
|
238 } |
|
239 private void prepareSSL() throws Exception { |
|
240 char[] passphrase = "passphrase".toCharArray(); |
|
241 KeyStore ks = KeyStore.getInstance("JKS"); |
|
242 String ksFile = (System.getenv("KSF") == null) ? CS.cePath + "/testkeys" : System.getenv("KSF"); |
|
243 FileInputStream kfs = new FileInputStream(ksFile); |
|
244 ks.load(kfs, passphrase); |
|
245 KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); |
|
246 kmf.init(ks, passphrase); |
|
247 KeyStore ts = KeyStore.getInstance("JKS"); |
|
248 FileInputStream tfs = new FileInputStream(ksFile); |
|
249 ts.load(tfs, passphrase); |
|
250 TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); |
|
251 tmf.init(ts); |
|
252 sslC = SSLContext.getInstance("TLS"); |
|
253 sslC.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); |
|
254 kfs.close(); |
|
255 tfs.close(); |
|
256 } |
|
257 private void bind() throws IOException { |
|
258 log(5, "binding..."); |
|
259 selector = Selector.open(); |
|
260 ssc = ServerSocketChannel.open(); |
|
261 ssc.configureBlocking(false); |
|
262 ssc.socket().bind(new InetSocketAddress(thisNode), 1024); |
|
263 ssk = ssc.register(selector, SelectionKey.OP_ACCEPT); |
|
264 log(2, "bound to " + thisNode); |
|
265 } |
|
266 private boolean conn(int remPort) { |
|
267 int retry = CS.connThreshold; |
|
268 boolean connected = false; |
|
269 SocketChannel sc = null; |
|
270 log(4, "connecting to " + remPort + ", timeout=" + (CS.connThreshold*CS.connTO)/1000 + " secs. ..."); |
|
271 while(!connected && retry-- > 0 && con.glob.doForward) { |
|
272 try { |
|
273 sc = SocketChannel.open(new InetSocketAddress("localhost", remPort)); |
|
274 connected = true; |
|
275 } catch(Exception x) { |
|
276 if(x.getMessage().equals("Connection refused")) { |
|
277 try { Thread.sleep(CS.connTO); } catch(InterruptedException i) {} |
|
278 } else { abendMsg("connection to " + remPort, x); return false; } |
|
279 } |
|
280 } |
|
281 if(!con.glob.doForward) return false; |
|
282 if(connected) { |
|
283 try { cliSide.put(remPort, new SockIO(this, sc, false)); |
|
284 } catch(Exception x) { abendMsg("connection to " + remPort, x); return false; } |
|
285 log(2, "connected after " + (CS.connThreshold - retry + 1) + " retries"); |
|
286 return true; |
|
287 } |
|
288 else { abendMsg("connection to " + remPort + " timeout", null); return false; } |
|
289 } |
|
290 private void acc() throws Exception { |
|
291 try { |
|
292 SocketChannel sc = ssc.accept(); |
|
293 sc.configureBlocking(false); |
|
294 SockIO si = new SockIO(this, sc, true); |
|
295 SelectionKey sk = sc.register(selector, SelectionKey.OP_READ); |
|
296 srvSide.put(sk, si); |
|
297 CS.connCnt++; |
|
298 log(2, "connection accepted"); |
|
299 } catch(Exception x) { abend("connection accept", x); throw new Exception(x); } |
|
300 } |
|
301 private class SockIO extends Debug { |
|
302 SocketChannel sc; |
|
303 Selector handshakeSelector; |
|
304 SelectionKey sk; |
|
305 Runnable ru; |
|
306 SSLSession session; |
|
307 SSLEngine e; |
|
308 SSLEngineResult r; |
|
309 ByteBuffer ci, co, ib; |
|
310 boolean wrapper; |
|
311 SockIO(Debug deb, SocketChannel sc, boolean server) throws Exception { |
|
312 this.debid = deb.debid + " socket " + (server ? "(server)" : "(client)"); |
|
313 log(5, "initializing..."); |
|
314 this.sc = sc; |
|
315 if(con.ssl) { |
|
316 handshakeSelector = Selector.open(); |
|
317 sc.configureBlocking(false); |
|
318 sk = sc.register(handshakeSelector, SelectionKey.OP_READ); |
|
319 e = sslC.createSSLEngine(); |
|
320 session = e.getSession(); |
|
321 int am = session.getApplicationBufferSize(); |
|
322 int pm = session.getPacketBufferSize(); |
|
323 ib = ByteBuffer.allocate(am); // pišvejcova konstanta |
|
324 co = ByteBuffer.allocateDirect(pm); |
|
325 ci = ByteBuffer.allocateDirect(pm); |
|
326 if(server) { |
|
327 e.setUseClientMode(false); |
|
328 e.setNeedClientAuth(true); |
|
329 } else e.setUseClientMode(true); |
|
330 } |
|
331 log(4, "initialized"); |
|
332 } |
|
333 String prBuf(ByteBuffer b) { return b.position() + "/" + b.remaining() + "/" + b.limit() + "/" + b.capacity(); } |
|
334 private String eStat() { |
|
335 String stat; |
|
336 if (r != null) |
|
337 stat = r.getStatus() + "/" + r.getHandshakeStatus() + "/" + e.getHandshakeStatus() + |
|
338 ", bytes: " + r.bytesConsumed() + "/" + r.bytesProduced(); |
|
339 else stat = "-/-/" + e.getHandshakeStatus() + " -/-"; |
|
340 return stat; |
|
341 } |
|
342 //-- result status |
|
343 private boolean isOK() { |
|
344 return r.getStatus() == SSLEngineResult.Status.OK; } |
|
345 private boolean isClosed() { |
|
346 return r.getStatus() == SSLEngineResult.Status.CLOSED; } |
|
347 private boolean isBad() { |
|
348 return r.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW; } |
|
349 //-- result handshake status |
|
350 private boolean handShake() { |
|
351 return r.getHandshakeStatus() != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING; } |
|
352 private boolean handShakeEnd() { |
|
353 return r.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.FINISHED; } |
|
354 private boolean needTask() { |
|
355 return r.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_TASK; } |
|
356 //-- engine handshake status |
|
357 private boolean needUnwrap() { |
|
358 return e.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_UNWRAP; } |
|
359 private boolean needWrap() { |
|
360 return e.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_WRAP; } |
|
361 private int read(SocketChannel sc, ByteBuffer b) { |
|
362 int n = -1, k = 0; |
|
363 log(5, "read, " + prBuf(b) + " ..."); |
|
364 if(con.ssl) { |
|
365 try { while((k = handshakeSelector.select(CS.selTO)) == 0) { if(!con.glob.doForward) break; } |
|
366 } catch (Exception x) { abendMsg("handshake select", x); k = 0; } |
|
367 if(k>0 && sk.isReadable()) { |
|
368 try { n = sc.read(b); } catch(Exception x) { abendMsg("socket channel read", x); n = -1; } |
|
369 handshakeSelector.selectedKeys().remove(sk); |
|
370 } |
|
371 } else |
|
372 try { while(b.hasRemaining()) { n += sc.read(b); if(n < 0) break; }; n++; |
|
373 } catch(Exception x) { abendMsg("socket channel read", x); n = -1; }; |
|
374 log(4, "read " + n); |
|
375 return n; |
|
376 } |
|
377 private int write(SocketChannel sc, ByteBuffer b) { |
|
378 log(5, "writing " + prBuf(b) + " ..."); |
|
379 int n = 0; |
|
380 try { n = sc.write(b); } catch(Exception x) { abendMsg("socket channel write", x); n = -1; } |
|
381 log(4, "written " + n); |
|
382 return n; |
|
383 } |
|
384 private boolean replenish() { |
|
385 log(5, "replenishing ci..."); |
|
386 ci.clear(); |
|
387 int n = read(sc, ci); |
|
388 ci.flip(); |
|
389 return (n >= 0); |
|
390 |
|
391 } |
|
392 void handleUnWrapStatus() { |
|
393 ByteBuffer b; |
|
394 if(r.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW) { |
|
395 log(5, "unwrap: ib BUFFER_OVERFLOW " + prBuf(ib)); |
|
396 if(ib.position() > 0) { ib.flip(); data.buf.put(ib); ib.clear(); } |
|
397 else { |
|
398 b = ByteBuffer.allocate((int)(1.25 * ib.capacity())); |
|
399 ib.flip(); b.put(ib); ib = b; } |
|
400 log(5, "ib " + prBuf(ib)); |
|
401 } |
|
402 if(r.getStatus() == SSLEngineResult.Status.BUFFER_UNDERFLOW) { |
|
403 int n; |
|
404 log(5, "unwrap: ci BUFFER_UNDERFLOW " + prBuf(ci)); |
|
405 if(ci.limit() < ci.capacity()) { |
|
406 ci.mark(); ci.position(ci.limit()); ci.limit(ci.capacity()); |
|
407 n = read(sc, ci); ci.limit(ci.position()); ci.reset(); } |
|
408 else { |
|
409 n = ci.capacity(); if(ci.position() == 0) n *= 2; |
|
410 b = ByteBuffer.allocate(n); b.put(ci); ci = b; |
|
411 n = read(sc, ci); ci.flip(); } |
|
412 log(5, "additional " + n + " bytes read: " + prBuf(ci)); } |
|
413 } |
|
414 boolean doWrap() { |
|
415 log(5, "entering " + "wrap, ob: " + prBuf(data.buf) + ", co: " + prBuf(co) + " ..."); |
|
416 try { r = e.wrap(data.buf, co); } catch (Exception x) { abend("SSL engine wrap", x); return false; } |
|
417 //if(isBad()) { con.glob.abend = true; return abendMsg("SSL engine status: " + r.getStatus().toString(), null); } |
|
418 if(isBad()) { abend("SSL engine status: " + r.getStatus().toString(), null); return false; } |
|
419 log(4, "after " + (wrapper ? "wrapper" : "unwrapper") + " wrap: " + eStat() + ", ob: " + prBuf(data.buf) + ", co: " + prBuf(co)); |
|
420 if(isClosed()) return false; |
|
421 return true; |
|
422 } |
|
423 private boolean wrap() { |
|
424 do {co.clear(); |
|
425 if(!doWrap()) return false; |
|
426 if(needTask()) while((ru = e.getDelegatedTask()) != null) ru.run(); |
|
427 co.flip(); |
|
428 if(handShake() && (write(sc, co) == -1)) return false; |
|
429 } while (needWrap()); |
|
430 if(needUnwrap() && !( replenish() && unwrap() )) return false; |
|
431 log(5, (wrapper ? "wrapper" : "unwrapper") + " wrap HS finished=" + handShakeEnd()); |
|
432 if(wrapper && handShakeEnd()) { |
|
433 co.clear(); |
|
434 if(!doWrap()) return false; |
|
435 co.flip(); |
|
436 } |
|
437 return true; |
|
438 } |
|
439 boolean doUnwrap() { |
|
440 log(5, "entering " + "unwrap, ib: " + prBuf(ib) + ", ci: " + prBuf(ci) + " ..."); |
|
441 try { r = e.unwrap(ci, ib); } catch (Exception x) { abend("SSL engine unwrap", x); return false; } |
|
442 log(4, "after " + (wrapper ? "wrapper" : "unwrapper") + " unwrap: " + eStat() + ", ib: " + prBuf(ib) + ", ci: " + prBuf(ci)); |
|
443 if(isClosed()) return false; |
|
444 return true; |
|
445 } |
|
446 private boolean unwrap() { |
|
447 do { // while( needUnwrap() ) |
|
448 do { // while( needUnwrap() && ci.hasRemaining() ) |
|
449 if(!doUnwrap()) return false; |
|
450 if(needTask()) while((ru = e.getDelegatedTask()) != null) ru.run(); |
|
451 } while(needUnwrap() && ci.hasRemaining()); |
|
452 if(needUnwrap() && !replenish()) return false; |
|
453 } while(needUnwrap()); |
|
454 if(needWrap() && !wrap()) return false; |
|
455 if(!wrapper) { |
|
456 if(handShakeEnd() && !replenish()) return false; |
|
457 if(handShakeEnd() || !handShake()) { |
|
458 handleUnWrapStatus(); |
|
459 while(ci.hasRemaining()) { |
|
460 do { |
|
461 if(!doUnwrap()) return false; |
|
462 handleUnWrapStatus(); |
|
463 } while(!isOK()); |
|
464 } |
|
465 } |
|
466 } |
|
467 return true; |
|
468 } |
|
469 boolean get() { |
|
470 wrapper = false; |
|
471 boolean got = false; |
|
472 data.buf.clear(); |
|
473 if(con.ssl) { |
|
474 while(data.buf.hasRemaining()) { |
|
475 ib.clear(); |
|
476 int n; |
|
477 ci.clear(); n = read(sc, ci); ci.flip(); |
|
478 if(n < 0) { got = false; break; } |
|
479 else got = (unwrap() && !isClosed()); |
|
480 if(got) { ib.flip(); data.buf.put(ib); log(4, "partially got " + prBuf(data.buf)); } |
|
481 else break; |
|
482 } |
|
483 } |
|
484 else try { got = (read(sc, data.buf) >= 0); } catch(Exception x) { got = abendMsg("socket read", x); } |
|
485 return got; |
|
486 } |
|
487 boolean put() { |
|
488 wrapper = true; |
|
489 boolean put = false; |
|
490 log(4, "put: ob: " + prBuf(data.buf)); |
|
491 try { |
|
492 do { |
|
493 if(con.ssl) { |
|
494 if(!wrap() || isClosed()) return false; |
|
495 else put = (write(sc, co) >= 0); |
|
496 } else put = (write(sc, data.buf) >= 0); |
|
497 } while(put && data.buf.hasRemaining()); |
|
498 if(put) CS.forwCnt++; |
|
499 } catch(Exception x) { put = abendMsg("socket write", x); } |
|
500 return put; |
|
501 } |
|
502 void close() { |
|
503 log(4, "terminating connection..."); |
|
504 try { |
|
505 if(con.ssl) { |
|
506 data.buf.put(ByteBuffer.wrap("".getBytes())); |
|
507 e.closeOutbound(); |
|
508 wrap(); |
|
509 } |
|
510 sc.close(); |
|
511 } catch(Exception x) {} |
|
512 } |
|
513 } |
|
514 } |
|
515 class Constellation extends Debug implements Runnable { |
|
516 class Glob { |
|
517 boolean doForward = true; |
|
518 boolean pacingGo = false; |
|
519 boolean stop = false; |
|
520 boolean abend = false; |
|
521 int D = 0; |
|
522 int spawned = 0; |
|
523 int from = 0, to = 0; |
|
524 } |
|
525 volatile Glob glob; |
|
526 boolean mash, ssl; |
|
527 int first, last, nodes; |
|
528 Data data; |
|
529 CS cs; |
|
530 Gui.CBox cBox; |
|
531 String label; |
|
532 Constellation(CS cs, Gui.CBox cBox, boolean mash, boolean ssl) { |
|
533 label = (ssl ? "" : "non") + "SSL " + (mash ? "MASH" : "RING"); |
|
534 debid = "DEMO " + label; |
|
535 this.cs = cs; |
|
536 this.cBox = cBox; |
|
537 this.mash = mash; |
|
538 this.ssl = ssl; |
|
539 if(mash) { first = CS.mp0; nodes = CS.mn; } |
|
540 else { first = CS.rp0; nodes = CS.rn; } |
|
541 glob = new Glob(); |
|
542 } |
|
543 Constellation(Constellation con) { |
|
544 super(con); |
|
545 this.cs = con.cs; |
|
546 this.mash = con.mash; |
|
547 this.ssl = con.ssl; |
|
548 this.first = con.first; |
|
549 this.last = con.last; |
|
550 this.nodes = con.nodes; |
|
551 this.glob = con.glob; |
|
552 } |
|
553 void stop() { glob.stop = true; } |
|
554 //void reset() { |
|
555 //first = mash ? CS.mp0 : CS.rp0; |
|
556 //first += nodes; |
|
557 //glob.doForward = true; |
|
558 //glob.abend = false; |
|
559 //glob.spawned = 0; |
|
560 //} |
|
561 public void run() { |
|
562 log(4, "starting"); |
|
563 glob.pacingGo = true; |
|
564 runNodes(); |
|
565 synchronized(CS.constls) { CS.constls.notify(); } |
|
566 synchronized(cs) { |
|
567 if(--CS.notFinished == 0) cs.notifyAll(); |
|
568 else try { cs.wait(); } catch(InterruptedException e) {}; |
|
569 } |
|
570 log(1, glob.abend ? "BAD: constellation not finished correctly" : "OK, constellation finished correctly"); |
|
571 if(glob.abend) CS.abend = true; |
|
572 CS.spawned--; |
|
573 synchronized(cs) { cs.notify(); } |
|
574 } |
|
575 void runNodes() { |
|
576 log(1, nodes + " nodes constellation, ttl=" + CS.pttl + " starting..."); |
|
577 first += (ssl ? 500 : 0); |
|
578 last = first + nodes - 1; |
|
579 if(data == null) |
|
580 try { data = new Data(this, CS.text, CS.pttl); |
|
581 } catch(Exception x) { abendMsg("creating initial data", x); return; } |
|
582 synchronized(glob) { |
|
583 for(int port = first; (port < first + nodes); port++) { |
|
584 try { |
|
585 new Thread(new Node(this, port), "Node " + port).start(); |
|
586 log(3, "node " + port + " established"); |
|
587 glob.spawned++; |
|
588 } catch(Exception x) { glob.doForward = false; glob.abend = true; break; } |
|
589 } |
|
590 if(glob.doForward) log(2, "all nodes established"); |
|
591 while(glob.spawned > 0) try { glob.wait(); } catch(InterruptedException e) {}; |
|
592 } |
|
593 log(2, "all nodes finished"); |
|
594 } |
|
595 } |
|
596 class Gui extends Debug implements Runnable { |
|
597 class Parms extends JPanel { |
|
598 static final long serialVersionUID = 43; |
|
599 class Parm implements ActionListener { |
|
600 JComboBox<Number> valueEntry; |
|
601 JLabel valueLabel; |
|
602 Parm(Number[] values, Number value, String label, boolean editable, boolean rowEnd) { |
|
603 log(5, "parm " + label); |
|
604 valueLabel = new JLabel(label); |
|
605 valueLabel.setBorder(b); |
|
606 if(orientation == HORIZONTAL) gridC.gridwidth = 1; |
|
607 else gridC.gridwidth = GridBagConstraints.REMAINDER; |
|
608 gridL.setConstraints(valueLabel, gridC); |
|
609 add(valueLabel); |
|
610 valueEntry = new JComboBox<Number>(values); |
|
611 valueEntry.setPreferredSize(new Dimension(prefComboWidth, prefComboHeight)); |
|
612 if(value != null) valueEntry.setSelectedItem(value); |
|
613 valueEntry.setEditable(editable); |
|
614 valueEntry.addActionListener(this); |
|
615 if(orientation == HORIZONTAL && rowEnd) gridC.gridwidth = GridBagConstraints.REMAINDER; |
|
616 gridL.setConstraints(valueEntry, gridC); |
|
617 add(valueEntry); |
|
618 } |
|
619 public void actionPerformed(ActionEvent e) { |
|
620 try { setVal((Number)((JComboBox)e.getSource()).getSelectedItem()); } catch(Exception x) { log(0, x.getMessage()); } |
|
621 } |
|
622 void getEnv() {} |
|
623 void setVal(Number v) {} |
|
624 } |
|
625 class Buttons extends Box { |
|
626 class GoButton extends JButton implements ActionListener { |
|
627 static final long serialVersionUID = 44; |
|
628 GoButton() { |
|
629 super("go"); |
|
630 addActionListener(this); |
|
631 gridC.gridwidth = 1; |
|
632 gridL.setConstraints(this, gridC); |
|
633 CS.go = true; |
|
634 } |
|
635 public void actionPerformed(ActionEvent e) { |
|
636 log(5, getText() + " button pressed"); |
|
637 if(getText().equals("go")) { setText("pause"); CS.go = true; CS.pacing = true; CS.isRun = true; awake(); } |
|
638 else { setText("go"); CS.go = false; } |
|
639 } |
|
640 } |
|
641 class StepButton extends JButton implements ActionListener { |
|
642 static final long serialVersionUID = 44; |
|
643 StepButton() { |
|
644 super("step"); |
|
645 addActionListener(this); |
|
646 gridC.gridwidth = 1; |
|
647 gridL.setConstraints(this, gridC); |
|
648 } |
|
649 public void actionPerformed(ActionEvent e) { |
|
650 log(5, getText() + " button pressed"); |
|
651 CS.go = false; CS.pacing = true; CS.isRun = true; |
|
652 setGo(); |
|
653 awake(); |
|
654 } |
|
655 } |
|
656 class ResetButton extends JButton implements ActionListener { |
|
657 static final long serialVersionUID = 45; |
|
658 ResetButton() { |
|
659 super("reset"); |
|
660 addActionListener(this); |
|
661 gridC.gridwidth = 1; |
|
662 gridL.setConstraints(this, gridC); |
|
663 } |
|
664 public void actionPerformed(ActionEvent e) { CS.isRun = false; CS.isReset = true; CS.go = true; awake(); } |
|
665 } |
|
666 class StopButton extends JButton implements ActionListener { |
|
667 static final long serialVersionUID = 46; |
|
668 StopButton() { |
|
669 super("end"); |
|
670 addActionListener(this); |
|
671 gridC.gridwidth = 1; |
|
672 //gridC.gridwidth = GridBagConstraints.REMAINDER; |
|
673 gridL.setConstraints(this, gridC); |
|
674 } |
|
675 public void actionPerformed(ActionEvent e) { closeUI(); } |
|
676 } |
|
677 GoButton go; |
|
678 ResetButton reset; |
|
679 StepButton step; |
|
680 StopButton stop; |
|
681 Box row1, row2; |
|
682 Buttons() { |
|
683 super(BoxLayout.Y_AXIS); |
|
684 setBorder(b); |
|
685 add(row1 = new Box(BoxLayout.X_AXIS)); |
|
686 add(row2 = new Box(BoxLayout.X_AXIS)); |
|
687 row1.add(go = new GoButton()); |
|
688 row1.add(step = new StepButton()); |
|
689 row2.add(reset = new ResetButton()); |
|
690 row2.add(stop = new StopButton()); |
|
691 } |
|
692 void setGo() { go.setText("go"); } |
|
693 void setPause() { go.setText("pause"); } |
|
694 void enableStep(boolean b) { step.setEnabled(b); } |
|
695 } |
|
696 static final boolean EDITABLE = true; |
|
697 static final boolean ROW_END = true; |
|
698 boolean orientation; |
|
699 EmptyBorder b = new EmptyBorder(0,7,0,7); |
|
700 GridBagLayout gridL = new GridBagLayout(); |
|
701 GridBagConstraints gridC = new GridBagConstraints(); |
|
702 int prefComboWidth, prefComboHeight; |
|
703 Buttons buttons; |
|
704 Parms(boolean orientation) { |
|
705 textHeight = (int)Math.round(1.5 * getFontMetrics(getFont()).getHeight()); |
|
706 prefComboWidth = (int)Math.round(1.5 * getFontMetrics(getFont()).bytesWidth("000000".getBytes(), 0, 6)); |
|
707 prefComboHeight = textHeight; |
|
708 this.orientation = orientation; |
|
709 gridC.fill = GridBagConstraints.BOTH; |
|
710 setFont(new Font("SansSerif", Font.PLAIN, 9)); |
|
711 setLayout(gridL); |
|
712 new Parm(new Integer[] {0,1,2,3,4,5,7,9}, new Integer(CS.debug), "Debug level", !EDITABLE, !ROW_END) { |
|
713 void setVal(Number v) { CS.debug = v.intValue(); } }; |
|
714 new Parm(new Integer[] {0,1,2}, new Integer(CS.issl), "SSL", !EDITABLE, !ROW_END) { |
|
715 void setVal(Number v) { CS.issl = v.intValue(); } }; |
|
716 new Parm(new Integer[] {CS.pttl}, null, "TTL", EDITABLE, ROW_END) { |
|
717 void setVal(Number v) { CS.pttl = v.intValue(); } }; |
|
718 new Parm(new Double[] {(double)CS.ipace/1000}, null, "pace in secs.", EDITABLE, !ROW_END) { |
|
719 void setVal(Number v) { CS.ipace = (int)(1000.0 * v.doubleValue()); CS.pace = CS.ipace; } }; |
|
720 new Parm(new Integer[] {CS.mp0}, null, "listen port of first MASH node", EDITABLE, !ROW_END) { |
|
721 void setVal(Number v) { CS.mp0 = v.intValue(); } }; |
|
722 new Parm(new Integer[] {CS.rp0}, null, "listen port of first RING node", EDITABLE, ROW_END) { |
|
723 void setVal(Number v) { CS.rp0 = v.intValue(); } }; |
|
724 new Parm(new Integer[] {CS.mn}, null, "MASH constellation size", EDITABLE, !ROW_END) { |
|
725 void setVal(Number v) { CS.mn = v.intValue(); } }; |
|
726 new Parm(new Integer[] {CS.rn}, null, "RING constellation size", EDITABLE, !ROW_END) { |
|
727 void setVal(Number v) { CS.rn = v.intValue(); } }; |
|
728 new Parm(new Double[] {(double)CS.selTO/1000}, null, "I/O selection timeout in secs.", EDITABLE, ROW_END) { |
|
729 void setVal(Number v) { CS.selTO = 1000 * v.intValue(); } }; |
|
730 new Parm(new Integer[] {CS.fake}, null, "point of faked exception (integer)", EDITABLE, !ROW_END) { |
|
731 void setVal(Number v) { CS.fake = v.intValue(); } }; |
|
732 new Parm(new Integer[] {CS.rs}, null, "random seed (integer)", EDITABLE, !ROW_END) { |
|
733 void setVal(Number v) { CS.rs = v.intValue(); } }; |
|
734 //gridC.gridwidth = 0; |
|
735 gridC.gridwidth = GridBagConstraints.REMAINDER; |
|
736 add(buttons = new Buttons()); |
|
737 } |
|
738 } |
|
739 class CBoxBg extends BufferedImage { |
|
740 final int nodeC[][]; // node centers |
|
741 CBoxBg(int n) { |
|
742 super(cBoxSize , cBoxSize, BufferedImage.TYPE_INT_RGB); |
|
743 nodeC = new int[n][2]; |
|
744 log(5, "cnstlltn bg image beg, node centers array length=" + nodeC.length); |
|
745 final double a0 = Math.PI / 2; |
|
746 final double aN = 2 * Math.PI / n; |
|
747 final int b = 3; // border |
|
748 final int r = 5; // node diameter |
|
749 int cx, cy; // constellation center coordinates |
|
750 cx = cy = cBoxSize/2; |
|
751 int R = cBoxSize/2 - r - 2*b; // distance of node centers from constellation center |
|
752 int dx, dy; // deltas of node center coordinates |
|
753 final Graphics2D g2 = (Graphics2D)this.getGraphics(); |
|
754 g2.setBackground(Color.WHITE); |
|
755 g2.clearRect(0, 0, cBoxSize, cBoxSize); |
|
756 g2.setColor(Color.BLACK); |
|
757 g2.draw3DRect(b, b, cBoxSize - 2*b, cBoxSize - 2*b, true); |
|
758 if(n < 2) return; |
|
759 for(int i=0; i<n; i++) { |
|
760 dx = (int)Math.round(Math.cos(a0 + i * aN) * R); |
|
761 dy = (int)Math.round(Math.sin(a0 + i * aN) * R); |
|
762 nodeC[i][0] = dx; nodeC[i][1] = dy; |
|
763 g2.drawOval(cx-dx-r, cy-dy-r, 2*r, 2*r); |
|
764 } |
|
765 log(5, "cnstlltn bg image end, node centers array length=" + nodeC.length); |
|
766 } |
|
767 } |
|
768 class Arrow extends Polygon { |
|
769 final double z, D, sin, cos, xd, yd, dx, dy; |
|
770 final int x0, y0, x3, y3; |
|
771 Arrow(int x1, int y1, int x2, int y2, int d) { |
|
772 super(); |
|
773 z=d/(2*1.618034); |
|
774 D = Math.sqrt(Math.pow(x2-x1, 2) + Math.pow(y2-y1, 2)); |
|
775 sin = (x2-x1)/D; |
|
776 cos = (y2-y1)/D; |
|
777 xd = x2-d*sin; |
|
778 yd = y2-d*cos; |
|
779 dx = z*cos; |
|
780 dy = z*sin; |
|
781 x0 = (int)Math.round(xd-dx); |
|
782 y0 = (int)Math.round(yd+dy); |
|
783 x3 = (int)Math.round(xd+dx); |
|
784 y3 = (int)Math.round(yd-dy); |
|
785 addPoint(x0, y0); |
|
786 addPoint(x2, y2); |
|
787 addPoint(x3, y3); |
|
788 } |
|
789 } |
|
790 class CBox extends Box { |
|
791 static final long serialVersionUID = 45; |
|
792 class CHead extends JPanel { |
|
793 final JLabel field = new JLabel(); |
|
794 CHead() { |
|
795 setPreferredSize(new Dimension(cBoxSize, textHeight - 4)); |
|
796 add(field); |
|
797 } |
|
798 public void paint(Graphics g) { |
|
799 super.paint(g); |
|
800 field.setText(label + ttl); |
|
801 } |
|
802 } |
|
803 class CPanel extends JPanel { |
|
804 CPanel() { setPreferredSize(new Dimension(cBoxSize, cBoxSize)); } |
|
805 public void paint(Graphics g) { |
|
806 super.paint(g); |
|
807 final Graphics2D g2 = (Graphics2D)g; |
|
808 Polygon p; |
|
809 g2.drawImage(bg, 0, 0, Color.WHITE, null); |
|
810 final int n1 = currLink[0], n2 = currLink[1]; |
|
811 if(n1 > -1) { |
|
812 final int x1 = cx-bg.nodeC[n1][0], y1 = cy-bg.nodeC[n1][1]; |
|
813 final int x2 = cx-bg.nodeC[n2][0], y2 = cy-bg.nodeC[n2][1]; |
|
814 log(5, label + " paint, n1=" + n1 + ", n2=" + n2 + ", nodeC.length=" + bg.nodeC.length); |
|
815 g2.setColor(Color.CYAN); |
|
816 g2.setStroke(new BasicStroke(2)); |
|
817 g2.drawLine(x1, y1, x2, y2); |
|
818 g2.setColor(Color.BLUE); |
|
819 g2.setStroke(new BasicStroke(0)); |
|
820 g2.drawPolygon(p = new Arrow(x1, y1, x2, y2, 12)); |
|
821 g2.fill(p); |
|
822 } |
|
823 } |
|
824 } |
|
825 volatile int[] currLink = {-1,-1}; |
|
826 volatile int ttl; |
|
827 CBoxBg bg; |
|
828 final int cx = cBoxSize / 2, cy = cx; |
|
829 final String label; |
|
830 CBox(String label, CBoxBg bg) { |
|
831 super(BoxLayout.Y_AXIS); |
|
832 setMaximumSize(new Dimension(cBoxSize, cBoxSize + textHeight)); |
|
833 this.bg = bg; |
|
834 this.label = label + ", ttl="; |
|
835 ttl = CS.pttl; |
|
836 add(new CHead()); |
|
837 add(new CPanel()); |
|
838 } |
|
839 void reset(CBoxBg bg) { currLink[0] = -1; currLink[1] = -1; ttl = CS.pttl; this.bg = bg; } |
|
840 } |
|
841 class CcBox extends Box { |
|
842 CBox ringBox, mashBox; |
|
843 CcBox(String label) { |
|
844 super(BoxLayout.X_AXIS); |
|
845 add(mashBox = new CBox("MASH " + label + " SSL", mashBg)); |
|
846 add(ringBox = new CBox("RING " + label + " SSL", ringBg)); |
|
847 } |
|
848 } |
|
849 CS cs; |
|
850 JFrame ui = new JFrame(debid); |
|
851 Container dashboard; |
|
852 static final boolean HORIZONTAL = true; |
|
853 static final boolean VERTICAL = false; |
|
854 boolean dashboardLayout = HORIZONTAL; |
|
855 int textHeight; |
|
856 Parms parms; |
|
857 Box resultBox = null; |
|
858 CcBox sslBox = null, nonSslBox = null; |
|
859 CBoxBg ringBg = null, mashBg = null; |
|
860 CBox nonSslMashBox, nonSslRingBox, sslMashBox, sslRingBox; |
|
861 int cBoxSize; |
|
862 WindowAdapter uiLstnr = new WindowAdapter() { |
|
863 public void windowOpened(WindowEvent e) { log(5, "window opened"); } |
|
864 public void windowClosing(WindowEvent e) { closeUI(); } |
|
865 public void windowClosed(WindowEvent e) { log(5, "window closed"); synchronized(CS.gui) { CS.gui.notify(); } |
|
866 } |
|
867 }; |
|
868 Gui(CS cs) { |
|
869 super(cs); |
|
870 this.cs = cs; |
|
871 debid = cs.debid + " GUI"; |
|
872 log(5, "start parms panel"); |
|
873 ui.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); |
|
874 ui.addWindowListener(uiLstnr); |
|
875 ui.setLocation(600, 100); |
|
876 dashboard = ui.getContentPane(); |
|
877 dashboard.setFont(new Font("SansSerif", Font.PLAIN, 9)); |
|
878 dashboard.setLayout(new BoxLayout(dashboard, dashboardLayout == HORIZONTAL ? BoxLayout.X_AXIS : BoxLayout.Y_AXIS)); |
|
879 ui.add(parms = new Parms(!dashboardLayout)); |
|
880 ui.pack(); |
|
881 } |
|
882 public void run() { |
|
883 log(5, "repaint"); |
|
884 ui.setVisible(true); |
|
885 ui.repaint(); |
|
886 } |
|
887 void cboxes() { |
|
888 log(5, "create constellation panels"); |
|
889 cBoxSize = (dashboardLayout == VERTICAL ? dashboard.getSize().width : dashboard.getSize().height)/2 - textHeight; |
|
890 ringBg = new CBoxBg(CS.rn); |
|
891 mashBg = new CBoxBg(CS.mn); |
|
892 if(resultBox != null) dashboard.remove(resultBox); |
|
893 dashboard.add(resultBox = new Box(BoxLayout.Y_AXIS)); |
|
894 if(CS.issl > 0) { |
|
895 resultBox.add(sslBox = new CcBox("w/")); |
|
896 sslMashBox = sslBox.mashBox; |
|
897 sslRingBox = sslBox.ringBox; |
|
898 } |
|
899 if(CS.issl != 1) { |
|
900 resultBox.add(nonSslBox = new CcBox("non")); |
|
901 nonSslMashBox = nonSslBox.mashBox; |
|
902 nonSslRingBox = nonSslBox.ringBox; |
|
903 } |
|
904 ui.pack(); |
|
905 } |
|
906 void awake() { synchronized(CS.gui) { CS.gui.notify(); } } |
|
907 void dashboardReset() { |
|
908 //parms.buttons.setGo(); |
|
909 parms.buttons.enableStep(true); |
|
910 } |
|
911 void closeUI() { |
|
912 log(5, "closing window"); |
|
913 CS.isGui = false; CS.isRun = false; CS.go = true; awake(); |
|
914 } |
|
915 } |
|
916 public class CS extends Debug { |
|
917 volatile static int |
|
918 connCnt = 0, |
|
919 forwCnt = 0, |
|
920 notFinished = 0, |
|
921 spawned = 0; |
|
922 volatile static boolean abend = false; |
|
923 static String text = "bla bla"; |
|
924 static String clsPath; |
|
925 static String cePath; |
|
926 static final String sslPathSuffP = "../CS"; |
|
927 static Random r; |
|
928 static int |
|
929 mn = 0, |
|
930 mp0 = 11000, |
|
931 rn = 0, |
|
932 rp0 = 12000, |
|
933 pttl = 3, |
|
934 issl = 0, |
|
935 connThreshold = 77, // connection retries threshold |
|
936 connTO = 99, // connection sleep time in msecs |
|
937 selTO = 999, // selection timeout in msecs |
|
938 ipace = 0, |
|
939 pace = 0, |
|
940 rs = 0, |
|
941 fake = 0; |
|
942 static boolean isGui = false, isRun = true, isReset = false, go = true, pacing = false; |
|
943 static ArrayList<Constellation> constls; |
|
944 public static Constellation nonSslMashCon, nonSslRingCon, sslMashCon, sslRingCon; |
|
945 static Gui gui; |
|
946 static Gui.CBox nonSslMashBox, nonSslRingBox, sslMashBox, sslRingBox; |
|
947 CS() { |
|
948 debid = "client server DEMO"; |
|
949 getArgs(); |
|
950 } |
|
951 boolean isArg(String a) { return System.getenv(a) != null; } |
|
952 int getArgI(String a) throws Exception { |
|
953 int i = -1; |
|
954 if(System.getenv(a) != null) |
|
955 if(!System.getenv(a).equals("")) |
|
956 try { i = Integer.valueOf(System.getenv(a)); |
|
957 } catch(NumberFormatException x) { throw new Exception(a + "=\terror in number format"); } |
|
958 return i; |
|
959 } |
|
960 double getArgF(String a) throws Exception { |
|
961 double d = -1; |
|
962 if(System.getenv(a) != null) |
|
963 if(!System.getenv(a).equals("")) |
|
964 try { d = Double.valueOf(System.getenv(a)); |
|
965 } catch(NumberFormatException x) { throw new Exception(a + "=\terror in number format"); } |
|
966 return d; |
|
967 } |
|
968 int getMArg(String a) throws Exception { |
|
969 int i; |
|
970 if((i = getArgI(a)) == 0) throw new Exception(a + " is mandatory"); |
|
971 return i; |
|
972 } |
|
973 void getArgs() { |
|
974 try { |
|
975 clsPath = getClass().getProtectionDomain().getCodeSource().getLocation().getPath(); |
|
976 if(getArgI("DEB") >= 0) debug = getArgI("DEB"); |
|
977 if(System.getenv("T") != null) text = System.getenv("T"); |
|
978 if(getArgI("TTL") > 0) pttl = getArgI("TTL"); |
|
979 if(getArgF("P") >= 0) ipace = (int)(getArgF("P") * 1000); |
|
980 if(getArgI("MP0") > 0) mp0 = getArgI("MP0"); |
|
981 if(getArgI("RP0") > 0) rp0 = getArgI("RP0"); |
|
982 if(getArgI("N") >= 0) { mn = getArgI("N"); rn = mn; } |
|
983 if(getArgI("SSL") >= 0) issl = getArgI("SSL"); |
|
984 if(System.getenv("CEP") != null) cePath = System.getenv("CEP"); |
|
985 else cePath = clsPath + sslPathSuffP; |
|
986 if(getArgI("MN") >= 0) mn = getArgI("MN"); |
|
987 if(getArgI("RN") >= 0) rn = getArgI("RN"); |
|
988 if(getArgF("STO") >= 0) selTO = (int)(getArgF("STO") * 1000); |
|
989 if(getArgI("FAKE") >= 0) fake = getArgI("FAKE"); |
|
990 if(isArg("RS")) rs = getArgI("RS"); |
|
991 if(isArg("G")) isGui = getArgI("G") == 1; |
|
992 } catch(Exception x) { log(0, x.getMessage()); return; } |
|
993 } |
|
994 void dashboard() { |
|
995 gui = new Gui(this); |
|
996 log(4, "wait for args from GUI"); |
|
997 synchronized(gui) { |
|
998 try { SwingUtilities.invokeAndWait(gui); } catch(Exception x) { throw new Error(x); } |
|
999 try { gui.wait(); } catch(InterruptedException x) {} |
|
1000 } |
|
1001 if(isGui) cboxes(); |
|
1002 } |
|
1003 void cboxes() { |
|
1004 log(4, "cboxes"); |
|
1005 try { gui.cboxes(); } catch(Exception x) { log(0, x.getMessage()); } |
|
1006 try { SwingUtilities.invokeAndWait(gui); } catch(Exception x) { throw new Error(x); } |
|
1007 nonSslMashBox = gui.nonSslMashBox; |
|
1008 nonSslRingBox = gui.nonSslRingBox; |
|
1009 sslMashBox = gui.sslMashBox; |
|
1010 sslRingBox = gui.sslRingBox; |
|
1011 |
|
1012 } |
|
1013 void constellations() { |
|
1014 pace = ipace; |
|
1015 spawned = 0; notFinished = 0; |
|
1016 constls = new ArrayList<Constellation>(); |
|
1017 if(mn == 1) log(0, "one-node MASH configuration not implemented"); |
|
1018 if(mn > 1) { |
|
1019 if(issl > 0) { sslMashCon = new Constellation(this, sslMashBox, true, true); constls.add(sslMashCon); } |
|
1020 if(issl != 1) { nonSslMashCon = new Constellation(this, nonSslMashBox, true, false); constls.add(nonSslMashCon); } |
|
1021 } |
|
1022 if(rn == 1) log(0, "one-node RING configuration not implemented"); |
|
1023 if(rn > 1) { |
|
1024 if(issl > 0) { sslRingCon = new Constellation(this, sslRingBox, false, true); constls.add(sslRingCon); } |
|
1025 if(issl != 1) { nonSslRingCon = new Constellation(this, nonSslRingBox, false, false); constls.add(nonSslRingCon); } |
|
1026 } |
|
1027 } |
|
1028 void stop() { |
|
1029 if(sslMashCon != null) sslMashCon.stop(); |
|
1030 if(sslRingCon != null) sslRingCon.stop(); |
|
1031 if(nonSslMashCon != null) nonSslMashCon.stop(); |
|
1032 if(nonSslRingCon != null) nonSslRingCon.stop(); |
|
1033 pacing = false; |
|
1034 pace = 0; |
|
1035 go = true; |
|
1036 } |
|
1037 void reset() { |
|
1038 gui.dashboardReset(); |
|
1039 cboxes(); |
|
1040 mp0 += mn; rp0 += rn; // port is unusable 30 secs after port close due to special timeout |
|
1041 constellations(); |
|
1042 isReset = false; |
|
1043 } |
|
1044 String switches(String label) { |
|
1045 return label + ": isGui=" + isGui + ", isRun=" + isRun + ", go=" + go + ", pacing=" + pacing + ", spawned=" + spawned; |
|
1046 } |
|
1047 void pacingGo() { for(Constellation con : constls) con.glob.pacingGo = true; } |
|
1048 void runGuiCon() { |
|
1049 synchronized(constls) { |
|
1050 while(isGui && isRun && spawned > 0) { |
|
1051 log(4, switches("runCons constls.wait")); |
|
1052 try { constls.wait(); } catch(InterruptedException x) {} |
|
1053 pacingGo(); |
|
1054 log(4, switches("runCons constls.paint")); |
|
1055 if(!isGui) break; |
|
1056 try { SwingUtilities.invokeAndWait(CS.gui); } catch(Exception x) { throw new Error(x); } |
|
1057 if(!isGui || !isRun) break; |
|
1058 if(!go) { |
|
1059 log(4, switches("runCons constls.gui.wait")); |
|
1060 synchronized(gui) { try { gui.wait(); } catch(InterruptedException x) {} } |
|
1061 } |
|
1062 if(!isGui || !isRun) break; |
|
1063 log(4, switches("runCons constls.notifyAll")); |
|
1064 constls.notifyAll(); |
|
1065 } |
|
1066 } |
|
1067 if(isGui && isRun) { |
|
1068 log(4, switches("runCons last repaint")); |
|
1069 try { SwingUtilities.invokeAndWait(CS.gui); } catch(Exception x) { throw new Error(x); } |
|
1070 if(!go) synchronized(gui) { try { gui.wait(); } catch(InterruptedException x) {} } |
|
1071 } |
|
1072 else { |
|
1073 log(4, switches("runCons stop")); |
|
1074 stop(); |
|
1075 synchronized(constls) { constls.notifyAll(); } |
|
1076 while(spawned > 0) synchronized(this) { try { wait(); } catch(InterruptedException x) {} } |
|
1077 } |
|
1078 } |
|
1079 void runCons() { |
|
1080 if(isGui) pacing = true; |
|
1081 else pacing = false; |
|
1082 for(Constellation con : constls) { notFinished++; new Thread(con, con.label).start(); spawned++; } |
|
1083 if(isGui) runGuiCon(); |
|
1084 else while(spawned > 0) synchronized(this) { try { wait(); } catch(InterruptedException x) {} } |
|
1085 } |
|
1086 public void run() { |
|
1087 if(isGui) dashboard(); |
|
1088 if(isRun) { |
|
1089 log(1, "pgm=" + clsPath + getClass().getName() + |
|
1090 ", ttl=" + pttl + ", pace=" + ipace + "msecs, seed=" + rs + ", SSL=" + issl + ", fake=" + fake + ", debug=" + debug); |
|
1091 if(issl > 0) log(3, "cePath=" + cePath); |
|
1092 constellations(); |
|
1093 if(!isGui) { r = new Random(rs); runCons(); } |
|
1094 else while(isGui) { |
|
1095 r = new Random(rs); |
|
1096 runCons(); |
|
1097 log(1, "all constellations finished"); |
|
1098 if(!isGui) break; |
|
1099 gui.parms.buttons.setGo(); |
|
1100 if(!go) gui.parms.buttons.step.setEnabled(false); |
|
1101 if(abend) { |
|
1102 gui.parms.buttons.go.setEnabled(false); |
|
1103 gui.parms.buttons.step.setEnabled(false); |
|
1104 gui.parms.buttons.reset.setEnabled(false); |
|
1105 } |
|
1106 try { SwingUtilities.invokeAndWait(gui); } catch(Exception x) { throw new Error(x); } |
|
1107 if(!isReset) synchronized(gui) { try { gui.wait(); } catch(InterruptedException x) {} } |
|
1108 if(isGui) { |
|
1109 reset(); |
|
1110 do synchronized(gui) { |
|
1111 try { SwingUtilities.invokeAndWait(gui); } catch(Exception x) { throw new Error(x); } |
|
1112 if(!isRun) try { gui.wait(); } catch(InterruptedException x) {} |
|
1113 if(isGui && isReset) reset(); |
|
1114 } while(isGui && !isRun); |
|
1115 } |
|
1116 } |
|
1117 } |
|
1118 log(1, "final balance, connections=" + connCnt + ", forwards=" + forwCnt); |
|
1119 if(gui != null) gui.ui.dispose(); |
|
1120 log(2, "run end"); |
|
1121 } |
|
1122 public static void main(String[] args) throws Exception { |
|
1123 CS cs = new CS(); |
|
1124 cs.run(); |
|
1125 cs.log(1, "cs end"); |
|
1126 //try { cs.run(); } catch(Exception x) { cs.log(0, "interrupted execution"); }; |
|
1127 } |
|
1128 } |
|
1129 // rozeznání konce ve step-módu |
|
1130 // pacing slide |
|
1131 // input fields |
|
1132 // ukládání parametrů |
|
1133 // too many open files |
|
1134 // exceptions |
|
1135 // stavové zprávy na dashboardu |