Featured Post

Applying Email Validation to a JavaFX TextField Using Binding

This example uses the same controller as in a previous post but adds a use case to support email validation.  A Commons Validator object is ...

Sunday, February 8, 2015

Geronimo Message Driven Beans (MDB) in Simple EJB Packaging

This post describes how to package a Java EE Message Driven Bean (MDB) as an EJB JAR file and deploy it to a Apache Geronimo 3.0 app server.

If you have long-running processes or workflows that can't be chained together in a single synchronous call, Java Messaging can provide you with the breathing room to allow software components to work at their own pace.  Messaging can keep your UI responsive while your server-side code handles a backlog of work items.  In Java EE, server-side messaging is performed with a special class of Enterprise Java Beans (EJB) called Message Driven Beans.

The MDB in this post provides a callback which is invoked when a message hits a queue.  This decouples the MDB (a server-side component) from the caller, likely the UI.  For example, the UI enqueues a message and returns immediately.  Because messaging is reliable, backed up with a datastore and terms of delivery, the UI can optimistically assume that the operation is being handled.  (A later post will discuss reporting status back to the UI from the server-side.)


The Resources

Like database connections, message queues are container-defined resources.  This screen shows how I've defined TestInboundQueue through the Apache Geronimo console.  It has been defined in its own resource group along with a TestOutboundQueue (needed for a later post).

TestInboundQueue Part of the TestResourceGroup as Defined in the Console
This Console panel is also used to test the MDB.  Press "Send" link in the TestInboundQueue row, enter a Correlation ID and text contents.  You'll see the MDB output in the geronimo.out log under $GERONIMO_HOME/var/log folder.  "Browse" can be used to test the client-side enqueuing if the MDB isn't deployed and consuming the messages.


Java Code

This .java file defines the MDB with the @MessageDriven annotation.  A reference is made to TestInboundQueue in the @ActivationConfigProperty.  The MessageListener interface is implemented which requires the onMessage() callback.

package se;

import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;

@MessageDriven(activationConfig = {
        @ActivationConfigProperty(propertyName="destinationType", propertyValue="javax.jms.Queue"),
        @ActivationConfigProperty(propertyName="destination", propertyValue="TestInboundQueue")
})
public class MessageReceiverBean implements MessageListener {

    @Override
    public void onMessage(Message message) {

        TextMessage textMessage = (TextMessage) message;
        try {
            System.out.println("Order Received \n"+ textMessage.getText());
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
}

onMessage() will receive a Message.  My simple implementation casts this to a TextMessage and other implementations may cast it to an Object, Map, or a chunk of Bytes.  The message exchange protocol is defined out-of-band between the enqueue-ers (in my case UI code) and the dequeue-ers (the MDB).


Container-Specific Configuration

This post is based on the Geronimo 3.0 App Server.  Messaging, although a requirement of all Java EE app servers, can be very non-standard in its configuration.  openejb-jar.xml is a config file for the Apache Geronimo App Server and not JBoss, WebSphere, WebLogic, or another other app server.

This openejb-jar.xml sets the TestResourceGroup which was defined in the Console as a dependency. I then establish a link between the MDB and the container resource with the resource-link element.

<openejb-jar
        version="1.0">

    <dep:environment>
        <dep:moduleId>
            <dep:groupId>com.bekwam</dep:groupId>
            <dep:artifactId>SimpleEJBProject</dep:artifactId>
            <dep:version>1.0</dep:version>
            <dep:type>car</dep:type>
        </dep:moduleId>

        <dep:dependencies>
            <dep:dependency>
                <dep:groupId>console.jms</dep:groupId>
                <dep:artifactId>TestResourceGroup</dep:artifactId>
                <dep:version>1.0</dep:version>
                <dep:type>car</dep:type>
            </dep:dependency>
        </dep:dependencies>

    </dep:environment>

    <openejb:enterprise-beans>
    <openejb:message-driven>
    <openejb:ejb-name>MessageReceiverBean</openejb:ejb-name>
    <naming:resource-adapter>
        <naming:resource-link>
            TestResourceGroup
        </naming:resource-link>
    </naming:resource-adapter>
    </openejb:message-driven>
    </openejb:enterprise-beans>
</openejb-jar>


Packaging

The .java code is compiled and added to a JAR file, simpleejb.jar.  There is a /META-INF folder that holds the openejb-jar.xml.  The contents of the JAR file are as follows

simpleejb.jar
-- /se
-- /se/MessageReceiverBean.class
-- /META-INF
-- /META-INF/openejb-jar.xml


Deployment

simpleejb.jar gets copied to the geronimo deploy folder where it is hot-deployed.  Check the log file to make sure that it's deployed correctly.  If you get an error, verify the groupId and artifactId of the resource group you're using for the queues.  Note that "console.jms" won't be appropriate if you're using the stock resource group.

Because of the non-standard nature of messaging, it's sometimes skipped over in applications.  This gives rise to inefficient polling mechanisms or unstable uses of java.lang.Thread.  This blog post shows you how you can set up a robust asynchronous infrastructure with a minimum of code and configuration: 1 Java class, 1 definition in the Console, and 1 config file linking the two.






No comments:

Post a Comment