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