JMS - Java Message Service



Replacing use of Weblogic "ForeignJMSServer" databridge to Websphere MQ 5.2.1 / 5.3 / 6.0

Often is used a Weblogic "ForeignJMSServer" to easily interface with a remote MQ queue:


Often used section in config.xml used by your Weblogic installation to define a MQ queue as an external ressource..
config.xml

...
<ForeignJMSServer ConnectionURL="file:/C:/jms-jndi-directory" InitialContextFactory="com.sun.jndi.fscontext.RefFSContextFactory" JNDIProperties="" Name="MQ JMS Server" Targets="myserver"> <ForeignJMSConnectionFactory LocalJNDIName="mqXAQCF" Name="mqXAQCF" RemoteJNDIName="TOPSECURITY_XAQCF"/> <ForeignJMSDestination LocalJNDIName="dk.topsecurity.queue" Name="topsecurity_queue" RemoteJNDIName="TOPSECURITY_Q"/> </ForeignJMSServer> ...

Obvious advantages here is that the a MQ queue looks just like any other native Weblogic JMS queue in the setup. Consequently an application need not worry about where messages originate.. or are heading... That's some of the reasons, why this approach is very polular.

But... the big problem here, is that ForeignJMSServer's are container managed - and thus the container always requires access to the com.ibm.mq*.jar files in global Weblogic classpath. For different reasons, including stuff in the global Weblogic server classpath is a very, very bad idea. If you've worked a long time with app-servers, you've probably tried a situation where old middleware blocked necessary upgrade of other middleware - eventually leaving you on an overall non-supported platform. So... I recommend you don't mix in middleware into your application server unless you *really* have to. Another few reasons : So, for various reasons, the Weblogic ForeignJMSServer concept is often not really the optimal solution. To get around it, we need is a message driven BridgeBean, which lifts messages straight out of MQ and delivers them in a normal Weblogic JMS queue. Transactionally.

First we need to define a native Weblogic queue, where messages from MQ will eventually be delivered - and may be picked up by another application :


Necessary to add to config.xml in your Weblogic installation to define a native JMS queue..
config.xml

...
<JMSFileStore Directory="topsec_jmsstore" Name="TOPSEC JMS File Store"/> <JMSConnectionFactory JNDIName="wlsXAQCF" Name="wlsXAQCF" Targets="myserver" XAConnectionFactoryEnabled="true"/> <JMSServer Name="TOPSEC JMS Server" Store="TOPSEC JMS File Store" Targets="myserver"> <JMSQueue JNDIName="dk.topsecurity.queue" Name="topsecurity_queue" StoreEnabled="true"/> </JMSServer> ...

Next we need the BridgeBean... It comes in different parts: Furthermore we need a couple of support classes. One class feeding messages directly to MQ so we have something to process. Another class retrieving messages from the internal Weblogic JMS queue, one our BridgeBean managed to transform them from MQ messages to Weblogic JMS messages. And that's really no trivial task.

Following is listing of the code:


Message-driven bean sucking out messages from MQ and delivering in Weblogic native JMS queue
TopsecurityBridgeBean.java

package dk.topsecurity;

import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import javax.ejb.CreateException;
import javax.ejb.MessageDrivenBean;
import javax.ejb.MessageDrivenContext;
import javax.jms.*;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import weblogic.rmi.RemoteException;

public class TopsecurityBridgeBean implements MessageDrivenBean, MessageListener {

  private String TARGETQUEUE_NAME = "dk.topsecurity.queue";
  private static final boolean VERBOSE = true;
  private MessageDrivenContext m_context;
  private Destination destination;
  private ConnectionFactory factory;
  
  private void log(String s) { // maybe consider using WebLogic's log service
    if (VERBOSE) System.out.println(s);
  } 
  
  /**
   * methods required by the EJB Specification, but not used by this example.
   */
  public void ejbActivate() {
    log("ejbActivate called -");
  }
  public void ejbRemove() {
    log("ejbRemove called -");
  }
  public void ejbPassivate() {
    log("ejbPassivate called -");
  }

  /**
   * Sets the session context.
   * @param ctx MessageDrivenContext Context for session
   */
  public void setMessageDrivenContext(MessageDrivenContext ctx) {
    log("setMessageDrivenContext called");
    m_context = ctx;
  }

  /**
   * This method corresponds to the create method in the home interface.
   */
  public void ejbCreate () throws CreateException {
    log("ejbCreate called -");

    Context context = null;
    try {
      context = new InitialContext();
    } catch (NamingException ne) {
      ne.printStackTrace();
      throw new CreateException(ne.getMessage());
    }
    String connectionFactoryName = "weblogic.jms.ConnectionFactory";
    try {
      factory = (ConnectionFactory)context.lookup(connectionFactoryName);
    } catch (Exception e) {
      e.printStackTrace();
      throw new CreateException(e.getMessage());
    }
    try {
      destination = (Destination)context.lookup(TARGETQUEUE_NAME);
    } catch (Exception e) {
      e.printStackTrace();
      throw new CreateException(e.getMessage());
    }
  }

  /**
   * Retrieve the value of the Message and immediately pass it on to
   * another JMS queue. Acting as a message bridge relay. Much in the
   * same way as a Weblogic data bridge.
   */
  public void onMessage(Message msg) {
    try {  
	  if (msg instanceof TextMessage) {
        String reply = ((TextMessage) msg).getText();
        log("MDB Text: " + reply);
        log(reply);
        sendText((QueueConnectionFactory)factory, (Queue)destination, reply);
	  }
	  else if (msg instanceof ObjectMessage) {
        ObjectMessage om = (ObjectMessage) msg;
        Object rec = om.getObject();
        log("MDB Object: " + rec);
        sendObject((QueueConnectionFactory)factory, (Queue)destination, 
          (Serializable)rec);
	  }
	  else {
        log("MDB: received an unsupported message type...discarded.");
      }
    }
    catch(Exception ex) {
      ex.printStackTrace();
    }
  }

  private static void sendObject(QueueConnectionFactory qfactory, Queue queue,
    Serializable o) throws InvocationTargetException
  {
    QueueConnection qconnection = null;
    QueueSession qsession = null;
	QueueSender qsender = null;
    try {
      qconnection = qfactory.createQueueConnection();
      qsession = qconnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
	  qsender = qsession.createSender(queue);

      ObjectMessage omessage = qsession.createObjectMessage();
      omessage.setObject(o);
      qconnection.start();
      qsender.send(omessage);

	} catch (JMSException jmse) {
      throw new InvocationTargetException(jmse, "Could not send message");
    } finally {
      try { 
        if (qsender != null) qsender.close(); 
      } catch (JMSException ignore) {}
      try {
        if (qsession != null) qsession.close();
      } catch (JMSException ignore) {}
      try {
        if (qconnection != null) qconnection.close();
      } catch (JMSException ignore) {}
    }
  }

  /**
   * actually taking care of sending the message - please notice that
   * only the textual contents of the message is passed on. If you have
   * set special JMS fields (correlation id's etc) you need to pass them on
   * specificly as well..
   */
  private static void sendText(QueueConnectionFactory qfactory, Queue queue, String t)
    throws InvocationTargetException
  {
    QueueConnection qconnection = null;
    QueueSession qsession = null;
	QueueSender qsender = null;
    try {
      qconnection = qfactory.createQueueConnection();
      qsession = qconnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
	  qsender = qsession.createSender(queue);

      TextMessage tmessage = qsession.createTextMessage();
      tmessage.setText(t);
      qconnection.start();
      qsender.send(tmessage);

	} catch (JMSException jmse) {
      throw new InvocationTargetException(jmse, "Could not send message");
    } finally {
      try { 
        if (qsender != null) qsender.close(); 
      } catch (JMSException ignore) {}
      try {
        if (qsession != null) qsession.close();
      } catch (JMSException ignore) {}
      try {
        if (qconnection != null) qconnection.close();
      } catch (JMSException ignore) {}
    }
  }
}


Standard interface for the message-driven bean
TopsecurityBridgeHome.java

package dk.topsecurity;

import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.EJBHome;

public interface TopsecurityBridgeHome extends EJBHome{
  TopsecurityBridgeInterface create() throws CreateException, RemoteException;
} 


Standard interface for the message-driven bean
TopsecurityBridgeInterface.java

package dk.topsecurity;

import java.rmi.RemoteException;

public interface TopsecurityBridgeInterface extends javax.ejb.EJBObject{
} 


Standard deployment descriptor for the message-driven bean
ejb-jar.xml

<?xml version="1.0"?>
<!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN" "http://java.sun.com/dtd/ejb-jar_2_0.dtd">

<ejb-jar>
 <enterprise-beans>
    <message-driven>
      <ejb-name>TopsecurityBridgeMDB</ejb-name>
      <ejb-class>dk.topsecurity.TopsecurityBridgeBean</ejb-class>
      <transaction-type>Container</transaction-type>
      <message-driven-destination>
        <destination-type>javax.jms.Queue</destination-type>
      </message-driven-destination>
    </message-driven>
 </enterprise-beans>

  <assembly-descriptor>
    <container-transaction>
      <method>
        <ejb-name>TopsecurityBridgeMDB</ejb-name>
        <method-name>*</method-name>
      </method>
      <trans-attribute>Required</trans-attribute>
    </container-transaction>
  </assembly-descriptor>
</ejb-jar>


Standard deployment descriptor for the message-driven bean
weblogic-ejb-jar.xml

<?xml version="1.0"?>
<!DOCTYPE weblogic-ejb-jar PUBLIC "-//BEA Systems, Inc.//DTD WebLogic 7.0.0 EJB//EN" "http://www.bea.com/servers/wls700/dtd/weblogic-ejb-jar.dtd">
<weblogic-ejb-jar>

  <weblogic-enterprise-bean>
    <ejb-name>TopsecurityBridgeMDB</ejb-name>
    <message-driven-descriptor>
      <pool>
        <max-beans-in-free-pool>200</max-beans-in-free-pool>
        <initial-beans-in-free-pool>20</initial-beans-in-free-pool>
      </pool>

      <destination-jndi-name>TOPSECURITY_QUEUE</destination-jndi-name>
      <initial-context-factory>com.sun.jndi.fscontext.RefFSContextFactory</initial-context-factory>
      <provider-url>file:/C:/jms-jndi-directory</provider-url>
      <connection-factory-jndi-name>TOPSECURITY_XAQCF</connection-factory-jndi-name>

    </message-driven-descriptor>
    <jndi-name>examplesMessageDriven</jndi-name>
  </weblogic-enterprise-bean>

</weblogic-ejb-jar>
/font>

And... supporting this example, we needed a couple of client - one delivering messages to MQ - and picking up messages from a native Weblogic JMS queue.


Message feeder - to MQ - straight out of Sun JVM and into MQ
TopsecurityMQXAClient.java

package dk.topsecurity;

import javax.jms.*;
import javax.naming.*;
import javax.naming.directory.*;
import java.util.*;
import com.ibm.mq.jms.*;

import javax.transaction.xa.Xid;
import javax.transaction.xa.XAResource;


public class TopsecurityMQXAClient implements ExceptionListener {

/** 
 * Setting up authorisation username. Used by classes in com.ibm.mqjms.jar
 * Setting up queue connection factory (QCF)
 * Setting up name of queue, as defined in the MQ setup
 * Setting up url for file-based, external JNDI provider 
 * Setting up context factory to use with external JNDI
 */

  public String mq_username = "noname"; //"Administrator";
  public String mq_qcf = "TOPSECURITY_XAQCF";
  public String mq_qname = "TOPSECURITY_QUEUE";
  public String mq_url = "file:/C:/jms-jndi-directory";
  public String mq_jndi =  "com.sun.jndi.fscontext.RefFSContextFactory";

/**
 * main method - binding to the Websphere MQ installation - and delivering a
 * number of messages.
 */
 
  public static void main(String[] args) {

    TopsecurityMQXAClient sender = null;
    try {
      sender = new TopsecurityMQXAClient();
      sender.setupQueueConnection();
      for(int i=0;;i++) {
        sender.send(sender.setMessage("Hello no "+i+" from client at "+new Date()));
        Thread.sleep(1000L);
      }
    }
    catch(JMSException je) {
      System.out.println("Caught JMSException: "+je);
      Exception le = je.getLinkedException();
      if (le != null) 
        System.out.println("Linked exception: "+le);
      je.printStackTrace();
    } catch(Exception e) {
       e.printStackTrace();
     } finally {
      try {
      if (sender != null)
        sender.cleanup();
      } catch (Exception e) { }
    }
  }

  private void setupQueueConnection() throws Exception {

    //set the username which to use for authentication
    System.out.println("Initial user.name="+System.getProperty("user.name"));
    System.setProperty("user.name",mq_username);
    System.out.println("Authrorisation required user.name="+System.getProperty("user.name"));

    Hashtable env = new Hashtable();
    env.put(Context.INITIAL_CONTEXT_FACTORY, mq_jndi);
    env.put(Context.PROVIDER_URL, mq_url);
//    env.put(Context.SECURITY_PRINCIPAL, "Administrator");

    InitialDirContext ctx = new InitialDirContext(env);
    com.ibm.mq.jms.MQXAQueueConnectionFactory factory =
            (com.ibm.mq.jms.MQXAQueueConnectionFactory)ctx.lookup(mq_qcf);

    System.out.println("Factory = " +factory);

    /* Create a QueueConnection, QueueSession
     * When a connection is made, use the createQueueSession method on the
     * QueueConnection to obtain a session. Parameters: 
     * boolean= determines whether the session is transacted or non-transacted.
     * int =  that determines the acknowledge mode.
     * Simplest case is that of the non-transacted session with AUTO_ACKNOWLEDGE
     * - p319 in the IBM redbook.
     */
    xaconnection = factory.createXAQueueConnection();
    xasession = xaconnection.createXAQueueSession();
    QueueSession normalsession = xasession.getQueueSession();

   ioQueue = (Queue)ctx.lookup(mq_qname);

    queueSender = normalsession.createSender(ioQueue);

/* //we only use this when we want to receive messages transactionally
    xaconnection.start();
*/
    xaconnection.setExceptionListener(this);
  }


  /**
   * For test purpose, create one of many possible types of messages: 
   * BytesMessage, MapMessage, ObjectMessage, StreamMessage
   */
  TextMessage setMessage(String text) throws Exception {

    TextMessage msg = xasession.createTextMessage();
    msg.setText(text);
    return(msg);
  }

  /**
   * Send message off to MQ
   */
  void send(TextMessage msg) throws Exception {

    System.out.println("To " + ioQueue.getQueueName() + " sending message=" + msg.getText());

    XAResource xares = xasession.getXAResource();
    Xid xid = new XidImpl(102);

    xares.start(xid, XAResource.TMNOFLAGS);

    System.out.println("Sending the message on queue " + ioQueue.getQueueName());
    try {
      queueSender.send(msg);
      System.out.println("sending successfull");
    } catch(Exception ex) {
      System.out.println("sending unsuccessfull");
      System.out.println(ex);
    }

    xares.end(xid,XAResource.TMNOFLAGS);
    //xares.prepare(xid);
    xares.commit(xid, true);
    System.out.println("sending completed");

  }

  public void onException(JMSException jms) {
    System.out.println("onException: jms" + jms);
    jms.printStackTrace();
  }

  void cleanup() throws Exception {
    if (xasession != null) {
      xasession.close();
      xasession = null;
    }
    if (xaconnection != null)
      xaconnection.close();
      xaconnection = null;
  }

  private Queue ioQueue;
  private XAQueueSession xasession;
  private XAQueueConnection xaconnection;
  private XAQueueConnectionFactory factory;
  private QueueSender queueSender;
  private InitialContext ctx;
  private TextMessage msg;

  static class XidImpl implements Xid {
     private byte _branch[] = new byte[64];
     private byte _global[] = new byte[64];
                                                                          
     public XidImpl(int id) {
       _branch[60] = (byte) ((id >>> 24) & 0xFF);
       _branch[61] = (byte) ((id >>> 16) & 0xFF);
       _branch[62] = (byte) ((id >>>  8) & 0xFF);
       _branch[63] = (byte) ( id         & 0xFF);
     }
                                                                          
     public byte[] getGlobalTransactionId() { return _global; }
                                                                          
     public byte[] getBranchQualifier() { return _branch; }
                                                                          
     public int getFormatId() { return 0; }
  }
}


Message receiver - commandline listener to native Weblogic JMS queue
TopsecurityWLReceiver.java

package dk.topsecurity;

import java.io.BufferedReader;
import java.io.FilterOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.Reader;
import java.io.Serializable;
import java.util.Dictionary;
import java.util.Hashtable;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueReceiver;
import javax.jms.QueueSender;
import javax.jms.QueueSession;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.BytesMessage;
import javax.jms.MessageEOFException;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.Name;
import javax.naming.NamingException;
import javax.jms.MessageEOFException;


/**
 * TopsecurityWLReceiver - this class simply listens on a specific Weblogic
 * JMS queue - for TextMessages andByteMEssages - printing out the contents.
 * 
 * @returns
 * @throws Exception
 * 
 */

final public class TopsecurityWLReceiver implements MessageListener {

  public final static String JNDI_FACTORY = 
    "weblogic.jndi.WLInitialContextFactory";
  public final static String JMS_FACTORY = "weblogic.jms.ConnectionFactory";
  public final static String QUEUE = "weblogic.jms.inqueue";

  private QueueConnectionFactory qconFactory;
  private QueueConnection qcon;
  private QueueSession qsession;
  private QueueReceiver qreceiver;
  private Queue queue;
  private static String myqueue = null;

  public void onMessage(Message jmsMsg) {
    
    try { //deal with TextMessage,BytesMessage - and discharge the rest..
     if (jmsMsg instanceof TextMessage) {
        String msgText = ((TextMessage) jmsMsg).getText();
        System.out.println("TextMessage:" + msgText);
     } else 
        if (jmsMsg instanceof BytesMessage) {
          int i; // put it in a TypedCArray and try to send it to console anyway
          byte[] carray = new byte[1000];
          for (i=0;;i++) { //rean until eof
            try {
                carray[i] = (byte) ((BytesMessage) jmsMsg).readByte();
            } catch (MessageEOFException eof) {
                break;
            }
          }
          System.out.println("BytesMessage: " + i + " bytes.");
          for (i = 0; i<10 ; i++) {
            System.out.println("  CArray[" + i + "] = " + carray[i]);
            if(carray[i] == 0) break;
          }   // end for
        } else {
          System.out.println("Message type not of type TextMessage or BytesMessage");
      }
    } catch (JMSException jmse) {
      jmse.printStackTrace();
    }
  } 

  /**
   * Create all the necessary objects for sending and receiving
   * messages from a JMS queue.
   */
  public void init(Context ctx, 
                   String queueName) throws NamingException, JMSException {

    qconFactory = (QueueConnectionFactory) ctx.lookup(JMS_FACTORY);
    qcon = qconFactory.createQueueConnection();
    qsession = qcon.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
    try {
      queue = (Queue) ctx.lookup(queueName);
    } catch (NamingException ne) {
      queue = qsession.createQueue(queueName);
      ctx.bind(queueName, queue);
    } 
    String selector = "";
    qreceiver = qsession.createReceiver(queue, selector);
    qreceiver.setMessageListener(this);
    qcon.start();
  } 

  public void close() throws JMSException {
    qreceiver.close();
    qsession.close();
    qcon.close();
  } 


  /**
   * get initial context to the server
   */
  private static InitialContext getInitialContext(String url) 
          throws NamingException {
    Hashtable env = new Hashtable();
    env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
    env.put(Context.PROVIDER_URL, url);
    return new InitialContext(env);
  } 


  public static void main(String[] args) throws Exception {
    if (args.length < 1 || args.length > 2) {
    System.out.println("call with parameters [queue] url-to-weblogic-location");
      return;
    } 
    TopsecurityWLReceiver receiver = new TopsecurityWLReceiver();
    if (args.length == 1) {
      InitialContext ic = getInitialContext(args[0]);
      receiver.init(ic, QUEUE);
      myqueue = QUEUE;
    } else {
      InitialContext ic = getInitialContext(args[1]);
      receiver.init(ic, args[0]);
      myqueue = args[0];
    } 
    System.out.println("JMS listener ready to recieve messages from: " + myqueue);
    synchronized (receiver) {
      for(;;) {
        try {
          receiver.wait();
        } 
        catch (InterruptedException ie) {}
    } }
   // receiver.close();
  } 
}

In order to build this example successfully you need the set of MQ client files. Put them into the library .\mqlib in the build environment. From there they will be extracted by the ant build-script and included into the finally built ear/jar-file containing the MDB application. That will ensure including at the lowest possible class-loader level.

Make sure to include "com.ibm.mqetclient.jar" since the MDB is transactional here.


Ant buildfile required to build classes in this example
build.xml

<project name="TopsecurityMDB" default="all" basedir=".">

  <target name="init">
        <!-- set global properties for this build -->
        <property name="wl_home" value="C:/bea/weblogic81"/>
        <property name="source" value="./src"/>
        <property name="compiledir" value="./classes"/>
        <property name="warstage" value="./stage"/>
        <property name="earstage" value="./earstage"/>
        <property name="project_name"   value="TopsecurityBridgeMDB"/>
        <property name="mqlibdir" value="./mqlib"/>
  </target>


  <target name="clean" depends="init">
    <delete dir="${compiledir}" />
    <mkdir dir="${compiledir}/temp" />
    <mkdir dir="${compiledir}/temp/META-INF"/>
    <delete dir="${warstage}" />
    <mkdir dir="${warstage}" />
    <mkdir dir="${warstage}/bouncer" />
    <mkdir dir="${warstage}/bouncer/WEB-INF" />
    <mkdir dir="${warstage}/bouncer/WEB-INF/classes" />
    <delete dir="${earstage}" />
    <mkdir dir="${earstage}" />
    <mkdir dir="${earstage}/META-INF" />
  </target>

  <target name="build" depends="clean">
  
    <!-- Create the EJB -->

    <copy file="${source}/ejb-jar.xml" todir="${compiledir}/temp/META-INF" overwrite="yes"/>
    <copy file="${source}/weblogic-ejb-jar.xml" todir="${compiledir}/temp/META-INF" 
      overwrite="yes"/>

<unzip dest="${compiledir}/temp">
    <fileset dir="${mqlibdir}">
        <include name="**/*.jar"/>
        <exclude name="**/weblogic*.jar"/>
    </fileset>
</unzip>

    <javac srcdir="${source}" classpath="${wl_home}\server\lib\weblogic.jar" 
       includes="**/*.java" destdir="${compiledir}/temp"/>

    <javac srcdir="${source}" classpath="${wl_home}\server\lib\weblogic.jar" 
      includes="**/TopsecurityBridgeHome.java,**/TopsecurityBridgeInterface.java"
      destdir="${warstage}/bouncer/WEB-INF/classes" />

    <!-- you need a .jar file as argument to ejbc compiler -->
    <jar jarfile="${compiledir}/${project_name}.jar" basedir="${compiledir}/temp" includes="**/TopsecurityBridge*.class,**/*.xml,com/**/*.class,*.properties" />

    <!--  -->
    <jar jarfile="${compiledir}/${project_name}_client.jar" basedir="${compiledir}/temp" excludes="**/TopsecurityBridge*.class,**/*.xml,com/**/*.class,*.properties" />

    <java classname="weblogic.ejbc" fork="true" failonerror="true" 
      classpath="${wl_home}\server\lib\weblogic.jar" >
      <arg line=" -noexit -compiler sj ${compiledir}/${project_name}.jar ${compiledir}/${project_name}.jar"/>
    </java>

    <copy file="${source}/web.xml" todir="${warstage}/bouncer/WEB-INF" />
    <copy file="${source}/web-services.xml" todir="${warstage}/bouncer/WEB-INF" />
    <copy file="${source}/index.html" todir="${warstage}/bouncer" />

    <jar jarfile="${compiledir}/${project_name}.war" basedir="${warstage}/bouncer" />

    <copy file="${compiledir}/${project_name}.jar" todir="${earstage}" />
    <copy file="${compiledir}/${project_name}.war" todir="${earstage}" />
    <copy file="${source}/application.xml" todir="${earstage}/META-INF" />

    <jar jarfile="${compiledir}/${project_name}.ear" basedir="${earstage}" />

    <delete dir="${compiledir}/temp" />
    <delete file="${compiledir}/${project_name}.jar" />
    <delete file="${compiledir}/${project_name}.war" />
    <delete dir="${warstage}" />
    <delete dir="${earstage}" />
    
  </target>


  <target name="all" depends="clean,build" />

</project>

Additional, we need the application deployment file to tell Weblogic what's the mini-application is all about (bean) and deploy another small .war file with an index.html - containing basicly hello-stuff, used to ensure that the .ear file was deployed successfully (in case we don't want to start up the Weblogic console all the time).


Application deployement descriptor
application.xml

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

<!DOCTYPE application PUBLIC '-//Sun Microsystems, Inc.//DTD J2EE Application 1.2//EN' 'http://java.sun.com/j2ee/dtds/application_1_2.dtd'>

<application>
  <display-name>webservice1</display-name>
  <description>An archived ear containing an archived jar</description>
  <module>
     <ejb>TopsecurityBridgeMDB.jar</ejb>  
  </module>
  <module>
     <web>
       <web-uri>TopsecurityBridgeMDB.war</web-uri>
       <context-root>/TopsecurityBridgeMDB</context-root>
     </web>
  </module>
  
</application>


dummy index.html so we can see if the stuff have really been deployed
index.html

<html><body>greetings - seems like something is working as intended</body></html>

Creating the .ear file with the "mini-application" and the .jar file with the external JMS clients.



Compilation of the MDB and client

Leaving us with a rather big .ear file (because it also contains all the MQ-interface classes from the com.ibm.mq*,.. jar-files) :

Listing of the compilation output

If we start the weblogic server, then we see a normal startup output - caused by instantiation of 20 BridgeBean's in the pool (we need a flexible setup where MQ will be serviced acceptable even in peak-situations) :

Weblogic 8.1.4 startup trace

Next we run the client feeding MQ with messages...


Batch file calling the client feeding MQ
send-TX.cmd

set BEA_HOME=C:\bea
set WL_HOME=C:\bea\weblogic81

java -classpath .;.\classes\TopsecurityBridgeMDB_client.jar;.\mqlib\weblogic.jar;.\mqlib\com.ibm.mqetclient.jar;.\mqlib\com.ibm.mq.jar;.\mqlib\com.ibm.mqjms.jar;.\mqlib\fscontext.jar;.\mqlib\providerutil.jar;.\mqlib\dbhcore.jar;.\TopsecurityMQ_client.jar   dk/topsecurity/TopsecurityMQXAClient

Leaving us with command line output trace (messages 0,1,2 sent - that's 3 messages in total) :

Weblogic 8.1.4 startup trace

The messages is transmitted to MQ - where they're immediately picked up by our BridgeBean - displayed while processed by the bean - and finally inserted into a native Weblogic queue.

Checking the Weblogic server trace, we notice the messages:

Weblogic 8.1.4 message processing trace

And checking the Weblogic server console, it's obvious where they ended up:

Weblogic 8.1.4 console login
Weblogic console shows messages pending

Finally we pick them up by starting the receiver..


Batch file calling the Weblogic JMS queue listener
receiveWL.cmd

set BEA_HOME=C:\bea
set WL_HOME=C:\bea\weblogic81

java -classpath .;./classes/TopsecurityBridgeMDB_client.jar;%WL_HOME%\server\lib\weblogic.jar dk.topsecurity.TopsecurityWLReceiver dk.topsecurity.queue t3://localhost:7001

Causing following output - which matches exactly with the message feed to MQ :


Client picking up JMS messages

Double-checking with the Weblogic console, we see our mesages have been picked up - and we see a consumer added to the queue:


Weblogic console shows messages picked up



Time for a summary...



Basicly this example demonstrates most basic aspects of MQ interfacing :
Sure... relaying MQ messages to a native Weblogic JMS queue could easily have been facilitated by a container-controlled Weblogic data bridge. But that would also have required MQ-client classes in the global classpath.

/www.topsecurity.dk 2005-11-15