Channels ▼

Eric Bruno

Dr. Dobb's Bloggers

Asynchronous Java Calls, Part 1

July 03, 2012

When called, the Java VM provides the actual proxy object that invoked the call, the method object, and an array of the arguments to the call. We use this to locate the interface of the object for which the method call is made, and the method name itself. This information, along with the arguments and the callback we received earlier, is used in a call to the AsyncHelper.call() static method, which does the following:


    protected static void call(String intName, String method, 
                                                Object[] params, AsyncCallback requestor) {
        try {
            Object[] paramsWithMethod = new Object[params.length + 1];
            paramsWithMethod[0] = method;
            System.arraycopy(params, 0, paramsWithMethod, 1, params.length);
            jms.sendMessage(intName, paramsWithMethod, requestor);
        }
        catch ( Exception e ) { }
    }

Here, the code repackages the arguments to include the method name as the first parameter, and then sends a message over JMS with this data using a helper class named JMSHelper (more on that later). The interface name is used as the queue name over which the message is sent. On the other side, there's an asynchronous listener on the same queue that will invoke the real object's method with the parameters provided. The return value is routed back to the caller's callback using a JMS replyTo destination, provided within the actual message. Let's look at how all this works now.

The Object Side: AsyncObject.java

To support asynchronous method calls with this framework, you extend the AsyncObject base class; no other changes are required. This class does all of the work to do the following:

  • Create a queue per interface that your object implements
  • Listen on those interfaces for messages
  • Call the appropriate method on your object when a message arrives

This is done through two methods: the onMessage() method, as defined by JMS' MessageListener interface; and handleCall(), which uses reflection to take the method name and parameters from the JMS message and invoke the actual method on your object. Because Java Reflection is so thorough, this code can be used to call any method on any object whether the parameters are primitives or other Java objects.

The MessageListener.onMessage() method is called by the JMS provider when a message arrives. Our implementation of this method looks like this:


  public abstract class AsyncObject implements MessageListener, IAsyncObject {  
    public void onMessage(Message msg) {
        try {
            Request req = new Request( (Object[])msg.getObjectProperty("params") );
            Destination reply = msg.getJMSReplyTo();
            Object ret = this.handleCall(req);
            if ( ret != null ) {
                // send return back to caller
                jms.sendMessage(ret, (String)req.params[0], reply);
            }
        }
        catch ( Exception e ) { … }
    }
    …
  }

First, the parameters and method name are pulled from the array and placed within an object called Request. Next, the JMS replyTo destination, to route the return value through, is retrieved. Then, the object's method is invoked, and finally the return value is sent back to the caller over JMS. The handleCall method uses Reflection to make the call:

    public Object handleCall(Request req) {
        Object paramsWithMethod[] = req.getParams();
        
        // Use reflection to create a proxy object to call real object
        String methodName = (String)paramsWithMethod[0];
        
        Object[] params = new Object[paramsWithMethod.length - 1];
        Class[] paramTypes = new Class[paramsWithMethod.length - 1];
        System.arraycopy(paramsWithMethod, 1, params, 0, params.length);
        int counter = 0;
        for ( Object param: params ) {
            paramTypes[counter++] = param.getClass();
        }

        try {
            Method method = this.getClass().getMethod(methodName, paramTypes);
            Object ret = method.invoke(this, params);
            return ret;
        }
        catch ( Exception e ) { }
        return null;
    }

Here's how the code works:

  1. The method parameters and method name are pulled from the Request object.
  2. An array whose entries represent the actual type of each parameter is created. A loop calls the getClass() method for each parameter object to get this information.
  3. Using Reflection, the code retrieves the Class object for the object to be called, and gets a reference to the method to be called using that method's name and the array of parameter types.
  4. The actual method is called by calling invoke on Reflection's Method object retrieved in the previous step.

Using the Asynchronous Java Framework

All of this code is packaged up in a JAR file to be included and used in your own project. As discussed early on, the only change required on the caller side is to use the AsyncHelper class to get a reference to the object you want to call asynchronously, which is actually a proxy object that uses JMS to invoke the real object's methods. You do this instead of creating an instance of the object using the new keyword; a reasonable change in my opinion. On the object's side, all you need to do is extend the abstract base class, AsyncObject, which uses introspection to route JMS messages to method calls. Another reasonable, and very transparent, requirement.

You can download the Asynchronous Java Framework here, along with a sample application here. In the next blog, we'll take a look at how JMS is used and implemented in the JMSHelper class to make all of this work, and how you can use it to spread the asynchronous calls over multiple JVMs and the network.

Happy Coding!
-EJB

Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 


Video