package org.jboss.seam.transaction; import static org.jboss.seam.annotations.Install.FRAMEWORK; import java.rmi.RemoteException; import java.util.LinkedList; import javax.ejb.EJBException; import javax.ejb.Remove; import javax.ejb.SessionSynchronization; import javax.ejb.Stateful; import javax.ejb.TransactionAttribute; import javax.ejb.TransactionAttributeType; import javax.transaction.Synchronization; import org.jboss.seam.ScopeType; import org.jboss.seam.annotations.Install; import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.Scope; import org.jboss.seam.annotations.intercept.BypassInterceptors; import org.jboss.seam.log.LogProvider; import org.jboss.seam.log.Logging; /** * Receives JTA transaction completion notifications from * the EJB container, and passes them on to the registered * Synchronizations. This implementation * is fully aware of container managed transactions and is * able to register Synchronizations for the container * transaction. * * @author Gavin King * */ @Stateful @Name("org.jboss.seam.transaction.synchronizations") @Scope(ScopeType.EVENT) @Install(precedence=FRAMEWORK, dependencies="org.jboss.seam.transaction.ejbTransaction") @BypassInterceptors @TransactionAttribute(TransactionAttributeType.REQUIRED) public class EjbSynchronizations implements LocalEjbSynchronizations, SessionSynchronization { private static final LogProvider log = Logging.getLogProvider(EjbSynchronizations.class); //maintain two lists to work around a bug in JBoss EJB3 where a new SessionSynchronization //gets registered each time the bean is called protected LinkedList<SynchronizationRegistry> synchronizations = new LinkedList<SynchronizationRegistry>(); protected LinkedList<SynchronizationRegistry> committing = new LinkedList<SynchronizationRegistry>(); public void afterBegin() { log.debug("afterBegin"); synchronizations.addLast( new SynchronizationRegistry() ); } public void beforeCompletion() throws EJBException, RemoteException { log.debug("beforeCompletion"); SynchronizationRegistry sync = synchronizations.removeLast(); sync.beforeTransactionCompletion(); committing.addLast(sync); } public void afterCompletion(boolean success) throws EJBException, RemoteException { log.debug("afterCompletion"); if ( committing.isEmpty() ) { if (success) { throw new IllegalStateException("beforeCompletion was never called"); } else { synchronizations.removeLast().afterTransactionCompletion(false); } } else { committing.removeFirst().afterTransactionCompletion(success); } } public boolean isAwareOfContainerTransactions() { return true; } public void afterTransactionBegin() { //noop, let JTA notify us } public void afterTransactionCommit(boolean success) { //noop, let JTA notify us } public void afterTransactionRollback() { //noop, let JTA notify us } public void beforeTransactionCommit() { //noop, let JTA notify us } public void registerSynchronization(Synchronization sync) { synchronizations.getLast().registerSynchronization(sync); } @Remove public void destroy() {} }