Using RMI


Remote Method Invocation (RMI) enables object-oriented distributed computing by enabling objects that live in different virtual machines to invoke each other's methods . RMI allows you to call a method on a remote object as if that object was local. Despite its ease of use, RMI is a powerful platform for distributed computing.

Programming RMI applications is somewhat complicated. The idea is simple, but several steps are required to make it work. In fact, there are issues in RMI that are pointed out later in this chapter. To add RMI functionality to your application, follow these 10 steps:

  1. Define the object that will be shuttled across the network.

  2. Define the methods of the remote class (signatures only).

  3. Write an interface from those methods.

  4. Write the concrete class that implements the interface (this is the remote object).

  5. Write the client that invokes the methods.

  6. Write the RMI server (easier than running rmiregistry).

  7. Compile these classes.

  8. Run the RMI interface compiler (rmic) on the .class file of the implementation class to create the stub.

  9. Start the server (starts the registry programmatically).

  10. Start the client.

Building the RMI Application

The first step in building an RMI application is defining the methods. The example is simple to keep the focus on the RMI building process and avoid application complexity.

First, note that this example doesn't use HTTP to retrieve remote objects dynamically. My solution didn't, and most candidates go this way. Not using HTTP removes one layer of complication (a layer that the assignment does not require). Without dynamic class loading over HTTP, you do not have to worry about a policy file. The policy file itself is not that difficult, but it is another part that could go wrong. The policy file's format, what you put in it, and where you place it can all cause RMI not to work properly. By not using HTTP, however, you don't need to worry about the policy file.

Define the Value Object

This application is a simple demonstration of how to implement RMI so that your value object is simple and the methods are few. The object represents a customer, with only a few attributes to describe it.

Listing 12.1 is the Customer class. It is the object passed from the server and client upon request by using RMI.

Listing 12.1 The Customer Class
 import java.io.Serializable; public class Customer implements Serializable {    private String firstName;    private String lastName;    private int userID;    private String password;    public Customer()    {       firstName = null;       lastName = null;       userID = 0;       password = null;    }    public void setFirstName( String firstName )    {       this.firstName = firstName;    }    public String getFirstName()    {       return firstName;    }    public void setLastName( String lastName )    {       this.lastName = lastName;    }    public String getLastName()    {       return lastName;    }    public void setUserID( int userID )    {       this.userID = userID;    }    public int getUserID()    {       return userID;    }    public void setPassword( String password )    {       this.password = password;    }    public String getPassword()    {       return password;    }    public String toString()    {       String description = "\nFirst Name :" + firstName                          + "\nLast Name :" + lastName                          + "\nuser ID :" + userID                          + "\nPassword :" + password;       return description;    } } 

Define Remote Object Methods

This portion of the application is straightforward. You simply want to get the Customer object from the server to the client. Therefore, you need only one method, called getCustomer() . The getDate() method is also added to test whether you can marshal an instance of the Date class.

Write the Remote Interface

Now that you have a Value Object and the remote object method to get it, you should write the remote interface. Because there is only one method, what makes this interface applicable for RMI will be clear. The following is a template for the interface:

 import java.rmi.*; public interface RemoteInterface extends Remote {     MyObject getMyObject() throws RemoteException; } 

Listing 12.2 is the RemoteInterface class, which is the class the client talks to through a stub by using RMI. Notice that two objects are used here: Customer and Date . In this listing, you try the Date object, too, because you know it has no serialization issues. That way, you can isolate any problems to the RMI code (if Date doesn't get marshaled correctly) or the Customer class (if Date works but Customer doesn't).

Listing 12.2 The RemoteInterface Class
 import java.io.IOException; import java.rmi.RemoteException; // This interface defines the basic remote methods. // For example, the methods in this interface can be used by an // application to obtain information about a customer through // the Customer class. public interface RemoteInterface extends java.rmi.Remote {     public Customer getCustomer() throws RemoteException;     public java.util.Date getDate() throws RemoteException; } 

Write the Concrete Remote Class

Next, you need to build the concrete class that implements the RemoteInterface class. The concrete class does only one thingreturning an instance of the Customer class. This Customer object is serialized by RMI. To illustrate how this works, please consider the following a template for the concrete remote class:

 import java.rmi.*; import java.rmi.server.UnicastRemoteObject; public class RemoteImplementation        extends UnicastRemoteObject        implements RemoteInterface {     public RemoteImplementation() throws RemoteException     {         super();     }     public MyObject getMyObject() throws RemoteException     {         MyObject myObject = new MyObject();         return myObject;     } } 

Notice how the getMyObject() method returns an instance of MyObject . There is nothing special about this method. However, RMI will serialize myObject, return it to the calling JVM, and deserialize it there so that the calling object treats the called object as if it is local. You don't have to do anything in the calling method to make this happen; Java's RMI handles all the details. Let's see how to implement the above template.

Listing 12.3 is the RemoteImplementation class, which is the object the client communicates with to request a Customer object.

Listing 12.3 The RemoteImplementation Class
 import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; import java.io.IOException; //This class returns a new instance of Customer. public class RemoteImplementation        extends UnicastRemoteObject        implements RemoteInterface {     private Customer customer;     //This constructor creates a new instance of Customer.     public RemoteImplementation() throws RemoteException     {         super();         customer = new Customer();     }     public Customer getCustomer() throws RemoteException     {         return customer;     }     public java.util.Date getDate() throws RemoteException     {         return new java.util.Date();     } } 

Write the RMI Server

There are two ways to start the RMI registry: manually with rmiregistry (including parameters) or programmatically. If you do it programmatically, you will avoid several headaches . For example, you don't need a policy file (which tells Java what permissions the application has) and you don't have to worry about classpath , which the registry needs to find the stub. Many candidates waste time trying to figure out how to start the RMI registry. I chose the programmatic approach and didn't lose points for it. The following snippet demonstrates how you bind an object to the RMI server:

 try {     RemoteInterface remoteImplementation = new RemoteImplementation();     Naming.rebind("//localhost/RemoteInterfaceServer",              remoteImplementation); } catch (MalformedURLException e) {} catch (UnknownHostException e) {} catch (RemoteException e) {} 

Listing 12.4 is the RMIServer class, which once instantiated will start the RMI registry.

Listing 12.4 The RMIServer Class
 import java.rmi.Naming; import java.rmi.RemoteException; import java.rmi.NotBoundException; import java.rmi.UnknownHostException; import java.rmi.registry.Registry; import java.rmi.registry.LocateRegistry; import java.rmi.server.UnicastRemoteObject; /**  * RMIServer starts RMI and registers an instance of RemoteImplementation.  *  */ public class RMIServer {     // Instance of this class     private static RMIServer  rmi;     public static void main(String[] args)     {         try         {             rmi = new RMIServer(args);         } catch (Exception ex)         {             System.out.println( ex );         }     }     public RMIServer(String[] cmdArgs)     {         try         {             Registry myRegistry = LocateRegistry.createRegistry( 1099 );             System.out.println( "Registry created on 1099");             RemoteInterface getMyCustomer = new RemoteImplementation();             System.out.println( "Remote implementation object created" );             String urlString = "rmi://localhost:1099/CustomerServer";             System.out.println( "Attempting Naming.rebind(" + urlString                                 + ",getMyCustomer);" );             myRegistry.rebind( "CustomerServer", getMyCustomer );             System.out.println( "Bindings Finished, waiting"                               + " for client requests." );         } catch (RemoteException rex)         {             System.out.println( rex );         } catch (Exception ex)         {             System.out.println( ex );         }     } } 

Write the Client That Invokes the Remote Object

Finally, you need a client that uses all this RMI functionality. The client will try to create an instance of the Customer class through RMI. First, it gets a reference to an instance of the RemoteImplementation class, and then the RemoteImplementation object gets an instance of the Customer class. After that, the client treats the Customer class as though it is local, which is the whole point of RMI, even though it is actually on a remote JVM. The following illustrates how to get an instance of an object residing on a remote JVM through RMI:

 try {     MyObject myObject = (MyObject) Naming.lookup(                         "//localhost/RemoteInterfaceServer");     // do something with myObject } catch (MalformedURLException e) {} catch (UnknownHostException e) {} catch (NotBoundException e) {} catch (RemoteException e) {} 

Listing 12.5 is the RMITestClient class, which is the application starting point. From here, you can test the RMI plumbing.

Listing 12.5 The RMITestClient Class
 import java.io.IOException; import java.rmi.RemoteException; import java.rmi.NotBoundException; import java.rmi.Naming; import java.rmi.registry.Registry; import java.rmi.registry.LocateRegistry; /**  * This client tests the sample RMI implementation.  *  */ public class RMITestClient {     private static Customer customer;     private static RemoteInterface rmi;     public static void main(String[] args)     {         try         {             String lookupString = "rmi://localhost:1099/CustomerServer";             rmi = (RemoteInterface)Naming.lookup(lookupString);             //try date that you know is serializable             java.util.Date date = (java.util.Date)rmi.getDate();             System.out.println( date );             //try your own object to see if you did it right             customer = (Customer)rmi.getCustomer();             customer.setFirstName("Patricia");             customer.setLastName("Kasienne");             customer.setUserID(1234);             customer.setPassword("umther");             System.out.println( customer );         } catch (RemoteException rex)         {             System.out.println( rex );         } catch (Exception ex)         {             System.out.println( ex );         }     } } 

Compile the Classes

Now that you have all the classes, it is time to compile them in the following order:

  1. Customer

  2. RemoteInterface

  3. RemoteImplementation

  4. RMIServer

  5. RMITestClient

The order is presented based on dependencies. For example, if you compile the source files individually, you can't compile the RemoteInterface class before you compile Customer because RemoteInterface refers to Customer . However, you can always just use *.java in the folder that has these files, and the compiler will figure out the dependencies for you.

Run the RMI Interface Compiler

The client doesn't actually communicate directly with the RemoteInterface object. You need a stub to do that. Java comes with a stub generatorthe rmic tool. Now that the classes are compiled, it is time to run the RMI interface compiler (rmic) on the RemoteImplementation.class file to create the stub. You can generate the skeleton and stub files for RemoteImplementation with the rmic RemoteImplementation command.

Run the Application

First, run the RMIServer class, which starts the rmiregistry and registers the RemoteImplementation class with it. Then run the RMITestClient . You see the following output:

 First Name :Patricia Last Name :Kasienne user ID :1234 Password :umther Sun Apr 20 13:59:55 PDT 2004 

You can revise any of these classes if you want additional practice. For example, you might set the attributes of the Customer object by calling get and set methods for the attributes, including firstName , lastName , userID , and password . This practice further demonstrates how you can call methods of an object residing on a remote JVM.

graphics/note_icon.gif

The registry assumes that the host is the localhost machine, if it's started without parameters.


graphics/alert_icon.gif

When writing an interface for a given class, as shown previously, what is not obvious is how you get the client to treat a remote object the same as the local one. The trick is to use an interface that works locally and for RMI. Use the same signatures, of course. However, the exceptions declared to be thrown have to work in both situations. Many candidates miss points for not getting this part correct.


When it is time to start the client, you can use the following command:

 java RMITestClient 


JavaT 2 Developer Exam CramT 2 (Exam CX-310-252A and CX-310-027)
JavaT 2 Developer Exam CramT 2 (Exam CX-310-252A and CX-310-027)
ISBN: N/A
EAN: N/A
Year: 2003
Pages: 187

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net