This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new 1110e5e CAMEL-16861: Cleanup and update EIP docs
1110e5e is described below
commit 1110e5e930ac234c14cc4302cf61f2053a59b101
Author: Claus Ibsen <[email protected]>
AuthorDate: Mon Oct 25 17:12:31 2021 +0200
CAMEL-16861: Cleanup and update EIP docs
---
.../modules/eips/pages/transactional-client.adoc | 177 ++++++++++++++++-----
1 file changed, 136 insertions(+), 41 deletions(-)
diff --git
a/core/camel-core-engine/src/main/docs/modules/eips/pages/transactional-client.adoc
b/core/camel-core-engine/src/main/docs/modules/eips/pages/transactional-client.adoc
index 7026c9e..5a9e2a2 100644
---
a/core/camel-core-engine/src/main/docs/modules/eips/pages/transactional-client.adoc
+++
b/core/camel-core-engine/src/main/docs/modules/eips/pages/transactional-client.adoc
@@ -69,7 +69,7 @@ TODO:
=== About Spring Transactions
-Camel uses Spring Transaction to manage transactions via its
`TransactionManager`
+Camel uses Spring Transaction (`camel-spring`) to manage transactions via its
`TransactionManager`
API. Depending on the kinds of resources that are taking part in the
transaction,
an appropriate implementation of the transaction manager must be chosen. Spring
offers a number of transaction managers out of the box that work for various
local
@@ -81,70 +81,124 @@ API abstract that Camel uses.
=== About JTA Transactions
TODO:
+TODO: (`camel-jta`)
== Using Transactions in Camel
-TODO:
+In Camel, transactions are used by:
+
+. Setting up transaction manager via either Spring Transactions or JTA
Transactions.
+. Marking routes as transacted
+. Using different transaction propagations for rare use-cases
+
+You will later in the two transactional examples further below, see how to set
up transaction manager in Camel.
+
+=== Marking a route as transacted
+
+When using transactions (JTA or Spring Transaction) in Camel then you enable
this on routes by using `transacted`
+right after `from` in the routes.
+
+For example in Java that would be:
+
+[source,java]
+----
+from("jms:cheese")
+ .transacted()
+ .to("bean:foo");
+----
+
+And in XML:
+
+[source,xml]
+----
+<route>
+ <from uri="jms:cheese"/>
+ <transacted/>
+ <to uri="bean:foo"/>
+</route>
+----
+
+When you specify `<transacted/>` in a route, Camel uses transactions for that
particular
+route and any other routes that the message may undertake.
+
+When a route is specified as `<transacted/>`, then under the hood Camel looks
up
+the Spring/JTA transaction manager and uses it. This is convention over
configuration.
-=== Transaction error handler
+The convention over configuration applies only when you have a single
Spring/JTA transaction
+manager configured. In more complex scenarios, where you either use multiple
+transaction managers or transaction propagation policies, you have to do
additional
+configuration.
-When a route is marked as transacted using ``<transacted/>` Camel will
-automatically use `TransactionErrorHandler` as the
-xref:latest@manual:ROOT:error-handler.adoc[Error Handler].
+=== Using different transaction propagations
-This error handler supports basically the same
-feature set as the
xref:latest@manual:ROOT:defaulterrorhandler.adoc[DefaultErrorHandler].
+In some rare situations, you may need to use multiple transactions with the
same exchange.
-=== Transaction Policies
+For example an exchange starts off using `PROPAGATION_REQUIRED`, and then you
need
+to use another transaction that’s independent of the existing transaction. You
can
+do this by using PROPAGATION_REQUIRES_NEW, which will start a new transaction.
-Outbound endpoints will automatically enlist in the current transaction
-context. But what if you do not want your outbound endpoint to enlist in
-the same transaction as your inbound endpoint? The solution is to add a
-Transaction Policy to the processing route. You first have to define
-transaction policies that you will be using. The policies use a spring
-TransactionTemplate under the covers for declaring the transaction
-demarcation to use. So you will need to add something like the following
-to your spring xml:
+NOTE: In Camel a route can only have exactly one transaction policy, which
means, that if
+you need to change transaction propagation, then you must use a new route.
+
+When the exchange completes, the transaction manager will issue commits
+or rollbacks to these two transactions, which ensures that they both complete
at
+the same time. Because two transaction legs are in play, they can have
different
+outcomes; for example, transaction 1 can roll back, while transaction 2
commits,
+and vice versa.
+
+In Camel, you need to configure the propagations using
`SpringTransactionPolicy`
+as shown in the following XML snippets:
[source,xml]
----
-<bean id="PROPAGATION_REQUIRED"
class="org.apache.camel.spring.spi.SpringTransactionPolicy">
+<bean id="txRequired"
class="org.apache.camel.spring.spi.SpringTransactionPolicy">
<property name="transactionManager" ref="jmsTransactionManager"/>
</bean>
-<bean id="PROPAGATION_REQUIRES_NEW"
class="org.apache.camel.spring.spi.SpringTransactionPolicy">
+<bean id="txRequiresNew"
class="org.apache.camel.spring.spi.SpringTransactionPolicy">
<property name="transactionManager" ref="jmsTransactionManager"/>
<property name="propagationBehaviorName" value="PROPAGATION_REQUIRES_NEW"/>
</bean>
+
+<bean id="txMandatory"
class="org.apache.camel.spring.spi.SpringTransactionPolicy">
+ <property name="transactionManager" ref="jmsTransactionManager"/>
+ <property name="propagationBehaviorName"
value="PROPAGATION_REQUIRES_MANDATORY"/>
+</bean>
----
-Then in your
-https://www.javadoc.io/doc/org.apache.camel/camel-spring/current/org/apache/camel/spring/SpringRouteBuilder.html[SpringRouteBuilder],
-you just need to create new SpringTransactionPolicy objects for each of
-the templates.
+Then we have routes, where each of the route use their different policy:
-[source,java]
-----
-public void configure() {
- ...
- Policy required = bean(SpringTransactionPolicy.class,
"PROPAGATION_REQUIRED"));
- Policy requirenew = bean(SpringTransactionPolicy.class,
"PROPAGATION_REQUIRES_NEW"));
- ...
-}
+[source,xml]
----
+<camelContext xmlns="http://camel.apache.org/schema/spring">
+ <route>
+ <from uri="activemq:queue:inbox"/>
+ <transacted ref="txRequired"/>
+ <to uri="direct:audit"/>
+ <to uri="direct:order"/>
+ <to uri="activemq:queue:order"/>
+ </route>
-Once created, you can use the Policy objects in your processing routes:
+ <route>
+ <from uri="direct:audit"/>
+ <transacted ref="txRequiresNew"/>
+ <bean ref="auditLogService" method="insertAuditLog"/>
+ </route>
-[source,java]
+ <route>
+ <from uri="direct:order"/>
+ <transacted ref="txMandatory"/>
+ <bean ref="orderService" method="insertOrder"/>
+ </route>
+</camelContext>
----
-// Send to bar in a new transaction
-from("activemq:queue:foo").policy(requirenew)
- .to("activemq:queue:bar");
-// Send to bar without a transaction.
-from("activemq:queue:foo").policy(notsupported)
- .to("activemq:queue:bar");
-----
+Notice how the ref attribute on `<transacted>` refers to the corresponding
bean id of the transaction policy.
+
+TIP: **Keep it simple:** Although you can use multiple propagation behaviors
with multiple routes in Camel, do
+so with care. Try to design your solutions with as few propagations as
possible, because
+complexity increases dramatically when you introduce new propagation behaviors
+
== Transaction example with database
@@ -241,7 +295,7 @@
https://github.com/apache/camel/tree/main/components/camel-jms/src/test/java/org
the destination is a mock endpoint.
First we configure the standard Spring XML to declare a JMS connection
-factory, a JMS transaction manager and our ActiveMQ component that we
+factory, a JMS transaction manager and our
xref:ROOT:activemq-component.adoc[ActiveMQ] component that we
use in our routing.
[source,xml]
@@ -292,3 +346,44 @@ route as transacted using the `<transacted/>` XML tag.
</camelContext>
----
+== Local vs Global Transactions
+
+When talking about transactions, you need to distinguish between single- and
+multiple-resource transactions. The former are also known as local
transactions,
+and the latter as global transactions
+
+=== Local Transactions
+
+If you only have a single resource (such as one database, or one messaging
system) then
+transactions can be simpler to orchestrate by the transaction manager. This is
known as local transactions.
+
+The previous two examples above are both using a single resource, and are
therefore using local transactions.
+When using local transactions and Spring Transactions, then you can use the
dedicated transaction manager for the resource type:
+
+- org.springframework.jdbc.datasource.DataSourceTransactionManager
+- org.springframework.jms.connection.JmsTransactionManager
+
+TIP: Consult the spring documentation for more local transaction managers.
+
+=== Global Transactions
+
+The situation changes when you need to span multiple resources in the
+same transaction, such as JMS and JDBC resources together.
+
+To support multiple resources you need to use a JTA (XA) capable transaction
manager,
+which means using `org.springframework.transaction.jta.JtaTransactionManager`
with Spring Transactions.
+
+NOTE: For more information on JTA, see the Wikipedia page on the subject:
+http://en.wikipedia.org/wiki/Java_Transaction_API. XA is also briefly discussed
+here: http://en.wikipedia.org/wiki/X/Open_XA.
+
+That is not all, you also need to use a JTA transaction implementation such as:
+
+- Atomikos - https://www.atomikos.com/
+- Narayana - https://narayana.io/
+- JEE Application Server with JTA
+
+And all of this must be configured correctly to have JTA transaction working.
+You may also need to do special configuration from the vendors of the
resources (i.e. database or messaging system)
+to have this work properly with JTA/XA transactions. Consult the documentation
of those systems for more details.
+