1   /**
2    * Copyright (c) 2000-2009 Liferay, Inc. All rights reserved.
3    *
4    * Permission is hereby granted, free of charge, to any person obtaining a copy
5    * of this software and associated documentation files (the "Software"), to deal
6    * in the Software without restriction, including without limitation the rights
7    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    * copies of the Software, and to permit persons to whom the Software is
9    * furnished to do so, subject to the following conditions:
10   *
11   * The above copyright notice and this permission notice shall be included in
12   * all copies or substantial portions 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.hibernate;
24  
25  import com.liferay.portal.kernel.cache.CacheRegistry;
26  import com.liferay.portal.kernel.cache.CacheRegistryItem;
27  import com.liferay.portal.kernel.cache.MultiVMPool;
28  import com.liferay.portal.kernel.cache.PortalCache;
29  import com.liferay.portal.kernel.dao.orm.EntityCache;
30  import com.liferay.portal.kernel.dao.orm.Session;
31  import com.liferay.portal.kernel.dao.orm.SessionFactory;
32  import com.liferay.portal.kernel.log.Log;
33  import com.liferay.portal.kernel.log.LogFactoryUtil;
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.Map;
42  import java.util.concurrent.ConcurrentHashMap;
43  
44  import org.apache.commons.collections.map.LRUMap;
45  
46  /**
47   * <a href="EntityCacheImpl.java.html"><b><i>View Source</i></b></a>
48   *
49   * @author Brian Wing Shun Chan
50   *
51   */
52  public class EntityCacheImpl implements CacheRegistryItem, EntityCache {
53  
54      public static final String CACHE_NAME = EntityCache.class.getName();
55  
56      public void afterPropertiesSet() {
57          CacheRegistry.register(this);
58      }
59  
60      public void clearCache() {
61          clearLocalCache();
62  
63          PortalCache[] portalCaches = _portalCaches.values().toArray(
64              new PortalCache[_portalCaches.size()]);
65  
66          for (PortalCache portalCache : portalCaches) {
67              portalCache.removeAll();
68          }
69      }
70  
71      public void clearCache(String className) {
72          clearLocalCache();
73  
74          PortalCache portalCache = _getPortalCache(className);
75  
76          portalCache.removeAll();
77      }
78  
79      public void clearLocalCache() {
80          if (_localCacheEnabled.get().booleanValue()) {
81              Map<String, Object> localCache = _localCache.get();
82  
83              localCache.clear();
84          }
85      }
86  
87      public String getRegistryName() {
88          return CACHE_NAME;
89      }
90  
91      public Object getResult(
92          boolean entityCacheEnabled, Class<?> classObj,
93          Serializable primaryKeyObj, SessionFactory sessionFactory) {
94  
95          if (!PropsValues.VALUE_OBJECT_ENTITY_CACHE_ENABLED ||
96              !entityCacheEnabled || !CacheRegistry.isActive()) {
97  
98              return null;
99          }
100 
101         Object result = 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(classObj, primaryKeyObj);
111 
112             result = localCache.get(localCacheKey);
113         }
114 
115         if (result == null) {
116             PortalCache portalCache = _getPortalCache(classObj.getName());
117 
118             String cacheKey = _encodeCacheKey(primaryKeyObj);
119 
120             result = _multiVMPool.get(portalCache, cacheKey);
121 
122             if (result == null) {
123                 result = StringPool.BLANK;
124 
125                 _multiVMPool.put(portalCache, cacheKey, result);
126             }
127 
128             if (_localCacheEnabled.get().booleanValue()) {
129                 localCache.put(localCacheKey, result);
130             }
131         }
132 
133         if (result != null) {
134             result = _objectToResult(result);
135         }
136 
137         return result;
138     }
139 
140     public void invalidate() {
141         clearCache();
142     }
143 
144     public Object loadResult(
145         boolean entityCacheEnabled, Class<?> classObj,
146         Serializable primaryKeyObj, SessionFactory sessionFactory) {
147 
148         if (!PropsValues.VALUE_OBJECT_ENTITY_CACHE_ENABLED ||
149             !entityCacheEnabled || !CacheRegistry.isActive()) {
150 
151             Session session = null;
152 
153             try {
154                 session = sessionFactory.openSession();
155 
156                 return session.load(classObj, primaryKeyObj);
157             }
158             finally {
159                 sessionFactory.closeSession(session);
160             }
161         }
162 
163         Object result = null;
164 
165         Map<String, Object> localCache = null;
166 
167         String localCacheKey = null;
168 
169         if (_localCacheEnabled.get().booleanValue()) {
170             localCache = _localCache.get();
171 
172             localCacheKey = _encodeLocalCacheKey(classObj, primaryKeyObj);
173 
174             result = localCache.get(localCacheKey);
175         }
176 
177         if (result == null) {
178             PortalCache portalCache = _getPortalCache(classObj.getName());
179 
180             String cacheKey = _encodeCacheKey(primaryKeyObj);
181 
182             result = _multiVMPool.get(portalCache, cacheKey);
183 
184             if (result == null) {
185                 if (_log.isDebugEnabled()) {
186                     _log.debug(
187                         "Load " + classObj + " " + primaryKeyObj +
188                             " from session");
189                 }
190 
191                 Session session = null;
192 
193                 try {
194                     session = sessionFactory.openSession();
195 
196                     result = session.load(classObj, primaryKeyObj);
197                 }
198                 finally {
199                     if (result == null) {
200                         result = StringPool.BLANK;
201                     }
202 
203                     result = _objectToResult(result);
204 
205                     _multiVMPool.put(portalCache, cacheKey, result);
206 
207                     sessionFactory.closeSession(session);
208                 }
209             }
210 
211             if (_localCacheEnabled.get().booleanValue()) {
212                 localCache.put(localCacheKey, result);
213             }
214         }
215 
216         result = _objectToResult(result);
217 
218         return result;
219     }
220 
221     public void putResult(
222         boolean entityCacheEnabled, Class<?> classObj,
223         Serializable primaryKeyObj, Object result) {
224 
225         if (!PropsValues.VALUE_OBJECT_ENTITY_CACHE_ENABLED ||
226             !entityCacheEnabled || !CacheRegistry.isActive() ||
227             (result == null)) {
228 
229             return;
230         }
231 
232         result = _objectToResult(result);
233 
234         if (_localCacheEnabled.get().booleanValue()) {
235             Map<String, Object> localCache = _localCache.get();
236 
237             String localCacheKey = _encodeLocalCacheKey(
238                 classObj, primaryKeyObj);
239 
240             localCache.put(localCacheKey, result);
241         }
242 
243         PortalCache portalCache = _getPortalCache(classObj.getName());
244 
245         String cacheKey = _encodeCacheKey(primaryKeyObj);
246 
247         _multiVMPool.put(portalCache, cacheKey, result);
248     }
249 
250     public void removeResult(
251         boolean entityCacheEnabled, Class<?> classObj,
252         Serializable primaryKeyObj) {
253 
254         if (!PropsValues.VALUE_OBJECT_ENTITY_CACHE_ENABLED ||
255             !entityCacheEnabled || !CacheRegistry.isActive()) {
256 
257             return;
258         }
259 
260         if (_localCacheEnabled.get().booleanValue()) {
261             Map<String, Object> localCache = _localCache.get();
262 
263             String localCacheKey = _encodeLocalCacheKey(
264                 classObj, primaryKeyObj);
265 
266             localCache.remove(localCacheKey);
267         }
268 
269         PortalCache portalCache = _getPortalCache(classObj.getName());
270 
271         String cacheKey = _encodeCacheKey(primaryKeyObj);
272 
273         _multiVMPool.remove(portalCache, cacheKey);
274     }
275 
276     public void setLocalCacheEnabled(boolean localCacheEnabled) {
277         if (_localCacheAvailable) {
278             _localCacheEnabled.set(Boolean.valueOf(localCacheEnabled));
279         }
280     }
281 
282     public void setMultiVMPool(MultiVMPool multiVMPool) {
283         _multiVMPool = multiVMPool;
284     }
285 
286     private String _encodeCacheKey(Serializable primaryKeyObj) {
287         return String.valueOf(primaryKeyObj);
288     }
289 
290     private String _encodeGroupKey(String className) {
291         StringBuilder sb = new StringBuilder();
292 
293         sb.append(CACHE_NAME);
294         sb.append(StringPool.PERIOD);
295         sb.append(className);
296 
297         return sb.toString();
298     }
299 
300     private String _encodeLocalCacheKey(
301         Class<?> classObj, Serializable primaryKeyObj) {
302 
303         StringBuilder sb = new StringBuilder();
304 
305         sb.append(classObj.getName());
306         sb.append(StringPool.PERIOD);
307         sb.append(primaryKeyObj);
308 
309         return sb.toString();
310     }
311 
312     private PortalCache _getPortalCache(String className) {
313         String groupKey = _encodeGroupKey(className);
314 
315         PortalCache portalCache = _portalCaches.get(groupKey);
316 
317         if (portalCache == null) {
318             portalCache = _multiVMPool.getCache(groupKey, true);
319 
320             _portalCaches.put(groupKey, portalCache);
321         }
322 
323         return portalCache;
324     }
325 
326     private Object _objectToResult(Object result) {
327         if (result instanceof String) {
328             return null;
329         }
330         else {
331             result = ((BaseModel<?>)result).clone();
332 
333             BaseModel<?> model = (BaseModel<?>)result;
334 
335             model.setCachedModel(true);
336 
337             return model;
338         }
339     }
340 
341     private static Log _log = LogFactoryUtil.getLog(EntityCacheImpl.class);
342 
343     private static ThreadLocal<Map> _localCache;
344     private static boolean _localCacheAvailable;
345     private static ThreadLocal<Boolean> _localCacheEnabled =
346         new InitialThreadLocal<Boolean>(Boolean.FALSE);
347 
348     static {
349         if (PropsValues.VALUE_OBJECT_ENTITY_THREAD_LOCAL_CACHE_MAX_SIZE > 0) {
350             _localCache = new InitialThreadLocal<Map>(new LRUMap(
351                 PropsValues.VALUE_OBJECT_ENTITY_THREAD_LOCAL_CACHE_MAX_SIZE));
352             _localCacheAvailable = true;
353         }
354     }
355 
356     private MultiVMPool _multiVMPool;
357     private Map<String, PortalCache> _portalCaches =
358         new ConcurrentHashMap<String, PortalCache>();
359 
360 }