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;
}
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.