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.StringPool;
36  import com.liferay.portal.model.BaseModel;
37  import com.liferay.portal.util.PropsValues;
38  
39  import java.io.Serializable;
40  
41  import java.util.ArrayList;
42  import java.util.List;
43  import java.util.Map;
44  import java.util.concurrent.ConcurrentHashMap;
45  
46  import org.apache.commons.collections.map.LRUMap;
47  
48  /**
49   * <a href="FinderCacheImpl.java.html"><b><i>View Source</i></b></a>
50   *
51   * @author Brian Wing Shun Chan
52   */
53  public class FinderCacheImpl implements CacheRegistryItem, FinderCache {
54  
55      public static final String CACHE_NAME = FinderCache.class.getName();
56  
57      public void afterPropertiesSet() {
58          CacheRegistry.register(this);
59      }
60  
61      public void clearCache() {
62          clearLocalCache();
63  
64          PortalCache[] portalCaches = _portalCaches.values().toArray(
65              new PortalCache[_portalCaches.size()]);
66  
67          for (PortalCache portalCache : portalCaches) {
68              portalCache.removeAll();
69          }
70      }
71  
72      public void clearCache(String className) {
73          clearLocalCache();
74  
75          PortalCache portalCache = _getPortalCache(className);
76  
77          portalCache.removeAll();
78      }
79  
80      public void clearLocalCache() {
81          if (_localCacheEnabled.get().booleanValue()) {
82              Map<String, Object> localCache = _localCache.get();
83  
84              localCache.clear();
85          }
86      }
87  
88      public String getRegistryName() {
89          return CACHE_NAME;
90      }
91  
92      public Object getResult(
93          FinderPath finderPath, Object[] args, SessionFactory sessionFactory) {
94  
95          if (!PropsValues.VALUE_OBJECT_FINDER_CACHE_ENABLED ||
96              !finderPath.isFinderCacheEnabled() || !CacheRegistry.isActive()) {
97  
98              return null;
99          }
100 
101         Object primaryKey = null;
102 
103         Map<String, Object> localCache = null;
104 
105         String localCacheKey = null;
106 
107         if (_localCacheEnabled.get().booleanValue()) {
108             localCache = _localCache.get();
109 
110             localCacheKey = _encodeLocalCacheKey(
111                 finderPath.getClassName(), finderPath.getMethodName(),
112                 finderPath.getParams(), args);
113 
114             primaryKey = localCache.get(localCacheKey);
115         }
116 
117         if (primaryKey == null) {
118             PortalCache portalCache = _getPortalCache(
119                 finderPath.getClassName());
120 
121             String cacheKey = _encodeCacheKey(
122                 finderPath.getMethodName(), finderPath.getParams(), args);
123 
124             primaryKey = _multiVMPool.get(portalCache, cacheKey);
125 
126             if (primaryKey != null) {
127                 if (_localCacheEnabled.get().booleanValue()) {
128                     localCache.put(localCacheKey, primaryKey);
129                 }
130             }
131         }
132 
133         if (primaryKey != null) {
134             return _primaryKeyToResult(finderPath, sessionFactory, primaryKey);
135         }
136         else {
137             return null;
138         }
139     }
140 
141     public void invalidate() {
142         clearCache();
143     }
144 
145     public void putResult(FinderPath finderPath, Object[] args, Object result) {
146         if (!PropsValues.VALUE_OBJECT_FINDER_CACHE_ENABLED ||
147             !finderPath.isFinderCacheEnabled() || !CacheRegistry.isActive() ||
148             (result == null)) {
149 
150             return;
151         }
152 
153         Object primaryKey = _resultToPrimaryKey(result);
154 
155         if (_localCacheEnabled.get().booleanValue()) {
156             Map<String, Object> localCache = _localCache.get();
157 
158             String localCacheKey = _encodeLocalCacheKey(
159                 finderPath.getClassName(), finderPath.getMethodName(),
160                 finderPath.getParams(), args);
161 
162             localCache.put(localCacheKey, primaryKey);
163         }
164 
165         PortalCache portalCache = _getPortalCache(finderPath.getClassName());
166 
167         String cacheKey = _encodeCacheKey(
168             finderPath.getMethodName(), finderPath.getParams(), args);
169 
170         _multiVMPool.put(portalCache, cacheKey, primaryKey);
171     }
172 
173     public void removeResult(FinderPath finderPath, Object[] args) {
174         if (!PropsValues.VALUE_OBJECT_FINDER_CACHE_ENABLED ||
175             !finderPath.isFinderCacheEnabled() || !CacheRegistry.isActive()) {
176 
177             return;
178         }
179 
180         if (_localCacheEnabled.get().booleanValue()) {
181             Map<String, Object> localCache = _localCache.get();
182 
183             String localCacheKey = _encodeLocalCacheKey(
184                 finderPath.getClassName(), finderPath.getMethodName(),
185                 finderPath.getParams(), args);
186 
187             localCache.remove(localCacheKey);
188         }
189 
190         PortalCache portalCache = _getPortalCache(finderPath.getClassName());
191 
192         String cacheKey = _encodeCacheKey(
193             finderPath.getMethodName(), finderPath.getParams(), args);
194 
195         _multiVMPool.remove(portalCache, cacheKey);
196     }
197 
198     public void setLocalCacheEnabled(boolean localCacheEnabled) {
199         if (_localCacheAvailable) {
200             _localCacheEnabled.set(Boolean.valueOf(localCacheEnabled));
201         }
202     }
203 
204     public void setMultiVMPool(MultiVMPool multiVMPool) {
205         _multiVMPool = multiVMPool;
206     }
207 
208     private String _encodeCacheKey(
209         String methodName, String[] params, Object[] args) {
210 
211         StringBuilder sb = new StringBuilder();
212 
213         sb.append(methodName);
214         sb.append(_PARAMS_SEPARATOR);
215 
216         for (String param : params) {
217             sb.append(StringPool.PERIOD);
218             sb.append(param);
219         }
220 
221         sb.append(_ARGS_SEPARATOR);
222 
223         for (Object arg : args) {
224             sb.append(StringPool.PERIOD);
225             sb.append(String.valueOf(arg));
226         }
227 
228         return sb.toString();
229     }
230 
231     private String _encodeGroupKey(String className) {
232         StringBuilder sb = new StringBuilder();
233 
234         sb.append(CACHE_NAME);
235         sb.append(StringPool.PERIOD);
236         sb.append(className);
237 
238         return sb.toString();
239     }
240 
241     private String _encodeLocalCacheKey(
242         String className, String methodName, String[] params, Object[] args) {
243 
244         StringBuilder sb = new StringBuilder();
245 
246         sb.append(className);
247         sb.append(StringPool.PERIOD);
248         sb.append(methodName);
249         sb.append(_PARAMS_SEPARATOR);
250 
251         for (String param : params) {
252             sb.append(StringPool.PERIOD);
253             sb.append(param);
254         }
255 
256         sb.append(_ARGS_SEPARATOR);
257 
258         for (Object arg : args) {
259             sb.append(StringPool.PERIOD);
260             sb.append(String.valueOf(arg));
261         }
262 
263         return sb.toString();
264     }
265 
266     private PortalCache _getPortalCache(String className) {
267         String groupKey = _encodeGroupKey(className);
268 
269         PortalCache portalCache = _portalCaches.get(groupKey);
270 
271         if (portalCache == null) {
272             portalCache = _multiVMPool.getCache(
273                 groupKey, PropsValues.VALUE_OBJECT_FINDER_BLOCKING_CACHE);
274 
275             _portalCaches.put(groupKey, portalCache);
276         }
277 
278         return portalCache;
279     }
280 
281     private Object _primaryKeyToResult(
282         FinderPath finderPath, SessionFactory sessionFactory,
283         Object primaryKey) {
284 
285         if (primaryKey instanceof CacheKVP) {
286             CacheKVP cacheKVP = (CacheKVP)primaryKey;
287 
288             Class<?> modelClass = cacheKVP.getModelClass();
289             Serializable primaryKeyObj = cacheKVP.getPrimaryKeyObj();
290 
291             return EntityCacheUtil.loadResult(
292                 finderPath.isEntityCacheEnabled(), modelClass, primaryKeyObj,
293                 sessionFactory);
294         }
295         else if (primaryKey instanceof List) {
296             List<Object> cachedList = (List<Object>)primaryKey;
297 
298             List<Object> list = new ArrayList<Object>(cachedList.size());
299 
300             for (Object curPrimaryKey : cachedList) {
301                 Object result = _primaryKeyToResult(
302                     finderPath, sessionFactory, curPrimaryKey);
303 
304                 list.add(result);
305             }
306 
307             return list;
308         }
309         else {
310             return primaryKey;
311         }
312     }
313 
314     private Object _resultToPrimaryKey(Object result) {
315         if (result instanceof BaseModel) {
316             BaseModel<?> model = (BaseModel<?>)result;
317 
318             Class<?> modelClass = model.getClass();
319             Serializable primaryKeyObj = model.getPrimaryKeyObj();
320 
321             return new CacheKVP(modelClass, primaryKeyObj);
322         }
323         else if (result instanceof List) {
324             List<Object> list = (List<Object>)result;
325 
326             List<Object> cachedList = new ArrayList<Object>(list.size());
327 
328             for (Object curResult : list) {
329                 Object primaryKey = _resultToPrimaryKey(curResult);
330 
331                 cachedList.add(primaryKey);
332             }
333 
334             return cachedList;
335         }
336         else {
337             return result;
338         }
339     }
340 
341     private static final String _ARGS_SEPARATOR = "_A_";
342 
343     private static final String _PARAMS_SEPARATOR = "_P_";
344 
345     private static ThreadLocal<Map> _localCache;
346     private static boolean _localCacheAvailable;
347     private static ThreadLocal<Boolean> _localCacheEnabled =
348         new InitialThreadLocal<Boolean>(Boolean.FALSE);
349 
350     static {
351         if (PropsValues.VALUE_OBJECT_FINDER_THREAD_LOCAL_CACHE_MAX_SIZE > 0) {
352             _localCache = new InitialThreadLocal<Map>(new LRUMap(
353                 PropsValues.VALUE_OBJECT_FINDER_THREAD_LOCAL_CACHE_MAX_SIZE));
354             _localCacheAvailable = true;
355         }
356     }
357 
358     private MultiVMPool _multiVMPool;
359     private Map<String, PortalCache> _portalCaches =
360         new ConcurrentHashMap<String, PortalCache>();
361 
362 }