javarmiremoteobjectrmiregistry

Java RMI return reference to remote object without binding


I have a server and a client communicating through RMI. Their goal is to share a simple object, work on it together, and then simulate a server disconnect.

The sever has its name bound to the rmiregistry. The client uses the rmiregistry to get a remote reference to the server. Then it uses this reference to call a method that returns a reference to the object they will share. Further communication is to be done through this shared object.

The shared object is a UnicastRemoteObject, the server owns its implementation and has a method the returns a reference to the client.

The client knows the interface of the shared object, gets a remote reference from the server and then acts on it. Please note the server should return a remote reference, not a serialized copy.

This is the shared object interface

public interface CInterface extends Remote {
    boolean testMethod() throws RemoteException;
}

And this is its implementation

public class CImpl implements CInterface {
    @Override
    public boolean testMethod() throws RemoteException {
        return false;
    }
}

This is the method of the server that should return a remote reference when the client calls it

public CInterface exportRefToC() throws RemoteException {
    return new CImpl();
}

If I call it from the client, it gives me this exception

java.rmi.UnmarshalException: error unmarshalling return; nested exception is: java.io.WriteAbortedException: writing aborted; java.io.NotSerializableException: sandbox.CImpl

I can get the method to work and return a remote reference if I write it this way

public CInterface exportRefToC() throws RemoteException {
    refToC = new CImpl();
    return (CInterface) UnicastRemoteObject.exportObject(refToC, 1099);
}

What I don't get is why I need to go through the registry again, though not explicitly binding a the name, just to return a remote reference. The client got a reference to the server before, using the rmiregistry, so the bootstrap is done. Now, why can't the server just return a reference to the client without knowing the port of the rmiregistry (1099)?

Bonus question, if the server wants to make the object it shared with the client unavailable (to simulate a disconnection), is there a better way than doing this

UnicastRemoteObject.unexportObject(refToC, true);

EDIT: if I extend the UnicastRemoteObject in the implementation of the shared object, the first version of the method works, however, the unexport does not work and return this exception

java.rmi.ServerException: RemoteException occurred in server thread; nested exception is: java.rmi.NoSuchObjectException: object not exported

EDIT2: checked again, it does work, just need to save the reference before returning it

public CInterface exportRefToC() throws RemoteException {
    refToC = new CImpl();
    return refToC;
}

Solution

  • The shared object is a UnicastRemoteObject

    No it isn't. Look again:

    public class CImpl implements CInterface
    

    Add extends UnicastRemoteObject to that, and provide an appropriate constructor, and your problem will disappear.

    You don't need to call exportObject(), and you don't need a second binding to the Registry.

    Your last question embodies a mistake. It should be unexportObject(), and that is the correct way of making the object unavailable.