8 March 2006 - 9:19Implementing Active Objects with Java Dynamic Proxies
Introduction
The Active Object pattern is a pattern for writing services that deal with the issue of concurrency in an unorthodox and elegant way. The simple definition of an active object is that it’s an object that is responsible for managing its own execution context instead of using the execution contexts of objects that call methods on it. Active objects are different from passive objects (normal objects) because passive objects re-use the execution context of other objects who call methods on them.
To make the distinction clearer, consider a normal Java object. When a second object calls a method on it, that method executes inside the same JVM process and thread as the caller method. Many times the object being called will have some concurrency requirements - access to that object may not be thread-safe so it must be serialized, or perhaps internally the called object serializes access to some data. On the other hand, when a method is called on an active object, control returns immediately to the caller, and the active object executes the method in some execution context that it manages. The active object may keep an internal worker thread (or pool of threads) to perform execution. A more advanced active object may delegate execution to a different process or a different computer.
For a much more detailed description of the Active Object pattern, see a University of Washington paper by Lavender and Schmidt. This paper discusses the motivation behind the active object pattern, shows a sophisticated implementation of the pattern, and explains some of the disadvantages of the pattern.
If you’re thinking to yourself at this point that the active object pattern doesn’t seem terribly complicated, you’re right. However, now that the free lunch is over, patterns like this one are going to become increasingly important as a means of improving the concurrency of programs and taking advantage of newer hardware.
Advantages of Active Objects
The cool thing about an active object is that is relieves clients of that object from having to deal with concurrency. It’s a much more elegant solution to concurrency problems than using something like the synchronized keyword which can block the caller’s execution context. An active object elegantly separates method invocation from method calling, and allows different strategies for method invocation to be used without having to modify calling code.
In addition, when active objects are realized using the proxy approach (which I’ll discuss below), they also relieve the service object from having to deal with concurrency. Active objects are so elegant because they wrap up all the concurrency handling in such a way that neither caller nor callee need to be written in any special way to deal with concurrent access.
Disadvantages of Active Objects
Active objects have a few disadvantages, of course. The main disadvantage is that all interactions with an active object happen asynchronously, and this style of method calling is not the most natural. Any method that returns a value cannot be used with the active object pattern. Instead, code must be written in an asynchronous style, where active methods take callback objects as parameters and notify their callers when execution has finished. While this style of programming isn’t as natural, it usually doesn’t take much effort to convert an interaction that’s written synchronously to be asynchronous.
As a side note, I believe the asynchronous style of programming is going to become more and more common as programming languages and libraries attempt to exploit high concurrency hardware to a greater degree. It makes sense to become familiar with this pattern now, as it’s very possible that in the future most method invocations will be written this way.
Another disadvantage of active objects is that they introduce a level of complexity and indirection that makes debugging difficult. Using a debugger to step through a call to an active method isn’t terribly useful, since the call returns immediately and the actual work of the method happens in another execution context.
Java Implementation
Now that I’ve introduced active objects and explained what’s good (and bad) about them, the rest of this entry is going to discuss a simple active object implementation in Java. The pattern itself could be implemented in any programming language, but Java has one particular feature that makes implementing active objects very easy.
A common way of implementing an active object is to use the proxy pattern. When implemented this way, both the service implementation and the client can be written in such a way as to have no concurrency concerns at all. A third actor, the proxy, wraps the service implementation and exposes the service interface to callers. The proxy object is responsible for managing an execution context and (eventually) invoking the service methods on the actual service object.
Java dynamic proxies are a perfect fit for implementing active objects in this way. In case you’re not familiar with dynamic proxies in Java, I’ll give a very quick description of them, and for more detail you can check out the official documentation. In a way, dynamic proxies are the opposite of reflection. Reflection allows you to generate method calls at runtime, and dynamic proxies allow you to generate method responses at runtime. Dynamic proxies allow you to “manufacture” an implementation of any interface at runtime by supplying a callback object that will get invoked for any interface method call. The most common use of dynamic proxies is to wrap some real implementation of an interface with some other service. For example, I could implement a very simple profiler by writing a dynamic proxy that wrapped a real service object, and kept track of the amount of time spent inside each method.
Writing a dynamic proxy to implement an active object is pretty simple. The proxy’s InvocationHandler keeps an internal worker thread. As methods are invoked on the proxy, the java.lang.reflect.Method objects and arguments are added to a queue, and control returns immediately to the caller. The internal worker thread pulls methods off of the queue and invokes them on the wrapped service object.
It’s easy to imagine a more advanced active object that spreads the work out over a pool of worker threads, or perhaps uses RMI to send work to other computers to spread out the computation load. The great thing about using dynamic proxies for this is that the original service implementation doesn’t need to know anything about what the active object proxy is doing. The only restriction is that the original service interface must be written in terms of asynchronous methods for this to work.
The following code demonstrates an implementation of the active object pattern using dynamic proxies. The first class is an InvocationHandler that implements a generic active object proxy. The second class is a test harness with a trivial service that adds two integers. If you run the code, note that all the calls to the adder service finish long before the results are done being computed (an artificial delay was put in the adder service to illustrate this point better).
public class ActiveObjectProxy implements InvocationHandler
{
private List callQueue = new ArrayList();
private Object serviceObject;
public ActiveObjectProxy(Object serviceObject)
{
this.serviceObject = serviceObject;
new WorkerThread().start();
}
private synchronized void put(Invokable invokable)
{
callQueue.add(invokable);
notifyAll();
}
private synchronized Invokable get()
{
while (callQueue.size() == 0)
{
try
{
wait();
}
catch (InterruptedException e) {}
}
return (Invokable) callQueue.remove(0);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
Invokable invokable = new Invokable();
invokable.method = method;
invokable.args = args;
put(invokable);
return null;
}
private class WorkerThread extends Thread
{
public void run()
{
while (true)
{
Invokable invokable = get();
try
{
invokable.method.invoke(serviceObject, invokable.args);
}
catch (Throwable t) {}
}
}
}
private static class Invokable
{
public Method method;
public Object[] args;
}
}
public class ActiveObjectTest
{
public static void main(String[] args)
{
AddService addService = (AddService)
Proxy.newProxyInstance(
ActiveObjectTest.class.getClassLoader(),
new Class[] {AddService.class},
new ActiveObjectProxy(new Adder()));
AddResultsCallback callback = new AddResultsCallback()
{
public void addResultsComputed(int x, int y, int sum)
{
System.out.println(x + ” + ” + y + ” = ” + sum);
}
};
long startTime = System.currentTimeMillis();
for (int i = 0; i < 60; i++)
{
addService.add(2 i, 3 i, callback);
}
long elapsed = System.currentTimeMillis() - startTime;
System.out.println(”All calls finished: ” + elapsed + ” ms elapsed”);
}
private static interface AddService
{
public void add(int x, int y, AddResultsCallback callback);
}
private static interface AddResultsCallback
{
public void addResultsComputed(int x, int y, int sum);
}
private static class Adder implements AddService
{
public void add(int x, int y, AddResultsCallback callback)
{
int sum = x + y;
try
{
Thread.sleep(1000);
}
catch (InterruptedException e) { }
callback.addResultsComputed(x, y, sum);
}
}
}
No Comments | Tags: Uncategorized