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