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?