From 96175a8e50f7fb067b3d54c6900ecabd31e1c72c Mon Sep 17 00:00:00 2001 From: Moritz Halbritter Date: Mon, 16 Jan 2023 11:17:13 +0100 Subject: [PATCH] Use OperationParameter consistently Closes gh-31240 --- .../EndpointAutoConfigurationTests.java | 8 +++++- .../endpoint/invoke/OperationParameter.java | 14 ++++++++++- .../reflect/OperationMethodParameter.java | 9 ++++++- .../annotation/DiscoveredWebOperation.java | 25 +++++++++---------- .../annotation/RequestPredicateFactory.java | 23 +++++++++-------- ...rsionServiceParameterValueMapperTests.java | 8 +++++- .../OperationMethodParameterTests.java | 19 +++++++++++++- 7 files changed, 77 insertions(+), 29 deletions(-) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/EndpointAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/EndpointAutoConfigurationTests.java index f3c0643fa5b..f4262b605a1 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/EndpointAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/EndpointAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2023 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. @@ -16,6 +16,7 @@ package org.springframework.boot.actuate.autoconfigure.endpoint; +import java.lang.annotation.Annotation; import java.util.Collections; import java.util.Set; @@ -200,6 +201,11 @@ class EndpointAutoConfigurationTests { return false; } + @Override + public T getAnnotation(Class annotation) { + return null; + } + } } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/invoke/OperationParameter.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/invoke/OperationParameter.java index 2b9b3c22e9f..d32ef8ad267 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/invoke/OperationParameter.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/invoke/OperationParameter.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2023 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. @@ -16,10 +16,13 @@ package org.springframework.boot.actuate.endpoint.invoke; +import java.lang.annotation.Annotation; + /** * A single operation parameter. * * @author Phillip Webb + * @author Moritz Halbritter * @since 2.0.0 */ public interface OperationParameter { @@ -42,4 +45,13 @@ public interface OperationParameter { */ boolean isMandatory(); + /** + * Returns this element's annotation for the specified type if such an annotation is + * present, else null. + * @param annotation class of the annotation + * @return annotation value + * @param type of the annotation + */ + T getAnnotation(Class annotation); + } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/invoke/reflect/OperationMethodParameter.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/invoke/reflect/OperationMethodParameter.java index 3aeac7d7613..41e8eeb4d26 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/invoke/reflect/OperationMethodParameter.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/invoke/reflect/OperationMethodParameter.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2023 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. @@ -16,6 +16,7 @@ package org.springframework.boot.actuate.endpoint.invoke.reflect; +import java.lang.annotation.Annotation; import java.lang.reflect.Parameter; import javax.annotation.Nonnull; @@ -32,6 +33,7 @@ import org.springframework.util.ObjectUtils; * {@link OperationParameter} created from an {@link OperationMethod}. * * @author Phillip Webb + * @author Moritz Halbritter */ class OperationMethodParameter implements OperationParameter { @@ -69,6 +71,11 @@ class OperationMethodParameter implements OperationParameter { return (jsr305Present) ? new Jsr305().isMandatory(this.parameter) : true; } + @Override + public T getAnnotation(Class annotation) { + return this.parameter.getAnnotation(annotation); + } + @Override public String toString() { return this.name + " of type " + this.parameter.getType().getName(); diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/annotation/DiscoveredWebOperation.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/annotation/DiscoveredWebOperation.java index 63d3af70153..aaa5726c330 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/annotation/DiscoveredWebOperation.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/annotation/DiscoveredWebOperation.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2023 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. @@ -16,10 +16,7 @@ package org.springframework.boot.actuate.endpoint.web.annotation; -import java.lang.reflect.Method; -import java.lang.reflect.Parameter; import java.util.stream.Collectors; -import java.util.stream.Stream; import org.reactivestreams.Publisher; @@ -28,6 +25,8 @@ import org.springframework.boot.actuate.endpoint.annotation.AbstractDiscoveredOp import org.springframework.boot.actuate.endpoint.annotation.DiscoveredOperationMethod; import org.springframework.boot.actuate.endpoint.annotation.Selector; import org.springframework.boot.actuate.endpoint.invoke.OperationInvoker; +import org.springframework.boot.actuate.endpoint.invoke.OperationParameter; +import org.springframework.boot.actuate.endpoint.invoke.reflect.OperationMethod; import org.springframework.boot.actuate.endpoint.web.WebOperation; import org.springframework.boot.actuate.endpoint.web.WebOperationRequestPredicate; import org.springframework.core.style.ToStringCreator; @@ -39,6 +38,7 @@ import org.springframework.util.ClassUtils; * @author Andy Wilkinson * @author Stephane Nicoll * @author Phillip Webb + * @author Moritz Halbritter */ class DiscoveredWebOperation extends AbstractDiscoveredOperation implements WebOperation { @@ -54,27 +54,26 @@ class DiscoveredWebOperation extends AbstractDiscoveredOperation implements WebO DiscoveredWebOperation(EndpointId endpointId, DiscoveredOperationMethod operationMethod, OperationInvoker invoker, WebOperationRequestPredicate requestPredicate) { super(operationMethod, invoker); - Method method = operationMethod.getMethod(); - this.id = getId(endpointId, method); - this.blocking = getBlocking(method); + this.id = getId(endpointId, operationMethod); + this.blocking = getBlocking(operationMethod); this.requestPredicate = requestPredicate; } - private String getId(EndpointId endpointId, Method method) { - return endpointId + Stream.of(method.getParameters()).filter(this::hasSelector).map(this::dashName) + private String getId(EndpointId endpointId, OperationMethod method) { + return endpointId + method.getParameters().stream().filter(this::hasSelector).map(this::dashName) .collect(Collectors.joining()); } - private boolean hasSelector(Parameter parameter) { + private boolean hasSelector(OperationParameter parameter) { return parameter.getAnnotation(Selector.class) != null; } - private String dashName(Parameter parameter) { + private String dashName(OperationParameter parameter) { return "-" + parameter.getName(); } - private boolean getBlocking(Method method) { - return !REACTIVE_STREAMS_PRESENT || !Publisher.class.isAssignableFrom(method.getReturnType()); + private boolean getBlocking(OperationMethod method) { + return !REACTIVE_STREAMS_PRESENT || !Publisher.class.isAssignableFrom(method.getMethod().getReturnType()); } @Override diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/annotation/RequestPredicateFactory.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/annotation/RequestPredicateFactory.java index 430ebb6c335..623e02b3f3c 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/annotation/RequestPredicateFactory.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/annotation/RequestPredicateFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2023 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. @@ -17,8 +17,6 @@ package org.springframework.boot.actuate.endpoint.web.annotation; import java.lang.reflect.Method; -import java.lang.reflect.Parameter; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.stream.Stream; @@ -27,6 +25,7 @@ import org.springframework.boot.actuate.endpoint.OperationType; import org.springframework.boot.actuate.endpoint.annotation.DiscoveredOperationMethod; import org.springframework.boot.actuate.endpoint.annotation.Selector; import org.springframework.boot.actuate.endpoint.annotation.Selector.Match; +import org.springframework.boot.actuate.endpoint.invoke.OperationParameter; import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes; import org.springframework.boot.actuate.endpoint.web.WebEndpointHttpMethod; import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse; @@ -41,6 +40,7 @@ import org.springframework.util.Assert; * @author Andy Wilkinson * @author Stephane Nicoll * @author Phillip Webb + * @author Moritz Halbritter */ class RequestPredicateFactory { @@ -53,9 +53,9 @@ class RequestPredicateFactory { WebOperationRequestPredicate getRequestPredicate(String rootPath, DiscoveredOperationMethod operationMethod) { Method method = operationMethod.getMethod(); - Parameter[] selectorParameters = Arrays.stream(method.getParameters()).filter(this::hasSelector) - .toArray(Parameter[]::new); - Parameter allRemainingPathSegmentsParameter = getAllRemainingPathSegmentsParameter(selectorParameters); + OperationParameter[] selectorParameters = operationMethod.getParameters().stream().filter(this::hasSelector) + .toArray(OperationParameter[]::new); + OperationParameter allRemainingPathSegmentsParameter = getAllRemainingPathSegmentsParameter(selectorParameters); String path = getPath(rootPath, selectorParameters, allRemainingPathSegmentsParameter != null); WebEndpointHttpMethod httpMethod = determineHttpMethod(operationMethod.getOperationType()); Collection consumes = getConsumes(httpMethod, method); @@ -63,9 +63,9 @@ class RequestPredicateFactory { return new WebOperationRequestPredicate(path, httpMethod, consumes, produces); } - private Parameter getAllRemainingPathSegmentsParameter(Parameter[] selectorParameters) { - Parameter trailingPathsParameter = null; - for (Parameter selectorParameter : selectorParameters) { + private OperationParameter getAllRemainingPathSegmentsParameter(OperationParameter[] selectorParameters) { + OperationParameter trailingPathsParameter = null; + for (OperationParameter selectorParameter : selectorParameters) { Selector selector = selectorParameter.getAnnotation(Selector.class); if (selector.match() == Match.ALL_REMAINING) { Assert.state(trailingPathsParameter == null, @@ -80,7 +80,8 @@ class RequestPredicateFactory { return trailingPathsParameter; } - private String getPath(String rootPath, Parameter[] selectorParameters, boolean matchRemainingPathSegments) { + private String getPath(String rootPath, OperationParameter[] selectorParameters, + boolean matchRemainingPathSegments) { StringBuilder path = new StringBuilder(rootPath); for (int i = 0; i < selectorParameters.length; i++) { path.append("/{"); @@ -93,7 +94,7 @@ class RequestPredicateFactory { return path.toString(); } - private boolean hasSelector(Parameter parameter) { + private boolean hasSelector(OperationParameter parameter) { return parameter.getAnnotation(Selector.class) != null; } diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/invoke/convert/ConversionServiceParameterValueMapperTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/invoke/convert/ConversionServiceParameterValueMapperTests.java index c2edffcee88..31c07a45cda 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/invoke/convert/ConversionServiceParameterValueMapperTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/invoke/convert/ConversionServiceParameterValueMapperTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 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. @@ -16,6 +16,7 @@ package org.springframework.boot.actuate.endpoint.invoke.convert; +import java.lang.annotation.Annotation; import java.time.OffsetDateTime; import org.junit.jupiter.api.Test; @@ -104,6 +105,11 @@ class ConversionServiceParameterValueMapperTests { return false; } + @Override + public T getAnnotation(Class annotation) { + return null; + } + } } diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/invoke/reflect/OperationMethodParameterTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/invoke/reflect/OperationMethodParameterTests.java index d1c324b074a..5f9e7e85422 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/invoke/reflect/OperationMethodParameterTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/invoke/reflect/OperationMethodParameterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2023 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. @@ -26,6 +26,8 @@ import javax.annotation.meta.When; 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.lang.Nullable; import org.springframework.util.ReflectionUtils; @@ -35,6 +37,7 @@ import static org.assertj.core.api.Assertions.assertThat; * Tests for {@link OperationMethodParameter}. * * @author Phillip Webb + * @author Moritz Halbritter */ class OperationMethodParameterTests { @@ -48,6 +51,8 @@ class OperationMethodParameterTests { private Method exampleJsr305NonNull = ReflectionUtils.findMethod(getClass(), "exampleJsr305NonNull", String.class, String.class); + private Method exampleAnnotation = ReflectionUtils.findMethod(getClass(), "exampleAnnotation", String.class); + @Test void getNameShouldReturnName() { OperationMethodParameter parameter = new OperationMethodParameter("name", this.example.getParameters()[0]); @@ -93,6 +98,15 @@ class OperationMethodParameterTests { assertThat(parameter.isMandatory()).isTrue(); } + @Test + void getAnnotationShouldReturnAnnotation() { + OperationMethodParameter parameter = new OperationMethodParameter("name", + this.exampleAnnotation.getParameters()[0]); + Selector annotation = parameter.getAnnotation(Selector.class); + assertThat(annotation).isNotNull(); + assertThat(annotation.match()).isEqualTo(Match.ALL_REMAINING); + } + void example(String one, @Nullable String two) { } @@ -105,6 +119,9 @@ class OperationMethodParameterTests { void exampleJsr305NonNull(String one, @javax.annotation.Nonnull String two) { } + void exampleAnnotation(@Selector(match = Match.ALL_REMAINING) String allRemaining) { + } + @TypeQualifier @Retention(RetentionPolicy.RUNTIME) @Nonnull(when = When.MAYBE)