From 450eb483034c3b1bbaf8e7a90c4512516eb0c54c Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 9 Sep 2025 13:00:50 -0700 Subject: [PATCH] Only support `@OptionalParameter` annotation with endpoint methods Remove `@Nullable` support for optional endpoint method parameters in favor of only supporting `@OptionalParameter`. The annotation processor now also only supports `@OptionalParameter` detection. Closes gh-47136 --- ...figurationMetadataAnnotationProcessor.java | 44 +++++++++---------- .../MetadataGenerationEnvironment.java | 18 ++++---- .../EndpointMetadataGenerationTests.java | 11 ----- ...figurationMetadataAnnotationProcessor.java | 4 +- .../endpoint/NullableParameterEndpoint.java | 37 ---------------- .../invoke/reflect/OperationMethod.java | 16 +------ .../reflect/OperationMethodParameter.java | 7 +-- .../OperationMethodParameterTests.java | 27 ------------ .../invoke/reflect/OperationMethodTests.java | 16 +++---- .../ReflectiveOperationInvokerTests.java | 4 +- .../invoke/reflect/TestOperationMethod.java | 42 ++++++++++++++++++ .../CachingOperationInvokerAdvisorTests.java | 3 +- 12 files changed, 86 insertions(+), 143 deletions(-) delete mode 100644 configuration-metadata/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/NullableParameterEndpoint.java create mode 100644 module/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/invoke/reflect/TestOperationMethod.java diff --git a/configuration-metadata/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessor.java b/configuration-metadata/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessor.java index 6234b422166..98b69eb0ef0 100644 --- a/configuration-metadata/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessor.java +++ b/configuration-metadata/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessor.java @@ -107,9 +107,9 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor static final String WEB_ENDPOINT_ANNOTATION = "org.springframework.boot.actuate.endpoint.web.annotation.WebEndpoint"; - static final String READ_OPERATION_ANNOTATION = "org.springframework.boot.actuate.endpoint.annotation.ReadOperation"; + static final String ENDPOINT_READ_OPERATION_ANNOTATION = "org.springframework.boot.actuate.endpoint.annotation.ReadOperation"; - static final String OPTIONAL_PARAMETER_ANNOTATION = "org.springframework.boot.actuate.endpoint.annotation.OptionalParameter"; + static final String ENDPOINT_OPTIONAL_PARAMETER_ANNOTATION = "org.springframework.boot.actuate.endpoint.annotation.OptionalParameter"; static final String NAME_ANNOTATION = "org.springframework.boot.context.properties.bind.Name"; @@ -158,16 +158,16 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor REST_CONTROLLER_ENDPOINT_ANNOTATION, SERVLET_ENDPOINT_ANNOTATION, WEB_ENDPOINT_ANNOTATION); } - protected String readOperationAnnotation() { - return READ_OPERATION_ANNOTATION; + protected String endpointReadOperationAnnotation() { + return ENDPOINT_READ_OPERATION_ANNOTATION; } protected String nameAnnotation() { return NAME_ANNOTATION; } - protected String optionalParameterAnnotation() { - return OPTIONAL_PARAMETER_ANNOTATION; + protected String endpointOptionalParameterAnnotation() { + return ENDPOINT_OPTIONAL_PARAMETER_ANNOTATION; } protected String endpointAccessEnum() { @@ -194,8 +194,8 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor this.metadataEnv = new MetadataGenerationEnvironment(env, configurationPropertiesAnnotation(), configurationPropertiesSourceAnnotation(), nestedConfigurationPropertyAnnotation(), deprecatedConfigurationPropertyAnnotation(), constructorBindingAnnotation(), autowiredAnnotation(), - defaultValueAnnotation(), endpointAnnotations(), readOperationAnnotation(), - optionalParameterAnnotation(), nameAnnotation()); + defaultValueAnnotation(), endpointAnnotations(), endpointReadOperationAnnotation(), + endpointOptionalParameterAnnotation(), nameAnnotation()); } @Override @@ -271,8 +271,7 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor } private void processExecutableElement(String prefix, ExecutableElement element, Deque seen) { - if ((!element.getModifiers().contains(Modifier.PRIVATE)) - && (TypeKind.VOID != element.getReturnType().getKind())) { + if ((!element.getModifiers().contains(Modifier.PRIVATE)) && returnsVoid(element)) { Element returns = this.processingEnv.getTypeUtils().asElement(element.getReturnType()); if (returns instanceof TypeElement typeElement) { ItemMetadata group = ItemMetadata.newGroup(prefix, @@ -354,7 +353,7 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor "Permitted level of access for the %s endpoint.".formatted(endpointId), defaultAccess, null); this.metadataCollector.add(accessProperty, (existing) -> checkDefaultAccessValueMatchesExisting(existing, defaultAccess, type)); - if (hasMainReadOperation(element)) { + if (isCachableEndpoint(element)) { this.metadataCollector.addIfAbsent(ItemMetadata.newProperty(endpointKey, "cache.time-to-live", Duration.class.getName(), type, null, "Maximum time that a response can be cached.", "0ms", null)); } @@ -371,28 +370,27 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor } } - private boolean hasMainReadOperation(TypeElement element) { + private boolean isCachableEndpoint(TypeElement element) { for (ExecutableElement method : ElementFilter.methodsIn(element.getEnclosedElements())) { - if (this.metadataEnv.getReadOperationAnnotation(method) != null - && (TypeKind.VOID != method.getReturnType().getKind()) && hasNoOrOptionalParameters(method)) { + if (this.metadataEnv.isEndpointReadOperation(method) && returnsVoid(method) + && !hasMandatoryEndpointParameter(method)) { return true; } } return false; } - private boolean hasNoOrOptionalParameters(ExecutableElement method) { + private boolean returnsVoid(ExecutableElement method) { + return TypeKind.VOID != method.getReturnType().getKind(); + } + + private boolean hasMandatoryEndpointParameter(ExecutableElement method) { for (VariableElement parameter : method.getParameters()) { - if (!isOptionalParameter(parameter)) { - return false; + if (!this.metadataEnv.hasEndpointOptionalParameterAnnotation(parameter)) { + return true; } } - return true; - } - - private boolean isOptionalParameter(VariableElement parameter) { - return this.metadataEnv.hasNullableAnnotation(parameter) - || this.metadataEnv.hasOptionalParameterAnnotation(parameter); + return false; } private String getPrefix(AnnotationMirror annotation) { diff --git a/configuration-metadata/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/MetadataGenerationEnvironment.java b/configuration-metadata/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/MetadataGenerationEnvironment.java index b4ace8decec..51ad6f5f07d 100644 --- a/configuration-metadata/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/MetadataGenerationEnvironment.java +++ b/configuration-metadata/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/MetadataGenerationEnvironment.java @@ -96,9 +96,9 @@ class MetadataGenerationEnvironment { private final Set endpointAnnotations; - private final String readOperationAnnotation; + private final String endpointReadOperationAnnotation; - private final String optionalParameterAnnotation; + private final String endpointOptionalParameterAnnotation; private final String nameAnnotation; @@ -108,7 +108,7 @@ class MetadataGenerationEnvironment { String configurationPropertiesSourceAnnotation, String nestedConfigurationPropertyAnnotation, String deprecatedConfigurationPropertyAnnotation, String constructorBindingAnnotation, String autowiredAnnotation, String defaultValueAnnotation, Set endpointAnnotations, - String readOperationAnnotation, String optionalParameterAnnotation, String nameAnnotation) { + String endpointReadOperationAnnotation, String endpointOptionalParameterAnnotation, String nameAnnotation) { this.typeUtils = new TypeUtils(environment); this.elements = environment.getElementUtils(); this.messager = environment.getMessager(); @@ -122,8 +122,8 @@ class MetadataGenerationEnvironment { this.autowiredAnnotation = autowiredAnnotation; this.defaultValueAnnotation = defaultValueAnnotation; this.endpointAnnotations = endpointAnnotations; - this.readOperationAnnotation = readOperationAnnotation; - this.optionalParameterAnnotation = optionalParameterAnnotation; + this.endpointReadOperationAnnotation = endpointReadOperationAnnotation; + this.endpointOptionalParameterAnnotation = endpointOptionalParameterAnnotation; this.nameAnnotation = nameAnnotation; } @@ -370,8 +370,8 @@ class MetadataGenerationEnvironment { .collect(Collectors.toSet()); } - AnnotationMirror getReadOperationAnnotation(Element element) { - return getAnnotation(element, this.readOperationAnnotation); + boolean isEndpointReadOperation(Element element) { + return getAnnotation(element, this.endpointReadOperationAnnotation) != null; } AnnotationMirror getNameAnnotation(Element element) { @@ -382,8 +382,8 @@ class MetadataGenerationEnvironment { return getTypeUseAnnotation(element, NULLABLE_ANNOTATION) != null; } - boolean hasOptionalParameterAnnotation(Element element) { - return getAnnotation(element, this.optionalParameterAnnotation) != null; + boolean hasEndpointOptionalParameterAnnotation(Element element) { + return getAnnotation(element, this.endpointOptionalParameterAnnotation) != null; } private boolean isElementDeprecated(Element element) { diff --git a/configuration-metadata/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/EndpointMetadataGenerationTests.java b/configuration-metadata/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/EndpointMetadataGenerationTests.java index 264f42585ac..093cdce81e2 100644 --- a/configuration-metadata/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/EndpointMetadataGenerationTests.java +++ b/configuration-metadata/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/EndpointMetadataGenerationTests.java @@ -28,7 +28,6 @@ import org.springframework.boot.configurationsample.endpoint.CamelCaseEndpoint; import org.springframework.boot.configurationsample.endpoint.CustomPropertiesEndpoint; import org.springframework.boot.configurationsample.endpoint.EnabledEndpoint; import org.springframework.boot.configurationsample.endpoint.NoAccessEndpoint; -import org.springframework.boot.configurationsample.endpoint.NullableParameterEndpoint; import org.springframework.boot.configurationsample.endpoint.OptionalParameterEndpoint; import org.springframework.boot.configurationsample.endpoint.ReadOnlyAccessEndpoint; import org.springframework.boot.configurationsample.endpoint.SimpleEndpoint; @@ -195,16 +194,6 @@ class EndpointMetadataGenerationTests extends AbstractMetadataGenerationTests { "Existing property 'management.endpoint.simple.access' from type org.springframework.boot.configurationsample.endpoint.SimpleEndpoint has a conflicting value. Existing value: unrestricted, new value from type org.springframework.boot.configurationsample.endpoint.SimpleEndpoint3: none"); } - @Test - void endpointWithNullableParameter() { - ConfigurationMetadata metadata = compile(NullableParameterEndpoint.class); - assertThat(metadata) - .has(Metadata.withGroup("management.endpoint.nullable").fromSource(NullableParameterEndpoint.class)); - assertThat(metadata).has(access("nullable", Access.UNRESTRICTED)); - assertThat(metadata).has(cacheTtl("nullable")); - assertThat(metadata.getItems()).hasSize(3); - } - @Test void endpointWithOptionalParameter() { ConfigurationMetadata metadata = compile(OptionalParameterEndpoint.class); diff --git a/configuration-metadata/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/test/TestConfigurationMetadataAnnotationProcessor.java b/configuration-metadata/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/test/TestConfigurationMetadataAnnotationProcessor.java index 83eaa5786d0..18783f63d7d 100644 --- a/configuration-metadata/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/test/TestConfigurationMetadataAnnotationProcessor.java +++ b/configuration-metadata/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/test/TestConfigurationMetadataAnnotationProcessor.java @@ -126,12 +126,12 @@ public class TestConfigurationMetadataAnnotationProcessor extends ConfigurationM } @Override - protected String readOperationAnnotation() { + protected String endpointReadOperationAnnotation() { return READ_OPERATION_ANNOTATION; } @Override - protected String optionalParameterAnnotation() { + protected String endpointOptionalParameterAnnotation() { return OPTIONAL_PARAMETER_ANNOTATION; } diff --git a/configuration-metadata/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/NullableParameterEndpoint.java b/configuration-metadata/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/NullableParameterEndpoint.java deleted file mode 100644 index f9488248141..00000000000 --- a/configuration-metadata/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/NullableParameterEndpoint.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2012-present 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. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.configurationsample.endpoint; - -import org.jspecify.annotations.Nullable; - -import org.springframework.boot.configurationsample.Endpoint; -import org.springframework.boot.configurationsample.ReadOperation; - -/** - * An endpoint that uses {@code Nullable} to signal an optional parameter. - * - * @author Wonyong Hwang - */ -@Endpoint(id = "nullable") -public class NullableParameterEndpoint { - - @ReadOperation - public String invoke(@Nullable String parameter) { - return "test with " + parameter; - } - -} diff --git a/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/invoke/reflect/OperationMethod.java b/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/invoke/reflect/OperationMethod.java index 564f69b7d3f..2910704f4e4 100644 --- a/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/invoke/reflect/OperationMethod.java +++ b/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/invoke/reflect/OperationMethod.java @@ -34,7 +34,7 @@ import org.springframework.util.Assert; * @since 2.0.0 * @see ReflectiveOperationInvoker */ -public class OperationMethod { +public abstract class OperationMethod { private static final ParameterNameDiscoverer DEFAULT_PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer(); @@ -44,18 +44,6 @@ public class OperationMethod { private final OperationParameters operationParameters; - /** - * Create a new {@link OperationMethod} instance. - * @param method the source method - * @param operationType the operation type - * @deprecated since 4.0.0 for removal in 4.2.0 in favor of - * {@link #OperationMethod(Method, OperationType, Predicate)} - */ - @Deprecated(since = "4.0.0", forRemoval = true) - public OperationMethod(Method method, OperationType operationType) { - this(method, operationType, (parameter) -> false); - } - /** * Create a new {@link OperationMethod} instance. * @param method the source method @@ -63,7 +51,7 @@ public class OperationMethod { * @param optionalParameters predicate to test if a parameter is optional * @since 4.0.0 */ - public OperationMethod(Method method, OperationType operationType, Predicate optionalParameters) { + protected OperationMethod(Method method, OperationType operationType, Predicate optionalParameters) { Assert.notNull(method, "'method' must not be null"); Assert.notNull(operationType, "'operationType' must not be null"); this.method = 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 89a89e4efa3..15368c34a46 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,7 +21,6 @@ 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}. @@ -61,11 +60,7 @@ class OperationMethodParameter implements OperationParameter { @Override public boolean isMandatory() { - return !isOptional(); - } - - private boolean isOptional() { - return Nullness.NULLABLE == Nullness.forParameter(this.parameter) || this.optional.test(this.parameter); + return !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 644583bef62..82f67600df4 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,12 +43,6 @@ 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); - private Method exampleAnnotation = ReflectionUtils.findMethod(getClass(), "exampleAnnotation", String.class); @Test @@ -79,20 +73,6 @@ 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", - this.exampleSpringNullable.getParameters()[1], this::isOptionalParameter); - assertThat(parameter.isMandatory()).isFalse(); - } - @Test void getAnnotationShouldReturnAnnotation() { OperationMethodParameter parameter = new OperationMethodParameter("name", @@ -109,13 +89,6 @@ 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) { - } - void exampleAnnotation(@Selector(match = Match.ALL_REMAINING) String allRemaining) { } diff --git a/module/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/invoke/reflect/OperationMethodTests.java b/module/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/invoke/reflect/OperationMethodTests.java index 987c7d02890..dba9e71a097 100644 --- a/module/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/invoke/reflect/OperationMethodTests.java +++ b/module/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/invoke/reflect/OperationMethodTests.java @@ -17,8 +17,6 @@ package org.springframework.boot.actuate.endpoint.invoke.reflect; import java.lang.reflect.Method; -import java.lang.reflect.Parameter; -import java.util.function.Predicate; import org.junit.jupiter.api.Test; @@ -36,39 +34,35 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException */ class OperationMethodTests { - private static final Predicate NON_OPTIONAL = (parameter) -> false; - private final Method exampleMethod = ReflectionUtils.findMethod(getClass(), "example", String.class); @Test void createWhenMethodIsNullShouldThrowException() { - assertThatIllegalArgumentException() - .isThrownBy(() -> new OperationMethod(null, OperationType.READ, NON_OPTIONAL)) + assertThatIllegalArgumentException().isThrownBy(() -> new TestOperationMethod(null, OperationType.READ)) .withMessageContaining("'method' must not be null"); } @Test void createWhenOperationTypeIsNullShouldThrowException() { - assertThatIllegalArgumentException() - .isThrownBy(() -> new OperationMethod(this.exampleMethod, null, NON_OPTIONAL)) + assertThatIllegalArgumentException().isThrownBy(() -> new TestOperationMethod(this.exampleMethod, null)) .withMessageContaining("'operationType' must not be null"); } @Test void getMethodShouldReturnMethod() { - OperationMethod operationMethod = new OperationMethod(this.exampleMethod, OperationType.READ, NON_OPTIONAL); + OperationMethod operationMethod = new TestOperationMethod(this.exampleMethod, OperationType.READ); assertThat(operationMethod.getMethod()).isEqualTo(this.exampleMethod); } @Test void getOperationTypeShouldReturnOperationType() { - OperationMethod operationMethod = new OperationMethod(this.exampleMethod, OperationType.READ, NON_OPTIONAL); + OperationMethod operationMethod = new TestOperationMethod(this.exampleMethod, OperationType.READ); assertThat(operationMethod.getOperationType()).isEqualTo(OperationType.READ); } @Test void getParametersShouldReturnParameters() { - OperationMethod operationMethod = new OperationMethod(this.exampleMethod, OperationType.READ, NON_OPTIONAL); + OperationMethod operationMethod = new TestOperationMethod(this.exampleMethod, OperationType.READ); OperationParameters parameters = operationMethod.getParameters(); assertThat(parameters.getParameterCount()).isOne(); assertThat(parameters.iterator().next().getName()).isEqualTo("name"); diff --git a/module/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/invoke/reflect/ReflectiveOperationInvokerTests.java b/module/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/invoke/reflect/ReflectiveOperationInvokerTests.java index 89f4574aa08..e80320cedf2 100644 --- a/module/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/invoke/reflect/ReflectiveOperationInvokerTests.java +++ b/module/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/invoke/reflect/ReflectiveOperationInvokerTests.java @@ -57,7 +57,7 @@ class ReflectiveOperationInvokerTests { @BeforeEach void setup() { this.target = new Example(); - this.operationMethod = new OperationMethod(ReflectionUtils.findMethod(Example.class, "reverse", + this.operationMethod = new TestOperationMethod(ReflectionUtils.findMethod(Example.class, "reverse", ApiVersion.class, SecurityContext.class, String.class), OperationType.READ, this::isOptional); this.parameterValueMapper = (parameter, value) -> (value != null) ? value.toString() : null; } @@ -102,7 +102,7 @@ class ReflectiveOperationInvokerTests { @Test void invokeWhenMissingOptionalArgumentShouldInvoke() { - OperationMethod operationMethod = new OperationMethod(ReflectionUtils.findMethod(Example.class, + OperationMethod operationMethod = new TestOperationMethod(ReflectionUtils.findMethod(Example.class, "reverseOptional", ApiVersion.class, SecurityContext.class, String.class), OperationType.READ, this::isOptional); ReflectiveOperationInvoker invoker = new ReflectiveOperationInvoker(this.target, operationMethod, diff --git a/module/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/invoke/reflect/TestOperationMethod.java b/module/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/invoke/reflect/TestOperationMethod.java new file mode 100644 index 00000000000..2d175c22032 --- /dev/null +++ b/module/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/invoke/reflect/TestOperationMethod.java @@ -0,0 +1,42 @@ +/* + * Copyright 2012-present 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.actuate.endpoint.invoke.reflect; + +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.util.function.Predicate; + +import org.springframework.boot.actuate.endpoint.OperationType; + +/** + * Test {@link OperationMethod}. + * + * @author Phillip Webb + */ +public class TestOperationMethod extends OperationMethod { + + public static final Predicate NON_OPTIONAL = (parameter) -> false; + + public TestOperationMethod(Method method, OperationType operationType) { + this(method, operationType, NON_OPTIONAL); + } + + public TestOperationMethod(Method method, OperationType operationType, Predicate optionalParameters) { + super(method, operationType, optionalParameters); + } + +} diff --git a/module/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/invoker/cache/CachingOperationInvokerAdvisorTests.java b/module/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/invoker/cache/CachingOperationInvokerAdvisorTests.java index 1621932fb3e..3f4bf5e8f27 100644 --- a/module/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/invoker/cache/CachingOperationInvokerAdvisorTests.java +++ b/module/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/invoker/cache/CachingOperationInvokerAdvisorTests.java @@ -37,6 +37,7 @@ import org.springframework.boot.actuate.endpoint.SecurityContext; import org.springframework.boot.actuate.endpoint.invoke.OperationInvoker; import org.springframework.boot.actuate.endpoint.invoke.OperationParameters; import org.springframework.boot.actuate.endpoint.invoke.reflect.OperationMethod; +import org.springframework.boot.actuate.endpoint.invoke.reflect.TestOperationMethod; import org.springframework.boot.actuate.endpoint.web.WebServerNamespace; import org.springframework.core.annotation.MergedAnnotations; import org.springframework.util.ReflectionUtils; @@ -163,7 +164,7 @@ class CachingOperationInvokerAdvisorTests { private OperationMethod getOperationMethod(String methodName, Class... parameterTypes) { Method method = ReflectionUtils.findMethod(TestOperations.class, methodName, parameterTypes); - return new OperationMethod(method, OperationType.READ, + return new TestOperationMethod(method, OperationType.READ, (parameter) -> MergedAnnotations.from(parameter).isPresent(TestOptional.class)); }