Archive for March 15th, 2010

RMI, JDK 1.6, OSGi ( 1 of 2 )

Introduction

If you ever wanted to search for some information about Rmi, you probably founds the sun tutorial which is located here : http://java.sun.com/docs/books/tutorial/rmi/overview.html

This tutorial is really complete, but it has been there for years now. And since then, some big improvements have been done on the rmi support of java. For example since 1.5 you do not have to create the stub or the skeleton class.

Due to that, I believe that there are easier ways to implement a RMI service now, and that is what I will try to show you in this article.

While preparing this small article, I digged a little bit on google. It seems that a lot of users have problems or at least had problems in the past, especially because the codebase classloading messes.
It is required to put your jars on a webserver, set a security manager, and set some system properties to find back the published jars, and only then the magic of rmi class loading will occur.
But does this fit on an osgi architecture, where each application has its own well defined dependency context, defined using a declarative way. And does the service support interface versionning ?
Basically no, especially for the interface versionning !

In this article, I will try to show you my way of implementing rmi application, in a OSGi environment. IMHO using the easier way, or at least a less messy way.

Writting the SimpleRmiServer

While trying to be as simple as I can, I will try to show you how to write a RMI Service that will use most of the main features of RMI :

  • Publishing a RMI interface
  • Sending custom object and receiving it back
  • And even show you how to implement a service callback

Design the remote interfaces

Sources : The svn repository is http://svn.gallot.be/blog/rmi-osgi-jvm16/part-1/SimpleRmiInterface/

The remote interface of my test service is really simple. It simply contains 2 methods

  • The first one is used to execute some codes on the server synchronously
  • The second method allows you to post a request, and to be called when the result is ready

public interface ISimpleRmiService extends Remote {
  public static final String BIND_NAME = "ISimpleRmiService";
  public Response request(Request request) throws RemoteException;
  public void asynchronousRequest(ICallback callback, Request request) throws RemoteException;
}

Remember that any RMI interface needs to follow some rules !

  • The interface must extend the interface java.rmi.Remote
  • Any methods of this interface must throw java.rmi.RemoteException
  • Any parameters of this interface must be serialisable
  • Any callback interface must follow these rules as well
  • And as a personal rule, I also add the binding name as a static field, and prefix the interface name with I

This is it ! As you can see the interface is composed of two methods

Let’s now design the callback interface using the same rules


public interface ICallback extends Remote {
  public void response( Response response ) throws RemoteException;
}

And the parameter object, request and response are basically a Serializable bean which is holding a string value.


public class Request implements Serializable {
  private static final long serialVersionUID = 1L;
  private String value;
  public Request( String value ) {
    this.value = value;
  }
  public String setValue() {
    return value;
  }
}

Then all these interfaces are putted in a single OSGi bundle, which is going to be shared between the client and server.

Implementing the remote server

Sources : The svn repository is http://svn.gallot.be/blog/rmi-osgi-jvm16/part-1/SimpleRmiServer/

The code of the remote service does 3 things.

Create the RMI Registry

The rmiregistry is a special component in the RMI architecture used to expose the published service.
The normal way of starting it, is to run the rmiregistry command. But personally I do not really like having to manually start the rmiregistry by hand. It is basically another process to monitor in the system, and even more, if for any reason the rmiregistry is restarted, every RMI server needs to be exported again!
But since the JDK 1.1, it is possible to start the rmiregistry using a simple call to LocateRegistry.createRegistry
I believe that this way of handling the rmiregisty is better most of the time, it is tying your rmi server with the rmiresitry within the same jvm. At least when you start your rmi server, the rmigistry starts automatically!
Also, having the rmiregistry and the rmi service inside the same jvm allows you to bypass some of the class loader messes of rmi.

Implementing the service class

The rmi service class is really easy to implement, it is a simple class implementing the remote interface, and extending UnicastRemoteObject.

public class SimpleRmiService extends UnicastRemoteObject implements ISimpleRmiService {
  public Response request(Request request) {
    Response response = new Response( "Response @ " + request.getValue());
    return response;
  }

  public void asynchronousRequest(final ICallback callback, final Request request) throws RemoteException {
    executor.execute( new Runnable() {
      public void run() {
        try {
          Response response = new Response( "Response @ " + request.getValue() );
          callback.response(response);
        } catch ( Exception ex ) {
          ex.printStackTrace();
        }
      }
    });
  }
}

And to publish it, simply register it with the remote registry previously created during the first step

Binding the Rmi service class

You can instantiate the rmi service class, and publish it to the rmiregistry.

One good place to start the rmi registry and to bind the rmi service is the Bundle Activator class of your bundle. In the start method of the bundle activator, simply do :

Registry registry = LocateRegistry.createRegistry( Registry.REGISTRY_PORT );
registry.bind( ISimpleRmiService.BIND_NAME, new SimpleRmiService());

To try the server part of this application, simply start your favourite osgi framework, and install the interface bundle and the service bundle. Or just right click on the “SimpleRmiServer.launch” file and run it.

Implementing the remote client

Sources : The svn repository is http://svn.gallot.be/blog/rmi-osgi-jvm16/part-1/SimpleRmiClient/

Implementing the client is even easier.

  • You just need to first connect on the rmi registry
  • Lookup the rmi service using the binding name
  • Call the method

Registry registry = LocateRegistry.getRegistry( Registry.REGISTRY_PORT );
ISimpleRmiService simpleRmiService = (ISimpleRmiService)registry.lookup( ISimpleRmiService.BIND_NAME );
Response response = simpleRmiService.request( request );

But what about the callback implementation ?

It is almost the same method as for the ‘direct’ call, you just need to provide a correct ICallback implementation.
The ICallback implementation is just another rmi service binded anonymously, therefor it needs to behave appropriately as a remote object.
As for the main service, the best way to achieve this is to extend the UnicastRemoteObject.


public class Callback extends UnicastRemoteObject implements ICallback {
  private static final long serialVersionUID = 1L;
  public Callback() throws RemoteException {
  }
  public void response( Response response ) {
    System.out.println("Response received: "+response);
    System.out.println("Response : "+response.getClass().getClassLoader());
  }
}

To test it, just run the run configuration named ‘SimpleRmiClient.launch’

Summary

As you can see, it is working. No need to create the stub using the rmic tools, no need to start the rmi registry, and you do not need to publish the jar interface on a webserver.

However this solution is too light for a real life example.

  • You cannot have multiple rmi services, since the registry is started with the SimpleRmiServer
  • Restarting the SimpleRmiServer will fail, when the registry is rebinded



These problems will be solved in my next article, and I will even try to show you how to publish different versions of the same interface !