001    /**
002     * Copyright (c) 2000-2010 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.kernel.cache.CacheRegistryItem;
018    import com.liferay.portal.kernel.cache.CacheRegistryUtil;
019    import com.liferay.portal.kernel.cache.MultiVMPool;
020    import com.liferay.portal.kernel.cache.PortalCache;
021    import com.liferay.portal.kernel.cache.key.CacheKeyGenerator;
022    import com.liferay.portal.kernel.cache.key.CacheKeyGeneratorUtil;
023    import com.liferay.portal.kernel.dao.orm.EntityCache;
024    import com.liferay.portal.kernel.dao.orm.Session;
025    import com.liferay.portal.kernel.dao.orm.SessionFactory;
026    import com.liferay.portal.kernel.log.Log;
027    import com.liferay.portal.kernel.log.LogFactoryUtil;
028    import com.liferay.portal.kernel.util.AutoResetThreadLocal;
029    import com.liferay.portal.kernel.util.StringPool;
030    import com.liferay.portal.model.BaseModel;
031    import com.liferay.portal.util.PropsValues;
032    
033    import java.io.Serializable;
034    
035    import java.util.Map;
036    import java.util.concurrent.ConcurrentHashMap;
037    import java.util.concurrent.ConcurrentMap;
038    
039    import org.apache.commons.collections.map.LRUMap;
040    
041    /**
042     * @author Brian Wing Shun Chan
043     */
044    public class EntityCacheImpl implements CacheRegistryItem, EntityCache {
045    
046            public static final String CACHE_NAME = EntityCache.class.getName();
047    
048            public void afterPropertiesSet() {
049                    CacheRegistryUtil.register(this);
050            }
051    
052            public void clearCache() {
053                    clearLocalCache();
054    
055                    for (PortalCache portalCache : _portalCaches.values()) {
056                            portalCache.removeAll();
057                    }
058            }
059    
060            public void clearCache(String className) {
061                    clearLocalCache();
062    
063                    PortalCache portalCache = _getPortalCache(className, false);
064    
065                    if (portalCache != null) {
066                            portalCache.removeAll();
067                    }
068            }
069    
070            public void clearLocalCache() {
071                    if (_localCacheAvailable) {
072                            _localCache.remove();
073                    }
074            }
075    
076            public String getRegistryName() {
077                    return CACHE_NAME;
078            }
079    
080            public Object getResult(
081                    boolean entityCacheEnabled, Class<?> classObj,
082                    Serializable primaryKeyObj, SessionFactory sessionFactory) {
083    
084                    if (!PropsValues.VALUE_OBJECT_ENTITY_CACHE_ENABLED ||
085                            !entityCacheEnabled || !CacheRegistryUtil.isActive()) {
086    
087                            return null;
088                    }
089    
090                    Object result = null;
091    
092                    Map<String, Object> localCache = null;
093    
094                    String localCacheKey = null;
095    
096                    if (_localCacheAvailable) {
097                            localCache = _localCache.get();
098    
099                            localCacheKey = _encodeLocalCacheKey(classObj, primaryKeyObj);
100    
101                            result = localCache.get(localCacheKey);
102                    }
103    
104                    if (result == null) {
105                            PortalCache portalCache = _getPortalCache(classObj.getName(), true);
106    
107                            String cacheKey = _encodeCacheKey(primaryKeyObj);
108    
109                            result = portalCache.get(cacheKey);
110    
111                            if (result == null) {
112                                    result = StringPool.BLANK;
113    
114                                    portalCache.put(cacheKey, result);
115                            }
116    
117                            if (_localCacheAvailable) {
118                                    localCache.put(localCacheKey, result);
119                            }
120                    }
121    
122                    if (result != null) {
123                            result = _objectToResult(result);
124                    }
125    
126                    return result;
127            }
128    
129            public void invalidate() {
130                    clearCache();
131            }
132    
133            public Object loadResult(
134                    boolean entityCacheEnabled, Class<?> classObj,
135                    Serializable primaryKeyObj, SessionFactory sessionFactory) {
136    
137                    if (!PropsValues.VALUE_OBJECT_ENTITY_CACHE_ENABLED ||
138                            !entityCacheEnabled || !CacheRegistryUtil.isActive()) {
139    
140                            Session session = null;
141    
142                            try {
143                                    session = sessionFactory.openSession();
144    
145                                    return session.load(classObj, primaryKeyObj);
146                            }
147                            finally {
148                                    sessionFactory.closeSession(session);
149                            }
150                    }
151    
152                    Object result = null;
153    
154                    Map<String, Object> localCache = null;
155    
156                    String localCacheKey = null;
157    
158                    if (_localCacheAvailable) {
159                            localCache = _localCache.get();
160    
161                            localCacheKey = _encodeLocalCacheKey(classObj, primaryKeyObj);
162    
163                            result = localCache.get(localCacheKey);
164                    }
165    
166                    if (result == null) {
167                            PortalCache portalCache = _getPortalCache(classObj.getName(), true);
168    
169                            String cacheKey = _encodeCacheKey(primaryKeyObj);
170    
171                            result = portalCache.get(cacheKey);
172    
173                            if (result == null) {
174                                    if (_log.isDebugEnabled()) {
175                                            _log.debug(
176                                                    "Load " + classObj + " " + primaryKeyObj +
177                                                            " from session");
178                                    }
179    
180                                    Session session = null;
181    
182                                    try {
183                                            session = sessionFactory.openSession();
184    
185                                            result = session.load(classObj, primaryKeyObj);
186                                    }
187                                    finally {
188                                            if (result == null) {
189                                                    result = StringPool.BLANK;
190                                            }
191                                            else {
192                                                    result = _objectToResult(result);
193                                            }
194    
195                                            portalCache.put(cacheKey, result);
196    
197                                            sessionFactory.closeSession(session);
198                                    }
199                            }
200    
201                            if (_localCacheAvailable) {
202                                    localCache.put(localCacheKey, result);
203                            }
204                    }
205    
206                    result = _objectToResult(result);
207    
208                    return result;
209            }
210    
211            public void putResult(
212                    boolean entityCacheEnabled, Class<?> classObj,
213                    Serializable primaryKeyObj, Object result) {
214    
215                    if (!PropsValues.VALUE_OBJECT_ENTITY_CACHE_ENABLED ||
216                            !entityCacheEnabled || !CacheRegistryUtil.isActive() ||
217                            (result == null)) {
218    
219                            return;
220                    }
221    
222                    result = _objectToResult(result);
223    
224                    if (_localCacheAvailable) {
225                            Map<String, Object> localCache = _localCache.get();
226    
227                            String localCacheKey = _encodeLocalCacheKey(
228                                    classObj, primaryKeyObj);
229    
230                            localCache.put(localCacheKey, result);
231                    }
232    
233                    PortalCache portalCache = _getPortalCache(classObj.getName(), true);
234    
235                    String cacheKey = _encodeCacheKey(primaryKeyObj);
236    
237                    portalCache.put(cacheKey, result);
238            }
239    
240            public void removeResult(
241                    boolean entityCacheEnabled, Class<?> classObj,
242                    Serializable primaryKeyObj) {
243    
244                    if (!PropsValues.VALUE_OBJECT_ENTITY_CACHE_ENABLED ||
245                            !entityCacheEnabled || !CacheRegistryUtil.isActive()) {
246    
247                            return;
248                    }
249    
250                    if (_localCacheAvailable) {
251                            Map<String, Object> localCache = _localCache.get();
252    
253                            String localCacheKey = _encodeLocalCacheKey(
254                                    classObj, primaryKeyObj);
255    
256                            localCache.remove(localCacheKey);
257                    }
258    
259                    PortalCache portalCache = _getPortalCache(classObj.getName(), true);
260    
261                    String cacheKey = _encodeCacheKey(primaryKeyObj);
262    
263                    portalCache.remove(cacheKey);
264            }
265    
266            public void setMultiVMPool(MultiVMPool multiVMPool) {
267                    _multiVMPool = multiVMPool;
268            }
269    
270            private String _encodeCacheKey(Serializable primaryKeyObj) {
271                    CacheKeyGenerator cacheKeyGenerator =
272                            CacheKeyGeneratorUtil.getCacheKeyGenerator(CACHE_NAME);
273    
274                    return cacheKeyGenerator.getCacheKey(primaryKeyObj.toString());
275            }
276    
277            private String _encodeLocalCacheKey(
278                    Class<?> classObj, Serializable primaryKeyObj) {
279    
280                    CacheKeyGenerator cacheKeyGenerator =
281                            CacheKeyGeneratorUtil.getCacheKeyGenerator(CACHE_NAME);
282    
283                    cacheKeyGenerator.append(classObj.getName());
284                    cacheKeyGenerator.append(primaryKeyObj.toString());
285    
286                    return cacheKeyGenerator.finish();
287            }
288    
289            private PortalCache _getPortalCache(
290                            String className, boolean createIfAbsent) {
291                    String groupKey = _GROUP_KEY_PREFIX.concat(className);
292    
293                    PortalCache portalCache = _portalCaches.get(groupKey);
294    
295                    if ((portalCache == null) && createIfAbsent) {
296                            portalCache = _multiVMPool.getCache(
297                                    groupKey, PropsValues.VALUE_OBJECT_ENTITY_BLOCKING_CACHE);
298    
299                            PortalCache previousPortalCache = _portalCaches.putIfAbsent(
300                                    groupKey, portalCache);
301    
302                            if (previousPortalCache != null) {
303                                    portalCache = previousPortalCache;
304                            }
305    
306                            portalCache.setDebug(true);
307                    }
308    
309                    return portalCache;
310            }
311    
312            private Object _objectToResult(Object result) {
313                    if (result instanceof String) {
314                            return null;
315                    }
316                    else {
317                            result = ((BaseModel<?>)result).clone();
318    
319                            BaseModel<?> model = (BaseModel<?>)result;
320    
321                            model.setCachedModel(true);
322    
323                            return model;
324                    }
325            }
326    
327            private static Log _log = LogFactoryUtil.getLog(EntityCacheImpl.class);
328    
329            private static final String _GROUP_KEY_PREFIX = CACHE_NAME.concat(
330                    StringPool.PERIOD);
331    
332            private static ThreadLocal<LRUMap> _localCache;
333            private static boolean _localCacheAvailable;
334    
335            static {
336                    if (PropsValues.VALUE_OBJECT_ENTITY_THREAD_LOCAL_CACHE_MAX_SIZE > 0) {
337                            _localCache = new AutoResetThreadLocal<LRUMap>(
338                                    EntityCacheImpl.class + "._localCache",
339                                    new LRUMap(
340                                            PropsValues.
341                                                    VALUE_OBJECT_ENTITY_THREAD_LOCAL_CACHE_MAX_SIZE));
342                            _localCacheAvailable = true;
343                    }
344            }
345    
346            private MultiVMPool _multiVMPool;
347            private ConcurrentMap<String, PortalCache> _portalCaches =
348                    new ConcurrentHashMap<String, PortalCache>();
349    
350    }