diff --git a/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/endpoints.adoc b/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/endpoints.adoc index d496638c48b..820cda3acd8 100644 --- a/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/endpoints.adoc +++ b/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/endpoints.adoc @@ -413,7 +413,8 @@ Operations on an endpoint receive input through their parameters. When exposed over the web, the values for these parameters are taken from the URL's query parameters and from the JSON request body. When exposed over JMX, the parameters are mapped to the parameters of the MBean's operations. Parameters are required by default. -They can be made optional by annotating them with either javadoc:org.springframework.boot.actuate.endpoint.annotation.OptionalParameter[format=annotation] or javadoc:org.springframework.lang.Nullable[format=annotation]. +They can be made optional by annotating them with either javadoc:org.springframework.boot.actuate.endpoint.annotation.OptionalParameter[format=annotation] or JSpecify's javadoc:org.jspecify.annotations.Nullable[format=annotation]. +Kotlin null safety is also supported. You can map each root property in the JSON request body to a parameter of the endpoint. Consider the following JSON request body: @@ -547,7 +548,7 @@ NOTE: Range requests are not supported when using Jersey. ==== Web Endpoint Security An operation on a web endpoint or a web-specific endpoint extension can receive the current javadoc:java.security.Principal[] or javadoc:org.springframework.boot.actuate.endpoint.SecurityContext[] as a method parameter. -The former is typically used in conjunction with either javadoc:org.springframework.boot.actuate.endpoint.annotation.OptionalParameter[format=annotation] or javadoc:org.springframework.lang.Nullable[format=annotation] to provide different behavior for authenticated and unauthenticated users. +The former is typically used in conjunction with either javadoc:org.springframework.boot.actuate.endpoint.annotation.OptionalParameter[format=annotation] or javadoc:org.jspecify.annotations.Nullable[format=annotation] to provide different behavior for authenticated and unauthenticated users. The latter is typically used to perform authorization checks by using its `isUserInRole(String)` method. diff --git a/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/invoke/reflect/OperationMethodParameter.java b/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/invoke/reflect/OperationMethodParameter.java index d12da1202a8..89a89e4efa3 100644 --- a/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/invoke/reflect/OperationMethodParameter.java +++ b/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/invoke/reflect/OperationMethodParameter.java @@ -21,6 +21,7 @@ import java.lang.reflect.Parameter; import java.util.function.Predicate; import org.springframework.boot.actuate.endpoint.invoke.OperationParameter; +import org.springframework.core.Nullness; /** * {@link OperationParameter} created from an {@link OperationMethod}. @@ -63,10 +64,8 @@ class OperationMethodParameter implements OperationParameter { return !isOptional(); } - @SuppressWarnings("deprecation") private boolean isOptional() { - return this.parameter.getAnnotationsByType(org.springframework.lang.Nullable.class).length > 0 - || this.optional.test(this.parameter); + return Nullness.NULLABLE == Nullness.forParameter(this.parameter) || this.optional.test(this.parameter); } @Override diff --git a/module/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/invoke/reflect/OperationMethodParameterTests.java b/module/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/invoke/reflect/OperationMethodParameterTests.java index fcc60841834..644583bef62 100644 --- a/module/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/invoke/reflect/OperationMethodParameterTests.java +++ b/module/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/invoke/reflect/OperationMethodParameterTests.java @@ -43,6 +43,9 @@ class OperationMethodParameterTests { private final Method example = ReflectionUtils.findMethod(getClass(), "example", String.class, String.class); + private final Method exampleJSpecifyNullable = ReflectionUtils.findMethod(getClass(), "exampleJSpecifyNullable", + String.class, String.class); + private final Method exampleSpringNullable = ReflectionUtils.findMethod(getClass(), "exampleSpringNullable", String.class, String.class); @@ -76,6 +79,13 @@ class OperationMethodParameterTests { assertThat(parameter.isMandatory()).isFalse(); } + @Test + void isMandatoryWhenJSpecifyNullableAnnotationShouldReturnFalse() { + OperationMethodParameter parameter = new OperationMethodParameter("name", + this.exampleJSpecifyNullable.getParameters()[1], this::isOptionalParameter); + assertThat(parameter.isMandatory()).isFalse(); + } + @Test void isMandatoryWhenSpringNullableAnnotationShouldReturnFalse() { OperationMethodParameter parameter = new OperationMethodParameter("name", @@ -99,6 +109,9 @@ class OperationMethodParameterTests { void example(String one, @TestOptional String two) { } + void exampleJSpecifyNullable(String one, @org.jspecify.annotations.Nullable String two) { + } + @SuppressWarnings("deprecation") void exampleSpringNullable(String one, @org.springframework.lang.Nullable String two) { }