001    /**
002     * Copyright (c) 2000-2013 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            @Override
170            public Object getProxy() {
171                    return getProxy(ClassUtils.getDefaultClassLoader());
172            }
173    
174            @Override
175            public Object getProxy(ClassLoader classLoader) {
176                    Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(
177                            _advisedSupport);
178    
179                    InvocationHandler invocationHandler = _pacl.getInvocationHandler(
180                            this, _advisedSupport);
181    
182                    return ProxyUtil.newProxyInstance(
183                            classLoader, proxiedInterfaces, invocationHandler);
184            }
185    
186            @Override
187            public Object invoke(Object proxy, Method method, Object[] arguments)
188                    throws Throwable {
189    
190                    TargetSource targetSource = _advisedSupport.getTargetSource();
191    
192                    Object target = null;
193    
194                    try {
195                            Class<?> targetClass = null;
196    
197                            target = targetSource.getTarget();
198    
199                            if (target != null) {
200                                    targetClass = target.getClass();
201                            }
202    
203                            ServiceBeanMethodInvocation serviceBeanMethodInvocation =
204                                    new ServiceBeanMethodInvocation(
205                                            target, targetClass, method, arguments);
206    
207                            Skip skip = ServiceMethodAnnotationCache.get(
208                                    serviceBeanMethodInvocation, Skip.class, null);
209    
210                            if (skip != null) {
211                                    serviceBeanMethodInvocation.setMethodInterceptors(
212                                            Collections.<MethodInterceptor>emptyList());
213                            }
214                            else {
215                                    _setMethodInterceptors(serviceBeanMethodInvocation);
216                            }
217    
218                            return serviceBeanMethodInvocation.proceed();
219                    }
220                    finally {
221                            if ((target != null) && !targetSource.isStatic()) {
222                                    targetSource.releaseTarget(target);
223                            }
224                    }
225            }
226    
227            private List<MethodInterceptor> _getMethodInterceptors(
228                    ServiceBeanMethodInvocation serviceBeanMethodInvocation) {
229    
230                    List<MethodInterceptor> methodInterceptors =
231                            new ArrayList<MethodInterceptor>(_fullMethodInterceptors);
232    
233                    if (!_mergeSpringMethodInterceptors) {
234                            return methodInterceptors;
235                    }
236    
237                    List<Object> list =
238                            _advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
239                                    _advisedSupport, serviceBeanMethodInvocation.getMethod(),
240                                    serviceBeanMethodInvocation.getTargetClass());
241    
242                    Iterator<Object> itr = list.iterator();
243    
244                    while (itr.hasNext()) {
245                            Object obj = itr.next();
246    
247                            if (obj instanceof MethodInterceptor) {
248                                    continue;
249                            }
250    
251                            if (_log.isWarnEnabled()) {
252                                    _log.warn(
253                                            "Skipping unsupported interceptor type " + obj.getClass());
254                            }
255    
256                            itr.remove();
257                    }
258    
259                    if (list.isEmpty()) {
260                            return methodInterceptors;
261                    }
262    
263                    for (Object object : list) {
264                            methodInterceptors.add((MethodInterceptor)object);
265                    }
266    
267                    return methodInterceptors;
268            }
269    
270            private void _setMethodInterceptors(
271                    ServiceBeanMethodInvocation serviceBeanMethodInvocation) {
272    
273                    MethodInterceptorsBag methodInterceptorsBag =
274                            _methodInterceptorBags.get(serviceBeanMethodInvocation);
275    
276                    if (methodInterceptorsBag == null) {
277                            List<MethodInterceptor> methodInterceptors = _getMethodInterceptors(
278                                    serviceBeanMethodInvocation);
279    
280                            methodInterceptorsBag = new MethodInterceptorsBag(
281                                    _classLevelMethodInterceptors, methodInterceptors);
282    
283                            _methodInterceptorBags.put(
284                                    serviceBeanMethodInvocation.toCacheKeyModel(),
285                                    methodInterceptorsBag);
286                    }
287    
288                    serviceBeanMethodInvocation.setMethodInterceptors(
289                            methodInterceptorsBag._mergedMethodInterceptors);
290            }
291    
292            private static Log _log = LogFactoryUtil.getLog(ServiceBeanAopProxy.class);
293    
294            private static Map <ServiceBeanMethodInvocation, MethodInterceptorsBag>
295                    _methodInterceptorBags = new ConcurrentHashMap
296                            <ServiceBeanMethodInvocation, MethodInterceptorsBag>();
297            private static PACL _pacl = new NoPACL();
298    
299            private AdvisedSupport _advisedSupport;
300            private AdvisorChainFactory _advisorChainFactory;
301            private final List<MethodInterceptor> _classLevelMethodInterceptors;
302            private final List<MethodInterceptor> _fullMethodInterceptors;
303            private boolean _mergeSpringMethodInterceptors;
304    
305            private static class MethodInterceptorsBag {
306    
307                    public MethodInterceptorsBag(
308                            List<MethodInterceptor> classLevelMethodInterceptors,
309                            List<MethodInterceptor> mergedMethodInterceptors) {
310    
311                            _classLevelMethodInterceptors = classLevelMethodInterceptors;
312                            _mergedMethodInterceptors = mergedMethodInterceptors;
313                    }
314    
315                    private List<MethodInterceptor> _classLevelMethodInterceptors;
316                    private List<MethodInterceptor> _mergedMethodInterceptors;
317    
318            }
319    
320            private static class NoPACL implements PACL {
321    
322                    @Override
323                    public InvocationHandler getInvocationHandler(
324                            InvocationHandler invocationHandler,
325                            AdvisedSupport advisedSupport) {
326    
327                            return invocationHandler;
328                    }
329    
330            }
331    
332            public static interface PACL {
333    
334                    public InvocationHandler getInvocationHandler(
335                            InvocationHandler invocationHandler, AdvisedSupport advisedSupport);
336    
337            }
338    
339    }