/* * Copyright 2017 Nokia Solutions and Networks * Licensed under the Apache License, Version 2.0, * see license.txt file for details. */ package org.rf.ide.core.execution.server; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketTimeoutException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import org.rf.ide.core.execution.RobotAgentEventListener; import org.rf.ide.core.execution.RobotAgentEventListener.RobotAgentEventsListenerException; public class AgentConnectionServer { public static final int RED_AGENT_PROTOCOL_VERSION = 1; public static final String DEFAULT_CONNECTION_HOST = "127.0.0.1"; public static final int DEFAULT_CONNECTION_PORT = 43_981; // 0xABCD public static final int DEFAULT_CONNECTION_TIMEOUT = 30; public static final int MIN_CONNECTION_PORT = 1; public static final int MIN_CONNECTION_TIMEOUT = 1; public static final int MAX_CONNECTION_PORT = 65_535; public static final int MAX_CONNECTION_TIMEOUT = 3_600; public static final int findFreePort() { try (ServerSocket socket = new ServerSocket(0)) { return socket.getLocalPort(); } catch (final IOException e) { return -1; } } private final int port; private final String host; private final int timeoutInMillis; private final List<AgentServerStatusListener> listeners = new ArrayList<>(); private final Semaphore serverSetupSemaphore = new Semaphore(0); private ServerSocket serverSocket; public AgentConnectionServer(final String host, final int port) { this(host, port, DEFAULT_CONNECTION_TIMEOUT, TimeUnit.SECONDS); } public AgentConnectionServer(final String host, final int port, final int timeout, final TimeUnit timeoutUnit) { this.host = host; this.port = port; this.timeoutInMillis = (int) timeoutUnit.toMillis(timeout); } public void addStatusListener(final AgentServerStatusListener listener) { listeners.add(listener); } public void removeStatusListener(final AgentServerStatusListener listener) { listeners.remove(listener); } public void start(final RobotAgentEventListener... eventsListeners) throws IOException { try { serverSetupSemaphore.release(); serverSocket = new ServerSocket(port, 50, InetAddress.getByName(host)); serverSocket.setReuseAddress(true); serverSocket.setSoTimeout(timeoutInMillis); listeners.forEach(listener -> listener.serverEstablished(host, port)); try (Socket clientSocket = serverSocket.accept()) { final int clientId = clientSocket.hashCode(); final BufferedReader eventsReader = new BufferedReader( new InputStreamReader(clientSocket.getInputStream())); final PrintWriter eventsWriter = new PrintWriter(clientSocket.getOutputStream()); final RobotAgentEventDispatcher eventsDispatcher = new RobotAgentEventDispatcher( new AgentClient(clientId, eventsWriter), eventsListeners); listeners.forEach(listener -> listener.clientConnected(clientId)); eventsDispatcher.runEventsLoop(eventsReader); listeners.forEach(listener -> listener.clientConnectionClosed(clientId)); } catch (final RobotAgentEventsListenerException e) { listeners.forEach(listener -> listener.clientEventHandlingError(e)); } catch (final SocketTimeoutException e) { listeners.forEach(listener -> listener.clientConnectionTimedOut(e)); } catch (final IOException e) { listeners.forEach(listener -> listener.clientConnectionError(e)); } } finally { stop(); } } public void waitForServerToSetup() throws InterruptedException { serverSetupSemaphore.acquire(); } public void stop() throws IOException { if (serverSocket != null) { serverSocket.close(); } } }