001    /**
002     * Copyright (c) 2000-2013 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.spring.hibernate.LastSessionRecorderUtil;
021    
022    import org.aopalliance.intercept.MethodInvocation;
023    
024    import org.springframework.transaction.PlatformTransactionManager;
025    import org.springframework.transaction.TransactionStatus;
026    import org.springframework.transaction.interceptor.TransactionAttribute;
027    import org.springframework.transaction.support.CallbackPreferringPlatformTransactionManager;
028    import org.springframework.transaction.support.TransactionCallback;
029    
030    /**
031     * @author Michael C. Han
032     * @author Shuyang Zhou
033     */
034    public class CallbackPreferringTransactionExecutor
035            extends BaseTransactionExecutor {
036    
037            @Override
038            public Object execute(
039                            PlatformTransactionManager platformTransactionManager,
040                            TransactionAttribute transactionAttribute,
041                            MethodInvocation methodInvocation)
042                    throws Throwable {
043    
044                    CallbackPreferringPlatformTransactionManager
045                            callbackPreferringPlatformTransactionManager =
046                                    (CallbackPreferringPlatformTransactionManager)
047                                            platformTransactionManager;
048    
049                    try {
050                            Object result =
051                                    callbackPreferringPlatformTransactionManager.execute(
052                                            transactionAttribute,
053                                            new CallbackPreferringTransactionCallback(
054                                                    transactionAttribute, methodInvocation));
055    
056                            if (result instanceof ThrowableHolder) {
057                                    ThrowableHolder throwableHolder = (ThrowableHolder)result;
058    
059                                    throw throwableHolder.getThrowable();
060                            }
061    
062                            return result;
063                    }
064                    catch (ThrowableHolderException the) {
065                            throw the.getCause();
066                    }
067            }
068    
069            protected static class ThrowableHolder {
070    
071                    public ThrowableHolder(Throwable throwable) {
072                            _throwable = throwable;
073                    }
074    
075                    public Throwable getThrowable() {
076                            return _throwable;
077                    }
078    
079                    private Throwable _throwable;
080    
081            }
082    
083            protected static class ThrowableHolderException extends RuntimeException {
084    
085                    public ThrowableHolderException(Throwable cause) {
086                            super(cause);
087                    }
088    
089            }
090    
091            private class CallbackPreferringTransactionCallback
092                    implements TransactionCallback<Object> {
093    
094                    private CallbackPreferringTransactionCallback(
095                            TransactionAttribute transactionAttribute,
096                            MethodInvocation methodInvocation) {
097    
098                            _transactionAttribute = transactionAttribute;
099                            _methodInvocation = methodInvocation;
100                    }
101    
102                    @Override
103                    public Object doInTransaction(TransactionStatus transactionStatus) {
104                            boolean newTransaction = transactionStatus.isNewTransaction();
105    
106                            if (newTransaction) {
107                                    TransactionalPortalCacheHelper.begin();
108    
109                                    TransactionCommitCallbackUtil.pushCallbackList();
110                            }
111    
112                            boolean rollback = false;
113    
114                            try {
115                                    if (newTransaction) {
116                                            LastSessionRecorderUtil.syncLastSessionState();
117                                    }
118    
119                                    return _methodInvocation.proceed();
120                            }
121                            catch (Throwable throwable) {
122                                    if (_transactionAttribute.rollbackOn(throwable)) {
123                                            if (newTransaction) {
124                                                    TransactionalPortalCacheHelper.rollback();
125    
126                                                    TransactionCommitCallbackUtil.popCallbackList();
127    
128                                                    EntityCacheUtil.clearLocalCache();
129                                                    FinderCacheUtil.clearLocalCache();
130    
131                                                    rollback = true;
132                                            }
133    
134                                            if (throwable instanceof RuntimeException) {
135                                                    throw (RuntimeException)throwable;
136                                            }
137                                            else {
138                                                    throw new ThrowableHolderException(throwable);
139                                            }
140                                    }
141                                    else {
142                                            return new ThrowableHolder(throwable);
143                                    }
144                            }
145                            finally {
146                                    if (newTransaction && !rollback) {
147                                            TransactionalPortalCacheHelper.commit();
148    
149                                            invokeCallbacks();
150                                    }
151                            }
152                    }
153    
154                    private MethodInvocation _methodInvocation;
155                    private TransactionAttribute _transactionAttribute;
156    
157            }
158    
159    }