From fdacda8b01d9dcc478026bc450b07f2d06300837 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Sun, 10 Mar 2019 13:56:33 -0700 Subject: [PATCH] Add AnnotationIntrospectionFailureTests Add a new test class to help cover annotation introspection failure handling. These tests were previously missing and are important to ensure that annotation util code changes don't introduce regressions. See gh-21697 --- .../AnnotationIntrospectionFailureTests.java | 141 ++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 spring-core/src/test/java/org/springframework/core/annotation/AnnotationIntrospectionFailureTests.java diff --git a/spring-core/src/test/java/org/springframework/core/annotation/AnnotationIntrospectionFailureTests.java b/spring-core/src/test/java/org/springframework/core/annotation/AnnotationIntrospectionFailureTests.java new file mode 100644 index 0000000000..6401b64903 --- /dev/null +++ b/spring-core/src/test/java/org/springframework/core/annotation/AnnotationIntrospectionFailureTests.java @@ -0,0 +1,141 @@ +/* + * Copyright 2002-2019 the original author or authors. + * + * Licensed 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.springframework.core.annotation; + +import java.lang.annotation.Annotation; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.reflect.Method; + +import org.junit.Test; + +import org.springframework.core.OverridingClassLoader; +import org.springframework.util.ClassUtils; +import org.springframework.util.ReflectionUtils; + +import static org.assertj.core.api.Assertions.*; + +/** + * Tests that trigger annotation introspection failures and ensure that they are + * dealt with correctly. + * + * @author Phillip Webb + * @since 5.2 + * @see AnnotationUtils + * @see AnnotatedElementUtils + */ +public class AnnotationIntrospectionFailureTests { + + @Test + public void filteredTypeThrowsTypeNotPresentException() throws Exception { + FilteringClassLoader classLoader = new FilteringClassLoader( + getClass().getClassLoader()); + Class withExampleAnnotation = ClassUtils.forName( + WithExampleAnnotation.class.getName(), classLoader); + Annotation annotation = withExampleAnnotation.getAnnotations()[0]; + Method method = annotation.annotationType().getMethod("value"); + method.setAccessible(true); + assertThatExceptionOfType(TypeNotPresentException.class).isThrownBy(() -> { + ReflectionUtils.invokeMethod(method, annotation); + }).withCauseInstanceOf(ClassNotFoundException.class); + } + + @Test + @SuppressWarnings("unchecked") + public void filteredTypeInMetaAnnotationWhenUsingAnnotatedElementUtilsHandlesException() throws Exception { + FilteringClassLoader classLoader = new FilteringClassLoader( + getClass().getClassLoader()); + Class withExampleMetaAnnotation = ClassUtils.forName( + WithExampleMetaAnnotation.class.getName(), classLoader); + Class exampleAnnotationClass = (Class) ClassUtils.forName( + ExampleAnnotation.class.getName(), classLoader); + Class exampleMetaAnnotationClass = (Class) ClassUtils.forName( + ExampleMetaAnnotation.class.getName(), classLoader); + assertThat(AnnotatedElementUtils.getMergedAnnotationAttributes( + withExampleMetaAnnotation, exampleAnnotationClass)).isNull(); + assertThat(AnnotatedElementUtils.getMergedAnnotationAttributes( + withExampleMetaAnnotation, + exampleMetaAnnotationClass)).isNull(); + // Ideally hasAnnotation should return false, however, it currently + // detects annotations that might ultimately not load + assertThat(AnnotatedElementUtils.hasAnnotation(withExampleMetaAnnotation, + exampleAnnotationClass)).isTrue(); + assertThat(AnnotatedElementUtils.hasAnnotation(withExampleMetaAnnotation, + exampleMetaAnnotationClass)).isTrue(); + } + + + static class FilteringClassLoader extends OverridingClassLoader { + + + FilteringClassLoader(ClassLoader parent) { + super(parent); + } + + + @Override + protected boolean isEligibleForOverriding(String className) { + return className.startsWith( + AnnotationIntrospectionFailureTests.class.getName()); + } + + @Override + protected Class loadClass(String name, boolean resolve) + throws ClassNotFoundException { + if (name.startsWith(AnnotationIntrospectionFailureTests.class.getName()) + && name.contains("Filtered")) { + throw new ClassNotFoundException(name); + } + return super.loadClass(name, resolve); + } + + } + + static class FilteredType { + + } + + @Retention(RetentionPolicy.RUNTIME) + static @interface ExampleAnnotation { + + Class value() default Void.class; + + } + + @ExampleAnnotation(FilteredType.class) + static class WithExampleAnnotation { + + } + + @Retention(RetentionPolicy.RUNTIME) + @ExampleAnnotation + static @interface ExampleMetaAnnotation { + + @AliasFor(annotation = ExampleAnnotation.class, attribute = "value") + Class example1() default Void.class; + + @AliasFor(annotation = ExampleAnnotation.class, attribute = "value") + Class example2() default Void.class; + + } + + @ExampleMetaAnnotation(example1 = FilteredType.class) + static class WithExampleMetaAnnotation { + + } + +}