/* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package com.github.geophile.erdo.transaction; import com.github.geophile.erdo.AbstractKey; import com.github.geophile.erdo.TransactionCallback; import com.github.geophile.erdo.map.Factory; import com.github.geophile.erdo.map.transactionalmap.TransactionalMap; import java.io.IOException; import java.util.List; public class TransactionManager { public final Transaction currentTransaction() { Transaction transaction = THREAD_TRANSACTION.get(); if (transaction == null) { synchronized (this) { transaction = Transaction.newTransaction(this, newTransactionalMap()); transactionRegistry.transactionStarted(transaction); } THREAD_TRANSACTION.set(transaction); } return transaction; } public final void lock(AbstractKey key) throws InterruptedException, DeadlockException, TransactionRolledBackException { Transaction transaction = currentTransaction(); key.transaction(transaction); transaction.waitingFor(key); try { lockManager().lock(key, transaction); } catch (DeadlockException | TransactionRolledBackException e) { rollbackTransaction(); throw e; } finally { transaction.doneWaitingForKey(); } } // TODO: What sort of cleanup should be done in case of exceptions thrown during // TODO: commitTransactionAsynchronously and rollbackTransaction? public final void commitTransaction(TransactionCallback transactionCallback, Object commitInfo) throws IOException, InterruptedException { Transaction transaction = THREAD_TRANSACTION.get(); if (transaction != null) { List<Transaction> irrelevantTransactions; synchronized (this) { transaction.commit(transactionCallback, commitInfo); makeUpdatesPublic(transaction); irrelevantTransactions = transactionRegistry.transactionCommitted(transaction); } lockManager().transactionCommitted(transaction, irrelevantTransactions); destroyIrrelevantTransactions(irrelevantTransactions); THREAD_TRANSACTION.remove(); } // else: A transaction was not started } public final void rollbackTransaction() { Transaction transaction = THREAD_TRANSACTION.get(); if (transaction != null) { List<Transaction> irrelevantTransactions; synchronized (this) { transaction.rollback(); irrelevantTransactions = transactionRegistry.transactionAborted(transaction); } lockManager().transactionAborted(transaction, irrelevantTransactions); destroyIrrelevantTransactions(irrelevantTransactions); THREAD_TRANSACTION.remove(); } // else: A transaction was not started } public Factory factory() { return factory; } public void makeUpdatesPublic(Transaction transaction) throws IOException, InterruptedException { } public TransactionalMap newTransactionalMap() { return null; } public TransactionManager(Factory factory) { this.factory = factory; THREAD_TRANSACTION.remove(); } // For testing public void clearThreadState() { THREAD_TRANSACTION.remove(); transactionRegistry.reset(); } protected LockManager lockManager() { return factory.lockManager(); } // For use by this class private void destroyIrrelevantTransactions(List<Transaction> irrelevantTransactions) { for (Transaction transaction : irrelevantTransactions) { transaction.destroy(); } } // Class state protected static final ThreadLocal<Transaction> THREAD_TRANSACTION = new ThreadLocal<>(); // Object state protected final Factory factory; protected final TransactionRegistry transactionRegistry = new TransactionRegistry(); }