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