WeakReferences and Dynamic Proxies
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,
Cachein our example. YesterdayCache
was a concrete class, but for the technique we're talking about
here it must be an interface, typically with a concrete class
likeCacheImplthat 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,
<a href="http://www.java.net/download/jdk6/doc/api/java/lang/reflect/Method.html">Method</a> 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) <a href="http://www.java.net/download/jdk6/doc/api/java/lang/reflect/Proxy.html#newProxyInstance(java.lang.ClassLoader,%20java.lang.Class[],%20java.lang.reflect.InvocationHandler)">Proxy.newProxyInstance</a>(<...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 <T> 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.
- Login or register to post comments
- Printer-friendly version
- emcmanus's blog
- 857 reads





