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.spring.aop;
016    
017    import com.liferay.portal.kernel.log.Log;
018    import com.liferay.portal.kernel.log.LogFactoryUtil;
019    import com.liferay.portal.kernel.spring.aop.Skip;
020    import com.liferay.portal.kernel.util.ArrayUtil;
021    import com.liferay.portal.kernel.util.ProxyUtil;
022    
023    import java.lang.annotation.Annotation;
024    import java.lang.annotation.ElementType;
025    import java.lang.annotation.Target;
026    import java.lang.reflect.InvocationHandler;
027    import java.lang.reflect.Method;
028    
029    import java.util.ArrayList;
030    import java.util.Collections;
031    import java.util.Iterator;
032    import java.util.List;
033    import java.util.Map;
034    import java.util.concurrent.ConcurrentHashMap;
035    
036    import org.aopalliance.intercept.MethodInterceptor;
037    import org.aopalliance.intercept.MethodInvocation;
038    
039    import org.springframework.aop.SpringProxy;
040    import org.springframework.aop.TargetSource;
041    import org.springframework.aop.framework.AdvisedSupport;
042    import org.springframework.aop.framework.AdvisorChainFactory;
043    import org.springframework.aop.framework.AopProxy;
044    import org.springframework.aop.framework.AopProxyUtils;
045    import org.springframework.util.ClassUtils;
046    
047    /**
048     * @author Shuyang Zhou
049     */
050    public class ServiceBeanAopProxy implements AopProxy, InvocationHandler {
051    
052            public static void clearMethodInterceptorCache() {
053                    _methodInterceptorBags.clear();
054            }
055    
056            public static void removeMethodInterceptor(
057                    MethodInvocation methodInvocation,
058                    MethodInterceptor methodInterceptor) {
059    
060                    if (!(methodInvocation instanceof ServiceBeanMethodInvocation)) {
061                            return;
062                    }
063    
064                    ServiceBeanMethodInvocation serviceBeanMethodInvocation =
065                            (ServiceBeanMethodInvocation)methodInvocation;
066    
067                    MethodInterceptorsBag methodInterceptorsBag =
068                            _methodInterceptorBags.get(serviceBeanMethodInvocation);
069    
070                    if (methodInterceptorsBag == null) {
071                            return;
072                    }
073    
074                    ArrayList<MethodInterceptor> methodInterceptors =
075                            new ArrayList<MethodInterceptor>(
076                                    methodInterceptorsBag._mergedMethodInterceptors);
077    
078                    methodInterceptors.remove(methodInterceptor);
079    
080                    MethodInterceptorsBag newMethodInterceptorsBag = null;
081    
082                    if (methodInterceptors.equals(
083                                    methodInterceptorsBag._classLevelMethodInterceptors)) {
084    
085                            newMethodInterceptorsBag = new MethodInterceptorsBag(
086                                    methodInterceptorsBag._classLevelMethodInterceptors,
087                                    methodInterceptorsBag._classLevelMethodInterceptors);
088                    }
089                    else {
090                            methodInterceptors.trimToSize();
091    
092                            newMethodInterceptorsBag = new MethodInterceptorsBag(
093                                    methodInterceptorsBag._classLevelMethodInterceptors,
094                                    methodInterceptors);
095                    }
096    
097                    _methodInterceptorBags.put(
098                            serviceBeanMethodInvocation.toCacheKeyModel(),
099                            newMethodInterceptorsBag);
100            }
101    
102            public ServiceBeanAopProxy(
103                    AdvisedSupport advisedSupport, MethodInterceptor methodInterceptor) {
104    
105                    _advisedSupport = advisedSupport;
106                    _advisorChainFactory = _advisedSupport.getAdvisorChainFactory();
107    
108                    Class<?>[] proxyInterfaces = _advisedSupport.getProxiedInterfaces();
109    
110                    _mergeSpringMethodInterceptors = !ArrayUtil.contains(
111                            proxyInterfaces, SpringProxy.class);
112    
113                    ArrayList<MethodInterceptor> classLevelMethodInterceptors =
114                            new ArrayList<MethodInterceptor>();
115                    ArrayList<MethodInterceptor> fullMethodInterceptors =
116                            new ArrayList<MethodInterceptor>();
117    
118                    while (true) {
119                            if (!(methodInterceptor instanceof ChainableMethodAdvice)) {
120                                    classLevelMethodInterceptors.add(methodInterceptor);
121                                    fullMethodInterceptors.add(methodInterceptor);
122    
123                                    break;
124                            }
125    
126                            ChainableMethodAdvice chainableMethodAdvice =
127                                    (ChainableMethodAdvice)methodInterceptor;
128    
129                            if (methodInterceptor instanceof AnnotationChainableMethodAdvice) {
130                                    AnnotationChainableMethodAdvice<?>
131                                            annotationChainableMethodAdvice =
132                                                    (AnnotationChainableMethodAdvice<?>)methodInterceptor;
133    
134                                    Class<? extends Annotation> annotationClass =
135                                            annotationChainableMethodAdvice.getAnnotationClass();
136    
137                                    Target target = annotationClass.getAnnotation(Target.class);
138    
139                                    if (target == null) {
140                                            classLevelMethodInterceptors.add(methodInterceptor);
141                                    }
142                                    else {
143                                            for (ElementType elementType : target.value()) {
144                                                    if (elementType == ElementType.TYPE) {
145                                                            classLevelMethodInterceptors.add(methodInterceptor);
146    
147                                                            break;
148                                                    }
149                                            }
150                                    }
151                            }
152                            else {
153                                    classLevelMethodInterceptors.add(methodInterceptor);
154                            }
155    
156                            fullMethodInterceptors.add(methodInterceptor);
157    
158                            methodInterceptor = chainableMethodAdvice.nextMethodInterceptor;
159                    }
160    
161                    classLevelMethodInterceptors.trimToSize();
162    
163                    _classLevelMethodInterceptors = classLevelMethodInterceptors;
164                    _fullMethodInterceptors = fullMethodInterceptors;
165    
166                    AnnotationChainableMethodAdvice.registerAnnotationClass(Skip.class);
167            }
168    
169            public Object getProxy() {
170                    return getProxy(ClassUtils.getDefaultClassLoader());
171            }
172    
173            public Object getProxy(ClassLoader classLoader) {
174                    Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(
175                            _advisedSupport);
176    
177                    return ProxyUtil.newProxyInstance(classLoader, proxiedInterfaces, this);
178            }
179    
180            public Object invoke(Object proxy, Method method, Object[] arguments)
181                    throws Throwable {
182    
183                    TargetSource targetSource = _advisedSupport.getTargetSource();
184    
185                    Object target = null;
186    
187                    try {
188                            Class<?> targetClass = null;
189    
190                            target = targetSource.getTarget();
191    
192                            if (target != null) {
193                                    targetClass = target.getClass();
194                            }
195    
196                            ServiceBeanMethodInvocation serviceBeanMethodInvocation =
197                                    new ServiceBeanMethodInvocation(
198                                            target, targetClass, method, arguments);
199    
200                            Skip skip = ServiceMethodAnnotationCache.get(
201                                    serviceBeanMethodInvocation, Skip.class, null);
202    
203                            if (skip != null) {
204                                    serviceBeanMethodInvocation.setMethodInterceptors(
205                                            Collections.<MethodInterceptor>emptyList());
206                            }
207                            else {
208                                    _setMethodInterceptors(serviceBeanMethodInvocation);
209                            }
210    
211                            return serviceBeanMethodInvocation.proceed();
212                    }
213                    finally {
214                            if ((target != null) && !targetSource.isStatic()) {
215                                    targetSource.releaseTarget(target);
216                            }
217                    }
218            }
219    
220            private List<MethodInterceptor> _getMethodInterceptors(
221                    ServiceBeanMethodInvocation serviceBeanMethodInvocation) {
222    
223                    List<MethodInterceptor> methodInterceptors =
224                            new ArrayList<MethodInterceptor>(_fullMethodInterceptors);
225    
226                    if (!_mergeSpringMethodInterceptors) {
227                            return methodInterceptors;
228                    }
229    
230                    List<Object> list =
231                            _advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
232                                    _advisedSupport, serviceBeanMethodInvocation.getMethod(),
233                                    serviceBeanMethodInvocation.getTargetClass());
234    
235                    Iterator<Object> itr = list.iterator();
236    
237                    while (itr.hasNext()) {
238                            Object obj = itr.next();
239    
240                            if (obj instanceof MethodInterceptor) {
241                                    continue;
242                            }
243    
244                            if (_log.isWarnEnabled()) {
245                                    _log.warn(
246                                            "Skipping unsupported interceptor type " + obj.getClass());
247                            }
248    
249                            itr.remove();
250                    }
251    
252                    if (list.isEmpty()) {
253                            return methodInterceptors;
254                    }
255    
256                    for (Object object : list) {
257                            methodInterceptors.add((MethodInterceptor)object);
258                    }
259    
260                    return methodInterceptors;
261            }
262    
263            private void _setMethodInterceptors(
264                    ServiceBeanMethodInvocation serviceBeanMethodInvocation) {
265    
266                    MethodInterceptorsBag methodInterceptorsBag =
267                            _methodInterceptorBags.get(serviceBeanMethodInvocation);
268    
269                    if (methodInterceptorsBag == null) {
270                            List<MethodInterceptor> methodInterceptors = _getMethodInterceptors(
271                                    serviceBeanMethodInvocation);
272    
273                            methodInterceptorsBag = new MethodInterceptorsBag(
274                                    _classLevelMethodInterceptors, methodInterceptors);
275    
276                            _methodInterceptorBags.put(
277                                    serviceBeanMethodInvocation.toCacheKeyModel(),
278                                    methodInterceptorsBag);
279                    }
280    
281                    serviceBeanMethodInvocation.setMethodInterceptors(
282                            methodInterceptorsBag._mergedMethodInterceptors);
283            }
284    
285            private static Log _log = LogFactoryUtil.getLog(ServiceBeanAopProxy.class);
286    
287            private static Map <ServiceBeanMethodInvocation, MethodInterceptorsBag>
288                    _methodInterceptorBags = new ConcurrentHashMap
289                            <ServiceBeanMethodInvocation, MethodInterceptorsBag>();
290    
291            private AdvisedSupport _advisedSupport;
292            private AdvisorChainFactory _advisorChainFactory;
293            private final List<MethodInterceptor> _classLevelMethodInterceptors;
294            private final List<MethodInterceptor> _fullMethodInterceptors;
295            private boolean _mergeSpringMethodInterceptors;
296    
297            private static class MethodInterceptorsBag {
298    
299                    public MethodInterceptorsBag(
300                            List<MethodInterceptor> classLevelMethodInterceptors,
301                            List<MethodInterceptor> mergedMethodInterceptors) {
302    
303                            _classLevelMethodInterceptors = classLevelMethodInterceptors;
304                            _mergedMethodInterceptors = mergedMethodInterceptors;
305                    }
306    
307                    private List<MethodInterceptor> _classLevelMethodInterceptors;
308                    private List<MethodInterceptor> _mergedMethodInterceptors;
309    
310            }
311    
312    }