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