/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.deltaspike.proxy.api; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import javax.enterprise.inject.spi.BeanManager; import javax.interceptor.InterceptorBinding; import org.apache.deltaspike.core.util.ClassUtils; import org.apache.deltaspike.core.util.ServiceUtils; import org.apache.deltaspike.proxy.spi.ProxyClassGenerator; public abstract class DeltaSpikeProxyFactory { private static final String SUPER_ACCESSOR_METHOD_SUFFIX = "$super"; public static class GeneratorHolder { private static ProxyClassGenerator generator; /** * Setter invoked by OSGi Service Component Runtime. * * @param generator * generator service */ public void setGenerator(ProxyClassGenerator generator) { GeneratorHolder.generator = generator; } } /** * Looks up a unique service implementation. * * @return ProxyClassGenerator service */ private static ProxyClassGenerator lookupService() { if (GeneratorHolder.generator == null) { List<ProxyClassGenerator> proxyClassGeneratorList = ServiceUtils .loadServiceImplementations(ProxyClassGenerator.class); if (proxyClassGeneratorList.size() != 1) { throw new IllegalStateException(proxyClassGeneratorList.size() + " implementations of " + ProxyClassGenerator.class.getName() + " found. Expected exactly one implementation."); } GeneratorHolder.generator = proxyClassGeneratorList.get(0); } return GeneratorHolder.generator; } public <T> Class<T> resolveAlreadyDefinedProxyClass(Class<T> targetClass) { Class<T> proxyClass = ClassUtils.tryToLoadClassForName(constructProxyClassName(targetClass), targetClass, targetClass.getClassLoader()); return proxyClass; } public <T> Class<T> getProxyClass(BeanManager beanManager, Class<T> targetClass) { // check if a proxy is already defined for this class Class<T> proxyClass = resolveAlreadyDefinedProxyClass(targetClass); if (proxyClass == null) { proxyClass = createProxyClass(beanManager, targetClass.getClassLoader(), targetClass); } return proxyClass; } private synchronized <T> Class<T> createProxyClass(BeanManager beanManager, ClassLoader classLoader, Class<T> targetClass) { Class<T> proxyClass = resolveAlreadyDefinedProxyClass(targetClass); if (proxyClass == null) { ArrayList<Method> allMethods = collectAllMethods(targetClass); ArrayList<Method> interceptMethods = filterInterceptMethods(targetClass, allMethods); ArrayList<Method> delegateMethods = getDelegateMethods(targetClass, allMethods); // check if a interceptor is defined on class level. if not, skip interceptor methods if (interceptMethods != null && !interceptMethods.isEmpty() && !containsInterceptorBinding(beanManager, targetClass.getDeclaredAnnotations())) { // loop every method and check if a interceptor is defined on the method -> otherwise don't overwrite // interceptMethods Iterator<Method> iterator = interceptMethods.iterator(); while (iterator.hasNext()) { Method method = iterator.next(); if (!containsInterceptorBinding(beanManager, method.getDeclaredAnnotations())) { iterator.remove(); } } } ProxyClassGenerator proxyClassGenerator = lookupService(); proxyClass = proxyClassGenerator.generateProxyClass(classLoader, targetClass, getProxyClassSuffix(), SUPER_ACCESSOR_METHOD_SUFFIX, getAdditionalInterfacesToImplement(targetClass), delegateMethods == null ? new Method[0] : delegateMethods.toArray(new Method[delegateMethods.size()]), interceptMethods == null ? new Method[0] : interceptMethods.toArray(new Method[interceptMethods.size()])); } return proxyClass; } protected boolean containsInterceptorBinding(BeanManager beanManager, Annotation[] annotations) { for (Annotation annotation : annotations) { Class<? extends Annotation> annotationType = annotation.annotationType(); if (annotationType.isAnnotationPresent(InterceptorBinding.class)) { return true; } if (beanManager.isStereotype(annotationType)) { boolean containsInterceptorBinding = containsInterceptorBinding( beanManager, annotationType.getDeclaredAnnotations()); if (containsInterceptorBinding) { return true; } } } return false; } protected String constructProxyClassName(Class<?> clazz) { return clazz.getName() + getProxyClassSuffix(); } protected static String constructSuperAccessorMethodName(Method method) { return method.getName() + SUPER_ACCESSOR_METHOD_SUFFIX; } public static Method getSuperAccessorMethod(Object proxy, Method method) throws NoSuchMethodException { return proxy.getClass().getMethod( constructSuperAccessorMethodName(method), method.getParameterTypes()); } /** * Checks if the given class is DS proxy class. * * @param clazz * @return */ public boolean isProxyClass(Class<?> clazz) { return clazz.getName().endsWith(getProxyClassSuffix()); } protected boolean hasSameSignature(Method a, Method b) { return a.getName().equals(b.getName()) && a.getReturnType().equals(b.getReturnType()) && Arrays.equals(a.getParameterTypes(), b.getParameterTypes()); } protected boolean ignoreMethod(Method method, List<Method> methods) { // we have no interest in generics bridge methods if (method.isBridge()) { return true; } // we do not proxy finalize() if ("finalize".equals(method.getName())) { return true; } // same method... if (methods.contains(method)) { return true; } // check if a method with the same signature is already available for (Method currentMethod : methods) { if (hasSameSignature(currentMethod, method)) { return true; } } return false; } protected ArrayList<Method> collectAllMethods(Class<?> clazz) { ArrayList<Method> methods = new ArrayList<Method>(); for (Method method : clazz.getDeclaredMethods()) { if (!ignoreMethod(method, methods)) { methods.add(method); } } for (Method method : clazz.getMethods()) { if (!ignoreMethod(method, methods)) { methods.add(method); } } // collect methods from abstract super classes... Class currentSuperClass = clazz.getSuperclass(); while (currentSuperClass != null) { if (Modifier.isAbstract(currentSuperClass.getModifiers())) { for (Method method : currentSuperClass.getDeclaredMethods()) { if (!ignoreMethod(method, methods)) { methods.add(method); } } for (Method method : currentSuperClass.getMethods()) { if (!ignoreMethod(method, methods)) { methods.add(method); } } } currentSuperClass = currentSuperClass.getSuperclass(); } // sort out somewhere implemented abstract methods Class currentClass = clazz; while (currentClass != null) { Iterator<Method> methodIterator = methods.iterator(); while (methodIterator.hasNext()) { Method method = methodIterator.next(); if (Modifier.isAbstract(method.getModifiers())) { try { Method foundMethod = currentClass.getMethod(method.getName(), method.getParameterTypes()); // if method is implementent in the current class -> remove it if (foundMethod != null && !Modifier.isAbstract(foundMethod.getModifiers())) { methodIterator.remove(); } } catch (Exception e) { // ignore... } } } currentClass = currentClass.getSuperclass(); } return methods; } protected ArrayList<Method> filterInterceptMethods(Class<?> targetClass, ArrayList<Method> allMethods) { ArrayList<Method> methods = new ArrayList<Method>(); Iterator<Method> it = allMethods.iterator(); while (it.hasNext()) { Method method = it.next(); if (Modifier.isPublic(method.getModifiers()) && !Modifier.isFinal(method.getModifiers()) && !Modifier.isAbstract(method.getModifiers())) { methods.add(method); } } return methods; } protected Class<?>[] getAdditionalInterfacesToImplement(Class<?> targetClass) { return null; } protected abstract ArrayList<Method> getDelegateMethods(Class<?> targetClass, ArrayList<Method> allMethods); protected abstract String getProxyClassSuffix(); }