1   /**
2    * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
3    *
4    * This library is free software; you can redistribute it and/or modify it under
5    * the terms of the GNU Lesser General Public License as published by the Free
6    * Software Foundation; either version 2.1 of the License, or (at your option)
7    * any later version.
8    *
9    * This library is distributed in the hope that it will be useful, but WITHOUT
10   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11   * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12   * details.
13   */
14  
15  package com.liferay.portal.dao.orm.common;
16  
17  import com.liferay.portal.kernel.cache.CacheRegistry;
18  import com.liferay.portal.kernel.cache.CacheRegistryItem;
19  import com.liferay.portal.kernel.cache.MultiVMPool;
20  import com.liferay.portal.kernel.cache.PortalCache;
21  import com.liferay.portal.kernel.cache.key.CacheKeyGenerator;
22  import com.liferay.portal.kernel.cache.key.CacheKeyGeneratorUtil;
23  import com.liferay.portal.kernel.dao.orm.EntityCache;
24  import com.liferay.portal.kernel.dao.orm.Session;
25  import com.liferay.portal.kernel.dao.orm.SessionFactory;
26  import com.liferay.portal.kernel.log.Log;
27  import com.liferay.portal.kernel.log.LogFactoryUtil;
28  import com.liferay.portal.kernel.util.AutoResetThreadLocal;
29  import com.liferay.portal.kernel.util.StringPool;
30  import com.liferay.portal.model.BaseModel;
31  import com.liferay.portal.util.PropsValues;
32  
33  import java.io.Serializable;
34  
35  import java.util.Map;
36  import java.util.concurrent.ConcurrentHashMap;
37  import java.util.concurrent.ConcurrentMap;
38  
39  import org.apache.commons.collections.map.LRUMap;
40  
41  /**
42   * <a href="EntityCacheImpl.java.html"><b><i>View Source</i></b></a>
43   *
44   * @author Brian Wing Shun Chan
45   */
46  public class EntityCacheImpl implements CacheRegistryItem, EntityCache {
47  
48      public static final String CACHE_NAME = EntityCache.class.getName();
49  
50      public void afterPropertiesSet() {
51          CacheRegistry.register(this);
52      }
53  
54      public void clearCache() {
55          clearLocalCache();
56  
57          for (PortalCache portalCache : _portalCaches.values()) {
58              portalCache.removeAll();
59          }
60      }
61  
62      public void clearCache(String className) {
63          clearLocalCache();
64  
65          PortalCache portalCache = _getPortalCache(className, false);
66  
67          if (portalCache != null) {
68              portalCache.removeAll();
69          }
70      }
71  
72      public void clearLocalCache() {
73          if (_localCacheAvailable) {
74              _localCache.remove();
75          }
76      }
77  
78      public String getRegistryName() {
79          return CACHE_NAME;
80      }
81  
82      public Object getResult(
83          boolean entityCacheEnabled, Class<?> classObj,
84          Serializable primaryKeyObj, SessionFactory sessionFactory) {
85  
86          if (!PropsValues.VALUE_OBJECT_ENTITY_CACHE_ENABLED ||
87              !entityCacheEnabled || !CacheRegistry.isActive()) {
88  
89              return null;
90          }
91  
92          Object result = null;
93  
94          Map<String, Object> localCache = null;
95  
96          String localCacheKey = null;
97  
98          if (_localCacheAvailable) {
99              localCache = _localCache.get();
100 
101             localCacheKey = _encodeLocalCacheKey(classObj, primaryKeyObj);
102 
103             result = localCache.get(localCacheKey);
104         }
105 
106         if (result == null) {
107             PortalCache portalCache = _getPortalCache(classObj.getName(), true);
108 
109             String cacheKey = _encodeCacheKey(primaryKeyObj);
110 
111             result = portalCache.get(cacheKey);
112 
113             if (result == null) {
114                 result = StringPool.BLANK;
115 
116                 portalCache.put(cacheKey, result);
117             }
118 
119             if (_localCacheAvailable) {
120                 localCache.put(localCacheKey, result);
121             }
122         }
123 
124         if (result != null) {
125             result = _objectToResult(result);
126         }
127 
128         return result;
129     }
130 
131     public void invalidate() {
132         clearCache();
133     }
134 
135     public Object loadResult(
136         boolean entityCacheEnabled, Class<?> classObj,
137         Serializable primaryKeyObj, SessionFactory sessionFactory) {
138 
139         if (!PropsValues.VALUE_OBJECT_ENTITY_CACHE_ENABLED ||
140             !entityCacheEnabled || !CacheRegistry.isActive()) {
141 
142             Session session = null;
143 
144             try {
145                 session = sessionFactory.openSession();
146 
147                 return session.load(classObj, primaryKeyObj);
148             }
149             finally {
150                 sessionFactory.closeSession(session);
151             }
152         }
153 
154         Object result = null;
155 
156         Map<String, Object> localCache = null;
157 
158         String localCacheKey = null;
159 
160         if (_localCacheAvailable) {
161             localCache = _localCache.get();
162 
163             localCacheKey = _encodeLocalCacheKey(classObj, primaryKeyObj);
164 
165             result = localCache.get(localCacheKey);
166         }
167 
168         if (result == null) {
169             PortalCache portalCache = _getPortalCache(classObj.getName(), true);
170 
171             String cacheKey = _encodeCacheKey(primaryKeyObj);
172 
173             result = portalCache.get(cacheKey);
174 
175             if (result == null) {
176                 if (_log.isDebugEnabled()) {
177                     _log.debug(
178                         "Load " + classObj + " " + primaryKeyObj +
179                             " from session");
180                 }
181 
182                 Session session = null;
183 
184                 try {
185                     session = sessionFactory.openSession();
186 
187                     result = session.load(classObj, primaryKeyObj);
188                 }
189                 finally {
190                     if (result == null) {
191                         result = StringPool.BLANK;
192                     }
193                     else {
194                         result = _objectToResult(result);
195                     }
196 
197                     portalCache.put(cacheKey, result);
198 
199                     sessionFactory.closeSession(session);
200                 }
201             }
202 
203             if (_localCacheAvailable) {
204                 localCache.put(localCacheKey, result);
205             }
206         }
207 
208         result = _objectToResult(result);
209 
210         return result;
211     }
212 
213     public void putResult(
214         boolean entityCacheEnabled, Class<?> classObj,
215         Serializable primaryKeyObj, Object result) {
216 
217         if (!PropsValues.VALUE_OBJECT_ENTITY_CACHE_ENABLED ||
218             !entityCacheEnabled || !CacheRegistry.isActive() ||
219             (result == null)) {
220 
221             return;
222         }
223 
224         result = _objectToResult(result);
225 
226         if (_localCacheAvailable) {
227             Map<String, Object> localCache = _localCache.get();
228 
229             String localCacheKey = _encodeLocalCacheKey(
230                 classObj, primaryKeyObj);
231 
232             localCache.put(localCacheKey, result);
233         }
234 
235         PortalCache portalCache = _getPortalCache(classObj.getName(), true);
236 
237         String cacheKey = _encodeCacheKey(primaryKeyObj);
238 
239         portalCache.put(cacheKey, result);
240     }
241 
242     public void removeResult(
243         boolean entityCacheEnabled, Class<?> classObj,
244         Serializable primaryKeyObj) {
245 
246         if (!PropsValues.VALUE_OBJECT_ENTITY_CACHE_ENABLED ||
247             !entityCacheEnabled || !CacheRegistry.isActive()) {
248 
249             return;
250         }
251 
252         if (_localCacheAvailable) {
253             Map<String, Object> localCache = _localCache.get();
254 
255             String localCacheKey = _encodeLocalCacheKey(
256                 classObj, primaryKeyObj);
257 
258             localCache.remove(localCacheKey);
259         }
260 
261         PortalCache portalCache = _getPortalCache(classObj.getName(), true);
262 
263         String cacheKey = _encodeCacheKey(primaryKeyObj);
264 
265         portalCache.remove(cacheKey);
266     }
267 
268     public void setMultiVMPool(MultiVMPool multiVMPool) {
269         _multiVMPool = multiVMPool;
270     }
271 
272     private String _encodeCacheKey(Serializable primaryKeyObj) {
273         CacheKeyGenerator cacheKeyGenerator =
274             CacheKeyGeneratorUtil.getCacheKeyGenerator(CACHE_NAME);
275 
276         return cacheKeyGenerator.getCacheKey(primaryKeyObj.toString());
277     }
278 
279     private String _encodeLocalCacheKey(
280         Class<?> classObj, Serializable primaryKeyObj) {
281 
282         CacheKeyGenerator cacheKeyGenerator =
283             CacheKeyGeneratorUtil.getCacheKeyGenerator(CACHE_NAME);
284 
285         cacheKeyGenerator.append(classObj.getName());
286         cacheKeyGenerator.append(primaryKeyObj.toString());
287 
288         return cacheKeyGenerator.finish();
289     }
290 
291     private PortalCache _getPortalCache(
292             String className, boolean createIfAbsent) {
293         String groupKey = _GROUP_KEY_PREFIX.concat(className);
294 
295         PortalCache portalCache = _portalCaches.get(groupKey);
296 
297         if ((portalCache == null) && createIfAbsent) {
298             portalCache = _multiVMPool.getCache(
299                 groupKey, PropsValues.VALUE_OBJECT_ENTITY_BLOCKING_CACHE);
300 
301             PortalCache previousPortalCache = _portalCaches.putIfAbsent(
302                 groupKey, portalCache);
303 
304             if (previousPortalCache != null) {
305                 portalCache = previousPortalCache;
306             }
307 
308             portalCache.setDebug(true);
309         }
310 
311         return portalCache;
312     }
313 
314     private Object _objectToResult(Object result) {
315         if (result instanceof String) {
316             return null;
317         }
318         else {
319             result = ((BaseModel<?>)result).clone();
320 
321             BaseModel<?> model = (BaseModel<?>)result;
322 
323             model.setCachedModel(true);
324 
325             return model;
326         }
327     }
328 
329     private static Log _log = LogFactoryUtil.getLog(EntityCacheImpl.class);
330 
331     private static final String _GROUP_KEY_PREFIX = CACHE_NAME.concat(
332         StringPool.PERIOD);
333 
334     private static ThreadLocal<LRUMap> _localCache;
335     private static boolean _localCacheAvailable;
336 
337     static {
338         if (PropsValues.VALUE_OBJECT_ENTITY_THREAD_LOCAL_CACHE_MAX_SIZE > 0) {
339             _localCache = new AutoResetThreadLocal<LRUMap>(
340                 EntityCacheImpl.class + "._localCache",
341                 new LRUMap(
342                     PropsValues.
343                         VALUE_OBJECT_ENTITY_THREAD_LOCAL_CACHE_MAX_SIZE));
344             _localCacheAvailable = true;
345         }
346     }
347 
348     private MultiVMPool _multiVMPool;
349     private ConcurrentMap<String, PortalCache> _portalCaches =
350         new ConcurrentHashMap<String, PortalCache>();
351 
352 }