001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.dao.orm.common;
016    
017    import com.liferay.portal.kernel.cache.CacheManagerListener;
018    import com.liferay.portal.kernel.cache.CacheRegistryItem;
019    import com.liferay.portal.kernel.cache.CacheRegistryUtil;
020    import com.liferay.portal.kernel.cache.MultiVMPool;
021    import com.liferay.portal.kernel.cache.PortalCache;
022    import com.liferay.portal.kernel.cache.PortalCacheHelperUtil;
023    import com.liferay.portal.kernel.cache.PortalCacheManager;
024    import com.liferay.portal.kernel.dao.orm.EntityCacheUtil;
025    import com.liferay.portal.kernel.dao.orm.FinderCache;
026    import com.liferay.portal.kernel.dao.orm.FinderPath;
027    import com.liferay.portal.kernel.security.pacl.DoPrivileged;
028    import com.liferay.portal.kernel.util.AutoResetThreadLocal;
029    import com.liferay.portal.kernel.util.StringPool;
030    import com.liferay.portal.model.BaseModel;
031    import com.liferay.portal.service.persistence.impl.BasePersistenceImpl;
032    import com.liferay.portal.util.PropsValues;
033    
034    import java.io.Serializable;
035    
036    import java.util.ArrayList;
037    import java.util.Collections;
038    import java.util.HashSet;
039    import java.util.List;
040    import java.util.Map;
041    import java.util.Set;
042    import java.util.concurrent.ConcurrentHashMap;
043    import java.util.concurrent.ConcurrentMap;
044    
045    import org.apache.commons.collections.map.LRUMap;
046    
047    /**
048     * @author Brian Wing Shun Chan
049     * @author Shuyang Zhou
050     */
051    @DoPrivileged
052    public class FinderCacheImpl
053            implements CacheManagerListener, CacheRegistryItem, FinderCache {
054    
055            public static final String CACHE_NAME = FinderCache.class.getName();
056    
057            public void afterPropertiesSet() {
058                    CacheRegistryUtil.register(this);
059            }
060    
061            @Override
062            public void clearCache() {
063                    clearLocalCache();
064    
065                    for (PortalCache<?, ?> portalCache : _portalCaches.values()) {
066                            portalCache.removeAll();
067                    }
068            }
069    
070            @Override
071            public void clearCache(String className) {
072                    clearLocalCache();
073    
074                    PortalCache<?, ?> portalCache = _getPortalCache(className, true);
075    
076                    if (portalCache != null) {
077                            portalCache.removeAll();
078                    }
079            }
080    
081            @Override
082            public void clearLocalCache() {
083                    if (_LOCAL_CACHE_AVAILABLE) {
084                            _localCache.remove();
085                    }
086            }
087    
088            @Override
089            public void dispose() {
090                    _portalCaches.clear();
091            }
092    
093            @Override
094            public String getRegistryName() {
095                    return CACHE_NAME;
096            }
097    
098            @Override
099            public Object getResult(
100                    FinderPath finderPath, Object[] args,
101                    BasePersistenceImpl<? extends BaseModel<?>> basePersistenceImpl) {
102    
103                    if (!PropsValues.VALUE_OBJECT_FINDER_CACHE_ENABLED ||
104                            !finderPath.isFinderCacheEnabled() ||
105                            !CacheRegistryUtil.isActive()) {
106    
107                            return null;
108                    }
109    
110                    Serializable primaryKey = null;
111    
112                    Map<Serializable, Serializable> localCache = null;
113    
114                    Serializable localCacheKey = null;
115    
116                    if (_LOCAL_CACHE_AVAILABLE) {
117                            localCache = _localCache.get();
118    
119                            localCacheKey = finderPath.encodeLocalCacheKey(args);
120    
121                            primaryKey = localCache.get(localCacheKey);
122                    }
123    
124                    if (primaryKey == null) {
125                            PortalCache<Serializable, Serializable> portalCache =
126                                    _getPortalCache(finderPath.getCacheName(), true);
127    
128                            Serializable cacheKey = finderPath.encodeCacheKey(args);
129    
130                            primaryKey = portalCache.get(cacheKey);
131    
132                            if (primaryKey != null) {
133                                    if (_LOCAL_CACHE_AVAILABLE) {
134                                            localCache.put(localCacheKey, primaryKey);
135                                    }
136                            }
137                    }
138    
139                    if (primaryKey != null) {
140                            return _primaryKeyToResult(
141                                    finderPath, basePersistenceImpl, primaryKey);
142                    }
143    
144                    return null;
145            }
146    
147            @Override
148            public void init() {
149            }
150    
151            @Override
152            public void invalidate() {
153                    clearCache();
154            }
155    
156            @Override
157            public void notifyCacheAdded(String portalCacheName) {
158            }
159    
160            @Override
161            public void notifyCacheRemoved(String portalCacheName) {
162                    _portalCaches.remove(portalCacheName);
163            }
164    
165            @Override
166            public void putResult(FinderPath finderPath, Object[] args, Object result) {
167                    putResult(finderPath, args, result, true);
168            }
169    
170            @Override
171            public void putResult(
172                    FinderPath finderPath, Object[] args, Object result, boolean quiet) {
173    
174                    if (!PropsValues.VALUE_OBJECT_FINDER_CACHE_ENABLED ||
175                            !finderPath.isFinderCacheEnabled() ||
176                            !CacheRegistryUtil.isActive() || (result == null)) {
177    
178                            return;
179                    }
180    
181                    Serializable primaryKey = _resultToPrimaryKey((Serializable)result);
182    
183                    if (_LOCAL_CACHE_AVAILABLE) {
184                            Map<Serializable, Serializable> localCache = _localCache.get();
185    
186                            Serializable localCacheKey = finderPath.encodeLocalCacheKey(args);
187    
188                            localCache.put(localCacheKey, primaryKey);
189                    }
190    
191                    PortalCache<Serializable, Serializable> portalCache = _getPortalCache(
192                            finderPath.getCacheName(), true);
193    
194                    Serializable cacheKey = finderPath.encodeCacheKey(args);
195    
196                    if (quiet) {
197                            PortalCacheHelperUtil.putWithoutReplicator(
198                                    portalCache, cacheKey, primaryKey);
199                    }
200                    else {
201                            portalCache.put(cacheKey, primaryKey);
202                    }
203            }
204    
205            @Override
206            public void removeCache(String className) {
207                    _portalCaches.remove(className);
208    
209                    String groupKey = _GROUP_KEY_PREFIX.concat(className);
210    
211                    _multiVMPool.removeCache(groupKey);
212            }
213    
214            @Override
215            public void removeResult(FinderPath finderPath, Object[] args) {
216                    if (!PropsValues.VALUE_OBJECT_FINDER_CACHE_ENABLED ||
217                            !finderPath.isFinderCacheEnabled() ||
218                            !CacheRegistryUtil.isActive()) {
219    
220                            return;
221                    }
222    
223                    if (_LOCAL_CACHE_AVAILABLE) {
224                            Map<Serializable, Serializable> localCache = _localCache.get();
225    
226                            Serializable localCacheKey = finderPath.encodeLocalCacheKey(args);
227    
228                            localCache.remove(localCacheKey);
229                    }
230    
231                    PortalCache<Serializable, Serializable> portalCache = _getPortalCache(
232                            finderPath.getCacheName(), true);
233    
234                    Serializable cacheKey = finderPath.encodeCacheKey(args);
235    
236                    portalCache.remove(cacheKey);
237            }
238    
239            public void setMultiVMPool(MultiVMPool multiVMPool) {
240                    _multiVMPool = multiVMPool;
241    
242                    PortalCacheManager<? extends Serializable, ? extends Serializable>
243                            portalCacheManager = _multiVMPool.getCacheManager();
244    
245                    portalCacheManager.registerCacheManagerListener(this);
246            }
247    
248            private PortalCache<Serializable, Serializable> _getPortalCache(
249                    String className, boolean createIfAbsent) {
250    
251                    PortalCache<Serializable, Serializable> portalCache = _portalCaches.get(
252                            className);
253    
254                    if ((portalCache == null) && createIfAbsent) {
255                            String groupKey = _GROUP_KEY_PREFIX.concat(className);
256    
257                            portalCache =
258                                    (PortalCache<Serializable, Serializable>)_multiVMPool.getCache(
259                                            groupKey, PropsValues.VALUE_OBJECT_FINDER_BLOCKING_CACHE);
260    
261                            PortalCache<Serializable, Serializable> previousPortalCache =
262                                    _portalCaches.putIfAbsent(className, portalCache);
263    
264                            if (previousPortalCache != null) {
265                                    portalCache = previousPortalCache;
266                            }
267                    }
268    
269                    return portalCache;
270            }
271    
272            private Serializable _primaryKeyToResult(
273                    FinderPath finderPath,
274                    BasePersistenceImpl<? extends BaseModel<?>> basePersistenceImpl,
275                    Serializable primaryKey) {
276    
277                    if (primaryKey instanceof List<?>) {
278                            List<Serializable> primaryKeys = (List<Serializable>)primaryKey;
279    
280                            if (primaryKeys.isEmpty()) {
281                                    return (Serializable)Collections.emptyList();
282                            }
283    
284                            Set<Serializable> primaryKeysSet = new HashSet<>(primaryKeys);
285    
286                            Map<Serializable, ? extends BaseModel<?>> map =
287                                    basePersistenceImpl.fetchByPrimaryKeys(primaryKeysSet);
288    
289                            if (map.size() < primaryKeysSet.size()) {
290                                    return null;
291                            }
292    
293                            List<Serializable> list = new ArrayList<>(primaryKeys.size());
294    
295                            for (Serializable curPrimaryKey : primaryKeys) {
296                                    list.add(map.get(curPrimaryKey));
297                            }
298    
299                            return (Serializable)Collections.unmodifiableList(list);
300                    }
301                    else if (BaseModel.class.isAssignableFrom(
302                                            finderPath.getResultClass())) {
303    
304                            return EntityCacheUtil.loadResult(
305                                    finderPath.isEntityCacheEnabled(), finderPath.getResultClass(),
306                                    primaryKey, basePersistenceImpl);
307                    }
308    
309                    return primaryKey;
310            }
311    
312            private Serializable _resultToPrimaryKey(Serializable result) {
313                    if (result instanceof BaseModel<?>) {
314                            BaseModel<?> model = (BaseModel<?>)result;
315    
316                            return model.getPrimaryKeyObj();
317                    }
318                    else if (result instanceof List<?>) {
319                            List<Serializable> list = (List<Serializable>)result;
320    
321                            if (list.isEmpty()) {
322                                    return (Serializable)Collections.emptyList();
323                            }
324    
325                            ArrayList<Serializable> cachedList = new ArrayList<>(list.size());
326    
327                            for (Serializable curResult : list) {
328                                    Serializable primaryKey = _resultToPrimaryKey(curResult);
329    
330                                    cachedList.add(primaryKey);
331                            }
332    
333                            return cachedList;
334                    }
335    
336                    return result;
337            }
338    
339            private static final String _GROUP_KEY_PREFIX = CACHE_NAME.concat(
340                    StringPool.PERIOD);
341    
342            private static final boolean _LOCAL_CACHE_AVAILABLE;
343    
344            private static final ThreadLocal<LRUMap> _localCache;
345    
346            static {
347                    if (PropsValues.VALUE_OBJECT_FINDER_THREAD_LOCAL_CACHE_MAX_SIZE > 0) {
348                            _LOCAL_CACHE_AVAILABLE = true;
349    
350                            _localCache = new AutoResetThreadLocal<LRUMap>(
351                                    FinderCacheImpl.class + "._localCache",
352                                    new LRUMap(
353                                            PropsValues.
354                                                    VALUE_OBJECT_FINDER_THREAD_LOCAL_CACHE_MAX_SIZE));
355                    }
356                    else {
357                            _LOCAL_CACHE_AVAILABLE = false;
358    
359                            _localCache = null;
360                    }
361            }
362    
363            private MultiVMPool _multiVMPool;
364            private final ConcurrentMap<String, PortalCache<Serializable, Serializable>>
365                    _portalCaches = new ConcurrentHashMap<>();
366    
367    }