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