177 lines
12 KiB
XML
177 lines
12 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
||
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="transactions"
|
||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||
<title>Transaction Support</title>
|
||
|
||
<section id="transaction-support">
|
||
<title>Understanding Transactions in Message flows</title>
|
||
<para>
|
||
Spring Integration exposes several hooks to address transactional needs of you message flows.
|
||
But to better understand these hooks and how you can benefit from them we must first revisit the 6 mechanisms
|
||
that could be used to initiate Message flows and see how transactional needs of these flows
|
||
could be addressed within each of these mechanisms.
|
||
</para>
|
||
<para>
|
||
Here are the 6 mechanisms to initiate a Message flow and their short summary (details for each are provided throughout this manual):
|
||
<itemizedlist>
|
||
<listitem>
|
||
<para><emphasis>Gateway Proxy</emphasis> - Your basic Messaging Gateway</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para><emphasis>MessageChannel</emphasis> - Direct interactions with MessageChannel methods (e.g., channel.send(message))</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para><emphasis>Message Publisher</emphasis> - the way to initiate message flow as the by-product of method invocations on Spring beans</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para><emphasis>Inbound Channel Adapters/Gateways</emphasis> - the way to initiate message flow based on connecting third-party
|
||
system with Spring Integration messaging system(e.g., [JmsMessage] -> Jms Inbound Adapter[SI Message] -> SI Channel)</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para><emphasis>Scheduler</emphasis> - the way to initiate message flow based on scheduling events distributed
|
||
by a pre-configured Scheduler</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para><emphasis>Poller</emphasis> - similar to the Scheduler and is the way to initiate message flow based on scheduling
|
||
or interval-based events distributed by a pre-configured Poller</para>
|
||
</listitem>
|
||
</itemizedlist>
|
||
</para>
|
||
<para>
|
||
These 6 could be split in 2 general categories:
|
||
<itemizedlist>
|
||
<listitem>
|
||
<para><emphasis>Message flows initiated by a USER process</emphasis> - Example scenarios in this category
|
||
would be invoking a Gateway method or explicitly sending a Message to a MessageChannel. In other words, these message flows depend on a third
|
||
party process (e.g., some code that we wrote) to be initiated.</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para><emphasis>Message flows initiated by a DAEMON process</emphasis> - Example scenarios in this category would be a Poller
|
||
polling a Message queue to initiate a new Message flow with the polled Message or a Scheduler scheduling the
|
||
process by creating a new Message and initiating a message flow at a predefined time.</para>
|
||
</listitem>
|
||
</itemizedlist>
|
||
</para>
|
||
<para>
|
||
Clearly the <emphasis>Gateway Proxy</emphasis>, <emphasis>MessageChannel.send(..)</emphasis> and <emphasis>MessagePublisher</emphasis>
|
||
all belong to the 1st category and <emphasis>Inbound Adapters/Gateways</emphasis>, <emphasis>Scheduler</emphasis> and <emphasis>Poller</emphasis>
|
||
belong to the 2nd.
|
||
</para>
|
||
<para>
|
||
So, how do we address transactional needs in various scenarios within each category and is there a need for Spring Integration
|
||
to provide something explicitly with regard to transactions for a particular scenario? Or, can Spring's Transaction Support be leveraged instead?.
|
||
</para>
|
||
|
||
<para>
|
||
The first and most obvious goal is NOT to re-invent something that has already been invented unless you can provide a better solution.
|
||
In our case Spring itself provides first class support for transaction management. So our goal here is not to provide something new but rather
|
||
delegate/use Spring to benefit from the existing support for transactions. In other words as a framework we must expose hooks to the Transaction management functionality
|
||
provided by Spring. But since Spring Integration configuration is based on Spring Configuration it is not always necessary to expose these hooks as they
|
||
are already exposed via Spring natively. Remember every Spring Integration component is a Spring Bean after all.
|
||
</para>
|
||
<para>
|
||
With this goal in mind let's look at the two scenarios.
|
||
</para>
|
||
<para>
|
||
If you think about it, Message flows that are initiated by the <emphasis>USER process</emphasis> (Category 1) and obviously configured in a Spring Application Context,
|
||
are subject to transactional configuration of such processes and therefore don't need to be explicitly configured by Spring Integration to support transactions.
|
||
The transaction could and should be initiated through standard Transaction support provided by Spring. The Spring Integration message flow will honor
|
||
the transactional semantics of the components naturally because it is Spring configured. For example, a Gateway or ServiceActivator method could
|
||
be annotated with <classname>@Transactional</classname> or <classname>TransactionInterceptor</classname> could be defined in an XML configuration
|
||
with a point-cut expression pointing to specific methods that should be transactional.
|
||
The bottom line is that you have full control over transaction configuration and boundaries in these scenarios.
|
||
</para>
|
||
|
||
<para>
|
||
However, things are a bit different when it comes to Message flows initiated by the <emphasis>DAEMON process</emphasis> (Category 2).
|
||
Although configured by the developer these flows do not directly involve a human or some other process to be initiated. These are trigger-based flows
|
||
that are initiated by a trigger process (DAEMON process) based on the configuration of such process. For example, we could have a Scheduler
|
||
initiating a message flow every Friday night of every week. We can also configure a trigger that initiates a Message flow every second, etc.
|
||
So, we obviously need a way to let these trigger-based processes know of our intention to make the resulting Message flows transactional so that
|
||
a Transaction context could be created whenever a new Message flow is initiated. In other words we need to expose some Transaction configuration, but ONLY enough
|
||
to delegate to Transaction support already provided by Spring (as we do in other scenarios).
|
||
</para>
|
||
|
||
<para>
|
||
Spring Integration provides transactional support for Pollers. Pollers are a special type of component because
|
||
we can call receive() within that poller task against a resource that is itself transactional thus including <emphasis>receive()</emphasis>
|
||
call in the the boundaries of the Transaction allowing it to be rolled back in case of a task failure. If we were to add the same support
|
||
for channels, the added transactions would affect all downstream components starting with that <emphasis>send()</emphasis> call. That is
|
||
providing a rather wide scope for transaction demarcation without any strong reason especially when Spring already provides several ways to
|
||
address the transactional needs of any component downstream. However the <emphasis>receive()</emphasis> method being included in a transaction
|
||
boundary is the "strong reason" for pollers.
|
||
</para>
|
||
|
||
<section id="transaction-poller">
|
||
<title>Poller Transaction Support</title>
|
||
<para>
|
||
Any time you configure a Poller you can provide transactional configuration via the <emphasis>transactional</emphasis> sub-element and its attributes:
|
||
<programlisting language="xml"><![CDATA[<poller max-messages-per-poll="1" fixed-rate="1000">
|
||
<transactional transaction-manager="txManager"
|
||
isolation="DEFAULT"
|
||
propagation="REQUIRED"
|
||
read-only="true"
|
||
timeout="1000"/>
|
||
</poller>]]></programlisting>
|
||
As you can see this configuration looks very similar to native Spring transaction configuration. You must still provide a reference to a Transaction manager and specify
|
||
transaction attributes or rely on defaults (e.g., if the 'transaction-manager' attribute is not specified, it will default to the bean with the name 'transactionManager').
|
||
Internally the process would be wrapped in Spring's native Transaction where <classname>TransactionInterceptor</classname> is responsible for handling transactions.
|
||
For more information on how to configure a Transaction Manager, the types of Transaction Managers (e.g., JTA, Datasource etc.) and other details related to
|
||
transaction configuration please refer to Spring's Reference manual (Chapter 10 - Transaction Management).
|
||
</para>
|
||
<para>
|
||
With the above configuration all Message flows initiated by this poller will be transactional. For more information and details on a
|
||
Poller's transactional configuration please refer to section - <emphasis>21.1.1. Polling and Transactions</emphasis>.
|
||
</para>
|
||
|
||
<para>
|
||
Along with transactions, several more cross cutting concerns might need to be addressed when running a Poller. To help with that,
|
||
the Poller element accepts an <emphasis><advice-chain> </emphasis> sub-element which allows you to define a custom chain of Advice
|
||
instances to be applied on the Poller. (see section 4.4 for more details)
|
||
In Spring Integration 2.0, the Poller went through the a refactoring effort and is now using a proxy mechanism to address transactional
|
||
concerns as well as other cross cutting concerns. One of the significant changes evolving from this effort is that we
|
||
made <emphasis><transactional></emphasis> and <emphasis><advice-chain></emphasis> elements mutually exclusive.
|
||
The rationale behind this is that if you need more than one advice, and one of them is Transaction advice, then you can simply
|
||
include it in the <emphasis><advice-chain></emphasis> with the same convenience as before but with much more control
|
||
since you now have an option to position any advice in the desired order.
|
||
<programlisting language="xml"><![CDATA[<poller max-messages-per-poll="1" fixed-rate="10000">
|
||
<advice-chain>
|
||
<ref bean="txAdvice"/>
|
||
<ref bean="someAotherAdviceBean" />
|
||
<beans:bean class="foo.bar.SampleAdvice"/>
|
||
</advice-chain>
|
||
</poller>
|
||
|
||
<tx:advice id="txAdvice" transaction-manager="txManager">
|
||
<tx:attributes>
|
||
<tx:method name="get*" read-only="true"/>
|
||
<tx:method name="*"/>
|
||
</tx:attributes>
|
||
</tx:advice>
|
||
]]></programlisting>
|
||
|
||
As yo can see from the example above, we have provided a very basic XML-based configuration of Spring Transaction advice - "txAdvice" and
|
||
included it within the <emphasis><advice-chain></emphasis> defined by the Poller.
|
||
If you only need to address transactional concerns of the Poller, then you can still use the <emphasis><transactional></emphasis> element
|
||
as a convinience.
|
||
</para>
|
||
</section>
|
||
</section>
|
||
|
||
<section id="transaction-boundaries">
|
||
<title>Transaction Boundaries</title>
|
||
<para>
|
||
Another important factor is the boundaries of Transactions within a Message flow.
|
||
When a transaction is started, the transaction context is bound to the current thread. So regardless of how many endpoints and channels you have in your
|
||
Message flow your transaction context will be preserved as long as you are ensuring that the flow continues on the same thread.
|
||
As soon as you break it by introducing a <emphasis>Pollable Channel</emphasis> or <emphasis>Executor Channel</emphasis> or initiate a new thread manually
|
||
in some service, the Transactional boundary will be broken as well. Essentially the Transaction will END right there, and if
|
||
a successful handoff has transpired between the threads, the flow would be considered a success and a COMMIT signal would be sent
|
||
even though the flow will continue and might still result in an Exception somewhere downstream. If such a flow were synchronous, that Exception could
|
||
be thrown back to the initiator of the Message flow who is also the initiator of the transactional context and the transaction would result in a ROLLBACK.
|
||
The middle ground is to use transactional channels at any point where a thread boundary is being broken. For example, you can use a Queue-backed Channel
|
||
that delegates to a transactional MessageStore strategy, or you could use a JMS-backed channel.
|
||
</para>
|
||
</section>
|
||
</chapter>
|