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