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