Files in a distributed transaction
At a client we are now using files and datasources together. We want the write actions to the database and the files to be in one transaction. We want this because the files represent the database data for legacy applications. As there is no distributed transaction API for files which is JTA (Java Transaction API) compatible we had to find a solution to implement this. This post describes the (big) steps we did and the solution we found. Of course, you can skip the steps and go straight to the solution. 😉
- files that are in a source control management system
- a relational database
- Hibernate for the mapping
- Spring for the wiring
- An object model with Organisation at the root of the object tree
- A file that needs to represent the Organisation as it is in the database for legacy reasons
- JBoss application server
- Java 5
- Maven 2 for building
First, we looked into a way to conform to the requirement that the the file represents the database state. The first thing that came to mind was using the interceptor that you can define for Hibernate. My first try was to use the postFlush method, the reasoning behind it was that if the flush is ok, the transaction will be fine and just for good measure we actually checked the transaction to see if everything was ok. This method is called each time after flushing of the data to the database. All the objects in the Hibernate session are passed in as an Iterator. We need a specific root of an object tree. That class is called Organisation. We need this class to write a file that represents this Organisation. However, this was performance intensive because it would write the file also if nothing was updated. The file is in SCM which locks the file if we want to update and the file is used a lot by legacy systems, so that wasn't an option.
The onFlushDirty and onSave methods were also not an option because these only get the Objects that have been changed and we need the Organisation object. So if an object in the Organisation tree was changed we only got that object. (It took some debugging before we actually found this, because our tests always modified the Organisation object itself.)
The next step was to implement our own XAResource that could handle files using Apache Commons Transaction file package, after reading a bit more we came to another option that we are now using with success.
Making use of: Synchronization. A Synchronization can be registered with a transaction and then it is called before and after a transaction to "Synchronize".
This had a big benefit:
During the process of finding a solution we looked a lot at Transactions. The bad thing about it was that during testing we don't have a JTA transaction but a Hibernate transaction. The JTA and Hibernate transaction interfaces are different. But one of the things that match is the use of Synchronization. Both transaction interfaces use the same interface. We created a wrapper over the two types of transactions and use that in our code to register the Synchronization in a Hibernate or JTA transaction (which we get using Spring).
The logic is now: on writing/updating the database (in code) call the object responsible for the file and the transaction within the transaction. This will write the file, using SCM for versioning and it will register the Synchronization implementation that implements the afterCompletion method, to check if the transaction is "rolled back" or not. If the transaction is rolled back it will also roll back the file update using SCM. Because the writing of the file is within the transaction it will roll back the transaction if something is wrong and the other way around it works because of the Synchronization.
Other solutions are probably also possible, but this one suits our needs and I like that the Synchronization interface is supported by both Hibernate and JTA.
If you are wondering about the spelling of Organisation. Read this 😉