001    /**
002     * Copyright (c) 2000-2011 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 processThrowable(
112                            Throwable throwable, TransactionAttribute transactionAttribute,
113                            TransactionStatus transactionStatus)
114                    throws Throwable {
115    
116                    if (transactionAttribute.rollbackOn(throwable)) {
117                            try {
118                                    _platformTransactionManager.rollback(transactionStatus);
119                            }
120                            catch (TransactionSystemException tse) {
121                                    _log.error(
122                                            "Application exception overridden by rollback exception",
123                                            tse);
124    
125                                    throw tse;
126                            }
127                            catch (RuntimeException re) {
128                                    _log.error(
129                                            "Application exception overridden by rollback exception",
130                                            re);
131    
132                                    throw re;
133                            }
134                            catch (Error e) {
135                                    _log.error(
136                                            "Application exception overridden by rollback error", e);
137    
138                                    throw e;
139                            }
140                            finally {
141                                    if (transactionStatus.isNewTransaction()) {
142                                            TransactionalPortalCacheHelper.rollback();
143    
144                                            TransactionCommitCallbackUtil.popCallbackList();
145                                    }
146                            }
147                    }
148                    else {
149                            boolean hasError = false;
150    
151                            try {
152                                    _platformTransactionManager.commit(transactionStatus);
153                            }
154                            catch (TransactionSystemException tse) {
155                                    _log.error(
156                                            "Application exception overridden by commit exception",
157                                            tse);
158    
159                                    hasError = true;
160    
161                                    throw tse;
162                            }
163                            catch (RuntimeException re) {
164                                    _log.error(
165                                            "Application exception overridden by commit exception", re);
166    
167                                    hasError = true;
168    
169                                    throw re;
170                            }
171                            catch (Error e) {
172                                    _log.error(
173                                            "Application exception overridden by commit error", e);
174    
175                                    hasError = true;
176    
177                                    throw e;
178                            }
179                            finally {
180                                    if (transactionStatus.isNewTransaction()) {
181                                            if (hasError) {
182                                                    TransactionalPortalCacheHelper.rollback();
183    
184                                                    TransactionCommitCallbackUtil.popCallbackList();
185                                            }
186                                            else {
187                                                    TransactionalPortalCacheHelper.commit();
188    
189                                                    invokeCallbacks();
190                                            }
191                                    }
192                            }
193                    }
194    
195                    throw throwable;
196            }
197    
198            private void invokeCallbacks() {
199                    List<Callable<?>> callables =
200                            TransactionCommitCallbackUtil.popCallbackList();
201    
202                    for (Callable<?> callable : callables) {
203                            try {
204                                    callable.call();
205                            }
206                            catch (Exception e) {
207                                    _log.error("Failed to execute transaction commit callback", e);
208                            }
209                    }
210            }
211    
212            protected TransactionAttributeSource transactionAttributeSource;
213    
214            private static Log _log = LogFactoryUtil.getLog(
215                    TransactionInterceptor.class);
216    
217            private PlatformTransactionManager _platformTransactionManager;
218    
219    }