If you just need to send some JMS messages to WSMQ from a bean deployed in JBoss, and you do not need any kind of XA transaction support, then you do not need to go through this readme. Use the WSMQ JMS interface in your bean code as any other Java API and you are done.
But if you need any XA transaction support or if you need to deploy, in JBoss, a Message Driven Bean listening to a WSMQ destination, then you might find this readme useful.
Thank you to everyone's help in the JBoss forum (http://www.jboss.org) as well as in the JBoss mailing lists (http://sourceforge.net/projects/jboss). A special thank to Hiram from Core Developers Network for the details on XA and the JBoss MDB.
Beside JBoss and WebSphere MQ, you will also need a JDK (http://www.javasoft.com) and Ant (http://www.apache.org) installed and properly configured in order to build and run the sample code. The sample was developed using Ant 1.5.3 and SUN JDK 1.4.1.
The current status of this sample is as follow:
The Extended Transactional Clients enables XA support through the JMS interface using the "client protocol". Internally, WSMQ has multiple protocols. The client protocol allows you to access a remote WSMQ server (also called queue manager) through regular TCP/IP. In this case, the WSMQ client is 100% java code. Before the Extended Transactional Clients, in order to enable XA support through the JMS interface, the "binding" protocol was required. WSMQ documentation specifies that this is the prefered connection mode (and the best for performance), but it has two drawbacks from my point of view:
If you do need any kind of XA support, you also do not need to worry about all that. Regular WSMQ 5.3.0 or 5.2.0 with the Java JMS interface is enough.
RecursiveSearch
is set to True
:
<attribute name="RecursiveSearch">True</attribute>
com.ibm.mq.jar
com.ibm.mqetclient.jar
com.ibm.mqjms.jar
com.ibm.mqbind.jar
com.ibm.mqetclient.jar
is a jar installed as part of the WebSphere MQ Extended
Transactional Clients. It is only required for WSMQ XA support.
mbean
element name
attibute, the ProviderName
and the
QueueFactoryRef
and TopicFactoryRef
):
<!-- The WSMQ JMS provider loader -->
<mbean code="org.jboss.jms.jndi.JMSProviderLoader"
name="jboss.mq:service=JMSProviderLoader,name=WSMQJMSProvider">
<attribute name="ProviderName">WSMQJMSProvider</attribute>
<attribute name="ProviderAdapterClass">org.jboss.jms.jndi.JBossMQProvider</attribute>
<attribute name="QueueFactoryRef">WSMQQueueConnectionFactory</attribute>
<attribute name="TopicFactoryRef">WSMQTopicConnectionFactory</attribute>
</mbean>
ProviderName
attribute is important as this is the way the MDB configuration
will specify this JMS provider.
The QueueFactoryRef
and TopicFactoryRef
are also important and
must match the JNDI names of the WSMQ connection factory object that you create and register
in JNDI (see below).
WSMQ Message Driven Bean
configuration. It will allow the deployment
of MDBs listening to WSMQ destinations.
Edit the JBOSS_HOME/server/default/conf/standardjboss.xml, add the following lines in
the container-configurations
(copy/paste the Standard Message Driven
Bean
configuration and update the container-name
and
invoker-proxy-binding-name
):
<container-configuration>
<container-name>WSMQ Message Driven Bean</container-name>
<call-logging>false</call-logging>
<invoker-proxy-binding-name>wsmq-message-driven-bean</invoker-proxy-binding-name>
<container-interceptors>
<interceptor>org.jboss.ejb.plugins.ProxyFactoryFinderInterceptor</interceptor>
<interceptor>org.jboss.ejb.plugins.LogInterceptor</interceptor>
<interceptor>org.jboss.ejb.plugins.RunAsSecurityInterceptor</interceptor>
<!-- CMT -->
<interceptor transaction="Container">org.jboss.ejb.plugins.TxInterceptorCMT</interceptor>
<interceptor transaction="Container" metricsEnabled="true">org.jboss.ejb.plugins.MetricsInterceptor</interceptor>
<interceptor transaction="Container">org.jboss.ejb.plugins.MessageDrivenInstanceInterceptor</interceptor>
<!-- BMT -->
<interceptor transaction="Bean">org.jboss.ejb.plugins.MessageDrivenInstanceInterceptor</interceptor>
<interceptor transaction="Bean">org.jboss.ejb.plugins.MessageDrivenTxInterceptorBMT</interceptor>
<interceptor transaction="Bean" metricsEnabled="true">org.jboss.ejb.plugins.MetricsInterceptor</interceptor>
<interceptor>org.jboss.resource.connectionmanager.CachedConnectionInterceptor</interceptor>
</container-interceptors>
<instance-pool>org.jboss.ejb.plugins.MessageDrivenInstancePool</instance-pool>
<instance-cache></instance-cache>
<persistence-manager></persistence-manager>
<container-pool-conf>
<MaximumSize>100</MaximumSize>
</container-pool-conf>
</container-configuration>
container-name
is the name that will be used in the MDB deployment
descriptor.
You also need to add the following lines in the invoker-proxy-bindings
(copy/paste
the message-driven-bean
invoker-proxy-binding
and update the
name
and JMSProviderAdapterJNDI
):
<invoker-proxy-binding>
<name>wsmq-message-driven-bean</name>
<invoker-mbean>default</invoker-mbean>
<proxy-factory>org.jboss.ejb.plugins.jms.JMSContainerInvoker</proxy-factory>
<proxy-factory-config>
<JMSProviderAdapterJNDI>WSMQJMSProvider</JMSProviderAdapterJNDI>
<ServerSessionPoolFactoryJNDI>StdJMSPool</ServerSessionPoolFactoryJNDI>
<MaximumSize>15</MaximumSize>
<MaxMessages>1</MaxMessages>
<MDBConfig>
<ReconnectIntervalSec>10</ReconnectIntervalSec>
<DLQConfig>
<DestinationQueue>queue/DLQ</DestinationQueue>
<MaxTimesRedelivered>10</MaxTimesRedelivered>
<TimeToLive>0</TimeToLive>
</DLQConfig>
</MDBConfig>
</proxy-factory-config>
</invoker-proxy-binding>
name
must match the invoker-proxy-binding-name
of the
WSMQ Message Driven Bean
configuration created previously.
The JMSProviderAdapterJNDI
must match the ProviderName
created
previously in the JBOSS_HOME/server/default/deploy/jms/jms-ds.xml file.
For this sample, we will create some custom MBeans that, once deployed in JBoss, will create
the WSMQ JMS objects and add them to JBoss JNDI space automatically.
With this option, it is very easy to add new objects (just adding a couple of lines to an xml
file and redeploy), and each time JBoss is started those objects are created and available.
The custom MBean code is under the jmx
package in the sources (src directory)
of this sample. It will be packaged in a sar
file and deployed in JBoss.
The jboss-service.xml file containing the WSMQ JMS object definitions is in the
resources folder.
To build and deploy the custom MBeans, perform the following steps:
QueueManagerName
, HostName
and
Channel
must match your WSMQ installation and configuration.
TransportType is the attribute you can tweak to use the different WSMQ transport (client,
binding or direct).
QueueManagerName
and DestinationName
must match your WSMQ configuration. This sample is using two different WSMQ queues
(RequestQueue and ResponseQueue) that must be created in your WSMQ queue
manager (you can also use some existing ones, you just need to update the
DestinationName
attribute accordingly).
jboss.dist
property to point to your
JBOSS_HOME.
ant mb-build
ant mb-deploy
service=JNDIView
service and invoking the list
or
listXML
MBean operation. In the returned list, you should see the WSMQ objects
you just deployed.
ant client-build
wsmq-tests
target to check that the custom MBeans are
correctly deployed and configured.
Make sure that the following properties near the top of the build.xml are properly
configured according to the resources/jboss-service.xml:
<property name="wsmq.factory" value="WSMQQueueConnectionFactory"/>
<property name="wsmq.request.destination" value="wsmq/RequestQueue"/>
<property name="wsmq.response.destination" value="wsmq/ResponseQueue"/>
ant wsmq-tests
WSMQ Message Driven Bean
listening to the
wsmq/RequestQueue
WSMQ Queue.
Those values must be consistent between the resources/jboss.xml,
resources/jboss-service.xml and the
JBOSS_HOME/server/default/conf/standardjboss.xml (see configuration steps above).
It also assumes that the connection factory associated to the
WSMQ Message Driven Bean
is not an XA one (see the QueueFactoryRef
in JBOSS_HOME/server/default/deploy/jms/jms-ds.xml which must match a WSMQ connection
factory in resources/jboss-service.xml for which IsXa
is false).
wsmq/ResponseQueue
using the WSMQQueueConnectionFactory
factory.
Those values must be consistent between the resources/jboss-service.xml and the
resources/ejb-jar.xml.
ant mdb-build
ant mdb-deploy
<property name="wsmq.factory" value="WSMQQueueConnectionFactory"/>
<property name="wsmq.request.destination" value="wsmq/RequestQueue"/>
<property name="wsmq.response.destination" value="wsmq/ResponseQueue"/>
ant wsmq2wsmq
wsmq2wsmq
.
ResponseQueue
used for the response message.
Edit the JBOSS_HOME/server/default/deploy/jms/jbossmq-destinations-service.xml and
add the following lines at the end of the file (just before the closing
<server>
element):
<mbean code="org.jboss.mq.server.jmx.Queue"
name="jboss.mq.destination:service=Queue,name=ResponseQueue">
<depends optional-attribute-name="DestinationManager">
jboss.mq:service=DestinationManager</depends>
</mbean>
env-entry-value
for the mdb/responseFactory
to
ConnectionFactory
which is the default JBoss MQ connection factory.
Change the env-entry-value
for the mdb/responseDestination
to
queue/ResponseQueue
which is the JBoss queue you created in the previous step.
ant mdb-build
ant mdb-deploy
<property name="jboss.factory" value="ConnectionFactory"/>
<property name="jboss.response.destination" value="queue/ResponseQueue"/>
ant wsmq2jboss
wsmq2jboss
.
env-entry-value
for the
mdb/responseFactory
to java:/JmsXA
(the JBoss JMS XA connection
factory), change the env-entry-value
for the mdb/responseTransacted
to true
, rebuild (ant mdb-build
), redeploy (ant mdb-deploy
)
and run the wsmq2jboss
client again. You will see that the output does not contain
any message duplicates anymore, even if some aborts are generated on the MDB side.
The client application is waiting for a fixed amount of seconds for messages to be delivered.
It might exit before all messages have been redelivered by the MDB in case multiple aborts are
generated. If you re-run the command, you should see the missing messages of the previous
run right away and then the new messages sent.
The details of the JBoss MDB XA internals came from Hiram from Core Developers Network (through the JBoss mailing list). Hiram, thanks again for those details. Hiram also provided some information on why a WSMQ MDB is not working with XA. Here are the details from his email:
The JMS ASF spec is a little weak in describing who/how the inbound XA transaction gets started. The only way JBossMQ was able to implement the ASF spec was by starting the transaction without an associated XID. Once the message gets delivered to the container, JBossMQ allows the container to associate an XID with the session. The session then assigns the XID to the TX that delivered the message. JBossMQ had to kind of jump through hoops to get this to work.
It would have been much easier to allow JBossMQ to assign an XID to the transaction to begin with. WebSphere MQ might be trying to do something like that. But since it's not in the JMS ASF spec, IBM would need to tell us what MQ is expecting (outside the spec) to get the asynch message delivery to work.
Other ways to skin this cat:
RequestQueue
used for the response message.
Edit the JBOSS_HOME/server/default/deploy/jms/jbossmq-destinations-service.xml and
add the following lines at the end of the file (just before the closing
<server>
element):
<mbean code="org.jboss.mq.server.jmx.Queue"
name="jboss.mq.destination:service=Queue,name=RequestQueue">
<depends optional-attribute-name="DestinationManager">
jboss.mq:service=DestinationManager</depends>
</mbean>
env-entry-value
for the mdb/responseFactory
to
WSMQQueueConnectionFactory
(matching the non-XA WSMQ connection factory defined
in the resources/jboss-service.xml.
Change the env-entry-value
for the mdb/responseDestination
to
wsmq/ResponseQueue
(matching the response queue defined in the
resources/jboss-service.xml.
Change the env-entry-value
for the mdb/responseTransacted
to
false
.
configuration-name
to Standard Message Driven Bean
in order to have the MDB using the JBoss JMS provider.
Change the destination-jndi-name
to queue/RequestQueue
(matching
the queue you created in the first step).
Set the xa-connection
to true
(or comment the line out) as
the JBoss connection factory associated with the Standard Message Driven Bean coonfiguration
is an XA one.
ant mdb-build
ant mdb-deploy
<property name="jboss.factory" value="ConnectionFactory"/>
<property name="jboss.request.destination" value="queue/RequestQueue"/>
ant jboss2wsmq
jboss2wsmq
.
QueueFactoryRef
to be the name of the XA connection factory configured in
the resources/jboss-service.xml (a connection factory for which IsXA
is set to true
):
<!-- The WSMQ JMS provider loader -->
<mbean code="org.jboss.jms.jndi.JMSProviderLoader"
name="jboss.mq:service=JMSProviderLoader,name=WSMQJMSProvider">
<attribute name="ProviderName">WSMQJMSProvider</attribute>
<attribute name="ProviderAdapterClass">org.jboss.jms.jndi.JBossMQProvider</attribute>
<attribute name="QueueFactoryRef">WSMQXAQueueConnectionFactory</attribute>
<attribute name="TopicFactoryRef">WSMQXATopicConnectionFactory</attribute>
</mbean>
connection-factories
element:
<tx-connection-factory>
<jndi-name>WSMQJmsXA</jndi-name>
<xa-transaction/>
<adapter-display-name>JMS Adapter</adapter-display-name>
<config-property name="JmsProviderAdapterJNDI" type="java.lang.String">java:WSMQJMSProvider</config-property>
<config-property name="SessionDefaultType" type="java.lang.String">javax.jms.Topic</config-property>
</tx-connection-factory>
env-entry-value
for the mdb/responseFactory
to
java:/WSMQJmsXA
(matching the transaction connection factory created in the
previous step).
Make sure that the env-entry-value
for the mdb/responseDestination
is set to wsmq/ResponseQueue
(matching the response queue defined in the
resources/jboss-service.xml.
Change the env-entry-value
for the mdb/responseTransacted
to
true
.
ant mdb-build
ant mdb-deploy
ant jboss2wsmq
jboss2wsmq
.
RecursiveSearch
in JBOSS_HOME/server/default/conf/jboss-service.xml
does not exist, no need to worry about it.
WSMQ Message Driven Bean
configuration in the
JBOSS_HOME/server/default/conf/standardjboss.xml, the separate
invoker-proxy-bindings
does not exist. Everything is bundled under the
the container-configurations
. Copy/paste the Standard Message
Driven Bean
configuration and update the container-name
and
the JMSProviderAdapterJNDI
.
wsmq/
JNDI context. With JBoss 3.0.7 it does not work if the context
is not the same as the JBoss queues one, meaning queue/
.
It also means that the JNDI name of the WSMQ queues (or the JBoss ones) must be renamed
as in both cases they are called RequestQueue
and ResponseQueue
.
The JNDI context was used to separate them. In JBoss 3.0.7, the JNDI context is the same
in both cases, meaning that the queue names must be different.
It involves some updates in the resources/jboss-service.xml, resources/jboss.xml,
resources/ejb-jar.xml and build.xml files.
name=jmsra
to something like
name=wsmqjmsra
.
properties
of the ManagedConnectionFactoryProperties
attribute, insert the following config-property
:
<config-property>
<config-property-name>JmsProviderAdapterJNDI</config-property-name>
<config-property-type>java.lang.String</config-property-type>
<config-property-value>java:WSMQJMSProvider</config-property-value>
</config-property>
JndiName
attribute to WSMQJmsXA
.
Criteria
attribute to ByNothing
.
SecurityDomainJndiName
attribute and the
associated dependency to the JaasSecurityManagerService
service.