Overview

This came up in the context of Spring Native, when the exception below was thrown.

TypeAccess is an enum used in the org.springframework.nativex.hint.TypeHint annotation.

@TypeHint is declared in the user code but is not present on the classpath at runtime.

java.lang.IllegalArgumentException: Could not find class [org.springframework.nativex.hint.TypeAccess]
    at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:334)
    at org.springframework.core.type.classreading.MergedAnnotationReadingVisitor.visitEnum(MergedAnnotationReadingVisitor.java:103)
    at org.springframework.core.type.classreading.MergedAnnotationReadingVisitor$ArrayVisitor.visitEnum(MergedAnnotationReadingVisitor.java:169)
    at org.springframework.asm.ClassReader.readElementValue(ClassReader.java:3081)
    at org.springframework.asm.ClassReader.readElementValues(ClassReader.java:3006)
    at org.springframework.asm.ClassReader.readElementValue(ClassReader.java:3180)
    at org.springframework.asm.ClassReader.readElementValues(ClassReader.java:3000)
    at org.springframework.asm.ClassReader.accept(ClassReader.java:610)
    at org.springframework.asm.ClassReader.accept(ClassReader.java:426)
    at org.springframework.core.type.classreading.SimpleMetadataReader.<init>(SimpleMetadataReader.java:49)
    at org.springframework.core.type.classreading.SimpleMetadataReaderFactory.getMetadataReader(SimpleMetadataReaderFactory.java:103)
    at org.springframework.core.type.classreading.CachingMetadataReaderFactory.getMetadataReader(CachingMetadataReaderFactory.java:132)
    at org.springframework.core.type.classreading.SimpleMetadataReaderFactory.getMetadataReader(SimpleMetadataReaderFactory.java:81)
    at org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider.addCandidateComponentsFromIndex(ClassPathScanningCandidateComponentProvider.java:387)
    at org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider.findCandidateComponents(ClassPathScanningCandidateComponentProvider.java:313)
    at org.springframework.boot.test.context.AnnotatedClassFinder.scanPackage(AnnotatedClassFinder.java:90)
    at org.springframework.boot.test.context.AnnotatedClassFinder.findFromPackage(AnnotatedClassFinder.java:82)
    at org.springframework.boot.test.context.AnnotatedClassFinder.findFromClass(AnnotatedClassFinder.java:68)
    at org.springframework.boot.test.context.SpringBootTestContextBootstrapper.getOrFindConfigurationClasses(SpringBootTestContextBootstrapper.java:235)
    at org.springframework.boot.test.context.SpringBootTestContextBootstrapper.processMergedContextConfiguration(SpringBootTestContextBootstrapper.java:152)
    at org.springframework.test.context.support.AbstractTestContextBootstrapper.buildMergedContextConfiguration(AbstractTestContextBootstrapper.java:392)
    at org.springframework.test.context.support.AbstractTestContextBootstrapper.buildDefaultMergedContextConfiguration(AbstractTestContextBootstrapper.java:309)
    at org.springframework.test.context.support.AbstractTestContextBootstrapper.buildMergedContextConfiguration(AbstractTestContextBootstrapper.java:262)
    at org.springframework.test.context.support.AbstractTestContextBootstrapper.buildTestContext(AbstractTestContextBootstrapper.java:107)
    at org.springframework.boot.test.context.SpringBootTestContextBootstrapper.buildTestContext(SpringBootTestContextBootstrapper.java:102)
    at org.springframework.test.context.TestContextManager.<init>(TestContextManager.java:137)
    at org.springframework.test.context.TestContextManager.<init>(TestContextManager.java:122)

Proposal

Introduce try-catch blocks in MergedAnnotationReadingVisitor's visitEnum(String, String, Consumer<E>) and visitAnnotation(String, Consumer<MergedAnnotation<T>>) methods, and ignore types that cannot be loaded via ClassUtils.resolveClassName(...).

Comment From: sbrannen

This is potentially a regression introduced when MergedAnnotation support was introduced.

Comment From: viktorardelean

@sbrannen I would like to work on this issue. Besides catching java.lang.IllegalArgumentException in visitEnum(String, String, Consumer) and visitAnnotation(String, Consumer>) methods, is there anything else to be done for this?

Should I add some specific logging in the catch block?

Comment From: sbrannen

Hi @viktorardelean,

Thanks for offering to help. Much appreciated!

However, I actually meant to assign this one to myself (which I've now done), since I will work on the fix and determine if it's a regression that needs to be backported.

Comment From: sbrannen

Prior to the introduction of the MergedAnnotation API in Spring Framework 5.2, the ASM-based metadata readers caught exceptions while attempting to load a visited annotation type or visited enum type (or enum value).

https://github.com/spring-projects/spring-framework/blob/5.1.x/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationAttributesReadingVisitor.java

https://github.com/spring-projects/spring-framework/blob/dafd904b14edb1a450e69411074b335e1e5cb47a/spring-core/src/main/java/org/springframework/core/type/classreading/AbstractRecursiveAnnotationVisitor.java#L83-L100

In light of that, I am labeling this as a regression for backporting to 5.2.x.

Comment From: sbrannen

Update: the status quo for Spring Framework 5.1.x has been tested in https://github.com/spring-projects/spring-framework/commit/8de2a9b74dbcdc98f00074d1a0e4456b32e5bc63.

Comment From: sbrannen

Since this issue has not surfaced again since it was first encountered in a Spring Native sample application, we are closing this issue.

If necessary, this issue can be reassessed at a later date.