Skip to content

Commit

Permalink
Allow handling loaded annotations that are loaded by a different clas…
Browse files Browse the repository at this point in the history
…s loader via any class loader that defines any types that are required by an annotation.
  • Loading branch information
raphw committed Aug 11, 2016
1 parent 500df4d commit 42910c5
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 68 deletions.
Expand Up @@ -1139,17 +1139,19 @@ protected AnnotationInvocationHandler(Class<T> annotationType, LinkedHashMap<Met
} }


/** /**
* Creates a proxy instance for the supplied annotation type and values.
*
* @param classLoader The class loader that should be used for loading the annotation's values. * @param classLoader The class loader that should be used for loading the annotation's values.
* @param annotationType The annotation's type. * @param annotationType The annotation's type.
* @param values The values that the annotation contains. * @param values The values that the annotation contains.
* @param <S> The type of the handled annotation. * @param <S> The type of the handled annotation.
* @return An appropriate invocation handler. * @return A proxy for the annotation type and values.
* @throws ClassNotFoundException If the class of an instance that is contained by this annotation could not be found. * @throws ClassNotFoundException If the class of an instance that is contained by this annotation could not be found.
*/ */
public static <S extends Annotation> InvocationHandler of(ClassLoader classLoader, @SuppressWarnings("unchecked")
Class<S> annotationType, public static <S extends Annotation> S of(ClassLoader classLoader,
Map<String, AnnotationDescription.AnnotationValue<?, ?>> values) Class<S> annotationType,
throws ClassNotFoundException { Map<String, AnnotationDescription.AnnotationValue<?, ?>> values) throws ClassNotFoundException {
Method[] declaredMethod = annotationType.getDeclaredMethods(); Method[] declaredMethod = annotationType.getDeclaredMethods();
LinkedHashMap<Method, AnnotationValue.Loaded<?>> loadedValues = new LinkedHashMap<Method, AnnotationValue.Loaded<?>>(); LinkedHashMap<Method, AnnotationValue.Loaded<?>> loadedValues = new LinkedHashMap<Method, AnnotationValue.Loaded<?>>();
for (Method method : declaredMethod) { for (Method method : declaredMethod) {
Expand All @@ -1158,7 +1160,7 @@ public static <S extends Annotation> InvocationHandler of(ClassLoader classLoade
? DefaultValue.of(method) ? DefaultValue.of(method)
: annotationValue.load(classLoader == null ? ClassLoader.getSystemClassLoader() : classLoader)); : annotationValue.load(classLoader == null ? ClassLoader.getSystemClassLoader() : classLoader));
} }
return new AnnotationInvocationHandler<S>(annotationType, loadedValues); return (S) Proxy.newProxyInstance(classLoader, new Class<?>[]{annotationType}, new AnnotationInvocationHandler<S>(annotationType, loadedValues));
} }


/** /**
Expand Down Expand Up @@ -1635,19 +1637,73 @@ public S load() {
} }


@Override @Override
public S load(ClassLoader classLoader) { @SuppressWarnings("unchecked")
ClassLoader thisClassLoader = annotation.getClass().getClassLoader(); public S load(ClassLoader classLoader) throws ClassNotFoundException {
ClassLoader otherClassLoader = classLoader; Class<?> annotationType = Class.forName(TypeDescription.ForLoadedType.getName(annotation.annotationType()), false, classLoader);
while (otherClassLoader != null) { return annotationType == annotation.annotationType()
if (otherClassLoader == thisClassLoader) { ? annotation
break; : AnnotationInvocationHandler.of(classLoader, (Class<S>) annotationType, asValue(annotation));
}

/**
* Extracts the annotation values of an annotation into a property map.
*
* @param annotation The annotation to convert.
* @return A mapping of property names to their annotation value.
*/
private static Map<String, AnnotationValue<?, ?>> asValue(Annotation annotation) {
Map<String, AnnotationValue<?, ?>> annotationValues = new HashMap<String, AnnotationValue<?, ?>>();
for (Method property : annotation.annotationType().getDeclaredMethods()) {
try {
annotationValues.put(property.getName(), asValue(property.getReturnType(), property.invoke(annotation)));
} catch (InvocationTargetException exception) {
throw new IllegalStateException("Cannot read " + property, exception.getCause());
} catch (IllegalAccessException exception) {
throw new IllegalStateException("Cannot access " + property, exception);
} }
otherClassLoader = otherClassLoader.getParent();
} }
if (otherClassLoader != thisClassLoader) { return annotationValues;
throw new IllegalArgumentException(annotation + " is not loaded using " + classLoader); }

/**
* Transforms an annotation property to an annotation value.
*
* @param type The annotation's type.
* @param value The annotations value.
* @return An annotation value representation.
*/
@SuppressWarnings("unchecked")
private static AnnotationValue<?, ?> asValue(Class<?> type, Object value) {
if (Enum.class.isAssignableFrom(type)) {
return AnnotationValue.ForEnumeration.of(new EnumerationDescription.ForLoadedEnumeration((Enum<?>) value));
} else if (Enum[].class.isAssignableFrom(type)) {
Enum<?>[] element = (Enum<?>[]) value;
List<AnnotationValue<?, ?>> annotationValues = new ArrayList<AnnotationValue<?, ?>>(element.length);
for (Enum<?> anElement : element) {
annotationValues.add(AnnotationValue.ForEnumeration.of(new EnumerationDescription.ForLoadedEnumeration(anElement)));
}
return new AnnotationValue.ForComplexArray<Object, Object>(Enum.class, new TypeDescription.ForLoadedType(type), annotationValues);
} else if (Annotation.class.isAssignableFrom(type)) {
return AnnotationValue.ForAnnotation.of(new TypeDescription.ForLoadedType(type), asValue((Annotation) value));
} else if (Annotation[].class.isAssignableFrom(type)) {
Annotation[] element = (Annotation[]) value;
List<AnnotationValue<?, ?>> annotationValues = new ArrayList<AnnotationValue<?, ?>>(element.length);
for (Annotation anElement : element) {
annotationValues.add(AnnotationValue.ForAnnotation.of(new TypeDescription.ForLoadedType(type), asValue(anElement)));
}
return new AnnotationValue.ForComplexArray<Object, Object>(Annotation.class, new TypeDescription.ForLoadedType(type), annotationValues);
} else if (Class.class.isAssignableFrom(type)) {
return AnnotationValue.ForType.of(new TypeDescription.ForLoadedType((Class<?>) value));
} else if (Class[].class.isAssignableFrom(type)) {
Class<?>[] element = (Class<?>[]) value;
List<AnnotationValue<?, ?>> annotationValues = new ArrayList<AnnotationValue<?, ?>>(element.length);
for (Class<?> anElement : element) {
annotationValues.add(AnnotationValue.ForType.of(new TypeDescription.ForLoadedType(anElement)));
}
return new AnnotationValue.ForComplexArray<Object, Object>(Class.class, TypeDescription.CLASS, annotationValues);
} else {
return new AnnotationValue.Trivial<Object>(value);
} }
return load();
} }


@Override @Override
Expand Down Expand Up @@ -1767,11 +1823,8 @@ public S load() throws ClassNotFoundException {
} }


@Override @Override
@SuppressWarnings("unchecked")
public S load(ClassLoader classLoader) throws ClassNotFoundException { public S load(ClassLoader classLoader) throws ClassNotFoundException {
return (S) Proxy.newProxyInstance(classLoader, return AnnotationDescription.AnnotationInvocationHandler.of(classLoader, annotationType, annotationValues);
new Class<?>[]{annotationType},
AnnotationDescription.AnnotationInvocationHandler.of(classLoader, annotationType, annotationValues));
} }


@Override @Override
Expand Down
14 changes: 4 additions & 10 deletions byte-buddy-dep/src/main/java/net/bytebuddy/pool/TypePool.java
Expand Up @@ -25,7 +25,6 @@
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.lang.reflect.GenericSignatureFormatError; import java.lang.reflect.GenericSignatureFormatError;
import java.lang.reflect.Proxy;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
Expand Down Expand Up @@ -601,11 +600,9 @@ public AnnotationDescription resolve() {
public Loaded<Annotation> load(ClassLoader classLoader) throws ClassNotFoundException { public Loaded<Annotation> load(ClassLoader classLoader) throws ClassNotFoundException {
Class<?> type = classLoader.loadClass(annotationToken.getBinaryName()); Class<?> type = classLoader.loadClass(annotationToken.getBinaryName());
if (type.isAnnotation()) { if (type.isAnnotation()) {
return new ForAnnotation.Loaded<Annotation>((Annotation) Proxy.newProxyInstance(classLoader, return new ForAnnotation.Loaded<Annotation>(AnnotationDescription.AnnotationInvocationHandler.of(classLoader,
new Class<?>[]{type}, (Class<? extends Annotation>) type,
AnnotationDescription.AnnotationInvocationHandler.of(classLoader, annotationToken.getValues()));
(Class<? extends Annotation>) type,
annotationToken.getValues())));
} else { } else {
return new ForAnnotation.IncompatibleRuntimeType(type); return new ForAnnotation.IncompatibleRuntimeType(type);
} }
Expand Down Expand Up @@ -6469,11 +6466,8 @@ public S load() throws ClassNotFoundException {
} }


@Override @Override
@SuppressWarnings("unchecked")
public S load(ClassLoader classLoader) throws ClassNotFoundException { public S load(ClassLoader classLoader) throws ClassNotFoundException {
return (S) Proxy.newProxyInstance(classLoader, return AnnotationInvocationHandler.of(classLoader, annotationType, values);
new Class<?>[]{annotationType},
AnnotationInvocationHandler.of(classLoader, annotationType, values));
} }


@Override @Override
Expand Down

0 comments on commit 42910c5

Please sign in to comment.