HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML
 

What Is RMI?
RMI, the acronym for Remote Method Invocation, is part of the core Java API. The central idea behind this technology is the ability to call the methods of a remote object, shielding the programmer from mundane Socket handling while promoting a cleaner software architecture.

Why Use RMI?
RMI allows a developer to create distributed applications while retaining 100% Java compatibility and reducing the overall complexity of a project. By using RMI, the programmer can get an instance of the server object and call its methods directly. By calling the server object's methods, we can avoid the use of large switch statements and proprietary protocols.

Comparing RMI to Sockets
RMI is actually an abstraction layer built over Sockets.

  1. It uses Sockets to communicate data over the network.
  2. It allows for persistent/stateless connections.
  3. It uses Serialization to transport objects over streams.
Advantages of RMI
A primary advantage is simplicity and clean implementation, leading to more maintainable, robust and flexible applications. This isn't to say a system can't be written using Sockets in place of RMI, just that RMI removes a great deal of mundane tasks -- such as parsing and switch logic. Since RMI has the potential to reduce a great deal of code, more complex systems can be built with relative ease. The greatest benefits don't revolve around ease of use, however.

RMI allows us to create a distributed system while at the same time decoupling the client/server objects.

RMI isn't the first API to put these benefits on the table, but it's a pure Java solution for doing so. This means that it's possible to create a zero-install client for your users. An example of this may be a Decision Support System (DSS) written as an applet that communicates with a "server interface" object using RMI. This server interface object could be designed to simply call methods from a server object that talks to the database (see Figure 1). By using such an architecture, you can build some extremely powerful applications that are easily maintained (relatively, of course!).

As you can see from Figure 1, a system can use RMI to its advantage in several ways:

Figure 1
Figure 1:
  1. There's no client installation needed, only a Java 1.1-capable browser (or a JRE for applications).
  2. If the DBMS is changed (i.e., if you move to Oracle from Access), then only the server object needs to be recompiled, while the server interface and client remain the same.
  3. All portions are easily distributed, and development teams can be given a "section" of the distributed architecture to work on. This simplifies coding and allows a group to leverage its talents better. For example, the GUI "expert" could focus on the client while the DBMS "expert" could focus on the server.
Disadvantages of RMI
RMI is slightly less efficient than Sockets because of the additional "layer" involved and because it must deal with the registry in order to communicate. Another concern is creating multithreaded servers safely; a common mistake is to assume the default threading will allow you to ignore code that ensures our server is thread-safe and robust. If you want to implement a concurrent user system, you'll still need to provide the proper structure for doing so.

How Does RMI Work?
RMI uses a registry to store information regarding servers that have been bound to it. This article uses the rmiregistry provided in the JDK; however, it's possible to write an RMI-based application without it.

Binding is done by calling the Naming.rebind() method in the server object's constructor (found in the java.rmi package). If the method fails, it'll throw one of the following exceptions RemoteException, MalformedURLException or UnknownHostException. In the case of RemoteException, there was an error with the registry, often occurring because rmiregistry wasn't executed before the server object attempted to bind. Once the server has been bound to the registry, a client can do a Naming.lookup() to get an instance of the RMI server object.

After the client has an instance of the server object, it'll be able to call all the methods defined in the server's list of promised remote methods. These methods are defined in an interface that both the client and server objects implement. By using rmic, we can create a stub and skeleton to use for compiling our client object.

The stub sits in the client's codebase or classpath (the client's .class file usually resides in the same directory). This stub object is what tells the client what methods may be called from the server and handles all the details that allow us to call a remote object's method via the registry.

The skeleton is similiar to the stub, except it must be in the server's classpath (like the stub, the skeleton usually resides in the same directory as the .class file for the server). The skeleton handles incoming requests/parameters from clients and returns the results via the registry.

Figure 2 should help you visualize this whole process. It's important to realize that while we're looking at a one-to-one relationship here, anything is possible. You can have multiple clients associated with a single server or multiple servers, or even allow a client to be a server as well. The only requirements are that the stub/skeletons must be available to each object that needs them (client/server) and that the registry must be located on the server's machine (or an alternative must be accounted for).

Figure 2
Figure 2:

Putting It All Together
The following sections outline the steps involved.
1. Writing the Interface

  • Create an interface to be implemented by the server class. This interface must contain all public methods, each of which includes a throws RemoteException clause in its definition. This interface must also extend the java.rmi.Remote object. For an example, see Listing 1.
2. Writing the Server object (please refer to Listing 2.)
  • The server must extend UnicastRemoteObject.
  • The server must implement the interface.
  • To make the server object concrete so that it may be instantiated, write the code and complete each of the methods defined in the interface.
  • The security manager should be set, possibly in the driver's main() method or the constructor. To do this, create a new security manager object as an argument to the system classes setSecurityManager method, i.e.:

    System.setSecurityManager (new
    RMISecurityManager());

  • Attempt a Naming.rebind() to bind the server to the registry in the driver's main() or the constructor. To use Naming.rebind(), you must specify the server's name and pass through an instance to the server object; for example, the main() approach, i.e.:

    MyServer myserver = new MyServer();
    Naming.rebind ("myserver", myserver);

3. Writing the Client object

  • Call the Naming.lookup() method to get an instance of the interface that the server implements. This interface contains all the promised methods (step 1). See Listing 3.

4. Getting things running

  • Compile the server object and use rmic to create the stub and skeleton .class files:

    javac MyServer.java rmic MyServer

  • Compile the client object:

    javac MyClient.java

  • Run the RMI registry provided with the JDK:

    rmiregistry

  • Run your server object:

    java MyServer

  • Run your client object:

    java MyClient

What Is Needed?
To access the RMI API, you must include the necessary packages from "java.rmi.*" and its child packages. You must also be using the JDK 1.1.x and not JDK 1.2.x, as 1.2 requires other considerations.

RMI with JDK 1.2.x vs JDK 1.1.x
Please note that, while in JDK 1.1.x, you simply need to create a new instance of RMISecurityManager to set your server with the appropriate permissions. JDK1.2 is a little different. Thankfully, the changes necessary aren't a big deal: simply write your own MySecurityManager class that inherits from SecurityManager and implement the following method (minimum):

public void checkPermission (Permission p)
{
return;
} //checkPermission

Be aware that this probably isn't something you'd want to use for a corporate app; however, for learning RMI with JDK 1.2.x it works fine. Just so you know, what's happening here is that the checkPermission() method will throw a security exception if a SecurityManager object doesn't allow certain requests. Otherwise, if the request is okay, it returns (just as above). So the above method allows everything please read up on Java security issues before doing corporate applications that may require (a little) more stringent security!

An Example Program Using RMI
Now that we've discussed RMI and its application, it's time to write a small example application to put RMI to the test. If you have any problems compiling or running this demo, you can e-mail me at [email protected] I'm using Sun's JDK 1.1.6 to compile and run this application, but if you comment/uncomment where indicated, it will compile/run under JDK 1.2.x.

The example code will compile to create a Server and Client, and should include the Server.java, Client.java and ServerInterface.java source files. To compile and execute the example code do the following:

javac Server.java   //compile Server.java file
javac Client.java   //compile Client.java file
rmic Server   //create stub/skeleton start/min
rmiregistry   //start up the RMI registry
start java Server   //start up the Server
javaClient server   //start client and connect
to "server" (defaul registry
name //used by Server

Resources
To find out more on how to leverage the RMI API, I've listed some URLs and book titles to help you get started.

  1. Java in a Nutshell Examples. ISBN 1-56592-371-5
  2. Beginning Java. ISBN 1-861000-27-8
  3. Java 1.1 Developers Handbook. ISBN 0-7821-1919-0
  4. http://adams.patriot.net/~tvalesky/easy-rmi.html
  5. www.nada.kth.se/javadoc/JDK1.1/guide/rmi/index.html
  6. www.javasoft.com/products/jdk/rmi/index.html
Conclusion
This article presents a quick and easy way to get started using RMI by example. I recommend that you refer to the URLs above for more information or consider the books listed. Enjoy using RMI and the best of luck in your programming endeavors!

About the Author
Christopher Lambert, a graduate of Lambton College with a computer programmer analyst degree, is currently completing a BS in computer science at the University of Northern British Columbia. He works at Canfor, a software consulting company pecializing in Java/Oracle. He can be reached at [email protected]

	

Listing 1: 

/** ServerInterface 
* 
* This interface should be implemented by any "server" objects 
*   in your application.  By defining your own ServerInterface 
*   (or whatever you name the interface which contains all the 
*   remote methods), you can define the distributed behavior of 
*   a server object. 
* 
* Note : These are the servers "promised" methods. 
*/ 

import java.rmi.*; 

public interface ServerInterface extends Remote 
{ 
 // Returns a word of the day to client. 
 public String getWord () throws RemoteException; 
} //ServerInterface 

Listing 2: 

/** Server 
* 
*       In this example our server class is small and only 
*   does one thing: return a "word of the day". 
* 
*       Note : We must keep the server running in order to 
*   ensure any references are valid.  To this end, I have 
*   made the server Runnable and used an infinite loop. 
*/ 

import java.rmi.*; 
import java.rmi.server.*; 
import java.net.*; 

// JDK 1.2 : import java.security.*; 

public class Server extends UnicastRemoteObject implements 
serverInterface, Runnable 
{ 
 // public constants 
 public static final int WORDCOUNT = 5; 

 // private object variables 
 private String [] wordTable; 

 /** main 
 * 
 *       Program entry point - instatiates an instance of the 
 *   server and wraps it in a thread. 
 */ 
 public static void main (String args[]) 
 { 
  try 
  { 
   Thread serverthread = new Thread (new Server()); 
   serverthread.start(); 
  } //try 
  catch (Exception x) 
  { 
   x.printStackTrace(); 
   System.exit (1); 
  } //catch 
 } //main 

 /** Server 
 * 
 *       Default constructor will attempt to bind itself to 
 *   the registry and build a table of words to send 
 *   back to a requesting client. 
 */ 
 public Server () throws RemoteException 
 { 
  try 
  { 
   // Set the system security manager to a new 
   // RMISecurityManager! 

   // If using JDK 1.1.x, uncomment the following line 
   System.setSecurityManager (new RMISecurityManager()); 

   // If using JDK 1.2.x, uncomment the following line: 
   //System.setSecurityManager (new MySecurityManager()); 

   // Bind this object to the registry under 
   // the name "server". 
   Naming.rebind ("server", this); 
   System.out.println ("Server has bound itself to the 
registry."); 
   // Build the word table. 
   buildTable (); 
   System.out.println ("Words have been created."); 
  } //try 
  catch (MalformedURLException x) 
  { 
   System.out.println (x); 
   x.printStackTrace(); 
  } //catch 
 } //Server 

 /** buildTable 
 * 
 *       This method will allocate space for the word table and 
 *       assign some sample "words of the day". 
 */ 
 public void buildTable () 
 { 
  wordTable = new String[WORDCOUNT]; 
  wordTable[0] = new String("Programming"); 
  wordTable[1] = new String("Analysis"); 
  wordTable[2] = new String("Llama"); 
  wordTable[3] = new String("Vortex"); 
  wordTable[4] = new String("Code"); 
 } //buildTable 

 /** getWord 
 * 
 *       Returns a random word to the client.  This word is extracted 
 *       from the wordTable array.  This is not the best way to get 
 *   a random value, but thats another story <g>. 
 * 
 *       Note: This method was "promised" by the ServerInterface 
 *   interface, and so it must be implemented here in order to 
 *   Server concrete. 
 */ 
 public String getWord () throws RemoteException 
 { 
  System.out.println ("Returning a word of the day."); 
  return wordTable[(int)(Math.random() * 100 % WORDCOUNT)]; 
 } //insertSpaces 

 /** run 
 * 
 *       Just loops indefinately, until the process is killed.  The 
 *   yield is put in for good measure. 
 */ 
 public void run () 
 { 
  System.out.println ("Server online."); 
  while (true) 
  { 
   Thread.yield(); 
  } //while 
 } //run 
} //Server 

// 
// THIS CLASS ALLOWS EVERYTHING AND IS USED ONLY TO ALLOW Server.java 
// TO COMPILE UNDER JDK1.2.x as well as JDK1.1.x -- PLEASE NEVER USE 
// IT UNLESS YOU KNOW WHAT YOUR DOING! :) 
// 

// If using JDK 1.2.x, uncomment the following class definition 
//class MySecurityManager extends SecurityManager 
//{ 
// public void checkPermission (Permission p) 
// { 
//  return ; 
// } //checkPermission 
//} //MySecurityManager 

Listing 3: 

import java.awt.*; 
import java.rmi.*; 
import java.io.*; 

public class Client 
{ 
 public static void main (String args[]) 
 { 
  // check if the user specified a server or not. 
  if (args.length != 1) 
  { 
   System.out.println ("Execution Failure : Bad Number 
of Arguments!\n"); 
   System.out.println ("Usage: java Client <servername>"); 
   System.exit (1); 
  } //if 

  // Attempt to lookup the server by using the Naming.lookup 
function.  If 
  // successful we will have an object of the servers class, 
at which point 
  // get can get our word of the day! 
  try 
  { 
   // Get the server instance from the registry. 
   ServerInterface si = (ServerInterface)Naming.lookup 
(args[0]); 

   // Get the word of the day and display it. 
   System.out.println ("The word for today is: " + 
si.getWord()); 
  } //try 
  catch (Exception x) 
  { 
   System.out.println (x); 
   System.exit (1); 
  } //catch 
 } //main 
} //Client 
  

 

All Rights Reserved
Copyright ©  2004 SYS-CON Media, Inc.
  E-mail: [email protected]

Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.