Browse Source

Replace @OptionalParameter with JSpecify's @Nullable

This commit removes OptionalParameter in favor of the nullness support
introduced in Spring Framework 7. The parameter of an action can now
be flagged as optional using JSpecify's @Nullable, and simplifies the
setup for those who are using JSpecify as only a single annotation is
required.

Closes gh-45390
pull/47162/head
Stéphane Nicoll 4 months ago
parent
commit
d7c482aa16
  1. 16
      configuration-metadata/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessor.java
  2. 9
      configuration-metadata/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/MetadataGenerationEnvironment.java
  3. 13
      configuration-metadata/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/EndpointMetadataGenerationTests.java
  4. 1
      configuration-metadata/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/MetadataGenerationEnvironmentFactory.java
  5. 7
      configuration-metadata/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/test/TestConfigurationMetadataAnnotationProcessor.java
  6. 36
      configuration-metadata/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/OptionalParameter.java
  7. 36
      configuration-metadata/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/OptionalParameterEndpoint.java
  8. 5
      configuration-metadata/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/SpecificEndpoint.java
  9. 5
      configuration-metadata/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/incremental/IncrementalEndpoint.java
  10. 4
      documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/endpoints.adoc
  11. 8
      integration-test/spring-boot-actuator-integration-tests/src/test/java/org/springframework/boot/actuate/endpoint/web/annotation/AbstractWebEndpointIntegrationTests.java
  12. 5
      module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/audit/AuditEventsEndpoint.java
  13. 8
      module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/DiscoveredOperationMethod.java
  14. 36
      module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/OptionalParameter.java
  15. 19
      module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/invoke/reflect/OperationMethod.java
  16. 13
      module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/invoke/reflect/OperationMethodParameter.java
  17. 13
      module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/invoke/reflect/OperationMethodParameters.java
  18. 3
      module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/env/EnvironmentEndpoint.java
  19. 4
      module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/env/EnvironmentEndpointWebExtension.java
  20. 3
      module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/logging/LoggersEndpoint.java
  21. 3
      module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/management/HeapDumpWebEndpoint.java
  22. 4
      module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/prometheus/PrometheusScrapeEndpoint.java
  23. 56
      module/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/invoke/reflect/OperationMethodParameterTests.java
  24. 22
      module/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/invoke/reflect/OperationMethodParametersTests.java
  25. 16
      module/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/invoke/reflect/OperationMethodTests.java
  26. 26
      module/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/invoke/reflect/ReflectiveOperationInvokerTests.java
  27. 28
      module/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/invoker/cache/CachingOperationInvokerAdvisorTests.java
  28. 6
      module/spring-boot-cache/src/main/java/org/springframework/boot/cache/actuate/endpoint/CachesEndpoint.java
  29. 7
      module/spring-boot-cache/src/main/java/org/springframework/boot/cache/actuate/endpoint/CachesEndpointWebExtension.java
  30. 4
      module/spring-boot-micrometer-metrics/src/main/java/org/springframework/boot/micrometer/metrics/actuate/endpoint/MetricsEndpoint.java

16
configuration-metadata/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessor.java

@ -109,8 +109,6 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor @@ -109,8 +109,6 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor
static final String 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 NAME_ANNOTATION = "org.springframework.boot.context.properties.bind.Name";
static final String ENDPOINT_ACCESS_ENUM = "org.springframework.boot.actuate.endpoint.Access";
@ -166,10 +164,6 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor @@ -166,10 +164,6 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor
return NAME_ANNOTATION;
}
protected String optionalParameterAnnotation() {
return OPTIONAL_PARAMETER_ANNOTATION;
}
protected String endpointAccessEnum() {
return ENDPOINT_ACCESS_ENUM;
}
@ -194,8 +188,7 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor @@ -194,8 +188,7 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor
this.metadataEnv = new MetadataGenerationEnvironment(env, configurationPropertiesAnnotation(),
configurationPropertiesSourceAnnotation(), nestedConfigurationPropertyAnnotation(),
deprecatedConfigurationPropertyAnnotation(), constructorBindingAnnotation(), autowiredAnnotation(),
defaultValueAnnotation(), endpointAnnotations(), readOperationAnnotation(),
optionalParameterAnnotation(), nameAnnotation());
defaultValueAnnotation(), endpointAnnotations(), readOperationAnnotation(), nameAnnotation());
}
@Override
@ -383,18 +376,13 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor @@ -383,18 +376,13 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor
private boolean hasNoOrOptionalParameters(ExecutableElement method) {
for (VariableElement parameter : method.getParameters()) {
if (!isOptionalParameter(parameter)) {
if (!this.metadataEnv.hasNullableAnnotation(parameter)) {
return false;
}
}
return true;
}
private boolean isOptionalParameter(VariableElement parameter) {
return this.metadataEnv.hasNullableAnnotation(parameter)
|| this.metadataEnv.hasOptionalParameterAnnotation(parameter);
}
private String getPrefix(AnnotationMirror annotation) {
String prefix = this.metadataEnv.getAnnotationElementStringValue(annotation, "prefix");
if (prefix != null) {

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

@ -98,8 +98,6 @@ class MetadataGenerationEnvironment { @@ -98,8 +98,6 @@ class MetadataGenerationEnvironment {
private final String readOperationAnnotation;
private final String optionalParameterAnnotation;
private final String nameAnnotation;
private final String autowiredAnnotation;
@ -108,7 +106,7 @@ class MetadataGenerationEnvironment { @@ -108,7 +106,7 @@ class MetadataGenerationEnvironment {
String configurationPropertiesSourceAnnotation, String nestedConfigurationPropertyAnnotation,
String deprecatedConfigurationPropertyAnnotation, String constructorBindingAnnotation,
String autowiredAnnotation, String defaultValueAnnotation, Set<String> endpointAnnotations,
String readOperationAnnotation, String optionalParameterAnnotation, String nameAnnotation) {
String readOperationAnnotation, String nameAnnotation) {
this.typeUtils = new TypeUtils(environment);
this.elements = environment.getElementUtils();
this.messager = environment.getMessager();
@ -123,7 +121,6 @@ class MetadataGenerationEnvironment { @@ -123,7 +121,6 @@ class MetadataGenerationEnvironment {
this.defaultValueAnnotation = defaultValueAnnotation;
this.endpointAnnotations = endpointAnnotations;
this.readOperationAnnotation = readOperationAnnotation;
this.optionalParameterAnnotation = optionalParameterAnnotation;
this.nameAnnotation = nameAnnotation;
}
@ -382,10 +379,6 @@ class MetadataGenerationEnvironment { @@ -382,10 +379,6 @@ class MetadataGenerationEnvironment {
return getTypeUseAnnotation(element, NULLABLE_ANNOTATION) != null;
}
boolean hasOptionalParameterAnnotation(Element element) {
return getAnnotation(element, this.optionalParameterAnnotation) != null;
}
private boolean isElementDeprecated(Element element) {
return hasAnnotation(element, "java.lang.Deprecated")
|| hasAnnotation(element, this.deprecatedConfigurationPropertyAnnotation);

13
configuration-metadata/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/EndpointMetadataGenerationTests.java

@ -29,7 +29,6 @@ import org.springframework.boot.configurationsample.endpoint.CustomPropertiesEnd @@ -29,7 +29,6 @@ import org.springframework.boot.configurationsample.endpoint.CustomPropertiesEnd
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;
import org.springframework.boot.configurationsample.endpoint.SimpleEndpoint2;
@ -153,7 +152,7 @@ class EndpointMetadataGenerationTests extends AbstractMetadataGenerationTests { @@ -153,7 +152,7 @@ class EndpointMetadataGenerationTests extends AbstractMetadataGenerationTests {
assertThat(metadata).has(access("incremental", Access.UNRESTRICTED));
assertThat(metadata).has(cacheTtl("incremental"));
assertThat(metadata.getItems()).hasSize(3);
project.replaceText(IncrementalEndpoint.class, "@OptionalParameter String param", "String param");
project.replaceText(IncrementalEndpoint.class, "@Nullable String param", "String param");
metadata = project.compile();
assertThat(metadata)
.has(Metadata.withGroup("management.endpoint.incremental").fromSource(IncrementalEndpoint.class));
@ -205,16 +204,6 @@ class EndpointMetadataGenerationTests extends AbstractMetadataGenerationTests { @@ -205,16 +204,6 @@ class EndpointMetadataGenerationTests extends AbstractMetadataGenerationTests {
assertThat(metadata.getItems()).hasSize(3);
}
@Test
void endpointWithOptionalParameter() {
ConfigurationMetadata metadata = compile(OptionalParameterEndpoint.class);
assertThat(metadata)
.has(Metadata.withGroup("management.endpoint.optional").fromSource(OptionalParameterEndpoint.class));
assertThat(metadata).has(access("optional", Access.UNRESTRICTED));
assertThat(metadata).has(cacheTtl("optional"));
assertThat(metadata.getItems()).hasSize(3);
}
private Metadata.MetadataItemCondition access(String endpointId, Access defaultValue) {
return defaultAccess(endpointId, endpointId, defaultValue);
}

1
configuration-metadata/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/MetadataGenerationEnvironmentFactory.java

@ -49,7 +49,6 @@ class MetadataGenerationEnvironmentFactory implements Function<ProcessingEnviron @@ -49,7 +49,6 @@ class MetadataGenerationEnvironmentFactory implements Function<ProcessingEnviron
TestConfigurationMetadataAnnotationProcessor.AUTOWIRED_ANNOTATION,
TestConfigurationMetadataAnnotationProcessor.DEFAULT_VALUE_ANNOTATION, endpointAnnotations,
TestConfigurationMetadataAnnotationProcessor.READ_OPERATION_ANNOTATION,
TestConfigurationMetadataAnnotationProcessor.OPTIONAL_PARAMETER_ANNOTATION,
TestConfigurationMetadataAnnotationProcessor.NAME_ANNOTATION);
}

7
configuration-metadata/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/test/TestConfigurationMetadataAnnotationProcessor.java

@ -75,8 +75,6 @@ public class TestConfigurationMetadataAnnotationProcessor extends ConfigurationM @@ -75,8 +75,6 @@ public class TestConfigurationMetadataAnnotationProcessor extends ConfigurationM
public static final String READ_OPERATION_ANNOTATION = "org.springframework.boot.configurationsample.ReadOperation";
public static final String OPTIONAL_PARAMETER_ANNOTATION = "org.springframework.boot.configurationsample.OptionalParameter";
public static final String NAME_ANNOTATION = "org.springframework.boot.configurationsample.Name";
public static final String ENDPOINT_ACCESS_ENUM = "org.springframework.boot.configurationsample.Access";
@ -130,11 +128,6 @@ public class TestConfigurationMetadataAnnotationProcessor extends ConfigurationM @@ -130,11 +128,6 @@ public class TestConfigurationMetadataAnnotationProcessor extends ConfigurationM
return READ_OPERATION_ANNOTATION;
}
@Override
protected String optionalParameterAnnotation() {
return OPTIONAL_PARAMETER_ANNOTATION;
}
@Override
protected String nameAnnotation() {
return NAME_ANNOTATION;

36
configuration-metadata/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/OptionalParameter.java

@ -1,36 +0,0 @@ @@ -1,36 +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;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Alternative to Spring Boot's {@code @OptionalParameter} for testing (removes the need
* for a dependency on the real annotation).
*
* @author Phillip Webb
*/
@Target({ ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OptionalParameter {
}

36
configuration-metadata/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/OptionalParameterEndpoint.java

@ -1,36 +0,0 @@ @@ -1,36 +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.springframework.boot.configurationsample.Endpoint;
import org.springframework.boot.configurationsample.OptionalParameter;
import org.springframework.boot.configurationsample.ReadOperation;
/**
* An endpoint that uses {@code OptionalParameter} to signal an optional parameter.
*
* @author Wonyong Hwang
*/
@Endpoint(id = "optional")
public class OptionalParameterEndpoint {
@ReadOperation
public String invoke(@OptionalParameter String parameter) {
return "test with " + parameter;
}
}

5
configuration-metadata/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/SpecificEndpoint.java

@ -16,8 +16,9 @@ @@ -16,8 +16,9 @@
package org.springframework.boot.configurationsample.endpoint;
import org.jspecify.annotations.Nullable;
import org.springframework.boot.configurationsample.Access;
import org.springframework.boot.configurationsample.OptionalParameter;
import org.springframework.boot.configurationsample.ReadOperation;
import org.springframework.boot.configurationsample.WebEndpoint;
@ -31,7 +32,7 @@ import org.springframework.boot.configurationsample.WebEndpoint; @@ -31,7 +32,7 @@ import org.springframework.boot.configurationsample.WebEndpoint;
public class SpecificEndpoint {
@ReadOperation
String invoke(@OptionalParameter String param) {
String invoke(@Nullable String param) {
return "test";
}

5
configuration-metadata/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/incremental/IncrementalEndpoint.java

@ -16,8 +16,9 @@ @@ -16,8 +16,9 @@
package org.springframework.boot.configurationsample.endpoint.incremental;
import org.jspecify.annotations.Nullable;
import org.springframework.boot.configurationsample.Endpoint;
import org.springframework.boot.configurationsample.OptionalParameter;
import org.springframework.boot.configurationsample.ReadOperation;
/**
@ -29,7 +30,7 @@ import org.springframework.boot.configurationsample.ReadOperation; @@ -29,7 +30,7 @@ import org.springframework.boot.configurationsample.ReadOperation;
public class IncrementalEndpoint {
@ReadOperation
public String invoke(@OptionalParameter String param) {
public String invoke(@Nullable String param) {
return "test";
}

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

@ -412,7 +412,7 @@ Operations on an endpoint receive input through their parameters. @@ -412,7 +412,7 @@ 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 JSpecify's javadoc:org.jspecify.annotations.Nullable[format=annotation].
They can be made optional by annotating them with 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.
@ -544,7 +544,7 @@ When using Spring MVC or Spring Web Flux, operations that return a javadoc:org.s @@ -544,7 +544,7 @@ When using Spring MVC or Spring Web Flux, operations that return a javadoc:org.s
==== 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.jspecify.annotations.Nullable[format=annotation] to provide different behavior for authenticated and unauthenticated users.
The former is typically used in conjunction with 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.

8
integration-test/spring-boot-actuator-integration-tests/src/test/java/org/springframework/boot/actuate/endpoint/web/annotation/AbstractWebEndpointIntegrationTests.java

@ -27,6 +27,7 @@ import java.util.function.BiConsumer; @@ -27,6 +27,7 @@ import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Supplier;
import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@ -34,7 +35,6 @@ import reactor.core.publisher.Mono; @@ -34,7 +35,6 @@ import reactor.core.publisher.Mono;
import org.springframework.boot.actuate.endpoint.SecurityContext;
import org.springframework.boot.actuate.endpoint.annotation.DeleteOperation;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.OptionalParameter;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.boot.actuate.endpoint.annotation.Selector;
import org.springframework.boot.actuate.endpoint.annotation.Selector.Match;
@ -982,7 +982,7 @@ public abstract class AbstractWebEndpointIntegrationTests<T extends Configurable @@ -982,7 +982,7 @@ public abstract class AbstractWebEndpointIntegrationTests<T extends Configurable
}
@WriteOperation
void write(@OptionalParameter String foo, @OptionalParameter String bar) {
void write(@Nullable String foo, @Nullable String bar) {
this.endpointDelegate.write(foo, bar);
}
@ -1167,7 +1167,7 @@ public abstract class AbstractWebEndpointIntegrationTests<T extends Configurable @@ -1167,7 +1167,7 @@ public abstract class AbstractWebEndpointIntegrationTests<T extends Configurable
static class RequiredParametersEndpoint {
@ReadOperation
String read(String foo, @OptionalParameter String bar) {
String read(String foo, @Nullable String bar) {
return foo;
}
@ -1177,7 +1177,7 @@ public abstract class AbstractWebEndpointIntegrationTests<T extends Configurable @@ -1177,7 +1177,7 @@ public abstract class AbstractWebEndpointIntegrationTests<T extends Configurable
static class PrincipalEndpoint {
@ReadOperation
String read(@OptionalParameter Principal principal) {
String read(@Nullable Principal principal) {
return (principal != null) ? principal.getName() : "None";
}

5
module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/audit/AuditEventsEndpoint.java

@ -24,7 +24,6 @@ import org.jspecify.annotations.Nullable; @@ -24,7 +24,6 @@ import org.jspecify.annotations.Nullable;
import org.springframework.boot.actuate.endpoint.OperationResponseBody;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.OptionalParameter;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.util.Assert;
@ -45,8 +44,8 @@ public class AuditEventsEndpoint { @@ -45,8 +44,8 @@ public class AuditEventsEndpoint {
}
@ReadOperation
public AuditEventsDescriptor events(@OptionalParameter @Nullable String principal,
@OptionalParameter @Nullable OffsetDateTime after, @OptionalParameter @Nullable String type) {
public AuditEventsDescriptor events(@Nullable String principal, @Nullable OffsetDateTime after,
@Nullable String type) {
List<AuditEvent> events = this.auditEventRepository.find(principal, getInstant(after), type);
return new AuditEventsDescriptor(events);
}

8
module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/DiscoveredOperationMethod.java

@ -17,7 +17,6 @@ @@ -17,7 +17,6 @@
package org.springframework.boot.actuate.endpoint.annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@ -27,7 +26,6 @@ import org.springframework.boot.actuate.endpoint.OperationType; @@ -27,7 +26,6 @@ import org.springframework.boot.actuate.endpoint.OperationType;
import org.springframework.boot.actuate.endpoint.Producible;
import org.springframework.boot.actuate.endpoint.invoke.reflect.OperationMethod;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.util.Assert;
/**
@ -42,7 +40,7 @@ public class DiscoveredOperationMethod extends OperationMethod { @@ -42,7 +40,7 @@ public class DiscoveredOperationMethod extends OperationMethod {
public DiscoveredOperationMethod(Method method, OperationType operationType,
AnnotationAttributes annotationAttributes) {
super(method, operationType, DiscoveredOperationMethod::isOptionalParameter);
super(method, operationType);
Assert.notNull(annotationAttributes, "'annotationAttributes' must not be null");
List<String> producesMediaTypes = new ArrayList<>();
producesMediaTypes.addAll(Arrays.asList(annotationAttributes.getStringArray("produces")));
@ -50,10 +48,6 @@ public class DiscoveredOperationMethod extends OperationMethod { @@ -50,10 +48,6 @@ public class DiscoveredOperationMethod extends OperationMethod {
this.producesMediaTypes = Collections.unmodifiableList(producesMediaTypes);
}
private static boolean isOptionalParameter(Parameter parameter) {
return MergedAnnotations.from(parameter).isPresent(OptionalParameter.class);
}
private <E extends Enum<E> & Producible<E>> List<String> getProducesFromProducible(
AnnotationAttributes annotationAttributes) {
Class<?> type = getProducesFrom(annotationAttributes);

36
module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/OptionalParameter.java

@ -1,36 +0,0 @@ @@ -1,36 +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.actuate.endpoint.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation that indicates that an operation parameter is optional.
*
* @author Phillip Webb
* @since 4.0.0
*/
@Target({ ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OptionalParameter {
}

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

@ -17,9 +17,7 @@ @@ -17,9 +17,7 @@
package org.springframework.boot.actuate.endpoint.invoke.reflect;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Locale;
import java.util.function.Predicate;
import org.springframework.boot.actuate.endpoint.OperationType;
import org.springframework.boot.actuate.endpoint.invoke.OperationParameters;
@ -48,28 +46,13 @@ public class OperationMethod { @@ -48,28 +46,13 @@ public class OperationMethod {
* 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
* @param operationType the operation type
* @param optionalParameters predicate to test if a parameter is optional
* @since 4.0.0
*/
public OperationMethod(Method method, OperationType operationType, Predicate<Parameter> optionalParameters) {
Assert.notNull(method, "'method' must not be null");
Assert.notNull(operationType, "'operationType' must not be null");
this.method = method;
this.operationType = operationType;
this.operationParameters = new OperationMethodParameters(method, DEFAULT_PARAMETER_NAME_DISCOVERER,
optionalParameters);
this.operationParameters = new OperationMethodParameters(method, DEFAULT_PARAMETER_NAME_DISCOVERER);
}
/**

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

@ -18,7 +18,6 @@ package org.springframework.boot.actuate.endpoint.invoke.reflect; @@ -18,7 +18,6 @@ package org.springframework.boot.actuate.endpoint.invoke.reflect;
import java.lang.annotation.Annotation;
import java.lang.reflect.Parameter;
import java.util.function.Predicate;
import org.springframework.boot.actuate.endpoint.invoke.OperationParameter;
import org.springframework.core.Nullness;
@ -35,18 +34,14 @@ class OperationMethodParameter implements OperationParameter { @@ -35,18 +34,14 @@ class OperationMethodParameter implements OperationParameter {
private final Parameter parameter;
private final Predicate<Parameter> optional;
/**
* Create a new {@link OperationMethodParameter} instance.
* @param name the parameter name
* @param parameter the parameter
* @param optionalParameters predicate to test if a parameter is optional
*/
OperationMethodParameter(String name, Parameter parameter, Predicate<Parameter> optionalParameters) {
OperationMethodParameter(String name, Parameter parameter) {
this.name = name;
this.parameter = parameter;
this.optional = optionalParameters;
}
@Override
@ -61,11 +56,7 @@ class OperationMethodParameter implements OperationParameter { @@ -61,11 +56,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 Nullness.NULLABLE != Nullness.forParameter(this.parameter);
}
@Override

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

@ -22,7 +22,6 @@ import java.util.ArrayList; @@ -22,7 +22,6 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Stream;
import org.jspecify.annotations.Nullable;
@ -45,26 +44,22 @@ class OperationMethodParameters implements OperationParameters { @@ -45,26 +44,22 @@ class OperationMethodParameters implements OperationParameters {
* Create a new {@link OperationMethodParameters} instance.
* @param method the source method
* @param parameterNameDiscoverer the parameter name discoverer
* @param optionalParameters predicate to test if a parameter is optional
*/
OperationMethodParameters(Method method, ParameterNameDiscoverer parameterNameDiscoverer,
Predicate<Parameter> optionalParameters) {
OperationMethodParameters(Method method, ParameterNameDiscoverer parameterNameDiscoverer) {
Assert.notNull(method, "'method' must not be null");
Assert.notNull(parameterNameDiscoverer, "'parameterNameDiscoverer' must not be null");
Assert.notNull(optionalParameters, "'optionalParameters' must not be null");
@Nullable String[] parameterNames = parameterNameDiscoverer.getParameterNames(method);
Parameter[] parameters = method.getParameters();
Assert.state(parameterNames != null, () -> "Failed to extract parameter names for " + method);
this.operationParameters = getOperationParameters(parameters, parameterNames, optionalParameters);
this.operationParameters = getOperationParameters(parameters, parameterNames);
}
private List<OperationParameter> getOperationParameters(Parameter[] parameters, @Nullable String[] names,
Predicate<Parameter> optionalParameters) {
private List<OperationParameter> getOperationParameters(Parameter[] parameters, @Nullable String[] names) {
List<OperationParameter> operationParameters = new ArrayList<>(parameters.length);
for (int i = 0; i < names.length; i++) {
String name = names[i];
Assert.state(name != null, "'name' must not be null");
operationParameters.add(new OperationMethodParameter(name, parameters[i], optionalParameters));
operationParameters.add(new OperationMethodParameter(name, parameters[i]));
}
return Collections.unmodifiableList(operationParameters);
}

3
module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/env/EnvironmentEndpoint.java vendored

@ -34,7 +34,6 @@ import org.springframework.boot.actuate.endpoint.Sanitizer; @@ -34,7 +34,6 @@ import org.springframework.boot.actuate.endpoint.Sanitizer;
import org.springframework.boot.actuate.endpoint.SanitizingFunction;
import org.springframework.boot.actuate.endpoint.Show;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.OptionalParameter;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.boot.actuate.endpoint.annotation.Selector;
import org.springframework.boot.context.properties.bind.PlaceholdersResolver;
@ -81,7 +80,7 @@ public class EnvironmentEndpoint { @@ -81,7 +80,7 @@ public class EnvironmentEndpoint {
}
@ReadOperation
public EnvironmentDescriptor environment(@OptionalParameter @Nullable String pattern) {
public EnvironmentDescriptor environment(@Nullable String pattern) {
boolean showUnsanitized = this.showValues.isShown(true);
return getEnvironmentDescriptor(pattern, showUnsanitized);
}

4
module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/env/EnvironmentEndpointWebExtension.java vendored

@ -22,7 +22,6 @@ import org.jspecify.annotations.Nullable; @@ -22,7 +22,6 @@ import org.jspecify.annotations.Nullable;
import org.springframework.boot.actuate.endpoint.SecurityContext;
import org.springframework.boot.actuate.endpoint.Show;
import org.springframework.boot.actuate.endpoint.annotation.OptionalParameter;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.boot.actuate.endpoint.annotation.Selector;
import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse;
@ -53,8 +52,7 @@ public class EnvironmentEndpointWebExtension { @@ -53,8 +52,7 @@ public class EnvironmentEndpointWebExtension {
}
@ReadOperation
public EnvironmentDescriptor environment(SecurityContext securityContext,
@OptionalParameter @Nullable String pattern) {
public EnvironmentDescriptor environment(SecurityContext securityContext, @Nullable String pattern) {
boolean showUnsanitized = this.showValues.isShown(securityContext, this.roles);
return this.delegate.getEnvironmentDescriptor(pattern, showUnsanitized);
}

3
module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/logging/LoggersEndpoint.java

@ -29,7 +29,6 @@ import org.jspecify.annotations.Nullable; @@ -29,7 +29,6 @@ import org.jspecify.annotations.Nullable;
import org.springframework.aot.hint.annotation.RegisterReflectionForBinding;
import org.springframework.boot.actuate.endpoint.OperationResponseBody;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.OptionalParameter;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.boot.actuate.endpoint.annotation.Selector;
import org.springframework.boot.actuate.endpoint.annotation.WriteOperation;
@ -100,7 +99,7 @@ public class LoggersEndpoint { @@ -100,7 +99,7 @@ public class LoggersEndpoint {
}
@WriteOperation
public void configureLogLevel(@Selector String name, @OptionalParameter @Nullable LogLevel configuredLevel) {
public void configureLogLevel(@Selector String name, @Nullable LogLevel configuredLevel) {
Assert.notNull(name, "'name' must not be empty");
LoggerGroup group = this.loggerGroups.get(name);
if (group != null && group.hasMembers()) {

3
module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/management/HeapDumpWebEndpoint.java

@ -40,7 +40,6 @@ import org.jspecify.annotations.Nullable; @@ -40,7 +40,6 @@ import org.jspecify.annotations.Nullable;
import org.springframework.boot.actuate.endpoint.Access;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.OptionalParameter;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse;
import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpoint;
@ -78,7 +77,7 @@ public class HeapDumpWebEndpoint { @@ -78,7 +77,7 @@ public class HeapDumpWebEndpoint {
}
@ReadOperation
public WebEndpointResponse<Resource> heapDump(@OptionalParameter @Nullable Boolean live) {
public WebEndpointResponse<Resource> heapDump(@Nullable Boolean live) {
try {
if (this.lock.tryLock(this.timeout, TimeUnit.MILLISECONDS)) {
try {

4
module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/prometheus/PrometheusScrapeEndpoint.java

@ -29,7 +29,6 @@ import io.prometheus.metrics.model.snapshots.MetricSnapshots; @@ -29,7 +29,6 @@ import io.prometheus.metrics.model.snapshots.MetricSnapshots;
import org.jspecify.annotations.Nullable;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.OptionalParameter;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse;
import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpoint;
@ -69,8 +68,7 @@ public class PrometheusScrapeEndpoint { @@ -69,8 +68,7 @@ public class PrometheusScrapeEndpoint {
}
@ReadOperation(producesFrom = PrometheusOutputFormat.class)
public WebEndpointResponse<byte[]> scrape(PrometheusOutputFormat format,
@OptionalParameter @Nullable Set<String> includedNames) {
public WebEndpointResponse<byte[]> scrape(PrometheusOutputFormat format, @Nullable Set<String> includedNames) {
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(this.nextMetricsScrapeSize);
MetricSnapshots metricSnapshots = (includedNames != null)

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

@ -16,19 +16,12 @@ @@ -16,19 +16,12 @@
package org.springframework.boot.actuate.endpoint.invoke.reflect;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import org.junit.jupiter.api.Test;
import org.springframework.boot.actuate.endpoint.annotation.Selector;
import org.springframework.boot.actuate.endpoint.annotation.Selector.Match;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.util.ReflectionUtils;
import static org.assertj.core.api.Assertions.assertThat;
@ -43,87 +36,60 @@ class OperationMethodParameterTests { @@ -43,87 +36,60 @@ 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);
private final Method exampleAnnotation = ReflectionUtils.findMethod(getClass(), "exampleAnnotation", String.class);
@Test
void getNameShouldReturnName() {
OperationMethodParameter parameter = new OperationMethodParameter("name", this.example.getParameters()[0],
this::isOptionalParameter);
OperationMethodParameter parameter = new OperationMethodParameter("name", this.example.getParameters()[0]);
assertThat(parameter.getName()).isEqualTo("name");
}
@Test
void getTypeShouldReturnType() {
OperationMethodParameter parameter = new OperationMethodParameter("name", this.example.getParameters()[0],
this::isOptionalParameter);
OperationMethodParameter parameter = new OperationMethodParameter("name", this.example.getParameters()[0]);
assertThat(parameter.getType()).isEqualTo(String.class);
}
@Test
void isMandatoryWhenNoAnnotationShouldReturnTrue() {
OperationMethodParameter parameter = new OperationMethodParameter("name", this.example.getParameters()[0],
this::isOptionalParameter);
OperationMethodParameter parameter = new OperationMethodParameter("name", this.example.getParameters()[0]);
assertThat(parameter.isMandatory()).isTrue();
}
@Test
void isMandatoryWhenOptionalAnnotationShouldReturnFalse() {
OperationMethodParameter parameter = new OperationMethodParameter("name", this.example.getParameters()[1],
this::isOptionalParameter);
assertThat(parameter.isMandatory()).isFalse();
}
@Test
void isMandatoryWhenJSpecifyNullableAnnotationShouldReturnFalse() {
OperationMethodParameter parameter = new OperationMethodParameter("name",
this.exampleJSpecifyNullable.getParameters()[1], this::isOptionalParameter);
void isMandatoryWhenNullableAnnotationShouldReturnFalse() {
OperationMethodParameter parameter = new OperationMethodParameter("name", this.example.getParameters()[1]);
assertThat(parameter.isMandatory()).isFalse();
}
@Test
@Deprecated(since = "4.0.0")
void isMandatoryWhenSpringNullableAnnotationShouldReturnFalse() {
OperationMethodParameter parameter = new OperationMethodParameter("name",
this.exampleSpringNullable.getParameters()[1], this::isOptionalParameter);
this.exampleSpringNullable.getParameters()[1]);
assertThat(parameter.isMandatory()).isFalse();
}
@Test
void getAnnotationShouldReturnAnnotation() {
OperationMethodParameter parameter = new OperationMethodParameter("name",
this.exampleAnnotation.getParameters()[0], this::isOptionalParameter);
this.exampleAnnotation.getParameters()[0]);
Selector annotation = parameter.getAnnotation(Selector.class);
assertThat(annotation).isNotNull();
assertThat(annotation.match()).isEqualTo(Match.ALL_REMAINING);
}
private boolean isOptionalParameter(Parameter parameter) {
return MergedAnnotations.from(parameter).isPresent(TestOptional.class);
void example(String one, @org.jspecify.annotations.Nullable String two) {
}
void example(String one, @TestOptional String two) {
}
void exampleJSpecifyNullable(String one, @org.jspecify.annotations.Nullable String two) {
}
@SuppressWarnings("deprecation")
@Deprecated(since = "4.0.0")
void exampleSpringNullable(String one, @org.springframework.lang.Nullable String two) {
}
void exampleAnnotation(@Selector(match = Match.ALL_REMAINING) String allRemaining) {
}
@Target({ ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TestOptional {
}
}

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

@ -17,12 +17,10 @@ @@ -17,12 +17,10 @@
package org.springframework.boot.actuate.endpoint.invoke.reflect;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Iterator;
import java.util.List;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.Predicate;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
@ -45,8 +43,6 @@ import static org.mockito.Mockito.mock; @@ -45,8 +43,6 @@ import static org.mockito.Mockito.mock;
*/
class OperationMethodParametersTests {
private static final Predicate<Parameter> NON_OPTIONAL = (parameter) -> false;
private final Method exampleMethod = ReflectionUtils.findMethod(getClass(), "example", String.class);
private final Method exampleNoParamsMethod = ReflectionUtils.findMethod(getClass(), "exampleNoParams");
@ -54,50 +50,48 @@ class OperationMethodParametersTests { @@ -54,50 +50,48 @@ class OperationMethodParametersTests {
@Test
void createWhenMethodIsNullShouldThrowException() {
assertThatIllegalArgumentException()
.isThrownBy(() -> new OperationMethodParameters(null, mock(ParameterNameDiscoverer.class), NON_OPTIONAL))
.isThrownBy(() -> new OperationMethodParameters(null, mock(ParameterNameDiscoverer.class)))
.withMessageContaining("'method' must not be null");
}
@Test
void createWhenParameterNameDiscovererIsNullShouldThrowException() {
assertThatIllegalArgumentException()
.isThrownBy(() -> new OperationMethodParameters(this.exampleMethod, null, NON_OPTIONAL))
assertThatIllegalArgumentException().isThrownBy(() -> new OperationMethodParameters(this.exampleMethod, null))
.withMessageContaining("'parameterNameDiscoverer' must not be null");
}
@Test
void createWhenParameterNameDiscovererReturnsNullShouldThrowException() {
assertThatIllegalStateException()
.isThrownBy(() -> new OperationMethodParameters(this.exampleMethod, mock(ParameterNameDiscoverer.class),
NON_OPTIONAL))
.isThrownBy(() -> new OperationMethodParameters(this.exampleMethod, mock(ParameterNameDiscoverer.class)))
.withMessageContaining("Failed to extract parameter names");
}
@Test
void hasParametersWhenHasParametersShouldReturnTrue() {
OperationMethodParameters parameters = new OperationMethodParameters(this.exampleMethod,
new DefaultParameterNameDiscoverer(), NON_OPTIONAL);
new DefaultParameterNameDiscoverer());
assertThat(parameters.hasParameters()).isTrue();
}
@Test
void hasParametersWhenHasNoParametersShouldReturnFalse() {
OperationMethodParameters parameters = new OperationMethodParameters(this.exampleNoParamsMethod,
new DefaultParameterNameDiscoverer(), NON_OPTIONAL);
new DefaultParameterNameDiscoverer());
assertThat(parameters.hasParameters()).isFalse();
}
@Test
void getParameterCountShouldReturnParameterCount() {
OperationMethodParameters parameters = new OperationMethodParameters(this.exampleMethod,
new DefaultParameterNameDiscoverer(), NON_OPTIONAL);
new DefaultParameterNameDiscoverer());
assertThat(parameters.getParameterCount()).isOne();
}
@Test
void iteratorShouldIterateOperationParameters() {
OperationMethodParameters parameters = new OperationMethodParameters(this.exampleMethod,
new DefaultParameterNameDiscoverer(), NON_OPTIONAL);
new DefaultParameterNameDiscoverer());
Iterator<OperationParameter> iterator = parameters.iterator();
assertParameters(
StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.ORDERED), false));
@ -106,7 +100,7 @@ class OperationMethodParametersTests { @@ -106,7 +100,7 @@ class OperationMethodParametersTests {
@Test
void streamShouldStreamOperationParameters() {
OperationMethodParameters parameters = new OperationMethodParameters(this.exampleMethod,
new DefaultParameterNameDiscoverer(), NON_OPTIONAL);
new DefaultParameterNameDiscoverer());
assertParameters(parameters.stream());
}

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

@ -17,8 +17,6 @@ @@ -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 @@ -36,39 +34,35 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
*/
class OperationMethodTests {
private static final Predicate<Parameter> 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 OperationMethod(null, OperationType.READ))
.withMessageContaining("'method' must not be null");
}
@Test
void createWhenOperationTypeIsNullShouldThrowException() {
assertThatIllegalArgumentException()
.isThrownBy(() -> new OperationMethod(this.exampleMethod, null, NON_OPTIONAL))
assertThatIllegalArgumentException().isThrownBy(() -> new OperationMethod(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 OperationMethod(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 OperationMethod(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 OperationMethod(this.exampleMethod, OperationType.READ);
OperationParameters parameters = operationMethod.getParameters();
assertThat(parameters.getParameterCount()).isOne();
assertThat(parameters.iterator().next().getName()).isEqualTo("name");

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

@ -16,14 +16,9 @@ @@ -16,14 +16,9 @@
package org.springframework.boot.actuate.endpoint.invoke.reflect;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Parameter;
import java.util.Collections;
import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@ -33,7 +28,6 @@ import org.springframework.boot.actuate.endpoint.OperationType; @@ -33,7 +28,6 @@ import org.springframework.boot.actuate.endpoint.OperationType;
import org.springframework.boot.actuate.endpoint.SecurityContext;
import org.springframework.boot.actuate.endpoint.invoke.MissingParametersException;
import org.springframework.boot.actuate.endpoint.invoke.ParameterValueMapper;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.util.ReflectionUtils;
import static org.assertj.core.api.Assertions.assertThat;
@ -58,7 +52,7 @@ class ReflectiveOperationInvokerTests { @@ -58,7 +52,7 @@ class ReflectiveOperationInvokerTests {
void setup() {
this.target = new Example();
this.operationMethod = new OperationMethod(ReflectionUtils.findMethod(Example.class, "reverse",
ApiVersion.class, SecurityContext.class, String.class), OperationType.READ, this::isOptional);
ApiVersion.class, SecurityContext.class, String.class), OperationType.READ);
this.parameterValueMapper = (parameter, value) -> (value != null) ? value.toString() : null;
}
@ -103,8 +97,7 @@ class ReflectiveOperationInvokerTests { @@ -103,8 +97,7 @@ class ReflectiveOperationInvokerTests {
@Test
void invokeWhenMissingOptionalArgumentShouldInvoke() {
OperationMethod operationMethod = new OperationMethod(ReflectionUtils.findMethod(Example.class,
"reverseOptional", ApiVersion.class, SecurityContext.class, String.class), OperationType.READ,
this::isOptional);
"reverseOptional", ApiVersion.class, SecurityContext.class, String.class), OperationType.READ);
ReflectiveOperationInvoker invoker = new ReflectiveOperationInvoker(this.target, operationMethod,
this.parameterValueMapper);
Object result = invoker
@ -121,10 +114,6 @@ class ReflectiveOperationInvokerTests { @@ -121,10 +114,6 @@ class ReflectiveOperationInvokerTests {
assertThat(result).isEqualTo("4321");
}
private boolean isOptional(Parameter parameter) {
return MergedAnnotations.from(parameter).isPresent(TestOptional.class);
}
static class Example {
String reverse(ApiVersion apiVersion, SecurityContext securityContext, String name) {
@ -133,7 +122,7 @@ class ReflectiveOperationInvokerTests { @@ -133,7 +122,7 @@ class ReflectiveOperationInvokerTests {
return new StringBuilder(name).reverse().toString();
}
String reverseOptional(ApiVersion apiVersion, SecurityContext securityContext, @TestOptional String name) {
String reverseOptional(ApiVersion apiVersion, SecurityContext securityContext, @Nullable String name) {
assertThat(apiVersion).isEqualTo(ApiVersion.LATEST);
assertThat(securityContext).isNotNull();
return new StringBuilder(String.valueOf(name)).reverse().toString();
@ -141,11 +130,4 @@ class ReflectiveOperationInvokerTests { @@ -141,11 +130,4 @@ class ReflectiveOperationInvokerTests {
}
@Target({ ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TestOptional {
}
}

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

@ -16,14 +16,10 @@ @@ -16,14 +16,10 @@
package org.springframework.boot.actuate.endpoint.invoker.cache;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.util.function.Function;
import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@ -38,7 +34,6 @@ import org.springframework.boot.actuate.endpoint.invoke.OperationInvoker; @@ -38,7 +34,6 @@ 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.web.WebServerNamespace;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.util.ReflectionUtils;
import static org.assertj.core.api.Assertions.assertThat;
@ -163,34 +158,32 @@ class CachingOperationInvokerAdvisorTests { @@ -163,34 +158,32 @@ class CachingOperationInvokerAdvisorTests {
private OperationMethod getOperationMethod(String methodName, Class<?>... parameterTypes) {
Method method = ReflectionUtils.findMethod(TestOperations.class, methodName, parameterTypes);
return new OperationMethod(method, OperationType.READ,
(parameter) -> MergedAnnotations.from(parameter).isPresent(TestOptional.class));
return new OperationMethod(method, OperationType.READ);
}
@SuppressWarnings("deprecation")
static class TestOperations {
String get() {
return "";
}
String getWithParameters(@TestOptional String foo, String bar) {
String getWithParameters(@Nullable String foo, String bar) {
return "";
}
String getWithAllOptionalParameters(@TestOptional String foo, @TestOptional String bar) {
String getWithAllOptionalParameters(@Nullable String foo, @Nullable String bar) {
return "";
}
String getWithSecurityContext(SecurityContext securityContext, @TestOptional String bar) {
String getWithSecurityContext(SecurityContext securityContext, @Nullable String bar) {
return "";
}
String getWithApiVersion(ApiVersion apiVersion, @TestOptional String bar) {
String getWithApiVersion(ApiVersion apiVersion, @Nullable String bar) {
return "";
}
String getWithServerNamespace(WebServerNamespace serverNamespace, @TestOptional String bar) {
String getWithServerNamespace(WebServerNamespace serverNamespace, @Nullable String bar) {
return "";
}
@ -200,11 +193,4 @@ class CachingOperationInvokerAdvisorTests { @@ -200,11 +193,4 @@ class CachingOperationInvokerAdvisorTests {
}
@Target({ ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TestOptional {
}
}

6
module/spring-boot-cache/src/main/java/org/springframework/boot/cache/actuate/endpoint/CachesEndpoint.java vendored

@ -27,7 +27,6 @@ import org.jspecify.annotations.Nullable; @@ -27,7 +27,6 @@ import org.jspecify.annotations.Nullable;
import org.springframework.boot.actuate.endpoint.OperationResponseBody;
import org.springframework.boot.actuate.endpoint.annotation.DeleteOperation;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.OptionalParameter;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.boot.actuate.endpoint.annotation.Selector;
import org.springframework.cache.Cache;
@ -82,8 +81,7 @@ public class CachesEndpoint { @@ -82,8 +81,7 @@ public class CachesEndpoint {
* {@code cacheManager} was provided to identify a unique candidate
*/
@ReadOperation
public @Nullable CacheEntryDescriptor cache(@Selector String cache,
@OptionalParameter @Nullable String cacheManager) {
public @Nullable CacheEntryDescriptor cache(@Selector String cache, @Nullable String cacheManager) {
return extractUniqueCacheEntry(cache, getCacheEntries((name) -> name.equals(cache), isNameMatch(cacheManager)));
}
@ -105,7 +103,7 @@ public class CachesEndpoint { @@ -105,7 +103,7 @@ public class CachesEndpoint {
* {@code cacheManager} was provided to identify a unique candidate
*/
@DeleteOperation
public boolean clearCache(@Selector String cache, @OptionalParameter @Nullable String cacheManager) {
public boolean clearCache(@Selector String cache, @Nullable String cacheManager) {
CacheEntryDescriptor entry = extractUniqueCacheEntry(cache,
getCacheEntries((name) -> name.equals(cache), isNameMatch(cacheManager)));
return (entry != null && clearCache(entry));

7
module/spring-boot-cache/src/main/java/org/springframework/boot/cache/actuate/endpoint/CachesEndpointWebExtension.java vendored

@ -19,7 +19,6 @@ package org.springframework.boot.cache.actuate.endpoint; @@ -19,7 +19,6 @@ package org.springframework.boot.cache.actuate.endpoint;
import org.jspecify.annotations.Nullable;
import org.springframework.boot.actuate.endpoint.annotation.DeleteOperation;
import org.springframework.boot.actuate.endpoint.annotation.OptionalParameter;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.boot.actuate.endpoint.annotation.Selector;
import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse;
@ -42,8 +41,7 @@ public class CachesEndpointWebExtension { @@ -42,8 +41,7 @@ public class CachesEndpointWebExtension {
}
@ReadOperation
public WebEndpointResponse<CacheEntryDescriptor> cache(@Selector String cache,
@OptionalParameter @Nullable String cacheManager) {
public WebEndpointResponse<CacheEntryDescriptor> cache(@Selector String cache, @Nullable String cacheManager) {
try {
CacheEntryDescriptor entry = this.delegate.cache(cache, cacheManager);
int status = (entry != null) ? WebEndpointResponse.STATUS_OK : WebEndpointResponse.STATUS_NOT_FOUND;
@ -55,8 +53,7 @@ public class CachesEndpointWebExtension { @@ -55,8 +53,7 @@ public class CachesEndpointWebExtension {
}
@DeleteOperation
public WebEndpointResponse<Void> clearCache(@Selector String cache,
@OptionalParameter @Nullable String cacheManager) {
public WebEndpointResponse<Void> clearCache(@Selector String cache, @Nullable String cacheManager) {
try {
boolean cleared = this.delegate.clearCache(cache, cacheManager);
int status = (cleared ? WebEndpointResponse.STATUS_NO_CONTENT : WebEndpointResponse.STATUS_NOT_FOUND);

4
module/spring-boot-micrometer-metrics/src/main/java/org/springframework/boot/micrometer/metrics/actuate/endpoint/MetricsEndpoint.java

@ -37,7 +37,6 @@ import org.jspecify.annotations.Nullable; @@ -37,7 +37,6 @@ import org.jspecify.annotations.Nullable;
import org.springframework.boot.actuate.endpoint.InvalidEndpointRequestException;
import org.springframework.boot.actuate.endpoint.OperationResponseBody;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.OptionalParameter;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.boot.actuate.endpoint.annotation.Selector;
@ -78,8 +77,7 @@ public class MetricsEndpoint { @@ -78,8 +77,7 @@ public class MetricsEndpoint {
}
@ReadOperation
public @Nullable MetricDescriptor metric(@Selector String requiredMetricName,
@OptionalParameter @Nullable List<String> tag) {
public @Nullable MetricDescriptor metric(@Selector String requiredMetricName, @Nullable List<String> tag) {
List<Tag> tags = parseTags(tag);
Collection<Meter> meters = findFirstMatchingMeters(this.registry, requiredMetricName, tags);
if (meters.isEmpty()) {

Loading…
Cancel
Save