001    /**
002     * Copyright (c) 2000-2012 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.dao.orm.EntityCache;
022    import com.liferay.portal.kernel.dao.orm.Session;
023    import com.liferay.portal.kernel.dao.orm.SessionFactory;
024    import com.liferay.portal.kernel.dao.shard.ShardUtil;
025    import com.liferay.portal.kernel.log.Log;
026    import com.liferay.portal.kernel.log.LogFactoryUtil;
027    import com.liferay.portal.kernel.util.AutoResetThreadLocal;
028    import com.liferay.portal.kernel.util.HashUtil;
029    import com.liferay.portal.kernel.util.StringPool;
030    import com.liferay.portal.model.BaseModel;
031    import com.liferay.portal.model.CacheModel;
032    import com.liferay.portal.util.PropsValues;
033    
034    import java.io.Serializable;
035    
036    import java.util.Map;
037    import java.util.concurrent.ConcurrentHashMap;
038    import java.util.concurrent.ConcurrentMap;
039    
040    import org.apache.commons.collections.map.LRUMap;
041    
042    /**
043     * @author Brian Wing Shun Chan
044     * @author Shuyang Zhou
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<?> clazz, Serializable primaryKey) {
084    
085                    if (!PropsValues.VALUE_OBJECT_ENTITY_CACHE_ENABLED ||
086                            !entityCacheEnabled || !CacheRegistryUtil.isActive()) {
087    
088                            return null;
089                    }
090    
091                    Object result = null;
092    
093                    Map<Serializable, Object> localCache = null;
094    
095                    Serializable localCacheKey = null;
096    
097                    if (_localCacheAvailable) {
098                            localCache = _localCache.get();
099    
100                            localCacheKey = _encodeLocalCacheKey(clazz, primaryKey);
101    
102                            result = localCache.get(localCacheKey);
103                    }
104    
105                    if (result == null) {
106                            PortalCache portalCache = _getPortalCache(clazz.getName(), true);
107    
108                            Serializable cacheKey = _encodeCacheKey(primaryKey);
109    
110                            result = portalCache.get(cacheKey);
111    
112                            if (result == null) {
113                                    result = StringPool.BLANK;
114                            }
115    
116                            if (_localCacheAvailable) {
117                                    localCache.put(localCacheKey, result);
118                            }
119                    }
120    
121                    return _toEntityModel(result);
122            }
123    
124            public void invalidate() {
125                    clearCache();
126            }
127    
128            public Object loadResult(
129                    boolean entityCacheEnabled, Class<?> clazz, Serializable primaryKey,
130                    SessionFactory sessionFactory) {
131    
132                    if (!PropsValues.VALUE_OBJECT_ENTITY_CACHE_ENABLED ||
133                            !entityCacheEnabled || !CacheRegistryUtil.isActive()) {
134    
135                            Session session = null;
136    
137                            try {
138                                    session = sessionFactory.openSession();
139    
140                                    return session.load(clazz, primaryKey);
141                            }
142                            finally {
143                                    sessionFactory.closeSession(session);
144                            }
145                    }
146    
147                    Object result = null;
148    
149                    Map<Serializable, Object> localCache = null;
150    
151                    Serializable localCacheKey = null;
152    
153                    if (_localCacheAvailable) {
154                            localCache = _localCache.get();
155    
156                            localCacheKey = _encodeLocalCacheKey(clazz, primaryKey);
157    
158                            result = localCache.get(localCacheKey);
159                    }
160    
161                    Object loadResult = null;
162    
163                    if (result == null) {
164                            PortalCache portalCache = _getPortalCache(clazz.getName(), true);
165    
166                            Serializable cacheKey = _encodeCacheKey(primaryKey);
167    
168                            result = portalCache.get(cacheKey);
169    
170                            if (result == null) {
171                                    if (_log.isDebugEnabled()) {
172                                            _log.debug(
173                                                    "Load " + clazz + " " + primaryKey + " from session");
174                                    }
175    
176                                    Session session = null;
177    
178                                    try {
179                                            session = sessionFactory.openSession();
180    
181                                            loadResult = session.load(clazz, primaryKey);
182                                    }
183                                    finally {
184                                            if (loadResult == null) {
185                                                    result = StringPool.BLANK;
186                                            }
187                                            else {
188                                                    result = ((BaseModel<?>)loadResult).toCacheModel();
189                                            }
190    
191                                            portalCache.put(cacheKey, result);
192    
193                                            sessionFactory.closeSession(session);
194                                    }
195                            }
196    
197                            if (_localCacheAvailable) {
198                                    localCache.put(localCacheKey, result);
199                            }
200                    }
201    
202                    if (loadResult != null) {
203                            return loadResult;
204                    }
205                    else {
206                            return _toEntityModel(result);
207                    }
208            }
209    
210            public void putResult(
211                    boolean entityCacheEnabled, Class<?> clazz, Serializable primaryKey,
212                    Object result) {
213    
214                    if (!PropsValues.VALUE_OBJECT_ENTITY_CACHE_ENABLED ||
215                            !entityCacheEnabled || !CacheRegistryUtil.isActive() ||
216                            (result == null)) {
217    
218                            return;
219                    }
220    
221                    result = ((BaseModel<?>)result).toCacheModel();
222    
223                    if (_localCacheAvailable) {
224                            Map<Serializable, Object> localCache = _localCache.get();
225    
226                            Serializable localCacheKey = _encodeLocalCacheKey(clazz,
227                                    primaryKey);
228    
229                            localCache.put(localCacheKey, result);
230                    }
231    
232                    PortalCache portalCache = _getPortalCache(clazz.getName(), true);
233    
234                    Serializable cacheKey = _encodeCacheKey(primaryKey);
235    
236                    portalCache.put(cacheKey, result);
237            }
238    
239            public void removeCache(String className) {
240                    _portalCaches.remove(className);
241    
242                    String groupKey = _GROUP_KEY_PREFIX.concat(className);
243    
244                    _multiVMPool.removeCache(groupKey);
245            }
246    
247            public void removeResult(
248                    boolean entityCacheEnabled, Class<?> clazz, Serializable primaryKey) {
249    
250                    if (!PropsValues.VALUE_OBJECT_ENTITY_CACHE_ENABLED ||
251                            !entityCacheEnabled || !CacheRegistryUtil.isActive()) {
252    
253                            return;
254                    }
255    
256                    if (_localCacheAvailable) {
257                            Map<Serializable, Object> localCache = _localCache.get();
258    
259                            Serializable localCacheKey = _encodeLocalCacheKey(clazz,
260                                    primaryKey);
261    
262                            localCache.remove(localCacheKey);
263                    }
264    
265                    PortalCache portalCache = _getPortalCache(clazz.getName(), true);
266    
267                    Serializable cacheKey = _encodeCacheKey(primaryKey);
268    
269                    portalCache.remove(cacheKey);
270            }
271    
272            public void setMultiVMPool(MultiVMPool multiVMPool) {
273                    _multiVMPool = multiVMPool;
274            }
275    
276            private Serializable _encodeCacheKey(Serializable primaryKey) {
277                    return new CacheKey(ShardUtil.getCurrentShardName(), primaryKey);
278            }
279    
280            private Serializable _encodeLocalCacheKey(
281                    Class<?> clazz, Serializable primaryKey) {
282    
283                    return new LocalCacheKey(
284                            ShardUtil.getCurrentShardName(), clazz.getName(), primaryKey);
285            }
286    
287            private PortalCache _getPortalCache(
288                    String className, boolean createIfAbsent) {
289    
290                    PortalCache portalCache = _portalCaches.get(className);
291    
292                    if ((portalCache == null) && createIfAbsent) {
293                            String groupKey = _GROUP_KEY_PREFIX.concat(className);
294    
295                            portalCache = _multiVMPool.getCache(
296                                    groupKey, PropsValues.VALUE_OBJECT_ENTITY_BLOCKING_CACHE);
297    
298                            PortalCache previousPortalCache = _portalCaches.putIfAbsent(
299                                    className, portalCache);
300    
301                            if (previousPortalCache != null) {
302                                    portalCache = previousPortalCache;
303                            }
304                    }
305    
306                    return portalCache;
307            }
308    
309            private Object _toEntityModel(Object result) {
310                    if (result == StringPool.BLANK) {
311                            return null;
312                    }
313                    else {
314                            CacheModel<?> cacheModel = (CacheModel<?>)result;
315    
316                            BaseModel<?> entityModel = (BaseModel<?>)cacheModel.toEntityModel();
317    
318                            entityModel.setCachedModel(true);
319    
320                            return entityModel;
321                    }
322            }
323    
324            private static final String _GROUP_KEY_PREFIX = CACHE_NAME.concat(
325                    StringPool.PERIOD);
326    
327            private static Log _log = LogFactoryUtil.getLog(EntityCacheImpl.class);
328    
329            private static ThreadLocal<LRUMap> _localCache;
330            private static boolean _localCacheAvailable;
331    
332            static {
333                    if (PropsValues.VALUE_OBJECT_ENTITY_THREAD_LOCAL_CACHE_MAX_SIZE > 0) {
334                            _localCache = new AutoResetThreadLocal<LRUMap>(
335                                    EntityCacheImpl.class + "._localCache",
336                                    new LRUMap(
337                                            PropsValues.
338                                                    VALUE_OBJECT_ENTITY_THREAD_LOCAL_CACHE_MAX_SIZE));
339                            _localCacheAvailable = true;
340                    }
341            }
342    
343            private MultiVMPool _multiVMPool;
344            private ConcurrentMap<String, PortalCache> _portalCaches =
345                    new ConcurrentHashMap<String, PortalCache>();
346    
347            private static class CacheKey implements Serializable {
348    
349                    public CacheKey(String shardName, Serializable primaryKey) {
350                            _shardName = shardName;
351                            _primaryKey = primaryKey;
352                    }
353    
354                    @Override
355                    public boolean equals(Object obj) {
356                            CacheKey cacheKey = (CacheKey)obj;
357    
358                            if (cacheKey._shardName.equals(_shardName) &&
359                                    cacheKey._primaryKey.equals(_primaryKey)) {
360    
361                                    return true;
362                            }
363                            else {
364                                    return false;
365                            }
366                    }
367    
368                    @Override
369                    public int hashCode() {
370                            return _shardName.hashCode() * 11 + _primaryKey.hashCode();
371                    }
372    
373                    private static final long serialVersionUID = 1L;
374    
375                    private final Serializable _primaryKey;
376                    private final String _shardName;
377    
378            }
379    
380            private static class LocalCacheKey implements Serializable {
381    
382                    public LocalCacheKey(
383                            String shardName, String className, Serializable primaryKey) {
384    
385                            _shardName = shardName;
386                            _className = className;
387                            _primaryKey = primaryKey;
388                    }
389    
390                    @Override
391                    public boolean equals(Object obj) {
392                            LocalCacheKey localCacheKey = (LocalCacheKey)obj;
393    
394                            if (localCacheKey._shardName.equals(_shardName) &&
395                                    localCacheKey._className.equals(_className) &&
396                                    localCacheKey._primaryKey.equals(_primaryKey)) {
397    
398                                    return true;
399                            }
400                            else {
401                                    return false;
402                            }
403                    }
404    
405                    @Override
406                    public int hashCode() {
407                            int hashCode = HashUtil.hash(0, _shardName);
408    
409                            hashCode = HashUtil.hash(hashCode, _className);
410                            hashCode = HashUtil.hash(hashCode, _primaryKey);
411    
412                            return hashCode;
413                    }
414    
415                    private static final long serialVersionUID = 1L;
416    
417                    private final String _className;
418                    private final Serializable _primaryKey;
419                    private final String _shardName;
420    
421            }
422    
423    }