Refine exception handling for type not present versus access exception
Includes TypeVariable bypass for reflection-free annotation retrieval.
Includes info log message for annotation attribute retrieval failure.
Closes gh-27182
(cherry picked from commit 70247c4a94)
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -214,7 +214,12 @@ final class SerializableTypeWrapper {
|
||||
return result;
|
||||
}
|
||||
|
||||
return ReflectionUtils.invokeMethod(method, this.provider.getType(), args);
|
||||
Type type = this.provider.getType();
|
||||
if (type instanceof TypeVariable<?> tv && method.getName().equals("getName")) {
|
||||
// Avoid reflection for common comparison of type variables
|
||||
return tv.getName();
|
||||
}
|
||||
return ReflectionUtils.invokeMethod(method, type, args);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -757,7 +757,7 @@ public abstract class AnnotationUtils {
|
||||
* Google App Engine's late arrival of {@code TypeNotPresentExceptionProxy} for
|
||||
* {@code Class} values (instead of early {@code Class.getAnnotations() failure}).
|
||||
* <p>This method not failing indicates that {@link #getAnnotationAttributes(Annotation)}
|
||||
* won't failure either (when attempted later on).
|
||||
* won't fail either (when attempted later on).
|
||||
* @param annotation the annotation to validate
|
||||
* @throws IllegalStateException if a declared {@code Class} attribute could not be read
|
||||
* @since 4.3.15
|
||||
@@ -1056,8 +1056,7 @@ public abstract class AnnotationUtils {
|
||||
return null;
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
rethrowAnnotationConfigurationException(ex);
|
||||
handleIntrospectionFailure(annotation.getClass(), ex);
|
||||
handleValueRetrievalFailure(annotation, ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1073,14 +1072,18 @@ public abstract class AnnotationUtils {
|
||||
* @return the value returned from the method invocation
|
||||
* @since 5.3.24
|
||||
*/
|
||||
static Object invokeAnnotationMethod(Method method, Object annotation) {
|
||||
@Nullable
|
||||
static Object invokeAnnotationMethod(Method method, @Nullable Object annotation) {
|
||||
if (annotation == null) {
|
||||
return null;
|
||||
}
|
||||
if (Proxy.isProxyClass(annotation.getClass())) {
|
||||
try {
|
||||
InvocationHandler handler = Proxy.getInvocationHandler(annotation);
|
||||
return handler.invoke(annotation, method, null);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// ignore and fall back to reflection below
|
||||
// Ignore and fall back to reflection below
|
||||
}
|
||||
}
|
||||
return ReflectionUtils.invokeMethod(method, annotation);
|
||||
@@ -1114,20 +1117,32 @@ public abstract class AnnotationUtils {
|
||||
* @see #rethrowAnnotationConfigurationException
|
||||
* @see IntrospectionFailureLogger
|
||||
*/
|
||||
static void handleIntrospectionFailure(@Nullable AnnotatedElement element, Throwable ex) {
|
||||
static void handleIntrospectionFailure(AnnotatedElement element, Throwable ex) {
|
||||
rethrowAnnotationConfigurationException(ex);
|
||||
IntrospectionFailureLogger logger = IntrospectionFailureLogger.INFO;
|
||||
boolean meta = false;
|
||||
if (element instanceof Class<?> clazz && Annotation.class.isAssignableFrom(clazz)) {
|
||||
// Meta-annotation or (default) value lookup on an annotation type
|
||||
// Meta-annotation introspection failure
|
||||
logger = IntrospectionFailureLogger.DEBUG;
|
||||
meta = true;
|
||||
}
|
||||
if (logger.isEnabled()) {
|
||||
String message = meta ?
|
||||
"Failed to meta-introspect annotation " :
|
||||
"Failed to introspect annotations on ";
|
||||
logger.log(message + element + ": " + ex);
|
||||
logger.log("Failed to " + (meta ? "meta-introspect annotation " : "introspect annotations on ") +
|
||||
element + ": " + ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the supplied value retrieval exception.
|
||||
* @param annotation the annotation instance from which to retrieve the value
|
||||
* @param ex the exception that we encountered
|
||||
* @see #handleIntrospectionFailure
|
||||
*/
|
||||
private static void handleValueRetrievalFailure(Annotation annotation, Throwable ex) {
|
||||
rethrowAnnotationConfigurationException(ex);
|
||||
IntrospectionFailureLogger logger = IntrospectionFailureLogger.INFO;
|
||||
if (logger.isEnabled()) {
|
||||
logger.log("Failed to retrieve value from " + annotation + ": " + ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -453,7 +453,7 @@ abstract class AnnotationsScanner {
|
||||
for (int i = 0; i < annotations.length; i++) {
|
||||
Annotation annotation = annotations[i];
|
||||
if (isIgnorable(annotation.annotationType()) ||
|
||||
!AttributeMethods.forAnnotationType(annotation.annotationType()).isValid(annotation)) {
|
||||
!AttributeMethods.forAnnotationType(annotation.annotationType()).canLoad(annotation)) {
|
||||
annotations[i] = null;
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -45,7 +45,7 @@ final class AttributeMethods {
|
||||
if (m1 != null && m2 != null) {
|
||||
return m1.getName().compareTo(m2.getName());
|
||||
}
|
||||
return m1 != null ? -1 : 1;
|
||||
return (m1 != null ? -1 : 1);
|
||||
};
|
||||
|
||||
|
||||
@@ -87,18 +87,26 @@ final class AttributeMethods {
|
||||
/**
|
||||
* Determine if values from the given annotation can be safely accessed without
|
||||
* causing any {@link TypeNotPresentException TypeNotPresentExceptions}.
|
||||
* <p>This method is designed to cover Google App Engine's late arrival of such
|
||||
* exceptions for {@code Class} values (instead of the more typical early
|
||||
* {@code Class.getAnnotations() failure} on a regular JVM).
|
||||
* @param annotation the annotation to check
|
||||
* @return {@code true} if all values are present
|
||||
* @see #validate(Annotation)
|
||||
*/
|
||||
boolean isValid(Annotation annotation) {
|
||||
boolean canLoad(Annotation annotation) {
|
||||
assertAnnotation(annotation);
|
||||
for (int i = 0; i < size(); i++) {
|
||||
if (canThrowTypeNotPresentException(i)) {
|
||||
try {
|
||||
AnnotationUtils.invokeAnnotationMethod(get(i), annotation);
|
||||
}
|
||||
catch (IllegalStateException ex) {
|
||||
// Plain invocation failure to expose -> leave up to attribute retrieval
|
||||
// (if any) where such invocation failure will be logged eventually.
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// TypeNotPresentException etc. -> annotation type not actually loadable.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -108,13 +116,13 @@ final class AttributeMethods {
|
||||
|
||||
/**
|
||||
* Check if values from the given annotation can be safely accessed without causing
|
||||
* any {@link TypeNotPresentException TypeNotPresentExceptions}. In particular,
|
||||
* this method is designed to cover Google App Engine's late arrival of such
|
||||
* any {@link TypeNotPresentException TypeNotPresentExceptions}.
|
||||
* <p>This method is designed to cover Google App Engine's late arrival of such
|
||||
* exceptions for {@code Class} values (instead of the more typical early
|
||||
* {@code Class.getAnnotations() failure}).
|
||||
* {@code Class.getAnnotations() failure} on a regular JVM).
|
||||
* @param annotation the annotation to validate
|
||||
* @throws IllegalStateException if a declared {@code Class} attribute could not be read
|
||||
* @see #isValid(Annotation)
|
||||
* @see #canLoad(Annotation)
|
||||
*/
|
||||
void validate(Annotation annotation) {
|
||||
assertAnnotation(annotation);
|
||||
@@ -123,6 +131,9 @@ final class AttributeMethods {
|
||||
try {
|
||||
AnnotationUtils.invokeAnnotationMethod(get(i), annotation);
|
||||
}
|
||||
catch (IllegalStateException ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new IllegalStateException("Could not obtain annotation attribute value for " +
|
||||
get(i).getName() + " declared on " + annotation.annotationType(), ex);
|
||||
@@ -147,7 +158,7 @@ final class AttributeMethods {
|
||||
@Nullable
|
||||
Method get(String name) {
|
||||
int index = indexOf(name);
|
||||
return index != -1 ? this.attributeMethods[index] : null;
|
||||
return (index != -1 ? this.attributeMethods[index] : null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user