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