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