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  import java.util.concurrent.ConcurrentMap;
38  
39  import org.apache.commons.collections.map.LRUMap;
40  
41  /**
42   * <a href="FinderCacheImpl.java.html"><b><i>View Source</i></b></a>
43   *
44   * @author Brian Wing Shun Chan
45   */
46  public class FinderCacheImpl implements CacheRegistryItem, FinderCache {
47  
48      public static final String CACHE_NAME = FinderCache.class.getName();
49  
50      public void afterPropertiesSet() {
51          CacheRegistry.register(this);
52      }
53  
54      public void clearCache() {
55          clearLocalCache();
56  
57          for (PortalCache portalCache : _portalCaches.values()) {
58              portalCache.removeAll();
59          }
60      }
61  
62      public void clearCache(String className) {
63          clearLocalCache();
64  
65          PortalCache portalCache = _getPortalCache(className, false);
66  
67          if (portalCache != null) {
68              portalCache.removeAll();
69          }
70      }
71  
72      public void clearLocalCache() {
73          if (_localCacheAvailable) {
74              _localCache.remove();
75          }
76      }
77  
78      public String getRegistryName() {
79          return CACHE_NAME;
80      }
81  
82      public Object getResult(
83          FinderPath finderPath, Object[] args, SessionFactory sessionFactory) {
84  
85          if (!PropsValues.VALUE_OBJECT_FINDER_CACHE_ENABLED ||
86              !finderPath.isFinderCacheEnabled() || !CacheRegistry.isActive()) {
87  
88              return null;
89          }
90  
91          Object primaryKey = null;
92  
93          Map<String, Object> localCache = null;
94  
95          String localCacheKey = null;
96  
97          if (_localCacheAvailable) {
98              localCache = _localCache.get();
99  
100             localCacheKey = finderPath.encodeLocalCacheKey(args);
101 
102             primaryKey = localCache.get(localCacheKey);
103         }
104 
105         if (primaryKey == null) {
106             PortalCache portalCache = _getPortalCache(
107                 finderPath.getClassName(), true);
108 
109             String cacheKey = finderPath.encodeCacheKey(args);
110 
111             primaryKey = portalCache.get(cacheKey);
112 
113             if (primaryKey != null) {
114                 if (_localCacheAvailable) {
115                     localCache.put(localCacheKey, primaryKey);
116                 }
117             }
118         }
119 
120         if (primaryKey != null) {
121             return _primaryKeyToResult(finderPath, sessionFactory, primaryKey);
122         }
123         else {
124             return null;
125         }
126     }
127 
128     public void invalidate() {
129         clearCache();
130     }
131 
132     public void putResult(FinderPath finderPath, Object[] args, Object result) {
133         if (!PropsValues.VALUE_OBJECT_FINDER_CACHE_ENABLED ||
134             !finderPath.isFinderCacheEnabled() || !CacheRegistry.isActive() ||
135             (result == null)) {
136 
137             return;
138         }
139 
140         Object primaryKey = _resultToPrimaryKey(result);
141 
142         if (_localCacheAvailable) {
143             Map<String, Object> localCache = _localCache.get();
144 
145             String localCacheKey = finderPath.encodeLocalCacheKey(args);
146 
147             localCache.put(localCacheKey, primaryKey);
148         }
149 
150         PortalCache portalCache = _getPortalCache(
151             finderPath.getClassName(), true);
152 
153         String cacheKey = finderPath.encodeCacheKey(args);
154 
155         portalCache.put(cacheKey, primaryKey);
156     }
157 
158     public void removeResult(FinderPath finderPath, Object[] args) {
159         if (!PropsValues.VALUE_OBJECT_FINDER_CACHE_ENABLED ||
160             !finderPath.isFinderCacheEnabled() || !CacheRegistry.isActive()) {
161 
162             return;
163         }
164 
165         if (_localCacheAvailable) {
166             Map<String, Object> localCache = _localCache.get();
167 
168             String localCacheKey = finderPath.encodeLocalCacheKey(args);
169 
170             localCache.remove(localCacheKey);
171         }
172 
173         PortalCache portalCache = _getPortalCache(
174             finderPath.getClassName(), true);
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(
186             String className, boolean createIfAbsent) {
187         String groupKey = _GROUP_KEY_PREFIX.concat(className);
188 
189         PortalCache portalCache = _portalCaches.get(groupKey);
190 
191         if ((portalCache == null) && createIfAbsent) {
192             portalCache = _multiVMPool.getCache(
193                 groupKey, PropsValues.VALUE_OBJECT_FINDER_BLOCKING_CACHE);
194 
195             PortalCache previousPortalCache = _portalCaches.putIfAbsent(
196                 groupKey, portalCache);
197 
198             if (previousPortalCache != null) {
199                 portalCache = previousPortalCache;
200             }
201 
202             portalCache.setDebug(true);
203         }
204 
205         return portalCache;
206     }
207 
208     private Object _primaryKeyToResult(
209         FinderPath finderPath, SessionFactory sessionFactory,
210         Object primaryKey) {
211 
212         if (primaryKey instanceof CacheKVP) {
213             CacheKVP cacheKVP = (CacheKVP)primaryKey;
214 
215             Class<?> modelClass = cacheKVP.getModelClass();
216             Serializable primaryKeyObj = cacheKVP.getPrimaryKeyObj();
217 
218             return EntityCacheUtil.loadResult(
219                 finderPath.isEntityCacheEnabled(), modelClass, primaryKeyObj,
220                 sessionFactory);
221         }
222         else if (primaryKey instanceof List<?>) {
223             List<Object> cachedList = (List<Object>)primaryKey;
224 
225             List<Object> list = new ArrayList<Object>(cachedList.size());
226 
227             for (Object curPrimaryKey : cachedList) {
228                 Object result = _primaryKeyToResult(
229                     finderPath, sessionFactory, curPrimaryKey);
230 
231                 list.add(result);
232             }
233 
234             return list;
235         }
236         else {
237             return primaryKey;
238         }
239     }
240 
241     private Object _resultToPrimaryKey(Object result) {
242         if (result instanceof BaseModel<?>) {
243             BaseModel<?> model = (BaseModel<?>)result;
244 
245             Class<?> modelClass = model.getClass();
246             Serializable primaryKeyObj = model.getPrimaryKeyObj();
247 
248             return new CacheKVP(modelClass, primaryKeyObj);
249         }
250         else if (result instanceof List<?>) {
251             List<Object> list = (List<Object>)result;
252 
253             List<Object> cachedList = new ArrayList<Object>(list.size());
254 
255             for (Object curResult : list) {
256                 Object primaryKey = _resultToPrimaryKey(curResult);
257 
258                 cachedList.add(primaryKey);
259             }
260 
261             return cachedList;
262         }
263         else {
264             return result;
265         }
266     }
267 
268     private static final String _GROUP_KEY_PREFIX = CACHE_NAME.concat(
269         StringPool.PERIOD);
270 
271     private static ThreadLocal<LRUMap> _localCache;
272     private static boolean _localCacheAvailable;
273 
274     static {
275         if (PropsValues.VALUE_OBJECT_FINDER_THREAD_LOCAL_CACHE_MAX_SIZE > 0) {
276             _localCache = new AutoResetThreadLocal<LRUMap>(
277                 FinderCacheImpl.class + "._localCache",
278                 new LRUMap(
279                     PropsValues.
280                         VALUE_OBJECT_FINDER_THREAD_LOCAL_CACHE_MAX_SIZE));
281             _localCacheAvailable = true;
282         }
283     }
284 
285     private MultiVMPool _multiVMPool;
286     private ConcurrentMap<String, PortalCache> _portalCaches =
287         new ConcurrentHashMap<String, PortalCache>();
288 
289 }