package nbtool.nio; import static nbtool.util.ToolSettings.NBCROSS_CALL_TIMEOUT; import static nbtool.util.ToolSettings.NBCROSS_PORT; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.LinkedList; import java.util.Scanner; import java.util.Timer; import java.util.TimerTask; import nbtool.data.SExpr; import nbtool.data.log.Log; import nbtool.io.CommonIO; import nbtool.io.CommonIO.GIOFirstResponder; import nbtool.io.CommonIO.IOFirstResponder; import nbtool.io.CommonIO.IOInstance; import nbtool.io.CommonIO.IOState; import nbtool.nio.LogRPC.RemoteCall; import nbtool.util.Debug; import nbtool.util.Events; import nbtool.util.SharedConstants; import nbtool.util.Utility; public class CrossServer { private static final Debug.DebugSettings debug = Debug.createSettings(true, true, true, Debug.WARN, null); public static void _NBL_REQUIRED_START_() { startCrossServer(); } public static void startCrossServer() { if (crossServerLive()) { Debug.error( "CrossServer already running!"); return; } Debug.info( "starting nbtool-CrossServer..."); serverThread = new Thread(new Server(), "nbtool-CrossServer"); serverThread.setDaemon(true); serverThread.start(); } public static boolean crossServerLive() { if (serverThread == null) return false; return serverThread.isAlive(); } public static class CrossInstance extends CommonIO.IOInstance { protected CrossInstance(Socket sock) { this.socket = sock; this.ifr = ifr; } public String name = null; private final LinkedList<RemoteCall> calls = new LinkedList<>(); public boolean tryAddCall(RemoteCall call) { synchronized(calls) { calls.add(call); calls.notify(); } return true; } public boolean tryAddCall(IOFirstResponder ifr, String name, Log...args) { return this.tryAddCall(new RemoteCall(ifr, name, args)); } private static final Object indexLock = new Object(); private static long class_index = 0; private static long getID() { long ret; synchronized(indexLock) { ret = class_index++; } return ret; } public final long unique_id = getID(); @Override public void run() { Debug.info( "CrossInstance %d starting up.", this.unique_id); assert(socket != null); Log heartbeat = Log.explicitLog(null, null, SharedConstants.LogClass_Null(), 0); int timeout = SharedConstants.REMOTE_HOST_TIMEOUT() / 2000; try { this.host = socket.getInetAddress().getHostAddress(); //Setup connection and gather functions. BufferedOutputStream os = new BufferedOutputStream(socket.getOutputStream()); BufferedInputStream is = new BufferedInputStream(socket.getInputStream()); move(IOState.STARTING, IOState.RUNNING); Events.GCrossStatus.generate(this, true); while (this.state() == IOState.RUNNING) { RemoteCall call = null; synchronized(calls) { if(calls.isEmpty()) { calls.wait(timeout); } call = calls.pollFirst(); } if (call == null) { debug.info("%s heartbeat...", this); ++(heartbeat.createdWhen); heartbeat.writeTo(os); os.flush(); Log hbBack = Log.parseFromStream(is); assert(hbBack.logClass.equals(SharedConstants.LogClass_Null())); } else { debug.info("%s call...", this); call.call.writeTo(os); os.flush(); Log ret = Log.parseFromStream(is); while(ret.logClass.equals(SharedConstants.LogClass_Null())) ret = Log.parseFromStream(is); assert(ret.logClass.equals(SharedConstants.LogClass_RPC_Return())); call.finish(this, ret); } } } catch (IOException e) { Debug.warn("%s got IOException: %s (terminating connection)", this, e.getMessage()); } catch (Throwable t) { t.printStackTrace(); } finally { Debug.warn( "%s dieing.", this); finish(); remove(this); Events.GCrossStatus.generate(this, false); } } @Override public String name() { if (name != null) { return String.format("CrossInstance{%d:%s}", unique_id, name); } else { return String.format("CrossInstance{%d}", unique_id); } } } private static final LinkedList<CrossInstance> instances = new LinkedList<>(); private static Thread serverThread = null; public static CrossInstance instanceByIndex(int i) { synchronized(instances) { if (i < instances.size()) return instances.get(i); } return null; } public static CrossInstance instanceByName(String name) { synchronized(instances) { for (CrossInstance ci : instances) { if (ci.name != null && ci.name.equals(name)) { return ci; } } } return null; } public static CrossInstance[] allInstances() { synchronized(instances) { return instances.toArray(new CrossInstance[0]); } } private static void remove(CrossInstance i) { synchronized(instances) { assert(instances.remove(i)); } } private static class Server implements Runnable { @Override public void run() { ServerSocket server = null; try { server = new ServerSocket(SharedConstants.CROSS_PORT(), 1, null); } catch (UnknownHostException e1) { e1.printStackTrace(); return; } catch (IOException e1) { e1.printStackTrace(); return; } Debug.info( "CrossServer.Server bound and up."); try { while (true) { Socket socket = null; try { socket = server.accept(); socket.setSoTimeout(SharedConstants.REMOTE_HOST_TIMEOUT() / 1000); CrossInstance ci = new CrossInstance(socket); synchronized(instances) { instances.add(ci); } Thread t = new Thread(ci, String.format("nbtool-CrossInstance-%d", ci.unique_id)); t.setDaemon(true); t.start(); } catch (Exception e) { Debug.error( "Exception in CrossServer while accepting connection."); e.printStackTrace(); } } } catch (Throwable t) { Debug.error( "CrossServer dieing because: %s", t.getMessage()); t.printStackTrace(); } finally { if (server != null) { try { server.close(); } catch (IOException e) { e.printStackTrace(); } } } } } public static void main(String[] args) throws InterruptedException, IOException { startCrossServer(); Scanner in = new Scanner(System.in); while(true) { Thread.sleep(1000); CrossInstance ci = CrossServer.instanceByIndex(0); if (ci != null) { in.nextLine(); ci.tryAddCall(null, "Test"); } } } } //CrossServer