12.4 Caching

To illustrate caching, I extend the server object used in the previous sections. I add three accessor methods to access the three server instance variables:

public boolean getBoolean(  );
public int getNumber(  );
public String getString (  );

Now you can add a generic server-object proxy implementation that handles caching. The implementation is essentially the same for all three communications layers:

package tuning.distrib.custom;
   
public class ServerObjectCacher
  implements ServerObject
{
  ServerObject stub;
  boolean b;
  boolean bInit;
  int i;
  boolean iInit;
  String s;
  boolean sInit;
   
  public ServerObjectCacher(ServerObject stub)
  {
    super (  );
    this.stub = stub;
  }
   
  public boolean getBoolean(  )
  {
    if (bInit)
      return b;
    else
    {
      b = stub.getBoolean(  );
      bInit = true;
      return b;
    }
  }
   
  public int getNumber(  )
  {
    if (iInit)
      return i;
    else
    {
      i = stub.getNumber(  );
      iInit = true;
      return i;
    }
  }
   
  public String getString (  )
  {
    if (sInit)
      return s;
    else
    {
      s = stub.getString(  );
      sInit = true;
      return s;
    }
  }
   
  public void setBoolean(boolean flag)
  {
    bInit = false;
    stub.setBoolean(flag);
  }
   
  public void setNumber (int i)
  {
    iInit = false;
    stub.setNumber(i);
  }
   
  public void setString(String obj)
  {
    sInit = false;
    stub.setString(obj);
  }
   
  public void setAll(boolean flag, int i, String obj)
  {
    bInit = iInit = sInit = false;
    stub.setAll(flag, i, obj);
  }
}

As you can see, this is a simple proxy object. Each accessor is lazily initialized, and calling any updating method resets the accessors so that they need to be reinitialized from the server. This ensures that any logic executed on the server is not bypassed. If the server object can be changed by other client programs, you need to add callback support for this caching proxy so that whenever the server object is changed, the client proxy is reset.

Running access tests using this caching proxy is simple. The client code needs to be changed in only one place; once the server object is resolved, the resolved proxy is wrapped in this caching proxy. Then it is used exactly as previously:

ServerObject obj = (ServerObject) Naming.lookup("/ServerObj");
//now wrap the server object with the caching proxy
obj = new ServerObjectCacher(obj);
//All the rest of the code is the same

The timing results are dependent on how many iterations you test of the uncached versus cached access. After the first access, the cached proxy access is a simple local-variable access, whereas the uncached access requires remote messaging. The difference in timings between these two access mechanisms is more than a factor of 1000, so the more iterations of the tests you make, the bigger the overall relative difference in timings you measure. For example, with accesses repeated 500 times, the average cached access takes about 0.5% of the average uncached access time. Doubling the number of repeated accesses to 1000 times doubles the time taken for the uncached access, but the cached access time is essentially the same, so the time is now 0.25% of the average uncached access time.