Any component that implements this interface indicates it is able to
accept method calls asynchronously. Clients and/or containers of the
component have the option to call the handle() method rather
than the work interface method directly.
The AsyncEnabled
contract
This interface is intentionally very broad. In fact, its method signature
could be used for an Interceptor as well, and thus even be the
basis of a generic Aspect-Oriented Programming tool. That's not the
intention here: the contract surrounding AsyncEnabled is more specific.
The invocation passed to the
AsyncEnabled.handle(Invocation) method should be
an invocation that should be directly applicable to the receiving instance.
In other words, the following implementation should always produce correct
behaviour:
// FRAGMENT
AsyncEnabled instance = new SomeAsyncEnabled()
{
public Object handle( Invocation invocation )
{
return invocation.getMethod().invoke( this, invocation.getArgs() );
}
public String someMethod()
{
return "Hello Asynchronous client";
}
}
This means that the first call in the code below is okay, while the
second one is problematic.
// FRAGMENT
// this is okay....
Method m = instance.getClass().getMethod( "someMethod", new Object[0] );
Object[] args = new Object[0];
Invocation invocation = new Invocation( instance, null, m, args );
instance.handle( invocation ); // returns "Hello Asynchronous client"
// but this is not!
Method m = SomeClass.getClass().getMethod( "doStuff", new Object[0] );
Object[] args = new Object[0];
Invocation invocation = new Invocation( instance, null, m, args );
instance.handle( invocation );
// exception: can't invoke SomeClass.doStuff() on instance!
Furthermore, you should not assume that all methods of an
AsyncEnabled class can be invoked asynchronously. The mechanism
to determine whether any particular method can indeed be invoked
asynchronously is not specified by this interface, but I suggest clear API
documentation in addition to any other mechanisms.
Dealing with return values
When a method is invoked asynchronously, it is not possible for the
caller to receive the return value of that method directly. This is best
solved by using a callback.
A callback is an object that the caller supplies as an argument to the
method call. The AsyncEnabled component will, on completion of
the asynchronous call, provide the return value to the callback object. In
the simplest case, the callback object is simply the caller itself, and the
return value is provided through some kind of simple setter method. The
below is a simple example; much more elaborate setups are possible.
A Complete Example
In the below example, the Client class will send its
commandline arguments asynchronously to an AsyncService . This
service creates a thread (which sleeps for a random time just to demonstrate
how asynchronous it all is) to process the argument, sending the result back
to the Client via the Callback interface. Of
course, this example is as contrived as any!
// SAMPLE CODE
interface Callback
{
public void receiveResult( Object result, long uniqueIdentifier );
}
interface SomeService
{
public String doSomethingImmediately( String argument );
}
public void doSomething( String argument,
Callback callback long uniqueIdentifier );
}
class AsyncService implements AsyncEnabled, SomeService
{
public Object handle( Invocation invocation )
{
new Thread( new Runnable()
{
public void run()
{
Thread.sleep((int)(Math.random()*1000));
invocation.getMethod().invoke( this, invocation.getArgs() );
} }.start();
return null;
}
public String doSomethingImmediately( String argument )
{
return "task " + * argument + " done!";
}
public void doSomething( String argument, Callback callback,
long uniqueIdentifier )
{
Object result = doSomethingImmediately( argument );
callback.receiveResult( result, uniqueIdentifier );
}
}
public class Client implements Callback
{
public static long uniqueIdentifier = 0;
public void receiveResult( Object result, long uniqueIdentifier )
{
System.out.println("received results of invocation " +
uniqueIdentifier + ": " + result;
}
public void main( String[] args )
{
AsyncService service = new AsyncService();
for( int i = 0; i < args.length; i++ )
{
String arg = args[i];
long id = uniqueIdentifier++;
Invocation inv = new Invocation( service, null,
service.getClass().getMethod("doSomething",
new Class[] { String.class, Callback.class, Long.class } ),
new Object[] { args[i], this, id } );
service.handle( inv );
}
}
}
author: Leo Simons version: $Id: AsyncEnabled.java,v 1.1 2004/03/23 13:37:56 lsimons Exp $ |