Skip to content

Commit

Permalink
Improve exception for mixed explicit/implicit aliases with @AliasFor
Browse files Browse the repository at this point in the history
Given the following improperly configured composed @RequestMapping
annotation:

@retention(RetentionPolicy.RUNTIME)
@RequestMapping
@interface PostApi {

	@AliasFor("value")
	String[] path() default {};

	@AliasFor(annotation = RequestMapping.class, attribute = "path")
	String[] value() default {};
}

Prior to this commit, an attempt to process the above annotation
resulted in an exception similar to the following, which is not
especially helpful to discern the problem.

> Attribute 'value' in annotation [PostApi] must be declared as an
> @AliasFor 'path', not 'path'.

This commit improves the exception message for such scenarios,
resulting in an exception message similar to the following.

> Attribute 'value' in annotation [PostApi] must be declared as an
> @AliasFor attribute 'path' in annotation [PostApi], not attribute
> 'path' in annotation [RequestMapping].

Closes gh-24168
  • Loading branch information
sbrannen committed Dec 10, 2019
1 parent 4466114 commit 2bd821c
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 17 deletions.
Expand Up @@ -182,9 +182,9 @@ private Method resolveAliasTarget(Method attribute, AliasFor aliasFor, boolean c
Method mirror = resolveAliasTarget(target, targetAliasFor, false);
if (!mirror.equals(attribute)) {
throw new AnnotationConfigurationException(String.format(
"%s must be declared as an @AliasFor '%s', not '%s'.",
"%s must be declared as an @AliasFor %s, not %s.",
StringUtils.capitalize(AttributeMethods.describe(target)),
attribute.getName(), mirror.getName()));
AttributeMethods.describe(attribute), AttributeMethods.describe(mirror)));
}
}
}
Expand Down
Expand Up @@ -160,11 +160,28 @@ void forAnnotationTypeWhenAliasForWithIncompatibleReturnTypes() {

@Test
void forAnnotationTypeWhenAliasForToSelfAnnotatedToOtherAttribute() {
assertThatExceptionOfType(AnnotationConfigurationException.class).isThrownBy(() ->
AnnotationTypeMappings.forAnnotationType(AliasForToSelfAnnotatedToOtherAttribute.class))
.withMessage("Attribute 'b' in annotation ["
+ AliasForToSelfAnnotatedToOtherAttribute.class.getName()
+ "] must be declared as an @AliasFor 'a', not 'c'.");
String annotationType = AliasForToSelfAnnotatedToOtherAttribute.class.getName();
assertThatExceptionOfType(AnnotationConfigurationException.class)
.isThrownBy(() -> AnnotationTypeMappings.forAnnotationType(AliasForToSelfAnnotatedToOtherAttribute.class))
.withMessage("Attribute 'b' in annotation [" + annotationType
+ "] must be declared as an @AliasFor attribute 'a' in annotation [" + annotationType
+ "], not attribute 'c' in annotation [" + annotationType + "].");
}

@Test
void forAnnotationTypeWhenAliasForHasMixedImplicitAndExplicitAliases() {
assertMixedImplicitAndExplicitAliases(AliasForWithMixedImplicitAndExplicitAliasesV1.class, "b");
assertMixedImplicitAndExplicitAliases(AliasForWithMixedImplicitAndExplicitAliasesV2.class, "a");
}

private void assertMixedImplicitAndExplicitAliases(Class<? extends Annotation> annotationType, String overriddenAttribute) {
String annotationName = annotationType.getName();
String metaAnnotationName = AliasPair.class.getName();
assertThatExceptionOfType(AnnotationConfigurationException.class)
.isThrownBy(() -> AnnotationTypeMappings.forAnnotationType(annotationType))
.withMessage("Attribute 'b' in annotation [" + annotationName
+ "] must be declared as an @AliasFor attribute 'a' in annotation [" + annotationName
+ "], not attribute '" + overriddenAttribute + "' in annotation [" + metaAnnotationName + "].");
}

@Test
Expand Down Expand Up @@ -641,6 +658,42 @@ private Object extractFromMap(Method attribute, Object map) {
String c() default "";
}

@Retention(RetentionPolicy.RUNTIME)
@interface AliasPair {

@AliasFor("b")
String a() default "";

@AliasFor("a")
String b() default "";
}

@Retention(RetentionPolicy.RUNTIME)
@AliasPair
@interface AliasForWithMixedImplicitAndExplicitAliasesV1 {

// attempted implicit alias via attribute override
@AliasFor(annotation = AliasPair.class, attribute = "b")
String b() default "";

// explicit local alias
@AliasFor("b")
String a() default "";
}

@Retention(RetentionPolicy.RUNTIME)
@AliasPair
@interface AliasForWithMixedImplicitAndExplicitAliasesV2 {

// attempted implicit alias via attribute override
@AliasFor(annotation = AliasPair.class, attribute = "a")
String b() default "";

// explicit local alias
@AliasFor("b")
String a() default "";
}

@Retention(RetentionPolicy.RUNTIME)
@interface AliasForNonMetaAnnotated {

Expand Down Expand Up @@ -712,16 +765,6 @@ private Object extractFromMap(Method attribute, Object map) {
String alias() default "";
}

@Retention(RetentionPolicy.RUNTIME)
@interface AliasPair {

@AliasFor("b")
String a() default "";

@AliasFor("a")
String b() default "";
}

@Retention(RetentionPolicy.RUNTIME)
@interface ImplicitMirrorsTarget {

Expand Down

0 comments on commit 2bd821c

Please sign in to comment.