All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.snapscript.studio.agent.client.ConnectTunnelClient Maven / Gradle / Ivy

There is a newer version: 1.4.6
Show newest version
package org.snapscript.studio.agent.client;

import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;

import org.snapscript.studio.agent.core.QueueExecutor;
import org.snapscript.studio.agent.event.BeginEvent;
import org.snapscript.studio.agent.event.BreakpointsEvent;
import org.snapscript.studio.agent.event.BrowseEvent;
import org.snapscript.studio.agent.event.EvaluateEvent;
import org.snapscript.studio.agent.event.ExecuteEvent;
import org.snapscript.studio.agent.event.ExitEvent;
import org.snapscript.studio.agent.event.FaultEvent;
import org.snapscript.studio.agent.event.PingEvent;
import org.snapscript.studio.agent.event.PongEvent;
import org.snapscript.studio.agent.event.ProcessEvent;
import org.snapscript.studio.agent.event.ProcessEventChannel;
import org.snapscript.studio.agent.event.ProcessEventConnection;
import org.snapscript.studio.agent.event.ProcessEventConsumer;
import org.snapscript.studio.agent.event.ProcessEventListener;
import org.snapscript.studio.agent.event.ProcessEventProducer;
import org.snapscript.studio.agent.event.ProfileEvent;
import org.snapscript.studio.agent.event.RegisterEvent;
import org.snapscript.studio.agent.event.ScopeEvent;
import org.snapscript.studio.agent.event.ScriptErrorEvent;
import org.snapscript.studio.agent.event.StepEvent;
import org.snapscript.studio.agent.event.WriteErrorEvent;
import org.snapscript.studio.agent.event.WriteOutputEvent;
import org.snapscript.studio.agent.log.TraceLogger;

public class ConnectTunnelClient {
   
   private static final String THREAD_NAME = "%s: %s@%s:%s";
   
   private final ProcessEventListener listener;
   private final QueueExecutor executor;
   private final ConnectionChecker checker;
   private final TraceLogger logger;
   
   public ConnectTunnelClient(ProcessEventListener listener, ConnectionChecker checker, TraceLogger logger) throws IOException {
      this.executor = new QueueExecutor();
      this.listener = listener;
      this.checker = checker;
      this.logger = logger;
   }
   
   public ProcessEventChannel connect(String process, String host, int port) throws Exception {
      String type = SocketConnection.class.getSimpleName();
      String name = String.format(THREAD_NAME, type, process, host, port);
      
      try {
         Socket socket = new Socket(host, port);
         ConnectTunnelHandler tunnel = new ConnectTunnelHandler(logger, process, port);
         InputStream input = socket.getInputStream();
         OutputStream output = socket.getOutputStream();
         SocketClientSession session = new SocketClientSession(checker, socket);
         SocketConnection connection = new SocketConnection(session, input, output);
      
         executor.start();
         connection.setName(name);
         tunnel.tunnel(socket); // do the tunnel handshake
         socket.setSoTimeout(10000);
         connection.start();
         return connection;
      }catch(Exception e) {
         throw new IllegalStateException("Could not connect to " + host + ":" + port, e);
      }
   }
   
   private class SocketClientSession implements Closeable {
      
      private final ConnectionChecker checker;
      private final Socket socket;
      
      public SocketClientSession(ConnectionChecker checker, Socket socket) {
         this.checker = checker;
         this.socket = socket;
      }

      @Override
      public void close() {
         try {
            checker.close();
         } catch(Exception e) {
            e.printStackTrace();
         }
         try {
            socket.shutdownInput();
         } catch(Exception e) {
            //e.printStackTrace();
         }
         try {
            socket.shutdownOutput();
         } catch(Exception e) {
            //e.printStackTrace();
         }
         try {
            executor.stop();
            socket.close();
         } catch(Exception e) {
            //e.printStackTrace();
         }
      }
   }

   private class SocketConnection extends Thread implements ProcessEventChannel {
      
      private final ProcessEventConnection connection;
      private final SocketClientSession session;
      private final AtomicBoolean closed;
      private final Set events;
      
      public SocketConnection(SocketClientSession session, InputStream input, OutputStream output) throws IOException {
         this.connection = new ProcessEventConnection(logger, executor, input, output, session);
         this.events = new CopyOnWriteArraySet();
         this.closed = new AtomicBoolean();
         this.session = session;
      }
      
      @Override
      public boolean send(ProcessEvent event) throws Exception {
         ProcessEventProducer producer = connection.getProducer();
         String process = event.getProcess();
         
         try {
            producer.produce(event);
            return true;
         } catch(Exception e) {
            logger.info(process + ": Error sending event", e);
            close(process + ": Error sending event " +event + ": " + e);
         }
         return false;
      }

      @Override
      public boolean sendAsync(ProcessEvent event) throws Exception {
         ProcessEventProducer producer = connection.getProducer();
         String process = event.getProcess();

         try {
            Future future = producer.produceAsync(event);
            return future.get();
         } catch(Exception e) {
            logger.info(process + ": Error sending async event", e);
            close(process + ": Error sending async event " +event + ": " + e);
         }
         return false;
      }
      
      @Override
      public void run() {
         try {
            ProcessEventConsumer consumer = connection.getConsumer();
            
            while(true) {
               ProcessEvent event = consumer.consume();
               Class type = event.getClass();
               
               events.add(type);
               
               if(event instanceof ExitEvent) {
                  listener.onExit(this, (ExitEvent)event);
               } else if(event instanceof ExecuteEvent) {
                  listener.onExecute(this, (ExecuteEvent)event);                  
               } else if(event instanceof RegisterEvent) {
                  listener.onRegister(this, (RegisterEvent)event);
               } else if(event instanceof ScriptErrorEvent) {
                  listener.onScriptError(this, (ScriptErrorEvent)event);
               } else if(event instanceof WriteErrorEvent) {
                  listener.onWriteError(this, (WriteErrorEvent)event);
               } else if(event instanceof WriteOutputEvent) {
                  listener.onWriteOutput(this, (WriteOutputEvent)event);
               } else if(event instanceof PingEvent) {
                  listener.onPing(this, (PingEvent)event);
               } else if(event instanceof PongEvent) {
                  listener.onPong(this, (PongEvent)event);
               } else if(event instanceof ScopeEvent) {
                  listener.onScope(this, (ScopeEvent)event);
               } else if(event instanceof BreakpointsEvent) {
                  listener.onBreakpoints(this, (BreakpointsEvent)event);
               } else if(event instanceof BeginEvent) {
                  listener.onBegin(this, (BeginEvent)event);
               } else if(event instanceof StepEvent) {
                  listener.onStep(this, (StepEvent)event);
               } else if(event instanceof BrowseEvent) {
                  listener.onBrowse(this, (BrowseEvent)event);
               } else if(event instanceof EvaluateEvent) {
                  listener.onEvaluate(this, (EvaluateEvent)event);                  
               } else if(event instanceof ProfileEvent) {
                  listener.onProfile(this, (ProfileEvent)event);
               } else if(event instanceof FaultEvent) {
                  listener.onFault(this, (FaultEvent)event);
               }
            }
         }catch(Exception e) {
            logger.info("Error processing events ["+ events + "]", e);
            close("Error in event loop: " + e);
         } finally {
            close("Event loop has finished");
         }
      }
      
      @Override
      public void close(String reason) {
         try {
            ProcessEventProducer producer = connection.getProducer();
            
            if(closed.compareAndSet(false, true)) {
               listener.onClose(this);
               producer.close(reason);
            }
            session.close();
         } catch(Exception e) {
            logger.info("Error closing client connection", e);
         }
      }
   }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy