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.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.cache.key.CacheKeyGenerator;
23  import com.liferay.portal.kernel.cache.key.CacheKeyGeneratorUtil;
24  import com.liferay.portal.kernel.dao.orm.EntityCache;
25  import com.liferay.portal.kernel.dao.orm.Session;
26  import com.liferay.portal.kernel.dao.orm.SessionFactory;
27  import com.liferay.portal.kernel.log.Log;
28  import com.liferay.portal.kernel.log.LogFactoryUtil;
29  import com.liferay.portal.kernel.util.AutoResetThreadLocal;
30  import com.liferay.portal.kernel.util.StringPool;
31  import com.liferay.portal.kernel.util.StringUtil;
32  import com.liferay.portal.model.BaseModel;
33  import com.liferay.portal.util.PropsValues;
34  
35  import java.io.Serializable;
36  
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="EntityCacheImpl.java.html"><b><i>View Source</i></b></a>
45   *
46   * @author Brian Wing Shun Chan
47   */
48  public class EntityCacheImpl implements CacheRegistryItem, EntityCache {
49  
50      public static final String CACHE_NAME = EntityCache.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          boolean entityCacheEnabled, Class<?> classObj,
86          Serializable primaryKeyObj, SessionFactory sessionFactory) {
87  
88          if (!PropsValues.VALUE_OBJECT_ENTITY_CACHE_ENABLED ||
89              !entityCacheEnabled || !CacheRegistry.isActive()) {
90  
91              return null;
92          }
93  
94          Object result = null;
95  
96          Map<String, Object> localCache = null;
97  
98          String localCacheKey = null;
99  
100         if (_localCacheAvailable) {
101             localCache = _localCache.get();
102 
103             localCacheKey = _encodeLocalCacheKey(classObj, primaryKeyObj);
104 
105             result = localCache.get(localCacheKey);
106         }
107 
108         if (result == null) {
109             PortalCache portalCache = _getPortalCache(classObj.getName(), true);
110 
111             String cacheKey = _encodeCacheKey(primaryKeyObj);
112 
113             result = portalCache.get(cacheKey);
114 
115             if (result == null) {
116                 result = StringPool.BLANK;
117 
118                 portalCache.put(cacheKey, result);
119             }
120 
121             if (_localCacheAvailable) {
122                 localCache.put(localCacheKey, result);
123             }
124         }
125 
126         if (result != null) {
127             result = _objectToResult(result);
128         }
129 
130         return result;
131     }
132 
133     public void invalidate() {
134         clearCache();
135     }
136 
137     public Object loadResult(
138         boolean entityCacheEnabled, Class<?> classObj,
139         Serializable primaryKeyObj, SessionFactory sessionFactory) {
140 
141         if (!PropsValues.VALUE_OBJECT_ENTITY_CACHE_ENABLED ||
142             !entityCacheEnabled || !CacheRegistry.isActive()) {
143 
144             Session session = null;
145 
146             try {
147                 session = sessionFactory.openSession();
148 
149                 return session.load(classObj, primaryKeyObj);
150             }
151             finally {
152                 sessionFactory.closeSession(session);
153             }
154         }
155 
156         Object result = null;
157 
158         Map<String, Object> localCache = null;
159 
160         String localCacheKey = null;
161 
162         if (_localCacheAvailable) {
163             localCache = _localCache.get();
164 
165             localCacheKey = _encodeLocalCacheKey(classObj, primaryKeyObj);
166 
167             result = localCache.get(localCacheKey);
168         }
169 
170         boolean load = false;
171 
172         if (result == null) {
173             PortalCache portalCache = _getPortalCache(classObj.getName(), true);
174 
175             String cacheKey = _encodeCacheKey(primaryKeyObj);
176 
177             result = portalCache.get(cacheKey);
178 
179             if (result == null) {
180                 if (_log.isDebugEnabled()) {
181                     _log.debug(
182                         "Load " + classObj + " " + primaryKeyObj +
183                             " from session");
184                 }
185 
186                 load = true;
187 
188                 Session session = null;
189 
190                 try {
191                     session = sessionFactory.openSession();
192 
193                     result = session.load(classObj, primaryKeyObj);
194                 }
195                 finally {
196                     if (result == null) {
197                         result = StringPool.BLANK;
198                     }
199                     else {
200                         result = _objectToResult(result);
201                     }
202 
203                     portalCache.put(cacheKey, result);
204 
205                     sessionFactory.closeSession(session);
206                 }
207             }
208 
209             if (_localCacheAvailable) {
210                 localCache.put(localCacheKey, result);
211             }
212         }
213 
214         if (!load) {
215             return _objectToResult(result);
216         }
217 
218         if (result instanceof String) {
219             return null;
220         }
221         else {
222             return result;
223         }
224     }
225 
226     public void putResult(
227         boolean entityCacheEnabled, Class<?> classObj,
228         Serializable primaryKeyObj, Object result) {
229 
230         if (!PropsValues.VALUE_OBJECT_ENTITY_CACHE_ENABLED ||
231             !entityCacheEnabled || !CacheRegistry.isActive() ||
232             (result == null)) {
233 
234             return;
235         }
236 
237         result = _objectToResult(result);
238 
239         if (_localCacheAvailable) {
240             Map<String, Object> localCache = _localCache.get();
241 
242             String localCacheKey = _encodeLocalCacheKey(
243                 classObj, primaryKeyObj);
244 
245             localCache.put(localCacheKey, result);
246         }
247 
248         PortalCache portalCache = _getPortalCache(classObj.getName(), true);
249 
250         String cacheKey = _encodeCacheKey(primaryKeyObj);
251 
252         portalCache.put(cacheKey, result);
253     }
254 
255     public void removeCache(String className) {
256         String groupKey = _GROUP_KEY_PREFIX.concat(className);
257 
258         _portalCaches.remove(groupKey);
259         _multiVMPool.removeCache(groupKey);
260     }
261 
262     public void removeResult(
263         boolean entityCacheEnabled, Class<?> classObj,
264         Serializable primaryKeyObj) {
265 
266         if (!PropsValues.VALUE_OBJECT_ENTITY_CACHE_ENABLED ||
267             !entityCacheEnabled || !CacheRegistry.isActive()) {
268 
269             return;
270         }
271 
272         if (_localCacheAvailable) {
273             Map<String, Object> localCache = _localCache.get();
274 
275             String localCacheKey = _encodeLocalCacheKey(
276                 classObj, primaryKeyObj);
277 
278             localCache.remove(localCacheKey);
279         }
280 
281         PortalCache portalCache = _getPortalCache(classObj.getName(), true);
282 
283         String cacheKey = _encodeCacheKey(primaryKeyObj);
284 
285         portalCache.remove(cacheKey);
286     }
287 
288     public void setMultiVMPool(MultiVMPool multiVMPool) {
289         _multiVMPool = multiVMPool;
290     }
291 
292     private String _encodeCacheKey(Serializable primaryKeyObj) {
293         CacheKeyGenerator cacheKeyGenerator =
294             CacheKeyGeneratorUtil.getCacheKeyGenerator(CACHE_NAME);
295 
296         return cacheKeyGenerator.getCacheKey(
297             StringUtil.toHexString(primaryKeyObj));
298     }
299 
300     private String _encodeLocalCacheKey(
301         Class<?> classObj, Serializable primaryKeyObj) {
302 
303         CacheKeyGenerator cacheKeyGenerator =
304             CacheKeyGeneratorUtil.getCacheKeyGenerator(CACHE_NAME);
305 
306         cacheKeyGenerator.append(classObj.getName());
307         cacheKeyGenerator.append(StringUtil.toHexString(primaryKeyObj));
308 
309         return cacheKeyGenerator.finish();
310     }
311 
312     private PortalCache _getPortalCache(
313         String className, boolean createIfAbsent) {
314 
315         String groupKey = _GROUP_KEY_PREFIX.concat(className);
316 
317         PortalCache portalCache = _portalCaches.get(groupKey);
318 
319         if ((portalCache == null) && createIfAbsent) {
320             portalCache = _multiVMPool.getCache(
321                 groupKey, PropsValues.VALUE_OBJECT_ENTITY_BLOCKING_CACHE);
322 
323             if (PropsValues.TRANSACTIONAL_CACHE_ENABLED) {
324                 portalCache = new TransactionalPortalCache(portalCache);
325             }
326 
327             PortalCache previousPortalCache = _portalCaches.putIfAbsent(
328                 groupKey, portalCache);
329 
330             if (previousPortalCache != null) {
331                 portalCache = previousPortalCache;
332             }
333 
334             portalCache.setDebug(true);
335         }
336 
337         return portalCache;
338     }
339 
340     private Object _objectToResult(Object result) {
341         if (result instanceof String) {
342             return null;
343         }
344         else {
345             result = ((BaseModel<?>)result).clone();
346 
347             BaseModel<?> model = (BaseModel<?>)result;
348 
349             model.setCachedModel(true);
350 
351             return model;
352         }
353     }
354 
355     private static Log _log = LogFactoryUtil.getLog(EntityCacheImpl.class);
356 
357     private static final String _GROUP_KEY_PREFIX = CACHE_NAME.concat(
358         StringPool.PERIOD);
359 
360     private static ThreadLocal<LRUMap> _localCache;
361     private static boolean _localCacheAvailable;
362 
363     static {
364         if (PropsValues.VALUE_OBJECT_ENTITY_THREAD_LOCAL_CACHE_MAX_SIZE > 0) {
365             _localCache = new AutoResetThreadLocal<LRUMap>(new LRUMap(
366                 PropsValues.VALUE_OBJECT_ENTITY_THREAD_LOCAL_CACHE_MAX_SIZE));
367             _localCacheAvailable = true;
368         }
369     }
370 
371     private MultiVMPool _multiVMPool;
372     private ConcurrentMap<String, PortalCache> _portalCaches =
373         new ConcurrentHashMap<String, PortalCache>();
374 
375 }