/* 2012-05-20 Version 2.0 Thanks John Reagan for this well-running code which repairs the original obsolete code for Elliott's HostServer program. I've made a few additional changes to John's code, so blame Elliott if something is not running. ----------------------------------------------------------------------- Play with this code. Add your own comments to it before you turn it in. ----------------------------------------------------------------------- NOTE: This is NOT a suggested implementation for your agent platform, but rather a running example of something that might serve some of your needs, or provide a way to start thinking about what YOU would like to do. You may freely use this code as long as you improve it and write your own comments. ----------------------------------------------------------------------- TO EXECUTE: 1. Start the HostServer in some shell. >> java HostServer 1. start a web browser and point it to http://localhost:1565. Enter some text and press the submit button to simulate a state-maintained conversation. 2. start a second web browser, also pointed to http://localhost:1565 and do the same. Note that the two agents do not interfere with one another. 3. To suggest to an agent that it migrate, enter the string "migrate" in the text box and submit. The agent will migrate to a new port, but keep its old state. During migration, stop at each step and view the source of the web page to see how the server informs the client where it will be going in this stateless environment. ----------------------------------------------------------------------------------- COMMENTS: This is a simple framework for hosting agents that can migrate from one server and port, to another server and port. For the example, the server is always localhost, but the code would work the same on different, and multiple, hosts. State is implemented simply as an integer that is incremented. This represents the state of some arbitrary conversation. The example uses a standard, default, HostListener port of 1565. ----------------------------------------------------------------------------------- DESIGN OVERVIEW Here is the high-level design, more or less: HOST SERVER Runs on some machine Port counter is just a global integer incrememented after each assignment Loop: Accept connection with a request for hosting Spawn an Agent Looper/Listener with the new, unique, port AGENT LOOPER/LISTENER Make an initial state, or accept an existing state if this is a migration Get an available port from this host server Set the port number back to the client which now knows IP address and port of its new home. Loop: Accept connections from web client(s) Spawn an agent worker, and pass it the state and the parent socket blocked in this loop AGENT WORKER If normal interaction, just update the state, and pretend to play the animal game (Migration should be decided autonomously by the agent, but we instigate it here with client) If Migration: Select a new host Send server a request for hosting, along with its state Get back a new port where it is now already living in its next incarnation Send HTML FORM to web client pointing to the new host/port. Wake up and kill the Parent AgentLooper/Listener by closing the socket Die WEB CLIENT Just a standard web browser pointing to http://localhost:1565 to start. -------------------------------------------------------------------------------*/ import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; import java.net.ServerSocket; import java.net.Socket; /** * HostServer Notes: This went pretty smoothly for me, although I did have to edit the HTML functions * to get an accurate content length so things would be compatible with browsers other than IE. I also modified * things to eliminate inaccurate state numbers based on fav.ico requests. If the string person wasnt found, * the requests was ignored */ /** * AgentWorker * * AgentWorker objects are created by AgentListeners and process requests made at the various * active ports occupied by agentlistener objects. They take a request and look for the string * migrate in that request(supplied from a get parameter via an html form). If migrate is found, * the worker finds the next availabel port and switches teh client to it. * * I made a small modification because my browser kept requesting fav.ico. So I verified that we receive * a person attribute before processing the request as valid(and incrementing agent state) * */ class AgentWorker extends Thread { Socket sock; //connection to client agentHolder parentAgentHolder; //maintains agentstate holding socket and state counter int localPort; //port being used by this request //basic constructor AgentWorker (Socket s, int prt, agentHolder ah) { sock = s; localPort = prt; parentAgentHolder = ah; } public void run() { //initialize variables PrintStream out = null; BufferedReader in = null; //server is hardcoded in, only acceptable for this basic implementation String NewHost = "localhost"; //port the main worker will run on int NewHostMainPort = 1565; String buf = ""; int newPort; Socket clientSock; BufferedReader fromHostServer; PrintStream toHostServer; try { out = new PrintStream(sock.getOutputStream()); in = new BufferedReader(new InputStreamReader(sock.getInputStream())); //read a line from the client String inLine = in.readLine(); //to allow for usage on non-ie browsers, I had to accurately determine the content //length and as a result need to build the html response so i can determine its length. StringBuilder htmlString = new StringBuilder(); //log a request System.out.println(); System.out.println("Request line: " + inLine); if(inLine.indexOf("migrate") > -1) { //the supplied request contains migrate, switch the user to a new port //create a new socket with the main server waiting on 1565 clientSock = new Socket(NewHost, NewHostMainPort); fromHostServer = new BufferedReader(new InputStreamReader(clientSock.getInputStream())); //send a request to port 1565 to receive the next open port toHostServer = new PrintStream(clientSock.getOutputStream()); toHostServer.println("Please host me. Send my port! [State=" + parentAgentHolder.agentState + "]"); toHostServer.flush(); //wait for the response and read a response until we find what should be a port for(;;) { //read the line and check it for what looks to be a valid port buf = fromHostServer.readLine(); if(buf.indexOf("[Port=") > -1) { break; } } //extract the port by leveraging the format of the port response String tempbuf = buf.substring( buf.indexOf("[Port=")+6, buf.indexOf("]", buf.indexOf("[Port=")) ); //parse the response for the integer containing the new port newPort = Integer.parseInt(tempbuf); //log it to the server console System.out.println("newPort is: " + newPort); //prepare the html response to send the user htmlString.append(AgentListener.sendHTMLheader(newPort, NewHost, inLine)); //inform the user that the migration request was received htmlString.append("