95-702 OCT May 2009 Homework 2 Due: 11:59:59 PM Friday, June 12, 2009 ========== ====================================== In class we reviewed three java programs that used TCP sockets to play knock knock jokes. In this lab you will remove all of the socket code and build two web based applications that plays knock knock. The first application will be a web site using Glassfish and the second will be a web service using Glassfish and JAX-WS 2.0. In the spirit of code reuse, we will not make any changes to the logic of the KnockKnockProtocol class provided. All of the code needs to be well documented. This includes the code provided by Sun. That is, you will comment the code that implements the finite state machine. Try to make it easy for a reader to understand what is going on. This will convince us that you have studied the code and understand it. Part 1. 50 Points Knock Knock Joke Web Application using Glassfish ================================================================== Build a small web application (web site) using Glassfish. This web application will contain the files KnockKnockServlet.java and KnockKnockProtocol.java only. That is, the application will not contain any separate HTML or JSP files. The servlet will be accessed by browsers with the URL: http://localhost:8080/KnockKnockProject/KnockKnock. On the initial visit, the servlet will create a new KnockKnockProtocol object and add it to an HTTPSession object. The first response will automatically return a cookie to the browser. You should ensure that this is the case by viewing cookies in your browser. You do not need to create the cookies in your code. Glassfish will generate cookies for you when it sees that you are using HTTPSessions. Experiment and try creating new sessions by clearing old cookies. The HTTPSession object will associate a particular KnockKnockProtocol object with a particular cookie. The servlet will first respond with the prompt "Knock Knock" followed by a text box. The prompt "Knock Knock", being sent to the browser, must be retrieved by the servlet from the KnockKnockProtocol object associated with this session. When the user responds to the initial "Knock Knock" the same servlet is executed again. The KnockKnockProtocol object associated with this user session is accessed from the HTTPSession object. This approach allows us to have several knock knock users playing knock knock jokes at the same time. There will be a separate KnockKnockProtocol object for each user. Each of these will track the state of the protocol. The game should be playable and error messages displayed on the browser as they are generated by the protocol handler. You should experiment with more than one browser using the application at the same time. You will need either two browsers (IE and Opera for example) or you may wish to visit the web application from a separate machine. In the latter case, you need to be connected to the internet and replace the string 'localhost' with the IP address of the web server. Part 2. 50 Points Knock Knock Joke Web Service using JAX-WS 2.0 =============================================================== In Part 1 we developed a web site or web application. In Part 2 we will develop a web service that performs in a similar manner - allowing users to play knock knock jokes with a server. In preparation, here is how we might build a simple web service using Netbeans 6.5 and Glassfish. 1) File/New Project/Java Web/Web Application/PrjName/Location/Glassfish V2/Finish 2) Right Click Project Node/New/Web Service/WSName/PkgName/Finish 3) Design/Add Operation/Provide method name, parameter names and types 4) Source/Complete details of method. 5) Right click Project Node/Deploy 6) Expand Web Services node/Right Click Service name/Test Web Service 7) Use the browser to test and examine input and output messages. 8) View WSDL and copy the WSDL URL to the clipboard. At this point, we can write a web service client. The client might be a stand alone application or a servlet. Here we will write a simple stand alone application. 1) File/New Project/Java/Java Application/PrjName/location/Finish 2) Right click project/New Web Service Client/WSDL URL/paste from clipboard 3) Note the Web Service Reference node in project tree. 4) Expand Web Service Reference down to desired method. 5) Drag and drop into appropriate source location. Write a web service that has a single method with the signature: public synchronized String play(String fromClient, String userID) We may have many simultaneous users playing knock knock jokes. The play method is marked as synchronized so that only one thread may execute the code at a time. The first parameter will represent the text input from the player. A typical input would be "Little Old Lady Who?". The second parameter represents the player's identification. This will be passed on each call from the player. This allows the play method to track each player's progress through the knock knock protocol. The response from the play method is a string that represents the protocol handler's response. A typical response would be "Knock Knock!". The play method makes use of the java.util.TreeMap class. A TreeMap object holds a set of name value pairs. The name is the userID and the value is the protocol handler associated with the userID. In this way, the service can track simultaneous users. Write a console application in Java that allows users to play knock knock jokes with the web service that you developed. The console application will be executed with the command "java KnockKnockClient userID". The execution will proceed in the same way as the original socket based solution. In order to establish a command line parameter using Netbeans, right click the project node and select properties. Select run and enter the command line arguments. The knock knock socket-level code from Sun is included below. KnockKnockClient.java ===================== // From Sun Microsystems import java.io.*; import java.net.*; public class KnockKnockClient { public static void main(String[] args) throws IOException { Socket kkSocket = null; PrintWriter out = null; BufferedReader in = null; try { kkSocket = new Socket("localhost", 4444); out = new PrintWriter(kkSocket.getOutputStream(), true); in = new BufferedReader(new InputStreamReader(kkSocket.getInputStream())); } catch (UnknownHostException e) { System.err.println("Don't know about host: localhost"); System.exit(1); } catch (IOException e) { System.err.println("Couldn't get I/O for the connection to: taranis."); System.exit(1); } BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in)); String fromServer; String fromUser; while ((fromServer = in.readLine()) != null) { System.out.println("Server: " + fromServer); if (fromServer.equals("Bye.")) break; fromUser = stdIn.readLine(); if (fromUser != null) { System.out.println("Client: " + fromUser); out.println(fromUser); } } out.close(); in.close(); stdIn.close(); kkSocket.close(); } } KnocKnockServer.java ==================== // From Sun Microsystems import java.net.*; import java.io.*; public class KnockKnockServer { public static void main(String[] args) throws IOException { ServerSocket serverSocket = null; try { serverSocket = new ServerSocket(4444); } catch (IOException e) { System.err.println("Could not listen on port: 4444."); System.exit(1); } Socket clientSocket = null; try { clientSocket = serverSocket.accept(); } catch (IOException e) { System.err.println("Accept failed."); System.exit(1); } PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true); BufferedReader in = new BufferedReader( new InputStreamReader( clientSocket.getInputStream())); String inputLine, outputLine; KnockKnockProtocol kkp = new KnockKnockProtocol(); outputLine = kkp.processInput(null); out.println(outputLine); while ((inputLine = in.readLine()) != null) { outputLine = kkp.processInput(inputLine); out.println(outputLine); if (outputLine.equals("Bye.")) break; } out.close(); in.close(); clientSocket.close(); serverSocket.close(); } } KnockKnockProtocol.java ======================= // From Sun Microsystems import java.net.*; import java.io.*; public class KnockKnockProtocol { private static final int WAITING = 0; private static final int SENTKNOCKKNOCK = 1; private static final int SENTCLUE = 2; private static final int ANOTHER = 3; private static final int NUMJOKES = 5; private int state = WAITING; private int currentJoke = 0; private String[] clues = { "Turnip", "Little Old Lady", "Atch", "Who", "Who" }; private String[] answers = { "Turnip the heat, it's cold in here!", "I didn't know you could yodel!", "Bless you!", "Is there an owl in here?", "Is there an echo in here?" }; public String processInput(String theInput) { String theOutput = null; if (state == WAITING) { theOutput = "Knock! Knock!"; state = SENTKNOCKKNOCK; } else if (state == SENTKNOCKKNOCK) { if (theInput.equalsIgnoreCase("Who's there?")) { theOutput = clues[currentJoke]; state = SENTCLUE; } else { theOutput = "You're supposed to say \"Who's there?\"! " + "Try again. Knock! Knock!"; } } else if (state == SENTCLUE) { if (theInput.equalsIgnoreCase(clues[currentJoke] + " who?")) { theOutput = answers[currentJoke] + " Want another? (y/n)"; state = ANOTHER; } else { theOutput = "You're supposed to say \"" + clues[currentJoke] + " who?\"" + "! Try again. Knock! Knock!"; state = SENTKNOCKKNOCK; } } else if (state == ANOTHER) { if (theInput.equalsIgnoreCase("y")) { theOutput = "Knock! Knock!"; if (currentJoke == (NUMJOKES - 1)) currentJoke = 0; else currentJoke++; state = SENTKNOCKKNOCK; } else { theOutput = "Bye."; state = WAITING; } } return theOutput; } }