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, false);
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                    Object primaryKey = null;
092    
093                    Map<Serializable, Object> 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 portalCache = _getPortalCache(
107                                    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                    Object primaryKey = _resultToPrimaryKey(result);
142    
143                    if (_localCacheAvailable) {
144                            Map<Serializable, Object> localCache = _localCache.get();
145    
146                            Serializable localCacheKey = finderPath.encodeLocalCacheKey(args);
147    
148                            localCache.put(localCacheKey, primaryKey);
149                    }
150    
151                    PortalCache 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, Object> localCache = _localCache.get();
177    
178                            Serializable localCacheKey = finderPath.encodeLocalCacheKey(args);
179    
180                            localCache.remove(localCacheKey);
181                    }
182    
183                    PortalCache 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 _getPortalCache(
196                    String className, boolean createIfAbsent) {
197    
198                    PortalCache portalCache = _portalCaches.get(className);
199    
200                    if ((portalCache == null) && createIfAbsent) {
201                            String groupKey = _GROUP_KEY_PREFIX.concat(className);
202    
203                            portalCache = _multiVMPool.getCache(
204                                    groupKey, PropsValues.VALUE_OBJECT_FINDER_BLOCKING_CACHE);
205    
206                            PortalCache previousPortalCache = _portalCaches.putIfAbsent(
207                                    className, portalCache);
208    
209                            if (previousPortalCache != null) {
210                                    portalCache = previousPortalCache;
211                            }
212                    }
213    
214                    return portalCache;
215            }
216    
217            private Object _primaryKeyToResult(
218                    FinderPath finderPath, SessionFactory sessionFactory,
219                    Object primaryKey) {
220    
221                    if (primaryKey instanceof List<?>) {
222                            List<Object> cachedList = (List<Object>)primaryKey;
223    
224                            if (cachedList.isEmpty()) {
225                                    return Collections.emptyList();
226                            }
227    
228                            List<Object> list = new ArrayList<Object>(cachedList.size());
229    
230                            for (Object curPrimaryKey : cachedList) {
231                                    Object result = _primaryKeyToResult(
232                                            finderPath, sessionFactory, curPrimaryKey);
233    
234                                    list.add(result);
235                            }
236    
237                            return list;
238                    }
239                    else if (BaseModel.class.isAssignableFrom(
240                                            finderPath.getResultClass())) {
241    
242                            return EntityCacheUtil.loadResult(
243                                    finderPath.isEntityCacheEnabled(), finderPath.getResultClass(),
244                                    (Serializable)primaryKey, sessionFactory);
245                    }
246                    else {
247                            return primaryKey;
248                    }
249            }
250    
251            private Object _resultToPrimaryKey(Object result) {
252                    if (result instanceof BaseModel<?>) {
253                            BaseModel<?> model = (BaseModel<?>)result;
254    
255                            return model.getPrimaryKeyObj();
256                    }
257                    else if (result instanceof List<?>) {
258                            List<Object> list = (List<Object>)result;
259    
260                            if (list.isEmpty()) {
261                                    return Collections.emptyList();
262                            }
263    
264                            List<Object> cachedList = new ArrayList<Object>(list.size());
265    
266                            for (Object curResult : list) {
267                                    Object primaryKey = _resultToPrimaryKey(curResult);
268    
269                                    cachedList.add(primaryKey);
270                            }
271    
272                            return cachedList;
273                    }
274                    else {
275                            return result;
276                    }
277            }
278    
279            private static final String _GROUP_KEY_PREFIX = CACHE_NAME.concat(
280                    StringPool.PERIOD);
281    
282            private static ThreadLocal<LRUMap> _localCache;
283            private static boolean _localCacheAvailable;
284    
285            static {
286                    if (PropsValues.VALUE_OBJECT_FINDER_THREAD_LOCAL_CACHE_MAX_SIZE > 0) {
287                            _localCache = new AutoResetThreadLocal<LRUMap>(
288                                    FinderCacheImpl.class + "._localCache",
289                                    new LRUMap(
290                                            PropsValues.
291                                                    VALUE_OBJECT_FINDER_THREAD_LOCAL_CACHE_MAX_SIZE));
292                            _localCacheAvailable = true;
293                    }
294            }
295    
296            private MultiVMPool _multiVMPool;
297            private ConcurrentMap<String, PortalCache> _portalCaches =
298                    new ConcurrentHashMap<String, PortalCache>();
299    
300    }