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