/** * 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; } }