Developing MDB with WildFly / JBoss EAP

MDB are a type of Enterprise Beans that are able to asynchronously deliver messages sent by any JMS producer. In this tutorial we wil learn how to develop and deploy a Message Driven Bean on the top of WildFly Application Server or JBoss EAP.

Starting the broker and the destinations

This article is a walk through the configuration and development of Message Driven Beans on WildFly application server. If you want a general introduction to MDBs, we recommend checking this article: Message Driven Bean example

Firstly, in order to use Message Driven Beans, you need to start the application server with a configuration which includes the embedded Artemis MQ Server, such as the standalone-full.xml

$ ./standalone.sh -c standalone-full.xml

In order to deploy an MDB, we will create from the Command Line Interface a JMS Destination that will be used in our MDB:

jms-queue add --queue-address= ExampleQueue --entries=queue/exampleQueue,java:/jboss/exported/jms/queue/exampleQueue

Next, let’s deploy a sample MDB. Here’s a very simple one which does a simple job of printing the text message received:

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 = "destinationLookup", propertyValue = "java:/queue/exampleQueue"),
		@ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"), })
public class MDBSample implements MessageListener {

	public void onMessage(Message message) {
		try {
			TextMessage tm = (TextMessage) message;
			System.out.println("Message received  : " + tm.getText());
		} catch (JMSException ex) {
			ex.printStackTrace();
		}
	}

}

This MDB consumes messages from the queue exampleQueue. Ok. Now all you need is a JMS sender. The following CDI Bean connects to the JMS Queue and sends a JMS message:

import javax.faces.application.FacesMessage;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.annotation.Resource;
import javax.ejb.Stateless;
import javax.enterprise.inject.Model;
import javax.inject.Inject;
import javax.jms.ConnectionFactory;
import javax.jms.DeliveryMode;
import javax.jms.JMSConnectionFactory;
import javax.jms.JMSContext;
import javax.jms.JMSSessionMode;
import javax.jms.Queue;

@Model
public class MessageSender {
    String message;

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    @Inject
    JMSContext context;

    @Resource(mappedName = "java:/queue/exampleQueue")
    private Queue queue;

    public void sendMessage() {
        context.createProducer().send(queue, message);
        printMessage("Sent message " + message);
    }

    private void printMessage(String string) {
        FacesContext.getCurrentInstance().addMessage(null,
                new FacesMessage(FacesMessage.SEVERITY_INFO, string, string));

    }
     
}

As you can see, in this example we are using the JMS Context, which is an interface that can be used to combine in a single object the functionalities of a Connection and a Session.

The JMS Context uses the default JMS ConnectionFactory, which is bound at “java:/ConnectionFactory“. You can however specify to use a non-default connection factory by including the @JMSConnectionFactory annotation that needs as argument its JNDI binding:

@Inject
@JMSConnectionFactory("java:/JmsXA")
JMSContext context;

Defining JMS Destinations Programmatically

Please note that you can use the javax.jms.JMSDestinationDefinition annotation to define JMS Destination Programmatically. This can be used for testing/development purposes. Mind it, JMS Destinations which are not created through the Management channel (such as the CLI), cannot be managed from the CLI. Therefore, it’s not adviced to use them in production. Here is an example of two @JMSDestinationDefinition to create a JMS Queue and a JMS Topic:

@JMSDestinationDefinitions(
    value = {
        @JMSDestinationDefinition(
            name = "java:/queue/HELLOWORLDMDBQueue",
            interfaceName = "javax.jms.Queue",
            destinationName = "HelloWorldMDBQueue"
        ),
        @JMSDestinationDefinition(
            name = "java:/topic/HELLOWORLDMDBTopic",
            interfaceName = "javax.jms.Topic",
            destinationName = "HelloWorldMDBTopic"
        )
    }
)

Using Property Substitution in MDBs

You can use System Properties instead of hardcoding the JMS Destination. For example, the above snippet can also be coded as follows

@JMSDestinationDefinitions(
    value = {
        @JMSDestinationDefinition(
            name = "java:/${property.helloworldmdb.queue}",
            interfaceName = "javax.jms.Queue",
            destinationName = "HelloWorldMDBQueue"
        ),
        @JMSDestinationDefinition(
            name = "java:/${property.helloworldmdb.topic}",
            interfaceName = "javax.jms.Topic",
            destinationName = "HelloWorldMDBTopic"
        )
    }
)

Then, you will need to execute the following command to enable property substituion in your code and set the required properties:

# Configure the ee subsystem to enable MDB annotation property substitution
/subsystem=ee:write-attribute(name=annotation-property-replacement,value=true)

# Define system properties to be used in the substititution
/system-property=property.helloworldmdb.queue:add(value=java:/queue/HELLOWORLDMDBPropQueue)
/system-property=property.helloworldmdb.topic:add(value=java:/topic/HELLOWORLDMDBPropTopic)

Source code

The source code for this tutorial is taken from the book Practical Enterprise Development and it’s available here: https://github.com/fmarchioni/practical-enterprise-development/tree/master/code/jms/ee-jms-basic