From f8d895e83ca6da1d9adc6a3211bd216d7b6cdc04 Mon Sep 17 00:00:00 2001 From: Rafael Winterhalter Date: Thu, 11 Aug 2016 22:14:26 +0200 Subject: [PATCH] Fixed annotation transfer if preparing from another class loader. --- .../annotation/AnnotationDescription.java | 89 +++++++------------ .../enumeration/EnumerationDescription.java | 7 +- .../java/net/bytebuddy/pool/TypePool.java | 16 +--- .../AbstractAnnotationDescriptionTest.java | 2 +- ...criptionAbstractPreparedExceptionTest.java | 10 --- ...dedAnnotationDifferentClassLoaderTest.java | 37 ++++++++ ...ionDescriptionForLoadedAnnotationTest.java | 11 +-- 7 files changed, 75 insertions(+), 97 deletions(-) create mode 100644 byte-buddy-dep/src/test/java/net/bytebuddy/description/annotation/AnnotationDescriptionForLoadedAnnotationDifferentClassLoaderTest.java diff --git a/byte-buddy-dep/src/main/java/net/bytebuddy/description/annotation/AnnotationDescription.java b/byte-buddy-dep/src/main/java/net/bytebuddy/description/annotation/AnnotationDescription.java index c5d88d83421..7c7af9bd42b 100644 --- a/byte-buddy-dep/src/main/java/net/bytebuddy/description/annotation/AnnotationDescription.java +++ b/byte-buddy-dep/src/main/java/net/bytebuddy/description/annotation/AnnotationDescription.java @@ -373,8 +373,8 @@ public AnnotationDescription resolve() { @Override public AnnotationValue.Loaded load(ClassLoader classLoader) throws ClassNotFoundException { @SuppressWarnings("unchecked") - Class annotationType = (Class) classLoader.loadClass(annotationDescription.getAnnotationType().getName()); - return new Loaded(annotationDescription.prepare(annotationType).load(classLoader)); + Class annotationType = (Class) Class.forName(annotationDescription.getAnnotationType().getName(), false, classLoader); + return new Loaded(annotationDescription.prepare(annotationType).load()); } @Override @@ -528,7 +528,7 @@ public EnumerationDescription resolve() { @Override public AnnotationValue.Loaded load(ClassLoader classLoader) throws ClassNotFoundException { @SuppressWarnings("unchecked") - Class enumerationType = (Class) classLoader.loadClass(enumerationDescription.getEnumerationType().getName()); + Class enumerationType = (Class) Class.forName(enumerationDescription.getEnumerationType().getName(), false, classLoader); return new Loaded(enumerationDescription.load(enumerationType)); } @@ -921,7 +921,7 @@ public AnnotationValue.Loaded load(ClassLoader classLoader) throws ClassNot for (AnnotationValue value : annotationValues) { loadedValues.add(value.load(classLoader)); } - return new Loaded((Class) classLoader.loadClass(componentType.getName()), loadedValues); + return new Loaded((Class) Class.forName(componentType.getName(), false, classLoader), loadedValues); } @Override @@ -1067,15 +1067,6 @@ interface Loadable extends AnnotationDescription { */ S load() throws ClassNotFoundException; - /** - * Loads this annotation description. This causes all classes referenced by the annotation value to be loaded. - * - * @param classLoader The class loader to be used for loading the annotation's linked types. - * @return A loaded version of this annotation description. - * @throws java.lang.ClassNotFoundException If any linked classes of the annotation cannot be loaded. - */ - S load(ClassLoader classLoader) throws ClassNotFoundException; - /** * Loads this annotation description. This causes all classes referenced by the annotation value to be loaded. * Without specifying a class loader, the annotation's class loader which was used to prepare this instance @@ -1084,15 +1075,6 @@ interface Loadable extends AnnotationDescription { * @return A loaded version of this annotation description. */ S loadSilent(); - - /** - * Loads this annotation description. This causes all classes referenced by the annotation value to be loaded. - * Any {@link java.lang.ClassNotFoundException} is wrapped in an {@link java.lang.IllegalStateException}. - * - * @param classLoader The class loader to be used for loading the annotation's linked types. - * @return A loaded version of this annotation description. - */ - S loadSilent(ClassLoader classLoader); } /** @@ -1557,15 +1539,6 @@ public S loadSilent() { throw new IllegalStateException(ERROR_MESSAGE, exception); } } - - @Override - public S loadSilent(ClassLoader classLoader) { - try { - return load(classLoader); - } catch (ClassNotFoundException exception) { - throw new IllegalStateException(ERROR_MESSAGE, exception); - } - } } } @@ -1581,13 +1554,21 @@ class ForLoadedAnnotation extends AbstractBase.ForPrepared */ private final S annotation; + private final Class annotationType; + /** * Creates a new annotation description for a loaded annotation. * * @param annotation The annotation to represent. */ + @SuppressWarnings("unchecked") protected ForLoadedAnnotation(S annotation) { + this(annotation, (Class) annotation.annotationType()); + } + + protected ForLoadedAnnotation(S annotation, Class annotationType) { this.annotation = annotation; + this.annotationType = annotationType; } /** @@ -1632,17 +1613,10 @@ public static Object describe(Object value, TypeDescription typeDescription) { } @Override - public S load() { - return annotation; - } - - @Override - @SuppressWarnings("unchecked") - public S load(ClassLoader classLoader) throws ClassNotFoundException { - Class annotationType = Class.forName(TypeDescription.ForLoadedType.getName(annotation.annotationType()), false, classLoader); + public S load() throws ClassNotFoundException { return annotationType == annotation.annotationType() ? annotation - : AnnotationInvocationHandler.of(classLoader, (Class) annotationType, asValue(annotation)); + : AnnotationInvocationHandler.of(annotationType.getClassLoader(), annotationType, asValue(annotation)); } /** @@ -1674,33 +1648,37 @@ public S load(ClassLoader classLoader) throws ClassNotFoundException { */ @SuppressWarnings("unchecked") private static AnnotationValue asValue(Class type, Object value) { + // Because enums can implement annotation interfaces, the enum property needs to be checked first. 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> annotationValues = new ArrayList>(element.length); + EnumerationDescription[] enumerationDescription = new EnumerationDescription[element.length]; + int index = 0; for (Enum anElement : element) { - annotationValues.add(AnnotationValue.ForEnumeration.of(new EnumerationDescription.ForLoadedEnumeration(anElement))); + enumerationDescription[index++] = new EnumerationDescription.ForLoadedEnumeration(anElement); } - return new AnnotationValue.ForComplexArray(Enum.class, new TypeDescription.ForLoadedType(type), annotationValues); + return AnnotationValue.ForComplexArray.of(new TypeDescription.ForLoadedType(type.getComponentType()), enumerationDescription); } 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> annotationValues = new ArrayList>(element.length); + AnnotationDescription[] annotationDescription = new AnnotationDescription[element.length]; + int index = 0; for (Annotation anElement : element) { - annotationValues.add(AnnotationValue.ForAnnotation.of(new TypeDescription.ForLoadedType(type), asValue(anElement))); + annotationDescription[index++] = new AnnotationDescription.Latent(new TypeDescription.ForLoadedType(type.getComponentType()), asValue(anElement)); } - return new AnnotationValue.ForComplexArray(Annotation.class, new TypeDescription.ForLoadedType(type), annotationValues); + return AnnotationValue.ForComplexArray.of(new TypeDescription.ForLoadedType(type.getComponentType()), annotationDescription); } 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> annotationValues = new ArrayList>(element.length); + TypeDescription[] typeDescription = new TypeDescription[element.length]; + int index = 0; for (Class anElement : element) { - annotationValues.add(AnnotationValue.ForType.of(new TypeDescription.ForLoadedType(anElement))); + typeDescription[index++] = new TypeDescription.ForLoadedType(anElement); } - return new AnnotationValue.ForComplexArray(Class.class, TypeDescription.CLASS, annotationValues); + return AnnotationValue.ForComplexArray.of(typeDescription); } else { return new AnnotationValue.Trivial(value); } @@ -1732,10 +1710,12 @@ public Object getValue(MethodDescription.InDefinedShape methodDescription) { @Override @SuppressWarnings("unchecked") public Loadable prepare(Class annotationType) { - if (annotation.annotationType() != annotationType && !annotation.annotationType().getName().equals(annotationType.getName())) { + if (!annotation.annotationType().getName().equals(annotationType.getName())) { throw new IllegalArgumentException(annotation + " does not represent " + annotationType); } - return (Loadable) this; + return annotationType == annotation.annotationType() + ? (Loadable) this + : new ForLoadedAnnotation((T) annotation, annotationType); } @Override @@ -1819,12 +1799,7 @@ protected Loadable(Class annotationType) { @Override public S load() throws ClassNotFoundException { - return load(annotationType.getClassLoader()); - } - - @Override - public S load(ClassLoader classLoader) throws ClassNotFoundException { - return AnnotationDescription.AnnotationInvocationHandler.of(classLoader, annotationType, annotationValues); + return AnnotationDescription.AnnotationInvocationHandler.of(annotationType.getClassLoader(), annotationType, annotationValues); } @Override diff --git a/byte-buddy-dep/src/main/java/net/bytebuddy/description/enumeration/EnumerationDescription.java b/byte-buddy-dep/src/main/java/net/bytebuddy/description/enumeration/EnumerationDescription.java index 1c77440ed47..31e7c936229 100644 --- a/byte-buddy-dep/src/main/java/net/bytebuddy/description/enumeration/EnumerationDescription.java +++ b/byte-buddy-dep/src/main/java/net/bytebuddy/description/enumeration/EnumerationDescription.java @@ -103,10 +103,9 @@ public TypeDescription getEnumerationType() { @Override @SuppressWarnings("unchecked") public > T load(Class type) { - if (value.getDeclaringClass() != type) { - throw new IllegalArgumentException(type + " does not represent " + value); - } - return (T) value; + return value.getDeclaringClass() == type + ? (T) value + : Enum.valueOf(type, value.name()); } } diff --git a/byte-buddy-dep/src/main/java/net/bytebuddy/pool/TypePool.java b/byte-buddy-dep/src/main/java/net/bytebuddy/pool/TypePool.java index b9aa0efdefb..eec70df07a3 100644 --- a/byte-buddy-dep/src/main/java/net/bytebuddy/pool/TypePool.java +++ b/byte-buddy-dep/src/main/java/net/bytebuddy/pool/TypePool.java @@ -6462,12 +6462,7 @@ private Loadable(TypePool typePool, Class annotationType, Map { @Override @@ -25,11 +20,6 @@ public Annotation load() throws ClassNotFoundException { throw new ClassNotFoundException(); } - @Override - public Annotation load(ClassLoader classLoader) throws ClassNotFoundException { - throw new ClassNotFoundException(); - } - @Override public Object getValue(MethodDescription.InDefinedShape methodDescription) { throw new UnsupportedOperationException(); diff --git a/byte-buddy-dep/src/test/java/net/bytebuddy/description/annotation/AnnotationDescriptionForLoadedAnnotationDifferentClassLoaderTest.java b/byte-buddy-dep/src/test/java/net/bytebuddy/description/annotation/AnnotationDescriptionForLoadedAnnotationDifferentClassLoaderTest.java new file mode 100644 index 00000000000..47dc9feef05 --- /dev/null +++ b/byte-buddy-dep/src/test/java/net/bytebuddy/description/annotation/AnnotationDescriptionForLoadedAnnotationDifferentClassLoaderTest.java @@ -0,0 +1,37 @@ +package net.bytebuddy.description.annotation; + +import net.bytebuddy.dynamic.loading.ByteArrayClassLoader; +import net.bytebuddy.dynamic.loading.ClassLoadingStrategy; +import net.bytebuddy.dynamic.loading.PackageDefinitionStrategy; +import net.bytebuddy.test.utility.ClassFileExtraction; +import org.junit.Before; + +import java.lang.annotation.Annotation; + +public class AnnotationDescriptionForLoadedAnnotationDifferentClassLoaderTest extends AbstractAnnotationDescriptionTest { + + private ClassLoader classLoader; + + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + classLoader = new ByteArrayClassLoader(ClassLoadingStrategy.BOOTSTRAP_LOADER, + ClassFileExtraction.of(Sample.class, SampleDefault.class, Other.class, SampleEnumeration.class, ExplicitTarget.class), + null, + ByteArrayClassLoader.PersistenceHandler.LATENT, + PackageDefinitionStrategy.Trivial.INSTANCE); + } + + @Override + @SuppressWarnings("unchecked") + protected AnnotationDescription describe(Annotation annotation, Class declaringType) { + try { + return AnnotationDescription.ForLoadedAnnotation.of(AnnotationDescription.ForLoadedAnnotation.of(annotation) + .prepare((Class) classLoader.loadClass(annotation.annotationType().getName())) + .load()); + } catch (ClassNotFoundException exception) { + throw new AssertionError(exception); + } + } +} diff --git a/byte-buddy-dep/src/test/java/net/bytebuddy/description/annotation/AnnotationDescriptionForLoadedAnnotationTest.java b/byte-buddy-dep/src/test/java/net/bytebuddy/description/annotation/AnnotationDescriptionForLoadedAnnotationTest.java index b2f6e28cfce..bfc0a7ca685 100644 --- a/byte-buddy-dep/src/test/java/net/bytebuddy/description/annotation/AnnotationDescriptionForLoadedAnnotationTest.java +++ b/byte-buddy-dep/src/test/java/net/bytebuddy/description/annotation/AnnotationDescriptionForLoadedAnnotationTest.java @@ -1,7 +1,6 @@ package net.bytebuddy.description.annotation; import net.bytebuddy.description.method.MethodDescription; -import net.bytebuddy.dynamic.loading.ClassLoadingStrategy; import org.junit.Ignore; import org.junit.Test; @@ -9,8 +8,6 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Method; -import java.net.URL; -import java.net.URLClassLoader; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; @@ -48,13 +45,7 @@ public void testInoperational() throws Exception { @Test(expected = IllegalArgumentException.class) @Ignore("Rewrite test to consider different class loader.") public void testLoadAnnotationWrongClassLoader() throws Exception { - describe(Carrier.class.getAnnotation(PrivateAnnotation.class), Carrier.class).prepare(PrivateAnnotation.class).load(ClassLoadingStrategy.BOOTSTRAP_LOADER); - } - - @Test - public void testLoadAnnotationSubClassLoader() throws Exception { - assertThat(describe(Carrier.class.getAnnotation(PrivateAnnotation.class), Carrier.class).prepare(PrivateAnnotation.class) - .load(new URLClassLoader(new URL[0], getClass().getClassLoader())), is(Carrier.class.getAnnotation(PrivateAnnotation.class))); + describe(Carrier.class.getAnnotation(PrivateAnnotation.class), Carrier.class).prepare(PrivateAnnotation.class).load(); } @Retention(RetentionPolicy.RUNTIME)