001    /**
002     * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
003     *
004     * The contents of this file are subject to the terms of the Liferay Enterprise
005     * Subscription License ("License"). You may not use this file except in
006     * compliance with the License. You can obtain a copy of the License by
007     * contacting Liferay, Inc. See the License for the specific language governing
008     * permissions and limitations under the License, including but not limited to
009     * distribution rights of the Software.
010     *
011     *
012     *
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.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     */
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<String, Object> localCache = null;
094    
095                    String 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.getClassName(), true);
108    
109                            String 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<String, Object> localCache = _localCache.get();
145    
146                            String localCacheKey = finderPath.encodeLocalCacheKey(args);
147    
148                            localCache.put(localCacheKey, primaryKey);
149                    }
150    
151                    PortalCache portalCache = _getPortalCache(
152                            finderPath.getClassName(), true);
153    
154                    String cacheKey = finderPath.encodeCacheKey(args);
155    
156                    portalCache.put(cacheKey, primaryKey);
157            }
158    
159            public void removeCache(String className) {
160                    String groupKey = _GROUP_KEY_PREFIX.concat(className);
161    
162                    _portalCaches.remove(groupKey);
163                    _multiVMPool.removeCache(groupKey);
164            }
165    
166            public void removeResult(FinderPath finderPath, Object[] args) {
167                    if (!PropsValues.VALUE_OBJECT_FINDER_CACHE_ENABLED ||
168                            !finderPath.isFinderCacheEnabled() ||
169                            !CacheRegistryUtil.isActive()) {
170    
171                            return;
172                    }
173    
174                    if (_localCacheAvailable) {
175                            Map<String, Object> localCache = _localCache.get();
176    
177                            String localCacheKey = finderPath.encodeLocalCacheKey(args);
178    
179                            localCache.remove(localCacheKey);
180                    }
181    
182                    PortalCache portalCache = _getPortalCache(
183                            finderPath.getClassName(), true);
184    
185                    String cacheKey = finderPath.encodeCacheKey(args);
186    
187                    portalCache.remove(cacheKey);
188            }
189    
190            public void setMultiVMPool(MultiVMPool multiVMPool) {
191                    _multiVMPool = multiVMPool;
192            }
193    
194            private PortalCache _getPortalCache(
195                    String className, boolean createIfAbsent) {
196    
197                    String groupKey = _GROUP_KEY_PREFIX.concat(className);
198    
199                    PortalCache portalCache = _portalCaches.get(groupKey);
200    
201                    if ((portalCache == null) && createIfAbsent) {
202                            portalCache = _multiVMPool.getCache(
203                                    groupKey, PropsValues.VALUE_OBJECT_FINDER_BLOCKING_CACHE);
204    
205                            PortalCache previousPortalCache = _portalCaches.putIfAbsent(
206                                    groupKey, portalCache);
207    
208                            if (previousPortalCache != null) {
209                                    portalCache = previousPortalCache;
210                            }
211    
212                            portalCache.setDebug(true);
213                    }
214    
215                    return portalCache;
216            }
217    
218            private Object _primaryKeyToResult(
219                    FinderPath finderPath, SessionFactory sessionFactory,
220                    Object primaryKey) {
221    
222                    if (primaryKey instanceof CacheKVP) {
223                            CacheKVP cacheKVP = (CacheKVP)primaryKey;
224    
225                            Class<?> modelClass = cacheKVP.getModelClass();
226                            Serializable primaryKeyObj = cacheKVP.getPrimaryKeyObj();
227    
228                            return EntityCacheUtil.loadResult(
229                                    finderPath.isEntityCacheEnabled(), modelClass, primaryKeyObj,
230                                    sessionFactory);
231                    }
232                    else if (primaryKey instanceof List<?>) {
233                            List<Object> cachedList = (List<Object>)primaryKey;
234    
235                            if (cachedList.isEmpty()) {
236                                    return Collections.EMPTY_LIST;
237                            }
238    
239                            List<Object> list = new ArrayList<Object>(cachedList.size());
240    
241                            for (Object curPrimaryKey : cachedList) {
242                                    Object result = _primaryKeyToResult(
243                                            finderPath, sessionFactory, curPrimaryKey);
244    
245                                    list.add(result);
246                            }
247    
248                            return list;
249                    }
250                    else {
251                            return primaryKey;
252                    }
253            }
254    
255            private Object _resultToPrimaryKey(Object result) {
256                    if (result instanceof BaseModel<?>) {
257                            BaseModel<?> model = (BaseModel<?>)result;
258    
259                            Class<?> modelClass = model.getClass();
260                            Serializable primaryKeyObj = model.getPrimaryKeyObj();
261    
262                            return new CacheKVP(modelClass, primaryKeyObj);
263                    }
264                    else if (result instanceof List<?>) {
265                            List<Object> list = (List<Object>)result;
266    
267                            if (list.isEmpty()) {
268                                    return Collections.EMPTY_LIST;
269                            }
270    
271                            List<Object> cachedList = new ArrayList<Object>(list.size());
272    
273                            for (Object curResult : list) {
274                                    Object primaryKey = _resultToPrimaryKey(curResult);
275    
276                                    cachedList.add(primaryKey);
277                            }
278    
279                            return cachedList;
280                    }
281                    else {
282                            return result;
283                    }
284            }
285    
286            private static final String _GROUP_KEY_PREFIX = CACHE_NAME.concat(
287                    StringPool.PERIOD);
288    
289            private static ThreadLocal<LRUMap> _localCache;
290            private static boolean _localCacheAvailable;
291    
292            static {
293                    if (PropsValues.VALUE_OBJECT_FINDER_THREAD_LOCAL_CACHE_MAX_SIZE > 0) {
294                            _localCache = new AutoResetThreadLocal<LRUMap>(
295                                    FinderCacheImpl.class + "._localCache",
296                                    new LRUMap(
297                                            PropsValues.
298                                                    VALUE_OBJECT_FINDER_THREAD_LOCAL_CACHE_MAX_SIZE));
299                            _localCacheAvailable = true;
300                    }
301            }
302    
303            private MultiVMPool _multiVMPool;
304            private ConcurrentMap<String, PortalCache> _portalCaches =
305                    new ConcurrentHashMap<String, PortalCache>();
306    
307    }