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 name) {
158            }
159    
160            @Override
161            public void notifyCacheRemoved(String name) {
162                    _portalCaches.remove(name);
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() ||
177                            (result == null)) {
178    
179                            return;
180                    }
181    
182                    Serializable primaryKey = _resultToPrimaryKey((Serializable)result);
183    
184                    if (_LOCAL_CACHE_AVAILABLE) {
185                            Map<Serializable, Serializable> localCache = _localCache.get();
186    
187                            Serializable localCacheKey = finderPath.encodeLocalCacheKey(args);
188    
189                            localCache.put(localCacheKey, primaryKey);
190                    }
191    
192                    PortalCache<Serializable, Serializable> portalCache = _getPortalCache(
193                            finderPath.getCacheName(), true);
194    
195                    Serializable cacheKey = finderPath.encodeCacheKey(args);
196    
197                    if (quiet) {
198                            PortalCacheHelperUtil.putWithoutReplicator(
199                                    portalCache, cacheKey, primaryKey);
200                    }
201                    else {
202                            portalCache.put(cacheKey, primaryKey);
203                    }
204            }
205    
206            @Override
207            public void removeCache(String className) {
208                    _portalCaches.remove(className);
209    
210                    String groupKey = _GROUP_KEY_PREFIX.concat(className);
211    
212                    _multiVMPool.removeCache(groupKey);
213            }
214    
215            @Override
216            public void removeResult(FinderPath finderPath, Object[] args) {
217                    if (!PropsValues.VALUE_OBJECT_FINDER_CACHE_ENABLED ||
218                            !finderPath.isFinderCacheEnabled() ||
219                            !CacheRegistryUtil.isActive()) {
220    
221                            return;
222                    }
223    
224                    if (_LOCAL_CACHE_AVAILABLE) {
225                            Map<Serializable, Serializable> localCache = _localCache.get();
226    
227                            Serializable localCacheKey = finderPath.encodeLocalCacheKey(args);
228    
229                            localCache.remove(localCacheKey);
230                    }
231    
232                    PortalCache<Serializable, Serializable> portalCache = _getPortalCache(
233                            finderPath.getCacheName(), true);
234    
235                    Serializable cacheKey = finderPath.encodeCacheKey(args);
236    
237                    portalCache.remove(cacheKey);
238            }
239    
240            public void setMultiVMPool(MultiVMPool multiVMPool) {
241                    _multiVMPool = multiVMPool;
242    
243                    PortalCacheManager<? extends Serializable, ? extends Serializable>
244                            portalCacheManager = _multiVMPool.getCacheManager();
245    
246                    portalCacheManager.registerCacheManagerListener(this);
247            }
248    
249            private PortalCache<Serializable, Serializable> _getPortalCache(
250                    String className, boolean createIfAbsent) {
251    
252                    PortalCache<Serializable, Serializable> portalCache = _portalCaches.get(
253                            className);
254    
255                    if ((portalCache == null) && createIfAbsent) {
256                            String groupKey = _GROUP_KEY_PREFIX.concat(className);
257    
258                            portalCache =
259                                    (PortalCache<Serializable, Serializable>)_multiVMPool.getCache(
260                                            groupKey, PropsValues.VALUE_OBJECT_FINDER_BLOCKING_CACHE);
261    
262                            PortalCache<Serializable, Serializable> previousPortalCache =
263                                    _portalCaches.putIfAbsent(className, portalCache);
264    
265                            if (previousPortalCache != null) {
266                                    portalCache = previousPortalCache;
267                            }
268                    }
269    
270                    return portalCache;
271            }
272    
273            private Serializable _primaryKeyToResult(
274                    FinderPath finderPath,
275                    BasePersistenceImpl<? extends BaseModel<?>> basePersistenceImpl,
276                    Serializable primaryKey) {
277    
278                    if (primaryKey instanceof List<?>) {
279                            List<Serializable> primaryKeys = (List<Serializable>)primaryKey;
280    
281                            if (primaryKeys.isEmpty()) {
282                                    return (Serializable)Collections.emptyList();
283                            }
284    
285                            Set<Serializable> primaryKeysSet = new HashSet<Serializable>(
286                                    primaryKeys);
287    
288                            Map<Serializable, ? extends BaseModel<?>> map =
289                                    basePersistenceImpl.fetchByPrimaryKeys(primaryKeysSet);
290    
291                            if (map.size() < primaryKeysSet.size()) {
292                                    return null;
293                            }
294    
295                            List<Serializable> list = new ArrayList<Serializable>(
296                                    primaryKeys.size());
297    
298                            for (Serializable curPrimaryKey : primaryKeys) {
299                                    list.add(map.get(curPrimaryKey));
300                            }
301    
302                            return (Serializable)Collections.unmodifiableList(list);
303                    }
304                    else if (BaseModel.class.isAssignableFrom(
305                                            finderPath.getResultClass())) {
306    
307                            return EntityCacheUtil.loadResult(
308                                    finderPath.isEntityCacheEnabled(), finderPath.getResultClass(),
309                                    primaryKey, basePersistenceImpl);
310                    }
311    
312                    return primaryKey;
313            }
314    
315            private Serializable _resultToPrimaryKey(Serializable result) {
316                    if (result instanceof BaseModel<?>) {
317                            BaseModel<?> model = (BaseModel<?>)result;
318    
319                            return model.getPrimaryKeyObj();
320                    }
321                    else if (result instanceof List<?>) {
322                            List<Serializable> list = (List<Serializable>)result;
323    
324                            if (list.isEmpty()) {
325                                    return (Serializable)Collections.emptyList();
326                            }
327    
328                            ArrayList<Serializable> cachedList = new ArrayList<Serializable>(
329                                    list.size());
330    
331                            for (Serializable curResult : list) {
332                                    Serializable primaryKey = _resultToPrimaryKey(curResult);
333    
334                                    cachedList.add(primaryKey);
335                            }
336    
337                            return cachedList;
338                    }
339    
340                    return result;
341            }
342    
343            private static final String _GROUP_KEY_PREFIX = CACHE_NAME.concat(
344                    StringPool.PERIOD);
345    
346            private static final boolean _LOCAL_CACHE_AVAILABLE;
347    
348            private static final ThreadLocal<LRUMap> _localCache;
349    
350            static {
351                    if (PropsValues.VALUE_OBJECT_FINDER_THREAD_LOCAL_CACHE_MAX_SIZE > 0) {
352                            _LOCAL_CACHE_AVAILABLE = true;
353    
354                            _localCache = new AutoResetThreadLocal<LRUMap>(
355                                    FinderCacheImpl.class + "._localCache",
356                                    new LRUMap(
357                                            PropsValues.
358                                                    VALUE_OBJECT_FINDER_THREAD_LOCAL_CACHE_MAX_SIZE));
359                    }
360                    else {
361                            _LOCAL_CACHE_AVAILABLE = false;
362    
363                            _localCache = null;
364                    }
365            }
366    
367            private MultiVMPool _multiVMPool;
368            private final ConcurrentMap<String, PortalCache<Serializable, Serializable>>
369                    _portalCaches =
370                            new ConcurrentHashMap
371                                    <String, PortalCache<Serializable, Serializable>>();
372    
373    }