1
votes

I'm new in MDB and EE. Please tell me there is i'm wrong. My app must interaction with Websphere MQ (wait a messaege in queue, do something and reply). I'm using NetBeans 7.3 ,GlassFish 3.1, Websphere MQ 6.2, resorce adapter wmq.jmsra.rar. Interaction must be not in jms format is, only Web MQ nature. I'm deploy adapter and create Connecton pool and Administrated Object. In domain.xml

  <connector-connection-pool description="" name="cpMqAdapter" resource-adapter-name="wmq.jmsra" connection-definition-name="javax.jms.QueueConnectionFactory" transaction-support="LocalTransaction">
  <property name="port" value="1414"></property>
  <property name="CCSID" value="866"></property>
  <property name="hostName" value="192.168.0.11"></property>
  <property name="queueManager" value="QM"></property>
  <property name="channel" value="SrvConn"></property>
  <property description="CLIENT - mq on other computer" name="transportType" value="CLIENT"></property>
</connector-connection-pool>


<admin-object-resource enabled="false" res-adapter="wmq.jmsra" res-type="javax.jms.Queue" description="" jndi-name="wmqJmsAOR" class-name="com.ibm.mq.connector.outbound.MQQueueProxy">
  <property name="priority" value="APP"></property>
  <property name="failIfQuiesce" value="true"></property>
  <property name="baseQueueManagerName" value="QM"></property>
  <property name="CCSID" value="1208"></property>
  <property name="persistence" value="APP"></property>
  <property name="encoding" value="NATIVE"></property>
  <property name="baseQueueName" value="TEST"></property>
  <property name="targetClient" value="MQ"></property>
  <property name="expiry" value="APP"></property>
</admin-object-resource>

' In netbeans i'm create EE project and message driven bean. i'm gett this code '

   @MessageDriven(mappedName = "wmqJmsAOR", activationConfig = {
   @ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto- 
   acknowledge"),
   @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue")

 })
public class NewMessage implements MessageListener {
public NewMessage() {
    super();
    System.out.println("I created");
}

@Override
public void onMessage(Message message) {

    System.out.println("I'm getting message");

}
`

Please tell me why this MDB is not listenig a queue (i'm put test message in Websphere MQ console). May be i'm must write something in config (now project as default netbeans created).

Alexei

1
Have you ever figured this out? I am running into the same issue. I don't get any errors but it just does not receive any messages. I even put all the connection information into the admin-object properties..Armand
@Armand Hello. No, this task was canceled. It is possible that i'm will be back later to this workasu

1 Answers

1
votes

I have a solution that works. It is not the best solution but it does work extremely well.

What we have done is to create a very simple ActivationSpecWrapper class to extend the IBM com.ibm.mq.connector.inbound.ActivationSpecImpl class. This wrapper class has one public set/get property (asJNDI). The purpose if the class is to read via JNDI context the Properties class defined in the App server that contains all the properties to be assigned in the activation of the MDB.

First, create the new ActivationSpecWrapper class. you can put this in any package of your choosing.

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.naming.InitialContext;
import com.ibm.mq.connector.inbound.ActivationSpecImpl;

public class ActivationSpecWrapper extends ActivationSpecImpl
{

   private static final long   serialVersionUID = -529716553593856979L;

   private static final String sourceClass      = ActivationSpecWrapper.class.getName();
   private static final Logger log              = Logger.getLogger(sourceClass);

   private String              asJNDI           = null;

   public void setAsJNDI(String asJNDI)
   {
      log.config("asJNDI = " + asJNDI);
      this.asJNDI = asJNDI;

      try
      {
         final InitialContext ctx = new InitialContext();
         final Properties properties = (Properties) ctx.lookup(asJNDI);

         for (final Object key : properties.keySet())
         {
            try
            {
               final String value = properties.getProperty((String) key);
               final Object field = getSetter((String) key);
               if (field != null)
               {
                  if (field instanceof Field)
                  {
                     log.fine("Setting " + key + " via Field " + (String) key + " = " + value);
                     ((Field) field).set(this, value);

                  }
                  else
                  {
                     log.fine("Setting " + key + " via Method " + (String) key + " = " + value);
                     ((Method) field).invoke(this, value);
                  }
                  log.config(key + " = " + value);
               }
               else
               {
                  log.warning("Invalid ActivationSpec Field: " + key);
               }
            }
            catch (final NoSuchFieldException e)
            {
               log.throwing(sourceClass, "setAsJNDI", e);
            }

         }

      }
      catch (final Exception e)
      {
         log.log(Level.SEVERE, "Error looking up " + asJNDI, e);
         return;
      }

   }

   public String getAsJNDI()
   {
      return asJNDI;
   }

   private static Object getField(String fieldName) throws NoSuchFieldException
   {
      return ActivationSpecWrapper.class.getField(fieldName);
   }

   private static Object getSetter(String fieldName) throws NoSuchFieldException
   {
      try
      {
         final StringBuilder sb = new StringBuilder(fieldName.length() + 3).append("set").append(fieldName);
         sb.setCharAt(3, Character.toUpperCase(sb.charAt(3)));

         return ActivationSpecWrapper.class.getMethod(sb.toString(), String.class);
      }
      catch (final NoSuchMethodException e)
      {
         return getField(fieldName);
      }
   }
}

To implement the class you just need to modify the META-INF/ra.xml file inside the wmq.jmsra.rar file. Change the one occurrence of the ActivationSpecImpl class to your class. This will be your new incoming connection factory's ActivationSpecWrapper class that it uses. So now your wrapper class can look to the app server for the properties to use.

I do this as follows:

: jar -xvf wmq.jmsra.rar META-INF/ra.xml
: perl -pi -e 's/com\.ibm\.mq\.connector\.inbound\.ActivationSpecImpl/your.new.package.ActivatonSpecWrapper/g' META-INF/ra.xml
: jar -uvf wmq.jmsra.rar META-INF/ra.xml

Before modifying the META-INF/ra.xml looks like:

<activationspec>
   <activationspec-class>
      com.ibm.mq.connector.inbound.ActivationSpecImpl
   </activationspec-class>
   <required-config-property>
      <config-property-name>destination</config-property-name>
   </required-config-property>
   <required-config-property>
      <config-property-name>destinationType</config-property-name>
   </required-config-property>
</activationspec>

After the change, the META-INF/ra.xml should like like:

<activationspec>
   <activationspec-class>
      your.new.package.ActivatonSpecWrapper
   </activationspec-class>
   <required-config-property>
      <config-property-name>destination</config-property-name>
   </required-config-property>
   <required-config-property>
      <config-property-name>destinationType</config-property-name>
   </required-config-property>
</activationspec>

Now you will need to add your new package to the RAR file. It should be in standard directory structure. like this:

: jar -uvf wmq.jmsra.rar your/new/package/ActivationSpecWrapper.class

The problem stems from IBM placing the host/port/queue manager/channel (etc.) into the activation spec instead of the administration object. It belongs in the administration object since that is the connection factory for MDB queues. IBM only allows two properties there.

Also if you are using glassfish, oracle really botched things up for MDB classes that need resource adapters, because the glassfish @MessageDriven annotation assumes the app containers default resource adapter (OpenMQ) for JMS. This means the vendor specific ActivationSpecImpl does not work, and thus IMB's custom parameters for host/port and other activation config properties are not supported via annotations until after the resource adaptor is switch via the glassfish-ejb-jar.xml.

JBoss allows for the @ResourceAdapter annotation to change the resource adapter but Glassfish only allows this via the glassfish-ejb-jar.xml file. And when this is used, you only need to annotate your MDB with three activation config properties (the destinationType). Everything else you will place in your JNDI published Properties.

The glassfish-ejb-jar.xml should look like this:

<?xml version="1.0" encoding="UTF-8"?>
<glassfish-ejb-jar>

    <enterprise-beans >
        <unique-id>1</unique-id>
        <ejb>
            <ejb-name>MyMDB</ejb-name>
            <mdb-resource-adapter>
                <resource-adapter-mid>wmq.jmsra</resource-adapter-mid>
                <activation-config>
                    <activation-config-property>
                        <activation-config-property-name>asJNDI</activation-config-property-name>
                        <activation-config-property-value>mq/InboundMessages</activation-config-property-value>
                    </activation-config-property>
                </activation-config>
            </mdb-resource-adapter>
        </ejb>
    </enterprise-beans> 
</glassfish-ejb-jar>

The MDB @MessageDriven annotation will look something like this:

@MessageDriven(activationConfig =
   {
    @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
    @ActivationConfigProperty(propertyName = "destination", propertyValue = "jms/InboundMessage_queue"),
    @ActivationConfigProperty(propertyName = "useJNDI", propertyValue = "true") })
public class MyMDB implement MessageListener
{
    public void onMessage(Message message)
    {
        // message handler code goes here...
    }
}

The last step to make this work, is to add the mq/InboundMessages properties to JDNI, to define the factory properties for the MQ listener resource. This is how it is defined in the domain.xml file:

<custom-resource res-type="java.util.Properties" jndi-name="mq/InboundMessages" factory-class="org.glassfish.resources.custom.factory.PropertiesFactory">
  <property name="hostName" value="mqserver"></property>
  <property name="port" value="1422"></property>
  <property name="queueManager" value="MQMNGR"></property>
  <property name="channel" value="MQMNGR.SM.S1"></property>
  <property name="transportType" value="CLIENT"></property>
</custom-resource>

I hope this helps. This isn't the easiest solution but it is simple enough, and once it has been established, it is very portable, and allows the app server administrator to manage the connection details to the MQ, instead of the developer.