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    import com.liferay.portal.spring.hibernate.LastSessionRecorderUtil;
021    
022    import java.lang.reflect.Method;
023    
024    import java.util.List;
025    import java.util.concurrent.Callable;
026    
027    import org.aopalliance.intercept.MethodInterceptor;
028    import org.aopalliance.intercept.MethodInvocation;
029    
030    import org.springframework.transaction.PlatformTransactionManager;
031    import org.springframework.transaction.TransactionStatus;
032    import org.springframework.transaction.TransactionSystemException;
033    import org.springframework.transaction.interceptor.TransactionAttribute;
034    import org.springframework.transaction.interceptor.TransactionAttributeSource;
035    
036    /**
037     * @author Shuyang Zhou
038     */
039    public class TransactionInterceptor implements MethodInterceptor {
040    
041            public Object invoke(MethodInvocation methodInvocation) throws Throwable {
042                    Method method = methodInvocation.getMethod();
043    
044                    Class<?> targetClass = null;
045    
046                    Object targetBean = methodInvocation.getThis();
047    
048                    if (targetBean != null) {
049                            targetClass = targetBean.getClass();
050                    }
051    
052                    TransactionAttribute transactionAttribute =
053                            transactionAttributeSource.getTransactionAttribute(
054                                    method, targetClass);
055    
056                    if (transactionAttribute == null) {
057                            return methodInvocation.proceed();
058                    }
059    
060                    TransactionStatus transactionStatus =
061                            _platformTransactionManager.getTransaction(transactionAttribute);
062    
063                    boolean newTransaction = transactionStatus.isNewTransaction();
064    
065                    if (newTransaction) {
066                            TransactionalPortalCacheHelper.begin();
067    
068                            TransactionCommitCallbackUtil.pushCallbackList();
069                    }
070    
071                    Object returnValue = null;
072    
073                    try {
074                            if (newTransaction) {
075                                    LastSessionRecorderUtil.syncLastSessionState();
076                            }
077    
078                            returnValue = methodInvocation.proceed();
079                    }
080                    catch (Throwable throwable) {
081                            processThrowable(
082                                    throwable, transactionAttribute, transactionStatus);
083                    }
084    
085                    _platformTransactionManager.commit(transactionStatus);
086    
087                    if (newTransaction) {
088                            TransactionalPortalCacheHelper.commit();
089    
090                            invokeCallbacks();
091                    }
092    
093                    return returnValue;
094            }
095    
096            public void setPlatformTransactionManager(
097                    PlatformTransactionManager platformTransactionManager) {
098    
099                    _platformTransactionManager = platformTransactionManager;
100            }
101    
102            public void setTransactionAttributeSource(
103                    TransactionAttributeSource transactionAttributeSource) {
104    
105                    this.transactionAttributeSource = transactionAttributeSource;
106            }
107    
108            /**
109             * @deprecated {@link
110             *             #setPlatformTransactionManager(PlatformTransactionManager)}
111             */
112            public void setTransactionManager(
113                    PlatformTransactionManager platformTransactionManager) {
114    
115                    _platformTransactionManager = platformTransactionManager;
116            }
117    
118            protected void invokeCallbacks() {
119                    List<Callable<?>> callables =
120                            TransactionCommitCallbackUtil.popCallbackList();
121    
122                    for (Callable<?> callable : callables) {
123                            try {
124                                    callable.call();
125                            }
126                            catch (Exception e) {
127                                    _log.error("Failed to execute transaction commit callback", e);
128                            }
129                    }
130            }
131    
132            protected void processThrowable(
133                            Throwable throwable, TransactionAttribute transactionAttribute,
134                            TransactionStatus transactionStatus)
135                    throws Throwable {
136    
137                    if (transactionAttribute.rollbackOn(throwable)) {
138                            try {
139                                    _platformTransactionManager.rollback(transactionStatus);
140                            }
141                            catch (TransactionSystemException tse) {
142                                    _log.error(
143                                            "Application exception overridden by rollback exception",
144                                            tse);
145    
146                                    throw tse;
147                            }
148                            catch (RuntimeException re) {
149                                    _log.error(
150                                            "Application exception overridden by rollback exception",
151                                            re);
152    
153                                    throw re;
154                            }
155                            catch (Error e) {
156                                    _log.error(
157                                            "Application exception overridden by rollback error", e);
158    
159                                    throw e;
160                            }
161                            finally {
162                                    if (transactionStatus.isNewTransaction()) {
163                                            TransactionalPortalCacheHelper.rollback();
164    
165                                            TransactionCommitCallbackUtil.popCallbackList();
166                                    }
167                            }
168                    }
169                    else {
170                            boolean hasError = false;
171    
172                            try {
173                                    _platformTransactionManager.commit(transactionStatus);
174                            }
175                            catch (TransactionSystemException tse) {
176                                    _log.error(
177                                            "Application exception overridden by commit exception",
178                                            tse);
179    
180                                    hasError = true;
181    
182                                    throw tse;
183                            }
184                            catch (RuntimeException re) {
185                                    _log.error(
186                                            "Application exception overridden by commit exception", re);
187    
188                                    hasError = true;
189    
190                                    throw re;
191                            }
192                            catch (Error e) {
193                                    _log.error(
194                                            "Application exception overridden by commit error", e);
195    
196                                    hasError = true;
197    
198                                    throw e;
199                            }
200                            finally {
201                                    if (transactionStatus.isNewTransaction()) {
202                                            if (hasError) {
203                                                    TransactionalPortalCacheHelper.rollback();
204    
205                                                    TransactionCommitCallbackUtil.popCallbackList();
206                                            }
207                                            else {
208                                                    TransactionalPortalCacheHelper.commit();
209    
210                                                    invokeCallbacks();
211                                            }
212                                    }
213                            }
214                    }
215    
216                    throw throwable;
217            }
218    
219            protected TransactionAttributeSource transactionAttributeSource;
220    
221            private static Log _log = LogFactoryUtil.getLog(
222                    TransactionInterceptor.class);
223    
224            private PlatformTransactionManager _platformTransactionManager;
225    
226    }