001    /**
002     * Copyright (c) 2000-present 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.mvcc.MVCCPortalCacheFactory;
018    import com.liferay.portal.kernel.cache.CacheManagerListener;
019    import com.liferay.portal.kernel.cache.CacheRegistryItem;
020    import com.liferay.portal.kernel.cache.CacheRegistryUtil;
021    import com.liferay.portal.kernel.cache.MultiVMPool;
022    import com.liferay.portal.kernel.cache.PortalCache;
023    import com.liferay.portal.kernel.cache.PortalCacheHelperUtil;
024    import com.liferay.portal.kernel.cache.PortalCacheManager;
025    import com.liferay.portal.kernel.dao.orm.EntityCache;
026    import com.liferay.portal.kernel.dao.orm.Session;
027    import com.liferay.portal.kernel.dao.orm.SessionFactory;
028    import com.liferay.portal.kernel.dao.shard.ShardUtil;
029    import com.liferay.portal.kernel.log.Log;
030    import com.liferay.portal.kernel.log.LogFactoryUtil;
031    import com.liferay.portal.kernel.security.pacl.DoPrivileged;
032    import com.liferay.portal.kernel.util.AutoResetThreadLocal;
033    import com.liferay.portal.kernel.util.HashUtil;
034    import com.liferay.portal.kernel.util.StringPool;
035    import com.liferay.portal.model.BaseModel;
036    import com.liferay.portal.model.CacheModel;
037    import com.liferay.portal.model.MVCCModel;
038    import com.liferay.portal.util.PropsValues;
039    
040    import java.io.Externalizable;
041    import java.io.IOException;
042    import java.io.ObjectInput;
043    import java.io.ObjectOutput;
044    import java.io.Serializable;
045    
046    import java.util.Map;
047    import java.util.concurrent.ConcurrentHashMap;
048    import java.util.concurrent.ConcurrentMap;
049    
050    import org.apache.commons.collections.map.LRUMap;
051    
052    /**
053     * @author Brian Wing Shun Chan
054     * @author Shuyang Zhou
055     */
056    @DoPrivileged
057    public class EntityCacheImpl
058            implements CacheManagerListener, CacheRegistryItem, EntityCache {
059    
060            public static final String CACHE_NAME = EntityCache.class.getName();
061    
062            public void afterPropertiesSet() {
063                    CacheRegistryUtil.register(this);
064            }
065    
066            @Override
067            public void clearCache() {
068                    clearLocalCache();
069    
070                    for (PortalCache<?, ?> portalCache : _portalCaches.values()) {
071                            portalCache.removeAll();
072                    }
073            }
074    
075            @Override
076            public void clearCache(Class<?> clazz) {
077                    clearLocalCache();
078    
079                    PortalCache<?, ?> portalCache = _getPortalCache(clazz, true);
080    
081                    if (portalCache != null) {
082                            portalCache.removeAll();
083                    }
084            }
085    
086            @Override
087            public void clearLocalCache() {
088                    if (_LOCAL_CACHE_AVAILABLE) {
089                            _localCache.remove();
090                    }
091            }
092    
093            @Override
094            public void dispose() {
095                    _portalCaches.clear();
096            }
097    
098            @Override
099            public PortalCache<Serializable, Serializable> getPortalCache(
100                    Class<?> clazz) {
101    
102                    return _getPortalCache(clazz, true);
103            }
104    
105            @Override
106            public String getRegistryName() {
107                    return CACHE_NAME;
108            }
109    
110            @Override
111            public Serializable getResult(
112                    boolean entityCacheEnabled, Class<?> clazz, Serializable primaryKey) {
113    
114                    if (!PropsValues.VALUE_OBJECT_ENTITY_CACHE_ENABLED ||
115                            !entityCacheEnabled || !CacheRegistryUtil.isActive()) {
116    
117                            return null;
118                    }
119    
120                    Serializable result = null;
121    
122                    Map<Serializable, Serializable> localCache = null;
123    
124                    Serializable localCacheKey = null;
125    
126                    if (_LOCAL_CACHE_AVAILABLE) {
127                            localCache = _localCache.get();
128    
129                            localCacheKey = _encodeLocalCacheKey(clazz, primaryKey);
130    
131                            result = localCache.get(localCacheKey);
132                    }
133    
134                    if (result == null) {
135                            PortalCache<Serializable, Serializable> portalCache =
136                                    _getPortalCache(clazz, true);
137    
138                            Serializable cacheKey = _encodeCacheKey(primaryKey);
139    
140                            result = portalCache.get(cacheKey);
141    
142                            if (result == null) {
143                                    result = StringPool.BLANK;
144                            }
145    
146                            if (_LOCAL_CACHE_AVAILABLE) {
147                                    localCache.put(localCacheKey, result);
148                            }
149                    }
150    
151                    return _toEntityModel(result);
152            }
153    
154            @Override
155            public void init() {
156            }
157    
158            @Override
159            public void invalidate() {
160                    clearCache();
161            }
162    
163            @Override
164            public Serializable loadResult(
165                    boolean entityCacheEnabled, Class<?> clazz, Serializable primaryKey,
166                    SessionFactory sessionFactory) {
167    
168                    if (!PropsValues.VALUE_OBJECT_ENTITY_CACHE_ENABLED ||
169                            !entityCacheEnabled || !CacheRegistryUtil.isActive()) {
170    
171                            Session session = null;
172    
173                            try {
174                                    session = sessionFactory.openSession();
175    
176                                    return (Serializable)session.load(clazz, primaryKey);
177                            }
178                            finally {
179                                    sessionFactory.closeSession(session);
180                            }
181                    }
182    
183                    Serializable result = null;
184    
185                    Map<Serializable, Serializable> localCache = null;
186    
187                    Serializable localCacheKey = null;
188    
189                    if (_LOCAL_CACHE_AVAILABLE) {
190                            localCache = _localCache.get();
191    
192                            localCacheKey = _encodeLocalCacheKey(clazz, primaryKey);
193    
194                            result = localCache.get(localCacheKey);
195                    }
196    
197                    Serializable loadResult = null;
198    
199                    if (result == null) {
200                            PortalCache<Serializable, Serializable> portalCache =
201                                    _getPortalCache(clazz, true);
202    
203                            Serializable cacheKey = _encodeCacheKey(primaryKey);
204    
205                            result = portalCache.get(cacheKey);
206    
207                            if (result == null) {
208                                    if (_log.isDebugEnabled()) {
209                                            _log.debug(
210                                                    "Load " + clazz + " " + primaryKey + " from session");
211                                    }
212    
213                                    Session session = null;
214    
215                                    try {
216                                            session = sessionFactory.openSession();
217    
218                                            loadResult = (Serializable)session.load(clazz, primaryKey);
219                                    }
220                                    finally {
221                                            if (loadResult == null) {
222                                                    result = StringPool.BLANK;
223                                            }
224                                            else {
225                                                    result = ((BaseModel<?>)loadResult).toCacheModel();
226                                            }
227    
228                                            PortalCacheHelperUtil.putWithoutReplicator(
229                                                    portalCache, cacheKey, result);
230    
231                                            sessionFactory.closeSession(session);
232                                    }
233                            }
234    
235                            if (_LOCAL_CACHE_AVAILABLE) {
236                                    localCache.put(localCacheKey, result);
237                            }
238                    }
239    
240                    if (loadResult != null) {
241                            return loadResult;
242                    }
243    
244                    return _toEntityModel(result);
245            }
246    
247            @Override
248            public void notifyCacheAdded(String portalCacheName) {
249            }
250    
251            @Override
252            public void notifyCacheRemoved(String portalCacheName) {
253                    _portalCaches.remove(portalCacheName);
254            }
255    
256            @Override
257            public void putResult(
258                    boolean entityCacheEnabled, Class<?> clazz, Serializable primaryKey,
259                    Serializable result) {
260    
261                    putResult(entityCacheEnabled, clazz, primaryKey, result, true);
262            }
263    
264            @Override
265            public void putResult(
266                    boolean entityCacheEnabled, Class<?> clazz, Serializable primaryKey,
267                    Serializable result, boolean quiet) {
268    
269                    if (!PropsValues.VALUE_OBJECT_ENTITY_CACHE_ENABLED ||
270                            !entityCacheEnabled || !CacheRegistryUtil.isActive() ||
271                            (result == null)) {
272    
273                            return;
274                    }
275    
276                    result = ((BaseModel<?>)result).toCacheModel();
277    
278                    if (_LOCAL_CACHE_AVAILABLE) {
279                            Map<Serializable, Serializable> localCache = _localCache.get();
280    
281                            Serializable localCacheKey = _encodeLocalCacheKey(
282                                    clazz, primaryKey);
283    
284                            localCache.put(localCacheKey, result);
285                    }
286    
287                    PortalCache<Serializable, Serializable> portalCache = _getPortalCache(
288                            clazz, true);
289    
290                    Serializable cacheKey = _encodeCacheKey(primaryKey);
291    
292                    if (quiet) {
293                            PortalCacheHelperUtil.putWithoutReplicator(
294                                    portalCache, cacheKey, result);
295                    }
296                    else {
297                            portalCache.put(cacheKey, result);
298                    }
299            }
300    
301            @Override
302            public void removeCache(String className) {
303                    _portalCaches.remove(className);
304    
305                    String groupKey = _GROUP_KEY_PREFIX.concat(className);
306    
307                    _multiVMPool.removeCache(groupKey);
308            }
309    
310            @Override
311            public void removeResult(
312                    boolean entityCacheEnabled, Class<?> clazz, Serializable primaryKey) {
313    
314                    if (!PropsValues.VALUE_OBJECT_ENTITY_CACHE_ENABLED ||
315                            !entityCacheEnabled || !CacheRegistryUtil.isActive()) {
316    
317                            return;
318                    }
319    
320                    if (_LOCAL_CACHE_AVAILABLE) {
321                            Map<Serializable, Serializable> localCache = _localCache.get();
322    
323                            Serializable localCacheKey = _encodeLocalCacheKey(
324                                    clazz, primaryKey);
325    
326                            localCache.remove(localCacheKey);
327                    }
328    
329                    PortalCache<Serializable, Serializable> portalCache = _getPortalCache(
330                            clazz, true);
331    
332                    Serializable cacheKey = _encodeCacheKey(primaryKey);
333    
334                    portalCache.remove(cacheKey);
335            }
336    
337            public void setMultiVMPool(MultiVMPool multiVMPool) {
338                    _multiVMPool = multiVMPool;
339    
340                    PortalCacheManager<? extends Serializable, ? extends Serializable>
341                            portalCacheManager = _multiVMPool.getCacheManager();
342    
343                    portalCacheManager.registerCacheManagerListener(this);
344            }
345    
346            private Serializable _encodeCacheKey(Serializable primaryKey) {
347                    if (ShardUtil.isEnabled()) {
348                            return new CacheKey(ShardUtil.getCurrentShardName(), primaryKey);
349                    }
350    
351                    return primaryKey;
352            }
353    
354            private Serializable _encodeLocalCacheKey(
355                    Class<?> clazz, Serializable primaryKey) {
356    
357                    if (ShardUtil.isEnabled()) {
358                            return new ShardLocalCacheKey(
359                                    ShardUtil.getCurrentShardName(), clazz.getName(), primaryKey);
360                    }
361    
362                    return new LocalCacheKey(clazz.getName(), primaryKey);
363            }
364    
365            private PortalCache<Serializable, Serializable> _getPortalCache(
366                    Class<?> clazz, boolean createIfAbsent) {
367    
368                    String className = clazz.getName();
369    
370                    PortalCache<Serializable, Serializable> portalCache = _portalCaches.get(
371                            className);
372    
373                    if ((portalCache == null) && createIfAbsent) {
374                            String groupKey = _GROUP_KEY_PREFIX.concat(className);
375    
376                            portalCache =
377                                    (PortalCache<Serializable, Serializable>)_multiVMPool.getCache(
378                                            groupKey, PropsValues.VALUE_OBJECT_ENTITY_BLOCKING_CACHE);
379    
380                            if (PropsValues.VALUE_OBJECT_MVCC_ENTITY_CACHE_ENABLED &&
381                                    MVCCModel.class.isAssignableFrom(clazz)) {
382    
383                                    portalCache =
384                                            (PortalCache<Serializable, Serializable>)
385                                                    MVCCPortalCacheFactory.createMVCCEhcachePortalCache(
386                                                            portalCache);
387                            }
388    
389                            PortalCache<Serializable, Serializable> previousPortalCache =
390                                    _portalCaches.putIfAbsent(className, portalCache);
391    
392                            if (previousPortalCache != null) {
393                                    portalCache = previousPortalCache;
394                            }
395                    }
396    
397                    return portalCache;
398            }
399    
400            private Serializable _toEntityModel(Serializable result) {
401                    if (result == StringPool.BLANK) {
402                            return null;
403                    }
404    
405                    CacheModel<?> cacheModel = (CacheModel<?>)result;
406    
407                    BaseModel<?> entityModel = (BaseModel<?>)cacheModel.toEntityModel();
408    
409                    entityModel.setCachedModel(true);
410    
411                    return entityModel;
412            }
413    
414            private static final String _GROUP_KEY_PREFIX = CACHE_NAME.concat(
415                    StringPool.PERIOD);
416    
417            private static final boolean _LOCAL_CACHE_AVAILABLE;
418    
419            private static final Log _log = LogFactoryUtil.getLog(
420                    EntityCacheImpl.class);
421    
422            private static final ThreadLocal<LRUMap> _localCache;
423    
424            static {
425                    if (PropsValues.VALUE_OBJECT_ENTITY_THREAD_LOCAL_CACHE_MAX_SIZE > 0) {
426                            _LOCAL_CACHE_AVAILABLE = true;
427    
428                            _localCache = new AutoResetThreadLocal<LRUMap>(
429                                    EntityCacheImpl.class + "._localCache",
430                                    new LRUMap(
431                                            PropsValues.
432                                                    VALUE_OBJECT_ENTITY_THREAD_LOCAL_CACHE_MAX_SIZE));
433                    }
434                    else {
435                            _LOCAL_CACHE_AVAILABLE = false;
436    
437                            _localCache = null;
438                    }
439            }
440    
441            private MultiVMPool _multiVMPool;
442            private final ConcurrentMap<String, PortalCache<Serializable, Serializable>>
443                    _portalCaches = new ConcurrentHashMap<>();
444    
445            private static class CacheKey implements Externalizable {
446    
447                    public CacheKey() {
448                    }
449    
450                    public CacheKey(String shardName, Serializable primaryKey) {
451                            _shardName = shardName;
452                            _primaryKey = primaryKey;
453                    }
454    
455                    @Override
456                    public boolean equals(Object obj) {
457                            CacheKey cacheKey = (CacheKey)obj;
458    
459                            if (cacheKey._shardName.equals(_shardName) &&
460                                    cacheKey._primaryKey.equals(_primaryKey)) {
461    
462                                    return true;
463                            }
464    
465                            return false;
466                    }
467    
468                    @Override
469                    public int hashCode() {
470                            return _shardName.hashCode() * 11 + _primaryKey.hashCode();
471                    }
472    
473                    @Override
474                    public void readExternal(ObjectInput objectInput)
475                            throws ClassNotFoundException, IOException {
476    
477                            _primaryKey = (Serializable)objectInput.readObject();
478                            _shardName = objectInput.readUTF();
479                    }
480    
481                    @Override
482                    public void writeExternal(ObjectOutput objectOutput)
483                            throws IOException {
484    
485                            objectOutput.writeObject(_primaryKey);
486                            objectOutput.writeUTF(_shardName);
487                    }
488    
489                    private static final long serialVersionUID = 1L;
490    
491                    private Serializable _primaryKey;
492                    private String _shardName;
493    
494            }
495    
496            private static class LocalCacheKey implements Serializable {
497    
498                    public LocalCacheKey(String className, Serializable primaryKey) {
499                            _className = className;
500                            _primaryKey = primaryKey;
501                    }
502    
503                    @Override
504                    public boolean equals(Object obj) {
505                            LocalCacheKey localCacheKey = (LocalCacheKey)obj;
506    
507                            if (localCacheKey._className.equals(_className) &&
508                                    localCacheKey._primaryKey.equals(_primaryKey)) {
509    
510                                    return true;
511                            }
512    
513                            return false;
514                    }
515    
516                    @Override
517                    public int hashCode() {
518                            return _className.hashCode() * 11 + _primaryKey.hashCode();
519                    }
520    
521                    private static final long serialVersionUID = 1L;
522    
523                    private final String _className;
524                    private final Serializable _primaryKey;
525    
526            }
527    
528            private static class ShardLocalCacheKey implements Serializable {
529    
530                    public ShardLocalCacheKey(
531                            String shardName, String className, Serializable primaryKey) {
532    
533                            _shardName = shardName;
534                            _className = className;
535                            _primaryKey = primaryKey;
536                    }
537    
538                    @Override
539                    public boolean equals(Object obj) {
540                            ShardLocalCacheKey shardLocalCacheKey = (ShardLocalCacheKey)obj;
541    
542                            if (shardLocalCacheKey._shardName.equals(_shardName) &&
543                                    shardLocalCacheKey._className.equals(_className) &&
544                                    shardLocalCacheKey._primaryKey.equals(_primaryKey)) {
545    
546                                    return true;
547                            }
548    
549                            return false;
550                    }
551    
552                    @Override
553                    public int hashCode() {
554                            int hashCode = HashUtil.hash(0, _shardName);
555    
556                            hashCode = HashUtil.hash(hashCode, _className);
557                            hashCode = HashUtil.hash(hashCode, _primaryKey);
558    
559                            return hashCode;
560                    }
561    
562                    private static final long serialVersionUID = 1L;
563    
564                    private final String _className;
565                    private final Serializable _primaryKey;
566                    private final String _shardName;
567    
568            }
569    
570    }