/** * 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.FinderCache; import com.liferay.portal.kernel.dao.orm.FinderPath; import com.liferay.portal.kernel.model.BaseModel; import com.liferay.portal.kernel.service.persistence.impl.BasePersistenceImpl; 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.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; 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, FinderCache.class} ) public class FinderCacheImpl implements PortalCacheManagerListener, CacheRegistryItem, FinderCache { @Override public void clearCache() { clearLocalCache(); for (PortalCache<?, ?> portalCache : _portalCaches.values()) { portalCache.removeAll(); } } @Override public void clearCache(String className) { clearLocalCache(); PortalCache<?, ?> portalCache = _getPortalCache(className); if (portalCache != null) { portalCache.removeAll(); } } @Override public void clearLocalCache() { if (_localCacheAvailable) { _localCache.remove(); } } @Override public void dispose() { _portalCaches.clear(); } @Override public String getRegistryName() { return FinderCache.class.getName(); } @Override public Object getResult( FinderPath finderPath, Object[] args, BasePersistenceImpl<? extends BaseModel<?>> basePersistenceImpl) { if (!_valueObjectFinderCacheEnabled || !finderPath.isFinderCacheEnabled() || !CacheRegistryUtil.isActive()) { return null; } String encodedArguments = finderPath.encodeArguments(args); Map<Serializable, Serializable> localCache = null; Serializable localCacheKey = null; Serializable primaryKey = null; if (_localCacheAvailable) { localCache = _localCache.get(); localCacheKey = finderPath.encodeLocalCacheKey(encodedArguments); primaryKey = localCache.get(localCacheKey); } if (primaryKey == null) { PortalCache<Serializable, Serializable> portalCache = _getPortalCache(finderPath.getCacheName()); primaryKey = portalCache.get( finderPath.encodeCacheKey(encodedArguments)); if (primaryKey != null) { if (_localCacheAvailable) { localCache.put(localCacheKey, primaryKey); } } } if (primaryKey != null) { return _primaryKeyToResult( finderPath, basePersistenceImpl, primaryKey); } return null; } @Override public void init() { } @Override public void invalidate() { clearCache(); } @Override public void notifyPortalCacheAdded(String portalCacheName) { } @Override public void notifyPortalCacheRemoved(String portalCacheName) { _portalCaches.remove(portalCacheName); } @Override public void putResult(FinderPath finderPath, Object[] args, Object result) { putResult(finderPath, args, result, true); } @Override public void putResult( FinderPath finderPath, Object[] args, Object result, boolean quiet) { if (!_valueObjectFinderCacheEnabled || !finderPath.isFinderCacheEnabled() || !CacheRegistryUtil.isActive() || (result == null)) { return; } String encodedArguments = finderPath.encodeArguments(args); Serializable primaryKey = _resultToPrimaryKey((Serializable)result); if (_localCacheAvailable) { Map<Serializable, Serializable> localCache = _localCache.get(); localCache.put( finderPath.encodeLocalCacheKey(encodedArguments), primaryKey); } PortalCache<Serializable, Serializable> portalCache = _getPortalCache( finderPath.getCacheName()); Serializable cacheKey = finderPath.encodeCacheKey(encodedArguments); if (quiet) { PortalCacheHelperUtil.putWithoutReplicator( portalCache, cacheKey, primaryKey); } else { portalCache.put(cacheKey, primaryKey); } } @Override public void removeCache(String className) { _portalCaches.remove(className); String groupKey = _GROUP_KEY_PREFIX.concat(className); _multiVMPool.removePortalCache(groupKey); } @Override public void removeResult(FinderPath finderPath, Object[] args) { if (!_valueObjectFinderCacheEnabled || !finderPath.isFinderCacheEnabled() || !CacheRegistryUtil.isActive()) { return; } String encodedArguments = finderPath.encodeArguments(args); if (_localCacheAvailable) { Map<Serializable, Serializable> localCache = _localCache.get(); localCache.remove(finderPath.encodeLocalCacheKey(encodedArguments)); } PortalCache<Serializable, Serializable> portalCache = _getPortalCache( finderPath.getCacheName()); portalCache.remove(finderPath.encodeCacheKey(encodedArguments)); } @Activate @Modified protected void activate() { _valueObjectEntityBlockingCacheEnabled = GetterUtil.getBoolean( _props.get(PropsKeys.VALUE_OBJECT_ENTITY_BLOCKING_CACHE)); _valueObjectFinderCacheEnabled = GetterUtil.getBoolean( _props.get(PropsKeys.VALUE_OBJECT_FINDER_CACHE_ENABLED)); int localCacheMaxSize = GetterUtil.getInteger( _props.get( PropsKeys.VALUE_OBJECT_FINDER_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( FinderCacheImpl.this); } @Reference(unbind = "-") protected void setEntityCache(EntityCache entityCache) { _entityCache = entityCache; } @Reference(unbind = "-") protected void setMultiVMPool(MultiVMPool multiVMPool) { _multiVMPool = multiVMPool; } @Reference(unbind = "-") protected void setProps(Props props) { _props = props; } private PortalCache<Serializable, Serializable> _getPortalCache( String className) { PortalCache<Serializable, Serializable> portalCache = _portalCaches.get( className); if (portalCache != null) { return portalCache; } String groupKey = _GROUP_KEY_PREFIX.concat(className); portalCache = (PortalCache<Serializable, Serializable>) _multiVMPool.getPortalCache( groupKey, _valueObjectEntityBlockingCacheEnabled); PortalCache<Serializable, Serializable> previousPortalCache = _portalCaches.putIfAbsent(className, portalCache); if (previousPortalCache != null) { return previousPortalCache; } return portalCache; } private Serializable _primaryKeyToResult( FinderPath finderPath, BasePersistenceImpl<? extends BaseModel<?>> basePersistenceImpl, Serializable primaryKey) { if (primaryKey instanceof List<?>) { List<Serializable> primaryKeys = (List<Serializable>)primaryKey; if (primaryKeys.isEmpty()) { return (Serializable)Collections.emptyList(); } Set<Serializable> primaryKeysSet = new HashSet<>(primaryKeys); Map<Serializable, ? extends BaseModel<?>> map = basePersistenceImpl.fetchByPrimaryKeys(primaryKeysSet); if (map.size() < primaryKeysSet.size()) { return null; } List<Serializable> list = new ArrayList<>(primaryKeys.size()); for (Serializable curPrimaryKey : primaryKeys) { list.add(map.get(curPrimaryKey)); } return (Serializable)Collections.unmodifiableList(list); } else if (BaseModel.class.isAssignableFrom( finderPath.getResultClass())) { return _entityCache.loadResult( finderPath.isEntityCacheEnabled(), finderPath.getResultClass(), primaryKey, basePersistenceImpl); } return primaryKey; } private Serializable _resultToPrimaryKey(Serializable result) { if (result instanceof BaseModel<?>) { BaseModel<?> model = (BaseModel<?>)result; return model.getPrimaryKeyObj(); } else if (result instanceof List<?>) { List<Serializable> list = (List<Serializable>)result; if (list.isEmpty()) { return (Serializable)Collections.emptyList(); } ArrayList<Serializable> cachedList = new ArrayList<>(list.size()); for (Serializable curResult : list) { Serializable primaryKey = _resultToPrimaryKey(curResult); cachedList.add(primaryKey); } return cachedList; } return result; } private static final String _GROUP_KEY_PREFIX = FinderCache.class.getName() + StringPool.PERIOD; private EntityCache _entityCache; 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 _valueObjectFinderCacheEnabled; }