Trying to set up a JMS bridge between HornetQ and Oracle AQPosted: 07/08/2012
I’ve been interested in setting up a JMS bridge between HornetQ and Oracle Advanced Queuing (AQ) for a while, even I wrote about this issue in a response to a question of the HornetQ user’s forum. I resume the matter in this post with some improvements, although, to be perfectly honest, I am not happy with the configuration.
In theory, the bridge is possible because both systems are JMS 1.1. compliant. If you use a HornetQ 2.2.5 standalone installation, you simply have to add some managed beans to the hornetq-beans.xml file, as is described in the user manual. The key point here is to use the proper connection factory and set up the destination following the rules of the Weblogic manual: “Interoperating with Oracle AQ JMS”. Here you have an excerpt of the hornetq-beans.xml file:
<bean name="JMSBridge"> <constructor> ... <!-- concatenate JMS messageID to the target's message header --> <parameter>true</parameter> ... </bean> ... <!-- TargetCFF describes the ConnectionFactory used to connect to the target destination --> <bean name="TargetCFF"> <constructor> <parameter> <inject bean="TargetJNDI" /> </parameter> <parameter>QueueConnectionFactory</parameter> </constructor> </bean> ... <!-- TargetDestinationFactory describes the Destination used as the target --> <bean name="TargetDestinationFactory"> <constructor> <parameter> <inject bean="TargetJNDI" /> </parameter> <parameter>Queues/testQueue</parameter> </constructor> </bean> ... <!-- JNDI is a Hashtable containing the JNDI properties required --> <!-- to connect to the *target* JMS resources --> <bean name="TargetJNDI"> <constructor> <map keyClass="java.lang.String" valueClass="java.lang.String"> <entry> <key>java.naming.factory.initial</key> <value>oracle.jms.AQjmsInitialContextFactory</value> </entry> <entry> <key>db_url</key> <value>jdbc:oracle:thin:@oraclehost:1521:test</value> </entry> <entry> <key>java.naming.security.principal</key> <value>test</value> </entry> <entry> <key>java.naming.security.credentials</key> <value>secret</value> </entry> </map> </constructor> </bean>
The former configuration runs properly with the DUPLICATES_OK quality of service, but when you moves to a ONCE_AND_ONLY_ONCE one, changing the TargetCFF to XAQueueConnectionFactory, you get a NullPointerException:
oracle.jms.AQjmsException: Error creating the db_connection at oracle.jms.AQjmsDBConnMgr.getConnection at oracle.jms.AQjmsDBConnMgr. at oracle.jms.AQjmsXAConnection. at oracle.jms.AQjmsXAQueueConnectionFactory.createAllXAConnection at oracle.jms.AQjmsXAQueueConnectionFactory.createXAQueueConnection
After a research on Oracle Support notes, I found that the Oracle API has a bug (10102373) that it will be fixed on the future release 12.1 There are workarounds for not receiving duplicate messages, for example: to save the JMS Message ID in a Oracle table and implement a unique index on that column, you have to take into account that the JMS Messege ID sent by HornetQ is a property that can be recovered with the method get_string_property of the Oracle type aq$_jms_message.
But, why Weblogic can interoperate with Oracle AQ in XA configurations? I suspect that the reason is because uses datasources with pre-created database connections. So, I’ve tried to deploy the bridge within JBoss AS 6.0.0, with HornetQ as its default JMS provider. First of all, I created an Oracle XA datasource. After that, I set up the configuration file jms-bridge-jboss-beans.xml in a similar way as before, the only change is to change the db_url parameter by the parameter datasource:
<bean name="TargetJNDI"> <constructor> <map keyClass="java.lang.String" valueClass="java.lang.String"> <entry> <key>java.naming.factory.initial</key> <value>oracle.jms.AQjmsInitialContextFactory</value> </entry> <entry> <key>datasource</key> <value>jdbc/testOracleXA</value> </entry> </map> </constructor> </bean>
But, when I tried to run the former configuration, the bridge didn’t start. Another bug, a HornetQ one in this case, prevented me from reaching my goal. To be perfectly honest, I didn’t patch the installation, but I restart the managed bean through the JMX console and I got another exception, a ClassCastException, because JBoss pass a wrapped Oracle connection to the class AQjmsInitialContextFactory, which is waiting for an native internal connection. I really felt powerless!
Finally, I adopted the solution of setting up an Oracle Weblogic server as a mediator, the guru Edwin Biemond describes this configuration in this article.