Carnegie Mellon University June 2009 95-702 Organizational Communications and Distributed Object Technologies Homework 3 Due by 11:59 PM on Friday, June 26, 2009 In homework 1 we worked with J2EE servlets and Java Server Pages using the Glassfish web container. In homework 2 we worked with J2EE servlets and web services using Glassfish and JAX-WS 2.0. In this lab we will be working at a lower level. That is, we will not have the Glassfish or the JAX-WS runtimes to rely on. In the first part of this homework you will work with UDP and TCP over IP. UDP is a simpler protocol than TCP and therefore usually requires more work from the application programmer. TCP presents the application programmer with a stream abstraction and is busier below the scenes. TCP, unlike UDP, tries its best to make sure packets are delivered to the recipient. In the second part you will work with low level RMI in Java. I call it "low level" because we are not using the traditional Java RMI infrastructure. We will do that in a later lab. There are five required submissions. In each case, you will create a zip file with a directory holding all of your work. The zip files will be submitted to the assignment section of blackboard. Part I. UDP and TCP From Chapter 4 of the Coulouris text ======================================================== (1) On pages 138 and 139 of the Coulouris text, two short programs are presented. Make modifications to the UDPClient program and the UDPServer program so that the client may ask the server to perform simple integer arithmetic. You only need implement addition and subtraction of primitive integers. You may assume that you have a well behaved user and all input is correct. You may also assume that the user uses spaces to separate the command line arguments. The execution of the client program will look like the following: java UDPClient 100 + 234 Reply:334 In Netbeans, command line arguments can be set by choosing Run/Set Project Configuration/Customize. Submission 1. 10 Points. Submit to blackboard a single zipped Netbeans project holding your UDP client and UDP server code. Name the zip file prj31.zip. The UDPClient and UDPServer code must be well documented. (2) Using the same UDP server that you wrote in part 1, write a new UDP client that has a main routine that computes and displays the sum of the integers 1+2+3+...+100. The main routine must be very clean and contain no socket level programming. All of the socket work will be done within a single method with the following signature: public static int add(int x,int y); The main routine will make 100 calls on the add method. The add method will not perform any addition. Instead, it will send a UDP message to the server and receive the server's response. It will return that response as a simple int. Submission 2. 10 Points. Submit to blackboard a single zipped Netbeans project holding your UDP client and UDP server code. Name the zip file prj32.zip. (3) On pages 142 and 143 of the Coulouris text, two short programs are presented. Make modifications to the TCPClient program and the TCPServer program so that the client may ask the server to perform simple integer arithmetic. You only need implement addition and subtraction of primitive integers. You may assume that you have a well behaved user and all input is correct. You may also assume that the user uses spaces to separate the command line arguments. The execution of the client program will look like the following: java TCPClient 100 + 234 Reply:334 Submission 3. 10 Points. Submit to blackboard a single zipped Netbeans project holding your TCP client and TCP server code. Name the zip file prj33.zip. The TCPClient and TCPServer code must be well documented. Part II. Low Level RMI ====================== See the programs at the end of this document. These programs illustrate low level remote method invocation (RMI). That is, we are not yet working with Java RMI. We are implementing a simple RMI system using TCP sockets. (4) Your first task is to get these programs running and to study them closely. The terms "skeleton" and "stub" are used and you should understand exactly what kind of objects these terms refer to. Get the programs working in two separate Netbeans projects as demonstrated in class. Two separate projects works best because it clearly separates the client code from the server code. The interface file (Person.java) must be placed on the server side as well as the client side. When the server and client are run, Netbeans will present two different console windows - one for the client and one for the server. There is a box on the console window that may be used to stop the server process. Submission 4. 20 Points. Submit to blackboard one zipped directory holding two Netbeans projects. One project will hold your client code and another will hold your server code. Use the same naming scheme as before. All of the Java source code will be well documented. Using a Registry ================ The following questions ask you to build a distributed object server, a registry and a client. The registry is located at port 9090. / \ lookup / \bind / \ The client makes a \ lookup call The object server at port 9000 on the registry -----rmi----- makes a bind call on the registry and then calls and then services calls methods on the remote on an object. object. (5) Create three new Netbeans projects named "ClientProject", "ObjectServerProject", and "RegistryServerProject". a. Write a new Java class called RemoteObjectReference that implements Serializable. This class will encapsulate the private fields found in Figure 4.13, on page 154, of the Coulouris text. This class will have getter and setter methods for each field and a small main routine used as a test driver. The "interface of object" field will be defined as a String object. The IPAddress field is an array of 4 bytes. The other two fields are simple integers. This class will be present in ClientProject, ObjectServerProject and the RegistryServerProject projects. b. Write a new Java class called RequestReplyMessage that implements Serializable. This class will encapsulate the private fields found in Figure 4.16, on page 157, of the Coulouris text. Note that a RequestReplyMessage object has a RemoteObjectReference member. This must be the same class as defined in the previous step. Setter and getter methods need to be defined. Note that a RemoteObjectReference object contains enough information to specify a remote object. The RequestReplyMessage contains, in addition, information on the method that will be invoked (or was invoked) on that object as well as an array of bytes specifying the arguments to or return values from the method. The RequestReplyMessage class will be present on the client and object server only. You need not include it on the registry. c. Write the following Java classes for the client project: * PersonClient.java PersonClient creates a Binder_Stub object so that it may make lookup calls on the registry. It will call lookup("Mike") and receive a RemoteObjectReference object from the registry. PersonClient will then create a Person_Stub object with the RemoteObjectReference as a parameter to the Person_Stub constructor. It will then make calls on the stub to retrieve the name and age in the remote object. * Binder_Stub.java The client needs to speak to the registry. This class implements the Binder interface and contains bind and lookup methods. The client only makes use of the lookup method. * Binder.java This interface defines the lookup method as taking a string argument and returning a RemoteObjectReference value. It also defines the bind method that takes a string and a RemoteObjectReference as input. Its return type is void. * Person_Stub.java The client needs to speak with the server. This class implements the Person interface and contains getAge and getName methods. When making a call on the server, it creates a RequestReplyMessage from the information found in the RemoteObjectReference and sends this message to a TCP socket. When receiving a reply, it reads a RequestReplyMessage from the socket, extracts bytes, and returns to the caller either the name or age. * Person.java This is the same interface as shown below. * RemoteObjectReference.java and RequestReplyMessage.java. See above for a description of these classes. d. Write the following Java classes for the registry project. * Binder_Servant.java This servant object holds a mapping of of names to RemoteObjectReference objects. This class implements the Binder interface. A Java TreeMap will be used to hold the mappings. * Binder.java See above for a description. * Binder_Skeleton.java This skeleton is used to communicate with the server and the client. The serve method of this class is written like the serve method in Person_Skeleton shown below. It reads String objects from sockets and makes calls on its Binder_Servant. For lookups, it writes a RemoteObjectReference object to the socket. Nothing is written for calls on bind. * BinderServer.java This class starts up the registry. It creates a Binder_Servant and passes that servant to its new Binder_Skeleton and calls serve. * RemoteObjectReference.java This class is described above. e. Write the following Java classes for the object server: * Binder_Stub.java See above. * Binder.java See above. * Person_Servant.java This class implements Person and is shown below. * Person_Skeleton.java This class has a server method that reads RequestReplyMessages and writes RequestReplyMessages. In between the reading and writing it makes a call on the Person_Servant object. * Person.java See below. * PersonServer.java This class creates a Person_Servant object and a RemoteObjectReference. It uses the Binder_Stub to make a bind call on the registry. It creates a Person_Skeleton object and asks it to serve. * RemoteObjectReference.java See above. * RequestReplyMessage.java See above. Submission 5. 50 Points. Submit to blackboard one zipped directory holding three Netbeans projects. One project will hold your client code and another will hold your server code. The third will hold registry code. All of the Java source code will be well documented. Use the same naming scheme as described before. ================================================================= // file: Interface.java on both the client and server side public interface Person { public int getAge() throws Exception; public String getName() throws Exception; } ================================================================== // file: Person_Stub.java found only on the client side import java.io.ObjectOutputStream; import java.io.ObjectInputStream; import java.net.Socket; public class Person_Stub implements Person { Socket socket; ObjectOutputStream o; ObjectInputStream i; public Person_Stub() throws Exception { } public int getAge() throws Exception { socket = new Socket("localhost",9000); o = new ObjectOutputStream(socket.getOutputStream()); o.writeObject("age"); o.flush(); i = new ObjectInputStream(socket.getInputStream()); int ret = i.readInt(); socket.close(); return ret; } public String getName() throws Exception { socket = new Socket("localhost",9000); o = new ObjectOutputStream(socket.getOutputStream()); o.writeObject("name"); o.flush(); i = new ObjectInputStream(socket.getInputStream()); String ret = (String)(i.readObject()); socket.close(); return (String)ret; } } ======================================================== // file: PersonClient.java exists only on the client side public class PersonClient { public static void main(String args[]) { try { Person p = new Person_Stub(); int age = p.getAge(); System.out.println("Age = " + age); String name = p.getName(); System.out.println(name + " is " + age + " years old"); } catch(Exception t) { t.printStackTrace(); System.exit(0); } } } ============================================================ // file: Person_Skeleton.java exists only on the server side import java.io.ObjectOutputStream; import java.io.ObjectInputStream; import java.net.Socket; import java.net.ServerSocket; public class Person_Skeleton { Person myServer; public Person_Skeleton(Person s) { myServer = s; } public void serve() { try { ServerSocket s = new ServerSocket(9000); while(true) { Socket socket = s.accept(); ObjectInputStream i = new ObjectInputStream(socket.getInputStream()); String method = (String)i.readObject(); if(method.equals("age")) { int a = myServer.getAge(); ObjectOutputStream o = new ObjectOutputStream(socket.getOutputStream()); o.writeInt(a); o.flush(); } else if(method.equals("name")) { String n = myServer.getName(); ObjectOutputStream o = new ObjectOutputStream(socket.getOutputStream()); o.writeObject(n); o.flush(); } } } catch(Exception t) { System.out.println("Error " + t); System.exit(0); } } } ============================================================== // file: Person_Servant.java exists only on the server side public class Person_Servant implements Person { int age; String name; public Person_Servant(String n, int a) { name = n; age = a; } public int getAge() { return age; } public String getName() { return name; } } // file: PersonServer.java exists only on the server side public class PersonServer { public static void main(String args[]) { Person p = new Person_Servant("Mike",23); Person_Skeleton ps = new Person_Skeleton(p); ps.serve(); } }