blob: b30c3986364782f46058b8ec6a881ebed3b2526b [file] [log] [blame]
/*
* 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.size() > 0
&& !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();
}