ONJava.com    
 Published on ONJava.com (http://www.onjava.com/)
 See this if you're having trouble printing code examples


XML Messaging Using JBoss XML Messaging Using JBoss

by Benoit Aumars
10/13/2004

It's common practice to share data using FTP, but an increasingly popular alternative is to use a messaging service. As always, each approach has its own pros and cons, depending on the nature of "what to share," how easy it is to implement the technology, the need to use an asynchronous process, etc.

In this article, I will focus on data sharing, with the common producer-consumer model, using open source software such as:

The interactions between them are managed by Java Management Extensions (JMX). Please refer to their respective home pages if you are unfamiliar with these frameworks.

As for the Java Messaging Service (JMS) provider, you can choose either WebSphere MQ or JBossMQ. This is fully configurable in the jboss-service.xml file provided. By using JMS, your application becomes "loosely coupled." Some benefits of using it are:

This article is about how to use the set of open source tools listed above, not about why I choose them, nor why I need them. There are obviously a million and one different ways to deal with the producer-consumer model. The idea behind using JMX is to have a more flexible "federated" approach, so that different functions are handled by different processes.

Case Study

Consider the following hypothetical scenario. We want to share two different kinds of data, coming from two different applications and two different data sources. One application (AppSource1) uses an Oracle database and needs to share a subset of its business contact data. Another application (AppSource2) uses a Sybase database and needs to share a subset its currency data.

The data is shared at specific times:

The contact data, persisted in an interface table named IProducerContact, is managed and controlled by the application AppSource1. The currency data is persisted in an interface table, IProducerCcy, by the application AppSource2. Both AppSource1 and AppSource2 are the producers.

The producers extract the data from the interface tables IProducerContact and IProducerCcy and then send the data, in an XML format, to a JMS server. The consumer processes each incoming message, which then populates two interface tables, IConsumerContact and IConsumerCcy. Once in the interface tables, AppTarget is responsible for extracting the data from them into the master database.

Figure 1 below depicts this scenario:

Figure1
Figure 1. Flow of messages

JMX in JBoss

JMX is an optional package for J2SE that provides a standard set of interfaces and enables you to add management capabilities to your applications. It allows developers to write more modular and loosely coupled system components and reduce the complexity of large, interoperating systems.

The core component of JMX is the managed bean, or MBean. A standard MBean is a Java class that implements an interface, which has the following characteristics:

An example in JBoss is shown below:

public class ProducerManager extends ServiceMBeanSupport 
             implements ProducerManagerMBean {
   ...
   // Constructor
   // define your getter/setter method
   // define your operations
    
   public void startService() throws Exception {
       ...
   }
    
   public void stopService() throws Exception {
       ...
   }
}

where the ProducerManagerMBean interface is:

public interface ProducerManagerMBean extends ServiceMBean {
   ...
   // define your getter/setter method
   // define your "invoked" operation, if any
}

Then, you need to define an XML file service, i.e. jboss-service.xml:

<?xml version="1.0" encoding="UTF-8"?>
<server>
  <mbean code="example.jmx.ProducerManager" 
         name="nusa:service=ProducerManager">
    <attribute name="Oracle">false
    </attribute>
  </mbean>
</server>

Once you have defined your MBean and the XML file service, then you can package them into a .sar file. This service archive file is ready to be deployed into JBoss server.

Let's find out what happens when you deploy an MBean. The sequence of an MBean initialization is in this order:

When you delete or override the .sar file--or stop the JBoss server, but you may not want to do this--the stopService() method is called. This is the appropriate place to do clean-up operations, like closing all open connections or sessions.

You can define a dependency between one MBean with another MBean by using the depends tag:

<?xml version="1.0" encoding="UTF-8"?>
<server>
  <mbean code="example.jmx.ProducerManager" 
         name="nusa:service=ProducerManager">
    <attribute name="Oracle">false
    </attribute>
    <depends optional-attribute-name="MOMManager">
        nusa:service=MOMManager
    </depends>
    <depends optional-attribute-name="DBManager">
        nusa:service=DBManager
    </depends>
  </mbean>
</server>

In this example, the ProducerManager MBean depends on the MOMManager MBean and DBManager MBean.

Again, what happens if you deploy a .sar file with the above file service? Here is the sequence:

When you delete or override that .sar file, here is the sequence:

Note: Take a look at the order. When you have a dependency, the order is important.

The next thing that we need to know is how to call a method in an MBean from another MBean. Given the file service mentioned above, how the MOMManager MBean call a method in DBManager MBean? The key is to use the invoke() method, such as:

instanceofmbeanserver.invoke ( yourObjectName,
                               yourMethod,
                               params,
                               signature );

The following code is taken from the provided source:

public void processXML(String xmlMsg ,String msgType) 
        throws Exception {

  log.info ( "=== XMLConsumerCcyMDB.processXML() : 
              invoke XMLConsumer service ");
  Object[] params    = new Object[] { xmlMsg 
                                     ,msgType };
  String[] signature = new String[] { "java.lang.String" 
                                     ,"java.lang.String" };
  server.invoke(mbean, "processXML", params, signature);
}

The javax.management.MBeanServer.invoke() method is powerful and can invoke methods of any given complexity by providing details about the method parameter signature and supplying the parameter values.

Note: The yourMethod() method must be defined in your MBean interface and implemented in your MBean class.

Using Quartz in JBoss

Quartz is a job scheduler. One of the provided features is an MBean, called QuartzService, which is a standard JMX implementation in JBoss:

public class QuartzService extends ServiceMBeanSupport
             implements QuartzServiceMBean {
  // Constructor
  // Getter/setter method ( Properties and JndiName )
  // Operations
    
  public void startService() throws Exception {
    ...
    Scheduler scheduler = schedulerFactory.getScheduler();
    scheduler.start();
    ...
  }
    
  public void stopService() throws Exception {
    ...
    Scheduler scheduler = schedulerFactory.getScheduler();
    scheduler.shutdown ();
    ...
  }
}

The XML file service that you'll need for JBoss is located in this directory:

<your_install_dir>/quartz-1.4.2/src/java \
   /main/org/quartz/ee/jmx/jboss/doc-files

When you deploy that XML file service, the MBean starts the scheduler based on the properties defined in quartz.properties or within the XML files service itself, and exposes the scheduler to JNDI.

Then you can get an instance of that MBean by calling a lookup() method:

InitialContext iniCtx = new InitialContext();
scheduler = (Scheduler)iniCtx.lookup("Quartz");

Once you obtain the scheduler, then you can schedule your jobs. To schedule a job, you need to define the JobDetail and Trigger. For JobDetail(), I use JMXInvokerJob, which is a Job implementation as a JMX MBean.

The core of the JMXInvokerJob class is:

instanceofmbeanserver.invoke ( yourObjectName,
                               yourMethod,
                               params,
                               signature );

This is exactly what I mention above: invoking a method within an MBean from another MBean.

So in your code, you can have something like this:

job = new JobDetail("job1", "group1", MXInvokerJob.class);

In our scenario, the following code is used:

log.info("=== DBManager.dbScheduler() for Oracle ...");
job = new JobDetail ( "OracleJob", 
                      "OracleGroup", 
                      JMXInvokerJob.class );
job.getJobDataMap().put ( "JMX_OBJECTNAME", 
                          "nusa:service=DBManager" );
job.getJobDataMap().put ( "JMX_METHOD",
                          "processOracle" );
trigger = new CronTrigger ( "OracleTrigger", 
                            "OracleGroup", 
                            oracleCron );
    
scheduler.scheduleJob(job, trigger);

That tells the scheduler, which is an instance of QuartzService (and is an MBean), to invoke the processOracle() method, implemented in nusa:service=DBManager MBean, using a cron expression.

What Quartz does in our scenario is just scheduling. The details and nature of the job are "federated" to another MBean, called nusa:service=DBManager.

Using Hibernate in JBoss

Hibernate is an object/relational persistence mapping, and has more to do with the data layer. Just like Quartz, it provides an MBean (called HibernateService). This MBean is responsible for constructing a Hibernate SessionFactory and exposing it through JNDI.

In our scenario, the Hibernate service is defined like this in our jboss-service.xml file:

<!--
 | HibernateService MBean for Oracle
-->
<mbean code="net.sf.hibernate.jmx.HibernateService" 
          name="jboss.jca:service=ProdOracleHibernateFactory">
  <depends>jboss.jca:service=RARDeployer</depends>

  <attribute name="MapResources">
    mappings/Contact.hbm.xml
  </attribute>
        
  <attribute name="JndiName">
    java:/OracleHibernateFactory
  </attribute>
  <attribute name="Datasource">
    java:/OracleDS
  </attribute>
  <attribute name="Dialect">
    net.sf.hibernate.dialect.OracleDialect
  </attribute>
        
  <attribute name="TransactionStrategy">
    net.sf.hibernate.transaction.JTATransactionFactory
  </attribute>
  <attribute name="TransactionManagerLookupStrategy">
    net.sf.hibernate.transaction.JBossTransactionManagerLookup
  </attribute>
  <attribute name="ShowSql">true</attribute>

  <attribute name="CacheProvider">
    net.sf.hibernate.cache.TreeCacheProvider
  </attribute>

  <attribute name="UserTransactionName">
    UserTransaction
  </attribute> 
</mbean>

The ProdOracleHibernateFactory is the Hibernate MBean service that deals with the Oracle database (through the JNDI namespace java:/OracleDS), using the Contact.hbm.xml mapping file.

The next step that you have to do is to obtain an instance of SessionFactory, as follows:

jndiName = (String)server.getAttribute(your_object_name ,"JndiName");
InitialContext ctx = new InitialContext();
sessionFactory = (SessionFactory)ctx.lookup(jndiName);

Your sessionFactory is now ready to be used. For example:

session = sessionFactory.openSession();
transaction = session.beginTransaction();
...

Although Hibernate is not a Java-XML data-binding framework--as opposed to Castor, JAXB, XMLBeans, etc.--it provides the functionality to generate XML output. This is handled by the Databinder.toGenericXML() or Databinder.toXML() methods. The structure of the XML generated is different.

Given the following mapping document:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping 
    PUBLIC "-//Hibernate/Hibernate Mapping DTD 2.0//EN"
           "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">

<hibernate-mapping>

  <class name="example.mapping.ICcy" 
         table="IProducerCcy">
    <id name="ccyID" type="long" 
        column="ccy_id" unsaved-value="0">
        <generator class="native"/>
    </id>
    <property name="origCcy"       
              column="orig_ccy"
              type="string" length="3" 
              not-null="true"/>
    <property name="settledCcy"    
              column="settled_ccy"
              type="string" length="3" 
              not-null="true"/>
    <property name="effectiveFrom" 
              column="effective_from_date"
              type="date"   
              not-null="true"/>
    <property name="ROE"           
              column="rate_of_exchange"
              type="float"  
              not-null="true"/>
    <property name="effectiveTo"   
              column="effective_to_date"
              type="date"   
              not-null="true"/>
    <property name="sendStatus"    
              column="send_status"
              type="string" length="1" 
              not-null="true"/>
    <property name="sendDate"      
              column="send_date"
              type="date"   
              not-null="true"/>
  </class>

</hibernate-mapping>

The toGenericXML() method generates the following XML structure:

<?xml version="1.0" encoding="UTF-8"?>
<hibernate-generic 
        datetime="04 November 2003 12:12:00">
  <object class="ICcy" 
          package="example.mapping">
    <id name="ccyID" type="long">
        1</id>
    <property name="origCcy" type="string">
        <![CDATA[ADP]]></property>
    <property name="settledCcy" type="string">
        <![CDATA[GBP]]></property>
    <property name="effectiveFrom" type="date">
        07 October 2003</property>
    <property name="roe" type="float">
        211.46</property>
    <property name="effectiveTo" type="date"/>
    <property name="sendStatus" type="string">
        <![CDATA[]]></property>
  </object>
  <object class="ICcy" 
          package="example.mapping">
    <id name="ccyID" type="long">
        2</id>
    <property name="origCcy" type="string">
        <![CDATA[AED]]></property>
    <property name="settledCcy" type="string">
        <![CDATA[GBP]]></property>
    <property name="effectiveFrom" type="date">
        07 October 2003</property>
    <property name="ROE" type="float">
        6.1241</property>
    <property name="effectiveTo" type="date"/>
    <property name="sendStatus" type="string">
        <![CDATA[]]></property>
  </object>
</hibernate-generic>

whereas the toXML() method generates this structure:

<?xml version="1.0" encoding="UTF-8"?>
<hibernate-custom 
        datetime="12 November 2003 11:47:28">
  <ICcy id="N10004">
    <ccyID>1</ccyID>
    <origCcy>ADP</origCcy>
    <settledCcy>GBP</settledCcy>
    <effectiveFrom>07 October 2003
    </effectiveFrom>
    <ROE>211.46</ROE>
    <effectiveTo>31 December 2999
    </effectiveTo>
    <sendStatus> </sendStatus>
    <sendDate/>
  </ICcy>
  <ICcy id="N10026">
    <ccyID>2</ccyID>
    <origCcy>AED</origCcy>
    <settledCcy>GBP</settledCcy>
    <effectiveFrom>07 October 2003
    </effectiveFrom>
    <ROE>6.1241</ROE>
    <effectiveTo>31 December 2999
    </effectiveTo>
    <sendStatus> </sendStatus>
    <sendDate/>
  </ICcy>
</hibernate-custom>

Using Castor in JBoss

Castor is one of the data-binding frameworks for Java. At the time of this writing, there is no JMX implementation for Castor (yet).

In order to use it, we just follow the instructions from the Castor home page:

Note: Although JBoss 4.0.0 provides a Castor .jar file (castor-0.9.5-xml.jar), you need the complete .jar (castor-0.9.5.3.jar). This is needed to handle a date field.

Design Pattern for the Producer

The figure below depicts the design pattern for the producer:

Figure2
Figure 2. Design pattern for the producer

Based on the following cron expression for Oracle:

<attribute name="OracleCron">
  <![CDATA[0 * 08-18 * * ?]]>
</attribute>

QuartzService schedules HibernateService to retrieve data from IProducerContact using the HSQL SELECT statement:

<attribute name="OracleQuery">
  <![CDATA[FROM IContact as contact 
   WHERE contact.sendStatus <> 'S']]>
</attribute>

Note: IContact here is the name of the persistence class, and not the name of the interface table.

HibernateService, armed with the mapping files, unmarshalls the data retrieved to construct the Java objects. These objects are passed on to the Databinder.toGenericXML() or Databinder.toXML() methods to generate XML output.

The XML output is then transformed by XSLT. This new XML format is sent to the JMS provider, such as JBossMQ or WebSphere MQ.

To help you understand the provided code, Figure 3 shows the class diagram:

Figure3
Figure 3. XMLProducer's class diagram

Figure 4 is the sequence diagram:

Figure4
Figure 4. XMLProducer's sequence diagram

Design Pattern for the Consumer

Figure 5 depicts the design pattern for the consumer:

Figure 5
Figure 5. Design Pattern for the consumer

The consumer has two components, XMLConsumer.jar and XMLConsumerService.sar. The first one is the MDB component, and the second one is the business process. There are two types of incoming messages: Contact messages and Ccy messages. These will be consumed by ContactMDB or CcyMDB, respectively. There's one MDB for one type of message. This allows a concurrency process. Each MDB will then delegate the process to an MBean, called XMLConsumerService.

When an XML message is consumed, Castor marshals that message to a Java object. Then Hibernate will take over this Java object to store the data into the interface tables IConsumerContact and IConsumerCcy.

Once the data in those interface tables, it's up to AppTarget to put the data into the master database.

To help you understand the provided code, Figure 6 shows a class diagram:

Figure6
Figure 6. XMLConsumer's class diagram

and Figure 7 shows a sequence diagram:

Figure7
Figure 7. XMLConsumer's sequence diagram

Deployment in JBoss

The provided code for this article has been developed under Windows and Linux using:

The structure of the project file is shown in Figure 8.

Figure 8
Figure 8. Eclipse project structure

Before building the XMLConsumer and XMLProducer, you need to copy the needed .jars--the Sybase JDBC driver, the Oracle JDBC driver, Quartz, and Castor--into /server/<deploy_config>/lib.

If you use WebSphere MQ, add the following into your standardjboss.xml file:

<invoker-proxy-binding>
  <name>wsmq-message-driven-bean</name>
  <invoker-mbean>whatever</invoker-mbean>
  <proxy-factory>
     org.jboss.ejb.plugins.jms.JMSContainerInvoker
  </proxy-factory>
  <proxy-factory-config>
    <JMSProviderAdapterJNDI>WSMQProvider
    </JMSProviderAdapterJNDI>
    <ServerSessionPoolFactoryJNDI>StdJMSPool
    </ServerSessionPoolFactoryJNDI>
    <MaximumSize>15</MaximumSize>
    <MaxMessages>1</MaxMessages>
    <MDBConfig>
      <ReconnectIntervalSec>10
      </ReconnectIntervalSec>
      <DLQConfig>
        <!-- Use this for Windows 
             file system JNDI namespace -->
        <DestinationQueue>queue\\DLQ
        </DestinationQueue>
        <!-- Use this for Linux 
             file system JNDI namespace -->
        <!--DestinationQueue>queue/DLQ
        </DestinationQueue-->
        <MaxTimesRedelivered>10
        </MaxTimesRedelivered>
        <TimeToLive>0</TimeToLive>
      </DLQConfig>
    </MDBConfig>
  </proxy-factory-config>
</invoker-proxy-binding>

<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">
        org.jboss.ejb.plugins.CallValidationInterceptor
    </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">
        org.jboss.ejb.plugins.CallValidationInterceptor
    </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>

Add the following into your jms-ds.xml file:

<mbean code="org.jboss.jms.jndi.JMSProviderLoader" 
      name="jboss.mq:service=JMSProviderLoader,
            name=WSMQProvider">
  <attribute name="ProviderName">WSMQProvider
  </attribute>
  <attribute name="ProviderAdapterClass">
    org.jboss.jms.jndi.JNDIProviderAdapter
  </attribute>
  <attribute name="QueueFactoryRef">
    QCFProducer
  </attribute>
  <attribute name="TopicFactoryRef">
    QCFProducer
  </attribute>
  <attribute name="Properties">
java.naming.factory.initial=com.sun.jndi.fscontext.RefFSContextFactory
java.naming.provider.url=file:/your-file-system-JNDI-namespace-directory
  </attribute>
</mbean>

Add the following into your META-INF/jboss.xml file:

<jboss>
  <enterprise-beans>
    <message-driven>
      <ejb-name>XMLConsumerCcyMDB
      </ejb-name>
 
      <!-- JBossMQ only -->
      <!--
      <configuration-name>
        Standard Message Driven Bean
      </configuration-name>
      <destination-jndi-name>queue/QProducer
      </destination-jndi-name>
      -->
     
      <!-- WSMQ only -->
      <configuration-name>
        WSMQ Message Driven Bean
      </configuration-name>
      <!-- Use this for Windows 
          file system JNDI namespace -->
      <destination-jndi-name>queue\\QProducer
      </destination-jndi-name> 
      <!-- Use this for Linux 
          file system JNDI namespace -->
      <!--destination-jndi-name>queue/QProducer
      </destination-jndi-name--> 
      <invoker-bindings>
        <invoker>
          <invoker-proxy-binding-name>
            wsmq-message-driven-bean
          </invoker-proxy-binding-name>
        </invoker>
      </invoker-bindings>
    </message-driven>
    ...
  </enterprise-beans>
</jboss>

Make sure you've set up the env variables as explained in pages 10-12 of the manual WebSphere MQ: Using Java (PDF). Run the following scripts:

  1. Setup/WSMQ/setupMQ.bat or Setup/WSMQ/setupMQ.sh
  2. Setup/WSMQ_FileSystemJNDI/setupJMS.bat or Setup/WSMQ_FileSystemJNDI/setupJMS.sh (amend the file system JNDI namespace directory)

Optionally, you can test if the queue created is available by running the SendAndReceive program (modify it if needed).

Note: If you define a subcontext when creating a queue within a file system JNDI namespace, the process to get the queue name is different under Windows and Linux:

If you use JBossMQ, add the following to your jbossmq-destinations-service.xml file:

<mbean code="org.jboss.mq.server.jmx.Queue" 
      name="jboss.mq.destination:service=Queue,name=QProducer">
  <depends optional-attribute-name="DestinationManager">
          jboss.mq:service=DestinationManager
  </depends>
</mbean>

Steps to deploy XMLConsumer:

  1. Run the SQL set-up script, located in XMLMessaging/Setup, against the appropriate database to create the interface tables (IConsumerSybase.sql if you use Sybase for the interface tables, or IConsumserOracle.sql for Oracle).
  2. From Eclipse, configure XMLConsumer/build.properties.
  3. From Eclipse, configure XMLConsumer/src/resources/META-INF/jboss-service.xml to choose which Hibernate service you are going to use.
  4. From Eclipse, configure XMLConsumer/src/resources/META-INF/jboss.xml to choose either JBossMQ or WebSphere MQ.
  5. Build XMLConsumer to produce XMLConsumerMDB.jar and XMLConsumerService.sar.
  6. Check your JBoss server.log file (the queue QProducer must be bound).

Here is a snapshot of server.log (after formatting the line) when the consumer processes an incoming message:

14:00:00,349 INFO  [example.mdb.XMLConsumerCcyMDB] \
   === XMLConsumerCcyMDB.processXML() : invoke XMLConsumer service for Ccy
...
14:00:00,349 INFO  [example.jmx.ConsumerManager] \
   === ConsumerManager.processXML() : \
<?xml version="1.0" encoding="UTF-8"?>
<CCY>
  <CCY_DATA>
    <ORIG_CCY>EUR</ORIG_CCY>
    <SETTLED_CCY>GBP</SETTLED_CCY>
    <EFFECTIVE_FROM>07 October 2003
    </EFFECTIVE_FROM>
    <ROE>1.4339</ROE>
    <EFFECTIVE_TO>31 December 2999
    </EFFECTIVE_TO>
  </CCY_DATA>
  <CCY_DATA>
    <ORIG_CCY>USD</ORIG_CCY>
    <SETTLED_CCY>GBP</SETTLED_CCY>
    <EFFECTIVE_FROM>07 October 2003
    </EFFECTIVE_FROM>
    <ROE>1.6674</ROE>
    <EFFECTIVE_TO>31 December 2999
    </EFFECTIVE_TO>
  </CCY_DATA>
</CCY>

14:00:00,349 INFO  [example.jmx.ConsumerManager] \
   === ConsumerManager.beginTransaction()
14:00:00,349 INFO  [example.jmx.ConsumerManager] \
   === ConsumerManager.updateDataCcy() : load mapping for Castor ... 
14:00:00,600 DEBUG [example.jmx.ConsumerManager] \
   === ConsumerManager.updateDataCcy() : Hibernate\ 
   insert this : (EUR,GBP,Tue Oct 07 00:00:00 BST 2003,\
   1.4339,Tue Dec 31 00:00:00 GMT 2999) 
14:00:00,600 INFO  [STDOUT] Hibernate: \
   insert into IConsumerCcy (orig_ccy, \
   settled_ccy,  effective_from, roe, \
   effective_to) values (?, ?, ?, ?, ?)
select @@identity
14:00:00,600 DEBUG [example.jmx.ConsumerManager] \
   === ConsumerManager.updateDataCcy() : Hibernate \
   insert this : (USD,GBP,Tue Oct 07 00:00:00 BST 2003,\
   1.6674,Tue Dec 31 00:00:00 GMT 2999) 
14:00:00,600 INFO  [STDOUT] Hibernate: \
   insert into IConsumerCcy (orig_ccy, \
   settled_ccy, effective_from, roe, \
   effective_to) values (?, ?, ?, ?, ?)
select @@identity
14:00:00,610 INFO  [example.jmx.ConsumerManager] \
   === ConsumerManager.endTransaction() : Commit 
14:00:00,610 INFO  [example.jmx.ConsumerManager] \
   === ConsumerManager.endSession()

Steps to deploy XMLProducer:

  1. Run the SQL set-up scripts, located in XMLMessaging/Setup, against the appropriate databases to create the interface tables (IProducerSybase.sql and IProducerOracle.sql) and tables needed by Quartz (Quartz-sybase.sql or Quartz-oracle.sql).
  2. Configure the JBoss oracle-ds.xml and sybase-ds.xml files and deploy them.
  3. Configure XMLMessaging/Setup/quartz-service.xml, then deploy it.
  4. At this point, check your JBoss server.log file too see if a problem has been encountered.
  5. From Eclipse, configure XMLProducer/src/resources/META-INF/jboss-service.xml where appropriate (JBossMQ or WebSphere MQ, and the cron expression used by Quartz).
  6. From Eclipse, configure XMLProducer/build.properties, and then build the XMLProducer to produce XMLProducer.sar.
  7. Recheck your JBoss server.log file if you encounter problems.

Here is a snapshot of server.log (after formatting the line) when the producer sends a message:

14:00:00,199 INFO  [example.db.SybaseMessaging] \
   === SybaseMessaging.sendMsg()
14:00:00,199 INFO  [example.jmx.MOMManager] \
   === MOMManager.enqueue() 
14:00:00,199 INFO  [example.jmx.JBossMQManager] \
   === JBossMQManager.enqueue()
14:00:00,209 DEBUG [org.jboss.resource.adapter.\
   jdbc.local.LocalManagedConnectionFactory] \
   Using properties: {user=baa, password=--hidden--}
14:00:00,269 INFO  [example.jmx.JBossMQManager] \
   === Message sent : \
<?xml version="1.0" encoding="UTF-8"?>
<CCY>
  <CCY_DATA>
    <ORIG_CCY>EUR</ORIG_CCY>
    <SETTLED_CCY>GBP</SETTLED_CCY>
    <EFFECTIVE_FROM>07 October 2003
    </EFFECTIVE_FROM>
    <ROE>1.4339</ROE>
    <EFFECTIVE_TO>31 December 2999
    </EFFECTIVE_TO>
  </CCY_DATA>
  <CCY_DATA>
    <ORIG_CCY>USD</ORIG_CCY>
    <SETTLED_CCY>GBP</SETTLED_CCY>
    <EFFECTIVE_FROM>07 October 2003
    </EFFECTIVE_FROM>
    <ROE>1.6674</ROE>
    <EFFECTIVE_TO>31 December 2999
    </EFFECTIVE_TO>
  </CCY_DATA>
</CCY>

14:00:00,279 INFO  [example.db.SybaseMessaging] \
   === SybaseMessaging.updateData()
14:00:00,279 INFO  [STDOUT] \
   Hibernate: select iccy0_.ccy_id as x0_0_ \
   from IProducerCcy iccy0_ \
   where (iccy0_.send_status<>'S' )
14:00:00,289 INFO  [example.db.DBMessaging] \
   === DBMessaging.endTransaction() : Commit 
14:00:00,289 INFO  [STDOUT] \
   Hibernate: update IProducerCcy set orig_ccy=?,\
   settled_ccy=?, effective_from_date=?, \
   rate_of_exchange=?, effective_to_date=?, \
   send_status=?, send_date=? where ccy_id=?
14:00:00,299 INFO  [example.db.DBMessaging] \
   === DBMessaging.endSession()

Conclusion

This article was about how to use JBoss, Quartz, Hibernate, Castor, and WebSphere MQ to build a simple XML messaging system. Please consult the forums, Wikis, tutorials, docs, etc. in the Reference section below if you want to find out more about the components used.

Reference

Benoit Aumars Benoit Aumars has 14 years' experience in IT, specializing in Java and J2EE. His major interests include design patterns, XML-related technologies, EAI, web services, and SOA. He is currently working with Jardine Lloyd Thompson in London.


Return to ONJava.com.

Copyright © 2009 O'Reilly Media, Inc.