Skip to main content

WeakReferences and Dynamic Proxies

Posted by emcmanus on July 21, 2005 at 6:45 AM PDT


Yesterday
I talked about how you can use

WeakReference
to allow a resource to be
garbage collected even if it is referenced by another object,
a JMX MBean in the case in question. In fact, you can use

dynamic proxies
to provide a more general
framework for this sort of situation.

In the specific situation I was discussing before, you have a
resource such as a cache, that may be garbage collected
when it is no longer referenced. And you have a
controller such as a JMX MBean that references the
resource. But you don't want this reference to stop the resource
from being garbage collected.

We can generalize this as follows:

  • The resource is defined by some Java interface,
    Cache in our example. Yesterday Cache
    was a concrete class, but for the technique we're talking about
    here it must be an interface, typically with a concrete class
    like CacheImpl that provides its
    implementation.
  • Some references to the resource are weak, meaning
    that they should not prevent the resource from being garbage
    collected.
  • If the resource is garbage collected, and these weak
    references try to use it, we want to be able to define what
    happens. This might just be to throw an exception, or it might
    be take an action such as unregistering an MBean.

Dynamic proxies are an enormously useful feature of the
Java platform once you understand what they are for. Suppose
you have a Java interface, like Cache. A dynamic
proxy allows you to produce a Java object that implements that
interface. Every time someone calls one of the interface's methods
on this object, the following method from the

InvocationHandler
interface is called to handle it:

public Object invoke(Object proxy,
             Method method,
         Object[] args) throws Throwable;

The method parameter allows you to know which
method of the interface was called, and the args
parameter allows you to know what arguments it was called with.
(The proxy argument is usually ignored.)

Inside invoke, you can do whatever's appropriate.
You can create a handler that always throws an exception, to
produce a dummy implementation of an interface that checks error
behaviour for example. You can create a handler that logs the
method call or performs security checks before reinvoking the
same Method on another object.

It is this second kind of possibility that interests us here.
If the WeakReference is still valid, then we just
want to invoke the same method on the resource. If it's not
valid, then we want to invoke the user-supplied
missingHandler instead.

Suppose the Cache interface looks like this:

public interface Cache {
    public int dropOldest(int n);
}

Suppose we create a dynamic proxy for this interface, like
this:

InvocationHandler handler = new SomeInvocationHandler(...);
Cache proxy = (Cache) Proxy.newProxyInstance(<...stuff...>, handler);

Then when we later call proxy.dropOldest(5), this will
result in a call to

handler.invoke(proxy, method, new Object[] {5});

where method is a
java.lang.reflect.Method representing the method
Cache.dropOldest(int).

What we want handler.invoke to do is to call this
same Method on the resource, assuming it still
exists, and otherwise run the user-supplied
missingHandler.

Putting this all together, we get this:

import java.lang.reflect.*;
import java.lang.ref.*;
import java.util.concurrent.Callable;

public class WeakProxy {
    public static <T> T make(T resource,
     Class<T> interfaceClass,
     Callable<Object> missingHandler) {
Handler handler = new Handler<T>(resource, missingHandler);
Object proxy = Proxy.newProxyInstance(interfaceClass.getClassLoader(),
      new Class[] {interfaceClass},
      handler);
return interfaceClass.cast(proxy);
    }

    private static class Handler<T> implements InvocationHandler {
Handler(T resource, Callable<Object> missingHandler) {
    this.resourceRef = new WeakReference<T>(resource);
    this.missingHandler = missingHandler;
}

public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
    T resource = resourceRef.get();
    if (resource == null)
return missingHandler.call();
    else
return method.invoke(resource, args);
}

private final WeakReference<T> resourceRef;
private final Callable<Object> missingHandler;
    }
}

The type parameter to WeakProxy.make
allows us to specify that the resource parameter
must implement the interface specified by the
interfaceClass parameter, and that the result of
WeakProxy.make also implements this interface.

In our example of a CacheControlMBean, we could
use this class as follows:

...
Cache cache = new CacheImpl();  // or whatever
Cache weakCache =
    WeakProxy.make(cache, Cache.class, exceptionWhenMissingHandler);
CacheControlMBean cc = new CacheControl(weakCache);
ObjectName on = ...;
mbeanServer.registerMBean(cc, on);
...

    private static final Callable<Object> exceptionWhenMissingHandler =
new Callable<Object>() {
    public Object call() {
throw new IllegalStateException("Object no longer exists");
    }
};
...

With some more work, we could have the WeakProxy
class create a thread that calls the missingHandler
as soon as the resource is garbage-collected, rather than waiting
for somebody to call the proxy. The code would look something
like the final version of the CacheControl MBean I
posted before.

Related Topics >>