package org.infinispan.transaction.tm;
import java.util.UUID;
import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.InvalidTransactionException;
import javax.transaction.NotSupportedException;
import javax.transaction.RollbackException;
import javax.transaction.Status;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;
/**
* A simple {@link TransactionManager} implementation.
* <p>
* It provides the basic to handle {@link Transaction}s and supports any {@link javax.transaction.xa.XAResource}.
* <p>
* Implementation notes: <ul> <li>The state is kept in memory only.</li> <li>Does not support recover.</li> <li>Does not
* support multi-thread transactions. Although it is possible to execute the transactions in multiple threads, this
* transaction manager does not wait for them to complete. It is the application responsibility to wait before invoking
* {@link #commit()} or {@link #rollback()}</li> <li>The transaction should not block. It is no possible to {@link
* #setTransactionTimeout(int)} and this transaction manager won't rollback the transaction if it takes too long.</li>
* </ul>
* <p>
* If you need any of the requirements above, please consider use another implementation.
* <p>
* Also, it does not implement any 1-phase-commit optimization.
*
* @author Bela Ban
* @author Pedro Ruivo
* @since 9.0
*/
public class EmbeddedBaseTransactionManager implements TransactionManager {
private static final Log log = LogFactory.getLog(EmbeddedBaseTransactionManager.class);
private static final boolean trace = log.isTraceEnabled();
private static ThreadLocal<EmbeddedTransaction> CURRENT_TRANSACTION = new ThreadLocal<>();
final UUID transactionManagerId = UUID.randomUUID();
/**
* Associate the transaction {@code tx} to the current thread.
*/
static void associateTransaction(Transaction tx) {
CURRENT_TRANSACTION.set((EmbeddedTransaction) tx);
}
/**
* Dissociate transaction from current thread.
*/
static void dissociateTransaction() {
CURRENT_TRANSACTION.remove();
}
@Override
public void begin() throws NotSupportedException, SystemException {
Transaction currentTx = getTransaction();
if (currentTx != null) {
throw new NotSupportedException(
Thread.currentThread() + " is already associated with a transaction (" + currentTx + ")");
}
associateTransaction(new EmbeddedTransaction(this));
}
@Override
public void commit() throws RollbackException, HeuristicMixedException,
HeuristicRollbackException, SecurityException,
IllegalStateException, SystemException {
getTransactionAndFailIfNone().commit();
dissociateTransaction();
}
@Override
public void rollback() throws IllegalStateException, SecurityException,
SystemException {
getTransactionAndFailIfNone().rollback();
dissociateTransaction();
}
@Override
public void setRollbackOnly() throws IllegalStateException, SystemException {
getTransactionAndFailIfNone().setRollbackOnly();
}
@Override
public int getStatus() throws SystemException {
Transaction tx = getTransaction();
return tx != null ? tx.getStatus() : Status.STATUS_NO_TRANSACTION;
}
@Override
public EmbeddedTransaction getTransaction() {
return CURRENT_TRANSACTION.get();
}
@Override
public void setTransactionTimeout(int seconds) throws SystemException {
throw new SystemException("not supported");
}
@Override
public Transaction suspend() throws SystemException {
Transaction tx = getTransaction();
dissociateTransaction();
if (trace) {
log.tracef("Suspending tx %s", tx);
}
return tx;
}
@Override
public void resume(Transaction tx) throws InvalidTransactionException, IllegalStateException, SystemException {
if (trace) {
log.tracef("Resuming tx %s", tx);
}
associateTransaction(tx);
}
/**
* @return the current transaction (not null!)
*/
private EmbeddedTransaction getTransactionAndFailIfNone() {
EmbeddedTransaction transaction = CURRENT_TRANSACTION.get();
if (transaction == null) {
throw new IllegalStateException("no transaction associated with calling thread");
}
return transaction;
}
}