com.liferay.portal.cache.internal.dao.orm.FinderCacheImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.liferay.portal.cache.internal.dao.orm.FinderCacheImpl.java

Source

/**
 * 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.petra.lang.CentralizedThreadLocal;
import com.liferay.petra.string.StringPool;
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.GetterUtil;
import com.liferay.portal.kernel.util.Props;
import com.liferay.portal.kernel.util.PropsKeys;

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
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.Objects;
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, args, 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;
        }

        Serializable primaryKey = _resultToPrimaryKey(args, (Serializable) result);

        PortalCache<Serializable, Serializable> portalCache = _getPortalCache(finderPath.getCacheName());

        String encodedArguments = finderPath.encodeArguments(args);

        Serializable cacheKey = finderPath.encodeCacheKey(encodedArguments);

        if (primaryKey == null) {
            if (_localCacheAvailable) {
                Map<Serializable, Serializable> localCache = _localCache.get();

                localCache.remove(finderPath.encodeLocalCacheKey(encodedArguments));
            }

            if (quiet) {
                PortalCacheHelperUtil.removeWithoutReplicator(portalCache, cacheKey);
            } else {
                portalCache.remove(cacheKey);
            }
        } else {
            if (_localCacheAvailable) {
                Map<Serializable, Serializable> localCache = _localCache.get();

                localCache.put(finderPath.encodeLocalCacheKey(encodedArguments), primaryKey);
            }

            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));
        _valueObjectFinderCacheListThreshold = GetterUtil
                .getInteger(_props.get(PropsKeys.VALUE_OBJECT_FINDER_CACHE_LIST_THRESHOLD));

        if (_valueObjectFinderCacheListThreshold == 0) {
            _valueObjectFinderCacheEnabled = false;
        }

        int localCacheMaxSize = GetterUtil
                .getInteger(_props.get(PropsKeys.VALUE_OBJECT_FINDER_THREAD_LOCAL_CACHE_MAX_SIZE));

        if (localCacheMaxSize > 0) {
            _localCacheAvailable = true;

            _localCache = new CentralizedThreadLocal<>(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, Object[] args,
            BasePersistenceImpl<? extends BaseModel<?>> basePersistenceImpl, Serializable primaryKey) {

        if (primaryKey instanceof EmptyResult) {
            EmptyResult emptyResult = (EmptyResult) primaryKey;

            if (emptyResult.matches(args)) {
                return (Serializable) Collections.emptyList();
            }

            return null;
        }

        if (primaryKey instanceof List<?>) {
            List<Serializable> primaryKeys = (List<Serializable>) primaryKey;

            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);
        }

        if (BaseModel.class.isAssignableFrom(finderPath.getResultClass())) {
            return _entityCache.loadResult(finderPath.isEntityCacheEnabled(), finderPath.getResultClass(),
                    primaryKey, basePersistenceImpl);
        }

        return primaryKey;
    }

    private Serializable _resultToPrimaryKey(Object[] args, Serializable result) {

        if (result instanceof BaseModel<?>) {
            BaseModel<?> model = (BaseModel<?>) result;

            return model.getPrimaryKeyObj();
        }

        if (result instanceof List<?>) {
            List<Serializable> list = (List<Serializable>) result;

            if (list.isEmpty()) {
                return new EmptyResult(args);
            }

            if ((list.size() > _valueObjectFinderCacheListThreshold)
                    && (_valueObjectFinderCacheListThreshold > 0)) {

                return null;
            }

            ArrayList<Serializable> cachedList = new ArrayList<>(list.size());

            for (Serializable curResult : list) {
                Serializable primaryKey = _resultToPrimaryKey(args, 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;
    private int _valueObjectFinderCacheListThreshold;

    private static class EmptyResult implements Externalizable {

        public EmptyResult() {
        }

        public boolean matches(Object[] args) {
            if (args.length != _args.length) {
                return false;
            }

            for (int i = 0; i < _args.length; i++) {
                if (!Objects.equals(args[i], _args[i])) {
                    return false;
                }
            }

            return true;
        }

        @Override
        public void readExternal(ObjectInput objectInput) throws ClassNotFoundException, IOException {

            _args = (Object[]) objectInput.readObject();
        }

        @Override
        public void writeExternal(ObjectOutput objectOutput) throws IOException {

            objectOutput.writeObject(_args);
        }

        private EmptyResult(Object[] args) {
            _args = args;
        }

        private Object[] _args;

    }

}