Michał Glapa
Michał Glapa

Reputation: 61

no such object in table exception with Java RMI callback

I'm trying to get callbacks working in Java using RMI.

I have a server running on AWS. My client succesfully connects to the server and registers the callback. However, while trying to call it I get the following exception on the server side:

java.rmi.NoSuchObjectException: no such object in table
    at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(StreamRemoteCall.java:276)
    at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:253)
    at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:162)
    at java.rmi.server.RemoteObjectInvocationHandler.invokeRemoteMethod(RemoteObjectInvocationHandler.java:194)
    at java.rmi.server.RemoteObjectInvocationHandler.invoke(RemoteObjectInvocationHandler.java:148)
    at com.sun.proxy.$Proxy3.call(Unknown Source)
    at server.SimpleUpdateManager.callAll(SimpleUpdateManager.java:42)
    at server.Server$1.run(Server.java:77)
    at java.lang.Thread.run(Thread.java:745)

I have no idea why it does happen. I have checked that rc (RemoteCallback implementation instance on the client) is not garbage collected. Do you have any idea why it might not work?

Here are my remote interfaces:

public interface RemoteCallback extends Remote {
    void call() throws RemoteException;
}

and

public interface CallbackManager extends Remote{
    void register(RemoteCallback uc) throws RemoteException;
    void unregister(RemoteCallback uc) throws RemoteException;
    void callAll() throws RemoteException;
}

The relevant server code:

 public class SimpleUpdateManager implements CallbackManager {

  private HashSet<RemoteCallback> callbacks;

  public SimpleUpdateManager() {
     callbacks = new HashSet<>();
  }

  @Override
  public synchronized void register(RemoteCallback uc) {
     callbacks.add(uc);
     System.out.println("Callback registered");
  }

  @Override
  public synchronized void unregister(RemoteCallback uc) {
     callbacks.remove(uc);
  }

  @Override
  public synchronized void callAll() {

     ArrayList<RemoteCallback> toDelete = new ArrayList<>();

     for(RemoteCallback rc : callbacks) {
        try {
           rc.call();
           System.out.println("  Called one listener");
        } catch (RemoteException re) {
           re.printStackTrace();
           toDelete.add(rc);
        }
     }

     for(RemoteCallback rc :toDelete)
       unregister(rc);

     System.out.println("Called all listeners");
  }
}

An excerpt from the Server class:

private static SimpleUpdateManager setupUpdateManager() {
    String name = "UpdateManager";
    SimpleUpdateManager sum = new SimpleUpdateManager();

    try {
        CallbackManager exp = (CallbackManager) UnicastRemoteObject.exportObject(sum, 1100);
        LocateRegistry.getRegistry().rebind(name, exp);
    }
    catch(RemoteException re) {
        System.out.println("Setting up update manager failed");
        re.printStackTrace();
    }
    return sum;
}

public static void runUpdateDaemon(SimpleUpdateManager sum) {

    Thread updateDaemon = new Thread(new Runnable() {
        @Override
        public void run() {
            while(true) {
                try {
                    System.out.println("I will try calling all listeners");
                    sum.callAll();
                    TimeUnit.SECONDS.sleep(5);
                }
                catch (InterruptedException ie) {
                    break;
                }
            }
        }
    });
    //updateDaemon.setDaemon(true);
    updateDaemon.start();
}

And the client code : RemoteCallback implementation

public class SimpleCallback  implements RemoteCallback {
    @Override
    public void call() throws RemoteException {
        System.out.println("Callback called");
    }

    @Override
    protected void finalize() {
        System.out.println("Callback gc'd");
    }
}

and the registering of a callback

try {
     rc = new SimpleCallback();
     UnicastRemoteObject.exportObject(rc, 1100);
     ((CallbackManager) LocateRegistry.getRegistry(host).lookup("UpdateManager")).register(rc);
     System.out.println("Callback registered");
  }
  catch (RemoteException|NotBoundException re) {
     System.out.println("Cannot register callback");
     re.printStackTrace();
  }

Upvotes: 2

Views: 4325

Answers (1)

Michał Glapa
Michał Glapa

Reputation: 61

Here's what the problem was:

The client was behind a firewall which did not allow to access port 1100 from the server, so the DGC assumed that the client has crashed and garbage collected the callback in some way.

Upvotes: 3

Related Questions