package nbtool.io; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.EOFException; import java.io.IOException; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketTimeoutException; import java.nio.channels.UnresolvedAddressException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.LinkedList; import javax.swing.SwingUtilities; import nbtool.data.SExpr; import nbtool.data.log.Log; import nbtool.util.Center; import nbtool.util.Center.ToolEvent; import nbtool.util.Events.EventListener; import nbtool.util.SharedConstants; import nbtool.util.Debug; import nbtool.util.ToolSettings; import nbtool.util.Utility; public class CommonIO { /* * Can't use (host, port) constructor because it doesn't allow setting of timeout. * */ public static Socket setupNetSocket(String addr, int port) throws IOException { Socket newsock = new Socket(); InetSocketAddress address = new InetSocketAddress(addr, port); if (address.isUnresolved()) { Debug.error( "CommonIO.setupNetSocket(): ERROR: Could not resolve address: " + addr); throw new UnresolvedAddressException(); } try { newsock.connect(new InetSocketAddress(addr, port), SharedConstants.REMOTE_HOST_TIMEOUT() / 1000); } catch (SocketTimeoutException ste) { Debug.error( "CommonIO.setupNetSocket() could not connect to socket within timeout!"); throw ste; } return newsock; } public static enum IOState { STARTING, RUNNING, ENDING, FINISHED, } public static abstract class IOInstance implements Runnable { protected IOFirstResponder ifr; protected volatile IOState state = IOState.STARTING; protected IOState state() { return state; } public boolean running() {return state == IOState.RUNNING;} public boolean finished() {return state == IOState.FINISHED;} protected Socket socket = null; protected String host; protected int port; public void kill() { if (state == IOState.FINISHED) return; synchronized(this) { state = IOState.ENDING; if (socket != null) { try { socket.shutdownInput(); socket.shutdownOutput(); socket.close(); } catch (IOException e) { Debug.info( "Exception generated by IOInstance.kill(): " + e.getMessage()); } } } } protected void finish() { synchronized(this) { state = IOState.FINISHED; if (socket != null) { try { socket.shutdownInput(); socket.shutdownOutput(); socket.close(); } catch (IOException e) { Debug.info( "Exception generated by IOInstance.finish(): " + e.getMessage()); } socket = null; } } } public String host() { return host; } public int port() { return port; } protected void move(IOState from, IOState to) { synchronized(this) { assert(state == from); state = to; } } public abstract String name(); @Override public String toString() { return name(); } } protected static class SequenceErrorException extends Exception { private static final long serialVersionUID = 1L; public int expected, was; SequenceErrorException(int e, int w) { super(); expected = e; was = w; } @Override public String toString() { return String.format("SequenceError: wanted %d, got %d.", expected, was); } } public static interface IOFirstResponder { public void ioFinished(IOInstance instance); public void ioReceived(IOInstance inst, int ret, Log ... out); /* !CALLED ASYNCHRONOUSLY! */ public boolean ioMayRespondOnCenterThread(IOInstance inst); } public static final class GIOFirstResponder { public static void generateReceived(IOInstance instance, IOFirstResponder responder, int ret, Log ... logs) { if (instance == null || responder == null) return; IOReceived ior = new IOReceived(); ior.instance = instance; ior.responder = responder; ior.ret = ret; ior.logs = logs; Center.addEvent(ior); } public static void generateFinished(IOInstance instance, IOFirstResponder responder) { if (instance == null || responder == null) return; IOFinished iof = new IOFinished(); iof.instance = instance; iof.responder = responder; Center.addEvent(iof); } } protected static class IOReceived extends Center.ToolEvent { protected IOInstance instance; protected IOFirstResponder responder; protected int ret; protected Log[] logs; protected IOReceived() {} @Override protected boolean canCombine() { return false; } @Override protected void combine(LinkedList<ToolEvent> others) {} @Override protected Class<? extends EventListener> listenerClass() { return null; } @Override protected void execute(ArrayList<EventListener> guiList, ArrayList<EventListener> centerList) { assert(guiList == null && centerList == null); if (responder.ioMayRespondOnCenterThread(instance)) { responder.ioReceived(instance, ret, logs); } else { final IOInstance inst = instance; final IOFirstResponder resp = responder; final int r = ret; final Log[] out = logs; SwingUtilities.invokeLater(new Runnable(){ @Override public void run() { resp.ioReceived(inst, r, out); } }); } } } protected static class IOFinished extends Center.ToolEvent { protected IOInstance instance; protected IOFirstResponder responder; protected IOFinished() {} @Override protected boolean canCombine() { return false; } @Override protected void combine(LinkedList<ToolEvent> others) {} @Override protected Class<? extends EventListener> listenerClass() { return null; } @Override protected void execute(ArrayList<EventListener> guiList, ArrayList<EventListener> centerList) { assert(guiList == null && centerList == null); if (responder.ioMayRespondOnCenterThread(instance)) { responder.ioFinished(instance); } else { final IOInstance inst = instance; final IOFirstResponder resp = responder; SwingUtilities.invokeLater(new Runnable(){ @Override public void run() { resp.ioFinished(inst); } }); } } } }