001    /**
002     * Copyright (c) 2000-2011 Liferay, Inc. All rights reserved.
003     *
004     * The contents of this file are subject to the terms of the Liferay Enterprise
005     * Subscription License ("License"). You may not use this file except in
006     * compliance with the License. You can obtain a copy of the License by
007     * contacting Liferay, Inc. See the License for the specific language governing
008     * permissions and limitations under the License, including but not limited to
009     * distribution rights of the Software.
010     *
011     *
012     *
013     */
014    
015    package com.liferay.portal.spring.transaction;
016    
017    import com.liferay.portal.cache.transactional.TransactionalPortalCacheHelper;
018    import com.liferay.portal.kernel.log.Log;
019    import com.liferay.portal.kernel.log.LogFactoryUtil;
020    
021    import java.lang.reflect.Method;
022    
023    import java.util.List;
024    import java.util.concurrent.Callable;
025    
026    import org.aopalliance.intercept.MethodInterceptor;
027    import org.aopalliance.intercept.MethodInvocation;
028    
029    import org.springframework.transaction.PlatformTransactionManager;
030    import org.springframework.transaction.TransactionStatus;
031    import org.springframework.transaction.TransactionSystemException;
032    import org.springframework.transaction.interceptor.TransactionAttribute;
033    import org.springframework.transaction.interceptor.TransactionAttributeSource;
034    
035    /**
036     * @author Shuyang Zhou
037     */
038    public class TransactionInterceptor implements MethodInterceptor {
039    
040            public Object invoke(MethodInvocation methodInvocation) throws Throwable {
041                    Method method = methodInvocation.getMethod();
042    
043                    Class<?> targetClass = null;
044    
045                    Object targetBean = methodInvocation.getThis();
046    
047                    if (targetBean != null) {
048                            targetClass = targetBean.getClass();
049                    }
050    
051                    TransactionAttribute transactionAttribute =
052                            transactionAttributeSource.getTransactionAttribute(
053                                    method, targetClass);
054    
055                    if (transactionAttribute == null) {
056                            return methodInvocation.proceed();
057                    }
058    
059                    TransactionStatus transactionStatus =
060                            _platformTransactionManager.getTransaction(transactionAttribute);
061    
062                    if (transactionStatus.isNewTransaction()) {
063                            TransactionalPortalCacheHelper.begin();
064    
065                            TransactionCommitCallbackUtil.pushCallbackList();
066                    }
067    
068                    Object returnValue = null;
069    
070                    try {
071                            returnValue = methodInvocation.proceed();
072                    }
073                    catch (Throwable throwable) {
074                            processThrowable(
075                                    throwable, transactionAttribute, transactionStatus);
076                    }
077    
078                    _platformTransactionManager.commit(transactionStatus);
079    
080                    if (transactionStatus.isNewTransaction()) {
081                            TransactionalPortalCacheHelper.commit();
082    
083                            invokeCallbacks();
084                    }
085    
086                    return returnValue;
087            }
088    
089            public void setPlatformTransactionManager(
090                    PlatformTransactionManager platformTransactionManager) {
091    
092                    _platformTransactionManager = platformTransactionManager;
093            }
094    
095            public void setTransactionAttributeSource(
096                    TransactionAttributeSource transactionAttributeSource) {
097    
098                    this.transactionAttributeSource = transactionAttributeSource;
099            }
100    
101            /**
102             * @deprecated {@link #setPlatformTransactionManager(PlatformTransactionManager)}
103             */
104            public void setTransactionManager(
105                    PlatformTransactionManager platformTransactionManager) {
106    
107                    _platformTransactionManager = platformTransactionManager;
108            }
109    
110            protected void processThrowable(
111                            Throwable throwable, TransactionAttribute transactionAttribute,
112                            TransactionStatus transactionStatus)
113                    throws Throwable {
114    
115                    if (transactionAttribute.rollbackOn(throwable)) {
116                            try {
117                                    _platformTransactionManager.rollback(transactionStatus);
118                            }
119                            catch (TransactionSystemException tse) {
120                                    _log.error(
121                                            "Application exception overridden by rollback exception",
122                                            tse);
123    
124                                    throw tse;
125                            }
126                            catch (RuntimeException re) {
127                                    _log.error(
128                                            "Application exception overridden by rollback exception",
129                                            re);
130    
131                                    throw re;
132                            }
133                            catch (Error e) {
134                                    _log.error(
135                                            "Application exception overridden by rollback error", e);
136    
137                                    throw e;
138                            }
139                            finally {
140                                    if (transactionStatus.isNewTransaction()) {
141                                            TransactionalPortalCacheHelper.rollback();
142    
143                                            TransactionCommitCallbackUtil.popCallbackList();
144                                    }
145                            }
146                    }
147                    else {
148                            boolean hasError = false;
149    
150                            try {
151                                    _platformTransactionManager.commit(transactionStatus);
152                            }
153                            catch (TransactionSystemException tse) {
154                                    _log.error(
155                                            "Application exception overridden by commit exception",
156                                            tse);
157    
158                                    hasError = true;
159    
160                                    throw tse;
161                            }
162                            catch (RuntimeException re) {
163                                    _log.error(
164                                            "Application exception overridden by commit exception", re);
165    
166                                    hasError = true;
167    
168                                    throw re;
169                            }
170                            catch (Error e) {
171                                    _log.error(
172                                            "Application exception overridden by commit error", e);
173    
174                                    hasError = true;
175    
176                                    throw e;
177                            }
178                            finally {
179                                    if (transactionStatus.isNewTransaction()) {
180                                            if (hasError) {
181                                                    TransactionalPortalCacheHelper.rollback();
182    
183                                                    TransactionCommitCallbackUtil.popCallbackList();
184                                            }
185                                            else {
186                                                    TransactionalPortalCacheHelper.commit();
187    
188                                                    invokeCallbacks();
189                                            }
190                                    }
191                            }
192                    }
193    
194                    throw throwable;
195            }
196    
197            private void invokeCallbacks() {
198                    List<Callable<?>> callables =
199                            TransactionCommitCallbackUtil.popCallbackList();
200    
201                    for (Callable<?> callable : callables) {
202                            try {
203                                    callable.call();
204                            }
205                            catch (Exception e) {
206                                    _log.error("Failed to execute transaction commit callback", e);
207                            }
208                    }
209            }
210    
211            protected TransactionAttributeSource transactionAttributeSource;
212    
213            private static Log _log = LogFactoryUtil.getLog(
214                    TransactionInterceptor.class);
215    
216            private PlatformTransactionManager _platformTransactionManager;
217    
218    }