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