Skip to content

Commit 94349f8

Browse files
committed
Use Nullness to determine if an endpoint parameter is optional
Closes gh-46593
1 parent 373e34e commit 94349f8

File tree

3 files changed

+18
-5
lines changed

3 files changed

+18
-5
lines changed

documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/endpoints.adoc

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,8 @@ Operations on an endpoint receive input through their parameters.
413413
When exposed over the web, the values for these parameters are taken from the URL's query parameters and from the JSON request body.
414414
When exposed over JMX, the parameters are mapped to the parameters of the MBean's operations.
415415
Parameters are required by default.
416-
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].
416+
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].
417+
Kotlin null safety is also supported.
417418

418419
You can map each root property in the JSON request body to a parameter of the endpoint.
419420
Consider the following JSON request body:
@@ -547,7 +548,7 @@ NOTE: Range requests are not supported when using Jersey.
547548
==== Web Endpoint Security
548549

549550
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.
550-
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.
551+
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.
551552
The latter is typically used to perform authorization checks by using its `isUserInRole(String)` method.
552553

553554

module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/invoke/reflect/OperationMethodParameter.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.util.function.Predicate;
2222

2323
import org.springframework.boot.actuate.endpoint.invoke.OperationParameter;
24+
import org.springframework.core.Nullness;
2425

2526
/**
2627
* {@link OperationParameter} created from an {@link OperationMethod}.
@@ -63,10 +64,8 @@ public boolean isMandatory() {
6364
return !isOptional();
6465
}
6566

66-
@SuppressWarnings("deprecation")
6767
private boolean isOptional() {
68-
return this.parameter.getAnnotationsByType(org.springframework.lang.Nullable.class).length > 0
69-
|| this.optional.test(this.parameter);
68+
return Nullness.NULLABLE == Nullness.forParameter(this.parameter) || this.optional.test(this.parameter);
7069
}
7170

7271
@Override

module/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/invoke/reflect/OperationMethodParameterTests.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ class OperationMethodParameterTests {
4343

4444
private final Method example = ReflectionUtils.findMethod(getClass(), "example", String.class, String.class);
4545

46+
private final Method exampleJSpecifyNullable = ReflectionUtils.findMethod(getClass(), "exampleJSpecifyNullable",
47+
String.class, String.class);
48+
4649
private final Method exampleSpringNullable = ReflectionUtils.findMethod(getClass(), "exampleSpringNullable",
4750
String.class, String.class);
4851

@@ -76,6 +79,13 @@ void isMandatoryWhenOptionalAnnotationShouldReturnFalse() {
7679
assertThat(parameter.isMandatory()).isFalse();
7780
}
7881

82+
@Test
83+
void isMandatoryWhenJSpecifyNullableAnnotationShouldReturnFalse() {
84+
OperationMethodParameter parameter = new OperationMethodParameter("name",
85+
this.exampleJSpecifyNullable.getParameters()[1], this::isOptionalParameter);
86+
assertThat(parameter.isMandatory()).isFalse();
87+
}
88+
7989
@Test
8090
void isMandatoryWhenSpringNullableAnnotationShouldReturnFalse() {
8191
OperationMethodParameter parameter = new OperationMethodParameter("name",
@@ -99,6 +109,9 @@ private boolean isOptionalParameter(Parameter parameter) {
99109
void example(String one, @TestOptional String two) {
100110
}
101111

112+
void exampleJSpecifyNullable(String one, @org.jspecify.annotations.Nullable String two) {
113+
}
114+
102115
@SuppressWarnings("deprecation")
103116
void exampleSpringNullable(String one, @org.springframework.lang.Nullable String two) {
104117
}

0 commit comments

Comments
 (0)