1   /**
2    * Copyright (c) 2000-2010 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   *
12   *
13   */
14  
15  package com.liferay.portal.dao.orm.common;
16  
17  import com.liferay.portal.cache.transactional.TransactionalPortalCache;
18  import com.liferay.portal.kernel.cache.CacheKVP;
19  import com.liferay.portal.kernel.cache.CacheRegistry;
20  import com.liferay.portal.kernel.cache.CacheRegistryItem;
21  import com.liferay.portal.kernel.cache.MultiVMPool;
22  import com.liferay.portal.kernel.cache.PortalCache;
23  import com.liferay.portal.kernel.dao.orm.EntityCacheUtil;
24  import com.liferay.portal.kernel.dao.orm.FinderCache;
25  import com.liferay.portal.kernel.dao.orm.FinderPath;
26  import com.liferay.portal.kernel.dao.orm.SessionFactory;
27  import com.liferay.portal.kernel.util.AutoResetThreadLocal;
28  import com.liferay.portal.kernel.util.StringPool;
29  import com.liferay.portal.model.BaseModel;
30  import com.liferay.portal.util.PropsValues;
31  
32  import java.io.Serializable;
33  
34  import java.util.ArrayList;
35  import java.util.Collections;
36  import java.util.List;
37  import java.util.Map;
38  import java.util.concurrent.ConcurrentHashMap;
39  import java.util.concurrent.ConcurrentMap;
40  
41  import org.apache.commons.collections.map.LRUMap;
42  
43  /**
44   * <a href="FinderCacheImpl.java.html"><b><i>View Source</i></b></a>
45   *
46   * @author Brian Wing Shun Chan
47   */
48  public class FinderCacheImpl implements CacheRegistryItem, FinderCache {
49  
50      public static final String CACHE_NAME = FinderCache.class.getName();
51  
52      public void afterPropertiesSet() {
53          CacheRegistry.register(this);
54      }
55  
56      public void clearCache() {
57          clearLocalCache();
58  
59          for (PortalCache portalCache : _portalCaches.values()) {
60              portalCache.removeAll();
61          }
62      }
63  
64      public void clearCache(String className) {
65          clearLocalCache();
66  
67          PortalCache portalCache = _getPortalCache(className, false);
68  
69          if (portalCache != null) {
70              portalCache.removeAll();
71          }
72      }
73  
74      public void clearLocalCache() {
75          if (_localCacheAvailable) {
76              _localCache.remove();
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(), true);
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(
153             finderPath.getClassName(), true);
154 
155         String cacheKey = finderPath.encodeCacheKey(args);
156 
157         portalCache.put(cacheKey, primaryKey);
158     }
159 
160     public void removeCache(String className) {
161         String groupKey = _GROUP_KEY_PREFIX.concat(className);
162 
163         _portalCaches.remove(groupKey);
164         _multiVMPool.removeCache(groupKey);
165     }
166 
167     public void removeResult(FinderPath finderPath, Object[] args) {
168         if (!PropsValues.VALUE_OBJECT_FINDER_CACHE_ENABLED ||
169             !finderPath.isFinderCacheEnabled() || !CacheRegistry.isActive()) {
170 
171             return;
172         }
173 
174         if (_localCacheAvailable) {
175             Map<String, Object> localCache = _localCache.get();
176 
177             String localCacheKey = finderPath.encodeLocalCacheKey(args);
178 
179             localCache.remove(localCacheKey);
180         }
181 
182         PortalCache portalCache = _getPortalCache(
183             finderPath.getClassName(), true);
184 
185         String cacheKey = finderPath.encodeCacheKey(args);
186 
187         portalCache.remove(cacheKey);
188     }
189 
190     public void setMultiVMPool(MultiVMPool multiVMPool) {
191         _multiVMPool = multiVMPool;
192     }
193 
194     private PortalCache _getPortalCache(
195         String className, boolean createIfAbsent) {
196 
197         String groupKey = _GROUP_KEY_PREFIX.concat(className);
198 
199         PortalCache portalCache = _portalCaches.get(groupKey);
200 
201         if ((portalCache == null) && createIfAbsent) {
202             portalCache = _multiVMPool.getCache(
203                 groupKey, PropsValues.VALUE_OBJECT_FINDER_BLOCKING_CACHE);
204 
205             if (PropsValues.TRANSACTIONAL_CACHE_ENABLED) {
206                 portalCache = new TransactionalPortalCache(portalCache);
207             }
208 
209             PortalCache previousPortalCache = _portalCaches.putIfAbsent(
210                 groupKey, portalCache);
211 
212             if (previousPortalCache != null) {
213                 portalCache = previousPortalCache;
214             }
215 
216             portalCache.setDebug(true);
217         }
218 
219         return portalCache;
220     }
221 
222     private Object _primaryKeyToResult(
223         FinderPath finderPath, SessionFactory sessionFactory,
224         Object primaryKey) {
225 
226         if (primaryKey instanceof CacheKVP) {
227             CacheKVP cacheKVP = (CacheKVP)primaryKey;
228 
229             Class<?> modelClass = cacheKVP.getModelClass();
230             Serializable primaryKeyObj = cacheKVP.getPrimaryKeyObj();
231 
232             return EntityCacheUtil.loadResult(
233                 finderPath.isEntityCacheEnabled(), modelClass, primaryKeyObj,
234                 sessionFactory);
235         }
236         else if (primaryKey instanceof List<?>) {
237             List<Object> cachedList = (List<Object>)primaryKey;
238 
239             if (cachedList.isEmpty()) {
240                 return Collections.EMPTY_LIST;
241             }
242 
243             List<Object> list = new ArrayList<Object>(cachedList.size());
244 
245             for (Object curPrimaryKey : cachedList) {
246                 Object result = _primaryKeyToResult(
247                     finderPath, sessionFactory, curPrimaryKey);
248 
249                 list.add(result);
250             }
251 
252             return list;
253         }
254         else {
255             return primaryKey;
256         }
257     }
258 
259     private Object _resultToPrimaryKey(Object result) {
260         if (result instanceof BaseModel<?>) {
261             BaseModel<?> model = (BaseModel<?>)result;
262 
263             Class<?> modelClass = model.getClass();
264             Serializable primaryKeyObj = model.getPrimaryKeyObj();
265 
266             return new CacheKVP(modelClass, primaryKeyObj);
267         }
268         else if (result instanceof List<?>) {
269             List<Object> list = (List<Object>)result;
270 
271             if (list.isEmpty()) {
272                 return Collections.EMPTY_LIST;
273             }
274 
275             List<Object> cachedList = new ArrayList<Object>(list.size());
276 
277             for (Object curResult : list) {
278                 Object primaryKey = _resultToPrimaryKey(curResult);
279 
280                 cachedList.add(primaryKey);
281             }
282 
283             return cachedList;
284         }
285         else {
286             return result;
287         }
288     }
289 
290     private static final String _GROUP_KEY_PREFIX = CACHE_NAME.concat(
291         StringPool.PERIOD);
292 
293     private static ThreadLocal<LRUMap> _localCache;
294     private static boolean _localCacheAvailable;
295 
296     static {
297         if (PropsValues.VALUE_OBJECT_FINDER_THREAD_LOCAL_CACHE_MAX_SIZE > 0) {
298             _localCache = new AutoResetThreadLocal<LRUMap>(new LRUMap(
299                 PropsValues.VALUE_OBJECT_FINDER_THREAD_LOCAL_CACHE_MAX_SIZE));
300             _localCacheAvailable = true;
301         }
302     }
303 
304     private MultiVMPool _multiVMPool;
305     private ConcurrentMap<String, PortalCache> _portalCaches =
306         new ConcurrentHashMap<String, PortalCache>();
307 
308 }