001
014
015 package com.liferay.portal.kernel.cache.transactional;
016
017 import com.liferay.portal.kernel.cache.PortalCache;
018 import com.liferay.portal.kernel.cache.PortalCacheHelperUtil;
019 import com.liferay.portal.kernel.cache.SkipReplicationThreadLocal;
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 <K extends Serializable, V> V get(
158 PortalCache<K, V> portalCache, K key) {
159
160 PortalCacheMap portalCacheMap = _peekPortalCacheMap();
161
162 UncommittedBuffer uncommittedBuffer = portalCacheMap.get(portalCache);
163
164 if (uncommittedBuffer == null) {
165 return null;
166 }
167
168 ValueEntry valueEntry = uncommittedBuffer.get(key);
169
170 if (valueEntry == null) {
171 return null;
172 }
173
174 return (V)valueEntry._value;
175 }
176
177 public static Serializable getNullHolder() {
178 return _NULL_HOLDER;
179 }
180
181 public static boolean isEnabled() {
182 if (!_isTransactionalCacheEnabled()) {
183 return false;
184 }
185
186 List<PortalCacheMap> portalCacheMaps =
187 _portalCacheMapsThreadLocal.get();
188
189 return !portalCacheMaps.isEmpty();
190 }
191
192 public static <K extends Serializable, V> void put(
193 PortalCache<K, V> portalCache, K key, V value, int ttl) {
194
195 PortalCacheMap portalCacheMap = _peekPortalCacheMap();
196
197 UncommittedBuffer uncommittedBuffer = portalCacheMap.get(portalCache);
198
199 if (uncommittedBuffer == null) {
200 uncommittedBuffer = new UncommittedBuffer();
201
202 portalCacheMap.put(portalCache, uncommittedBuffer);
203 }
204
205 uncommittedBuffer.put(
206 key,
207 new ValueEntry(value, ttl, SkipReplicationThreadLocal.isEnabled()));
208 }
209
210 public static <K extends Serializable, V> void removeAll(
211 PortalCache<K, V> portalCache) {
212
213 PortalCacheMap portalCacheMap = _peekPortalCacheMap();
214
215 UncommittedBuffer uncommittedBuffer = portalCacheMap.get(portalCache);
216
217 if (uncommittedBuffer == null) {
218 uncommittedBuffer = new UncommittedBuffer();
219
220 portalCacheMap.put(portalCache, uncommittedBuffer);
221 }
222
223 uncommittedBuffer.removeAll(SkipReplicationThreadLocal.isEnabled());
224 }
225
226 public static void rollback() {
227 PortalCacheMap portalCacheMap = _popPortalCacheMap();
228
229 portalCacheMap.clear();
230 }
231
232 protected static class PortalCacheMap
233 extends HashMap
234 <PortalCache<? extends Serializable, ?>, UncommittedBuffer> {
235 }
236
237 private static boolean _isTransactionalCacheEnabled() {
238 if (_transactionalCacheEnabled == null) {
239 _transactionalCacheEnabled = GetterUtil.getBoolean(
240 PropsUtil.get(PropsKeys.TRANSACTIONAL_CACHE_ENABLED));
241 }
242
243 return _transactionalCacheEnabled;
244 }
245
246 private static PortalCacheMap _peekPortalCacheMap() {
247 List<PortalCacheMap> portalCacheMaps =
248 _portalCacheMapsThreadLocal.get();
249
250 return portalCacheMaps.get(portalCacheMaps.size() - 1);
251 }
252
253 private static PortalCacheMap _popPortalCacheMap() {
254 List<PortalCacheMap> portalCacheMaps =
255 _portalCacheMapsThreadLocal.get();
256
257 return portalCacheMaps.remove(portalCacheMaps.size() - 1);
258 }
259
260 private static final Serializable _NULL_HOLDER = "NULL_HOLDER";
261
262 private static final ValueEntry _NULL_HOLDER_VALUE_ENTRY = new ValueEntry(
263 _NULL_HOLDER, PortalCache.DEFAULT_TIME_TO_LIVE, false);
264
265 private static final ThreadLocal<List<List<PortalCacheMap>>>
266 _backupPortalCacheMapsThreadLocal =
267 new InitialThreadLocal<List<List<PortalCacheMap>>>(
268 TransactionalPortalCacheHelper.class.getName() +
269 "._backupPortalCacheMapsThreadLocal",
270 new ArrayList<List<PortalCacheMap>>());
271 private static final ThreadLocal<List<PortalCacheMap>>
272 _portalCacheMapsThreadLocal =
273 new InitialThreadLocal<List<PortalCacheMap>>(
274 TransactionalPortalCacheHelper.class.getName() +
275 "._portalCacheMapsThreadLocal",
276 new ArrayList<PortalCacheMap>());
277
278 private volatile static Boolean _transactionalCacheEnabled;
279
280 private static class UncommittedBuffer {
281
282 public void commitTo(PortalCache<Serializable, Object> portalCache) {
283 if (_removeAll) {
284 if (_skipReplicator) {
285 PortalCacheHelperUtil.removeAllWithoutReplicator(
286 portalCache);
287 }
288 else {
289 portalCache.removeAll();
290 }
291 }
292
293 for (Map.Entry<? extends Serializable, ValueEntry> entry :
294 _uncommittedMap.entrySet()) {
295
296 ValueEntry valueEntry = entry.getValue();
297
298 valueEntry.commitTo(portalCache, entry.getKey());
299 }
300 }
301
302 public ValueEntry get(Serializable key) {
303 ValueEntry valueEntry = _uncommittedMap.get(key);
304
305 if ((valueEntry == null) && _removeAll) {
306 valueEntry = _NULL_HOLDER_VALUE_ENTRY;
307 }
308
309 return valueEntry;
310 }
311
312 public void put(Serializable key, ValueEntry valueEntry) {
313 ValueEntry oldValueEntry = _uncommittedMap.put(key, valueEntry);
314
315 if (oldValueEntry != null) {
316 oldValueEntry.merge(valueEntry);
317 }
318 }
319
320 public void removeAll(boolean skipReplicator) {
321 _uncommittedMap.clear();
322
323 _removeAll = true;
324
325 if (_skipReplicator) {
326 _skipReplicator = skipReplicator;
327 }
328 }
329
330 private boolean _removeAll;
331 private boolean _skipReplicator = true;
332 private final Map<Serializable, ValueEntry> _uncommittedMap =
333 new HashMap<>();
334
335 }
336
337 private static class ValueEntry {
338
339 public ValueEntry(Object value, int ttl, boolean skipReplicator) {
340 _value = value;
341 _ttl = ttl;
342 _skipReplicator = skipReplicator;
343 }
344
345 public void commitTo(
346 PortalCache<Serializable, Object> portalCache, Serializable key) {
347
348 if (_value == _NULL_HOLDER) {
349 if (_skipReplicator) {
350 PortalCacheHelperUtil.removeWithoutReplicator(
351 portalCache, key);
352 }
353 else {
354 portalCache.remove(key);
355 }
356 }
357 else {
358 if (_skipReplicator) {
359 PortalCacheHelperUtil.putWithoutReplicator(
360 portalCache, key, _value, _ttl);
361 }
362 else {
363 portalCache.put(key, _value, _ttl);
364 }
365 }
366 }
367
368 public void merge(ValueEntry valueEntry) {
369 if (!_skipReplicator) {
370 valueEntry._skipReplicator = false;
371 }
372 }
373
374 private boolean _skipReplicator;
375 private final int _ttl;
376 private final Object _value;
377
378 }
379
380 }