Search code examples
javarmi

Java RMI - I don't understand why there is no shared state


I have a problem (maybe a fundamental understanding problem), with Java RMI. I have a "server" object and man "clients". Both are RMI objects (they implement to different Remote interfaces). The clients register themself at the server, and are stored in a list. The server will later distribute some work to the clients and fetch the result.

The problem is, that the list, where the clients are stored, seems to be not shared (I have no better description). I.e. if one client calls the server interface and registers itself, it will be added into the list, but the call seems to be only locally.

Following is an minimal example of the code:

IRemote.java

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface IRemote extends Remote {
    public void remoteCall() throws RemoteException;
}

RemoteImple.java

import java.io.Serializable;
import java.rmi.RemoteException;
import java.util.ArrayList;

public class RemoteImpl implements IRemote, Serializable {
    public ArrayList<Integer> list = new ArrayList<Integer>();

    @Override
    public void remoteCall() throws RemoteException {
        list.add(23);
        System.out.println("In remote call: "+list.size());
    }
}

RegAndServer.java

import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class RegAndServer {
    public static void main(String[] args) throws RemoteException, InterruptedException {
        Registry reg = LocateRegistry.createRegistry(Registry.REGISTRY_PORT);
        RemoteImpl impl = new RemoteImpl();
        reg.rebind("server", impl);

        while(true) {
            Thread.sleep(2000);
            System.out.println("Server sees: "+impl.list.size());
        }
    }
}

Client.java

import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;

public class Client {
    public static void main(String[] args) throws MalformedURLException, RemoteException, NotBoundException {
        IRemote remote = (IRemote) Naming.lookup("//localhost/server");
        remote.remoteCall();
    }
}

If I start the server java RegAndServer it will output "Server sees: 0" in a loop. If I now start the client java Client in a different shell I get "In remote call: 1". However the server process still outputs "Server sees: 0".

Why is this? What am I doing wrong? I actually thought I had understood Java RMI :(


Solution

  • Your RemoteImpl should not be Serializable (you don't want to send a copy of it over the network, just the stub, and the runtime does that for you), and must extend UnicastRemoteObject

    import java.rmi.RemoteException;
    import java.rmi.server.UnicastRemoteObject;
    import java.util.ArrayList;
    
    public class RemoteImpl extends UnicastRemoteObject implements IRemote {
        public ArrayList<Integer> list = new ArrayList<Integer>();
    
        public RemoteImpl() throws RemoteException {
    
        }
        @Override
        public void remoteCall() throws RemoteException {
            list.add(23);
            System.out.println("In remote call: "+list.size());
        }
    }
    

    Keep in mind that the code is executed in the sever side, even though the call is made from the client, so the message "In remote call" will appear in the server log.