CSj/CS.java
changeset 0 5c129dd80d4f
equal deleted inserted replaced
-1:000000000000 0:5c129dd80d4f
       
     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