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;
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            public static void clearAll(Lifecycle lifecycle) {
069                    Map<Lifecycle, Map<Serializable, ThreadLocalCache<?>>>
070                            threadLocalCacheMaps = _threadLocalCacheMaps.get();
071    
072                    Map<Serializable, ThreadLocalCache<?>> threadLocalCacheMap =
073                            threadLocalCacheMaps.get(lifecycle);
074    
075                    if (threadLocalCacheMap != null) {
076                            threadLocalCacheMap.clear();
077                    }
078            }
079    
080            public static void destroy() {
081                    _threadLocalCacheMaps.remove();
082            }
083    
084            public static void disable(Lifecycle lifecycle) {
085                    Map<Lifecycle, Boolean> threadLocalCacheDisabledFlags =
086                            _threadLocalCacheDisabledFlags.get();
087    
088                    threadLocalCacheDisabledFlags.put(lifecycle, Boolean.TRUE);
089    
090                    clearAll(lifecycle);
091            }
092    
093            public static void enable(Lifecycle lifecycle) {
094                    Map<Lifecycle, Boolean> threadLocalCacheDisabledFlags =
095                            _threadLocalCacheDisabledFlags.get();
096    
097                    threadLocalCacheDisabledFlags.remove(lifecycle);
098            }
099    
100            public static <T> ThreadLocalCache<T> getThreadLocalCache(
101                    Lifecycle lifecycle, Serializable name) {
102    
103                    Map<Lifecycle, Boolean> threadLocalCacheDisabledFlags =
104                            _threadLocalCacheDisabledFlags.get();
105    
106                    if (threadLocalCacheDisabledFlags.get(lifecycle) == Boolean.TRUE) {
107                            return (ThreadLocalCache<T>)_emptyThreadLocalCache;
108                    }
109    
110                    Map<Lifecycle, Map<Serializable, ThreadLocalCache<?>>>
111                            threadLocalCacheMaps = _threadLocalCacheMaps.get();
112    
113                    Map<Serializable, ThreadLocalCache<?>> threadLocalCacheMap =
114                            threadLocalCacheMaps.get(lifecycle);
115    
116                    if (threadLocalCacheMap == null) {
117                            threadLocalCacheMap = new HashMap<>();
118    
119                            threadLocalCacheMaps.put(lifecycle, threadLocalCacheMap);
120                    }
121    
122                    ThreadLocalCache<?> threadLocalCache = threadLocalCacheMap.get(name);
123    
124                    if (threadLocalCache == null) {
125                            threadLocalCache = new ThreadLocalCache<>(name, lifecycle);
126    
127                            threadLocalCacheMap.put(name, threadLocalCache);
128                    }
129    
130                    return (ThreadLocalCache<T>)threadLocalCache;
131            }
132    
133            private static final EmptyThreadLocalCahce<?> _emptyThreadLocalCache =
134                    new EmptyThreadLocalCahce<>();
135            private static final ThreadLocal
136                    <Map<Lifecycle, Boolean>>
137                            _threadLocalCacheDisabledFlags = new InitialThreadLocal
138                                    <Map<Lifecycle, Boolean>>(
139                                            ThreadLocalCacheManager.class +
140                                                    "._threadLocalCacheDisabledFlags",
141                                            new EnumMap<Lifecycle, Boolean>(Lifecycle.class));
142            private static final ThreadLocal
143                    <Map<Lifecycle, Map<Serializable, ThreadLocalCache<?>>>>
144                            _threadLocalCacheMaps = new InitialThreadLocal
145                                    <Map<Lifecycle, Map<Serializable, ThreadLocalCache<?>>>>(
146                                            ThreadLocalCacheManager.class + "._threadLocalCacheMaps",
147                                            new EnumMap
148                                                    <Lifecycle, Map<Serializable, ThreadLocalCache<?>>>(
149                                                            Lifecycle.class));
150    
151            private static class EmptyThreadLocalCahce<T> extends ThreadLocalCache<T> {
152    
153                    public EmptyThreadLocalCahce() {
154                            super(null, null);
155                    }
156    
157                    @Override
158                    public T get(String key) {
159                            return null;
160                    }
161    
162                    @Override
163                    public void put(String key, T obj) {
164                    }
165    
166                    @Override
167                    public void remove(String key) {
168                    }
169    
170                    @Override
171                    public void removeAll() {
172                    }
173    
174                    @Override
175                    public String toString() {
176                            return EmptyThreadLocalCahce.class.getName();
177                    }
178    
179            }
180    
181    }