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.PortalCacheManager;
023    import com.liferay.portal.kernel.dao.orm.EntityCacheUtil;
024    import com.liferay.portal.kernel.dao.orm.FinderCache;
025    import com.liferay.portal.kernel.dao.orm.FinderPath;
026    import com.liferay.portal.kernel.security.pacl.DoPrivileged;
027    import com.liferay.portal.kernel.util.AutoResetThreadLocal;
028    import com.liferay.portal.kernel.util.StringPool;
029    import com.liferay.portal.model.BaseModel;
030    import com.liferay.portal.service.persistence.impl.BasePersistenceImpl;
031    import com.liferay.portal.util.PropsValues;
032    
033    import java.io.Serializable;
034    
035    import java.util.ArrayList;
036    import java.util.Collections;
037    import java.util.HashSet;
038    import java.util.List;
039    import java.util.Map;
040    import java.util.Set;
041    import java.util.concurrent.ConcurrentHashMap;
042    import java.util.concurrent.ConcurrentMap;
043    
044    import org.apache.commons.collections.map.LRUMap;
045    
046    /**
047     * @author Brian Wing Shun Chan
048     * @author Shuyang Zhou
049     */
050    @DoPrivileged
051    public class FinderCacheImpl
052            implements CacheManagerListener, CacheRegistryItem, FinderCache {
053    
054            public static final String CACHE_NAME = FinderCache.class.getName();
055    
056            public void afterPropertiesSet() {
057                    CacheRegistryUtil.register(this);
058            }
059    
060            @Override
061            public void clearCache() {
062                    clearLocalCache();
063    
064                    for (PortalCache<?, ?> portalCache : _portalCaches.values()) {
065                            portalCache.removeAll();
066                    }
067            }
068    
069            @Override
070            public void clearCache(String className) {
071                    clearLocalCache();
072    
073                    PortalCache<?, ?> portalCache = _getPortalCache(className, true);
074    
075                    if (portalCache != null) {
076                            portalCache.removeAll();
077                    }
078            }
079    
080            @Override
081            public void clearLocalCache() {
082                    if (_LOCAL_CACHE_AVAILABLE) {
083                            _localCache.remove();
084                    }
085            }
086    
087            @Override
088            public void dispose() {
089                    _portalCaches.clear();
090            }
091    
092            @Override
093            public String getRegistryName() {
094                    return CACHE_NAME;
095            }
096    
097            @Override
098            public Object getResult(
099                    FinderPath finderPath, Object[] args,
100                    BasePersistenceImpl<? extends BaseModel<?>> basePersistenceImpl) {
101    
102                    if (!PropsValues.VALUE_OBJECT_FINDER_CACHE_ENABLED ||
103                            !finderPath.isFinderCacheEnabled() ||
104                            !CacheRegistryUtil.isActive()) {
105    
106                            return null;
107                    }
108    
109                    Serializable primaryKey = null;
110    
111                    Map<Serializable, Serializable> localCache = null;
112    
113                    Serializable localCacheKey = null;
114    
115                    if (_LOCAL_CACHE_AVAILABLE) {
116                            localCache = _localCache.get();
117    
118                            localCacheKey = finderPath.encodeLocalCacheKey(args);
119    
120                            primaryKey = localCache.get(localCacheKey);
121                    }
122    
123                    if (primaryKey == null) {
124                            PortalCache<Serializable, Serializable> portalCache =
125                                    _getPortalCache(finderPath.getCacheName(), true);
126    
127                            Serializable cacheKey = finderPath.encodeCacheKey(args);
128    
129                            primaryKey = portalCache.get(cacheKey);
130    
131                            if (primaryKey != null) {
132                                    if (_LOCAL_CACHE_AVAILABLE) {
133                                            localCache.put(localCacheKey, primaryKey);
134                                    }
135                            }
136                    }
137    
138                    if (primaryKey != null) {
139                            return _primaryKeyToResult(
140                                    finderPath, basePersistenceImpl, primaryKey);
141                    }
142    
143                    return null;
144            }
145    
146            @Override
147            public void init() {
148            }
149    
150            @Override
151            public void invalidate() {
152                    clearCache();
153            }
154    
155            @Override
156            public void notifyCacheAdded(String name) {
157            }
158    
159            @Override
160            public void notifyCacheRemoved(String name) {
161                    _portalCaches.remove(name);
162            }
163    
164            @Override
165            public void putResult(FinderPath finderPath, Object[] args, Object result) {
166                    putResult(finderPath, args, result, true);
167            }
168    
169            @Override
170            public void putResult(
171                    FinderPath finderPath, Object[] args, Object result, boolean quiet) {
172    
173                    if (!PropsValues.VALUE_OBJECT_FINDER_CACHE_ENABLED ||
174                            !finderPath.isFinderCacheEnabled() ||
175                            !CacheRegistryUtil.isActive() ||
176                            (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                            portalCache.putQuiet(cacheKey, primaryKey);
198                    }
199                    else {
200                            portalCache.put(cacheKey, primaryKey);
201                    }
202            }
203    
204            @Override
205            public void removeCache(String className) {
206                    _portalCaches.remove(className);
207    
208                    String groupKey = _GROUP_KEY_PREFIX.concat(className);
209    
210                    _multiVMPool.removeCache(groupKey);
211            }
212    
213            @Override
214            public void removeResult(FinderPath finderPath, Object[] args) {
215                    if (!PropsValues.VALUE_OBJECT_FINDER_CACHE_ENABLED ||
216                            !finderPath.isFinderCacheEnabled() ||
217                            !CacheRegistryUtil.isActive()) {
218    
219                            return;
220                    }
221    
222                    if (_LOCAL_CACHE_AVAILABLE) {
223                            Map<Serializable, Serializable> localCache = _localCache.get();
224    
225                            Serializable localCacheKey = finderPath.encodeLocalCacheKey(args);
226    
227                            localCache.remove(localCacheKey);
228                    }
229    
230                    PortalCache<Serializable, Serializable> portalCache = _getPortalCache(
231                            finderPath.getCacheName(), true);
232    
233                    Serializable cacheKey = finderPath.encodeCacheKey(args);
234    
235                    portalCache.remove(cacheKey);
236            }
237    
238            public void setMultiVMPool(MultiVMPool multiVMPool) {
239                    _multiVMPool = multiVMPool;
240    
241                    PortalCacheManager<? extends Serializable, ? extends Serializable>
242                            portalCacheManager = _multiVMPool.getCacheManager();
243    
244                    portalCacheManager.registerCacheManagerListener(this);
245            }
246    
247            private PortalCache<Serializable, Serializable> _getPortalCache(
248                    String className, boolean createIfAbsent) {
249    
250                    PortalCache<Serializable, Serializable> portalCache = _portalCaches.get(
251                            className);
252    
253                    if ((portalCache == null) && createIfAbsent) {
254                            String groupKey = _GROUP_KEY_PREFIX.concat(className);
255    
256                            portalCache =
257                                    (PortalCache<Serializable, Serializable>)_multiVMPool.getCache(
258                                            groupKey, PropsValues.VALUE_OBJECT_FINDER_BLOCKING_CACHE);
259    
260                            PortalCache<Serializable, Serializable> previousPortalCache =
261                                    _portalCaches.putIfAbsent(className, portalCache);
262    
263                            if (previousPortalCache != null) {
264                                    portalCache = previousPortalCache;
265                            }
266                    }
267    
268                    return portalCache;
269            }
270    
271            private Serializable _primaryKeyToResult(
272                    FinderPath finderPath,
273                    BasePersistenceImpl<? extends BaseModel<?>> basePersistenceImpl,
274                    Serializable primaryKey) {
275    
276                    if (primaryKey instanceof List<?>) {
277                            List<Serializable> primaryKeys = (List<Serializable>)primaryKey;
278    
279                            if (primaryKeys.isEmpty()) {
280                                    return (Serializable)Collections.emptyList();
281                            }
282    
283                            Set<Serializable> primaryKeysSet = new HashSet<Serializable>(
284                                    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<Serializable>(
294                                    primaryKeys.size());
295    
296                            for (Serializable curPrimaryKey : primaryKeys) {
297                                    list.add(map.get(curPrimaryKey));
298                            }
299    
300                            return (Serializable)Collections.unmodifiableList(list);
301                    }
302                    else if (BaseModel.class.isAssignableFrom(
303                                            finderPath.getResultClass())) {
304    
305                            return EntityCacheUtil.loadResult(
306                                    finderPath.isEntityCacheEnabled(), finderPath.getResultClass(),
307                                    primaryKey, basePersistenceImpl);
308                    }
309    
310                    return primaryKey;
311            }
312    
313            private Serializable _resultToPrimaryKey(Serializable result) {
314                    if (result instanceof BaseModel<?>) {
315                            BaseModel<?> model = (BaseModel<?>)result;
316    
317                            return model.getPrimaryKeyObj();
318                    }
319                    else if (result instanceof List<?>) {
320                            List<Serializable> list = (List<Serializable>)result;
321    
322                            if (list.isEmpty()) {
323                                    return (Serializable)Collections.emptyList();
324                            }
325    
326                            ArrayList<Serializable> cachedList = new ArrayList<Serializable>(
327                                    list.size());
328    
329                            for (Serializable curResult : list) {
330                                    Serializable primaryKey = _resultToPrimaryKey(curResult);
331    
332                                    cachedList.add(primaryKey);
333                            }
334    
335                            return cachedList;
336                    }
337    
338                    return result;
339            }
340    
341            private static final String _GROUP_KEY_PREFIX = CACHE_NAME.concat(
342                    StringPool.PERIOD);
343    
344            private static final boolean _LOCAL_CACHE_AVAILABLE;
345    
346            private static final ThreadLocal<LRUMap> _localCache;
347    
348            static {
349                    if (PropsValues.VALUE_OBJECT_FINDER_THREAD_LOCAL_CACHE_MAX_SIZE > 0) {
350                            _LOCAL_CACHE_AVAILABLE = true;
351    
352                            _localCache = new AutoResetThreadLocal<LRUMap>(
353                                    FinderCacheImpl.class + "._localCache",
354                                    new LRUMap(
355                                            PropsValues.
356                                                    VALUE_OBJECT_FINDER_THREAD_LOCAL_CACHE_MAX_SIZE));
357                    }
358                    else {
359                            _LOCAL_CACHE_AVAILABLE = false;
360    
361                            _localCache = null;
362                    }
363            }
364    
365            private MultiVMPool _multiVMPool;
366            private final ConcurrentMap<String, PortalCache<Serializable, Serializable>>
367                    _portalCaches =
368                            new ConcurrentHashMap
369                                    <String, PortalCache<Serializable, Serializable>>();
370    
371    }