13.5 Implementing a Simple Program Using RMI

 < Day Day Up > 



13.5 Implementing a Simple Program Using RMI

The first program to write when encountering any new technology is a simple HelloWorld program. This type of program simply prints a string to a terminal. Because the program does not implement any complex logic that can cause problems, it is easier to make sure that the components needed to compile and implement the program are present and used correctly and that the various parts of the program are correctly placed to be found when the program is run. This section gives step-by-step instructions for how to implement a simple HelloWorld program in Java. The steps needed to implement RMI are:

  1. Create the interface that will be used to allow the objects to communicate.

  2. Write and compile the server class.

  3. Write and compile the client class.

  4. Run rmic on the server to create the skeleton and stub files for RMI.

  5. Put the stub file in the directory on the computer where the client will run.

  6. Run rmiregistry on the server's computer.

  7. Run the server application.

  8. Run the client application.

13.5.1 Implement the RMI Interface

Chapter 5 presented the use of interfaces. In subsequent chapters, we have emphasized that interfaces permit more robust designs as they allow what the object is to do to be abstracted from how the object actually implements the behavior needed. This is particularly important in distributed computing. Because a distributed program is run on more than one computer, the actual implementation of the class might not be available to the object that is accessing the remote resource. Interfaces allow remote objects to be accessed by the definition of what they are going to do, not how they do it. The interface provides the link between the local object and the remote resource.

Some form of an interface is needed for any distributed architecture; for example, CORBA, which can be used for any number of languages (e.g., C/C++, Ada), implements a language called Interface Definition Language (IDL) to allow interfaces to be written that can work for all of these languages. However, Java has an advantage over a number of these older languages in that it already has the concept of an interface built into the language, so RMI simply uses the Java interface language facility.

While the interface definition used for RMI is a standard Java interface, some particular features must be included in an interface that runs remotely using RMI. The first is that the interface must extend java.rmi.Remote, which simply tells java that any class implementing this interface may be run remotely using RMI. The second is that any method that is to be called remotely needs to be defined in the interface, and these methods need to throw the exception java.rmi.RemoteException. The reason this exception must be thrown is that any RMI will automatically handle any communication-related exceptions that might occur when the method is called. This allows the programmer to call the remote method as if it were a local method and not to be concerned with such issues as how to ensure that messages are properly sent to the remote methods.

The interface needed to run the HelloWorld program is shown in Program13.1a (Exhibit 5). Note that this interface (or at least its class file) must be present on both the client and server when compiling the program.

Exhibit 5: Program13.1a: HelloWorld Interface

start example

 import java.rmi.Remote; import java.rmi.server.UnicastRemoteObject; /**  *  This is an RMI interface. To work with RMI the  *  following must occur:  *    1 - The interface must extend java.rmi.Remote.  *    2 - All remote methods must be declared to throw  *        java.rmi.RemoteException.  *        Note that the exception does not have to be thrown  *        when the Server implements the method, as rmic adds  *        the exception to the method in the stub class.  *  *  This interface is needed by both the client and the server  *  and defines how they communicate with each other; however,  *  the client only needs the class file to compile, so only the  *  ".class" file should be put with the client code.  */  public interface HelloWorld extends java.rmi.Remote {    public void PrintHello()      throws java.rmi.RemoteException;  } 

end example

13.5.2 Implement the RMI Server Class

Once the interface is implemented which specifies how messages will be passed between the client and server, a server to implement the behavior for that interface can be written. The server class is a normal Java class that has the following four characteristics:

  1. The server class must extend java.rmi.server.UnicastRemoteObject. This is necessary to allow an object to set up the infrastructure to respond to messages from remote clients.

  2. The server class must implement the interface defined in step 1 to ensure that the promises made in the interface are kept when the Server is called from the Client.

  3. The constructor method for the Server class must throw java.rmi.RemoteException. This exception is thrown by the default constructor for the UnicastRemoteObject, and because the default constructor for that object is automatically called, this exception propagates to this method. Note that it is not possible to catch this exception in the constructor and then try to handle it, as Java forces an explicit call to a super constructor to be the first statement in a constructor. To implement a try block violates this, so a try block cannot be put around a call to a super constructor; therefore, at least one constructor must be created to throw this exception.

  4. Even though the Server will be called as a passive object, it must have a main that calls Naming.rebind to register the object with rmiregistry. This main class needs only to create the object to register and then to register it with a name. The main thread can then suspend, as the object will run in threads created when the objects methods are called.

An example of a Server class for the HelloWorld program is provided in Exhibit 6 (Program13.1b). A few details about the implementation of the Server class should be emphasized. First is the convention for naming the Server class the same as the interface with a suffix of "Imp" (for implementation), which was followed here. Obviously, this convention could not be used if several different servers were implemented for a single interface. Second, even though the method that is defined in the interface must throw RemoteException, the actual implementation of this method does not. The reason is that proxies for these methods will be created for these methods in the skeleton and stub classes, and these proxy methods will throw these exceptions, but, because the method as defined in the Server does not throw the exception, there is no need to put it in the signature for the method.

Exhibit 6: Program13.1b: HelloWorld Server Class

start example

 import java.rmi.*; import java.rmi.server.UnicastRemoteObject; import java.io.*; /**  *  This class is a simple RMI server. To be a server using RMI,  *  this class implements the following:  *  *   1 - It extends the remote interface, in this case  *       interface HelloWorld.  *   2 - It extends UnicastRemoteObject.  *   3 - The constructor must throw java.rmi.RemoteException,  *       as the parent constructor for UnicastRemoteObject  *       throws this exception.  *   4 - It sets up the security manager (if needed). It is not  *       used here, so it is commented out. To use it, the  *       policytool program must be run to set the policy  *       before running this application.  *   5 - The object is created and registered with rmiregistry  *       using the Naming.rebind() call. Note that, in this  *       example, to show that the default RMI port does not  *       have to be used, port 5012 is used. Be careful when  *       starting this program to use the correct port.  */ class HelloWorldImp extends UnicastRemoteObject     implements HelloWorld {   /**    *  Public constructor, which simply runs    *  the parent constructor.    */   public HelloWorldImp() throws java.rmi.RemoteException {     super();   }   /**    *  Method from the remote interface. Note that this method is    *  called remotely from the client. Also note that this    *  method does not throw java.rmi.RemoteException, even    *  though the interface defines it for this method. The    *  exception will be added when rmic is run.    */   public void PrintHello() {     System.out.println("Hello from Local");   }   /**    *  The main method, which creates the object    *  and registers it with rmiregistry.    */   public static void main(String argv[]) {     try {       // The security manager is only needed if the class is to       // be loaded across the network. This code is commented       // out because we will distribute the classes before       // starting the client and the server.       /******       if (System.getSecurityManager() = = null) {        System.setSecurityManager(new RMISecurityManager());       }       */       HelloWorldImp thisExample = new HelloWorldImp();       Naming.rebind("// localhost:5012/HelloGuy," thisExample);       // This is not needed, but it tells us the rebind worked.       System.out.println("HelloGuy bound in registry");     } catch (Exception e) {       System.out.println("HelloGuy err: "+ e.getMessage());       e.printStackTrace();     }   } } 

end example

The last thing to look at is the call to Naming.rebind. To use this method, an object instance of this class must first be created, then the object, along with a name, is passed to the Naming.rebind method. The name for the object is a URL-formatted name just like those typed into a browser window. In the Server defined for this project, a port other than the standard port for RMI is used to run rmiregistry, so the computer (localhost) and the port (5012) are specified as part of the name as "/ /localhost:5012." The rest of the string is the name associated with that object in the rmiregistry program associated with that computer and port.

13.5.3 Implement the RMI Client Class

The client class that will interface with the server should now be implemented. The client class is a normal Java class except that one of the objects, the server object, is retrieved from the remote rmiregistry by calling Naming.lookup. A name is passed to Naming.lookup that matches the name used by the server to register with rmiregistry. The object returned is an instance of an object that allows methods of the server to be called, so the object is cast to the interface on the client computer. The client class for the HelloWorld program is shown in Exhibit 7 (Program13.1c).

Exhibit 7: Program13.1c: HelloWorld Client Class

start example

 import java.rmi.*; /**  *  Client program for an RMI program. To be a server, the  *  program must do the following:  *  *   1 - Set the security manager to allow the stub class to be  *       loaded from the server (if needed). It is not used here,  *       so it is commented out.  *   2 - Retrieve the remote object from the rmiregistry  *       programming running on the server computer. Note that in  *       this example, to show that the default RMI port does not  *       have to be used, port 5012 is used. Be careful when  *       starting this program to use the correct port.  *   3 - Call the object using the interface as if it were local.  */ class HelloWorldClient {   public static void main(String argv[]) {     try {       // If the stub class was loaded across the network, this       // would have to be set, but we are distributing all the       // class files to the right directories, so this is not       // needed here. See the Server code for more details.       /***********       if (System.getSecurityManager() = = null) {         System.setSecurityManager(new RMISecurityManager());       }       */       HelloWorld H = (HelloWorld)Naming.lookup(         "// localhost:5012/HelloGuy");       H.PrintHello();     } catch(Exception e) {       e.printStackTrace();     }   } } 

end example

13.5.4 Creating and Distributing the Skeleton and Stub Files

The next step is to produce skeleton and stub files. These files are used by RMI to perform what is called marshaling and unmarshaling of the data. Basically, this means that the skeleton and stub files translate the data into a format that can be transferred across the network and can then be reconstituted into proper objects. How it does this is not important to understand here because it always works. If an object or primitive is sent across the network as a parameter or return value, it will be correct when it is used on the remote computer, or an exception will be thrown which can then be handled.

To create skeleton and stub files, the rmic program is run on the Server ".class" file. For example, in the HelloWorld program, the command "rmic HelloWorldImp" will produce the files HelloWorldImp_Skel.class and HelloWorld_Stub.class. These are the skeleton and stub files for the HelloWorld program. In more recent versions of rmic, the skeleton file is simply included in the Server ".class" file; however, the command as shown here will create both the skeleton and stub files.

Take note, however, that normally in Java objects are passed by reference (actually, the reference is copied so it is a call by value, but the effect is a call by reference); therefore, the values of an object can be changed in the method and reflected in the object when the method returns to the calling program. Because RMI does not have access to remote memory, all calls treat objects as call by value, meaning that the object itself, and not simply its reference, must be copied, and changes made in the remote method will not be reflected in the object when control is returned to the calling program. It also means that passing large composite objects, such as vectors and arrays, can require many more resources than a similar call to a local method, and passing of such structures should be carefully considered before being initiated.

13.5.5 Distributing and Running the Program

Now that all the parts of the program are ready, the pieces can be distributed and the program tested and run. The example here shows how to run the program on a single computer running MS Windows, but the same principles apply to running RMI on any computer. The first thing to do is to create two directories, one for the server and the other for the client. In the server directory should be the HelloWorld interface, the HelloWorldImp server, and both the skeleton and stub files. In the client directory should be the HelloWorld interface, the HelloWorldClient, and the stub file. Now start three DOS command prompts, and change the directory for two of them to the directory for the server, and one to the directory for the client. In one of the server directories, type "rmiregistry 5012". This starts the rmiregistry program and tells it to listen to port 5012. In the second server directory, start the server application. It should tell you that it is registered with rmiregistry. Finally, in the client directory run the client application. Each time you run the client application, a message should appear in the server window telling you that the client has contacted the server. When this happens, you have gotten a simple RMI program to run. The next step is to see some ways RMI can be used to solve larger problems.



 < Day Day Up > 



Creating Components. Object Oriented, Concurrent, and Distributed Computing in Java
The .NET Developers Guide to Directory Services Programming
ISBN: 849314992
EAN: 2147483647
Year: 2003
Pages: 162

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