/* James Raitsev 2012-05-20. Improvements to Elliott. */ import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; import java.net.ServerSocket; import java.net.Socket; /** * Host Server class. Main responsibility is to accept connections on predefined * port , figure out what next available port is and pass the request to the * AgentListener class, that will do all the work */ public class HostServer { /** * Statically defined starting position */ public static int nextPort = 30000; public static final int PORT = 2525; public static final String HOST = "localhost"; public static void main(String a[]) throws IOException { int q_len = 6; // Queue length Socket sock; ServerSocket servsock = new ServerSocket(PORT, q_len); System.out.println("James's DIA Master receiver started at " + PORT + "\n"); /** * Now until the end of time, Host Server will listen for incoming * requests (which will come only on port 2565) in order to delegate * handling of this request to the AgentListener (new Thread), which * would would handle incoming connections on a new post. * * We hardcode a starting location at port 30,000. Each subsequent * connection will start off with that number incremented by 1. This * guarantees that each AgentListener will start off using unique port. */ while (true) { nextPort = nextPort + 1; sock = servsock.accept(); System.out.println("Starting an AgentListener at port " + nextPort); new AgentListener(sock, nextPort).start(); } } } class AgentWorker extends Thread { Socket sock; int port; AgentHolder parentAgentHolder; /** * Constructor used to set the state of the worker * * @param sock Socket, on which connection was accepted by the Listener * @param port HTTP port passed from the Listener (and Host Server before * that) * @param agentHolder AhentHolder contains current application state. This * is how state is passed around from the listener to worker */ AgentWorker(Socket sock, int port, AgentHolder agentHolder) { this.sock = sock; this.port = port; this.parentAgentHolder = agentHolder; } /* * (non-Javadoc) * @see java.lang.Thread#run() */ public void run() { System.out.println("In agentWorker Thread for agent."); PrintStream out = null; BufferedReader in = null; // int NewHostMainPort = 1565; int newPort; Socket clientSock; BufferedReader fromHostServer; PrintStream toHostServer; try { out = new PrintStream(sock.getOutputStream()); in = new BufferedReader(new InputStreamReader(sock.getInputStream())); String inLine = in.readLine(); /** * If client request a migration, we need to effectively recognize * that migration will be asked of the Host Server. This means that * we'll need to connect to the Host Server, pretending as if * request came from outside */ if (inLine.toLowerCase().contains("migrate")) { System.out.println("Migration request received"); /** * Go back to the Host Server and ask for a new connection. * * By going so, effectively, we're asking for the new cycle of * HostServer->AgentListener->AgentWorker * */ clientSock = new Socket(HostServer.HOST, HostServer.PORT); fromHostServer = new BufferedReader(new InputStreamReader(clientSock.getInputStream())); // Get new PrintStream to write interesting things to server toHostServer = new PrintStream(clientSock.getOutputStream()); // Plz host me! toHostServer.println("Please host me. Send my port! [State=" + parentAgentHolder.agentState + "]"); toHostServer.flush(); /* * We really only needed the port. The HTML was sent as * convenience for starting with an initial request to the * HostServer from a web client: Read until we find the port * number... */ String message = ""; while (true) { message = fromHostServer.readLine(); if (message.indexOf("[Port=") > -1) { break; } } // String message received from the host server String tempbuf = message.substring(message.indexOf("[Port=") + 6, message.indexOf("]", message.indexOf("[Port="))); // new port to be used (Ultimately given out by the Host Server) newPort = Integer.parseInt(tempbuf); System.out.println("newPort is: " + newPort); // Friendly message to be sent back to browser out.println("

We are Migrating to " + HostServer.HOST + " " + newPort + "

"); AgentListener.sendHTMLsubmit(out); System.out.println("Killing parent listening loop."); // Close socket which was originally assigned to Worker (prior // to migration) parentAgentHolder.sock.close(); } else { // Update state parentAgentHolder.agentState++; AgentListener.sendHTMLheader(port, HostServer.HOST, inLine, out); // Print a friendly message back to client out.println("

We are playing the animal game with state " + parentAgentHolder.agentState + "

"); AgentListener.sendHTMLsubmit(out); } // close this connection, but not the server sock.close(); } catch (IOException e) { e.printStackTrace(); } } } /** * Minion of HostServer. Will accept connection on the port (hidden from user), * which it was given and will handle communication between */ class AgentListener extends Thread { Socket sock; int port; int agentState = 0; /** * @param sock Socket created by Host Server * @param port Post this AgentListener will receive requests on */ public AgentListener(Socket sock, int port) { this.sock = sock; this.port = port; } /* * (non-Javadoc) * @see java.lang.Thread#run() */ public void run() { BufferedReader fromBrowser = null; PrintStream toBrowser = null; // Host server is running on. In our example it'll be localhost String host = "localhost"; try { String httpRequest; toBrowser = new PrintStream(sock.getOutputStream()); // Reader used to get requests from Browser fromBrowser = new BufferedReader(new InputStreamReader(sock.getInputStream())); // Actual request we got httpRequest = fromBrowser.readLine(); /** * If http request contains word [State=, we'll figure out what it * is (numeric value assigned to it) and print it out. * * It is important to recognize that likely sender of this message * is AgentWorker (see migration code in AgentWorker) */ if (httpRequest.indexOf("[State=") > -1) { System.out.println(" ------> State message received!"); String tempbuf = httpRequest.substring(httpRequest.indexOf("[State=") + 7, httpRequest.indexOf("]", httpRequest.indexOf("[State="))); agentState = Integer.parseInt(tempbuf); System.out.println("AgentState is: " + agentState); } System.out.println("httpRequest:" + httpRequest); // Send HTML header to the browser to be displayed sendHTMLheader(port, host, httpRequest, toBrowser); // Additional text to be displayed on the browser toBrowser.println("Now in Agent Looper starting Agent Listening Loop\n
"); toBrowser.println("[Port=" + port + "]
"); // Sent HTML code for submit button sendHTMLsubmit(toBrowser); // Create new socket connection (with queue of 2), on the port we // got (from Host Server). ServerSocket servsock = new ServerSocket(port, 2); // Initialize new Agent Holder with the servSock (to hold the state) AgentHolder agenthold = new AgentHolder(servsock); // Advise the agent of current state System.out.println("\nAgent Holder knows of state: " + agentState); agenthold.agentState = agentState; /** * As long as we're on this Agent Listener, we may accept requests * on whatever port we were given (by Host Server). Once new request * comes int we'll spawn a worker thread to do things for us. * * Example of a request will be user hitting the submit button on * the html form and passing us some stuff */ while (true) { sock = servsock.accept(); System.out.println("Got a connection to agent at port " + port); new AgentWorker(sock, port, agenthold).start(); } } catch (IOException e) { System.err.println("Either connection failed, or just killed Listener Loop for agent at port " + port); e.printStackTrace(); } } /** * Sends HTML Header back to the out stream * * @param localPort Port on which we're running locally * @param host Host on which we're running * @param message Message we got * @param out PrintStream to which we write */ static void sendHTMLheader(int localPort, String host, String message, PrintStream out) { out.println("HTTP/1.1 200 OK"); // Actual length does not matter out.println("Content-Length: 700"); out.println("Content-Type: text/html"); out.println(""); out.println(" "); out.println("

This is for submission to PORT " + localPort + " on " + host + "

"); out.println("

You sent:" + message + "

"); out.println("
"); out.println("Enter text or migrate:"); out.println("

"); out.flush(); } /** * Sends Html submit form to the browser * * @param out PrintStream to which we write */ static void sendHTMLsubmit(PrintStream out) { out.println(""); out.println("

"); out.flush(); } } /** * The purpose of this class is to hold numeric representation of state and the * socket used */ class AgentHolder { ServerSocket sock; int agentState; public AgentHolder(ServerSocket servsock) { sock = servsock; } }