camel-users mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Daniel Pocock <dan...@pocock.pro>
Subject Camel distributed transactions/XA without full J2EE container?
Date Thu, 04 Dec 2014 15:41:22 GMT


The wiki explains the general concepts around transactions and
distributed transactions.

Running Camel as a standalone J2SE Spring application (using
org.apache.camel.spring.Main) is a popular choice for people who don't
want to use a container and I've worked on several projects where this
type of deployment was desired.

In this scenario, without a container, Camel transactions involving a
single JMS or JDBC provider work using the transaction capabilities of
the provider.

When a distributed transaction is required, Camel relies on Spring's
transaction support and Spring seems to need some standalone JTA
implementation.  This is where it gets tricky.

I managed to get this working on one project using JOTM but it is
awkward (reasons below) and I'm just wondering if there is an example of
a solution that is simpler to implement.

The JOTM-based solution involved the following:
- add xbean-spring, JOTM and Carol JARs to classpath
- include JotmFactoryBean and JtaPersistenceUnitPostProcessor classes in
the project (I don't recall why this was necessary as it appears to
exist in Spring too)
- in the jndi.xml for xbean-spring, create the java:comp/UserTransaction
in JNDI using JotmFactoryBean:

                <entry key="java:comp/UserTransaction">
                    <bean class="org.example.util.JotmFactoryBean" />
                </entry>

   and also create JNDI entries for each DataSource using the bean class
com.mchange.v2.c3p0.ComboPooledDataSource:

               <entry key="java:comp/env/jdbc/testDS">
                    <bean
class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
                        <property name="driverClass"
value="com.mysql.jdbc.Driver" />
                        <property name="jdbcUrl"
value="jdbc:mysql://localhost:3306/test1" />
                        <property name="user" value="test" />
                        <property name="password" value="123" />
                        <property name="maxIdleTime" value="1800" />
                    </bean>
                </entry>

- in the camelContext.xml, create spring beans using the above objects
from JNDI:

    <bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate">
        <property name="environment">
            <props>
                <prop
key="java.naming.factory.initial">org.apache.xbean.spring.jndi.SpringInitialContextFactory</prop>
                <prop
key="java.naming.provider.url">classpath:jndi.xml</prop>
            </props>
        </property>
    </bean>

    <bean id="testDataSource"
class="org.springframework.jndi.JndiObjectFactoryBean" autowire="no">
        <property name="jndiName" value="java:comp/env/jdbc/testDS" />
        <property name="jndiTemplate" ref="jndiTemplate" />
    </bean>

    <bean id="myPersistenceProvider"
class="org.hibernate.ejb.HibernatePersistence" />

    <bean id="pu-test"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="persistenceXmlLocation"
value="classpath:META-INF/persistence-test.xml" />
        <property name="persistenceUnitName" value="pu-test" />
        <property name="dataSource" ref="testDataSource" />
        <property name="persistenceProvider" ref="myPersistenceProvider" />
        <property name="persistenceUnitPostProcessors">
            <bean class="org.example.util.JtaPersistenceUnitPostProcessor">
                <property name="jtaDataSource" ref="testDataSource" />
            </bean>
        </property>
        <property name="jpaVendorAdapter">
            <bean
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="showSql" value="true" />
                <property name="database" value="MYSQL" />
            </bean>
        </property>
        <property name="jpaProperties">
            <props>
                <prop
key="hibernate.transaction.factory_class">org.hibernate.transaction.JTATransactionFactory</prop>
                <prop
key="hibernate.transaction.manager_lookup_class">org.hibernate.transaction.JOTMTransactionManagerLookup</prop>
                <prop
key="jta.UserTransaction">java:comp/UserTransaction</prop>
            </props>
        </property>
    </bean>

    <bean id="jotmTransactionManager"
class="org.springframework.transaction.jta.JtaTransactionManager">
        <property name="userTransaction">
            <jee:jndi-lookup jndi-name="java:comp/UserTransaction"/>
        </property>
    </bean>

    <bean id="PROPAGATION_REQUIRED"
class="org.apache.camel.spring.spi.SpringTransactionPolicy">
        <property name="transactionManager" ref="jotmTransactionManager" />
        <property name="propagationBehaviorName"
value="PROPAGATION_REQUIRED" />
    </bean>
   
    <bean
class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"
/>

    <tx:annotation-driven transaction-manager="jotmTransactionManager" />

    <bean id="jpa" class="org.apache.camel.component.jpa.JpaComponent">
        <property name="entityManagerFactory" ref="pu-test" />
        <property name="transactionManager" ref="jotmTransactionManager" />
    </bean>


- and also add ActiveMQ with JTA support:

    <bean id="activemq"
class="org.apache.activemq.camel.component.ActiveMQComponent">
        <property name="brokerURL" value="tcp://localhost:61616" />
        <property name="transactionManager" ref="jotmTransactionManager" />
    </bean>


The good news is, this seems to work without any container.

The bad news is, it is not trivial to create the runtime classpath.
- I have a working, hand-crafted runtime classpath based on the specific
project concerned.
- when I tried to put new Camel JARs in the classpath (not changing
anything else or the order of the JARS), JOTM would fail to initialize,
exceptions from Carol
- when I tried to build the project with Maven (it was originally built
with Ant), I can run it successfully with:

       mvn exec:java -Dexec.mainClass=org.apache.camel.spring.Main

   but if I try to run the JVM from a script using the classpath
generated by

       mvn dependency:build-classpath

    then JOTM fails to initialize, once again, giving exceptions from
the Carol JAR

My overall impression is that this is quite tedious to get it right and
can break easily when more dependencies are added, especially if Maven
is helping.  Does anybody have a suggestion or examples, possibly using
another transaction manager?



Mime
View raw message