The Source for Java Technology Collaboration
User: Password:



Eamonn McManus

Eamonn McManus's Blog

WeakReferences and Dynamic Proxies

Posted by emcmanus on July 21, 2005 at 06:45 AM | Comments (2)

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 <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.


Bookmark blog post: del.icio.us del.icio.us Digg Digg DZone DZone Furl Furl Reddit Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment

  • I'll find few more examples about dynamic proxies paired with annotations in my article at http://today.java.net/pub/a/today/2005/07/05/IOCAnnotation.html

    Posted by: euxx on July 21, 2005 at 09:29 AM

  • Glenn Skinner notes that you could also modify the WeakProxy class so that the object it returns is already an MBean. In other words, if the CacheImpl object implements the CacheControlMBean interface, then you could wrap it in a proxy that also implements that interface, and forwards the methods of the interface to the CacheImpl object through a WeakReference.

    However, if you do this you will run into a problem. The returned proxy will be an object of a class called something like $Proxy7, but the Standard MBean pattern requires the class that implements CacheControlMBean to be called CacheControl. There are two ways to work around this. The first is to use the class javax.management.StandardMBean, which is the usual way to lift the naming restriction for Standard MBeans. The second is to use an MXBean instead of a Standard MBean. MXBeans don't have this naming restriction.

    Posted by: emcmanus on February 13, 2008 at 05:52 AM



Only logged in users may post comments. Login Here.


Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds