Interview Questions

Spring Java RMI

In this section lets discuss about Java RMI (Remote Method Invocation) and how to implement the same using Spring.

Consider a scenario where you want to access a method which is available in a class in a different system or different JVM. This is usually called as Remote Procedure call.

Java provides a valuable API called RMI to make this work.

Following are the steps required to expose a RMI service in java.

1. Create a remote interface, an interface which extends java.rmi.Remote. Also the methods in this interface should throw RemoteException. Only the methods of this interface will be exposed as RMI service.

2. Create a Server class which implements the remote interface. Compile the server classes to generate skeletons.

3. Server instance should be bonded with RMI registry using Naming.bind(,) method.

4. Initiate the RMI Registry (rmic) utility.

5. Create a client class to access the server object or service using RMI url for eg rmi:://:port/serviceName

Compile the Client classes using rmic tool to generate stubs.

6.) Client should also contain the remote interface in its system .

7.) Default port for RMI is 1099.

When the client calls the methods in server object, its actually making calls to the client stub created locally. The stub is the one which acts as a proxy to make remote invocation on the server skeletons. The server skeleton is the one which makes call to the actual server object method and returns the result. The result is returned back to the client stub and finally to the client.
The client stub and server skeleton are those which take care of the networking/serialization of objects between client and server. The parameters which are passed between client/server should be serializable (implement Serializable interface).

Oops! Quite a list of tasks to be done to create RMI Service in Java.

Lets find out whether its easy to implement RMI service using Spring framework.

We will expose simple PrimitiveProcessorIntf interface as a RMI service.

Follow the steps below to create and access a RMI service using spring api.

1. Create a primitive Processor interface as follow. Its a normal java interface and it need not extend java.rmi.remote interface.

PrimitiveProcessorIntf Interface

package in.techdive.spring.examples;

public interface PrimitiveProcessorIntf
{
  /**
   * This method reverses the digits of the integer passed as the parameter.
   */

  int revDigits(int i);

  /**
   * This method converts string to an integer
   */

  int convStrToInteger(String s);
}

2. Create an implementation class for the above interface.

PrimitiveProcessor Class

/**
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

package in.techdive.spring.examples;

import in.techdive.spring.rmi.PrimitiveProcessorIntf;

public class PrimitiveProcessor implements PrimitiveProcessorIntf
{
  public PrimitiveProcessor()
  {
  }

  @Override
  public int convStrToInteger(String x)
  {
    String q = x;
    int s = 0;
    int r = 0;
    int j = 10;
    for (int i = 0; i < q.length(); i++)
    {
      char c = q.charAt(i);
      int m = c - 48;
      s = (s * j) + m;

    }
    System.out.println(s);
    return s;
  }

  @Override
  public int revDigits(int x)
  {
    int q = x;
    int s = 0;
    int r = 0;
    int i = 10;
    while (q >= 1)
    {
      r = q % i;
      q = q / i;
      if (q != 0)
      {
        s = (s + r) * i;
      }
      else
      {
        s = s + r;
      }
    }
    System.out.println(s);
    return s;
  }
}

3. create Spring bean configuration class as follow

RMI-Service.xml

<description>
    This file contains the bean descriptions for the beans to expose RMI Service
</description>

<bean id="primitiveProcessor" class="in.techdive.spring.rmi.impl.PrimitiveProcessor"/>

<bean id="rmiService" class="org.springframework.remoting.rmi.RmiServiceExporter">
    <description>
        RMI Service Exporter, takes care of exporting the Service as a RMI Server
    </description>

    <property name="service" ref="primitiveProcessor"/>
    <property name="serviceName" value="primitiveProcessor"/>
    <property name="serviceInterface" value="in.techdive.spring.rmi.PrimitiveProcessorIntf"/>
    <property name="registryPort" value="1299" />
</bean>

4. As shown above, org.springframework.remoting.rmi.RmiServiceExporter bean should be injected with RMI service bean, service name, service interface name and RMI port name. This is the bean which exposes RMI service. This is where most of the work is getting done and springs saves work for us.

5. Now create a server class, RMIServer.java to initiate spring context and start the RMI Service as follows.

RMIServer Class

/**
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

package in.techdive.spring.examples;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

public class RMIServer
{
  public RMIServer()
  {
  }

  public static void main(String[] args)
  {
    ApplicationContext ctx = new FileSystemXmlApplicationContext(
      new String[] { "classpath*:RMI-Service.xml" });
  }
}

6. Now create a client class, RMIClient.java to access the RMIService as follows

RMIClient Class

package in.techdive.spring.rmi.client;

import org.springframework.aop.framework.ProxyFactory;
import org.springframework.remoting.rmi.RmiProxyFactoryBean;

import in.techdive.spring.rmi.PrimitiveProcessorIntf;
import java.rmi.Remote;
import java.rmi.server.UnicastRemoteObject;

public class RMIClient
{

    public RMIClient()
    {
    }

    public static void main(String[] args)
    {
        PrimitiveProcessorIntf intf = getRMIServiceImpl(
                "rmi://localhost:1299/primitiveProcessor",
                PrimitiveProcessorIntf.class);

        System.out.println(intf.revDigits(1349));
    }

    public static PrimitiveProcessorIntf getRMIServiceImpl(String url,
            Class clazz)
    {
        RmiProxyFactoryBean proxy = new RmiProxyFactoryBean();
        proxy.setServiceInterface(clazz);
        proxy.setServiceUrl(url);

        PrimitiveProcessorIntf intf = (PrimitiveProcessorIntf) ProxyFactory
                .getProxy(proxy.getServiceInterface(), proxy);

        return intf;
    }
}

Note that the method getRMIServiceImpl(String url, Class clazz) uses a RMIProxyFactoryBean to create proxies for the RMI service implementation. This proxy acts as a stub from client side to invoke the remote server object.