package org.jboss.seam.transaction; import static org.jboss.seam.ComponentType.JAVA_BEAN; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import org.jboss.seam.annotations.TransactionPropagationType; import org.jboss.seam.annotations.Transactional; import org.jboss.seam.annotations.intercept.AroundInvoke; import org.jboss.seam.annotations.intercept.Interceptor; import org.jboss.seam.bpm.BusinessProcessInterceptor; import org.jboss.seam.core.BijectionInterceptor; import org.jboss.seam.core.ConversationInterceptor; import org.jboss.seam.intercept.AbstractInterceptor; import org.jboss.seam.intercept.InvocationContext; import org.jboss.seam.util.Work; /** * Implements transaction propagation rules for Seam JavaBean components. * * @author Gavin King * @author Shane Bryzak */ @Interceptor(stateless=false, around={RollbackInterceptor.class, BusinessProcessInterceptor.class, ConversationInterceptor.class, BijectionInterceptor.class}) public class TransactionInterceptor extends AbstractInterceptor { private static final long serialVersionUID = -4364203056333738988L; transient private Map<AnnotatedElement,TransactionMetadata> transactionMetadata = new HashMap<AnnotatedElement, TransactionMetadata>(); private class TransactionMetadata { private boolean annotationPresent; TransactionPropagationType propType; public TransactionMetadata(AnnotatedElement element) { annotationPresent = element.isAnnotationPresent(Transactional.class); if (annotationPresent) { propType = element.getAnnotation(Transactional.class).value(); } } public boolean isAnnotationPresent() { return annotationPresent; } public boolean isNewTransactionRequired(boolean transactionActive) { return propType != null && propType.isNewTransactionRequired(transactionActive); } } private TransactionMetadata lookupTransactionMetadata(AnnotatedElement element) { if (transactionMetadata == null) { transactionMetadata = new HashMap<AnnotatedElement, TransactionMetadata>(); } TransactionMetadata metadata = transactionMetadata.get(element); if (metadata == null) { metadata = loadMetadata(element); } return metadata; } private synchronized TransactionMetadata loadMetadata(AnnotatedElement element) { if (!transactionMetadata.containsKey(element)) { TransactionMetadata metadata = new TransactionMetadata(element); transactionMetadata.put(element, metadata); return metadata; } return transactionMetadata.get(element); } @AroundInvoke public Object aroundInvoke(final InvocationContext invocation) throws Exception { return new Work() { @Override protected Object work() throws Exception { return invocation.proceed(); } @Override protected boolean isNewTransactionRequired(boolean transactionActive) { return isNewTransactionRequired( invocation.getMethod(), getComponent().getBeanClass(), transactionActive ); } private boolean isNewTransactionRequired(Method method, Class beanClass, boolean transactionActive) { TransactionMetadata metadata = lookupTransactionMetadata(method); if (metadata.isAnnotationPresent()) { return metadata.isNewTransactionRequired(transactionActive); } else { metadata = lookupTransactionMetadata(beanClass); return metadata.isNewTransactionRequired(transactionActive); } } }.workInTransaction(); } public boolean isInterceptorEnabled() { return getComponent().getType()==JAVA_BEAN && getComponent().beanClassHasAnnotation(Transactional.class); } }