Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to configure ActiveMQ JCA connector in JBoss to use XA connections?

On JBoss 5.1.0 I have Datasource (PostgreSQL 8.3.11) configured using *-ds.xml (standard jboss DS). It uses XADataSource (PGXADataSource). I also have ActiveMQ broker (right now it runs as in-VM, under JBoss, but it will be on separate server latter).

What I want to do is to make ActiveMQ Connection Factory and Datasource to participate in XA Transactions. For example, I want to update DB record and send a JMS message as a UOW. You get the idea.

I configured PGXADataSource in my-pg-ds.xml and it works (I can trace execution all the way to PGXAConnection's start method). I have tried to configure ActiveMQXAConnectionFactory directly in Spring (I am using Spring 3.0.2.RELEASE), but this does not work, because in this case Spring transaction manager (I use annotation to let Spring configure JtaTransactionManager which simply delegates all the work to Jboss transaction manager) does not enlist XAResource for given ActiveMQXAConnection. Whenever I try to send a message I get an exception JMSException saying "Session's XAResource has not been enlisted in a distributed transaction." thrown from ActiveMQXASession.

Since that did not work, I have switched to JCA configuration of ActiveMQ ConnectionFactory (based on this document) and it works for regular ConnectionFactory, but I do not understand how can I configure it to use XAConnectionFactory. It seems like Resource Adapter simply does not have proper ManagedConnectionFactory, ManagedConnection, etc. implementations for XA connection factory.

Am I missing something or do I have no choice but to write XA wrappers for resource adapter?

like image 330
Georgy Bolyuba Avatar asked Jul 02 '10 17:07

Georgy Bolyuba


1 Answers

Ok, I found the solution. Jboss includes JCA connector for any JMS factory (supports both types of transactions: XA and local). It is located in /server//deploy/jms-ra.rar. Here is how I configured it.

First, activemq-jms-ds.xml file that goes into deploy directory next to jms-ra.rar:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE connection-factories
    PUBLIC "-//JBoss//DTD JBOSS JCA Config 1.5//EN"
    "http://www.jboss.org/j2ee/dtd/jboss-ds_1_5.dtd">

<connection-factories>
    <mbean code="org.jboss.jms.jndi.JMSProviderLoader"
       name="jboss.messaging:service=JMSProviderLoader,name=ActiveMQJMSProvider">
        <attribute name="ProviderName">ActiveMQJMSProvider</attribute>
        <attribute name="ProviderAdapterClass">org.jboss.jms.jndi.JNDIProviderAdapter</attribute>
        <attribute name="FactoryRef">java:/activemq/XAConnectionFactory</attribute>
        <attribute name="QueueFactoryRef">java:/activemq/XAConnectionFactory</attribute>
        <attribute name="TopicFactoryRef">java:/activemq/XAConnectionFactory</attribute>
    </mbean>

    <tx-connection-factory>
        <jndi-name>JmsXAConnectionFactory</jndi-name>
        <xa-transaction/>
        <rar-name>jms-ra.rar</rar-name>
        <connection-definition>org.jboss.resource.adapter.jms.JmsConnectionFactory</connection-definition>
        <config-property name="JmsProviderAdapterJNDI" type="java.lang.String">java:/ActiveMQJMSProvider</config-property>
    </tx-connection-factory>
</connection-factories>

This tells Jboss to look into jms-ra.rar and find adapter that can provide managed connection factory for org.jboss.resource.adapter.jms.JmsConnectionFactory. Internally jms adapter depends on JmsProviderAdapter, which is used to store JNDI names of connection factories (in my config all names are the same).

I use mbean tag to configure JMSProviderLoader (this is copied from one of internal JBoss configs). Now, all I have to do is somehow create an instance of my XA connection factory and bind it to java:/activemq/XAConnectionFactory. There are several ways to do it (implement MBean wrapper, for example).

Since I am Jboss 5 I used microcontainer (which is likely to work in Jboss 6). I added activemq-jms-jboss-beans.xml file into deployersdirecotry:

<?xml version="1.0" encoding="UTF-8"?>
<deployment xmlns="urn:jboss:bean-deployer:2.0">
    <!-- Define a Jndi binding aspect/annotation that exposes beans via jndi
        when they are registered with the kernel.
    -->
    <aop:lifecycle-configure xmlns:aop="urn:jboss:aop-beans:1.0"
        name="DependencyAdvice"
        class="org.jboss.aop.microcontainer.aspects.jndi.JndiLifecycleCallback"
        classes="@org.jboss.aop.microcontainer.aspects.jndi.JndiBinding"
        manager-bean="AspectManager"
        manager-property="aspectManager">
    </aop:lifecycle-configure>

    <bean name="ActiveMQXAConnectionFactory" class="org.apache.activemq.ActiveMQXAConnectionFactory">
        <annotation>@org.jboss.aop.microcontainer.aspects.jndi.JndiBinding(name="activemq/XAConnectionFactory", aliases={"java:/activemq/XAConnectionFactory"})</annotation>
        <property name="brokerURL">vm://localhost</property>
    </bean>
</deployment>

I create a ActiveMQXAConnectionFactory bean. To bind it to JNDI, I annotate it with JndiBinding annotation. For this annotation to work, we need JndiLifecycleCallback. As far as I can tell, JndiLifecycleCallback is called on every bean created by microcontainer and checks for JndiBinding annotation on that bean.

like image 87
Georgy Bolyuba Avatar answered Nov 15 '22 03:11

Georgy Bolyuba