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