ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Seamlessly Caching Stubs for Improved Performance
Pages: 1, 2, 3

Once ServerDescription is in place, implementing RemoteStubCache is fairly simple. All RemoteStubCache needs to do is maintain a hash table of stubs using instances of ServerDescription as keys. Here's the code for RemoteStubCache:



public class RemoteStubCache
{
  private static Hashtable _serverDescriptionsToStubs= new Hashtable();

  public static RemoteStub getStubToRemoteObject(ServerDescription serverDescription) throws ServerUnavailable
  {
    RemoteStub returnValue = (RemoteStub) _serverDescriptionsToStubs.get(serverDescription);
    if (null == returnValue) { returnValue = serverDescription.getStub() ;
if (null!= returnValue) {
  _serverDescriptionsToStubs.put(serverDescription, returnValue);
}
else {
  throw new ServerUnavailable();
}
    }
    return returnValue;

  }

  public static void removeStubFromCache(ServerDescription serverDescription) {
    _serverDescriptionsToStubs.remove(serverDescription);
  }
}

Extending the Command Object Framework to Use RemoteStubCache

We are now in a position to extend the framework for command objects to use a RemoteStubCache. This involves making a slight change to AbstractRemoteMethodCall by adding a new method, remoteExceptionOccurred, to be called when a remote method call fails for unknown reasons (e.g., when the RMI runtime throws an instance of RemoteException).

After I've changed AbstractRemoteMethodCall, I'll introduce a new abstract command object, ServerDescriptionBasedRemoteMethodCall, which extends AbstractRemoteMethodCall and uses instances of ServerDescription to retrieve stubs from RemoteStubCache.

The changes to AbstractRemoteMethodCall are slight. We need to add the remoteExceptionOccurred method and call it from within the retry logic whenever a remote exception is thrown. Here is the new implementation of makeCall, with all of the the new lines highlighted.

  public Object makeCall() throws ServerUnavailable, Exception {
    RetryStrategy strategy = getRetryStrategy();
    while (strategy.shouldRetry()) {
Remote remoteObject = getRemoteObject();
if (null==remoteObject) {
  throw new ServerUnavailable();
}
try {
  return performRemoteCall(remoteObject);
}
catch (RemoteException remoteException) {
  try {
    remoteExceptionOccurred(remoteException);
    strategy.remoteExceptionOccurred();
  }
  catch (RetryException retryException) {
    handleRetryException(remoteObject);
  }
}
    }
    return null;
  }

  
  protected void remoteExceptionOccurred(RemoteException remoteException) {
    /* ignored in based class. */
  }
g

This version of AbstractRemoteMethodCall will actually work with the framework from the first article; the only difference is that when an instance of RemoteException is thrown, the empty method remoteExceptionOccurred might be called (in practice, HotSpot will eventually get rid of the method call).

But remoteExceptionOccurred turns out to be a very useful method when you're caching stubs. The reason is simple: the goal of a stub cache is to reuse the same stub to a remote server unless the stub isn't valid any longer. In general, it's very hard to tell if a stub isn't valid, but we do know the following facts:

  • If using the stub in a remote method call results in an instance of RemoteException being thrown, then the stub probably isn't valid.
  • If the stub isn't valid, an instance of RemoteException will be thrown when our code attempts to use it to make a remote method call.

For these reasons, the new subclass of AbstractRemoteMethodCall, which has the singularly unlovely name ServerDescriptionBasedRemoteMethodCall, will flush the stub from the cache if an instance of RemoteException is thrown (by calling the RemoteStubCache's static removeStubFromCache method).

Here's the code for ServerDescriptionBasedRemoteMethodCall:

public abstract class ServerDescriptionBasedRemoteMethodCall extends AbstractRemoteMethodCall {
  protected ServerDescription _serverDescription;

  public ServerDescriptionBasedRemoteMethodCall(ServerDescription serverDescription){
    _serverDescription = serverDescription;
  }

  protected Remote getRemoteObject() throws ServerUnavailable {
    try {
RemoteStub stub = RemoteStubCache.getStubToRemoteObject(_serverDescription);
return stub;
    }
    catch (ServerUnavailable serverUnavailable) {
System.out.println("Can't find stub for server " + _serverDescription);
throw serverUnavailable;
    }
  }

  protected void remoteExceptionOccured(RemoteException remoteException) {
    RemoteStubCache.removeStubFromCache(_serverDescription);
  }
}

This simply implements getRemoteObject as a call on a static method of RemoteStubCache. If RemoteStubCache already has the stub, it simply retrieves the stub from the hash table and returns the stub (without any remote method calls being made). Otherwise, RemoteStubCache fetches a stub from the remote registry. But, whenever an instance ofRemoteException is thrown, RemoteStubCache will be told to discard the stub. This means that if a stub has gone bad for any reason, it will be thrown away (and a replacement stub will be pulled from the remote registry) the next time any command object attempts to use it.

This will all happen without the programmer of the client code needing to do anything (or even knowing that a local stub cache is being used). It all just fits seamlessly into our framework.

Pages: 1, 2, 3

Next Pagearrow