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.cache.transactional;
016    
017    import com.liferay.portal.kernel.cache.PortalCache;
018    import com.liferay.portal.kernel.dao.orm.EntityCacheUtil;
019    import com.liferay.portal.kernel.dao.orm.FinderCacheUtil;
020    import com.liferay.portal.kernel.transaction.TransactionAttribute;
021    import com.liferay.portal.kernel.transaction.TransactionLifecycleListener;
022    import com.liferay.portal.kernel.transaction.TransactionStatus;
023    import com.liferay.portal.kernel.util.InitialThreadLocal;
024    import com.liferay.portal.util.PropsValues;
025    
026    import java.io.Serializable;
027    
028    import java.util.ArrayList;
029    import java.util.HashMap;
030    import java.util.List;
031    import java.util.Map;
032    
033    /**
034     * @author Shuyang Zhou
035     */
036    public class TransactionalPortalCacheHelper {
037    
038            public static final TransactionLifecycleListener
039                    TRANSACTION_LIFECYCLE_LISTENER = new TransactionLifecycleListener() {
040    
041                            @Override
042                            public void created(
043                                    TransactionAttribute transactionAttribute,
044                                    TransactionStatus transactionStatus) {
045    
046                                    begin();
047                            }
048    
049                            @Override
050                            public void committed(
051                                    TransactionAttribute transactionAttribute,
052                                    TransactionStatus transactionStatus) {
053    
054                                    commit();
055                            }
056    
057                            @Override
058                            public void rollbacked(
059                                    TransactionAttribute transactionAttribute,
060                                    TransactionStatus transactionStatus, Throwable throwable) {
061    
062                                    rollback();
063    
064                                    EntityCacheUtil.clearLocalCache();
065                                    FinderCacheUtil.clearLocalCache();
066                            }
067    
068                    };
069    
070            public static void begin() {
071                    if (!PropsValues.TRANSACTIONAL_CACHE_ENABLED) {
072                            return;
073                    }
074    
075                    _pushPortalCacheMap();
076            }
077    
078            public static void commit() {
079                    if (!PropsValues.TRANSACTIONAL_CACHE_ENABLED) {
080                            return;
081                    }
082    
083                    PortalCacheMap portalCacheMap = _popPortalCacheMap();
084    
085                    for (Map.Entry
086                                    <PortalCache<? extends Serializable, ?>, UncommittedBuffer>
087                                            portalCacheMapEntry : portalCacheMap.entrySet()) {
088    
089                            PortalCache<Serializable, Object> portalCache =
090                                    (PortalCache<Serializable, Object>)portalCacheMapEntry.getKey();
091    
092                            UncommittedBuffer uncommittedBuffer =
093                                    portalCacheMapEntry.getValue();
094    
095                            uncommittedBuffer.commitTo(portalCache);
096                    }
097    
098                    portalCacheMap.clear();
099            }
100    
101            public static boolean isEnabled() {
102                    if (!PropsValues.TRANSACTIONAL_CACHE_ENABLED) {
103                            return false;
104                    }
105    
106                    List<PortalCacheMap> portalCacheMaps =
107                            _portalCacheMapsThreadLocal.get();
108    
109                    return !portalCacheMaps.isEmpty();
110            }
111    
112            public static void rollback() {
113                    if (!PropsValues.TRANSACTIONAL_CACHE_ENABLED) {
114                            return;
115                    }
116    
117                    PortalCacheMap portalCacheMap = _popPortalCacheMap();
118    
119                    portalCacheMap.clear();
120            }
121    
122            protected static <K extends Serializable, V> V get(
123                    PortalCache<K, V> portalCache, K key) {
124    
125                    PortalCacheMap portalCacheMap = _peekPortalCacheMap();
126    
127                    UncommittedBuffer uncommittedBuffer = portalCacheMap.get(portalCache);
128    
129                    if (uncommittedBuffer == null) {
130                            return null;
131                    }
132    
133                    ValueEntry valueEntry = uncommittedBuffer.get(key);
134    
135                    if (valueEntry == null) {
136                            return null;
137                    }
138    
139                    return (V)valueEntry._value;
140            }
141    
142            protected static <K extends Serializable, V> void put(
143                    PortalCache<K, V> portalCache, K key, V value, boolean quiet, int ttl) {
144    
145                    PortalCacheMap portalCacheMap = _peekPortalCacheMap();
146    
147                    UncommittedBuffer uncommittedBuffer = portalCacheMap.get(portalCache);
148    
149                    if (uncommittedBuffer == null) {
150                            uncommittedBuffer = new UncommittedBuffer();
151    
152                            portalCacheMap.put(portalCache, uncommittedBuffer);
153                    }
154    
155                    uncommittedBuffer.put(key, new ValueEntry(value, ttl, quiet));
156            }
157    
158            protected static <K extends Serializable, V> void removeAll(
159                    PortalCache<K, V> portalCache) {
160    
161                    PortalCacheMap portalCacheMap = _peekPortalCacheMap();
162    
163                    UncommittedBuffer uncommittedBuffer = portalCacheMap.get(portalCache);
164    
165                    if (uncommittedBuffer == null) {
166                            uncommittedBuffer = new UncommittedBuffer();
167    
168                            portalCacheMap.put(portalCache, uncommittedBuffer);
169                    }
170    
171                    uncommittedBuffer.removeAll();
172            }
173    
174            private static PortalCacheMap _peekPortalCacheMap() {
175                    List<PortalCacheMap> portalCacheMaps =
176                            _portalCacheMapsThreadLocal.get();
177    
178                    return portalCacheMaps.get(portalCacheMaps.size() - 1);
179            }
180    
181            private static PortalCacheMap _popPortalCacheMap() {
182                    List<PortalCacheMap> portalCacheMaps =
183                            _portalCacheMapsThreadLocal.get();
184    
185                    return portalCacheMaps.remove(portalCacheMaps.size() - 1);
186            }
187    
188            private static void _pushPortalCacheMap() {
189                    List<PortalCacheMap> portalCacheMaps =
190                            _portalCacheMapsThreadLocal.get();
191    
192                    portalCacheMaps.add(new PortalCacheMap());
193            }
194    
195            private static final ValueEntry _NULL_HOLDER_VALUE_ENTRY = new ValueEntry(
196                    TransactionalPortalCache.NULL_HOLDER, PortalCache.DEFAULT_TIME_TO_LIVE,
197                    false);
198    
199            private static final ThreadLocal<List<PortalCacheMap>>
200                    _portalCacheMapsThreadLocal =
201                            new InitialThreadLocal<List<PortalCacheMap>>(
202                                    TransactionalPortalCacheHelper.class.getName() +
203                                            "._portalCacheMapsThreadLocal",
204                                    new ArrayList<PortalCacheMap>());
205    
206            private static class PortalCacheMap
207                    extends HashMap
208                            <PortalCache<? extends Serializable, ?>, UncommittedBuffer> {
209            }
210    
211            private static class UncommittedBuffer {
212    
213                    public void commitTo(PortalCache<Serializable, Object> portalCache) {
214                            if (_removeAll) {
215                                    portalCache.removeAll();
216                            }
217    
218                            for (Map.Entry<? extends Serializable, ValueEntry> entry :
219                                            _uncommittedMap.entrySet()) {
220    
221                                    ValueEntry valueEntry = entry.getValue();
222    
223                                    valueEntry.commitTo(portalCache, entry.getKey());
224                            }
225                    }
226    
227                    public ValueEntry get(Serializable key) {
228                            ValueEntry valueEntry = _uncommittedMap.get(key);
229    
230                            if ((valueEntry == null) && _removeAll) {
231                                    valueEntry = _NULL_HOLDER_VALUE_ENTRY;
232                            }
233    
234                            return valueEntry;
235                    }
236    
237                    public void put(Serializable key, ValueEntry valueEntry) {
238                            ValueEntry oldValueEntry = _uncommittedMap.put(key, valueEntry);
239    
240                            if (oldValueEntry != null) {
241                                    oldValueEntry.merge(valueEntry);
242                            }
243                    }
244    
245                    public void removeAll() {
246                            _uncommittedMap.clear();
247    
248                            _removeAll = true;
249                    }
250    
251                    private boolean _removeAll;
252                    private final Map<Serializable, ValueEntry> _uncommittedMap =
253                            new HashMap<Serializable, ValueEntry>();
254    
255            }
256    
257            private static class ValueEntry {
258    
259                    public ValueEntry(Object value, int ttl, boolean quiet) {
260                            _value = value;
261                            _ttl = ttl;
262                            _quiet = quiet;
263                    }
264    
265                    public void commitTo(
266                            PortalCache<Serializable, Object> portalCache, Serializable key) {
267    
268                            if (_value == TransactionalPortalCache.NULL_HOLDER) {
269                                    portalCache.remove(key);
270                            }
271                            else if (_quiet) {
272                                    portalCache.putQuiet(key, _value, _ttl);
273                            }
274                            else {
275                                    portalCache.put(key, _value, _ttl);
276                            }
277                    }
278    
279                    public void merge(ValueEntry valueEntry) {
280                            if (!_quiet) {
281                                    valueEntry._quiet = false;
282                            }
283                    }
284    
285                    private boolean _quiet;
286                    private final int _ttl;
287                    private Object _value;
288    
289            }
290    
291    }