Martin Thurau
Martin Thurau

Reputation: 7654

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 :(

Upvotes: 1

Views: 569

Answers (2)

fortran
fortran

Reputation: 76077

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.

Upvotes: 2

andrey
andrey

Reputation: 842

Try to change:

public ArrayList<Integer> list = new ArrayList<Integer>();

to

public static ArrayList<Integer> list = new ArrayList<Integer>();

If it helps. Make it safe:

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

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

Upvotes: 0

Related Questions