com.liferay.portal.kernel.cache.transactional.TransactionalPortalCacheUtil Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of com.liferay.portal.kernel Show documentation
Show all versions of com.liferay.portal.kernel Show documentation
Contains interfaces for the portal services. Interfaces are only loaded by the global class loader and are shared by all plugins.
/**
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
package com.liferay.portal.kernel.cache.transactional;
import com.liferay.petra.concurrent.ConcurrentReferenceValueHashMap;
import com.liferay.petra.lang.CentralizedThreadLocal;
import com.liferay.petra.memory.FinalizeManager;
import com.liferay.portal.kernel.cache.PortalCache;
import com.liferay.portal.kernel.cache.PortalCacheHelperUtil;
import com.liferay.portal.kernel.cache.SkipReplicationThreadLocal;
import com.liferay.portal.kernel.dao.orm.EntityCacheUtil;
import com.liferay.portal.kernel.dao.orm.FinderCacheUtil;
import com.liferay.portal.kernel.transaction.Propagation;
import com.liferay.portal.kernel.transaction.TransactionAttribute;
import com.liferay.portal.kernel.transaction.TransactionDefinition;
import com.liferay.portal.kernel.transaction.TransactionLifecycleListener;
import com.liferay.portal.kernel.transaction.TransactionStatus;
import com.liferay.portal.kernel.util.GetterUtil;
import com.liferay.portal.kernel.util.PropsKeys;
import com.liferay.portal.kernel.util.PropsUtil;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* @author Shuyang Zhou
*/
public class TransactionalPortalCacheUtil {
public static final TransactionLifecycleListener
TRANSACTION_LIFECYCLE_LISTENER = new TransactionLifecycleListener() {
@Override
public void committed(
TransactionAttribute transactionAttribute,
TransactionStatus transactionStatus) {
if (!_isTransactionalCacheEnabled()) {
return;
}
Propagation propagation = transactionAttribute.getPropagation();
if (propagation.value() >=
TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
List> backupPortalCacheMaps =
_backupPortalCacheMapsThreadLocal.get();
_portalCacheMapsThreadLocal.set(
backupPortalCacheMaps.remove(
backupPortalCacheMaps.size() - 1));
}
else if (transactionStatus.isNewTransaction()) {
commit(transactionAttribute.isReadOnly());
}
}
@Override
public void created(
TransactionAttribute transactionAttribute,
TransactionStatus transactionStatus) {
if (!_isTransactionalCacheEnabled()) {
return;
}
Propagation propagation = transactionAttribute.getPropagation();
if (propagation.value() >=
TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
List> backupPortalCacheMaps =
_backupPortalCacheMapsThreadLocal.get();
backupPortalCacheMaps.add(
_portalCacheMapsThreadLocal.get());
_portalCacheMapsThreadLocal.remove();
}
else if (transactionStatus.isNewTransaction()) {
begin();
}
}
@Override
public void rollbacked(
TransactionAttribute transactionAttribute,
TransactionStatus transactionStatus, Throwable throwable) {
if (!_isTransactionalCacheEnabled()) {
return;
}
Propagation propagation = transactionAttribute.getPropagation();
if (propagation.value() >=
TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
List> backupPortalCacheMaps =
_backupPortalCacheMapsThreadLocal.get();
_portalCacheMapsThreadLocal.set(
backupPortalCacheMaps.remove(
backupPortalCacheMaps.size() - 1));
}
else if (transactionStatus.isNewTransaction()) {
rollback();
EntityCacheUtil.clearLocalCache();
FinderCacheUtil.clearLocalCache();
}
}
};
public static void begin() {
List portalCacheMaps =
_portalCacheMapsThreadLocal.get();
portalCacheMaps.add(new PortalCacheMap());
}
public static void commit(boolean readOnly) {
PortalCacheMap portalCacheMap = _popPortalCacheMap();
for (UncommittedBuffer uncommittedBuffer : portalCacheMap.values()) {
uncommittedBuffer.commit(readOnly);
}
portalCacheMap.clear();
}
public static V get(
PortalCache portalCache, K key) {
PortalCacheMap portalCacheMap = _peekPortalCacheMap();
UncommittedBuffer uncommittedBuffer = portalCacheMap.get(portalCache);
if (uncommittedBuffer == null) {
return null;
}
ValueEntry valueEntry = uncommittedBuffer.get(key);
if (valueEntry == null) {
return null;
}
return (V)valueEntry._value;
}
public static Serializable getNullHolder() {
return _NULL_HOLDER;
}
public static boolean isEnabled() {
if (!_isTransactionalCacheEnabled()) {
return false;
}
List portalCacheMaps =
_portalCacheMapsThreadLocal.get();
return !portalCacheMaps.isEmpty();
}
public static void put(
PortalCache portalCache, K key, V value, int ttl, boolean mvcc) {
PortalCacheMap portalCacheMap = _peekPortalCacheMap();
UncommittedBuffer uncommittedBuffer = portalCacheMap.get(portalCache);
if (uncommittedBuffer == null) {
if (mvcc) {
uncommittedBuffer = new UncommittedBuffer(
(PortalCache)portalCache);
}
else {
uncommittedBuffer = new MarkerUncommittedBuffer(
(PortalCache)portalCache);
}
portalCacheMap.put(portalCache, uncommittedBuffer);
}
uncommittedBuffer.put(
key,
new ValueEntry(value, ttl, SkipReplicationThreadLocal.isEnabled()));
}
public static void removeAll(
PortalCache portalCache, boolean mvcc) {
PortalCacheMap portalCacheMap = _peekPortalCacheMap();
UncommittedBuffer uncommittedBuffer = portalCacheMap.get(portalCache);
if (uncommittedBuffer == null) {
if (mvcc) {
uncommittedBuffer = new UncommittedBuffer(
(PortalCache)portalCache);
}
else {
uncommittedBuffer = new MarkerUncommittedBuffer(
(PortalCache)portalCache);
}
portalCacheMap.put(portalCache, uncommittedBuffer);
}
uncommittedBuffer.removeAll(SkipReplicationThreadLocal.isEnabled());
}
public static void rollback() {
PortalCacheMap portalCacheMap = _popPortalCacheMap();
portalCacheMap.clear();
}
protected static class PortalCacheMap
extends HashMap
, UncommittedBuffer> {
}
private static boolean _isTransactionalCacheEnabled() {
if (_transactionalCacheEnabled == null) {
_transactionalCacheEnabled = GetterUtil.getBoolean(
PropsUtil.get(PropsKeys.TRANSACTIONAL_CACHE_ENABLED));
}
return _transactionalCacheEnabled;
}
private static PortalCacheMap _peekPortalCacheMap() {
List portalCacheMaps =
_portalCacheMapsThreadLocal.get();
return portalCacheMaps.get(portalCacheMaps.size() - 1);
}
private static PortalCacheMap _popPortalCacheMap() {
List portalCacheMaps =
_portalCacheMapsThreadLocal.get();
return portalCacheMaps.remove(portalCacheMaps.size() - 1);
}
private static final Serializable _NULL_HOLDER = "NULL_HOLDER";
private static final ValueEntry _NULL_HOLDER_VALUE_ENTRY = new ValueEntry(
_NULL_HOLDER, PortalCache.DEFAULT_TIME_TO_LIVE, false);
private static final ThreadLocal>>
_backupPortalCacheMapsThreadLocal = new CentralizedThreadLocal<>(
TransactionalPortalCacheUtil.class.getName() +
"._backupPortalCacheMapsThreadLocal",
ArrayList::new, false);
private static final ThreadLocal>
_portalCacheMapsThreadLocal = new CentralizedThreadLocal<>(
TransactionalPortalCacheUtil.class.getName() +
"._portalCacheMapsThreadLocal",
ArrayList::new, false);
private static volatile Boolean _transactionalCacheEnabled;
private static class MarkerUncommittedBuffer extends UncommittedBuffer {
@Override
public void commit(boolean readOnly) {
if (skipCommit(readOnly)) {
return;
}
_markers.compute(
_portalCacheName,
(key, placeHolder) -> {
if (placeHolder != _marker) {
commitByRemove = true;
}
if (!readOnly || !commitByRemove) {
doCommit();
}
if (readOnly) {
return placeHolder;
}
return null;
});
}
@Override
public void put(Serializable key, ValueEntry valueEntry) {
ValueEntry oldValueEntry = super._uncommittedMap.put(
key, valueEntry);
if (oldValueEntry != null) {
oldValueEntry.merge(valueEntry);
}
}
private MarkerUncommittedBuffer(
PortalCache portalCache) {
super(portalCache);
_portalCacheName = portalCache.getPortalCacheName();
_marker = _markers.computeIfAbsent(
_portalCacheName, key -> new Object());
}
private static final Map _markers =
new ConcurrentReferenceValueHashMap<>(
FinalizeManager.WEAK_REFERENCE_FACTORY);
private final Object _marker;
private final String _portalCacheName;
}
private static class UncommittedBuffer {
public void commit(boolean readOnly) {
if (skipCommit(readOnly)) {
return;
}
doCommit();
}
public ValueEntry get(Serializable key) {
ValueEntry valueEntry = _uncommittedMap.get(key);
if ((valueEntry == null) && _removeAll) {
valueEntry = _NULL_HOLDER_VALUE_ENTRY;
}
return valueEntry;
}
public void put(Serializable key, ValueEntry valueEntry) {
ValueEntry oldValueEntry = _uncommittedMap.put(key, valueEntry);
if (oldValueEntry != null) {
oldValueEntry.merge(valueEntry);
if (oldValueEntry.isRemove()) {
valueEntry._removed = true;
}
}
}
public void removeAll(boolean skipReplicator) {
_uncommittedMap.clear();
_removeAll = true;
if (_skipReplicator) {
_skipReplicator = skipReplicator;
}
}
protected void doCommit() {
if (_removeAll) {
if (_skipReplicator) {
PortalCacheHelperUtil.removeAllWithoutReplicator(
_portalCache);
}
else {
_portalCache.removeAll();
}
}
for (Map.Entry entry :
_uncommittedMap.entrySet()) {
ValueEntry valueEntry = entry.getValue();
if (commitByRemove) {
valueEntry.commitToByRemove(_portalCache, entry.getKey());
}
else {
valueEntry.commitTo(_portalCache, entry.getKey());
}
}
}
protected boolean skipCommit(boolean readOnly) {
if (readOnly) {
_removeAll = false;
Collection valueEntries = _uncommittedMap.values();
Iterator iterator = valueEntries.iterator();
while (iterator.hasNext()) {
ValueEntry valueEntry = iterator.next();
if (valueEntry.isRemove()) {
iterator.remove();
}
}
}
if (!_removeAll && _uncommittedMap.isEmpty()) {
return true;
}
return false;
}
protected boolean commitByRemove;
private UncommittedBuffer(
PortalCache portalCache) {
_portalCache = portalCache;
}
private final PortalCache _portalCache;
private boolean _removeAll;
private boolean _skipReplicator = true;
private final Map _uncommittedMap =
new HashMap<>();
}
private static class ValueEntry {
public void commitTo(
PortalCache portalCache, Serializable key) {
boolean remove = isRemove();
if (remove || _removed) {
if (_skipReplicator) {
PortalCacheHelperUtil.removeWithoutReplicator(
portalCache, key);
}
else {
portalCache.remove(key);
}
}
if (!remove) {
if (_skipReplicator) {
PortalCacheHelperUtil.putWithoutReplicator(
portalCache, key, _value, _ttl);
}
else {
portalCache.put(key, _value, _ttl);
}
}
}
public void commitToByRemove(
PortalCache portalCache, Serializable key) {
if (_skipReplicator) {
PortalCacheHelperUtil.removeWithoutReplicator(portalCache, key);
}
else {
portalCache.remove(key);
}
}
public boolean isRemove() {
if (_value == _NULL_HOLDER) {
return true;
}
return false;
}
public void merge(ValueEntry valueEntry) {
if (!_skipReplicator) {
valueEntry._skipReplicator = false;
}
}
private ValueEntry(Object value, int ttl, boolean skipReplicator) {
_value = value;
_ttl = ttl;
_skipReplicator = skipReplicator;
}
private boolean _removed;
private boolean _skipReplicator;
private final int _ttl;
private final Object _value;
}
}