001    /**
002     * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
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
103             *             #setPlatformTransactionManager(PlatformTransactionManager)}
104             */
105            public void setTransactionManager(
106                    PlatformTransactionManager platformTransactionManager) {
107    
108                    _platformTransactionManager = platformTransactionManager;
109            }
110    
111            protected void invokeCallbacks() {
112                    List<Callable<?>> callables =
113                            TransactionCommitCallbackUtil.popCallbackList();
114    
115                    for (Callable<?> callable : callables) {
116                            try {
117                                    callable.call();
118                            }
119                            catch (Exception e) {
120                                    _log.error("Failed to execute transaction commit callback", e);
121                            }
122                    }
123            }
124    
125            protected void processThrowable(
126                            Throwable throwable, TransactionAttribute transactionAttribute,
127                            TransactionStatus transactionStatus)
128                    throws Throwable {
129    
130                    if (transactionAttribute.rollbackOn(throwable)) {
131                            try {
132                                    _platformTransactionManager.rollback(transactionStatus);
133                            }
134                            catch (TransactionSystemException tse) {
135                                    _log.error(
136                                            "Application exception overridden by rollback exception",
137                                            tse);
138    
139                                    throw tse;
140                            }
141                            catch (RuntimeException re) {
142                                    _log.error(
143                                            "Application exception overridden by rollback exception",
144                                            re);
145    
146                                    throw re;
147                            }
148                            catch (Error e) {
149                                    _log.error(
150                                            "Application exception overridden by rollback error", e);
151    
152                                    throw e;
153                            }
154                            finally {
155                                    if (transactionStatus.isNewTransaction()) {
156                                            TransactionalPortalCacheHelper.rollback();
157    
158                                            TransactionCommitCallbackUtil.popCallbackList();
159                                    }
160                            }
161                    }
162                    else {
163                            boolean hasError = false;
164    
165                            try {
166                                    _platformTransactionManager.commit(transactionStatus);
167                            }
168                            catch (TransactionSystemException tse) {
169                                    _log.error(
170                                            "Application exception overridden by commit exception",
171                                            tse);
172    
173                                    hasError = true;
174    
175                                    throw tse;
176                            }
177                            catch (RuntimeException re) {
178                                    _log.error(
179                                            "Application exception overridden by commit exception", re);
180    
181                                    hasError = true;
182    
183                                    throw re;
184                            }
185                            catch (Error e) {
186                                    _log.error(
187                                            "Application exception overridden by commit error", e);
188    
189                                    hasError = true;
190    
191                                    throw e;
192                            }
193                            finally {
194                                    if (transactionStatus.isNewTransaction()) {
195                                            if (hasError) {
196                                                    TransactionalPortalCacheHelper.rollback();
197    
198                                                    TransactionCommitCallbackUtil.popCallbackList();
199                                            }
200                                            else {
201                                                    TransactionalPortalCacheHelper.commit();
202    
203                                                    invokeCallbacks();
204                                            }
205                                    }
206                            }
207                    }
208    
209                    throw throwable;
210            }
211    
212            protected TransactionAttributeSource transactionAttributeSource;
213    
214            private static Log _log = LogFactoryUtil.getLog(
215                    TransactionInterceptor.class);
216    
217            private PlatformTransactionManager _platformTransactionManager;
218    
219    }