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.cache;
016    
017    import com.liferay.portal.kernel.cache.Lifecycle;
018    import com.liferay.portal.kernel.cache.ThreadLocalCachable;
019    import com.liferay.portal.kernel.cache.ThreadLocalCache;
020    import com.liferay.portal.kernel.cache.ThreadLocalCacheManager;
021    import com.liferay.portal.kernel.util.StringBundler;
022    import com.liferay.portal.kernel.util.StringPool;
023    import com.liferay.portal.kernel.util.StringUtil;
024    import com.liferay.portal.spring.aop.AnnotationChainableMethodAdvice;
025    import com.liferay.portal.spring.aop.ServiceBeanMethodInvocation;
026    
027    import java.io.Serializable;
028    
029    import java.lang.annotation.Annotation;
030    
031    import org.aopalliance.intercept.MethodInvocation;
032    
033    /**
034     * @author Shuyang Zhou
035     * @author Brian Wing Shun Chan
036     */
037    public class ThreadLocalCacheAdvice
038            extends AnnotationChainableMethodAdvice<ThreadLocalCachable> {
039    
040            @Override
041            public void afterReturning(MethodInvocation methodInvocation, Object result)
042                    throws Throwable {
043    
044                    ThreadLocalCachable threadLocalCachable = findAnnotation(
045                            methodInvocation);
046    
047                    if (threadLocalCachable == _nullThreadLocalCacheable) {
048                            return;
049                    }
050    
051                    Serializable cacheName = _getCacheName(methodInvocation);
052    
053                    ThreadLocalCache<Object> threadLocalCache =
054                            ThreadLocalCacheManager.getThreadLocalCache(
055                                    threadLocalCachable.scope(), cacheName);
056    
057                    String cacheKey = _getCacheKey(methodInvocation.getArguments());
058    
059                    if (result == null) {
060                            threadLocalCache.put(cacheKey, nullResult);
061                    }
062                    else {
063                            threadLocalCache.put(cacheKey, result);
064                    }
065            }
066    
067            @Override
068            public Object before(MethodInvocation methodInvocation) throws Throwable {
069                    ThreadLocalCachable threadLocalCachable = findAnnotation(
070                            methodInvocation);
071    
072                    if (threadLocalCachable == _nullThreadLocalCacheable) {
073                            return null;
074                    }
075    
076                    Serializable cacheName = _getCacheName(methodInvocation);
077    
078                    ThreadLocalCache<?> threadLocalCache =
079                            ThreadLocalCacheManager.getThreadLocalCache(
080                                    threadLocalCachable.scope(), cacheName);
081    
082                    String cacheKey = _getCacheKey(methodInvocation.getArguments());
083    
084                    Object value = threadLocalCache.get(cacheKey);
085    
086                    if (value == nullResult) {
087                            return null;
088                    }
089    
090                    return value;
091            }
092    
093            @Override
094            public ThreadLocalCachable getNullAnnotation() {
095                    return _nullThreadLocalCacheable;
096            }
097    
098            private String _getCacheKey(Object[] arguments) {
099                    StringBundler sb = new StringBundler(arguments.length * 2 - 1);
100    
101                    for (int i = 0; i < arguments.length; i++) {
102                            sb.append(StringUtil.toHexString(arguments[i]));
103    
104                            if ((i + 1) < arguments.length) {
105                                    sb.append(StringPool.POUND);
106                            }
107                    }
108    
109                    return sb.toString();
110            }
111    
112            private Serializable _getCacheName(MethodInvocation methodInvocation) {
113                    if (methodInvocation instanceof ServiceBeanMethodInvocation) {
114                            ServiceBeanMethodInvocation serviceBeanMethodInvocation =
115                                    (ServiceBeanMethodInvocation)methodInvocation;
116    
117                            return serviceBeanMethodInvocation.toCacheKeyModel();
118                    }
119                    else {
120                            return methodInvocation.toString();
121                    }
122            }
123    
124            private static ThreadLocalCachable _nullThreadLocalCacheable =
125                    new ThreadLocalCachable() {
126    
127                            public Class<? extends Annotation> annotationType() {
128                                    return ThreadLocalCachable.class;
129                            }
130    
131                            public Lifecycle scope() {
132                                    return null;
133                            }
134    
135                    };
136    
137    }