From 2ad9a47d08e56549704740cceacf9cf84f73a91e Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 5 Jan 2021 14:03:27 -0800 Subject: [PATCH] Support JSR-305 annotations on endpoint methods Update `OperationMethodParameter` to additionally support JSR-305 based `@Nullable` annotations. Closes gh-24647 --- .../reflect/OperationMethodParameter.java | 24 ++++++++- .../OperationMethodParameterTests.java | 52 ++++++++++++++++--- 2 files changed, 68 insertions(+), 8 deletions(-) diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/invoke/reflect/OperationMethodParameter.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/invoke/reflect/OperationMethodParameter.java index 8b6cbcc07a8e..3aeac7d7613c 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/invoke/reflect/OperationMethodParameter.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/invoke/reflect/OperationMethodParameter.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2021 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. @@ -18,8 +18,14 @@ import java.lang.reflect.Parameter; +import javax.annotation.Nonnull; +import javax.annotation.meta.When; + import org.springframework.boot.actuate.endpoint.invoke.OperationParameter; +import org.springframework.core.annotation.MergedAnnotation; +import org.springframework.core.annotation.MergedAnnotations; import org.springframework.lang.Nullable; +import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; /** @@ -29,6 +35,8 @@ */ class OperationMethodParameter implements OperationParameter { + private static final boolean jsr305Present = ClassUtils.isPresent("javax.annotation.Nonnull", null); + private final String name; private final Parameter parameter; @@ -55,7 +63,10 @@ public Class getType() { @Override public boolean isMandatory() { - return ObjectUtils.isEmpty(this.parameter.getAnnotationsByType(Nullable.class)); + if (!ObjectUtils.isEmpty(this.parameter.getAnnotationsByType(Nullable.class))) { + return false; + } + return (jsr305Present) ? new Jsr305().isMandatory(this.parameter) : true; } @Override @@ -63,4 +74,13 @@ public String toString() { return this.name + " of type " + this.parameter.getType().getName(); } + private static class Jsr305 { + + boolean isMandatory(Parameter parameter) { + MergedAnnotation annotation = MergedAnnotations.from(parameter).get(Nonnull.class); + return !annotation.isPresent() || annotation.getEnum("when", When.class) == When.ALWAYS; + } + + } + } diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/invoke/reflect/OperationMethodParameterTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/invoke/reflect/OperationMethodParameterTests.java index 5a5a3695457d..d633ce948be4 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/invoke/reflect/OperationMethodParameterTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/invoke/reflect/OperationMethodParameterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2021 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. @@ -16,8 +16,14 @@ package org.springframework.boot.actuate.endpoint.invoke.reflect; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Method; +import javax.annotation.Nonnull; +import javax.annotation.meta.TypeQualifier; +import javax.annotation.meta.When; + import org.junit.jupiter.api.Test; import org.springframework.lang.Nullable; @@ -32,29 +38,48 @@ */ class OperationMethodParameterTests { - private Method method = ReflectionUtils.findMethod(getClass(), "example", String.class, String.class); + private Method example = ReflectionUtils.findMethod(getClass(), "example", String.class, String.class); + + private Method exampleJsr305 = ReflectionUtils.findMethod(getClass(), "exampleJsr305", String.class, String.class); + + private Method exampleMetaJsr305 = ReflectionUtils.findMethod(getClass(), "exampleMetaJsr305", String.class, + String.class); @Test void getNameShouldReturnName() { - OperationMethodParameter parameter = new OperationMethodParameter("name", this.method.getParameters()[0]); + OperationMethodParameter parameter = new OperationMethodParameter("name", this.example.getParameters()[0]); assertThat(parameter.getName()).isEqualTo("name"); } @Test void getTypeShouldReturnType() { - OperationMethodParameter parameter = new OperationMethodParameter("name", this.method.getParameters()[0]); + OperationMethodParameter parameter = new OperationMethodParameter("name", this.example.getParameters()[0]); assertThat(parameter.getType()).isEqualTo(String.class); } @Test void isMandatoryWhenNoAnnotationShouldReturnTrue() { - OperationMethodParameter parameter = new OperationMethodParameter("name", this.method.getParameters()[0]); + OperationMethodParameter parameter = new OperationMethodParameter("name", this.example.getParameters()[0]); assertThat(parameter.isMandatory()).isTrue(); } @Test void isMandatoryWhenNullableAnnotationShouldReturnFalse() { - OperationMethodParameter parameter = new OperationMethodParameter("name", this.method.getParameters()[1]); + OperationMethodParameter parameter = new OperationMethodParameter("name", this.example.getParameters()[1]); + assertThat(parameter.isMandatory()).isFalse(); + } + + @Test + void isMandatoryWhenJsrNullableAnnotationShouldReturnFalse() { + OperationMethodParameter parameter = new OperationMethodParameter("name", + this.exampleJsr305.getParameters()[1]); + assertThat(parameter.isMandatory()).isFalse(); + } + + @Test + void isMandatoryWhenJsrMetaNullableAnnotationShouldReturnFalse() { + OperationMethodParameter parameter = new OperationMethodParameter("name", + this.exampleMetaJsr305.getParameters()[1]); assertThat(parameter.isMandatory()).isFalse(); } @@ -62,4 +87,19 @@ void example(String one, @Nullable String two) { } + void exampleJsr305(String one, @javax.annotation.Nullable String two) { + + } + + void exampleMetaJsr305(String one, @MetaNullable String two) { + + } + + @TypeQualifier + @Retention(RetentionPolicy.RUNTIME) + @Nonnull(when = When.MAYBE) + @interface MetaNullable { + + } + }