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