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.cache.thread.local;
016    
017    import com.liferay.portal.kernel.transaction.NewTransactionLifecycleListener;
018    import com.liferay.portal.kernel.transaction.TransactionAttribute;
019    import com.liferay.portal.kernel.transaction.TransactionLifecycleListener;
020    import com.liferay.portal.kernel.transaction.TransactionStatus;
021    import com.liferay.portal.kernel.util.InitialThreadLocal;
022    
023    import java.io.Serializable;
024    
025    import java.util.EnumMap;
026    import java.util.HashMap;
027    import java.util.Map;
028    
029    /**
030     * @author Shuyang Zhou
031     */
032    public class ThreadLocalCacheManager {
033    
034            public static final TransactionLifecycleListener
035                    TRANSACTION_LIFECYCLE_LISTENER = new NewTransactionLifecycleListener() {
036    
037                            @Override
038                            protected void doCommitted(
039                                    TransactionAttribute transactionAttribute,
040                                    TransactionStatus transactionStatus) {
041    
042                                    if (!transactionAttribute.isReadOnly()) {
043                                            enable(Lifecycle.REQUEST);
044                                    }
045                            }
046    
047                            @Override
048                            protected void doCreated(
049                                    TransactionAttribute transactionAttribute,
050                                    TransactionStatus transactionStatus) {
051    
052                                    if (!transactionAttribute.isReadOnly()) {
053                                            disable(Lifecycle.REQUEST);
054                                    }
055                            }
056    
057                            @Override
058                            protected void doRollbacked(
059                                    TransactionAttribute transactionAttribute,
060                                    TransactionStatus transactionStatus, Throwable throwable) {
061    
062                                    if (!transactionAttribute.isReadOnly()) {
063                                            enable(Lifecycle.REQUEST);
064                                    }
065                            }
066    
067                    };
068    
069            public static void clearAll(Lifecycle lifecycle) {
070                    Map<Lifecycle, Map<Serializable, ThreadLocalCache<?>>>
071                            threadLocalCacheMaps = _threadLocalCacheMaps.get();
072    
073                    Map<Serializable, ThreadLocalCache<?>> threadLocalCacheMap =
074                            threadLocalCacheMaps.get(lifecycle);
075    
076                    if (threadLocalCacheMap != null) {
077                            threadLocalCacheMap.clear();
078                    }
079            }
080    
081            public static void destroy() {
082                    _threadLocalCacheMaps.remove();
083            }
084    
085            public static void disable(Lifecycle lifecycle) {
086                    Map<Lifecycle, Boolean> threadLocalCacheDisabledFlags =
087                            _threadLocalCacheDisabledFlags.get();
088    
089                    threadLocalCacheDisabledFlags.put(lifecycle, Boolean.TRUE);
090    
091                    clearAll(lifecycle);
092            }
093    
094            public static void enable(Lifecycle lifecycle) {
095                    Map<Lifecycle, Boolean> threadLocalCacheDisabledFlags =
096                            _threadLocalCacheDisabledFlags.get();
097    
098                    threadLocalCacheDisabledFlags.remove(lifecycle);
099            }
100    
101            public static <T> ThreadLocalCache<T> getThreadLocalCache(
102                    Lifecycle lifecycle, Serializable name) {
103    
104                    Map<Lifecycle, Boolean> threadLocalCacheDisabledFlags =
105                            _threadLocalCacheDisabledFlags.get();
106    
107                    if (threadLocalCacheDisabledFlags.get(lifecycle) == Boolean.TRUE) {
108                            return (ThreadLocalCache<T>)_emptyThreadLocalCache;
109                    }
110    
111                    Map<Lifecycle, Map<Serializable, ThreadLocalCache<?>>>
112                            threadLocalCacheMaps = _threadLocalCacheMaps.get();
113    
114                    Map<Serializable, ThreadLocalCache<?>> threadLocalCacheMap =
115                            threadLocalCacheMaps.get(lifecycle);
116    
117                    if (threadLocalCacheMap == null) {
118                            threadLocalCacheMap = new HashMap<>();
119    
120                            threadLocalCacheMaps.put(lifecycle, threadLocalCacheMap);
121                    }
122    
123                    ThreadLocalCache<?> threadLocalCache = threadLocalCacheMap.get(name);
124    
125                    if (threadLocalCache == null) {
126                            threadLocalCache = new ThreadLocalCache<>(name, lifecycle);
127    
128                            threadLocalCacheMap.put(name, threadLocalCache);
129                    }
130    
131                    return (ThreadLocalCache<T>)threadLocalCache;
132            }
133    
134            private static final EmptyThreadLocalCahce<?> _emptyThreadLocalCache =
135                    new EmptyThreadLocalCahce<>();
136            private static final ThreadLocal
137                    <Map<Lifecycle, Boolean>>
138                            _threadLocalCacheDisabledFlags = new InitialThreadLocal
139                                    <Map<Lifecycle, Boolean>>(
140                                            ThreadLocalCacheManager.class +
141                                                    "._threadLocalCacheDisabledFlags",
142                                            new EnumMap<Lifecycle, Boolean>(Lifecycle.class));
143            private static final ThreadLocal
144                    <Map<Lifecycle, Map<Serializable, ThreadLocalCache<?>>>>
145                            _threadLocalCacheMaps = new InitialThreadLocal
146                                    <Map<Lifecycle, Map<Serializable, ThreadLocalCache<?>>>>(
147                                            ThreadLocalCacheManager.class + "._threadLocalCacheMaps",
148                                            new EnumMap
149                                                    <Lifecycle, Map<Serializable, ThreadLocalCache<?>>>(
150                                                            Lifecycle.class));
151    
152            private static class EmptyThreadLocalCahce<T> extends ThreadLocalCache<T> {
153    
154                    public EmptyThreadLocalCahce() {
155                            super(null, null);
156                    }
157    
158                    @Override
159                    public T get(String key) {
160                            return null;
161                    }
162    
163                    @Override
164                    public void put(String key, T obj) {
165                    }
166    
167                    @Override
168                    public void remove(String key) {
169                    }
170    
171                    @Override
172                    public void removeAll() {
173                    }
174    
175                    @Override
176                    public String toString() {
177                            return EmptyThreadLocalCahce.class.getName();
178                    }
179    
180            }
181    
182    }