001    /**
002     * Copyright (c) 2000-2013 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.CacheRegistryItem;
018    import com.liferay.portal.kernel.cache.CacheRegistryUtil;
019    import com.liferay.portal.kernel.cache.MultiVMPool;
020    import com.liferay.portal.kernel.cache.PortalCache;
021    import com.liferay.portal.kernel.dao.orm.EntityCacheUtil;
022    import com.liferay.portal.kernel.dao.orm.FinderCache;
023    import com.liferay.portal.kernel.dao.orm.FinderPath;
024    import com.liferay.portal.kernel.dao.orm.SessionFactory;
025    import com.liferay.portal.kernel.security.pacl.DoPrivileged;
026    import com.liferay.portal.kernel.util.AutoResetThreadLocal;
027    import com.liferay.portal.kernel.util.StringPool;
028    import com.liferay.portal.model.BaseModel;
029    import com.liferay.portal.util.PropsValues;
030    
031    import java.io.Serializable;
032    
033    import java.util.ArrayList;
034    import java.util.Collections;
035    import java.util.List;
036    import java.util.Map;
037    import java.util.concurrent.ConcurrentHashMap;
038    import java.util.concurrent.ConcurrentMap;
039    
040    import org.apache.commons.collections.map.LRUMap;
041    
042    /**
043     * @author Brian Wing Shun Chan
044     * @author Shuyang Zhou
045     */
046    @DoPrivileged
047    public class FinderCacheImpl implements CacheRegistryItem, FinderCache {
048    
049            public static final String CACHE_NAME = FinderCache.class.getName();
050    
051            public void afterPropertiesSet() {
052                    CacheRegistryUtil.register(this);
053            }
054    
055            public void clearCache() {
056                    clearLocalCache();
057    
058                    for (PortalCache<?, ?> portalCache : _portalCaches.values()) {
059                            portalCache.removeAll();
060                    }
061            }
062    
063            public void clearCache(String className) {
064                    clearLocalCache();
065    
066                    PortalCache<?, ?> portalCache = _getPortalCache(className, true);
067    
068                    if (portalCache != null) {
069                            portalCache.removeAll();
070                    }
071            }
072    
073            public void clearLocalCache() {
074                    if (_localCacheAvailable) {
075                            _localCache.remove();
076                    }
077            }
078    
079            public String getRegistryName() {
080                    return CACHE_NAME;
081            }
082    
083            public Object getResult(
084                    FinderPath finderPath, Object[] args, SessionFactory sessionFactory) {
085    
086                    if (!PropsValues.VALUE_OBJECT_FINDER_CACHE_ENABLED ||
087                            !finderPath.isFinderCacheEnabled() ||
088                            !CacheRegistryUtil.isActive()) {
089    
090                            return null;
091                    }
092    
093                    Serializable primaryKey = null;
094    
095                    Map<Serializable, Serializable> localCache = null;
096    
097                    Serializable localCacheKey = null;
098    
099                    if (_localCacheAvailable) {
100                            localCache = _localCache.get();
101    
102                            localCacheKey = finderPath.encodeLocalCacheKey(args);
103    
104                            primaryKey = localCache.get(localCacheKey);
105                    }
106    
107                    if (primaryKey == null) {
108                            PortalCache<Serializable, Serializable> portalCache =
109                                    _getPortalCache(finderPath.getCacheName(), true);
110    
111                            Serializable cacheKey = finderPath.encodeCacheKey(args);
112    
113                            primaryKey = portalCache.get(cacheKey);
114    
115                            if (primaryKey != null) {
116                                    if (_localCacheAvailable) {
117                                            localCache.put(localCacheKey, primaryKey);
118                                    }
119                            }
120                    }
121    
122                    if (primaryKey != null) {
123                            return _primaryKeyToResult(finderPath, sessionFactory, primaryKey);
124                    }
125                    else {
126                            return null;
127                    }
128            }
129    
130            public void invalidate() {
131                    clearCache();
132            }
133    
134            public void putResult(FinderPath finderPath, Object[] args, Object result) {
135                    if (!PropsValues.VALUE_OBJECT_FINDER_CACHE_ENABLED ||
136                            !finderPath.isFinderCacheEnabled() ||
137                            !CacheRegistryUtil.isActive() ||
138                            (result == null)) {
139    
140                            return;
141                    }
142    
143                    Serializable primaryKey = _resultToPrimaryKey((Serializable)result);
144    
145                    if (_localCacheAvailable) {
146                            Map<Serializable, Serializable> localCache = _localCache.get();
147    
148                            Serializable localCacheKey = finderPath.encodeLocalCacheKey(args);
149    
150                            localCache.put(localCacheKey, primaryKey);
151                    }
152    
153                    PortalCache<Serializable, Serializable> portalCache = _getPortalCache(
154                            finderPath.getCacheName(), true);
155    
156                    Serializable cacheKey = finderPath.encodeCacheKey(args);
157    
158                    portalCache.put(cacheKey, primaryKey);
159            }
160    
161            public void removeCache(String className) {
162                    _portalCaches.remove(className);
163    
164                    String groupKey = _GROUP_KEY_PREFIX.concat(className);
165    
166                    _multiVMPool.removeCache(groupKey);
167            }
168    
169            public void removeResult(FinderPath finderPath, Object[] args) {
170                    if (!PropsValues.VALUE_OBJECT_FINDER_CACHE_ENABLED ||
171                            !finderPath.isFinderCacheEnabled() ||
172                            !CacheRegistryUtil.isActive()) {
173    
174                            return;
175                    }
176    
177                    if (_localCacheAvailable) {
178                            Map<Serializable, Serializable> localCache = _localCache.get();
179    
180                            Serializable localCacheKey = finderPath.encodeLocalCacheKey(args);
181    
182                            localCache.remove(localCacheKey);
183                    }
184    
185                    PortalCache<Serializable, Serializable> portalCache = _getPortalCache(
186                            finderPath.getCacheName(), true);
187    
188                    Serializable cacheKey = finderPath.encodeCacheKey(args);
189    
190                    portalCache.remove(cacheKey);
191            }
192    
193            public void setMultiVMPool(MultiVMPool multiVMPool) {
194                    _multiVMPool = multiVMPool;
195            }
196    
197            private PortalCache<Serializable, Serializable> _getPortalCache(
198                    String className, boolean createIfAbsent) {
199    
200                    PortalCache<Serializable, Serializable> portalCache = _portalCaches.get(
201                            className);
202    
203                    if ((portalCache == null) && createIfAbsent) {
204                            String groupKey = _GROUP_KEY_PREFIX.concat(className);
205    
206                            portalCache =
207                                    (PortalCache<Serializable, Serializable>)_multiVMPool.getCache(
208                                            groupKey, PropsValues.VALUE_OBJECT_FINDER_BLOCKING_CACHE);
209    
210                            PortalCache<Serializable, Serializable> previousPortalCache =
211                                    _portalCaches.putIfAbsent(className, portalCache);
212    
213                            if (previousPortalCache != null) {
214                                    portalCache = previousPortalCache;
215                            }
216                    }
217    
218                    return portalCache;
219            }
220    
221            private Serializable _primaryKeyToResult(
222                    FinderPath finderPath, SessionFactory sessionFactory,
223                    Serializable primaryKey) {
224    
225                    if (primaryKey instanceof List<?>) {
226                            List<Serializable> cachedList = (List<Serializable>)primaryKey;
227    
228                            if (cachedList.isEmpty()) {
229                                    return (Serializable)Collections.emptyList();
230                            }
231    
232                            List<Serializable> list = new ArrayList<Serializable>(
233                                    cachedList.size());
234    
235                            for (Serializable curPrimaryKey : cachedList) {
236                                    Serializable result = _primaryKeyToResult(
237                                            finderPath, sessionFactory, curPrimaryKey);
238    
239                                    list.add(result);
240                            }
241    
242                            return (Serializable)list;
243                    }
244                    else if (BaseModel.class.isAssignableFrom(
245                                            finderPath.getResultClass())) {
246    
247                            return EntityCacheUtil.loadResult(
248                                    finderPath.isEntityCacheEnabled(), finderPath.getResultClass(),
249                                    primaryKey, sessionFactory);
250                    }
251                    else {
252                            return primaryKey;
253                    }
254            }
255    
256            private Serializable _resultToPrimaryKey(Serializable result) {
257                    if (result instanceof BaseModel<?>) {
258                            BaseModel<?> model = (BaseModel<?>)result;
259    
260                            return model.getPrimaryKeyObj();
261                    }
262                    else if (result instanceof List<?>) {
263                            List<Serializable> list = (List<Serializable>)result;
264    
265                            if (list.isEmpty()) {
266                                    return (Serializable)Collections.emptyList();
267                            }
268    
269                            ArrayList<Serializable> cachedList = new ArrayList<Serializable>(
270                                    list.size());
271    
272                            for (Serializable curResult : list) {
273                                    Serializable primaryKey = _resultToPrimaryKey(curResult);
274    
275                                    cachedList.add(primaryKey);
276                            }
277    
278                            return cachedList;
279                    }
280                    else {
281                            return result;
282                    }
283            }
284    
285            private static final String _GROUP_KEY_PREFIX = CACHE_NAME.concat(
286                    StringPool.PERIOD);
287    
288            private static ThreadLocal<LRUMap> _localCache;
289            private static boolean _localCacheAvailable;
290    
291            static {
292                    if (PropsValues.VALUE_OBJECT_FINDER_THREAD_LOCAL_CACHE_MAX_SIZE > 0) {
293                            _localCache = new AutoResetThreadLocal<LRUMap>(
294                                    FinderCacheImpl.class + "._localCache",
295                                    new LRUMap(
296                                            PropsValues.
297                                                    VALUE_OBJECT_FINDER_THREAD_LOCAL_CACHE_MAX_SIZE));
298                            _localCacheAvailable = true;
299                    }
300            }
301    
302            private MultiVMPool _multiVMPool;
303            private ConcurrentMap<String, PortalCache<Serializable, Serializable>>
304                    _portalCaches =
305                            new ConcurrentHashMap
306                                    <String, PortalCache<Serializable, Serializable>>();
307    
308    }