Channels ▼

Eric Bruno

Dr. Dobb's Bloggers

Asynchronous Java Calls, Part 2

July 17, 2012

In my previous blog, I presented a framework that almost transparently turns Java method calls into asynchronous calls, much like JavaScript code works in the Ajax paradigm, or that of Node.js. It accomplishes all of this with Java Reflection, and JMS as the asynchronous transport. In summary, Reflection is used to transform method calls into messages that are sent asynchronously. In this blog, we'll explore the JMS portion of the framework, and the helper class that makes it easy, JMSHelper.java.

Asynchronous Objects

To support asynchronous method calls for any object, simply extend the abstract base class, AsyncObject.java. In that class's constructor, a call is made to the method JMSHelper.listen(), with the object's this pointer passed as a parameter. Before this method is invoked, however, the JMS machinery is put in place in the JMSHelper constructor:


    public JMSHelper() {
        try {
            // Setup JMS stuff here
            Hashtable env = new Hashtable();
            env.put("java.naming.factory.initial",
                    "com.allure.JetStream.jndi.JetStreamInitialContextFactory");
            ctx = new InitialContext(env);
            ConnectionFactory factory = (ConnectionFactory)ctx.lookup("ConnectionFactory");
            connection = factory.createConnection();
            session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
            connection.start();
        }
        catch ( Exception e ) { }
    }

JNDI is used to create a JMS ConnectionFactory object, from which a Connection is established with the JMS provider. A call to start() is all it takes to get the connection ready to send and receive messages.

The JMSHelper.listen() method uses reflection to discover all of the interfaces the object implements, and then creates and listens on a queue per interface. The full package name of each interface is used to avoid name collisions. Here is the code:


    public void listen(Object listener) {
        try {
            // Use the class's interfaces, and create a queue per interface
            Class cls = listener.getClass();
            Class[] interfaces = cls.getInterfaces();
            for ( Class inface: interfaces ) {
                String queueName = inface.getName();
                Queue queue = null;
                try {
                    queue = (Queue)ctx.lookup(queueName);
                }
                catch (Exception e) { }
                if ( queue == null )
                    queue = session.createQueue( queueName );

                MessageConsumer receiver = session.createConsumer(queue);
                receiver.setMessageListener((MessageListener)listener);
            }
        }
        catch ( Exception e ) { }
    }

Since we covered the Reflection code in the previous blog, let's focus on the JMS parts now. After retrieving the array of interfaces from the Java runtime, a JMS Queue is looked for (to see if it already exists) or created for each interface, along with a JMS MessageConsumer to receive messages for the specific queue. A reference to the AsyncObject base class, which implements the JMS MessageListener interface, is passed as the message listener. When messages arrive for each queue, the appropriate Java object's onMessage() is called with the message itself. Since this is implemented in the abstract based class, AsyncObject, there's no need to write this code in your own objects.

The onMessage() method converts the JMS message to a method call, which was examined in detail on the previous blog.

Sending Return Values

When a message is received from the caller, JMSHelper wraps the call into a JMS Message object, using a JMS TemporaryQueue as the "Reply To" destination. This queue has no real name, and is only associated with the delivered message to serve as a direct path back to the sender, implementing the request-reply pattern. This temporary queue is created, a listener is provided, and it then is set as the replyTo field for the message as shown here:


            Queue replyTo = session.createTemporaryQueue();
            MessageConsumer subscriber = session.createConsumer(replyTo);
            ReplyListner replyListener = new ReplyListner(managerName, callback);
            subscriber.setMessageListener(replyListener);
            Message msg = session.createTextMessage("ManagerMessage");
            msg.setObjectProperty("params", params);
            msg.setJMSReplyTo(replyTo);

Recall that all JMS messages in the AsyncJava package contain the method name and parameters in an array, which is placed into the message as the object property, "params". The replyTo TemporaryQueue is created, with the nested listener class ReplyListener, which routes the return message (with the embedded return value) back to the original caller via the method onReturn() on the caller's AsyncCallback interface implementation.

Conclusion

The combination of two powerful features of the Java platform, Reflection and JMS, makes some very useful design patterns and paradigms possible. Here, I've shown how Reflection can be used to abstract Java method calls and turn them into asynchronous calls to more easily perform parallel processing without the need for complicated multi-threaded code. JMS abstracts the delivery and transport used to perform the actual asynchronous message delivery. And thanks to the built-in features and support in both, this implementation works within one JVM, or across multiple JVMs on a LAN or WAN.

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