A long post, sorry! Most is pasted code though.
Update: If you get to the bottom of this post, you may be interested in this update.
One of the underlying design tenets of my application is that everything should happen, or nothing should happen. This covers all the database access, as well as file access, and there will be a lot of the latter. This means transactions for databases and transactions for filesystems, and since both database changes and filesystem changes will be required together, this means XA transactions across the database(s) and the filesystem(s).
There are two big problems with the above requirement: firstly, DDL is rarely transactional (PostgreSQL and Informix can do it, but since choice of RDBMS is my customer’s, not mine, I can’t rely on them); and secondly, filesystems aren’t transactional either.
The DDL issue is a major one, and one not easily resolved. Given that it won’t be transactional, is it worth pursuing transactions in the other areas? I think so – the effort of writing recovery code is so much more otherwise. The layer which abstracts the application from the database can be written to understand the capabilities of the RDBMS – so if transactional DDL is supported, then it can be used.
That’s for another day though, today I’m concerned with filesystem transactions. Luckily here some work has been done. Filesystem transactions in Java are supported by the Commons Transaction Apache project. I used this really nice step-by-step tutorial to get up and running.
package txtest;
import org.apache.commons.transaction.file.FileResourceManager;
import org.apache.commons.transaction.util.Log4jLogger;
import org.apache.log4j.Logger;
import org.junit.Test;
public class CommonsTxTest {
private static final Logger logger = Logger.getLogger(CommonsTxTest.class);
@Test
public void test() {
try {
FileResourceManager fileResourceManager = new FileResourceManager(
"/Users/mdneale/txtest/store",
"/Users/mdneale/txtest/work",
false,
new Log4jLogger(logger));
fileResourceManager.start();
String txId = fileResourceManager.generatedUniqueTxId();
fileResourceManager.startTransaction(txId);
fileResourceManager.createResource(txId, "new.file");
fileResourceManager.commitTransaction(txId);
fileResourceManager.stop(FileResourceManager.SHUTDOWN_MODE_NORMAL);
} catch (Exception e) {
e.printStackTrace();
}
}
}
And it works. Running this short program creates the new file “new.file” in the folder /Users/mdneale/txtest/store. Now swap the line:
fileResourceManager.commitTransaction(txId);
for:
fileResourceManager.rollbackTransaction(txId);
Remove the file “new.file” that was created by the last run of the program, and run the program again. This time the file doesn’t appear. So far, so good.
The next step is XA. I’m going to explain here how to interface the Commons Transaction File Resource Manager to a J2EE Transaction Manager so that the Transaction Manager can control the resource as part of an XA Transaction. It shouldn’t be difficult to convert it for a non-J2EE Transaction Manager if necessary.
Read the rest of this entry »














