diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/mappings.adoc b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/mappings.adoc index 2ef662f1a53..b2c6b6d5d49 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/mappings.adoc +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/mappings.adoc @@ -40,7 +40,7 @@ When using Spring MVC, the response contains details of any `DispatcherServlet` request mappings beneath `contexts.*.mappings.dispatcherServlets`. The following table describes the structure of this section of the response: -[cols="2,1,3"] +[cols="3,1,3"] include::{snippets}mappings/response-fields-dispatcher-servlets.adoc[] @@ -69,12 +69,12 @@ include::{snippets}mappings/response-fields-servlet-filters.adoc[] -[[mappings-retrieving-response-structure-dispatcher-servlets]] +[[mappings-retrieving-response-structure-dispatcher-handlers]] === Dispatcher Handlers Response Structure When using Spring WebFlux, the response contains details of any `DispatcherHandler` request mappings beneath `contexts.*.mappings.dispatcherHandlers`. The following table describes the structure of this section of the response: -[cols="2,1,3"] +[cols="3,1,3"] include::{snippets}mappings/response-fields-dispatcher-handlers.adoc[] diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/MappingsEndpointReactiveDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/MappingsEndpointReactiveDocumentationTests.java index f84513a901f..18f20c1200c 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/MappingsEndpointReactiveDocumentationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/MappingsEndpointReactiveDocumentationTests.java @@ -35,8 +35,13 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.restdocs.JUnitRestDocumentation; +import org.springframework.restdocs.payload.JsonFieldType; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.reactive.server.WebTestClient; +import org.springframework.web.reactive.function.server.RequestPredicates; +import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.RouterFunctions; +import org.springframework.web.reactive.function.server.ServerResponse; import static org.springframework.restdocs.payload.PayloadDocumentation.beneathPath; import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; @@ -84,7 +89,37 @@ public class MappingsEndpointReactiveDocumentationTests fieldWithPath("*.[].handler") .description("Handler for the mapping."), fieldWithPath("*.[].predicate") - .description("Predicate for the mapping.")))); + .description("Predicate for the mapping."), + fieldWithPath("*.[].details").optional() + .type(JsonFieldType.OBJECT) + .description("Additional implementation-specific " + + "details about the mapping. Optional."), + fieldWithPath("*.[].details.handlerMethod").optional() + .type(JsonFieldType.OBJECT) + .description("Details of the method, if any, " + + "that will handle requests to " + + "this mapping."), + fieldWithPath("*.[].details.handlerMethod.className") + .type(JsonFieldType.STRING) + .description("Fully qualified name of the class" + + " of the method."), + fieldWithPath("*.[].details.handlerMethod.name") + .type(JsonFieldType.STRING) + .description("Name of the method."), + fieldWithPath("*.[].details.handlerMethod.descriptor") + .type(JsonFieldType.STRING) + .description("Descriptor of the method as " + + "specified in the Java Language " + + "Specification."), + fieldWithPath("*.[].details.handlerFunction") + .optional().type(JsonFieldType.OBJECT) + .description("Details of the function, if any, " + + "that will handle requests to this " + + "mapping."), + fieldWithPath("*.[].details.handlerFunction.className") + .type(JsonFieldType.STRING).description( + "Fully qualified name of the class of " + + "the function.")))); } @Configuration @@ -108,6 +143,12 @@ public class MappingsEndpointReactiveDocumentationTests return new MappingsEndpoint(descriptionProviders, context); } + @Bean + public RouterFunction exampleRouter() { + return RouterFunctions.route(RequestPredicates.GET("/foo"), + (request) -> ServerResponse.ok().build()); + } + } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/MappingsEndpointServletDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/MappingsEndpointServletDocumentationTests.java index 9cd7aea9829..a382a70a76e 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/MappingsEndpointServletDocumentationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/MappingsEndpointServletDocumentationTests.java @@ -102,7 +102,28 @@ public class MappingsEndpointServletDocumentationTests fieldWithPath("*.[].handler") .description("Handler for the mapping."), fieldWithPath("*.[].predicate") - .description("Predicate for the mapping.")), + .description("Predicate for the mapping."), + fieldWithPath("*.[].details").optional() + .type(JsonFieldType.OBJECT) + .description("Additional implementation-specific " + + "details about the mapping. Optional."), + fieldWithPath("*.[].details.handlerMethod").optional() + .type(JsonFieldType.OBJECT) + .description("Details of the method, if any, " + + "that will handle requests to " + + "this mapping."), + fieldWithPath("*.[].details.handlerMethod.className") + .type(JsonFieldType.STRING) + .description("Fully qualified name of the class" + + " of the method."), + fieldWithPath("*.[].details.handlerMethod.name") + .type(JsonFieldType.STRING) + .description("Name of the method."), + fieldWithPath("*.[].details.handlerMethod.descriptor") + .type(JsonFieldType.STRING) + .description("Descriptor of the method as " + + "specified in the Java Language " + + "Specification.")), responseFields( beneathPath("contexts.*.mappings.servletFilters") .withSubsectionId("servlet-filters"), diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/mappings/HandlerMethodDescription.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/mappings/HandlerMethodDescription.java new file mode 100644 index 00000000000..4f1ecf119ef --- /dev/null +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/mappings/HandlerMethodDescription.java @@ -0,0 +1,54 @@ +/* + * Copyright 2012-2018 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 + * + * http://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.web.mappings; + +import org.springframework.asm.Type; +import org.springframework.web.method.HandlerMethod; + +/** + * A description of a {@link HandlerMethod}. + * + * @author Andy Wilkinson + * @since 2.0.0 + */ +public class HandlerMethodDescription { + + private final String className; + + private final String name; + + private final String descriptor; + + public HandlerMethodDescription(HandlerMethod handlerMethod) { + this.name = handlerMethod.getMethod().getName(); + this.className = handlerMethod.getMethod().getDeclaringClass().getCanonicalName(); + this.descriptor = Type.getMethodDescriptor(handlerMethod.getMethod()); + } + + public String getName() { + return this.name; + } + + public String getDescriptor() { + return this.descriptor; + } + + public String getClassName() { + return this.className; + } + +} diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/mappings/reactive/DispatcherHandlerMappingDescription.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/mappings/reactive/DispatcherHandlerMappingDescription.java index ca00bf7564d..ea9e428a764 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/mappings/reactive/DispatcherHandlerMappingDescription.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/mappings/reactive/DispatcherHandlerMappingDescription.java @@ -30,9 +30,13 @@ public class DispatcherHandlerMappingDescription { private final String handler; - DispatcherHandlerMappingDescription(String predicate, String handler) { + private final DispatcherHandlerMappingDetails details; + + DispatcherHandlerMappingDescription(String predicate, String handler, + DispatcherHandlerMappingDetails details) { this.predicate = predicate; this.handler = handler; + this.details = details; } public String getHandler() { @@ -43,4 +47,8 @@ public class DispatcherHandlerMappingDescription { return this.predicate; } + public DispatcherHandlerMappingDetails getDetails() { + return this.details; + } + } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/mappings/reactive/DispatcherHandlerMappingDetails.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/mappings/reactive/DispatcherHandlerMappingDetails.java new file mode 100644 index 00000000000..10183643b3c --- /dev/null +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/mappings/reactive/DispatcherHandlerMappingDetails.java @@ -0,0 +1,61 @@ +/* + * Copyright 2012-2018 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 + * + * http://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.web.mappings.reactive; + +import org.springframework.boot.actuate.web.mappings.HandlerMethodDescription; +import org.springframework.web.reactive.DispatcherHandler; + +/** + * Details of a {@link DispatcherHandler} mapping. + * + * @author Andy Wilkinson + * @since 2.0.0 + */ +public class DispatcherHandlerMappingDetails { + + private HandlerMethodDescription handlerMethod; + + private HandlerFunctionDescription handlerFunction; + + private RequestMappingConditionsDescription requestMappingConditions; + + public HandlerMethodDescription getHandlerMethod() { + return this.handlerMethod; + } + + void setHandlerMethod(HandlerMethodDescription handlerMethod) { + this.handlerMethod = handlerMethod; + } + + public HandlerFunctionDescription getHandlerFunction() { + return this.handlerFunction; + } + + void setHandlerFunction(HandlerFunctionDescription handlerFunction) { + this.handlerFunction = handlerFunction; + } + + public RequestMappingConditionsDescription getRequestMappingConditions() { + return this.requestMappingConditions; + } + + void setRequestMappingConditions( + RequestMappingConditionsDescription requestMappingConditions) { + this.requestMappingConditions = requestMappingConditions; + } + +} diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/mappings/reactive/DispatcherHandlersMappingDescriptionProvider.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/mappings/reactive/DispatcherHandlersMappingDescriptionProvider.java index 9ac7e95f8fc..8374219bd74 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/mappings/reactive/DispatcherHandlersMappingDescriptionProvider.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/mappings/reactive/DispatcherHandlersMappingDescriptionProvider.java @@ -28,6 +28,7 @@ import java.util.stream.Stream; import reactor.core.publisher.Mono; +import org.springframework.boot.actuate.web.mappings.HandlerMethodDescription; import org.springframework.boot.actuate.web.mappings.MappingDescriptionProvider; import org.springframework.context.ApplicationContext; import org.springframework.core.io.Resource; @@ -121,8 +122,11 @@ public class DispatcherHandlersMappingDescriptionProvider private DispatcherHandlerMappingDescription describe( Entry mapping) { + DispatcherHandlerMappingDetails handlerMapping = new DispatcherHandlerMappingDetails(); + handlerMapping + .setHandlerMethod(new HandlerMethodDescription(mapping.getValue())); return new DispatcherHandlerMappingDescription(mapping.getKey().toString(), - mapping.getValue().toString()); + mapping.getValue().toString(), handlerMapping); } } @@ -145,7 +149,8 @@ public class DispatcherHandlersMappingDescriptionProvider private DispatcherHandlerMappingDescription describe( Entry mapping) { return new DispatcherHandlerMappingDescription( - mapping.getKey().getPatternString(), mapping.getValue().toString()); + mapping.getKey().getPatternString(), mapping.getValue().toString(), + null); } } @@ -186,8 +191,10 @@ public class DispatcherHandlersMappingDescriptionProvider @Override public void route(RequestPredicate predicate, HandlerFunction handlerFunction) { + DispatcherHandlerMappingDetails details = new DispatcherHandlerMappingDetails(); + details.setHandlerFunction(new HandlerFunctionDescription(handlerFunction)); this.descriptions.add(new DispatcherHandlerMappingDescription( - predicate.toString(), handlerFunction.toString())); + predicate.toString(), handlerFunction.toString(), details)); } @Override diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/mappings/reactive/HandlerFunctionDescription.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/mappings/reactive/HandlerFunctionDescription.java new file mode 100644 index 00000000000..e8fdafd437c --- /dev/null +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/mappings/reactive/HandlerFunctionDescription.java @@ -0,0 +1,39 @@ +/* + * Copyright 2012-2018 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 + * + * http://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.web.mappings.reactive; + +import org.springframework.web.reactive.function.server.HandlerFunction; + +/** + * Description of a {@link HandlerFunction}. + * + * @author Andy Wilkinson + * @since 2.0.0 + */ +public class HandlerFunctionDescription { + + private final String className; + + HandlerFunctionDescription(HandlerFunction handlerFunction) { + this.className = handlerFunction.getClass().getCanonicalName(); + } + + public String getClassName() { + return this.className; + } + +} diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/mappings/servlet/DispatcherServletMappingDescription.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/mappings/servlet/DispatcherServletMappingDescription.java index 691944190f2..9b79dc069a2 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/mappings/servlet/DispatcherServletMappingDescription.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/mappings/servlet/DispatcherServletMappingDescription.java @@ -30,32 +30,25 @@ public class DispatcherServletMappingDescription { private final String predicate; - /** - * Creates a new {@code DispatcherServletMappingDescription} for the given - * {@code handler} that will receives requests that match the given {@code predicate}. - * - * @param predicate the predicate - * @param handler the handler - */ - public DispatcherServletMappingDescription(String predicate, String handler) { + private final DispatcherServletMappingDetails details; + + DispatcherServletMappingDescription(String predicate, String handler, + DispatcherServletMappingDetails details) { this.handler = handler; this.predicate = predicate; + this.details = details; } - /** - * Returns the handler for the described mapping. - * @return the handler - */ public String getHandler() { return this.handler; } - /** - * Returns the predicate for the described mapping. - * @return the predicate - */ public String getPredicate() { return this.predicate; } + public DispatcherServletMappingDetails getDetails() { + return this.details; + } + } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/mappings/servlet/DispatcherServletMappingDetails.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/mappings/servlet/DispatcherServletMappingDetails.java new file mode 100644 index 00000000000..0ec7c4ebde8 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/mappings/servlet/DispatcherServletMappingDetails.java @@ -0,0 +1,40 @@ +/* + * Copyright 2012-2018 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 + * + * http://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.web.mappings.servlet; + +import org.springframework.boot.actuate.web.mappings.HandlerMethodDescription; +import org.springframework.web.servlet.DispatcherServlet; + +/** + * Details of a {@link DispatcherServlet} mapping. + * + * @author Andy Wilkinson + * @since 2.0.0 + */ +public class DispatcherServletMappingDetails { + + private final HandlerMethodDescription handlerMethod; + + DispatcherServletMappingDetails(HandlerMethodDescription handlerMethod) { + this.handlerMethod = handlerMethod; + } + + public HandlerMethodDescription getHandlerMethod() { + return this.handlerMethod; + } + +} diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/mappings/servlet/DispatcherServletsMappingDescriptionProvider.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/mappings/servlet/DispatcherServletsMappingDescriptionProvider.java index 51e6f86608b..5ad601adcc2 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/mappings/servlet/DispatcherServletsMappingDescriptionProvider.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/mappings/servlet/DispatcherServletsMappingDescriptionProvider.java @@ -25,6 +25,7 @@ import java.util.Map.Entry; import java.util.stream.Collectors; import java.util.stream.Stream; +import org.springframework.boot.actuate.web.mappings.HandlerMethodDescription; import org.springframework.boot.actuate.web.mappings.MappingDescriptionProvider; import org.springframework.context.ApplicationContext; import org.springframework.data.rest.webmvc.support.DelegatingHandlerMapping; @@ -140,7 +141,8 @@ public class DispatcherServletsMappingDescriptionProvider private DispatcherServletMappingDescription describe( Entry mapping) { return new DispatcherServletMappingDescription(mapping.getKey().toString(), - mapping.getValue().toString()); + mapping.getValue().toString(), new DispatcherServletMappingDetails( + new HandlerMethodDescription(mapping.getValue()))); } } @@ -163,7 +165,7 @@ public class DispatcherServletsMappingDescriptionProvider private DispatcherServletMappingDescription describe( Entry mapping) { return new DispatcherServletMappingDescription(mapping.getKey().toString(), - mapping.getValue().toString()); + mapping.getValue().toString(), null); } }