package org.argeo.transaction.simple; import java.util.Collections; import java.util.HashMap; import java.util.Map; 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.Synchronization; import javax.transaction.SystemException; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import javax.transaction.TransactionSynchronizationRegistry; import javax.transaction.UserTransaction; import javax.transaction.xa.Xid; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; public class SimpleTransactionManager implements TransactionManager, UserTransaction { private final static Log log = LogFactory.getLog(SimpleTransactionManager.class); private ThreadLocal current = new ThreadLocal(); private Map knownTransactions = Collections .synchronizedMap(new HashMap()); private SyncRegistry syncRegistry = new SyncRegistry(); @Override public void begin() throws NotSupportedException, SystemException { if (getCurrent() != null) throw new NotSupportedException("Nested transactions are not supported"); SimpleTransaction transaction = new SimpleTransaction(this); knownTransactions.put(transaction.getXid(), transaction); current.set(transaction); if (log.isTraceEnabled()) log.trace("STARTED " + transaction.getXid()); } @Override public void commit() throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, IllegalStateException, SystemException { if (getCurrent() == null) throw new IllegalStateException("No transaction registered with the current thread."); getCurrent().commit(); } @Override public int getStatus() throws SystemException { if (getCurrent() == null) return Status.STATUS_NO_TRANSACTION; return getTransaction().getStatus(); } @Override public Transaction getTransaction() throws SystemException { return getCurrent(); } protected SimpleTransaction getCurrent() throws SystemException { SimpleTransaction transaction = current.get(); if (transaction == null) return null; int status = transaction.getStatus(); if (Status.STATUS_COMMITTED == status || Status.STATUS_ROLLEDBACK == status) { current.remove(); return null; } return transaction; } void unregister(Xid xid) { knownTransactions.remove(xid); } @Override public void resume(Transaction tobj) throws InvalidTransactionException, IllegalStateException, SystemException { if (getCurrent() != null) throw new IllegalStateException("Transaction " + current.get() + " already registered"); current.set((SimpleTransaction) tobj); } @Override public void rollback() throws IllegalStateException, SecurityException, SystemException { if (getCurrent() == null) throw new IllegalStateException("No transaction registered with the current thread."); getCurrent().rollback(); } @Override public void setRollbackOnly() throws IllegalStateException, SystemException { if (getCurrent() == null) throw new IllegalStateException("No transaction registered with the current thread."); getCurrent().setRollbackOnly(); } @Override public void setTransactionTimeout(int seconds) throws SystemException { throw new UnsupportedOperationException(); } @Override public Transaction suspend() throws SystemException { Transaction transaction = getCurrent(); current.remove(); return transaction; } public TransactionSynchronizationRegistry getTsr() { return syncRegistry; } private class SyncRegistry implements TransactionSynchronizationRegistry { @Override public Object getTransactionKey() { try { SimpleTransaction transaction = getCurrent(); if (transaction == null) return null; return getCurrent().getXid(); } catch (SystemException e) { throw new SimpleTransactionException("Cannot get transaction key", e); } } @Override public void putResource(Object key, Object value) { throw new UnsupportedOperationException(); } @Override public Object getResource(Object key) { throw new UnsupportedOperationException(); } @Override public void registerInterposedSynchronization(Synchronization sync) { throw new UnsupportedOperationException(); } @Override public int getTransactionStatus() { try { return getStatus(); } catch (SystemException e) { throw new SimpleTransactionException("Cannot get status", e); } } @Override public boolean getRollbackOnly() { try { return getStatus() == Status.STATUS_MARKED_ROLLBACK; } catch (SystemException e) { throw new SimpleTransactionException("Cannot get status", e); } } @Override public void setRollbackOnly() { try { getCurrent().setRollbackOnly(); } catch (Exception e) { throw new SimpleTransactionException("Cannot set rollback only", e); } } } }