Browse Source

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
pull/47162/head
Phillip Webb 3 months ago
parent
commit
450eb48303
  1. 44
      configuration-metadata/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessor.java
  2. 18
      configuration-metadata/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/MetadataGenerationEnvironment.java
  3. 11
      configuration-metadata/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/EndpointMetadataGenerationTests.java
  4. 4
      configuration-metadata/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/test/TestConfigurationMetadataAnnotationProcessor.java
  5. 37
      configuration-metadata/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/NullableParameterEndpoint.java
  6. 16
      module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/invoke/reflect/OperationMethod.java
  7. 7
      module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/invoke/reflect/OperationMethodParameter.java
  8. 27
      module/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/invoke/reflect/OperationMethodParameterTests.java
  9. 16
      module/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/invoke/reflect/OperationMethodTests.java
  10. 4
      module/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/invoke/reflect/ReflectiveOperationInvokerTests.java
  11. 42
      module/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/invoke/reflect/TestOperationMethod.java
  12. 3
      module/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/invoker/cache/CachingOperationInvokerAdvisorTests.java

44
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 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"; 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); REST_CONTROLLER_ENDPOINT_ANNOTATION, SERVLET_ENDPOINT_ANNOTATION, WEB_ENDPOINT_ANNOTATION);
} }
protected String readOperationAnnotation() { protected String endpointReadOperationAnnotation() {
return READ_OPERATION_ANNOTATION; return ENDPOINT_READ_OPERATION_ANNOTATION;
} }
protected String nameAnnotation() { protected String nameAnnotation() {
return NAME_ANNOTATION; return NAME_ANNOTATION;
} }
protected String optionalParameterAnnotation() { protected String endpointOptionalParameterAnnotation() {
return OPTIONAL_PARAMETER_ANNOTATION; return ENDPOINT_OPTIONAL_PARAMETER_ANNOTATION;
} }
protected String endpointAccessEnum() { protected String endpointAccessEnum() {
@ -194,8 +194,8 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor
this.metadataEnv = new MetadataGenerationEnvironment(env, configurationPropertiesAnnotation(), this.metadataEnv = new MetadataGenerationEnvironment(env, configurationPropertiesAnnotation(),
configurationPropertiesSourceAnnotation(), nestedConfigurationPropertyAnnotation(), configurationPropertiesSourceAnnotation(), nestedConfigurationPropertyAnnotation(),
deprecatedConfigurationPropertyAnnotation(), constructorBindingAnnotation(), autowiredAnnotation(), deprecatedConfigurationPropertyAnnotation(), constructorBindingAnnotation(), autowiredAnnotation(),
defaultValueAnnotation(), endpointAnnotations(), readOperationAnnotation(), defaultValueAnnotation(), endpointAnnotations(), endpointReadOperationAnnotation(),
optionalParameterAnnotation(), nameAnnotation()); endpointOptionalParameterAnnotation(), nameAnnotation());
} }
@Override @Override
@ -271,8 +271,7 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor
} }
private void processExecutableElement(String prefix, ExecutableElement element, Deque<TypeElement> seen) { private void processExecutableElement(String prefix, ExecutableElement element, Deque<TypeElement> seen) {
if ((!element.getModifiers().contains(Modifier.PRIVATE)) if ((!element.getModifiers().contains(Modifier.PRIVATE)) && returnsVoid(element)) {
&& (TypeKind.VOID != element.getReturnType().getKind())) {
Element returns = this.processingEnv.getTypeUtils().asElement(element.getReturnType()); Element returns = this.processingEnv.getTypeUtils().asElement(element.getReturnType());
if (returns instanceof TypeElement typeElement) { if (returns instanceof TypeElement typeElement) {
ItemMetadata group = ItemMetadata.newGroup(prefix, 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); "Permitted level of access for the %s endpoint.".formatted(endpointId), defaultAccess, null);
this.metadataCollector.add(accessProperty, this.metadataCollector.add(accessProperty,
(existing) -> checkDefaultAccessValueMatchesExisting(existing, defaultAccess, type)); (existing) -> checkDefaultAccessValueMatchesExisting(existing, defaultAccess, type));
if (hasMainReadOperation(element)) { if (isCachableEndpoint(element)) {
this.metadataCollector.addIfAbsent(ItemMetadata.newProperty(endpointKey, "cache.time-to-live", 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)); 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())) { for (ExecutableElement method : ElementFilter.methodsIn(element.getEnclosedElements())) {
if (this.metadataEnv.getReadOperationAnnotation(method) != null if (this.metadataEnv.isEndpointReadOperation(method) && returnsVoid(method)
&& (TypeKind.VOID != method.getReturnType().getKind()) && hasNoOrOptionalParameters(method)) { && !hasMandatoryEndpointParameter(method)) {
return true; return true;
} }
} }
return false; return false;
} }
private boolean hasNoOrOptionalParameters(ExecutableElement method) { private boolean returnsVoid(ExecutableElement method) {
for (VariableElement parameter : method.getParameters()) { return TypeKind.VOID != method.getReturnType().getKind();
if (!isOptionalParameter(parameter)) {
return false;
}
} }
private boolean hasMandatoryEndpointParameter(ExecutableElement method) {
for (VariableElement parameter : method.getParameters()) {
if (!this.metadataEnv.hasEndpointOptionalParameterAnnotation(parameter)) {
return true; return true;
} }
}
private boolean isOptionalParameter(VariableElement parameter) { return false;
return this.metadataEnv.hasNullableAnnotation(parameter)
|| this.metadataEnv.hasOptionalParameterAnnotation(parameter);
} }
private String getPrefix(AnnotationMirror annotation) { private String getPrefix(AnnotationMirror annotation) {

18
configuration-metadata/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/MetadataGenerationEnvironment.java

@ -96,9 +96,9 @@ class MetadataGenerationEnvironment {
private final Set<String> endpointAnnotations; private final Set<String> endpointAnnotations;
private final String readOperationAnnotation; private final String endpointReadOperationAnnotation;
private final String optionalParameterAnnotation; private final String endpointOptionalParameterAnnotation;
private final String nameAnnotation; private final String nameAnnotation;
@ -108,7 +108,7 @@ class MetadataGenerationEnvironment {
String configurationPropertiesSourceAnnotation, String nestedConfigurationPropertyAnnotation, String configurationPropertiesSourceAnnotation, String nestedConfigurationPropertyAnnotation,
String deprecatedConfigurationPropertyAnnotation, String constructorBindingAnnotation, String deprecatedConfigurationPropertyAnnotation, String constructorBindingAnnotation,
String autowiredAnnotation, String defaultValueAnnotation, Set<String> endpointAnnotations, String autowiredAnnotation, String defaultValueAnnotation, Set<String> endpointAnnotations,
String readOperationAnnotation, String optionalParameterAnnotation, String nameAnnotation) { String endpointReadOperationAnnotation, String endpointOptionalParameterAnnotation, String nameAnnotation) {
this.typeUtils = new TypeUtils(environment); this.typeUtils = new TypeUtils(environment);
this.elements = environment.getElementUtils(); this.elements = environment.getElementUtils();
this.messager = environment.getMessager(); this.messager = environment.getMessager();
@ -122,8 +122,8 @@ class MetadataGenerationEnvironment {
this.autowiredAnnotation = autowiredAnnotation; this.autowiredAnnotation = autowiredAnnotation;
this.defaultValueAnnotation = defaultValueAnnotation; this.defaultValueAnnotation = defaultValueAnnotation;
this.endpointAnnotations = endpointAnnotations; this.endpointAnnotations = endpointAnnotations;
this.readOperationAnnotation = readOperationAnnotation; this.endpointReadOperationAnnotation = endpointReadOperationAnnotation;
this.optionalParameterAnnotation = optionalParameterAnnotation; this.endpointOptionalParameterAnnotation = endpointOptionalParameterAnnotation;
this.nameAnnotation = nameAnnotation; this.nameAnnotation = nameAnnotation;
} }
@ -370,8 +370,8 @@ class MetadataGenerationEnvironment {
.collect(Collectors.toSet()); .collect(Collectors.toSet());
} }
AnnotationMirror getReadOperationAnnotation(Element element) { boolean isEndpointReadOperation(Element element) {
return getAnnotation(element, this.readOperationAnnotation); return getAnnotation(element, this.endpointReadOperationAnnotation) != null;
} }
AnnotationMirror getNameAnnotation(Element element) { AnnotationMirror getNameAnnotation(Element element) {
@ -382,8 +382,8 @@ class MetadataGenerationEnvironment {
return getTypeUseAnnotation(element, NULLABLE_ANNOTATION) != null; return getTypeUseAnnotation(element, NULLABLE_ANNOTATION) != null;
} }
boolean hasOptionalParameterAnnotation(Element element) { boolean hasEndpointOptionalParameterAnnotation(Element element) {
return getAnnotation(element, this.optionalParameterAnnotation) != null; return getAnnotation(element, this.endpointOptionalParameterAnnotation) != null;
} }
private boolean isElementDeprecated(Element element) { private boolean isElementDeprecated(Element element) {

11
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.CustomPropertiesEndpoint;
import org.springframework.boot.configurationsample.endpoint.EnabledEndpoint; import org.springframework.boot.configurationsample.endpoint.EnabledEndpoint;
import org.springframework.boot.configurationsample.endpoint.NoAccessEndpoint; 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.OptionalParameterEndpoint;
import org.springframework.boot.configurationsample.endpoint.ReadOnlyAccessEndpoint; import org.springframework.boot.configurationsample.endpoint.ReadOnlyAccessEndpoint;
import org.springframework.boot.configurationsample.endpoint.SimpleEndpoint; 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"); "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 @Test
void endpointWithOptionalParameter() { void endpointWithOptionalParameter() {
ConfigurationMetadata metadata = compile(OptionalParameterEndpoint.class); ConfigurationMetadata metadata = compile(OptionalParameterEndpoint.class);

4
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 @Override
protected String readOperationAnnotation() { protected String endpointReadOperationAnnotation() {
return READ_OPERATION_ANNOTATION; return READ_OPERATION_ANNOTATION;
} }
@Override @Override
protected String optionalParameterAnnotation() { protected String endpointOptionalParameterAnnotation() {
return OPTIONAL_PARAMETER_ANNOTATION; return OPTIONAL_PARAMETER_ANNOTATION;
} }

37
configuration-metadata/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/NullableParameterEndpoint.java

@ -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;
}
}

16
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 * @since 2.0.0
* @see ReflectiveOperationInvoker * @see ReflectiveOperationInvoker
*/ */
public class OperationMethod { public abstract class OperationMethod {
private static final ParameterNameDiscoverer DEFAULT_PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer(); private static final ParameterNameDiscoverer DEFAULT_PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer();
@ -44,18 +44,6 @@ public class OperationMethod {
private final OperationParameters operationParameters; 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. * Create a new {@link OperationMethod} instance.
* @param method the source method * @param method the source method
@ -63,7 +51,7 @@ public class OperationMethod {
* @param optionalParameters predicate to test if a parameter is optional * @param optionalParameters predicate to test if a parameter is optional
* @since 4.0.0 * @since 4.0.0
*/ */
public OperationMethod(Method method, OperationType operationType, Predicate<Parameter> optionalParameters) { protected OperationMethod(Method method, OperationType operationType, Predicate<Parameter> optionalParameters) {
Assert.notNull(method, "'method' must not be null"); Assert.notNull(method, "'method' must not be null");
Assert.notNull(operationType, "'operationType' must not be null"); Assert.notNull(operationType, "'operationType' must not be null");
this.method = method; this.method = method;

7
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 java.util.function.Predicate;
import org.springframework.boot.actuate.endpoint.invoke.OperationParameter; import org.springframework.boot.actuate.endpoint.invoke.OperationParameter;
import org.springframework.core.Nullness;
/** /**
* {@link OperationParameter} created from an {@link OperationMethod}. * {@link OperationParameter} created from an {@link OperationMethod}.
@ -61,11 +60,7 @@ class OperationMethodParameter implements OperationParameter {
@Override @Override
public boolean isMandatory() { public boolean isMandatory() {
return !isOptional(); return !this.optional.test(this.parameter);
}
private boolean isOptional() {
return Nullness.NULLABLE == Nullness.forParameter(this.parameter) || this.optional.test(this.parameter);
} }
@Override @Override

27
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 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); private Method exampleAnnotation = ReflectionUtils.findMethod(getClass(), "exampleAnnotation", String.class);
@Test @Test
@ -79,20 +73,6 @@ class OperationMethodParameterTests {
assertThat(parameter.isMandatory()).isFalse(); 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 @Test
void getAnnotationShouldReturnAnnotation() { void getAnnotationShouldReturnAnnotation() {
OperationMethodParameter parameter = new OperationMethodParameter("name", OperationMethodParameter parameter = new OperationMethodParameter("name",
@ -109,13 +89,6 @@ class OperationMethodParameterTests {
void example(String one, @TestOptional String two) { 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) { void exampleAnnotation(@Selector(match = Match.ALL_REMAINING) String allRemaining) {
} }

16
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; package org.springframework.boot.actuate.endpoint.invoke.reflect;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.function.Predicate;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -36,39 +34,35 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
*/ */
class OperationMethodTests { class OperationMethodTests {
private static final Predicate<Parameter> NON_OPTIONAL = (parameter) -> false;
private final Method exampleMethod = ReflectionUtils.findMethod(getClass(), "example", String.class); private final Method exampleMethod = ReflectionUtils.findMethod(getClass(), "example", String.class);
@Test @Test
void createWhenMethodIsNullShouldThrowException() { void createWhenMethodIsNullShouldThrowException() {
assertThatIllegalArgumentException() assertThatIllegalArgumentException().isThrownBy(() -> new TestOperationMethod(null, OperationType.READ))
.isThrownBy(() -> new OperationMethod(null, OperationType.READ, NON_OPTIONAL))
.withMessageContaining("'method' must not be null"); .withMessageContaining("'method' must not be null");
} }
@Test @Test
void createWhenOperationTypeIsNullShouldThrowException() { void createWhenOperationTypeIsNullShouldThrowException() {
assertThatIllegalArgumentException() assertThatIllegalArgumentException().isThrownBy(() -> new TestOperationMethod(this.exampleMethod, null))
.isThrownBy(() -> new OperationMethod(this.exampleMethod, null, NON_OPTIONAL))
.withMessageContaining("'operationType' must not be null"); .withMessageContaining("'operationType' must not be null");
} }
@Test @Test
void getMethodShouldReturnMethod() { 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); assertThat(operationMethod.getMethod()).isEqualTo(this.exampleMethod);
} }
@Test @Test
void getOperationTypeShouldReturnOperationType() { 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); assertThat(operationMethod.getOperationType()).isEqualTo(OperationType.READ);
} }
@Test @Test
void getParametersShouldReturnParameters() { void getParametersShouldReturnParameters() {
OperationMethod operationMethod = new OperationMethod(this.exampleMethod, OperationType.READ, NON_OPTIONAL); OperationMethod operationMethod = new TestOperationMethod(this.exampleMethod, OperationType.READ);
OperationParameters parameters = operationMethod.getParameters(); OperationParameters parameters = operationMethod.getParameters();
assertThat(parameters.getParameterCount()).isOne(); assertThat(parameters.getParameterCount()).isOne();
assertThat(parameters.iterator().next().getName()).isEqualTo("name"); assertThat(parameters.iterator().next().getName()).isEqualTo("name");

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

@ -57,7 +57,7 @@ class ReflectiveOperationInvokerTests {
@BeforeEach @BeforeEach
void setup() { void setup() {
this.target = new Example(); 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); ApiVersion.class, SecurityContext.class, String.class), OperationType.READ, this::isOptional);
this.parameterValueMapper = (parameter, value) -> (value != null) ? value.toString() : null; this.parameterValueMapper = (parameter, value) -> (value != null) ? value.toString() : null;
} }
@ -102,7 +102,7 @@ class ReflectiveOperationInvokerTests {
@Test @Test
void invokeWhenMissingOptionalArgumentShouldInvoke() { 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, "reverseOptional", ApiVersion.class, SecurityContext.class, String.class), OperationType.READ,
this::isOptional); this::isOptional);
ReflectiveOperationInvoker invoker = new ReflectiveOperationInvoker(this.target, operationMethod, ReflectiveOperationInvoker invoker = new ReflectiveOperationInvoker(this.target, operationMethod,

42
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<Parameter> NON_OPTIONAL = (parameter) -> false;
public TestOperationMethod(Method method, OperationType operationType) {
this(method, operationType, NON_OPTIONAL);
}
public TestOperationMethod(Method method, OperationType operationType, Predicate<Parameter> optionalParameters) {
super(method, operationType, optionalParameters);
}
}

3
module/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/invoker/cache/CachingOperationInvokerAdvisorTests.java vendored

@ -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.OperationInvoker;
import org.springframework.boot.actuate.endpoint.invoke.OperationParameters; 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.OperationMethod;
import org.springframework.boot.actuate.endpoint.invoke.reflect.TestOperationMethod;
import org.springframework.boot.actuate.endpoint.web.WebServerNamespace; import org.springframework.boot.actuate.endpoint.web.WebServerNamespace;
import org.springframework.core.annotation.MergedAnnotations; import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.util.ReflectionUtils; import org.springframework.util.ReflectionUtils;
@ -163,7 +164,7 @@ class CachingOperationInvokerAdvisorTests {
private OperationMethod getOperationMethod(String methodName, Class<?>... parameterTypes) { private OperationMethod getOperationMethod(String methodName, Class<?>... parameterTypes) {
Method method = ReflectionUtils.findMethod(TestOperations.class, methodName, 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)); (parameter) -> MergedAnnotations.from(parameter).isPresent(TestOptional.class));
} }

Loading…
Cancel
Save