Skip to content

Commit

Permalink
Fixed annotation transfer if preparing from another class loader.
Browse files Browse the repository at this point in the history
  • Loading branch information
raphw committed Aug 11, 2016
1 parent d472fa1 commit f8d895e
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 97 deletions.
Expand Up @@ -373,8 +373,8 @@ public AnnotationDescription resolve() {
@Override
public AnnotationValue.Loaded<U> load(ClassLoader classLoader) throws ClassNotFoundException {
@SuppressWarnings("unchecked")
Class<U> annotationType = (Class<U>) classLoader.loadClass(annotationDescription.getAnnotationType().getName());
return new Loaded<U>(annotationDescription.prepare(annotationType).load(classLoader));
Class<U> annotationType = (Class<U>) Class.forName(annotationDescription.getAnnotationType().getName(), false, classLoader);
return new Loaded<U>(annotationDescription.prepare(annotationType).load());
}

@Override
Expand Down Expand Up @@ -528,7 +528,7 @@ public EnumerationDescription resolve() {
@Override
public AnnotationValue.Loaded<U> load(ClassLoader classLoader) throws ClassNotFoundException {
@SuppressWarnings("unchecked")
Class<U> enumerationType = (Class<U>) classLoader.loadClass(enumerationDescription.getEnumerationType().getName());
Class<U> enumerationType = (Class<U>) Class.forName(enumerationDescription.getEnumerationType().getName(), false, classLoader);
return new Loaded<U>(enumerationDescription.load(enumerationType));
}

Expand Down Expand Up @@ -921,7 +921,7 @@ public AnnotationValue.Loaded<V[]> load(ClassLoader classLoader) throws ClassNot
for (AnnotationValue<?, ?> value : annotationValues) {
loadedValues.add(value.load(classLoader));
}
return new Loaded<V>((Class<V>) classLoader.loadClass(componentType.getName()), loadedValues);
return new Loaded<V>((Class<V>) Class.forName(componentType.getName(), false, classLoader), loadedValues);
}

@Override
Expand Down Expand Up @@ -1067,15 +1067,6 @@ interface Loadable<S extends Annotation> 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
Expand All @@ -1084,15 +1075,6 @@ interface Loadable<S extends Annotation> 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);
}

/**
Expand Down Expand Up @@ -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);
}
}
}
}

Expand All @@ -1581,13 +1554,21 @@ class ForLoadedAnnotation<S extends Annotation> extends AbstractBase.ForPrepared
*/
private final S annotation;

private final Class<S> 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<S>) annotation.annotationType());
}

protected ForLoadedAnnotation(S annotation, Class<S> annotationType) {
this.annotation = annotation;
this.annotationType = annotationType;
}

/**
Expand Down Expand Up @@ -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<S>) annotationType, asValue(annotation));
: AnnotationInvocationHandler.of(annotationType.getClassLoader(), annotationType, asValue(annotation));
}

/**
Expand Down Expand Up @@ -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.<Enum>of(new EnumerationDescription.ForLoadedEnumeration((Enum<?>) value));
} else if (Enum[].class.isAssignableFrom(type)) {
Enum<?>[] element = (Enum<?>[]) value;
List<AnnotationValue<?, ?>> annotationValues = new ArrayList<AnnotationValue<?, ?>>(element.length);
EnumerationDescription[] enumerationDescription = new EnumerationDescription[element.length];
int index = 0;
for (Enum<?> anElement : element) {
annotationValues.add(AnnotationValue.ForEnumeration.<Enum>of(new EnumerationDescription.ForLoadedEnumeration(anElement)));
enumerationDescription[index++] = new EnumerationDescription.ForLoadedEnumeration(anElement);
}
return new AnnotationValue.ForComplexArray<Object, Object>(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.<Annotation>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);
AnnotationDescription[] annotationDescription = new AnnotationDescription[element.length];
int index = 0;
for (Annotation anElement : element) {
annotationValues.add(AnnotationValue.ForAnnotation.<Annotation>of(new TypeDescription.ForLoadedType(type), asValue(anElement)));
annotationDescription[index++] = new AnnotationDescription.Latent(new TypeDescription.ForLoadedType(type.getComponentType()), asValue(anElement));
}
return new AnnotationValue.ForComplexArray<Object, Object>(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.<Class>of(new TypeDescription.ForLoadedType((Class<?>) value));
} else if (Class[].class.isAssignableFrom(type)) {
Class<?>[] element = (Class<?>[]) value;
List<AnnotationValue<?, ?>> annotationValues = new ArrayList<AnnotationValue<?, ?>>(element.length);
TypeDescription[] typeDescription = new TypeDescription[element.length];
int index = 0;
for (Class<?> anElement : element) {
annotationValues.add(AnnotationValue.ForType.<Class>of(new TypeDescription.ForLoadedType(anElement)));
typeDescription[index++] = new TypeDescription.ForLoadedType(anElement);
}
return new AnnotationValue.ForComplexArray<Object, Object>(Class.class, TypeDescription.CLASS, annotationValues);
return AnnotationValue.ForComplexArray.of(typeDescription);
} else {
return new AnnotationValue.Trivial<Object>(value);
}
Expand Down Expand Up @@ -1732,10 +1710,12 @@ public Object getValue(MethodDescription.InDefinedShape methodDescription) {
@Override
@SuppressWarnings("unchecked")
public <T extends Annotation> Loadable<T> prepare(Class<T> 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<T>) this;
return annotationType == annotation.annotationType()
? (Loadable<T>) this
: new ForLoadedAnnotation<T>((T) annotation, annotationType);
}

@Override
Expand Down Expand Up @@ -1819,12 +1799,7 @@ protected Loadable(Class<S> 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
Expand Down
Expand Up @@ -103,10 +103,9 @@ public TypeDescription getEnumerationType() {
@Override
@SuppressWarnings("unchecked")
public <T extends Enum<T>> T load(Class<T> 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());
}
}

Expand Down
16 changes: 1 addition & 15 deletions byte-buddy-dep/src/main/java/net/bytebuddy/pool/TypePool.java
Expand Up @@ -6462,12 +6462,7 @@ private Loadable(TypePool typePool, Class<S> annotationType, Map<String, Annotat

@Override
public S load() throws ClassNotFoundException {
return load(annotationType.getClassLoader());
}

@Override
public S load(ClassLoader classLoader) throws ClassNotFoundException {
return AnnotationInvocationHandler.of(classLoader, annotationType, values);
return AnnotationInvocationHandler.of(annotationType.getClassLoader(), annotationType, values);
}

@Override
Expand All @@ -6478,15 +6473,6 @@ public S loadSilent() {
throw new IllegalStateException(ForLoadedAnnotation.ERROR_MESSAGE, exception);
}
}

@Override
public S loadSilent(ClassLoader classLoader) {
try {
return load(classLoader);
} catch (ClassNotFoundException exception) {
throw new IllegalStateException(ForLoadedAnnotation.ERROR_MESSAGE, exception);
}
}
}
}

Expand Down
Expand Up @@ -679,7 +679,7 @@ private static class OtherEnumerationCarrier {

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
private @interface ExplicitTarget {
protected @interface ExplicitTarget {

@ExplicitTarget
class Carrier {
Expand Down
Expand Up @@ -13,23 +13,13 @@ public void testThrowWithoutClassLoader() throws Exception {
new PseudoDescription().loadSilent();
}

@Test(expected = IllegalStateException.class)
public void testThrowWithClassLoader() throws Exception {
new PseudoDescription().loadSilent(getClass().getClassLoader());
}

private static class PseudoDescription extends AnnotationDescription.AbstractBase.ForPrepared<Annotation> {

@Override
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();
Expand Down
@@ -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<Annotation>) classLoader.loadClass(annotation.annotationType().getName()))
.load());
} catch (ClassNotFoundException exception) {
throw new AssertionError(exception);
}
}
}
@@ -1,16 +1,13 @@
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;

import java.lang.annotation.Annotation;
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;
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit f8d895e

Please sign in to comment.