001    /**
002     * Copyright (c) 2000-2011 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 void afterReturning(
042                            MethodInvocation methodInvocation, Object result)
043                    throws Throwable {
044    
045                    ThreadLocalCachable threadLocalCachable = findAnnotation(
046                            methodInvocation);
047    
048                    if (threadLocalCachable == _nullThreadLocalCacheable) {
049                            return;
050                    }
051    
052                    Serializable cacheName = _getCacheName(methodInvocation);
053    
054                    ThreadLocalCache<Object> threadLocalCache =
055                            ThreadLocalCacheManager.getThreadLocalCache(
056                                    threadLocalCachable.scope(), cacheName);
057    
058                    String cacheKey = _getCacheKey(methodInvocation.getArguments());
059    
060                    if (result == null) {
061                            threadLocalCache.put(cacheKey, nullResult);
062                    }
063                    else {
064                            threadLocalCache.put(cacheKey, result);
065                    }
066            }
067    
068            @Override
069            public Object before(MethodInvocation methodInvocation) throws Throwable {
070                    ThreadLocalCachable threadLocalCachable = findAnnotation(
071                            methodInvocation);
072    
073                    if (threadLocalCachable == _nullThreadLocalCacheable) {
074                            return null;
075                    }
076    
077                    Serializable cacheName = _getCacheName(methodInvocation);
078    
079                    ThreadLocalCache<?> threadLocalCache =
080                            ThreadLocalCacheManager.getThreadLocalCache(
081                                    threadLocalCachable.scope(), cacheName);
082    
083                    String cacheKey = _getCacheKey(methodInvocation.getArguments());
084    
085                    Object value = threadLocalCache.get(cacheKey);
086    
087                    if (value == nullResult) {
088                            return null;
089                    }
090    
091                    return value;
092            }
093    
094            @Override
095            public ThreadLocalCachable getNullAnnotation() {
096                    return _nullThreadLocalCacheable;
097            }
098    
099            private String _getCacheKey(Object[] arguments) {
100                    StringBundler sb = new StringBundler(arguments.length * 2 - 1);
101    
102                    for (int i = 0; i < arguments.length; i++) {
103                            sb.append(StringUtil.toHexString(arguments[i]));
104    
105                            if ((i + 1) < arguments.length) {
106                                    sb.append(StringPool.POUND);
107                            }
108                    }
109    
110                    return sb.toString();
111            }
112    
113            private Serializable _getCacheName(MethodInvocation methodInvocation) {
114                    if (methodInvocation instanceof ServiceBeanMethodInvocation) {
115                            ServiceBeanMethodInvocation serviceBeanMethodInvocation =
116                                    (ServiceBeanMethodInvocation)methodInvocation;
117    
118                            return serviceBeanMethodInvocation.toCacheKeyModel();
119                    }
120                    else {
121                            return methodInvocation.toString();
122                    }
123            }
124    
125            private static ThreadLocalCachable _nullThreadLocalCacheable =
126                    new ThreadLocalCachable() {
127    
128                            public Class<? extends Annotation> annotationType() {
129                                    return ThreadLocalCachable.class;
130                            }
131    
132                            public Lifecycle scope() {
133                                    return null;
134                            }
135    
136                    };
137    
138    }