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