001    /**
002     * Copyright (c) 2000-present 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.kernel.transaction;
016    
017    import com.liferay.portal.kernel.log.Log;
018    import com.liferay.portal.kernel.log.LogFactoryUtil;
019    import com.liferay.portal.kernel.util.AutoResetThreadLocal;
020    
021    import java.util.ArrayList;
022    import java.util.Collections;
023    import java.util.List;
024    import java.util.concurrent.Callable;
025    
026    /**
027     * @author Shuyang Zhou
028     */
029    public class TransactionCommitCallbackUtil {
030    
031            public static final TransactionLifecycleListener
032                    TRANSACTION_LIFECYCLE_LISTENER = new NewTransactionLifecycleListener() {
033    
034                            @Override
035                            protected void doCreated(
036                                    TransactionAttribute transactionAttribute,
037                                    TransactionStatus transactionStatus) {
038    
039                                    pushCallbackList();
040                            }
041    
042                            @Override
043                            protected void doCommitted(
044                                    TransactionAttribute transactionAttribute,
045                                    TransactionStatus transactionStatus) {
046    
047                                    List<Callable<?>> callables = popCallbackList();
048    
049                                    for (Callable<?> callable : callables) {
050                                            try {
051                                                    callable.call();
052                                            }
053                                            catch (Exception e) {
054                                                    _log.error(
055                                                            "Unable to execute transaction commit callback", e);
056                                            }
057                                    }
058                            }
059    
060                            @Override
061                            protected void doRollbacked(
062                                    TransactionAttribute transactionAttribute,
063                                    TransactionStatus transactionStatus, Throwable throwable) {
064    
065                                    popCallbackList();
066                            }
067    
068                    };
069    
070            public static void registerCallback(Callable<?> callable) {
071                    List<List<Callable<?>>> callbackListList =
072                            _callbackListListThreadLocal.get();
073    
074                    if (callbackListList.isEmpty()) {
075    
076                            // Not within a transaction boundary, should only happen during an
077                            // upgrade and verify process. See DBUpgrader#_disableTransactions.
078    
079                            try {
080                                    callable.call();
081                            }
082                            catch (Exception e) {
083                                    throw new RuntimeException(e);
084                            }
085                    }
086                    else {
087                            int index = callbackListList.size() - 1;
088    
089                            List<Callable<?>> callableList = callbackListList.get(index);
090    
091                            if (callableList == Collections.<Callable<?>>emptyList()) {
092                                    callableList = new ArrayList<>();
093    
094                                    callbackListList.set(index, callableList);
095                            }
096    
097                            callableList.add(callable);
098                    }
099            }
100    
101            protected static List<Callable<?>> popCallbackList() {
102                    List<List<Callable<?>>> callbackListList =
103                            _callbackListListThreadLocal.get();
104    
105                    return callbackListList.remove(callbackListList.size() - 1);
106            }
107    
108            protected static void pushCallbackList() {
109                    List<List<Callable<?>>> callbackListList =
110                            _callbackListListThreadLocal.get();
111    
112                    callbackListList.add(Collections.<Callable<?>>emptyList());
113            }
114    
115            private static final Log _log = LogFactoryUtil.getLog(
116                    TransactionCommitCallbackUtil.class);
117    
118            private static final ThreadLocal<List<List<Callable<?>>>>
119                    _callbackListListThreadLocal =
120                            new AutoResetThreadLocal<List<List<Callable<?>>>>(
121                                    TransactionCommitCallbackUtil.class +
122                                            "._callbackListListThreadLocal") {
123    
124                                    @Override
125                                    protected List<List<Callable<?>>> initialValue() {
126                                            return new ArrayList<>();
127                                    }
128    
129                            };
130    
131    }