1
votes

Although documented as tested against Glassfish, use of the IBM native MQ resource adapter (wmq.jmsra.rar) by Message Driven Beans in Glassfish is far from being obvious. There are numerous standard configuration facilities defined by EJB and JCA standards that actually do not work, especially regarding activation specifications.

I know three MQ adapters for glassfish: one is the GenericJMSRA from Oracle: here and here. The second is wmq.jmsra from IBM (installation notes) and available from * IBM Fix Central*. To use it, you must indeed own a MQ licence for the queue manager you will connect to but you can download with a free IBM ID. I am using this second Resource Adapter from IBM because it provides access to native MQ API in java which I need in order to address precisely crafted MQ messages to some pure MQ (non-JMS) destinations.

There exists a third--legacy--alternative: JMSJCA here, once my favorite, but support stopped at MQ V6 in Glassfish 3 and JRE6. My attemps to upgrade the source code to MQ v7.5 (or V8 = JMS2.0) with JRE7 in Glassfish 4 (=JMS2.0) or even down to glassfish 3.1 (=JMS1.1) have been defeated on classloader issues and, once that was solved, class cast exceptions / incompatible connection object versions, etc.

I finally got the IBM RA working in a narrow configuration that I document in the reply post. If anyone found alternate remedies and configurations that work too, notably avoiding some pitfalls evoked below, that will be much welcome.

here is a quick listing of the major problems encountered:

  • admin objects once created yield: RAR8029: Resource [ jms/MyAdminObj ] of type [ aor ] is not enabled: solved here
  • MQException: JMSCMQ0001: ... ('MQRC_HOST_NOT_AVAILABLE') caused by JMSWMQ0018: Failed to connect to queue manager '' with connection mode 'Client' and host name 'localhost(1414)' ... although you can sware it the relevant remote_host-port-channel-queueManager details are well in place in the created connection pools or even as default values in the ra.xml of the RA being deployed
  • Exception during endpoint activation for ra [ jmsra ], activationSpecClass [ com.sun.messaging.jms.ra.ActivationSpec ] ... revealing that your activation specs actually gets routed to the internal JMS Resource adapter and not the MQ Resource adapter as you believe it should
  • WARN j.e.r.r.com.sun.enterprise.connectors.util - RAR8000 : The method setConnectionFactoryLookup is not present in the class : com.sun.messaging.jms.ra.ActivationSpec and WARN j.e.r.r.com.sun.enterprise.connectors.util - RAR8000 : The method setConnectionFactoryLookup is not present in the class : com.sun.messaging.jms.ra.ActivationSpec followed by successful deployment of your Message Driven Bean, possibly making you believe, unless you keep an eye on server logs, that your MDB is now listening to the right destination... but will actually not consume a single message

Finding the places where the relevant documentation is stored was also a messy endeavor. Here is:

Clues on the meaning of all parameters in the wmq.jmsra ra.xml in MQ v7.5 can be found in MQ Websphere 7.5 infocenter > Reference >… Properties of WebSphere MQ classes for JMS objects and use of the resource adapter is documented in MQ Websphere 7.5 infocenter > ... The WebSphere MQ resource adapter but will not work strictly as documented

1

1 Answers

1
votes

Here is the one sequence of steps I presently found to get a pair of respectively input and output message driven beans to work with the IBM MQ wmq.jmsra adapter.

First, be sure to install compatible releases, for instance:

  • GF3 (GlassFish v3.1.2 precisely) with IBM MQ 7.5 Resource adapter and JRE7, altogether supporting JMS1.1. This is the configuration I do refer to in the following.
  • GF4 with MQ 8 RA and JRE7 or JRE8... should work too under JMS2.0, but personally not yet tested

For the outbound side, follow these steps:

  1. Install Glassfish 3 (docs here)
  2. download the IBM MQ 7.5 Resource adapter from IBM fix central, search key == "resource adapter" under "Websphere MQ" product, v7.5 and follow installation procedure here (you get a jar that you must execute with java -jar ...); docs here and ra.xml configuration properties here
  3. start the glassfish web console and under "applications" deploy the IBM RA wmq.jmsra.rar; no need to update any ra.xml configuration properties.
  4. still in the console, create a Resources > Connector > connection pool to whom you can attach the usual set of MQ parameters for a Queue manager like: port 1414, localAddress 10.0.0.66, failIfQuiesce true, hostName bhdev, queueManager QM_BHDEV, channel BRK.SVRCONN. Be sure to select the needed type of factory, e.g. javax.jms.QueueConnectionFactory
  5. then create a Resources > Connector > Resources and give a JNDI name to your just created pool, e.g. jms/QM_BHDEV_QCF

that is enough to try outbound connections. Using EJB3.1 annotations, the following code pieces do the job:

in your class scope:

@Resource(mappedName = "jms/QM_BHDEV_QCF")
private QueueConnectionFactory brokerQCF;

then under a method scope:

QueueConnection queueConn = null; 
QueueSession queueSession = null;
QueueSender sender = null;
String queueUrl = "queue:///TEST.Q4?persistence=-1"; // cf. url-open MQ URL IBM format, or use a plain MQ queue name and set parameters via java API.
try {
  queueConn = brokerQCF.createQueueConnection();
  queueSession = queueConn.createQueueSession(true, queueSession.SESSION_TRANSACTED);
  sender = queueSession.createSender(queueSession.createQueue(queueUrl));
  TextMessage txtMsg = queueSession.createTextMessage();
  txtMsg.setText("helllo world");
  sender.send(txtMsg);
  sender.close();
  queueSession.close();
  queueConn.close();
} catch (JMSException e) {
        // error handling ...
}

On the inbound side, you need an activation spec. The wmq.jmsra connection factories will not allow you to specify the relevant MQ connection pool using annotations (e.g. "connectionFactoryLookup" Activation config property in EJB3.1 specs). You must instead combine a glassfish-ejb-jar.xml deployment descriptor with your EJB annotations. Moreover, I noted that all queue manager definition parameters in the referenced connection pool are ignored and you must explicitly re-specify them either in the Activation config properties (as illustrated below), or in the ejb-jar.xml deployment descriptor. Last but not least, you shall not forget that the needed administered objects (for the MQ destination to listen to) will (mysteriously) not be enabled and a extra step is required to fix this.

Follow these steps:

  1. in glassfish admin console, create a Resources > Connector > Admin object for the destination queue to listen to. Do not much care about properties like the physical queue name which will be overloaded! give it a name, e.g. 'jms/testq1'
  2. glassfish will complain about the 'aor' (admin object resource) not enabled even if you checked the relevant enabled checkbox; moreover, if you inspect the jndi tree, your admin object is not present.
  3. Stop glassfish, and edit the domain.xml for <admin-object-resource enabled="false" ; change to "true" or remove the attribute.
  4. restart glassfish and check the admin object now listed in JNDI (using asadmin list-jndi-entries command for instance)
  5. Associate your MDB (EJB 3.1) project to the glassfish runtime (with may require you to import the OEPE Eclipse plug-in for Glassfish and then declare the glassfish server and runtime target in eclipse), and generate/edit the glassfish-ejb-jar

as follows:

<!DOCTYPE glassfish-ejb-jar PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 EJB 3.1//EN" "http://glassfish.org/dtds/glassfish-ejb-jar_3_1-1.dtd">
<glassfish-ejb-jar>
  <enterprise-beans>
    <ejb>
        <ejb-name>connectorMQin</ejb-name>
        <jndi-name>jms/testq1</jndi-name>
        <mdb-connection-factory>
            <jndi-name>jms/QM_BHDEV_QCF</jndi-name>
        </mdb-connection-factory>
        <mdb-resource-adapter>
            <resource-adapter-mid>wmq.jmsra</resource-adapter-mid>
        </mdb-resource-adapter>
    </ejb>
  </enterprise-beans>
</glassfish-ejb-jar>

You may then use the following activation spec and code extract to listen to an MQ queue:

@MessageDriven(name="connectorMQin",
activationConfig = {
        @ActivationConfigProperty(propertyName="destination", propertyValue="TEST.Q1"),  
        @ActivationConfigProperty(propertyName="hostName", propertyValue="10.0.0.66"),  
        @ActivationConfigProperty(propertyName="port", propertyValue="1414"),  
        @ActivationConfigProperty(propertyName="queueManager", propertyValue="QM_BHDEV"),
        @ActivationConfigProperty(  propertyName ="destinationType", propertyValue = "javax.jms.Queue"),
        @ActivationConfigProperty(  propertyName ="clientId", propertyValue = "wmq.jmsra.id") }, 
mappedName = "jms/testq1")
@TransactionManagement(TransactionManagementType.BEAN)
public class MqIn implements MessageListener {

 public void onMessage(Message in_msg) {
 // ...
 try {
    if (in_msg instanceof TextMessage) {
                in_bodyText = ((TextMessage) in_msg).getText();
       // ... etc
    }
 } catch ( // ... etc

... that worked for me.

The pitfall is: the entire configuration details are now hard-wired into the source code. With dozen connections, and because (to my knowledge) EL expressions will not work in this source code, you may want to relocate all activation properties (inclusive of the mapped name pointing to the queue to listen to) into corresponding ejb-jar.xml elements, and then you will be able to build connector instances with variant ejb-jar.xml templates...

? Has any one a better suggestion or alternative that avoids this unnecessary complexity? (reminding that I observed that connection pool properties handled ok in the outbound/factory side are ignored on the inbound/activation side...)