001    /**
002     * Copyright (c) 2000-2011 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.dao.orm.common;
016    
017    import com.liferay.portal.cache.transactional.TransactionalPortalCache;
018    import com.liferay.portal.kernel.cache.CacheRegistryItem;
019    import com.liferay.portal.kernel.cache.CacheRegistryUtil;
020    import com.liferay.portal.kernel.cache.MultiVMPool;
021    import com.liferay.portal.kernel.cache.PortalCache;
022    import com.liferay.portal.kernel.cache.key.CacheKeyGenerator;
023    import com.liferay.portal.kernel.cache.key.CacheKeyGeneratorUtil;
024    import com.liferay.portal.kernel.dao.orm.EntityCache;
025    import com.liferay.portal.kernel.dao.orm.Session;
026    import com.liferay.portal.kernel.dao.orm.SessionFactory;
027    import com.liferay.portal.kernel.dao.shard.ShardUtil;
028    import com.liferay.portal.kernel.log.Log;
029    import com.liferay.portal.kernel.log.LogFactoryUtil;
030    import com.liferay.portal.kernel.util.AutoResetThreadLocal;
031    import com.liferay.portal.kernel.util.StringPool;
032    import com.liferay.portal.model.BaseModel;
033    import com.liferay.portal.util.PropsValues;
034    
035    import java.io.Serializable;
036    
037    import java.util.Map;
038    import java.util.concurrent.ConcurrentHashMap;
039    import java.util.concurrent.ConcurrentMap;
040    
041    import org.apache.commons.collections.map.LRUMap;
042    
043    /**
044     * @author Brian Wing Shun Chan
045     */
046    public class EntityCacheImpl implements CacheRegistryItem, EntityCache {
047    
048            public static final String CACHE_NAME = EntityCache.class.getName();
049    
050            public void afterPropertiesSet() {
051                    CacheRegistryUtil.register(this);
052            }
053    
054            public void clearCache() {
055                    clearLocalCache();
056    
057                    for (PortalCache portalCache : _portalCaches.values()) {
058                            portalCache.removeAll();
059                    }
060            }
061    
062            public void clearCache(String className) {
063                    clearLocalCache();
064    
065                    PortalCache portalCache = _getPortalCache(className, false);
066    
067                    if (portalCache != null) {
068                            portalCache.removeAll();
069                    }
070            }
071    
072            public void clearLocalCache() {
073                    if (_localCacheAvailable) {
074                            _localCache.remove();
075                    }
076            }
077    
078            public String getRegistryName() {
079                    return CACHE_NAME;
080            }
081    
082            public Object getResult(
083                    boolean entityCacheEnabled, Class<?> classObj,
084                    Serializable primaryKeyObj, SessionFactory sessionFactory) {
085    
086                    if (!PropsValues.VALUE_OBJECT_ENTITY_CACHE_ENABLED ||
087                            !entityCacheEnabled || !CacheRegistryUtil.isActive()) {
088    
089                            return null;
090                    }
091    
092                    Object result = null;
093    
094                    Map<String, Object> localCache = null;
095    
096                    String localCacheKey = null;
097    
098                    if (_localCacheAvailable) {
099                            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 || !CacheRegistryUtil.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                    boolean load = false;
169    
170                    if (result == null) {
171                            PortalCache portalCache = _getPortalCache(classObj.getName(), true);
172    
173                            String cacheKey = _encodeCacheKey(primaryKeyObj);
174    
175                            result = portalCache.get(cacheKey);
176    
177                            if (result == null) {
178                                    if (_log.isDebugEnabled()) {
179                                            _log.debug(
180                                                    "Load " + classObj + " " + primaryKeyObj +
181                                                            " from session");
182                                    }
183    
184                                    load = true;
185    
186                                    Session session = null;
187    
188                                    try {
189                                            session = sessionFactory.openSession();
190    
191                                            result = session.load(classObj, primaryKeyObj);
192                                    }
193                                    finally {
194                                            if (result == null) {
195                                                    result = StringPool.BLANK;
196                                            }
197                                            else {
198                                                    result = _objectToResult(result);
199                                            }
200    
201                                            portalCache.put(cacheKey, result);
202    
203                                            sessionFactory.closeSession(session);
204                                    }
205                            }
206    
207                            if (_localCacheAvailable) {
208                                    localCache.put(localCacheKey, result);
209                            }
210                    }
211    
212                    if (!load) {
213                            return _objectToResult(result);
214                    }
215    
216                    if (result instanceof String) {
217                            return null;
218                    }
219                    else {
220                            return result;
221                    }
222            }
223    
224            public void putResult(
225                    boolean entityCacheEnabled, Class<?> classObj,
226                    Serializable primaryKeyObj, Object result) {
227    
228                    if (!PropsValues.VALUE_OBJECT_ENTITY_CACHE_ENABLED ||
229                            !entityCacheEnabled || !CacheRegistryUtil.isActive() ||
230                            (result == null)) {
231    
232                            return;
233                    }
234    
235                    result = _objectToResult(result);
236    
237                    if (_localCacheAvailable) {
238                            Map<String, Object> localCache = _localCache.get();
239    
240                            String localCacheKey = _encodeLocalCacheKey(
241                                    classObj, primaryKeyObj);
242    
243                            localCache.put(localCacheKey, result);
244                    }
245    
246                    PortalCache portalCache = _getPortalCache(classObj.getName(), true);
247    
248                    String cacheKey = _encodeCacheKey(primaryKeyObj);
249    
250                    portalCache.put(cacheKey, result);
251            }
252    
253            public void removeCache(String className) {
254                    String groupKey = _GROUP_KEY_PREFIX.concat(className);
255    
256                    _portalCaches.remove(groupKey);
257                    _multiVMPool.removeCache(groupKey);
258            }
259    
260            public void removeResult(
261                    boolean entityCacheEnabled, Class<?> classObj,
262                    Serializable primaryKeyObj) {
263    
264                    if (!PropsValues.VALUE_OBJECT_ENTITY_CACHE_ENABLED ||
265                            !entityCacheEnabled || !CacheRegistryUtil.isActive()) {
266    
267                            return;
268                    }
269    
270                    if (_localCacheAvailable) {
271                            Map<String, Object> localCache = _localCache.get();
272    
273                            String localCacheKey = _encodeLocalCacheKey(
274                                    classObj, primaryKeyObj);
275    
276                            localCache.remove(localCacheKey);
277                    }
278    
279                    PortalCache portalCache = _getPortalCache(classObj.getName(), true);
280    
281                    String cacheKey = _encodeCacheKey(primaryKeyObj);
282    
283                    portalCache.remove(cacheKey);
284            }
285    
286            public void setMultiVMPool(MultiVMPool multiVMPool) {
287                    _multiVMPool = multiVMPool;
288            }
289    
290            private String _encodeCacheKey(Serializable primaryKeyObj) {
291                    CacheKeyGenerator cacheKeyGenerator =
292                            CacheKeyGeneratorUtil.getCacheKeyGenerator(CACHE_NAME);
293    
294                    cacheKeyGenerator.append(ShardUtil.getCurrentShardName());
295                    cacheKeyGenerator.append(primaryKeyObj.toString());
296    
297                    return cacheKeyGenerator.finish();
298            }
299    
300            private String _encodeLocalCacheKey(
301                    Class<?> classObj, Serializable primaryKeyObj) {
302    
303                    CacheKeyGenerator cacheKeyGenerator =
304                            CacheKeyGeneratorUtil.getCacheKeyGenerator(CACHE_NAME);
305    
306                    cacheKeyGenerator.append(ShardUtil.getCurrentShardName());
307                    cacheKeyGenerator.append(classObj.getName());
308                    cacheKeyGenerator.append(primaryKeyObj.toString());
309    
310                    return cacheKeyGenerator.finish();
311            }
312    
313            private PortalCache _getPortalCache(
314                    String className, boolean createIfAbsent) {
315    
316                    String groupKey = _GROUP_KEY_PREFIX.concat(className);
317    
318                    PortalCache portalCache = _portalCaches.get(groupKey);
319    
320                    if ((portalCache == null) && createIfAbsent) {
321                            portalCache = _multiVMPool.getCache(
322                                    groupKey, PropsValues.VALUE_OBJECT_ENTITY_BLOCKING_CACHE);
323    
324                            if (PropsValues.TRANSACTIONAL_CACHE_ENABLED) {
325                                    portalCache = new TransactionalPortalCache(portalCache);
326                            }
327    
328                            PortalCache previousPortalCache = _portalCaches.putIfAbsent(
329                                    groupKey, portalCache);
330    
331                            if (previousPortalCache != null) {
332                                    portalCache = previousPortalCache;
333                            }
334    
335                            portalCache.setDebug(true);
336                    }
337    
338                    return portalCache;
339            }
340    
341            private Object _objectToResult(Object result) {
342                    if (result instanceof String) {
343                            return null;
344                    }
345                    else {
346                            result = ((BaseModel<?>)result).clone();
347    
348                            BaseModel<?> model = (BaseModel<?>)result;
349    
350                            model.setCachedModel(true);
351    
352                            return model;
353                    }
354            }
355    
356            private static Log _log = LogFactoryUtil.getLog(EntityCacheImpl.class);
357    
358            private static final String _GROUP_KEY_PREFIX = CACHE_NAME.concat(
359                    StringPool.PERIOD);
360    
361            private static ThreadLocal<LRUMap> _localCache;
362            private static boolean _localCacheAvailable;
363    
364            static {
365                    if (PropsValues.VALUE_OBJECT_ENTITY_THREAD_LOCAL_CACHE_MAX_SIZE > 0) {
366                            _localCache = new AutoResetThreadLocal<LRUMap>(
367                                    EntityCacheImpl.class + "._localCache",
368                                    new LRUMap(
369                                            PropsValues.
370                                                    VALUE_OBJECT_ENTITY_THREAD_LOCAL_CACHE_MAX_SIZE));
371                            _localCacheAvailable = true;
372                    }
373            }
374    
375            private MultiVMPool _multiVMPool;
376            private ConcurrentMap<String, PortalCache> _portalCaches =
377                    new ConcurrentHashMap<String, PortalCache>();
378    
379    }