What's New in Spring Integration 2.2 (Part 2 – Transaction Synchronization) |
|

Introduction
This is the second part in a series of blog posts highlighting some of the new features available in Spring Integration 2.2 following the recent release of Release Candidate 1. The first part talks about the MongoDB adapters.
Spring Integration 2.2 introduces extended support for synchronizing non-transactional resources with transactions.
Background
Spring has provided first class support for synchronizing resources with transactions for many years. In many cases, this facility is used to synchronize transactions managed by multiple transaction managers, in order to implement the 'Best Efforts 1PC' pattern described in Dave Syer's excellent JavaWorld article.
It is often used, for example, to synchronize a JMS commit with a JDBC commit.
Spring Integration has long supported this feature, by defining a <transactional/> element on a poller. This release takes this feature one step further, by allowing synchronization of non-transactional resources with a transaction.
For example, consider a <file:inbound-channel-adapter/> and an Integration application that reads a file when it appears in a directory, and updates a database. This feature allows us to configure different actions to be taken if the transaction commits or rolls back.
We will also show how these "post processing" actions can be configured to occur, even if there is not a real transactional resource involved.
Caveat
The feature does NOT make an inherently non-transaction resource (such as a simple file system) transactional; rather, it simply allows us to synchronize file system (or other) activities with a transaction.
Internals
The fundamental component of the Spring transaction synchronization feature is an object that implements TransactionSynchronization. Such an object has a number of callbacks that the framework invokes during synchronization (examples include afterCommit, afterRollback).
Spring Integration 2.2 introduces the concept of a TransactionSynchronizationFactory. This factory is responsible for creating a TransactionSynchronization object for each message. A default implementation is provided that allows the execution of SpEL expressions against the message as part of transaction synchronization.
Configuration
Let's continue with the file system example. In order to enable transaction synchronization, we simply add a reference to a TransactionSynchronizationFactory on the <transactional/> element, and define a factory along with the SpEL expressions we wish to evaluate during transaction synchronization.
<int-file:inbound-channel-adaper ...>
<poller fixed-rate="1000">
<transactional synchronization-factory="syncFactory"/>
</poller>
</int-file:inbound-channel-adapter>
<int:transaction-synchronization-factory id="syncFactory">
<int:after-commit expression="payload.renameTo('/successful/' + payload.name" />
<int:after-rollback expression="payload.renameTo('/failed'/ + payload.name" />
</int:transaction-synchronization-factory>
As you can see, we rename the original inbound file, putting the file into a different directory, depending on whether the associated transaction commits or rolls back. Optionally, the result of the evaluation can be sent to a channel defined using the 'channel' attribute on the child elements of the transaction-synchronization-factory.
(Note: The "before-commit" element is also available which can also be used to affect whether the commit is actually completed or not).
Pseudo Transactions
As can be seen above, we now have a convenient mechanism to perform some action before or after a transaction commits, or after it rolls back. But, what if we have a flow that does not involve a transactional resource at all? For example: file:inbound-adapter<-poller->transformer->ftp:outbound adapter.
Let's say we want to rename the input file, depending on success or failure of the ftp file transfer of the transformed file. In order to accommodate this use case, we have introduced the PseudoTransactionManager. This class implements PlatformTransactionManager for the sole purpose of allowing the use of the above synchronization techniques, even if there is no real transaction involved. Simply add the <transactional/> element as before, giving it a reference to a PseudoTransactionManager (or use the default bean id of 'transactionManager').
Conclusion
With Spring Integration 2.2, users now have the ability to perform appropriate actions, normally affecting the original input source, after a flow completes (either successfully, or otherwise). This can be synchronized with a transaction on some other transactional resource, or even in cases where there is no real transaction involved.
A sample application exploring these features is provided here.
Similar Posts
- What's New in Spring Integration 2.2 (Part 4 – Retry and More)
- What's New in Spring Integration 2.2 (Part 3 – JPA Support)
- What's New in Spring Integration 2.2.RC1 (Part 1 – MongoDb)
- Using JPA in Spring without referencing Spring
- So should you still use Spring's HibernateTemplate and/or JpaTemplate??





Mark says:
Added on October 7th, 2012 at 5:55 amIt would be better if you've also provided a demo on how to use PseudoTransactionManager here so that we don't have to jump to the samples page.
Gary Russell (blog author) says:
Added on October 7th, 2012 at 9:35 amThe sample doesn't explicitly talk about the PseudoTransactionManager; I will update it to do so in a few days. In the meantime, there is documentation here… http://static.springsource.org/spring-integration/docs/2.2.0.RC1/reference/htmlsingle/#pseudo-transactions
Paul Allen says:
Added on October 11th, 2012 at 12:16 pmThanks, Gary. Please let your "This Week in Spring" colleagues know that too many of the examples they cite are old (in some cases 4 years old) and no longer work (Josh Long). This is very frustrating for those of us trying the examples.
I tried your example with Mac OS X 10.7.5 and Spring STS 3.1.0 RELEASE after fetching from git and performing mvn eclipse:eclipse in the project. Using "Run as Java Application" I get the temp directory, put the file in that directory, rerun it and hit return, and see nothing changed. Please let us know the execution environment and how you actually ran the program so we can try and reproduce the results.
(This is another big stumbling block of these posts.)
Thanks!
Gary Russell (blog author) says:
Added on October 11th, 2012 at 1:57 pmSorry about that; there is a typo in the second println of the directory in which to put the file:
Notice in the main message, it says
…
Place a file in /var/folders/k0/gch26h6d2ms9t0g7pyhtzfkc0000gn/T/txSynchDemo ending
with .txt
…
And there's a left-over debug line that emits…
/var/folders/k0/gch26h6d2ms9t0g7pyhtzfkc0000gn/T/txSynchDir
(Dir Vs. Demo).
I built it on OSX 10.7.4; I have subsequently updated to 10.7.5 and just retested it and it worked fine (also on Ubuntu). Just put the .txt file in …/txSynchDemo
Gary
Paul Allen says:
Added on October 12th, 2012 at 11:26 amThanks! I refetched from github and took out the leading slash in "txSynchDemo" TransactionSynchronizationDemo.java lines 52 and 59 and things worked wonderfully!