This page describes MDB (Message Driven Bean) configuration with Wildfly (> version 8 implementing Java EE 7/8) and Websphere MQ messaging broker.
Websphere MQ provides a resource adapter which is able to interact with latest and even legacy Websphere MQ versions. (See this link for WMQ v8.0 Resource Adapter compatibility details)
It implements Java Connector Architecture (JCA) Java EE specification, allowing to interact with it from a Java EE application server.
Finally it allows Java EE 7 applications using JMS 2.0 specification to interact with legacy WMQ JMS 1.1 providers in an implicit way.
Bellow is a sample of MDB
package org.rembx.sample.jee;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.ejb.MessageDrivenContext;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
/**
* User: bantos
*/
@MessageDriven(activationConfig = {
@ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
@ActivationConfigProperty(propertyName = "useJNDI", propertyValue = "true"),
@ActivationConfigProperty(propertyName = "destination", propertyValue = "java:/jboss/test"),
@ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge")})
public class TestMDB implements MessageListener {
@Override
public void onMessage(Message message) {
if (message instanceof TextMessage) {
try {
System.out.println(((TextMessage) message).getText());
} catch (JMSException e) {
// propagate for transaction rollback
throw new IllegalArgumentException(e);
}
}
}
}
Note that ActivationSpec annotations used here do not depend on environment. (See following section)
WebsphereMQ resource adapter does not allow to use standalone.xml admin-objects for destination configuration (admin-objects can only be used by messages producers).
Thus we need to configure the whole destination parameters such as JMS provider host, port, (...), in MDB configuration.
Configuring such parameters with MDB ActivationSpec annotations would not help to maintain contextual configuration (related to environment).
This custom deployment descriptor (See this link for details) allows to declare and maintain properly contextual MDB configuration related to destination by using system properties.
<?xml version="1.0" encoding="UTF-8"?>
<jboss:ejb-jar xmlns:jboss="http://www.jboss.com/xml/ns/javaee"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.jboss.com/xml/ns/javaee http://www.jboss.org/j2ee/schema/jboss-ejb3-2_0.xsd
http://java.sun.com/xml/ns/javaee http://www.jboss.org/j2ee/schema/jboss-ejb3-spec-2_0.xsd"
xmlns:mdb="urn:resource-adapter-binding" version="3.1" impl-version="2.0">
<jboss:enterprise-beans>
<message-driven>
<ejb-name>TestMDB</ejb-name>
<ejb-class>org.rembx.sample.jee.TestMDB</ejb-class>
<transaction-type>Container</transaction-type>
<message-destination-type>javax.jms.Queue</message-destination-type>
<activation-config>
<activation-config-property>
<activation-config-property-name>channel</activation-config-property-name>
<activation-config-property-value>${websphere.channel}</activation-config-property-value>
</activation-config-property>
<activation-config-property>
<activation-config-property-name>queueManager</activation-config-property-name>
<activation-config-property-value>${websphere.queueManager}</activation-config-property-value>
</activation-config-property>
<activation-config-property>
<activation-config-property-name>transportType</activation-config-property-name>
<activation-config-property-value>${websphere.transportType}</activation-config-property-value>
</activation-config-property>
<activation-config-property>
<activation-config-property-name>hostName</activation-config-property-name>
<activation-config-property-value>${websphere.hostName}</activation-config-property-value>
</activation-config-property>
<activation-config-property>
<activation-config-property-name>port</activation-config-property-name>
<activation-config-property-value>${websphere.port}</activation-config-property-value>
</activation-config-property>
</activation-config>
</message-driven>
</jboss:enterprise-beans>
<assembly-descriptor>
<mdb:resource-adapter-binding>
<ejb-name>TestMDB</ejb-name>
<mdb:resource-adapter-name>${websphere.resource.adapter}</mdb:resource-adapter-name>
</mdb:resource-adapter-binding>
</assembly-descriptor>
</jboss:ejb-jar>
The following resource-adapters subsystem configuration has to be added
<subsystem xmlns="urn:jboss:domain:resource-adapters:2.0">
<resource-adapters>
<resource-adapter id="wmq.jmsra.rar">
<archive>
wmq.jmsra.rar
</archive>
<transaction-support>NoTransaction</transaction-support>
<connection-definitions>
<connection-definition class-name="com.ibm.mq.connector.outbound.ManagedConnectionFactoryImpl" jndi-name="java:/jboss/MyConnectionFactory" use-java-context="true" pool-name="mqconn">
<config-property name="hostName">
${websphere.hostName:localhost}
</config-property>
<config-property name="password">
${websphere.password:mqm}
</config-property>
<config-property name="queueManager">
${websphere.queueManager:QUEUE.MANAGER}
</config-property>
<config-property name="port">
${websphere.port:1414}
</config-property>
<config-property name="channel">
${websphere.channel:SYSTEM.AUTO.SVRCONN}
</config-property>
<config-property name="transportType">
${websphere.transportType:CLIENT}
</config-property>
<config-property name="username">
${websphere.username:mqm}
</config-property>
<security>
<application/>
</security>
</connection-definition>
</connection-definitions>
<admin-objects>
<admin-object class-name="com.ibm.mq.connector.outbound.MQQueueProxy" jndi-name="java:/jboss/test" use-java-context="true" pool-name="MQ.QUEUE.NAME">
<config-property name="baseQueueName">
${websphere.queueName:Q_QUEUE}
</config-property>
</admin-object>
</admin-objects>
</resource-adapter>
</resource-adapters>
</subsystem>
Then, destination contextual configuration is defined through syste-properties. (These properties are referenced by WMQ resource-adapter configuration (see above) and jboss-ejb3.xml MDB configuration)
<system-properties>
<property name="websphere.hostName" value="< WMQ HOST >"/>
<property name="websphere.port" value="< WMQ PORT >"/>
<property name="websphere.channel" value="< WMQ CHANNEL >"/>
<property name="websphere.transportType" value="< WMQ TRANSPORT TYPE >"/>
<property name="websphere.queueManager" value="< WMQ HQUEUE MANAGER >"/>
<property name="websphere.queueName" value="< WMQ QUEUE NAME >"/>
<property name="websphere.resource.adapter" value="< WMQ RESOURCE ADAPTER ARCHIVE NAME (such as wmq.jmsra.rar) >"/>
</system-properties>
The following EJB pushes messages to configured test destination :
package org.rembx.sample.jee;
import javax.annotation.Resource;
import javax.ejb.Stateless;
import javax.jms.*;
import java.util.UUID;
@Stateless
public class ResourceSample {
@Resource(mappedName = "java:/jboss/MyConnectionFactory")
ConnectionFactory factory;
@Resource(mappedName = "java:/jboss/test")
Queue queue;
public void pushMessage() throws JMSException {
try (JMSContext context = connectionFactory.createContext()) {
context.createProducer().send(queue, "Hello World !");
}
}
}
@mtrs thanks, indeed. @JMSConnectionFactory("java:/jboss/MyConnectionFactory") would have to be added too in order to reference the factory declared in WMQ resource adapter configuration/