001    /**
002     * Copyright (c) 2000-2010 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.spring.aop;
016    
017    import com.liferay.portal.kernel.util.AutoResetThreadLocal;
018    import com.liferay.portal.kernel.util.MethodTargetClassKey;
019    import com.liferay.portal.servlet.filters.threadlocal.ThreadLocalFilter;
020    
021    import java.lang.annotation.Annotation;
022    import java.lang.reflect.Method;
023    
024    import java.util.HashMap;
025    import java.util.Map;
026    import java.util.concurrent.ConcurrentHashMap;
027    import java.util.concurrent.ConcurrentMap;
028    
029    import org.aopalliance.intercept.MethodInvocation;
030    
031    import org.springframework.beans.factory.BeanFactory;
032    import org.springframework.beans.factory.BeanFactoryAware;
033    
034    /**
035     * @author Shuyang Zhou
036     * @author Brian Wing Shun Chan
037     */
038    public abstract class AnnotationChainableMethodAdvice<T extends Annotation>
039            extends ChainableMethodAdvice implements BeanFactoryAware {
040    
041            public AnnotationChainableMethodAdvice() {
042                    _nullAnnotation = getNullAnnotation();
043                    _annotationType = _nullAnnotation.annotationType();
044            }
045    
046            public abstract T getNullAnnotation();
047    
048            public void setBeanFactory(BeanFactory beanFactory) {
049                    if (beanFactory == null) {
050                            _beanFactoryAnnotations.remove(_beanFactory);
051                    }
052                    else {
053                            _beanFactory = beanFactory;
054    
055                            _beanFactoryAnnotations.putIfAbsent(
056                                    _beanFactory,
057                                    new ConcurrentHashMap<MethodTargetClassKey, Annotation[]>());
058                    }
059            }
060    
061            protected MethodTargetClassKey buildMethodTargetClassKey(
062                    MethodInvocation methodInvocation) {
063    
064                    Map<MethodInvocation, MethodTargetClassKey> methodTargetClassKeyMap =
065                            null;
066    
067                    if (_methodTargetClassKeyThreadLocalCache != null) {
068                            methodTargetClassKeyMap =
069                                    _methodTargetClassKeyThreadLocalCache.get();
070                    }
071    
072                    MethodTargetClassKey methodTargetClassKey = null;
073    
074                    if (methodTargetClassKeyMap != null) {
075                            methodTargetClassKey = methodTargetClassKeyMap.get(
076                                    methodInvocation);
077                    }
078    
079                    if (methodTargetClassKey != null) {
080                            return methodTargetClassKey;
081                    }
082    
083                    Method method = methodInvocation.getMethod();
084    
085                    Class<?> targetClass = null;
086    
087                    Object thisObject = methodInvocation.getThis();
088    
089                    if (thisObject != null) {
090                            targetClass = thisObject.getClass();
091                    }
092    
093                    methodTargetClassKey = new MethodTargetClassKey(method, targetClass);
094    
095                    if (methodTargetClassKeyMap != null) {
096                            methodTargetClassKeyMap.put(methodInvocation, methodTargetClassKey);
097                    }
098    
099                    return methodTargetClassKey;
100            }
101    
102            protected T findAnnotation(MethodTargetClassKey methodTargetClassKey){
103                    Map<MethodTargetClassKey, Annotation[]> annotationsMap =
104                            _beanFactoryAnnotations.get(_beanFactory);
105    
106                    Annotation[] annotations = annotationsMap.get(methodTargetClassKey);
107    
108                    if (annotations != null) {
109                            return getAnnotation(annotations);
110                    }
111    
112                    Method method = methodTargetClassKey.getMethod();
113    
114                    Method targetMethod = methodTargetClassKey.getTargetMethod();
115    
116                    if (targetMethod != null) {
117                            annotations = targetMethod.getAnnotations();
118                    }
119    
120                    if ((annotations == null) || (annotations.length == 0)) {
121                            annotations = method.getAnnotations();
122                    }
123    
124                    if ((annotations == null) || (annotations.length == 0)) {
125                            annotations = _emptyAnnotations;
126                    }
127    
128                    annotationsMap.put(methodTargetClassKey, annotations);
129    
130                    return getAnnotation(annotations);
131            }
132    
133            protected T getAnnotation(Annotation[] annotations) {
134                    for(Annotation annotation : annotations) {
135                            if (annotation.annotationType() == _annotationType) {
136                                    return (T)annotation;
137                            }
138                    }
139    
140                    return _nullAnnotation;
141            }
142    
143            private static ConcurrentMap
144                    <BeanFactory, Map<MethodTargetClassKey, Annotation[]>>
145                            _beanFactoryAnnotations =
146                                    new ConcurrentHashMap
147                                            <BeanFactory, Map<MethodTargetClassKey, Annotation[]>>();
148            private static Annotation[] _emptyAnnotations = new Annotation[0];
149            private static ThreadLocal<Map<MethodInvocation, MethodTargetClassKey>>
150                    _methodTargetClassKeyThreadLocalCache;
151    
152            private Class<? extends Annotation> _annotationType;
153            private BeanFactory _beanFactory;
154            private T _nullAnnotation;
155    
156            static {
157                    if (ThreadLocalFilter.ENABLED) {
158                            _methodTargetClassKeyThreadLocalCache = new AutoResetThreadLocal
159                                    <Map<MethodInvocation, MethodTargetClassKey>>(
160                                            AnnotationChainableMethodAdvice.class +
161                                                    "._methodTargetClassKeyThreadLocalCache",
162                                                    new HashMap<MethodInvocation, MethodTargetClassKey>());
163                    }
164            }
165    
166    }