1   /**
2    * Copyright (c) 2000-2009 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   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
14   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
15   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
16   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
17   * SOFTWARE.
18   */
19  
20  package com.liferay.portal.dao.orm.hibernate;
21  
22  import com.liferay.portal.kernel.cache.CacheRegistry;
23  import com.liferay.portal.kernel.cache.CacheRegistryItem;
24  import com.liferay.portal.kernel.cache.MultiVMPool;
25  import com.liferay.portal.kernel.cache.PortalCache;
26  import com.liferay.portal.kernel.dao.orm.EntityCache;
27  import com.liferay.portal.kernel.dao.orm.Session;
28  import com.liferay.portal.kernel.dao.orm.SessionFactory;
29  import com.liferay.portal.kernel.log.Log;
30  import com.liferay.portal.kernel.log.LogFactoryUtil;
31  import com.liferay.portal.kernel.util.InitialThreadLocal;
32  import com.liferay.portal.kernel.util.StringPool;
33  import com.liferay.portal.model.BaseModel;
34  import com.liferay.portal.util.PropsValues;
35  
36  import java.io.Serializable;
37  
38  import java.util.Map;
39  import java.util.concurrent.ConcurrentHashMap;
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   */
49  public class EntityCacheImpl implements CacheRegistryItem, EntityCache {
50  
51      public static final String CACHE_NAME = EntityCache.class.getName();
52  
53      public void afterPropertiesSet() {
54          CacheRegistry.register(this);
55      }
56  
57      public void clearCache() {
58          clearLocalCache();
59  
60          PortalCache[] portalCaches = _portalCaches.values().toArray(
61              new PortalCache[_portalCaches.size()]);
62  
63          for (PortalCache portalCache : portalCaches) {
64              portalCache.removeAll();
65          }
66      }
67  
68      public void clearCache(String className) {
69          clearLocalCache();
70  
71          PortalCache portalCache = _getPortalCache(className);
72  
73          portalCache.removeAll();
74      }
75  
76      public void clearLocalCache() {
77          if (_localCacheEnabled.get().booleanValue()) {
78              Map<String, Object> localCache = _localCache.get();
79  
80              localCache.clear();
81          }
82      }
83  
84      public String getRegistryName() {
85          return CACHE_NAME;
86      }
87  
88      public Object getResult(
89          boolean entityCacheEnabled, Class<?> classObj,
90          Serializable primaryKeyObj, SessionFactory sessionFactory) {
91  
92          if (!PropsValues.VALUE_OBJECT_ENTITY_CACHE_ENABLED ||
93              !entityCacheEnabled || !CacheRegistry.isActive()) {
94  
95              return null;
96          }
97  
98          Object result = null;
99  
100         Map<String, Object> localCache = null;
101 
102         String localCacheKey = null;
103 
104         if (_localCacheEnabled.get().booleanValue()) {
105             localCache = _localCache.get();
106 
107             localCacheKey = _encodeLocalCacheKey(classObj, primaryKeyObj);
108 
109             result = localCache.get(localCacheKey);
110         }
111 
112         if (result == null) {
113             PortalCache portalCache = _getPortalCache(classObj.getName());
114 
115             String cacheKey = _encodeCacheKey(primaryKeyObj);
116 
117             result = _multiVMPool.get(portalCache, cacheKey);
118 
119             if (result == null) {
120                 result = StringPool.BLANK;
121 
122                 _multiVMPool.put(portalCache, cacheKey, result);
123             }
124 
125             if (_localCacheEnabled.get().booleanValue()) {
126                 localCache.put(localCacheKey, result);
127             }
128         }
129 
130         if (result != null) {
131             result = _objectToResult(result);
132         }
133 
134         return result;
135     }
136 
137     public void invalidate() {
138         clearCache();
139     }
140 
141     public Object loadResult(
142         boolean entityCacheEnabled, Class<?> classObj,
143         Serializable primaryKeyObj, SessionFactory sessionFactory) {
144 
145         if (!PropsValues.VALUE_OBJECT_ENTITY_CACHE_ENABLED ||
146             !entityCacheEnabled || !CacheRegistry.isActive()) {
147 
148             Session session = null;
149 
150             try {
151                 session = sessionFactory.openSession();
152 
153                 return session.load(classObj, primaryKeyObj);
154             }
155             finally {
156                 sessionFactory.closeSession(session);
157             }
158         }
159 
160         Object result = null;
161 
162         Map<String, Object> localCache = null;
163 
164         String localCacheKey = null;
165 
166         if (_localCacheEnabled.get().booleanValue()) {
167             localCache = _localCache.get();
168 
169             localCacheKey = _encodeLocalCacheKey(classObj, primaryKeyObj);
170 
171             result = localCache.get(localCacheKey);
172         }
173 
174         if (result == null) {
175             PortalCache portalCache = _getPortalCache(classObj.getName());
176 
177             String cacheKey = _encodeCacheKey(primaryKeyObj);
178 
179             result = _multiVMPool.get(portalCache, cacheKey);
180 
181             if (result == null) {
182                 if (_log.isDebugEnabled()) {
183                     _log.debug(
184                         "Load " + classObj + " " + primaryKeyObj +
185                             " from session");
186                 }
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 
200                     result = _objectToResult(result);
201 
202                     _multiVMPool.put(portalCache, cacheKey, result);
203 
204                     sessionFactory.closeSession(session);
205                 }
206             }
207 
208             if (_localCacheEnabled.get().booleanValue()) {
209                 localCache.put(localCacheKey, result);
210             }
211         }
212 
213         result = _objectToResult(result);
214 
215         return result;
216     }
217 
218     public void putResult(
219         boolean entityCacheEnabled, Class<?> classObj,
220         Serializable primaryKeyObj, Object result) {
221 
222         if (!PropsValues.VALUE_OBJECT_ENTITY_CACHE_ENABLED ||
223             !entityCacheEnabled || !CacheRegistry.isActive() ||
224             (result == null)) {
225 
226             return;
227         }
228 
229         result = _objectToResult(result);
230 
231         if (_localCacheEnabled.get().booleanValue()) {
232             Map<String, Object> localCache = _localCache.get();
233 
234             String localCacheKey = _encodeLocalCacheKey(
235                 classObj, primaryKeyObj);
236 
237             localCache.put(localCacheKey, result);
238         }
239 
240         PortalCache portalCache = _getPortalCache(classObj.getName());
241 
242         String cacheKey = _encodeCacheKey(primaryKeyObj);
243 
244         _multiVMPool.put(portalCache, cacheKey, result);
245     }
246 
247     public void removeResult(
248         boolean entityCacheEnabled, Class<?> classObj,
249         Serializable primaryKeyObj) {
250 
251         if (!PropsValues.VALUE_OBJECT_ENTITY_CACHE_ENABLED ||
252             !entityCacheEnabled || !CacheRegistry.isActive()) {
253 
254             return;
255         }
256 
257         if (_localCacheEnabled.get().booleanValue()) {
258             Map<String, Object> localCache = _localCache.get();
259 
260             String localCacheKey = _encodeLocalCacheKey(
261                 classObj, primaryKeyObj);
262 
263             localCache.remove(localCacheKey);
264         }
265 
266         PortalCache portalCache = _getPortalCache(classObj.getName());
267 
268         String cacheKey = _encodeCacheKey(primaryKeyObj);
269 
270         _multiVMPool.remove(portalCache, cacheKey);
271     }
272 
273     public void setLocalCacheEnabled(boolean localCacheEnabled) {
274         if (_localCacheAvailable) {
275             _localCacheEnabled.set(Boolean.valueOf(localCacheEnabled));
276         }
277     }
278 
279     public void setMultiVMPool(MultiVMPool multiVMPool) {
280         _multiVMPool = multiVMPool;
281     }
282 
283     private String _encodeCacheKey(Serializable primaryKeyObj) {
284         return String.valueOf(primaryKeyObj);
285     }
286 
287     private String _encodeGroupKey(String className) {
288         StringBuilder sb = new StringBuilder();
289 
290         sb.append(CACHE_NAME);
291         sb.append(StringPool.PERIOD);
292         sb.append(className);
293 
294         return sb.toString();
295     }
296 
297     private String _encodeLocalCacheKey(
298         Class<?> classObj, Serializable primaryKeyObj) {
299 
300         StringBuilder sb = new StringBuilder();
301 
302         sb.append(classObj.getName());
303         sb.append(StringPool.PERIOD);
304         sb.append(primaryKeyObj);
305 
306         return sb.toString();
307     }
308 
309     private PortalCache _getPortalCache(String className) {
310         String groupKey = _encodeGroupKey(className);
311 
312         PortalCache portalCache = _portalCaches.get(groupKey);
313 
314         if (portalCache == null) {
315             portalCache = _multiVMPool.getCache(groupKey, true);
316 
317             _portalCaches.put(groupKey, portalCache);
318         }
319 
320         return portalCache;
321     }
322 
323     private Object _objectToResult(Object result) {
324         if (result instanceof String) {
325             return null;
326         }
327         else {
328             result = ((BaseModel<?>)result).clone();
329 
330             BaseModel<?> model = (BaseModel<?>)result;
331 
332             model.setCachedModel(true);
333 
334             return model;
335         }
336     }
337 
338     private static Log _log = LogFactoryUtil.getLog(EntityCacheImpl.class);
339 
340     private static ThreadLocal<Map> _localCache;
341     private static boolean _localCacheAvailable;
342     private static ThreadLocal<Boolean> _localCacheEnabled =
343         new InitialThreadLocal<Boolean>(Boolean.FALSE);
344 
345     static {
346         if (PropsValues.VALUE_OBJECT_ENTITY_THREAD_LOCAL_CACHE_MAX_SIZE > 0) {
347             _localCache = new InitialThreadLocal<Map>(new LRUMap(
348                 PropsValues.VALUE_OBJECT_ENTITY_THREAD_LOCAL_CACHE_MAX_SIZE));
349             _localCacheAvailable = true;
350         }
351     }
352 
353     private MultiVMPool _multiVMPool;
354     private Map<String, PortalCache> _portalCaches =
355         new ConcurrentHashMap<String, PortalCache>();
356 
357 }