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            public void clearCache() {
061                    clearLocalCache();
062    
063                    for (PortalCache<?, ?> portalCache : _portalCaches.values()) {
064                            portalCache.removeAll();
065                    }
066            }
067    
068            public void clearCache(String className) {
069                    clearLocalCache();
070    
071                    PortalCache<?, ?> portalCache = _getPortalCache(className, true);
072    
073                    if (portalCache != null) {
074                            portalCache.removeAll();
075                    }
076            }
077    
078            public void clearLocalCache() {
079                    if (_localCacheAvailable) {
080                            _localCache.remove();
081                    }
082            }
083    
084            public String getRegistryName() {
085                    return CACHE_NAME;
086            }
087    
088            public Serializable getResult(
089                    boolean entityCacheEnabled, Class<?> clazz, Serializable primaryKey) {
090    
091                    if (!PropsValues.VALUE_OBJECT_ENTITY_CACHE_ENABLED ||
092                            !entityCacheEnabled || !CacheRegistryUtil.isActive()) {
093    
094                            return null;
095                    }
096    
097                    Serializable result = null;
098    
099                    Map<Serializable, Serializable> localCache = null;
100    
101                    Serializable localCacheKey = null;
102    
103                    if (_localCacheAvailable) {
104                            localCache = _localCache.get();
105    
106                            localCacheKey = _encodeLocalCacheKey(clazz, primaryKey);
107    
108                            result = localCache.get(localCacheKey);
109                    }
110    
111                    if (result == null) {
112                            PortalCache<Serializable, Serializable> portalCache =
113                                    _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            public void invalidate() {
132                    clearCache();
133            }
134    
135            public Serializable loadResult(
136                    boolean entityCacheEnabled, Class<?> clazz, Serializable primaryKey,
137                    SessionFactory sessionFactory) {
138    
139                    if (!PropsValues.VALUE_OBJECT_ENTITY_CACHE_ENABLED ||
140                            !entityCacheEnabled || !CacheRegistryUtil.isActive()) {
141    
142                            Session session = null;
143    
144                            try {
145                                    session = sessionFactory.openSession();
146    
147                                    return (Serializable)session.load(clazz, primaryKey);
148                            }
149                            finally {
150                                    sessionFactory.closeSession(session);
151                            }
152                    }
153    
154                    Serializable result = null;
155    
156                    Map<Serializable, Serializable> localCache = null;
157    
158                    Serializable localCacheKey = null;
159    
160                    if (_localCacheAvailable) {
161                            localCache = _localCache.get();
162    
163                            localCacheKey = _encodeLocalCacheKey(clazz, primaryKey);
164    
165                            result = localCache.get(localCacheKey);
166                    }
167    
168                    Serializable loadResult = null;
169    
170                    if (result == null) {
171                            PortalCache<Serializable, Serializable> portalCache =
172                                    _getPortalCache(clazz.getName(), true);
173    
174                            Serializable cacheKey = _encodeCacheKey(primaryKey);
175    
176                            result = portalCache.get(cacheKey);
177    
178                            if (result == null) {
179                                    if (_log.isDebugEnabled()) {
180                                            _log.debug(
181                                                    "Load " + clazz + " " + primaryKey + " from session");
182                                    }
183    
184                                    Session session = null;
185    
186                                    try {
187                                            session = sessionFactory.openSession();
188    
189                                            loadResult = (Serializable)session.load(clazz, primaryKey);
190                                    }
191                                    finally {
192                                            if (loadResult == null) {
193                                                    result = StringPool.BLANK;
194                                            }
195                                            else {
196                                                    result = ((BaseModel<?>)loadResult).toCacheModel();
197                                            }
198    
199                                            portalCache.put(cacheKey, result);
200    
201                                            sessionFactory.closeSession(session);
202                                    }
203                            }
204    
205                            if (_localCacheAvailable) {
206                                    localCache.put(localCacheKey, result);
207                            }
208                    }
209    
210                    if (loadResult != null) {
211                            return loadResult;
212                    }
213                    else {
214                            return _toEntityModel(result);
215                    }
216            }
217    
218            public void putResult(
219                    boolean entityCacheEnabled, Class<?> clazz, Serializable primaryKey,
220                    Serializable result) {
221    
222                    if (!PropsValues.VALUE_OBJECT_ENTITY_CACHE_ENABLED ||
223                            !entityCacheEnabled || !CacheRegistryUtil.isActive() ||
224                            (result == null)) {
225    
226                            return;
227                    }
228    
229                    result = ((BaseModel<?>)result).toCacheModel();
230    
231                    if (_localCacheAvailable) {
232                            Map<Serializable, Serializable> localCache = _localCache.get();
233    
234                            Serializable localCacheKey = _encodeLocalCacheKey(
235                                    clazz, primaryKey);
236    
237                            localCache.put(localCacheKey, result);
238                    }
239    
240                    PortalCache<Serializable, Serializable> portalCache = _getPortalCache(
241                            clazz.getName(), true);
242    
243                    Serializable cacheKey = _encodeCacheKey(primaryKey);
244    
245                    portalCache.put(cacheKey, result);
246            }
247    
248            public void removeCache(String className) {
249                    _portalCaches.remove(className);
250    
251                    String groupKey = _GROUP_KEY_PREFIX.concat(className);
252    
253                    _multiVMPool.removeCache(groupKey);
254            }
255    
256            public void removeResult(
257                    boolean entityCacheEnabled, Class<?> clazz, Serializable primaryKey) {
258    
259                    if (!PropsValues.VALUE_OBJECT_ENTITY_CACHE_ENABLED ||
260                            !entityCacheEnabled || !CacheRegistryUtil.isActive()) {
261    
262                            return;
263                    }
264    
265                    if (_localCacheAvailable) {
266                            Map<Serializable, Serializable> localCache = _localCache.get();
267    
268                            Serializable localCacheKey = _encodeLocalCacheKey(
269                                    clazz, primaryKey);
270    
271                            localCache.remove(localCacheKey);
272                    }
273    
274                    PortalCache<Serializable, Serializable> portalCache = _getPortalCache(
275                            clazz.getName(), true);
276    
277                    Serializable cacheKey = _encodeCacheKey(primaryKey);
278    
279                    portalCache.remove(cacheKey);
280            }
281    
282            public void setMultiVMPool(MultiVMPool multiVMPool) {
283                    _multiVMPool = multiVMPool;
284            }
285    
286            private Serializable _encodeCacheKey(Serializable primaryKey) {
287                    return new CacheKey(ShardUtil.getCurrentShardName(), primaryKey);
288            }
289    
290            private Serializable _encodeLocalCacheKey(
291                    Class<?> clazz, Serializable primaryKey) {
292    
293                    return new LocalCacheKey(
294                            ShardUtil.getCurrentShardName(), clazz.getName(), primaryKey);
295            }
296    
297            private PortalCache<Serializable, Serializable> _getPortalCache(
298                    String className, boolean createIfAbsent) {
299    
300                    PortalCache<Serializable, Serializable> portalCache = _portalCaches.get(
301                            className);
302    
303                    if ((portalCache == null) && createIfAbsent) {
304                            String groupKey = _GROUP_KEY_PREFIX.concat(className);
305    
306                            portalCache =
307                                    (PortalCache<Serializable, Serializable>)_multiVMPool.getCache(
308                                            groupKey, PropsValues.VALUE_OBJECT_ENTITY_BLOCKING_CACHE);
309    
310                            PortalCache<Serializable, Serializable> previousPortalCache =
311                                    _portalCaches.putIfAbsent(className, portalCache);
312    
313                            if (previousPortalCache != null) {
314                                    portalCache = previousPortalCache;
315                            }
316                    }
317    
318                    return portalCache;
319            }
320    
321            private Serializable _toEntityModel(Serializable 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<Serializable, Serializable>>
357                    _portalCaches =
358                            new ConcurrentHashMap
359                                    <String, PortalCache<Serializable, Serializable>>();
360    
361            private static class CacheKey implements Externalizable {
362    
363                    @SuppressWarnings("unused")
364                    public CacheKey() {
365                    }
366    
367                    public CacheKey(String shardName, Serializable primaryKey) {
368                            _shardName = shardName;
369                            _primaryKey = primaryKey;
370                    }
371    
372                    @Override
373                    public boolean equals(Object obj) {
374                            CacheKey cacheKey = (CacheKey)obj;
375    
376                            if (cacheKey._shardName.equals(_shardName) &&
377                                    cacheKey._primaryKey.equals(_primaryKey)) {
378    
379                                    return true;
380                            }
381                            else {
382                                    return false;
383                            }
384                    }
385    
386                    @Override
387                    public int hashCode() {
388                            return _shardName.hashCode() * 11 + _primaryKey.hashCode();
389                    }
390    
391                    public void readExternal(ObjectInput objectInput)
392                            throws ClassNotFoundException, IOException {
393    
394                            _primaryKey = (Serializable)objectInput.readObject();
395                            _shardName = objectInput.readUTF();
396                    }
397    
398                    public void writeExternal(ObjectOutput objectOutput)
399                            throws IOException {
400    
401                            objectOutput.writeObject(_primaryKey);
402                            objectOutput.writeUTF(_shardName);
403                    }
404    
405                    private static final long serialVersionUID = 1L;
406    
407                    private Serializable _primaryKey;
408                    private String _shardName;
409    
410            }
411    
412            private static class LocalCacheKey implements Serializable {
413    
414                    public LocalCacheKey(
415                            String shardName, String className, Serializable primaryKey) {
416    
417                            _shardName = shardName;
418                            _className = className;
419                            _primaryKey = primaryKey;
420                    }
421    
422                    @Override
423                    public boolean equals(Object obj) {
424                            LocalCacheKey localCacheKey = (LocalCacheKey)obj;
425    
426                            if (localCacheKey._shardName.equals(_shardName) &&
427                                    localCacheKey._className.equals(_className) &&
428                                    localCacheKey._primaryKey.equals(_primaryKey)) {
429    
430                                    return true;
431                            }
432                            else {
433                                    return false;
434                            }
435                    }
436    
437                    @Override
438                    public int hashCode() {
439                            int hashCode = HashUtil.hash(0, _shardName);
440    
441                            hashCode = HashUtil.hash(hashCode, _className);
442                            hashCode = HashUtil.hash(hashCode, _primaryKey);
443    
444                            return hashCode;
445                    }
446    
447                    private static final long serialVersionUID = 1L;
448    
449                    private final String _className;
450                    private final Serializable _primaryKey;
451                    private final String _shardName;
452    
453            }
454    
455    }