The 10 minutes “Getting started with RMI” tutorial

Since Java 5 working with RMI (Remote Method Invocation) is very easy. You don’t need the rmic compiler unless you work with legacy RMI clients. Now stubs are generated automatically at runtime.
As a result, writing distributed or client-server applications becomes trivial. Let’s see a very minimalistic example.

Our scenario will have a server sharing an object via RMI and a client calling the shared instance.

First we need to define the interface for our remote object. In our case it is a simple interface for incrementing a shared counter.

Api.java

package com.littletutorials.rmi.api;

import java.rmi.*;

public interface Api extends Remote {
    public Data incrementCounter(Data value) throws RemoteException;
}

Notice the fact that our interface implements java.rmi.Remote to mark the interface as available remotely. Also the interface uses a Data class to hold data. The purpose of this class in this tutorial is only to show how to send your own objects across the wire. In itself it doesn’t add any value over an int.

Data.java

package com.littletutorials.rmi.api;

import java.io.*;

public class Data implements Serializable {
    private static final long serialVersionUID = 1L;
    private int value;

    public Data(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }

    public void setValue(int value) {
        this.value = value;
    }
}

The Data class implements java.io.Serializable to specify that its instances can be sent across the network.

And here is the implementation for our shared counter:

ApiImpl.java

package com.littletutorials.rmi.server;

import java.rmi.*;
import java.rmi.server.*;
import com.littletutorials.rmi.api.*;

public class ApiImpl extends UnicastRemoteObject implements Api {
    private static final long serialVersionUID = 1L;
    private int counter = 0;

    public ApiImpl() throws RemoteException {
        super();
    }

    @Override
    public synchronized Data incrementCounter(Data value) throws RemoteException {
        counter += value.getValue();
        return new Data(counter);
    }
}

Notice that I decided to extend UnicastRemoteObject to have my object automatically exported for RMI access with JRMP (Java Remote Method Protocol). If you don’t want to extend this class or you cannot because you need to extend some other class you could call:

UnicastRemoteObject.exportObject(this);

But keep in mind, in this case you have to generate your stub using rmic!

Now it is time to write the server application

Server.java

package com.littletutorials.rmi.server;

import java.rmi.*;
import java.rmi.registry.*;

import com.littletutorials.rmi.api.*;

public class Server {
    private static final int PORT = 1099;
    private static Registry registry;

    public static void startRegistry() throws RemoteException {
        // create in server registry
        registry = java.rmi.registry.LocateRegistry.createRegistry(PORT);
    }

    public static void registerObject(String name, Remote remoteObj)
        throws RemoteException, AlreadyBoundException {
        registry.bind(name, remoteObj);
        System.out.println("Registered: " + name + " -> " +
            remoteObj.getClass().getName() + "[" + remoteObj + "]");
    }

    public static void main(String[] args) throws Exception {
        startRegistry();
        registerObject(Api.class.getSimpleName(), new ApiImpl());
        Thread.sleep(5 * 60 * 1000);
    }
}

Usually most of the examples require the rmiregistry to be started as a separate process. But, since this tutorial is about simpler ways, we create the RMI registry “in-process“. Then we register an instance of the API implementation with our registry. As instance name we use the API interface class name.

The only piece of the puzzle remains the client code:

Client.java

package com.littletutorials.rmi.client;

import java.rmi.registry.*;
import com.littletutorials.rmi.api.*;

public class Client {
    private static final String HOST = "localhost";
    private static final int PORT = 1099;
    private static Registry registry;

    public static void main(String[] args) throws Exception {
        registry = LocateRegistry.getRegistry(HOST, PORT);
        Api remoteApi = (Api) registry.lookup(Api.class.getSimpleName());
        for (int i = 1; i <= 100; i++) {
            System.out.println("counter = " +
                remoteApi.incrementCounter(new Data(1)).getValue());
            Thread.sleep(100);
        }
    }
}

An this is it. You can now start the server and the clients!

To deploy this code on different machines you need to create 3 jar files:

api.jar: Api.java, Data.java
server.jar: Server.java, ApiImpl.java
client.jar: Client.java

On the server machine the class path will contain api.jar and server.jar
On the client machine the class path will contain api.jar and client.jar

Notes:

  1. It is your responsibility to make the remote objects thread safe
  2. This example is not production code. For example there is no exception handling
  3. It is possible to download the shared code in api.jar at runtime from an HTTP server but this is beyond the purpose of this short tutorial

5 thoughts on “The 10 minutes “Getting started with RMI” tutorial”

  1. Why Api extends Serializable?? You don’t expect the service itself to be transferred over the wire, do you? (I don’t know much about RMI, but I don’t think rmiregistry offers persistence for remote objects, which would make Serializable a sensible requirement).

    Why ApiImpl ctor throws RemoteException??

  2. @ Dimitris Andreou

    You are right, it doesn’t make sense. The Serializable was a leftover from a more complex example. I changed the tutorial to show how to use Serializable with RMI.

    The constructor throws RemoteException because super() throws RemoteException. Since the super() call has to be the first statement in the constructor you cannot mask this exception.

  3. On the client side, can the remoteApi object be shared across threads ? (Assuming the implmentation is threadsafe. Are there any limitations to this ?

    Rajeev

  4. Hi!
    Thanks very much for your example. It’s been very helpful!
    I’ve got one question, why the server class launches a Thread.sleep(5 * 60 * 1000) instruction?

    Diego

  5. Hello! Your tutorial has been most helpful. I have a question: Why doesn’t your example require to meddle with java.policy files or with the System Security Manager?
    Thanks!

Comments are closed.