Architecture & DesignGetting Started with Java RMI Applications

Getting Started with Java RMI Applications

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

RMI (Remote Method Invocation) is an architecture to support distributed computing. In a standalone application architecture, all Java codes reside on a single machine. JVM manages the interaction between Java objects locally. RMI specifically extends the idea to support a distributed system where application code and execution are spread across multiple remote machine aka JVM. The package java.rmi provides the necessary API to implement it in Java. From the programmer’s perspective, the remote objects and methods works similar to local objects and methods. In recent years, many technologies evolved to support client-server architecture, such as EJBs, SOAP, REST, WebSocket, and so forth, which invariably can be used in distributed computing. Java RMI is a veteran, and many of the recent techs such as remote EJB relied on RMI. RMI has a minimum protocol overhead and, apart from supporting legacy applications, it can be used reliably in a distributed application framework. However, there are certain norms to be followed while implementing RMI in Java. The article explores the concepts and intricacies of getting started with Java RMI with appropriate code examples.

RMI Architecture Overview

In RMI architecture, a POJO can be designated as a remote object. The step involved in the process of designating a Java class object into a remote RMI object is called exporting. The exported reference object is called a stub and is responsible for exposing methods that can be called remotely. On successful exporting, the remote object is ready to handle and receive method invocations from the client. The server binds the remote reference to the RMI registry with a unique name. The client looks up the remote reference and maps the method call according to the reference name supplied in the registry. The skeleton is the server-side counterpart of the client stub. It is primarily responsible for receiving data sent by the stub. To avoid security vulnerability, the server registers remote reference objects to the RMI registry running on the same machine.

RMI1
Figure 1: Architectural overview

RMI2
Figure 2: RMI layer model

In a nutshell, the client invokes a server method through a stub. Thestub is aware of the server location and details about how to connect a remote object on the server. A request then passes through the network layer. (In fact, there are several layers involved in transmitting the request. For the sake of simplicity, let’s club them into a network layer.) The skeleton on the server side receives the data and arranges it in a meaningful format and subsequently complies with appropriate responses. The idea gets repeated in a similar fashion on every request-response cycle.

Thus, the key points of RMI architecture are:

  • The server binds the remote reference to the RMI registry with an unique name.
  • the client looks up the RMI registry to get the stub of the remote object.
  • Remote method calls in Java are similar to local method calls.
  • Remote objects from the server can also return a stub to a remote client. As a result, the RMI registry looks up once at the beginning by the client can improve performance considerably.

Implementing an RMI Application

The following steps are involved to create a minimal RMI application:

  1. Create a remote interface.
  2. Implement the remote interface. This class will define methods that will be called remotely.
  3. Create a server program. The server class registers the remote class with the RMI registry.
  4. Create a client program that will access the server object remotely.

Creating a Remote Interface

There are a couple of requirements to be followed while creating a remote interface:

  • A remote interface must be a sub interface of java.rmi.Remote.
  • Each method declared must throw RemoteException.
import java.rmi.Remote;
import java.rmi.RemoteException;

public interface RemoteUtil extends Remote{

   public String getMessage() throws RemoteException;
   public int getFibonacci(int num)
      throws RemoteException;
}

Implementing a Remote Interface

The implementation class is basically the definition of the remote interface. Here, this class specifically extends UnicastRemoteObject because the implementation class of the remote interface goes through the export process to have remote method access by the client. UnicastRemoteObject‘s constructor does all the hard work of exporting automatically on the programmer’s behalf behind the scenes. Because the constructor of UnicastRemoteObject‘s constructor throws RemoteException, the constructor of the inherited class must also throw RemoteException.

import java.rmi.Remote;
import java.rmi.RemoteException;
import java.time.ZonedDateTime;
import java.rmi.server.UnicastRemoteObject;


public class RemoteUtilImpl
   extends UnicastRemoteObject implements RemoteUtil{

   public RemoteUtilImpl() throws RemoteException{
   }

   @Override
   public String getMessage() throws RemoteException{
      return "Hi! from Server
         n Today is "+ZonedDateTime.now();
   }

   @Override
   public int getFibonacci(int num) throws RemoteException{
      if(num==0 || num==1)
         return num;
      int a,b,c, count;
      a=0;
      b=1;
      c=a+b;
      count=2;
      while(count<=num){
         c=a+b;
         a=b;
         b=c;
         count++;
      }
      return c;
   }
}

Creating a Server Program

A server program contains the main code of the server machine. This program updates the RMI registry of the current machine with the help of the rebind() method of the java.rmi.Naming class. The method rebind() associates a name with an object reference.

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

public class RMIServer{

   public static void main(String[] args){
      try {
         RemoteUtilImpl impl=new RemoteUtilImpl();
         Naming.rebind("RMIServer", impl);
      }catch(Exception e) {
         System.out.println("Exception: " + e);
      }
   }
}

Creating a Client Program

The client class uses RMI protocol URL to look up the server name and get a reference to the object. This reference object is used to call the remote methods.

import java.rmi.*;

public class RMIClient{

   public static void main(String[] args){
      try {
         String serverURL="rmi://"+args[0]+"/RMIServer";
         RemoteUtil remoteServer=
            (RemoteUtil)Naming.lookup(serverURL);

         System.out.println(remoteServer.getMessage());
         System.out.println(remoteServer.getFibonacci(5));
      }catch(Exception e) {
         System.out.println("Exception:  " + e);
      }
   }
}

So far so good, but running the application requires certain steps must be followed.

Running the Application

  1. Assuming the PATH and CLASSPATH environments are set properly, compile all four Java files created.
    $ javac *.java
  2. Because the stub presents the remote server interface, it needs to be created. Create it with the following command. This will create the stub file called RemoteUtilImpl_Stub.class.
    $ rmic RemoteUtilImpl

    Note: We may also optionally keep the source file of the stub by using rmic command as follows:

    $ rmic -keep RemoteUtilImpl

    Again, be sure that CLASSPATH is set to the current directory. The utility command rmic can be found in the bin directory of the Java SDK.

  3. Open one terminal and type the following command. This will execute the server machine that maps names to the object reference.
    $ rmiregistry

    The utility command rmiregistry also resides in the bin directory of the ava SDK. The command will simply run the program; there will be no output. Be sure to keep it running and do not close the terminal.

  4. Open another terminal and run the server as follows:
    $ java RMIServer

    The server program is now ready to accept client request, provided no exceptions are thrown.

  5. Now, open another terminal and run the client program as follows:
    $ java RMIClient 127.0.0.1

    The client will show the output received from the server program (as per the code we have written). We are using IP address 127.0.0.1 (loopback) because our experimental client server program resides on the same machine.

  6. That’s all.

Things We Have Overlooked

Security is an issue when working with Java RMI. In a real life application, we need to make sure that the server program runs under a security manager. The security manager restricts access to the privileged server resources with the help of a Java policy. This policy is nothing but a simple text file with some permission code. For example, to give completely unrestricted access privilege to all code, we may write the permission policy file as follows:

grant{
   permission java.security.AllPermission
};

However, this is not a good idea in a real application, but it’s all right to experiment with RMI in Java.

Conclusion

The intricacy involved in Java RMI is more than meets the eye. In fact, a lot of complexity goes behind the scenes. This technology still survives as a legacy, gradually overshadowed by the latest techs. Another tech closely related with RMI is CORBA. They are gradually becoming obsolete in the commercial application arena, yet they are conceptually rich, intriguing, and can be good point to learn prior to learning many of the newest techs.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories