001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * The contents of this file are subject to the terms of the Liferay Enterprise
005     * Subscription License ("License"). You may not use this file except in
006     * compliance with the License. You can obtain a copy of the License by
007     * contacting Liferay, Inc. See the License for the specific language governing
008     * permissions and limitations under the License, including but not limited to
009     * distribution rights of the Software.
010     *
011     *
012     *
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 ThreadLocalCachable getNullAnnotation() {
042                    return _nullThreadLocalCacheable;
043            }
044    
045            @Override
046            public Object invoke(MethodInvocation methodInvocation) throws Throwable {
047                    ThreadLocalCachable threadLocalCachable = findAnnotation(
048                            methodInvocation);
049    
050                    if (threadLocalCachable == _nullThreadLocalCacheable) {
051                            return methodInvocation.proceed();
052                    }
053    
054                    Serializable cacheName = _getCacheName(methodInvocation);
055    
056                    ThreadLocalCache<Object> threadLocalCache =
057                            ThreadLocalCacheManager.getThreadLocalCache(
058                                    threadLocalCachable.scope(), cacheName);
059    
060                    String cacheKey = _getCacheKey(methodInvocation.getArguments());
061    
062                    Object value = threadLocalCache.get(cacheKey);
063    
064                    if (value != null) {
065                            if (value == nullResult) {
066                                    return null;
067                            }
068    
069                            return value;
070                    }
071    
072                    Object result = methodInvocation.proceed();
073    
074                    if (result == null) {
075                            threadLocalCache.put(cacheKey, nullResult);
076                    }
077                    else {
078                            threadLocalCache.put(cacheKey, result);
079                    }
080    
081                    return result;
082            }
083    
084            private String _getCacheKey(Object[] arguments) {
085                    if (arguments.length == 1) {
086                            return StringUtil.toHexString(arguments[0]);
087                    }
088    
089                    StringBundler sb = new StringBundler(arguments.length * 2 - 1);
090    
091                    for (int i = 0; i < arguments.length; i++) {
092                            sb.append(StringUtil.toHexString(arguments[i]));
093    
094                            if ((i + 1) < arguments.length) {
095                                    sb.append(StringPool.POUND);
096                            }
097                    }
098    
099                    return sb.toString();
100            }
101    
102            private Serializable _getCacheName(MethodInvocation methodInvocation) {
103                    if (methodInvocation instanceof ServiceBeanMethodInvocation) {
104                            ServiceBeanMethodInvocation serviceBeanMethodInvocation =
105                                    (ServiceBeanMethodInvocation)methodInvocation;
106    
107                            return serviceBeanMethodInvocation.toCacheKeyModel();
108                    }
109                    else {
110                            return methodInvocation.toString();
111                    }
112            }
113    
114            private static final ThreadLocalCachable _nullThreadLocalCacheable =
115                    new ThreadLocalCachable() {
116    
117                            @Override
118                            public Class<? extends Annotation> annotationType() {
119                                    return ThreadLocalCachable.class;
120                            }
121    
122                            @Override
123                            public Lifecycle scope() {
124                                    return null;
125                            }
126    
127                    };
128    
129    }