1   /**
2    * Copyright (c) 2000-2009 Liferay, Inc. All rights reserved.
3    *
4    *
5    *
6    *
7    * The contents of this file are subject to the terms of the Liferay Enterprise
8    * Subscription License ("License"). You may not use this file except in
9    * compliance with the License. You can obtain a copy of the License by
10   * contacting Liferay, Inc. See the License for the specific language governing
11   * permissions and limitations under the License, including but not limited to
12   * distribution rights of the Software.
13   *
14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20   * SOFTWARE.
21   */
22  
23  package com.liferay.portal.dao.orm.common;
24  
25  import com.liferay.portal.kernel.cache.CacheKVP;
26  import com.liferay.portal.kernel.cache.CacheRegistry;
27  import com.liferay.portal.kernel.cache.CacheRegistryItem;
28  import com.liferay.portal.kernel.cache.MultiVMPool;
29  import com.liferay.portal.kernel.cache.PortalCache;
30  import com.liferay.portal.kernel.dao.orm.EntityCacheUtil;
31  import com.liferay.portal.kernel.dao.orm.FinderCache;
32  import com.liferay.portal.kernel.dao.orm.FinderPath;
33  import com.liferay.portal.kernel.dao.orm.SessionFactory;
34  import com.liferay.portal.kernel.util.InitialThreadLocal;
35  import com.liferay.portal.kernel.util.StringBundler;
36  import com.liferay.portal.kernel.util.StringPool;
37  import com.liferay.portal.model.BaseModel;
38  import com.liferay.portal.util.PropsValues;
39  
40  import java.io.Serializable;
41  
42  import java.util.ArrayList;
43  import java.util.List;
44  import java.util.Map;
45  import java.util.concurrent.ConcurrentHashMap;
46  
47  import org.apache.commons.collections.map.LRUMap;
48  
49  /**
50   * <a href="FinderCacheImpl.java.html"><b><i>View Source</i></b></a>
51   *
52   * @author Brian Wing Shun Chan
53   */
54  public class FinderCacheImpl implements CacheRegistryItem, FinderCache {
55  
56      public static final String CACHE_NAME = FinderCache.class.getName();
57  
58      public void afterPropertiesSet() {
59          CacheRegistry.register(this);
60      }
61  
62      public void clearCache() {
63          clearLocalCache();
64  
65          PortalCache[] portalCaches = _portalCaches.values().toArray(
66              new PortalCache[_portalCaches.size()]);
67  
68          for (PortalCache portalCache : portalCaches) {
69              portalCache.removeAll();
70          }
71      }
72  
73      public void clearCache(String className) {
74          clearLocalCache();
75  
76          PortalCache portalCache = _getPortalCache(className);
77  
78          portalCache.removeAll();
79      }
80  
81      public void clearLocalCache() {
82          if (_localCacheEnabled.get().booleanValue()) {
83              Map<String, Object> localCache = _localCache.get();
84  
85              localCache.clear();
86          }
87      }
88  
89      public String getRegistryName() {
90          return CACHE_NAME;
91      }
92  
93      public Object getResult(
94          FinderPath finderPath, Object[] args, SessionFactory sessionFactory) {
95  
96          if (!PropsValues.VALUE_OBJECT_FINDER_CACHE_ENABLED ||
97              !finderPath.isFinderCacheEnabled() || !CacheRegistry.isActive()) {
98  
99              return null;
100         }
101 
102         Object primaryKey = null;
103 
104         Map<String, Object> localCache = null;
105 
106         String localCacheKey = null;
107 
108         if (_localCacheEnabled.get().booleanValue()) {
109             localCache = _localCache.get();
110 
111             localCacheKey = _encodeLocalCacheKey(
112                 finderPath.getClassName(), finderPath.getMethodName(),
113                 finderPath.getParams(), args);
114 
115             primaryKey = localCache.get(localCacheKey);
116         }
117 
118         if (primaryKey == null) {
119             PortalCache portalCache = _getPortalCache(
120                 finderPath.getClassName());
121 
122             String cacheKey = _encodeCacheKey(
123                 finderPath.getMethodName(), finderPath.getParams(), args);
124 
125             primaryKey = portalCache.get(cacheKey);
126 
127             if (primaryKey != null) {
128                 if (_localCacheEnabled.get().booleanValue()) {
129                     localCache.put(localCacheKey, primaryKey);
130                 }
131             }
132         }
133 
134         if (primaryKey != null) {
135             return _primaryKeyToResult(finderPath, sessionFactory, primaryKey);
136         }
137         else {
138             return null;
139         }
140     }
141 
142     public void invalidate() {
143         clearCache();
144     }
145 
146     public void putResult(FinderPath finderPath, Object[] args, Object result) {
147         if (!PropsValues.VALUE_OBJECT_FINDER_CACHE_ENABLED ||
148             !finderPath.isFinderCacheEnabled() || !CacheRegistry.isActive() ||
149             (result == null)) {
150 
151             return;
152         }
153 
154         Object primaryKey = _resultToPrimaryKey(result);
155 
156         if (_localCacheEnabled.get().booleanValue()) {
157             Map<String, Object> localCache = _localCache.get();
158 
159             String localCacheKey = _encodeLocalCacheKey(
160                 finderPath.getClassName(), finderPath.getMethodName(),
161                 finderPath.getParams(), args);
162 
163             localCache.put(localCacheKey, primaryKey);
164         }
165 
166         PortalCache portalCache = _getPortalCache(finderPath.getClassName());
167 
168         String cacheKey = _encodeCacheKey(
169             finderPath.getMethodName(), finderPath.getParams(), args);
170 
171         portalCache.put(cacheKey, primaryKey);
172     }
173 
174     public void removeResult(FinderPath finderPath, Object[] args) {
175         if (!PropsValues.VALUE_OBJECT_FINDER_CACHE_ENABLED ||
176             !finderPath.isFinderCacheEnabled() || !CacheRegistry.isActive()) {
177 
178             return;
179         }
180 
181         if (_localCacheEnabled.get().booleanValue()) {
182             Map<String, Object> localCache = _localCache.get();
183 
184             String localCacheKey = _encodeLocalCacheKey(
185                 finderPath.getClassName(), finderPath.getMethodName(),
186                 finderPath.getParams(), args);
187 
188             localCache.remove(localCacheKey);
189         }
190 
191         PortalCache portalCache = _getPortalCache(finderPath.getClassName());
192 
193         String cacheKey = _encodeCacheKey(
194             finderPath.getMethodName(), finderPath.getParams(), args);
195 
196         portalCache.remove(cacheKey);
197     }
198 
199     public void setLocalCacheEnabled(boolean localCacheEnabled) {
200         if (_localCacheAvailable) {
201             _localCacheEnabled.set(Boolean.valueOf(localCacheEnabled));
202         }
203     }
204 
205     public void setMultiVMPool(MultiVMPool multiVMPool) {
206         _multiVMPool = multiVMPool;
207     }
208 
209     private String _encodeCacheKey(
210         String methodName, String[] params, Object[] args) {
211 
212         StringBundler sb = new StringBundler(
213             (params.length + args.length) * 2 + 3);
214 
215         sb.append(methodName);
216         sb.append(_PARAMS_SEPARATOR);
217 
218         for (String param : params) {
219             sb.append(StringPool.PERIOD);
220             sb.append(param);
221         }
222 
223         sb.append(_ARGS_SEPARATOR);
224 
225         for (Object arg : args) {
226             sb.append(StringPool.PERIOD);
227             sb.append(String.valueOf(arg));
228         }
229 
230         return sb.toString();
231     }
232 
233     private String _encodeGroupKey(String className) {
234         return CACHE_NAME.concat(StringPool.PERIOD).concat(className);
235     }
236 
237     private String _encodeLocalCacheKey(
238         String className, String methodName, String[] params, Object[] args) {
239 
240         StringBundler sb = new StringBundler(
241             (params.length + args.length) * 2 + 5);
242 
243         sb.append(className);
244         sb.append(StringPool.PERIOD);
245         sb.append(methodName);
246         sb.append(_PARAMS_SEPARATOR);
247 
248         for (String param : params) {
249             sb.append(StringPool.PERIOD);
250             sb.append(param);
251         }
252 
253         sb.append(_ARGS_SEPARATOR);
254 
255         for (Object arg : args) {
256             sb.append(StringPool.PERIOD);
257             sb.append(String.valueOf(arg));
258         }
259 
260         return sb.toString();
261     }
262 
263     private PortalCache _getPortalCache(String className) {
264         String groupKey = _encodeGroupKey(className);
265 
266         PortalCache portalCache = _portalCaches.get(groupKey);
267 
268         if (portalCache == null) {
269             portalCache = _multiVMPool.getCache(
270                 groupKey, PropsValues.VALUE_OBJECT_FINDER_BLOCKING_CACHE);
271 
272             _portalCaches.put(groupKey, portalCache);
273         }
274 
275         return portalCache;
276     }
277 
278     private Object _primaryKeyToResult(
279         FinderPath finderPath, SessionFactory sessionFactory,
280         Object primaryKey) {
281 
282         if (primaryKey instanceof CacheKVP) {
283             CacheKVP cacheKVP = (CacheKVP)primaryKey;
284 
285             Class<?> modelClass = cacheKVP.getModelClass();
286             Serializable primaryKeyObj = cacheKVP.getPrimaryKeyObj();
287 
288             return EntityCacheUtil.loadResult(
289                 finderPath.isEntityCacheEnabled(), modelClass, primaryKeyObj,
290                 sessionFactory);
291         }
292         else if (primaryKey instanceof List<?>) {
293             List<Object> cachedList = (List<Object>)primaryKey;
294 
295             List<Object> list = new ArrayList<Object>(cachedList.size());
296 
297             for (Object curPrimaryKey : cachedList) {
298                 Object result = _primaryKeyToResult(
299                     finderPath, sessionFactory, curPrimaryKey);
300 
301                 list.add(result);
302             }
303 
304             return list;
305         }
306         else {
307             return primaryKey;
308         }
309     }
310 
311     private Object _resultToPrimaryKey(Object result) {
312         if (result instanceof BaseModel<?>) {
313             BaseModel<?> model = (BaseModel<?>)result;
314 
315             Class<?> modelClass = model.getClass();
316             Serializable primaryKeyObj = model.getPrimaryKeyObj();
317 
318             return new CacheKVP(modelClass, primaryKeyObj);
319         }
320         else if (result instanceof List<?>) {
321             List<Object> list = (List<Object>)result;
322 
323             List<Object> cachedList = new ArrayList<Object>(list.size());
324 
325             for (Object curResult : list) {
326                 Object primaryKey = _resultToPrimaryKey(curResult);
327 
328                 cachedList.add(primaryKey);
329             }
330 
331             return cachedList;
332         }
333         else {
334             return result;
335         }
336     }
337 
338     private static final String _ARGS_SEPARATOR = "_A_";
339 
340     private static final String _PARAMS_SEPARATOR = "_P_";
341 
342     private static ThreadLocal<LRUMap> _localCache;
343     private static boolean _localCacheAvailable;
344     private static ThreadLocal<Boolean> _localCacheEnabled =
345         new InitialThreadLocal<Boolean>(Boolean.FALSE);
346 
347     static {
348         if (PropsValues.VALUE_OBJECT_FINDER_THREAD_LOCAL_CACHE_MAX_SIZE > 0) {
349             _localCache = new InitialThreadLocal<LRUMap>(new LRUMap(
350                 PropsValues.VALUE_OBJECT_FINDER_THREAD_LOCAL_CACHE_MAX_SIZE));
351             _localCacheAvailable = true;
352         }
353     }
354 
355     private MultiVMPool _multiVMPool;
356     private Map<String, PortalCache> _portalCaches =
357         new ConcurrentHashMap<String, PortalCache>();
358 
359 }