Limiting the scope of a transaction

The technique shown in the Using transactions with a database recipe used the transacted DSL statement to initiate a transaction. Using this approach, the transaction is committed when the exchange's processing completes. This recipe will show you how you can control the scope of transactions in a much more granular fashion using the policy statement.

Getting ready

The Java code for this recipe is located in the org.camelcookbook.transactions.transactionpolicies package. The Spring XML files are located under src/main/resources/META-INF/spring and prefixed with transactionPolicies.

How to do it...

Set up a SpringTransactionPolicy associated with a transaction manager as shown in the Using transactions with a database recipe.

Tip

The type of transaction manager is not relevant, as this approach applies to JDBC, JMS, and XA transactions.

Using the policy DSL statement, wrap the individual processing steps that you want to enclose in a transaction, referring to the SpringTransactionPolicy by name (in this example, we refer to a bean with the id "PROPAGATION_REQUIRED").

In the XML DSL, this is expressed as follows:

<from uri="direct:policies"/>
<setHeader headerName="message">
  <simple>${body}</simple>
</setHeader>
<policy ref="PROPAGATION_REQUIRED">
  <!-- Transaction 1 -->
  <to uri="sql:insert into audit_log (message) values(:#message)"/>
  <to uri="mock:out1"/>
</policy>
<policy ref="PROPAGATION_REQUIRED">
  <!-- Transaction 2 -->
  <to uri="sql:insert into messages (message) values(:#message)"/>
  <to uri="mock:out2"/>
</policy>

In the Java DSL, the same routing logic can be expressed as:

from("direct:policies")
  .setHeader("message", body())
  .policy("PROPAGATION_REQUIRED") // Transaction 1
    .to("sql:insert into audit_log (message) values (:#message)")
    .to("mock:out1")
  .end()
  .policy("PROPAGATION_REQUIRED") // Transaction 2
    .to("sql:insert into messages (message) values (:#message)")
    .to("mock:out2")
  .end();

How it works...

In the preceding example, the route defines two separate transactions. When the end of the first policy block is reached, Transaction 1 will be committed. When the exchange reaches the second policy block, a new transaction will be started. If an exception is thrown by the mock:out2 endpoint, only Transaction 2 will be rolled back.

The policy block defines a boundary around a series of processing steps with a "before" and "after" processing step being performed by an implementation of the org.apache.camel.spi.Policy interface. SpringTransactionPolicy implements this interface.

When referring to a SpringTransactionPolicy instance, a transaction will be initialized according to the propagation behavior that you have specified (see the Using transactions with a database recipe for an outline of the available options). When the policy block completes, the transaction will be completed as per the logic defined by that propagation behavior.

There's more...

The policy block is a general-purpose mechanism for scoping "before" and "after" behavior. Aside from transactions, it is also used by the Camel Shiro and Camel Spring Security Components (see Authentication and authorization using Spring Security in Chapter 11, Security) to allow you to define security constrains around processing steps.

Transactions may be nested using this mechanism by referring to different SpringTransactionPolicy objects. In the following example, we embed a non-transacted SQL insert into an audit_log table inside a wider transaction. An exception thrown by the mock:out1 endpoint would remove the entry from the messages table, but not the audit_log table.

In the XML DSL, this is written as:

<route>
  <from uri="direct:policies"/>
  <setHeader headerName="message">
    <simple>${body}</simple>
  </setHeader>
  <policy ref="PROPAGATION_REQUIRED">
    <to uri="sql:insert into messages (message) values(:#message)"/>
    <to uri="direct:nestedPolicy"/>
    <to uri="mock:out1"/>
  </policy>
</route>

<route>
  <from uri="direct:nestedPolicy"/>
  <policy ref="PROPAGATION_NOT_SUPPORTED">
    <to uri="sql:insert into audit_log (message) values(:#message)"/>
    <to uri="mock:out2"/>
  </policy>
</route>

In the Java DSL, the same routing logic is expressed as:

from("direct:policies")
  .setHeader("message", simple("${body}"))
  .policy("PROPAGATION_REQUIRED")
    .to("sql:insert into messages (message) values (:#message)")
    .to("direct:nestedPolicy")
    .to("mock:out1")
  .end();

from("direct:nestedPolicy")
  .policy("PROPAGATION_NOT_SUPPORTED")
    .to("sql:insert into audit_log (message) values (:#message)")
    .to("mock:out2")
  .end();

If nesting two transactions with the PROPAGATION_REQUIRES_NEW behavior, both policy blocks should refer to different SpringTransactionPolicy objects with the same behavior otherwise the second policy will have no impact:

<bean id="PROPAGATION_REQUIRES_NEW" 
      class="org.apache.camel.spring.spi.SpringTransactionPolicy">
  <property name="transactionManager"
            ref="transactionManager"/>
  <property name="propagationBehaviorName" 
            value="PROPAGATION_REQUIRES_NEW"/>
</bean>

<bean id="PROPAGATION_REQUIRES_NEW-2" 
      class="org.apache.camel.spring.spi.SpringTransactionPolicy">
  <property name="transactionManager"
            ref="transactionManager"/>
  <property name="propagationBehaviorName" 
            value="PROPAGATION_REQUIRES_NEW"/>
</bean>

The preceding two policy blocks are identical except for their id attributes. The following code example shows how you could reference the preceding policy definitions:

<route>
  <from uri="direct:policies"/>
  <setHeader headerName="message">
    <simple>${body}</simple>
  </setHeader>
  <policy ref="PROPAGATION_REQUIRES_NEW">
    <to uri="sql:insert into messages (message) values(:#message)"/>
    <to uri="direct:nestedPolicy"/>
    <to uri="mock:out1"/>
  </policy>
</route>

<route>
  <from uri="direct:nestedPolicy"/>
  <policy ref="PROPAGATION_REQUIRES_NEW-2">
    <to uri="sql:insert into audit_log (message) values(:#message)"/>
    <to uri="mock:out2"/>
  </policy>
</route>

Tip

If you intend to wrap all of the DSL statements in a route within a single policy block, it is much clearer to use a transacted statement instead.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.139.83.62