summaryrefslogtreecommitdiff
path: root/container-core/src/main/java/io/trygvis/container/tx/PlatformTransactionManager.java
diff options
context:
space:
mode:
Diffstat (limited to 'container-core/src/main/java/io/trygvis/container/tx/PlatformTransactionManager.java')
-rw-r--r--container-core/src/main/java/io/trygvis/container/tx/PlatformTransactionManager.java188
1 files changed, 188 insertions, 0 deletions
diff --git a/container-core/src/main/java/io/trygvis/container/tx/PlatformTransactionManager.java b/container-core/src/main/java/io/trygvis/container/tx/PlatformTransactionManager.java
new file mode 100644
index 0000000..3ab7b6f
--- /dev/null
+++ b/container-core/src/main/java/io/trygvis/container/tx/PlatformTransactionManager.java
@@ -0,0 +1,188 @@
+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<TxEntry> tx = new ThreadLocal<TxEntry>() {
+ @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> T doInTransaction(TransactionIsolation isolation, TransactionPropagation propagation, TransactionTemplate<T> 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> {
+ T doInTransaction();
+ }
+}