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