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