package io.trygvis.container.tx; public class PlatformTransactionManager { public static Tx currentTx() { return tx.get().tx(); } public static interface TxFactory { Tx create(TransactionIsolation isolation); } static final ThreadLocal tx = new ThreadLocal() { @Override protected TxEntry initialValue() { return new RootTxState(); } }; public abstract static class TxEntry { abstract Tx tx(); abstract boolean isActive(); abstract TxEntry pop(); } public static class NewTxTxEntry extends TxEntry { private final TxEntry parent; private final Tx tx; public NewTxTxEntry(TxEntry parent, Tx tx) { this.parent = parent; this.tx = tx; } @Override Tx tx() { return tx; } public boolean isActive() { return true; } @Override TxEntry pop() { return parent; } } public static class NestedTxEntry extends TxEntry { private final NestedTxEntry parent; public NestedTxEntry(NestedTxEntry parent) { this.parent = parent; } @Override Tx tx() { return parent.tx(); } public boolean isActive() { return parent.isActive(); } @Override TxEntry pop() { return parent; } } private final static class InactiveTxEntry extends TxEntry { private final TxEntry parent; private InactiveTxEntry(TxEntry parent) { this.parent = parent; } @Override public Tx tx() { throw new RuntimeException("No transaction currently running."); } @Override public boolean isActive() { return false; } @Override TxEntry pop() { return parent; } } private final static class RootTxState extends TxEntry { @Override public Tx tx() { throw new RuntimeException("No transaction currently running."); } @Override public boolean isActive() { return false; } @Override TxEntry pop() { throw new RuntimeException("Trying to pop root entry"); } } public enum TransactionIsolation { ISOLATION_DEFAULT, ISOLATION_READ_COMMITTED, ISOLATION_READ_UNCOMMITTED, ISOLATION_REPEATABLE_READ, ISOLATION_SERIALIZABLE } public enum TransactionPropagation { PROPAGATION_NESTED, // PROPAGATION_NEVER, PROPAGATION_NOT_SUPPORTED, PROPAGATION_REQUIRED, PROPAGATION_REQUIRES_NEW, PROPAGATION_SUPPORTS } private final TxFactory txFactory; private final TransactionPropagation defaultPropagation; public PlatformTransactionManager(TxFactory txFactory, TransactionPropagation defaultPropagation) { this.txFactory = txFactory; this.defaultPropagation = defaultPropagation; } public T doInTransaction(TransactionIsolation isolation, TransactionPropagation propagation, TransactionTemplate transactionTemplate) { final TxEntry prevEntry = PlatformTransactionManager.tx.get(); final TxEntry currentEntry = nextTxEntry(prevEntry, isolation, propagation); PlatformTransactionManager.tx.set(currentEntry); try { return transactionTemplate.doInTransaction(); } finally { PlatformTransactionManager.tx.set(prevEntry); } } private TxEntry nextTxEntry(TxEntry prevEntry, TransactionIsolation isolation, TransactionPropagation propagation) { TxEntry currentEntry; switch (propagation) { case PROPAGATION_NESTED: throw new RuntimeException("Propagation 'nested' not supported."); case PROPAGATION_NOT_SUPPORTED: currentEntry = new InactiveTxEntry(prevEntry); PlatformTransactionManager.tx.set(currentEntry); break; default: throw new RuntimeException("Unknown transaction propagation: " + propagation); case PROPAGATION_REQUIRED: if (prevEntry.isActive()) { currentEntry = new NestedTxEntry((NestedTxEntry) prevEntry); } else { Tx tx = txFactory.create(isolation); currentEntry = new NewTxTxEntry(prevEntry, tx); } break; case PROPAGATION_REQUIRES_NEW: Tx tx = txFactory.create(isolation); currentEntry = new NewTxTxEntry(prevEntry, tx); break; case PROPAGATION_SUPPORTS: if (prevEntry.isActive()) { currentEntry = new NestedTxEntry((NestedTxEntry) prevEntry); } else { currentEntry = new InactiveTxEntry(prevEntry); } break; } return currentEntry; } public interface TransactionTemplate { T doInTransaction(); } }