/** * EasyBeans * Copyright (C) 2006 Bull S.A.S. * Contact: easybeans@ow2.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA * * -------------------------------------------------------------------------- * $Id: TransactionResolver.java 5643 2010-10-18 15:17:00Z benoitf $ * -------------------------------------------------------------------------- */ package org.ow2.easybeans.deployment.annotations.helper.bean; import static org.ow2.util.ee.metadata.common.api.struct.ITransactionAttributeType.NOT_SUPPORTED; import static org.ow2.util.ee.metadata.common.api.struct.ITransactionAttributeType.REQUIRED; import java.util.ArrayList; import java.util.List; import org.ow2.easybeans.asm.Type; import org.ow2.easybeans.deployment.metadata.ejbjar.EasyBeansEjbJarClassMetadata; import org.ow2.easybeans.deployment.metadata.ejbjar.EasyBeansEjbJarMethodMetadata; import org.ow2.easybeans.transaction.interceptors.BMTStatefulTransactionInterceptor; import org.ow2.easybeans.transaction.interceptors.BMTStatelessTransactionInterceptor; import org.ow2.easybeans.transaction.interceptors.BMTTransactionInterceptor; import org.ow2.easybeans.transaction.interceptors.CMTMandatoryTransactionInterceptor; import org.ow2.easybeans.transaction.interceptors.CMTNeverTransactionInterceptor; import org.ow2.easybeans.transaction.interceptors.CMTNotSupportedTransactionInterceptor; import org.ow2.easybeans.transaction.interceptors.CMTRequiredTransactionInterceptor; import org.ow2.easybeans.transaction.interceptors.CMTRequiresNewTransactionInterceptor; import org.ow2.easybeans.transaction.interceptors.CMTSupportsTransactionInterceptor; import org.ow2.easybeans.transaction.interceptors.ExtendedPersistenceContextInterceptor; import org.ow2.easybeans.transaction.interceptors.ListenerSFSBTransactionInterceptor; import org.ow2.easybeans.transaction.interceptors.ListenerSessionSynchronizationInterceptor; import org.ow2.easybeans.transaction.interceptors.MDBCMTRequiredTransactionInterceptor; import org.ow2.util.ee.metadata.common.api.struct.ITransactionAttributeType; import org.ow2.util.ee.metadata.common.api.struct.ITransactionManagementType; import org.ow2.util.ee.metadata.ejbjar.api.IJClassInterceptor; import org.ow2.util.ee.metadata.ejbjar.impl.JClassInterceptor; import org.ow2.util.log.Log; import org.ow2.util.log.LogFactory; import org.ow2.util.scan.api.metadata.structures.IMethod; import org.ow2.util.scan.impl.metadata.JMethod; /** * This class adds the interceptor for transaction on a given method. * @author Florent Benoit */ public final class TransactionResolver { /** * Logger. */ private static Log logger = LogFactory.getLog(TransactionResolver.class); /** * Signature of EasyBeans interceptors. */ private static final IMethod EASYBEANS_INTERCEPTOR = new JMethod(0, "intercept", "(Lorg/ow2/easybeans/api/EasyBeansInvocationContext;)Ljava/lang/Object;", null, new String[] {"java/lang/Exception"}); /** * CMT Required transaction interceptor. */ private static final String CMT_REQUIRED_INTERCEPTOR = Type.getInternalName(CMTRequiredTransactionInterceptor.class); /** * CMT Mandatory transaction interceptor. */ private static final String CMT_MANDATORY_INTERCEPTOR = Type.getInternalName(CMTMandatoryTransactionInterceptor.class); /** * CMT Never transaction interceptor. */ private static final String CMT_NEVER_INTERCEPTOR = Type.getInternalName(CMTNeverTransactionInterceptor.class); /** * CMT NotSupported transaction interceptor. */ private static final String CMT_NOT_SUPPORTED_INTERCEPTOR = Type .getInternalName(CMTNotSupportedTransactionInterceptor.class); /** * CMT Supports transaction interceptor. */ private static final String CMT_SUPPORTS_INTERCEPTOR = Type.getInternalName(CMTSupportsTransactionInterceptor.class); /** * CMT RequiresNew transaction interceptor. */ private static final String CMT_REQUIRES_NEW_INTERCEPTOR = Type.getInternalName(CMTRequiresNewTransactionInterceptor.class); /** * BMT transaction interceptor. */ private static final String BMT_INTERCEPTOR = Type.getInternalName(BMTTransactionInterceptor.class); /** * BMT stateful transaction interceptor. */ private static final String BMT_STATEFUL_INTERCEPTOR = Type.getInternalName(BMTStatefulTransactionInterceptor.class); /** * BMT stateless transaction interceptor. */ private static final String BMT_STATELESS_INTERCEPTOR = Type.getInternalName(BMTStatelessTransactionInterceptor.class); /** * MDB enlist resource interceptor. */ private static final String MDB_CMT_REQUIRED_INTERCEPTOR = Type.getInternalName(MDBCMTRequiredTransactionInterceptor.class); /** * ListenerSessionSynchronizationInterceptor transaction interceptor. */ private static final String LISTENER_SESSION_SYNCHRO_INTERCEPTOR = Type .getInternalName(ListenerSessionSynchronizationInterceptor.class); /** * Interceptor used to join the current TX for extended persistence contexts. */ private static final String EXTENDED_PERSISTENCECONTEXT_INTERCEPTOR = Type .getInternalName(ExtendedPersistenceContextInterceptor.class); /** * ListenerSessionSynchronizationInterceptor transaction interceptor. */ private static final String LISTENER_SFSB_TRANSACTION_INTERCEPTOR = Type .getInternalName(ListenerSFSBTransactionInterceptor.class); /** * Helper class, no public constructor. */ private TransactionResolver() { } /** * Adds the right transaction interceptor depending of the transactional * attribute set by the user. * @param bean the given bean on which set the transactional interceptor. */ public static void resolve(final EasyBeansEjbJarClassMetadata bean) { for (EasyBeansEjbJarMethodMetadata method : bean.getMethodMetadataCollection()) { resolveMethod(bean, method); } } /** * Adds the right transaction interceptor depending of the transactional * attribute set by the user. * @param bean the given bean on which set the transactional interceptor. * @param method the method on which to add the interceptors */ public static void resolveMethod(final EasyBeansEjbJarClassMetadata bean, final EasyBeansEjbJarMethodMetadata method) { ITransactionAttributeType beanTxType = bean.getTransactionAttributeType(); ITransactionManagementType beanTxManaged = bean.getTransactionManagementType(); List<? extends IJClassInterceptor> previousInterceptors = method.getInterceptors(); List<IJClassInterceptor> interceptors = new ArrayList<IJClassInterceptor>(); if (previousInterceptors != null) { interceptors.addAll(previousInterceptors); } // Bean managed or container managed ? if (ITransactionManagementType.BEAN.equals(beanTxManaged)) { // BMT if (bean.isStateful()) { interceptors.add(new JClassInterceptor(BMT_STATEFUL_INTERCEPTOR, EASYBEANS_INTERCEPTOR)); } else if (bean.isStateless()) { interceptors.add(new JClassInterceptor(BMT_STATELESS_INTERCEPTOR, EASYBEANS_INTERCEPTOR)); } else { interceptors.add(new JClassInterceptor(BMT_INTERCEPTOR, EASYBEANS_INTERCEPTOR)); } } else { // CMT ITransactionAttributeType methodTx = method.getTransactionAttributeType(); // Set method tx attribute to the class tx attribute if none was // set. if (methodTx == null) { if (!method.isInherited()) { methodTx = beanTxType; } else { // inherited method, take value of the original class methodTx = method.getOriginalEasyBeansClassMetadata().getTransactionAttributeType(); } } // Apply MDB interceptors and performs checks for authorized modes if (bean.isMdb()) { switch (methodTx) { case REQUIRED: case NOT_SUPPORTED: break; case MANDATORY: case NEVER: case REQUIRES_NEW: case SUPPORTS: default: logger.error("For MDB, the TX attribute '" + methodTx + "' is not a valid attribute (only Required or Not supported is available). " + "The error is on the method '" + method.getMethodName() + "' of class '" + method.getClassMetadata().getClassName() + "' for the bean '" + method.getClassMetadata().getLinkedBean() + "'. Sets to the default REQUIRED mode."); methodTx = REQUIRED; break; } if (NOT_SUPPORTED == methodTx) { interceptors.add(new JClassInterceptor(CMT_NOT_SUPPORTED_INTERCEPTOR, EASYBEANS_INTERCEPTOR)); } else if (REQUIRED == methodTx) { method.setTransacted(true); interceptors.add(new JClassInterceptor(MDB_CMT_REQUIRED_INTERCEPTOR, EASYBEANS_INTERCEPTOR)); } else { // invalid case throw new IllegalStateException("Shouldn't be in another mode. Expected NOT_Supported/Required and got '" + methodTx + "' for the method '" + method.getMethodName() + "' of class '" + method.getClassMetadata().getClassName() + "' for the bean '" + method.getClassMetadata().getLinkedBean() + "'. Sets to the default REQUIRED mode."); } } else { switch (methodTx) { case MANDATORY: method.setTransacted(true); interceptors.add(new JClassInterceptor(CMT_MANDATORY_INTERCEPTOR, EASYBEANS_INTERCEPTOR)); break; case NEVER: interceptors.add(new JClassInterceptor(CMT_NEVER_INTERCEPTOR, EASYBEANS_INTERCEPTOR)); break; case NOT_SUPPORTED: interceptors.add(new JClassInterceptor(CMT_NOT_SUPPORTED_INTERCEPTOR, EASYBEANS_INTERCEPTOR)); break; case REQUIRED: method.setTransacted(true); interceptors.add(new JClassInterceptor(CMT_REQUIRED_INTERCEPTOR, EASYBEANS_INTERCEPTOR)); break; case REQUIRES_NEW: method.setTransacted(true); interceptors.add(new JClassInterceptor(CMT_REQUIRES_NEW_INTERCEPTOR, EASYBEANS_INTERCEPTOR)); break; case SUPPORTS: method.setTransacted(true); interceptors.add(new JClassInterceptor(CMT_SUPPORTS_INTERCEPTOR, EASYBEANS_INTERCEPTOR)); break; default: throw new IllegalStateException("Invalid tx attribute on method '" + method.getMethodName() + "', value = '" + methodTx + "'."); } } // End CMT } // Add listener interceptor for stateul bean only if the bean // implements SessionSynchronization interface if (bean.hasSessionSynchronization() && !method.isSessionSynchronization()) { interceptors.add(new JClassInterceptor(LISTENER_SESSION_SYNCHRO_INTERCEPTOR, EASYBEANS_INTERCEPTOR)); } // Add stateful interceptor in order to synchronize the extended persistence contexts on the current transaction if (bean.isStateful() && bean.hasExtendedPersistenceContext()) { interceptors.add(new JClassInterceptor(EXTENDED_PERSISTENCECONTEXT_INTERCEPTOR, EASYBEANS_INTERCEPTOR)); } // Add a listener on transaction end for stateful session beans having a non negative idle timeout. // Because a bean in transaction should not be timed-out. if (bean.isStateful() && bean.getJavaxEjbStatefulTimeout() != null && bean.getJavaxEjbStatefulTimeout().getValue() >= 0) { interceptors.add(new JClassInterceptor(LISTENER_SFSB_TRANSACTION_INTERCEPTOR, EASYBEANS_INTERCEPTOR)); } method.setInterceptors(interceptors); } }