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