/**
* 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.cache.internal.dao.orm;
import com.liferay.portal.kernel.cache.CacheRegistryItem;
import com.liferay.portal.kernel.cache.CacheRegistryUtil;
import com.liferay.portal.kernel.cache.MultiVMPool;
import com.liferay.portal.kernel.cache.PortalCache;
import com.liferay.portal.kernel.cache.PortalCacheHelperUtil;
import com.liferay.portal.kernel.cache.PortalCacheManager;
import com.liferay.portal.kernel.cache.PortalCacheManagerListener;
import com.liferay.portal.kernel.dao.orm.EntityCache;
import com.liferay.portal.kernel.dao.orm.Session;
import com.liferay.portal.kernel.dao.orm.SessionFactory;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.model.BaseModel;
import com.liferay.portal.kernel.model.CacheModel;
import com.liferay.portal.kernel.model.MVCCModel;
import com.liferay.portal.kernel.util.AutoResetThreadLocal;
import com.liferay.portal.kernel.util.GetterUtil;
import com.liferay.portal.kernel.util.Props;
import com.liferay.portal.kernel.util.PropsKeys;
import com.liferay.portal.kernel.util.StringPool;
import java.io.Serializable;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.apache.commons.collections.map.LRUMap;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Modified;
import org.osgi.service.component.annotations.Reference;
/**
* @author Brian Wing Shun Chan
* @author Shuyang Zhou
*/
@Component(
immediate = true, service = {CacheRegistryItem.class, EntityCache.class}
)
public class EntityCacheImpl
implements PortalCacheManagerListener, CacheRegistryItem, EntityCache {
@Override
public void clearCache() {
clearLocalCache();
for (PortalCache<?, ?> portalCache : _portalCaches.values()) {
portalCache.removeAll();
}
}
@Override
public void clearCache(Class<?> clazz) {
clearLocalCache();
PortalCache<?, ?> portalCache = getPortalCache(clazz);
if (portalCache != null) {
portalCache.removeAll();
}
}
@Override
public void clearLocalCache() {
if (_localCacheAvailable) {
_localCache.remove();
}
}
@Override
public void dispose() {
_portalCaches.clear();
}
@Override
public PortalCache<Serializable, Serializable> getPortalCache(
Class<?> clazz) {
String className = clazz.getName();
PortalCache<Serializable, Serializable> portalCache = _portalCaches.get(
className);
if (portalCache != null) {
return portalCache;
}
String groupKey = _GROUP_KEY_PREFIX.concat(className);
boolean mvcc = false;
if (_valueObjectMVCCEntityCacheEnabled &&
MVCCModel.class.isAssignableFrom(clazz)) {
mvcc = true;
}
portalCache =
(PortalCache<Serializable, Serializable>)
_multiVMPool.getPortalCache(
groupKey, _valueObjectEntityBlockingCacheEnabled, mvcc);
PortalCache<Serializable, Serializable> previousPortalCache =
_portalCaches.putIfAbsent(className, portalCache);
if (previousPortalCache != null) {
return previousPortalCache;
}
return portalCache;
}
@Override
public String getRegistryName() {
return EntityCache.class.getName();
}
@Override
public Serializable getResult(
boolean entityCacheEnabled, Class<?> clazz, Serializable primaryKey) {
if (!_valueObjectEntityCacheEnabled || !entityCacheEnabled ||
!CacheRegistryUtil.isActive()) {
return null;
}
Serializable result = null;
Map<Serializable, Serializable> localCache = null;
Serializable localCacheKey = null;
if (_localCacheAvailable) {
localCache = _localCache.get();
localCacheKey = new LocalCacheKey(clazz.getName(), primaryKey);
result = localCache.get(localCacheKey);
}
if (result == null) {
PortalCache<Serializable, Serializable> portalCache =
getPortalCache(clazz);
result = portalCache.get(primaryKey);
if (result == null) {
result = StringPool.BLANK;
}
if (_localCacheAvailable) {
localCache.put(localCacheKey, result);
}
}
return _toEntityModel(result);
}
@Override
public void init() {
}
@Override
public void invalidate() {
clearCache();
}
@Override
public Serializable loadResult(
boolean entityCacheEnabled, Class<?> clazz, Serializable primaryKey,
SessionFactory sessionFactory) {
if (!_valueObjectEntityCacheEnabled || !entityCacheEnabled ||
!CacheRegistryUtil.isActive()) {
Session session = null;
try {
session = sessionFactory.openSession();
return (Serializable)session.load(clazz, primaryKey);
}
finally {
sessionFactory.closeSession(session);
}
}
Serializable result = null;
Map<Serializable, Serializable> localCache = null;
Serializable localCacheKey = null;
if (_localCacheAvailable) {
localCache = _localCache.get();
localCacheKey = new LocalCacheKey(clazz.getName(), primaryKey);
result = localCache.get(localCacheKey);
}
Serializable loadResult = null;
if (result == null) {
PortalCache<Serializable, Serializable> portalCache =
getPortalCache(clazz);
result = portalCache.get(primaryKey);
if (result == null) {
if (_log.isDebugEnabled()) {
_log.debug(
"Load " + clazz + " " + primaryKey + " from session");
}
Session session = null;
try {
session = sessionFactory.openSession();
loadResult = (Serializable)session.load(clazz, primaryKey);
}
finally {
if (loadResult == null) {
result = StringPool.BLANK;
}
else {
result = ((BaseModel<?>)loadResult).toCacheModel();
PortalCacheHelperUtil.putWithoutReplicator(
portalCache, primaryKey, result);
}
sessionFactory.closeSession(session);
}
}
if (_localCacheAvailable) {
localCache.put(localCacheKey, result);
}
}
if (loadResult != null) {
return loadResult;
}
return _toEntityModel(result);
}
@Override
public void notifyPortalCacheAdded(String portalCacheName) {
}
@Override
public void notifyPortalCacheRemoved(String portalCacheName) {
_portalCaches.remove(portalCacheName);
}
@Override
public void putResult(
boolean entityCacheEnabled, Class<?> clazz, Serializable primaryKey,
Serializable result) {
putResult(entityCacheEnabled, clazz, primaryKey, result, true);
}
@Override
public void putResult(
boolean entityCacheEnabled, Class<?> clazz, Serializable primaryKey,
Serializable result, boolean quiet) {
if (!_valueObjectEntityCacheEnabled || !entityCacheEnabled ||
!CacheRegistryUtil.isActive() || (result == null)) {
return;
}
result = ((BaseModel<?>)result).toCacheModel();
if (_localCacheAvailable) {
Map<Serializable, Serializable> localCache = _localCache.get();
Serializable localCacheKey = new LocalCacheKey(
clazz.getName(), primaryKey);
localCache.put(localCacheKey, result);
}
PortalCache<Serializable, Serializable> portalCache = getPortalCache(
clazz);
if (quiet) {
PortalCacheHelperUtil.putWithoutReplicator(
portalCache, primaryKey, result);
}
else {
portalCache.put(primaryKey, result);
}
}
@Override
public void removeCache(String className) {
_portalCaches.remove(className);
String groupKey = _GROUP_KEY_PREFIX.concat(className);
_multiVMPool.removePortalCache(groupKey);
}
@Override
public void removeResult(
boolean entityCacheEnabled, Class<?> clazz, Serializable primaryKey) {
if (!_valueObjectEntityCacheEnabled || !entityCacheEnabled ||
!CacheRegistryUtil.isActive()) {
return;
}
if (_localCacheAvailable) {
Map<Serializable, Serializable> localCache = _localCache.get();
Serializable localCacheKey = new LocalCacheKey(
clazz.getName(), primaryKey);
localCache.remove(localCacheKey);
}
PortalCache<Serializable, Serializable> portalCache = getPortalCache(
clazz);
portalCache.remove(primaryKey);
}
@Activate
@Modified
protected void activate() {
_valueObjectEntityBlockingCacheEnabled = GetterUtil.getBoolean(
_props.get(PropsKeys.VALUE_OBJECT_ENTITY_BLOCKING_CACHE));
_valueObjectEntityCacheEnabled = GetterUtil.getBoolean(
_props.get(PropsKeys.VALUE_OBJECT_ENTITY_CACHE_ENABLED));
_valueObjectMVCCEntityCacheEnabled = GetterUtil.getBoolean(
_props.get(PropsKeys.VALUE_OBJECT_MVCC_ENTITY_CACHE_ENABLED));
int localCacheMaxSize = GetterUtil.getInteger(
_props.get(
PropsKeys.VALUE_OBJECT_ENTITY_THREAD_LOCAL_CACHE_MAX_SIZE));
if (localCacheMaxSize > 0) {
_localCacheAvailable = true;
_localCache = new AutoResetThreadLocal<>(
FinderCacheImpl.class + "._localCache",
() -> new LRUMap(localCacheMaxSize));
}
else {
_localCacheAvailable = false;
_localCache = null;
}
PortalCacheManager
<? extends Serializable, ? extends Serializable>
portalCacheManager = _multiVMPool.getPortalCacheManager();
portalCacheManager.registerPortalCacheManagerListener(
EntityCacheImpl.this);
}
@Reference(unbind = "-")
protected void setMultiVMPool(MultiVMPool multiVMPool) {
_multiVMPool = multiVMPool;
}
@Reference(unbind = "-")
protected void setProps(Props props) {
_props = props;
}
private Serializable _toEntityModel(Serializable result) {
if (result == StringPool.BLANK) {
return null;
}
CacheModel<?> cacheModel = (CacheModel<?>)result;
BaseModel<?> entityModel = (BaseModel<?>)cacheModel.toEntityModel();
entityModel.setCachedModel(true);
return entityModel;
}
private static final String _GROUP_KEY_PREFIX =
EntityCache.class.getName() + StringPool.PERIOD;
private static final Log _log = LogFactoryUtil.getLog(
EntityCacheImpl.class);
private ThreadLocal<LRUMap> _localCache;
private boolean _localCacheAvailable;
private MultiVMPool _multiVMPool;
private final ConcurrentMap<String, PortalCache<Serializable, Serializable>>
_portalCaches = new ConcurrentHashMap<>();
private Props _props;
private boolean _valueObjectEntityBlockingCacheEnabled;
private boolean _valueObjectEntityCacheEnabled;
private boolean _valueObjectMVCCEntityCacheEnabled;
private static class LocalCacheKey implements Serializable {
public LocalCacheKey(String className, Serializable primaryKey) {
_className = className;
_primaryKey = primaryKey;
}
@Override
public boolean equals(Object obj) {
LocalCacheKey localCacheKey = (LocalCacheKey)obj;
if (localCacheKey._className.equals(_className) &&
localCacheKey._primaryKey.equals(_primaryKey)) {
return true;
}
return false;
}
@Override
public int hashCode() {
return _className.hashCode() * 11 + _primaryKey.hashCode();
}
private static final long serialVersionUID = 1L;
private final String _className;
private final Serializable _primaryKey;
}
}