through some kind of remote method invocation(RMi)protocol. An RMI protocol is sed to communicate method invocations over a network. CORBA. Java RMi. and Microsoft. NET all use their own RMI protocol. Every instance of the business object the middle tier is wrapped by an instance of its matching skeleton class. The skeleton is set up on a port and IP address and listens for requests from the stub, which resides on the client machine and is connected via the network to the skeleton the stub acts as the business objects surrogate on the client and is responsible for communicating requests from the client to the business object through the skeleton. Figure 1-3 illustrates the process of communicating a method invocation from the client to the server object and back. The stub and the skeleton hide the communication specifics of the RMI protocol from the client and the implementation class, respectively FIGUREI Figure 1-2: RMI loop The business object implements a public interface that declares its business methods. The stub implements the same interface as the business object, but the stubs methods do not contain business logic. Instead, the business methods on the stub implement whatever networking operations are required to forward the request to the business object and receive the results. When a client invokes a business method on the stub, the request is communicated over the network by streaming the name of the method invoked, and the values passed in as parameters, to the skeleton. When the skeleton receives the incoming stream, it parses the stream to discover which method is requested, and then invokes the corresponding business method on the business object. Any value that is returned from he method invoked on the business object is streamed back to the stub by the skeleton The stub then returns the value to the client application as if it had processed the business logic locally Rolling Your Own Distributed object The best way to illustrate how distributed objects work is to show how you can implement a distributed object yourself, with your own distributed object protocol. This will give you some appreciation for what a true distributed object protocol like CORBA does. Actual distributed object systems such as DCOM, CORBA, and Java RMI are, however, much more complex and robust than the simple example we will develop here. The dis tributed bject system we develop in this chapter is only illustrative; it is not a real technology 4 The acronym RMI isn t specific to Java RMI. This section uses the term RMI to describe distributed object protocols in general. Java RMI is the Java language version of a distributed object protocol Copyright(c)2001 OReilly Associates
DRAFT 10/21/0107/06/01 Copyright (c) 2001 O'Reilly & Associates through some kind of remote method invocation (RMI) protocol. An RMI protocol is used to communicate method invocations over a network. CORBA, Java RMI, and Microsoft .NET all use their own RMI protocol.4 Every instance of the business object on the middle tier is wrapped by an instance of its matching skeleton class. The skeleton is set up on a port and IP address and listens for requests from the stub, which resides on the client machine and is connected via the network to the skeleton. The stub acts as the business object’s surrogate on the client and is responsible for communicating requests from the client to the business object through the skeleton. Figure 1-3 illustrates the process of communicating a method invocation from the client to the server object and back. The stub and the skeleton hide the communication specifics of the RMI protocol from the client and the implementation class, respectively. [FIGURE] Figure 1-2: RMI loop The business object implements a public interface that declares its business methods. The stub implements the same interface as the business object, but the stub’s methods do not contain business logic. Instead, the business methods on the stub implement whatever networking operations are required to forward the request to the business object and receive the results. When a client invokes a business method on the stub, the request is communicated over the network by streaming the name of the method invoked, and the values passed in as parameters, to the skeleton. When the skeleton receives the incoming stream, it parses the stream to discover which method is requested, and then invokes the corresponding business method on the business object. Any value that is returned from the method invoked on the business object is streamed back to the stub by the skeleton. The stub then returns the value to the client application as if it had processed the business logic locally. Rolling Your Own Distributed Object The best way to illustrate how distributed objects work is to show how you can implement a distributed object yourself, with your own distributed object protocol. This will give you some appreciation for what a true distributed object protocol like CORBA does. Actual distributed object systems such as DCOM, CORBA, and Java RMI are, however, much more complex and robust than the simple example we will develop here. The distributed object system we develop in this chapter is only illustrative; it is not a real technology, 4 The acronym RMI isn’t specific to Java RMI. This section uses the term RMI to describe distributed object protocols in general. Java RMI is the Java language version of a distributed object protocol
understanding of how a more sophisticated distributed object system works with some nor is it part of Enterprise JavaBeans. The purpose is Here's a very simple distributed business object called PersonServer that implements the Person interface. The Person interface captures the concept of a person business object. It has two business methods: getAge()and getName ().In a real application, we would probably define many more behaviors for the Person business object, but two ethods are enough for this example public interface Person I public int getAge ()throws Throwable; public String getName() throws Throwable; The implementation of this interface, PersonServer, doesn't contain anything at al surprising. It defines the business logic and state for a Person public class PersonServer implements Person i Int age: String name; public Personserver(String name, int age)t this age=age; this name name public int getAge () return age: public String getName()I return name Now we need some way to make the Personserver available to a remote client. Thats the job of Person skeleton and person stub. The person interface describes the concept of a person independent of implementation. Both the Person Server and the Person stub implement the Person interface because they are both expected to support the concept of a person. The Personserver implements the interface to provide the actual business logic and state; the Person stub implements the interface so that it can look like a Person business object on the client and relay requests back to the skeleton, which in turn sends them to the object itself. Here's what the stub looks like. import java. ioObjectoutputstream; import java. io ObjectInputStream; Copyright(c)2001 O Reilly Associates
DRAFT 10/21/0107/06/01 Copyright (c) 2001 O'Reilly & Associates nor is it part of Enterprise JavaBeans. The purpose is to provide you with some understanding of how a more sophisticated distributed object system works. Here’s a very simple distributed business object called PersonServer that implements the Person interface. The Person interface captures the concept of a person business object. It has two business methods: getAge() and getName(). In a real application, we would probably define many more behaviors for the Person business object, but two methods are enough for this example: public interface Person { public int getAge() throws Throwable; public String getName() throws Throwable; } The implementation of this interface, PersonServer, doesn’t contain anything at all surprising. It defines the business logic and state for a Person: public class PersonServer implements Person { int age; String name; public PersonServer(String name, int age){ this.age = age; this.name = name; } public int getAge(){ return age; } public String getName(){ return name; } } Now we need some way to make the PersonServer available to a remote client. That’s the job of the Person_Skeleton and Person_Stub. The Person interface describes the concept of a person independent of implementation. Both the PersonServer and the Person_Stub implement the Person interface because they are both expected to support the concept of a person. The PersonServer implements the interface to provide the actual business logic and state; the Person_Stub implements the interface so that it can look like a Person business object on the client and relay requests back to the skeleton, which in turn sends them to the object itself. Here’s what the stub looks like: import java.io.ObjectOutputStream; import java.io.ObjectInputStream;
import java. net. Socket. public class Person Stub implements Person t Socketsocket: public Person Stub() throws Throwable / Create a network connection to the skeleton Use "localhost or the Ip Address of the skeleton if it's on a different machine. * socket new Socket("localhost",9000); public int getAge() throws Throwable t // When this method is invoked, stream the method name to the / skeleton ObjectoutputStream outStream new ObjectoutputStream(socket getOutputstream())i outStream writeObject("age") utStream flush ObjectInputstream instream new ObjectInputStream(socket. getInputstream() return instream readInt( public String getName() throws Throwable t // When this method is invoked, stream the method name to the / skeleton Objectoutputstream outstream new Objectoutputstream(socket getoutputStream()) outStream writeObject("name") outStream flush ObjectInputStream instream new ObjectInputstream(socket. getInputstream return (String)instream readobject()i When a method is invoked on the person stub, a string token is created and streamed to the skeleton. The token identifies the method that was invoked on the stub The skeleton parses the method-identifying token, invokes the corresponding method on the business object, and streams back the result. When the stub reads the reply from the skeleton, it parses the value and returns it to the client. From the clients perspective, the stub processed the request locally. Now lets look at the skeleton import java. io. CbjectOutputstream; import java. io ObjectInputStream; Copyright(c)2001 OReilly Associates
DRAFT 10/21/0107/06/01 Copyright (c) 2001 O'Reilly & Associates import java.net.Socket; public class Person_Stub implements Person { Socket socket; public Person_Stub() throws Throwable { /* Create a network connection to the skeleton. Use "localhost" or the IP Address of the skeleton if it's on a different machine. */ socket = new Socket("localhost",9000); } public int getAge() throws Throwable { // When this method is invoked, stream the method name to the // skeleton. ObjectOutputStream outStream = new ObjectOutputStream(socket.getOutputStream()); outStream.writeObject("age"); outStream.flush(); ObjectInputStream inStream = new ObjectInputStream(socket.getInputStream()); return inStream.readInt(); } public String getName() throws Throwable { // When this method is invoked, stream the method name to the // skeleton. ObjectOutputStream outStream = new ObjectOutputStream(socket.getOutputStream()); outStream.writeObject("name"); outStream.flush(); ObjectInputStream inStream = new ObjectInputStream(socket.getInputStream()); return (String)inStream.readObject(); } } When a method is invoked on the Person_Stub, a String token is created and streamed to the skeleton. The token identifies the method that was invoked on the stub. The skeleton parses the method-identifying token, invokes the corresponding method on the business object, and streams back the result. When the stub reads the reply from the skeleton, it parses the value and returns it to the client. From the client’s perspective, the stub processed the request locally. Now let’s look at the skeleton: import java.io.ObjectOutputStream; import java.io.ObjectInputStream; import java.net.Socket;
import java. net. ServerSocket public class Person Skeleton extends Thread t PersonServer my Serve public Person Skeleton(PersonServer server)i // Get a reference to the business object that this skeleton wraps this my Server server: public void run(i // Create a server socket on port 9000 Server Socket server Socket new Server Socket(9000) // Wait for and obtain a socket connection from stub Socket socket serverSocket accept ()i while(socket != null)I // Create an input stream to receive requests from stub ObjectInputstream instream new ObjectInputstream(socket. getInputstream ())i // Read next method request from stub. Block until request // sent String method=(String)instream readobject(); // Evaluate the type of method requested if (methodequals("age))( // Invoke business method on server object int age myserver getAge() // Create an output stream to send return values back to // stub ObjectoutputStream outstream new ObjectOutputStream( socket getoutputstream()) / Send results back to stub outStream. writeInt(age)i 1 else if (method equals("name ))t // Invoke business method on server object String name= my Server getName() // Create an output stream to send return values back to / the stub Objectoutputstream outstream new ObjectoutputStream(socket getOutputstream()) 7/ Send results back to stub. outStreamwriteObject(name) utStream. flush( Copyright(c)2001 OReilly Associates
DRAFT 10/21/0107/06/01 Copyright (c) 2001 O'Reilly & Associates import java.net.ServerSocket; public class Person_Skeleton extends Thread { PersonServer myServer; public Person_Skeleton(PersonServer server){ // Get a reference to the business object that this skeleton wraps. this.myServer = server; } public void run(){ try { // Create a server socket on port 9000. ServerSocket serverSocket = new ServerSocket(9000); // Wait for and obtain a socket connection from stub. Socket socket = serverSocket.accept(); while (socket != null){ // Create an input stream to receive requests from stub. ObjectInputStream inStream = new ObjectInputStream(socket.getInputStream()); // Read next method request from stub. Block until request is // sent. String method = (String)inStream.readObject(); // Evaluate the type of method requested. if (method.equals("age")){ // Invoke business method on server object. int age = myServer.getAge(); // Create an output stream to send return values back to // stub. ObjectOutputStream outStream = new ObjectOutputStream(socket.getOutputStream()); // Send results back to stub. outStream.writeInt(age); outStream.flush(); } else if(method.equals("name")){ // Invoke business method on server object. String name = myServer.getName(); // Create an output stream to send return values back to // the stub. ObjectOutputStream outStream = new ObjectOutputStream(socket.getOutputStream()); // Send results back to stub. outStream.writeObject(name); outStream.flush(); }
catch(Throwable t)It. printstackTrace(); System. exit(0);) public static void main(String args [ )i // Obtain a unique instance Person Personserver person= new Person Server("Richard", 36 Person Skeleton skel new Person Skeleton(person) The Person skeleton routes requests received from the stub to the business object, Personserver. Essentially, the Person skeleton spends all its time waiting for he stub to stream it a request. Once a request is received, it is parsed and delegated to the corresponding method on the Personserver. The return value from the business object is then streamed back to the stub, which returns it as if it was processed locally Now that we've created all the machinery, lets look at a simple client that makes use of the person public class PersonClient public static void main(String args)i try t Person person= new Person Stub()i int age= person getAge ()i ring name= person. getName()i System. out. println(name+"is"+age+years old") y catch(Throwable t) t printStackTrace ();) This client application shows how the stub is used on the client. Except for the instantia tion of the Person Stub at the beginning, the client is unaware that the Person busi- ness object is actually a network proxy to the real business object on the middle tier. In Figure 1-5, the RMI loop diagram is changed to represent the RMi process as applied to our code FIGUREI Figure 1-3: RMI Loop with Person business object As you examine Figure 1-5, notice how the RMI loop was implemented by our distributed Person object. RMI is the basis of distributed object systems and is responsible for making distributed objects location transparent. Location transparency means that a server objects actual location--usually on the middle tier-is unknown and unimportant Copyright(c)2001 OReilly Associates
DRAFT 10/21/0107/06/01 Copyright (c) 2001 O'Reilly & Associates } } catch(Throwable t) {t.printStackTrace();System.exit(0); } } public static void main(String args [] ){ // Obtain a unique instance Person. PersonServer person = new PersonServer("Richard", 36); Person_Skeleton skel = new Person_Skeleton(person); skel.start(); } } The Person_Skeleton routes requests received from the stub to the business object, PersonServer. Essentially, the Person_Skeleton spends all its time waiting for the stub to stream it a request. Once a request is received, it is parsed and delegated to the corresponding method on the PersonServer. The return value from the business object is then streamed back to the stub, which returns it as if it was processed locally. Now that we’ve created all the machinery, let’s look at a simple client that makes use of the Person: public class PersonClient { public static void main(String [] args){ try { Person person = new Person_Stub(); int age = person.getAge(); String name = person.getName(); System.out.println(name+" is "+age+" years old"); } catch(Throwable t) {t.printStackTrace();} } } This client application shows how the stub is used on the client. Except for the instantiation of the Person_Stub at the beginning, the client is unaware that the Person business object is actually a network proxy to the real business object on the middle tier. In Figure 1-5, the RMI loop diagram is changed to represent the RMI process as applied to our code. [FIGURE] Figure 1-3: RMI Loop with Person business object As you examine Figure 1-5, notice how the RMI loop was implemented by our distributed Person object. RMI is the basis of distributed object systems and is responsible for making distributed objects location transparent. Location transparency means that a server object’s actual location—usually on the middle tier—is unknown and unimportant