1   /**
2    * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
3    *
4    * This library is free software; you can redistribute it and/or modify it under
5    * the terms of the GNU Lesser General Public License as published by the Free
6    * Software Foundation; either version 2.1 of the License, or (at your option)
7    * any later version.
8    *
9    * This library is distributed in the hope that it will be useful, but WITHOUT
10   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11   * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12   * details.
13   */
14  
15  package com.liferay.portal.dao.orm.common;
16  
17  import com.liferay.portal.kernel.cache.CacheKVP;
18  import com.liferay.portal.kernel.cache.CacheRegistry;
19  import com.liferay.portal.kernel.cache.CacheRegistryItem;
20  import com.liferay.portal.kernel.cache.MultiVMPool;
21  import com.liferay.portal.kernel.cache.PortalCache;
22  import com.liferay.portal.kernel.dao.orm.EntityCacheUtil;
23  import com.liferay.portal.kernel.dao.orm.FinderCache;
24  import com.liferay.portal.kernel.dao.orm.FinderPath;
25  import com.liferay.portal.kernel.dao.orm.SessionFactory;
26  import com.liferay.portal.kernel.util.AutoResetThreadLocal;
27  import com.liferay.portal.kernel.util.StringPool;
28  import com.liferay.portal.model.BaseModel;
29  import com.liferay.portal.util.PropsValues;
30  
31  import java.io.Serializable;
32  
33  import java.util.ArrayList;
34  import java.util.List;
35  import java.util.Map;
36  import java.util.concurrent.ConcurrentHashMap;
37  
38  import org.apache.commons.collections.map.LRUMap;
39  
40  /**
41   * <a href="FinderCacheImpl.java.html"><b><i>View Source</i></b></a>
42   *
43   * @author Brian Wing Shun Chan
44   */
45  public class FinderCacheImpl implements CacheRegistryItem, FinderCache {
46  
47      public static final String CACHE_NAME = FinderCache.class.getName();
48  
49      public void afterPropertiesSet() {
50          CacheRegistry.register(this);
51      }
52  
53      public void clearCache() {
54          clearLocalCache();
55  
56          PortalCache[] portalCaches = _portalCaches.values().toArray(
57              new PortalCache[_portalCaches.size()]);
58  
59          for (PortalCache portalCache : portalCaches) {
60              portalCache.removeAll();
61          }
62      }
63  
64      public void clearCache(String className) {
65          clearLocalCache();
66  
67          PortalCache portalCache = _getPortalCache(className);
68  
69          portalCache.removeAll();
70      }
71  
72      public void clearLocalCache() {
73          if (_localCacheAvailable) {
74              Map<String, Object> localCache = _localCache.get();
75  
76              localCache.clear();
77          }
78      }
79  
80      public String getRegistryName() {
81          return CACHE_NAME;
82      }
83  
84      public Object getResult(
85          FinderPath finderPath, Object[] args, SessionFactory sessionFactory) {
86  
87          if (!PropsValues.VALUE_OBJECT_FINDER_CACHE_ENABLED ||
88              !finderPath.isFinderCacheEnabled() || !CacheRegistry.isActive()) {
89  
90              return null;
91          }
92  
93          Object primaryKey = null;
94  
95          Map<String, Object> localCache = null;
96  
97          String localCacheKey = null;
98  
99          if (_localCacheAvailable) {
100             localCache = _localCache.get();
101 
102             localCacheKey = finderPath.encodeLocalCacheKey(args);
103 
104             primaryKey = localCache.get(localCacheKey);
105         }
106 
107         if (primaryKey == null) {
108             PortalCache portalCache = _getPortalCache(
109                 finderPath.getClassName());
110 
111             String cacheKey = finderPath.encodeCacheKey(args);
112 
113             primaryKey = portalCache.get(cacheKey);
114 
115             if (primaryKey != null) {
116                 if (_localCacheAvailable) {
117                     localCache.put(localCacheKey, primaryKey);
118                 }
119             }
120         }
121 
122         if (primaryKey != null) {
123             return _primaryKeyToResult(finderPath, sessionFactory, primaryKey);
124         }
125         else {
126             return null;
127         }
128     }
129 
130     public void invalidate() {
131         clearCache();
132     }
133 
134     public void putResult(FinderPath finderPath, Object[] args, Object result) {
135         if (!PropsValues.VALUE_OBJECT_FINDER_CACHE_ENABLED ||
136             !finderPath.isFinderCacheEnabled() || !CacheRegistry.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(finderPath.getClassName());
153 
154         String cacheKey = finderPath.encodeCacheKey(args);
155 
156         portalCache.put(cacheKey, primaryKey);
157     }
158 
159     public void removeResult(FinderPath finderPath, Object[] args) {
160         if (!PropsValues.VALUE_OBJECT_FINDER_CACHE_ENABLED ||
161             !finderPath.isFinderCacheEnabled() || !CacheRegistry.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(finderPath.getClassName());
175 
176         String cacheKey = finderPath.encodeCacheKey(args);
177 
178         portalCache.remove(cacheKey);
179     }
180 
181     public void setMultiVMPool(MultiVMPool multiVMPool) {
182         _multiVMPool = multiVMPool;
183     }
184 
185     private PortalCache _getPortalCache(String className) {
186         String groupKey = _GROUP_KEY_PREFIX.concat(className);
187 
188         PortalCache portalCache = _portalCaches.get(groupKey);
189 
190         if (portalCache == null) {
191             portalCache = _multiVMPool.getCache(
192                 groupKey, PropsValues.VALUE_OBJECT_FINDER_BLOCKING_CACHE);
193 
194             _portalCaches.put(groupKey, portalCache);
195         }
196 
197         return portalCache;
198     }
199 
200     private Object _primaryKeyToResult(
201         FinderPath finderPath, SessionFactory sessionFactory,
202         Object primaryKey) {
203 
204         if (primaryKey instanceof CacheKVP) {
205             CacheKVP cacheKVP = (CacheKVP)primaryKey;
206 
207             Class<?> modelClass = cacheKVP.getModelClass();
208             Serializable primaryKeyObj = cacheKVP.getPrimaryKeyObj();
209 
210             return EntityCacheUtil.loadResult(
211                 finderPath.isEntityCacheEnabled(), modelClass, primaryKeyObj,
212                 sessionFactory);
213         }
214         else if (primaryKey instanceof List<?>) {
215             List<Object> cachedList = (List<Object>)primaryKey;
216 
217             List<Object> list = new ArrayList<Object>(cachedList.size());
218 
219             for (Object curPrimaryKey : cachedList) {
220                 Object result = _primaryKeyToResult(
221                     finderPath, sessionFactory, curPrimaryKey);
222 
223                 list.add(result);
224             }
225 
226             return list;
227         }
228         else {
229             return primaryKey;
230         }
231     }
232 
233     private Object _resultToPrimaryKey(Object result) {
234         if (result instanceof BaseModel<?>) {
235             BaseModel<?> model = (BaseModel<?>)result;
236 
237             Class<?> modelClass = model.getClass();
238             Serializable primaryKeyObj = model.getPrimaryKeyObj();
239 
240             return new CacheKVP(modelClass, primaryKeyObj);
241         }
242         else if (result instanceof List<?>) {
243             List<Object> list = (List<Object>)result;
244 
245             List<Object> cachedList = new ArrayList<Object>(list.size());
246 
247             for (Object curResult : list) {
248                 Object primaryKey = _resultToPrimaryKey(curResult);
249 
250                 cachedList.add(primaryKey);
251             }
252 
253             return cachedList;
254         }
255         else {
256             return result;
257         }
258     }
259 
260     private static final String _GROUP_KEY_PREFIX = CACHE_NAME.concat(
261         StringPool.PERIOD);
262 
263     private static ThreadLocal<LRUMap> _localCache;
264     private static boolean _localCacheAvailable;
265 
266     static {
267         if (PropsValues.VALUE_OBJECT_FINDER_THREAD_LOCAL_CACHE_MAX_SIZE > 0) {
268             _localCache = new AutoResetThreadLocal<LRUMap>(new LRUMap(
269                 PropsValues.VALUE_OBJECT_FINDER_THREAD_LOCAL_CACHE_MAX_SIZE));
270             _localCacheAvailable = true;
271         }
272     }
273 
274     private MultiVMPool _multiVMPool;
275     private Map<String, PortalCache> _portalCaches =
276         new ConcurrentHashMap<String, PortalCache>();
277 
278 }