Protect against stack overflow when searching meta annotations
It is legal for an annotation to be annotated with itself. Previously, when searching for meta annotations this could lead to a stack overflow. This was likely to occur when using Kotlin as, like Java, its Target annotation is annotated with itself. A stack overflow doesn’t occur with Java’s Target annotation due to some short-circuiting logic for annotations in java.lang. This commit updates the logic for finding meta-annotations to short-circuit when an annotation that has already been seen is encountered. Closes gh-5902
This commit is contained in:
@@ -20,6 +20,7 @@ import java.lang.annotation.Annotation;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@@ -34,6 +35,7 @@ import org.springframework.util.ClassUtils;
|
||||
* {@link ImportAutoConfiguration}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class ImportAutoConfigurationImportSelector
|
||||
extends EnableAutoConfigurationImportSelector {
|
||||
@@ -66,29 +68,30 @@ class ImportAutoConfigurationImportSelector
|
||||
|
||||
private List<String> getCandidateConfigurations(Class<?> source) {
|
||||
Set<String> candidates = new LinkedHashSet<String>();
|
||||
collectCandidateConfigurations(source, candidates);
|
||||
collectCandidateConfigurations(source, candidates, new HashSet<Class<?>>());
|
||||
return new ArrayList<String>(candidates);
|
||||
}
|
||||
|
||||
private void collectCandidateConfigurations(Class<?> source, Set<String> candidates) {
|
||||
if (source != null) {
|
||||
private void collectCandidateConfigurations(Class<?> source, Set<String> candidates,
|
||||
Set<Class<?>> seen) {
|
||||
if (source != null && seen.add(source)) {
|
||||
for (Annotation annotation : source.getDeclaredAnnotations()) {
|
||||
if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotation)) {
|
||||
collectCandidateConfigurations(annotation, candidates);
|
||||
collectCandidateConfigurations(annotation, candidates, seen);
|
||||
}
|
||||
}
|
||||
collectCandidateConfigurations(source.getSuperclass(), candidates);
|
||||
collectCandidateConfigurations(source.getSuperclass(), candidates, seen);
|
||||
}
|
||||
}
|
||||
|
||||
private void collectCandidateConfigurations(Annotation annotation,
|
||||
Set<String> candidates) {
|
||||
Set<String> candidates, Set<Class<?>> seen) {
|
||||
if (ANNOTATION_NAMES.contains(annotation.annotationType().getName())) {
|
||||
String[] value = (String[]) AnnotationUtils
|
||||
.getAnnotationAttributes(annotation, true).get("value");
|
||||
candidates.addAll(Arrays.asList(value));
|
||||
}
|
||||
collectCandidateConfigurations(annotation.annotationType(), candidates);
|
||||
collectCandidateConfigurations(annotation.annotationType(), candidates, seen);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.boot.autoconfigure;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
@@ -41,6 +42,7 @@ import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
* Tests for {@link ImportAutoConfigurationImportSelector}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class ImportAutoConfigurationImportSelectorTests {
|
||||
@@ -87,6 +89,15 @@ public class ImportAutoConfigurationImportSelectorTests {
|
||||
ThymeleafAutoConfiguration.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void selfAnnotatingAnnotationDoesNotCauseStackOverflow() throws IOException {
|
||||
AnnotationMetadata annotationMetadata = new SimpleMetadataReaderFactory()
|
||||
.getMetadataReader(ImportWithSelfAnnotatingAnnotation.class.getName())
|
||||
.getAnnotationMetadata();
|
||||
String[] imports = this.importSelector.selectImports(annotationMetadata);
|
||||
assertThat(imports).containsOnly(ThymeleafAutoConfiguration.class.getName());
|
||||
}
|
||||
|
||||
@ImportAutoConfiguration(FreeMarkerAutoConfiguration.class)
|
||||
static class ImportFreemarker {
|
||||
|
||||
@@ -98,6 +109,11 @@ public class ImportAutoConfigurationImportSelectorTests {
|
||||
|
||||
}
|
||||
|
||||
@SelfAnnotating
|
||||
static class ImportWithSelfAnnotatingAnnotation {
|
||||
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@ImportAutoConfiguration(FreeMarkerAutoConfiguration.class)
|
||||
static @interface ImportOne {
|
||||
@@ -110,4 +126,11 @@ public class ImportAutoConfigurationImportSelectorTests {
|
||||
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@ImportAutoConfiguration(ThymeleafAutoConfiguration.class)
|
||||
@SelfAnnotating
|
||||
static @interface SelfAnnotating {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user