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.dao.orm.EntityCache;
22  import com.liferay.portal.kernel.dao.orm.Session;
23  import com.liferay.portal.kernel.dao.orm.SessionFactory;
24  import com.liferay.portal.kernel.log.Log;
25  import com.liferay.portal.kernel.log.LogFactoryUtil;
26  import com.liferay.portal.kernel.util.AutoResetThreadLocal;
27  import com.liferay.portal.kernel.util.StringPool;
28  import com.liferay.portal.model.BaseModel;
29  import com.liferay.portal.util.PropsValues;
30  
31  import java.io.Serializable;
32  
33  import java.util.Map;
34  import java.util.concurrent.ConcurrentHashMap;
35  
36  import org.apache.commons.collections.map.LRUMap;
37  
38  /**
39   * <a href="EntityCacheImpl.java.html"><b><i>View Source</i></b></a>
40   *
41   * @author Brian Wing Shun Chan
42   */
43  public class EntityCacheImpl implements CacheRegistryItem, EntityCache {
44  
45      public static final String CACHE_NAME = EntityCache.class.getName();
46  
47      public void afterPropertiesSet() {
48          CacheRegistry.register(this);
49      }
50  
51      public void clearCache() {
52          clearLocalCache();
53  
54          PortalCache[] portalCaches = _portalCaches.values().toArray(
55              new PortalCache[_portalCaches.size()]);
56  
57          for (PortalCache portalCache : portalCaches) {
58              portalCache.removeAll();
59          }
60      }
61  
62      public void clearCache(String className) {
63          clearLocalCache();
64  
65          PortalCache portalCache = _getPortalCache(className);
66  
67          portalCache.removeAll();
68      }
69  
70      public void clearLocalCache() {
71          if (_localCacheAvailable) {
72              Map<String, Object> localCache = _localCache.get();
73  
74              localCache.clear();
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());
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());
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());
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());
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         return String.valueOf(primaryKeyObj);
274     }
275 
276     private String _encodeLocalCacheKey(
277         Class<?> classObj, Serializable primaryKeyObj) {
278 
279         return classObj.getName().concat(StringPool.PERIOD).concat(
280             primaryKeyObj.toString());
281     }
282 
283     private PortalCache _getPortalCache(String className) {
284         String groupKey = _GROUP_KEY_PREFIX.concat(className);
285 
286         PortalCache portalCache = _portalCaches.get(groupKey);
287 
288         if (portalCache == null) {
289             portalCache = _multiVMPool.getCache(
290                 groupKey, PropsValues.VALUE_OBJECT_ENTITY_BLOCKING_CACHE);
291 
292             _portalCaches.put(groupKey, portalCache);
293         }
294 
295         return portalCache;
296     }
297 
298     private Object _objectToResult(Object result) {
299         if (result instanceof String) {
300             return null;
301         }
302         else {
303             result = ((BaseModel<?>)result).clone();
304 
305             BaseModel<?> model = (BaseModel<?>)result;
306 
307             model.setCachedModel(true);
308 
309             return model;
310         }
311     }
312 
313     private static Log _log = LogFactoryUtil.getLog(EntityCacheImpl.class);
314 
315     private static final String _GROUP_KEY_PREFIX = CACHE_NAME.concat(
316         StringPool.PERIOD);
317 
318     private static ThreadLocal<LRUMap> _localCache;
319     private static boolean _localCacheAvailable;
320 
321     static {
322         if (PropsValues.VALUE_OBJECT_ENTITY_THREAD_LOCAL_CACHE_MAX_SIZE > 0) {
323             _localCache = new AutoResetThreadLocal<LRUMap>(new LRUMap(
324                 PropsValues.VALUE_OBJECT_ENTITY_THREAD_LOCAL_CACHE_MAX_SIZE));
325             _localCacheAvailable = true;
326         }
327     }
328 
329     private MultiVMPool _multiVMPool;
330     private Map<String, PortalCache> _portalCaches =
331         new ConcurrentHashMap<String, PortalCache>();
332 
333 }