Skip to content

Commit 2c19257

Browse files
committed
Add HandlerMethod and HandlerFunction details to mappings endpoint
Closes spring-projectsgh-11864
1 parent 7ed4273 commit 2c19257

File tree

11 files changed

+293
-27
lines changed

11 files changed

+293
-27
lines changed

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/mappings.adoc

+3-3
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ When using Spring MVC, the response contains details of any `DispatcherServlet`
4040
request mappings beneath `contexts.*.mappings.dispatcherServlets`. The following
4141
table describes the structure of this section of the response:
4242

43-
[cols="2,1,3"]
43+
[cols="3,1,3"]
4444
include::{snippets}mappings/response-fields-dispatcher-servlets.adoc[]
4545

4646

@@ -69,12 +69,12 @@ include::{snippets}mappings/response-fields-servlet-filters.adoc[]
6969

7070

7171

72-
[[mappings-retrieving-response-structure-dispatcher-servlets]]
72+
[[mappings-retrieving-response-structure-dispatcher-handlers]]
7373
=== Dispatcher Handlers Response Structure
7474

7575
When using Spring WebFlux, the response contains details of any `DispatcherHandler`
7676
request mappings beneath `contexts.*.mappings.dispatcherHandlers`. The following
7777
table describes the structure of this section of the response:
7878

79-
[cols="2,1,3"]
79+
[cols="3,1,3"]
8080
include::{snippets}mappings/response-fields-dispatcher-handlers.adoc[]

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/MappingsEndpointReactiveDocumentationTests.java

+42-1
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,13 @@
3535
import org.springframework.context.annotation.Configuration;
3636
import org.springframework.context.annotation.Import;
3737
import org.springframework.restdocs.JUnitRestDocumentation;
38+
import org.springframework.restdocs.payload.JsonFieldType;
3839
import org.springframework.test.context.junit4.SpringRunner;
3940
import org.springframework.test.web.reactive.server.WebTestClient;
41+
import org.springframework.web.reactive.function.server.RequestPredicates;
42+
import org.springframework.web.reactive.function.server.RouterFunction;
43+
import org.springframework.web.reactive.function.server.RouterFunctions;
44+
import org.springframework.web.reactive.function.server.ServerResponse;
4045

4146
import static org.springframework.restdocs.payload.PayloadDocumentation.beneathPath;
4247
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
@@ -84,7 +89,37 @@ public void mappings() throws Exception {
8489
fieldWithPath("*.[].handler")
8590
.description("Handler for the mapping."),
8691
fieldWithPath("*.[].predicate")
87-
.description("Predicate for the mapping."))));
92+
.description("Predicate for the mapping."),
93+
fieldWithPath("*.[].details").optional()
94+
.type(JsonFieldType.OBJECT)
95+
.description("Additional implementation-specific "
96+
+ "details about the mapping. Optional."),
97+
fieldWithPath("*.[].details.handlerMethod").optional()
98+
.type(JsonFieldType.OBJECT)
99+
.description("Details of the method, if any, "
100+
+ "that will handle requests to "
101+
+ "this mapping."),
102+
fieldWithPath("*.[].details.handlerMethod.className")
103+
.type(JsonFieldType.STRING)
104+
.description("Fully qualified name of the class"
105+
+ " of the method."),
106+
fieldWithPath("*.[].details.handlerMethod.name")
107+
.type(JsonFieldType.STRING)
108+
.description("Name of the method."),
109+
fieldWithPath("*.[].details.handlerMethod.descriptor")
110+
.type(JsonFieldType.STRING)
111+
.description("Descriptor of the method as "
112+
+ "specified in the Java Language "
113+
+ "Specification."),
114+
fieldWithPath("*.[].details.handlerFunction")
115+
.optional().type(JsonFieldType.OBJECT)
116+
.description("Details of the function, if any, "
117+
+ "that will handle requests to this "
118+
+ "mapping."),
119+
fieldWithPath("*.[].details.handlerFunction.className")
120+
.type(JsonFieldType.STRING).description(
121+
"Fully qualified name of the class of "
122+
+ "the function."))));
88123
}
89124

90125
@Configuration
@@ -108,6 +143,12 @@ public MappingsEndpoint mappingsEndpoint(
108143
return new MappingsEndpoint(descriptionProviders, context);
109144
}
110145

146+
@Bean
147+
public RouterFunction<ServerResponse> exampleRouter() {
148+
return RouterFunctions.route(RequestPredicates.GET("/foo"),
149+
(request) -> ServerResponse.ok().build());
150+
}
151+
111152
}
112153

113154
}

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/MappingsEndpointServletDocumentationTests.java

+22-1
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,28 @@ public void mappings() throws Exception {
102102
fieldWithPath("*.[].handler")
103103
.description("Handler for the mapping."),
104104
fieldWithPath("*.[].predicate")
105-
.description("Predicate for the mapping.")),
105+
.description("Predicate for the mapping."),
106+
fieldWithPath("*.[].details").optional()
107+
.type(JsonFieldType.OBJECT)
108+
.description("Additional implementation-specific "
109+
+ "details about the mapping. Optional."),
110+
fieldWithPath("*.[].details.handlerMethod").optional()
111+
.type(JsonFieldType.OBJECT)
112+
.description("Details of the method, if any, "
113+
+ "that will handle requests to "
114+
+ "this mapping."),
115+
fieldWithPath("*.[].details.handlerMethod.className")
116+
.type(JsonFieldType.STRING)
117+
.description("Fully qualified name of the class"
118+
+ " of the method."),
119+
fieldWithPath("*.[].details.handlerMethod.name")
120+
.type(JsonFieldType.STRING)
121+
.description("Name of the method."),
122+
fieldWithPath("*.[].details.handlerMethod.descriptor")
123+
.type(JsonFieldType.STRING)
124+
.description("Descriptor of the method as "
125+
+ "specified in the Java Language "
126+
+ "Specification.")),
106127
responseFields(
107128
beneathPath("contexts.*.mappings.servletFilters")
108129
.withSubsectionId("servlet-filters"),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Copyright 2012-2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.actuate.web.mappings;
18+
19+
import org.springframework.asm.Type;
20+
import org.springframework.web.method.HandlerMethod;
21+
22+
/**
23+
* A description of a {@link HandlerMethod}.
24+
*
25+
* @author Andy Wilkinson
26+
* @since 2.0.0
27+
*/
28+
public class HandlerMethodDescription {
29+
30+
private final String className;
31+
32+
private final String name;
33+
34+
private final String descriptor;
35+
36+
public HandlerMethodDescription(HandlerMethod handlerMethod) {
37+
this.name = handlerMethod.getMethod().getName();
38+
this.className = handlerMethod.getMethod().getDeclaringClass().getCanonicalName();
39+
this.descriptor = Type.getMethodDescriptor(handlerMethod.getMethod());
40+
}
41+
42+
public String getName() {
43+
return this.name;
44+
}
45+
46+
public String getDescriptor() {
47+
return this.descriptor;
48+
}
49+
50+
public String getClassName() {
51+
return this.className;
52+
}
53+
54+
}

spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/mappings/reactive/DispatcherHandlerMappingDescription.java

+9-1
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,13 @@ public class DispatcherHandlerMappingDescription {
3030

3131
private final String handler;
3232

33-
DispatcherHandlerMappingDescription(String predicate, String handler) {
33+
private final DispatcherHandlerMappingDetails details;
34+
35+
DispatcherHandlerMappingDescription(String predicate, String handler,
36+
DispatcherHandlerMappingDetails details) {
3437
this.predicate = predicate;
3538
this.handler = handler;
39+
this.details = details;
3640
}
3741

3842
public String getHandler() {
@@ -43,4 +47,8 @@ public String getPredicate() {
4347
return this.predicate;
4448
}
4549

50+
public DispatcherHandlerMappingDetails getDetails() {
51+
return this.details;
52+
}
53+
4654
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright 2012-2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.actuate.web.mappings.reactive;
18+
19+
import org.springframework.boot.actuate.web.mappings.HandlerMethodDescription;
20+
import org.springframework.web.reactive.DispatcherHandler;
21+
22+
/**
23+
* Details of a {@link DispatcherHandler} mapping.
24+
*
25+
* @author Andy Wilkinson
26+
* @since 2.0.0
27+
*/
28+
public class DispatcherHandlerMappingDetails {
29+
30+
private HandlerMethodDescription handlerMethod;
31+
32+
private HandlerFunctionDescription handlerFunction;
33+
34+
private RequestMappingConditionsDescription requestMappingConditions;
35+
36+
public HandlerMethodDescription getHandlerMethod() {
37+
return this.handlerMethod;
38+
}
39+
40+
void setHandlerMethod(HandlerMethodDescription handlerMethod) {
41+
this.handlerMethod = handlerMethod;
42+
}
43+
44+
public HandlerFunctionDescription getHandlerFunction() {
45+
return this.handlerFunction;
46+
}
47+
48+
void setHandlerFunction(HandlerFunctionDescription handlerFunction) {
49+
this.handlerFunction = handlerFunction;
50+
}
51+
52+
public RequestMappingConditionsDescription getRequestMappingConditions() {
53+
return this.requestMappingConditions;
54+
}
55+
56+
void setRequestMappingConditions(
57+
RequestMappingConditionsDescription requestMappingConditions) {
58+
this.requestMappingConditions = requestMappingConditions;
59+
}
60+
61+
}

spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/mappings/reactive/DispatcherHandlersMappingDescriptionProvider.java

+10-3
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
import reactor.core.publisher.Mono;
3030

31+
import org.springframework.boot.actuate.web.mappings.HandlerMethodDescription;
3132
import org.springframework.boot.actuate.web.mappings.MappingDescriptionProvider;
3233
import org.springframework.context.ApplicationContext;
3334
import org.springframework.core.io.Resource;
@@ -121,8 +122,11 @@ public List<DispatcherHandlerMappingDescription> describe(
121122

122123
private DispatcherHandlerMappingDescription describe(
123124
Entry<RequestMappingInfo, HandlerMethod> mapping) {
125+
DispatcherHandlerMappingDetails handlerMapping = new DispatcherHandlerMappingDetails();
126+
handlerMapping
127+
.setHandlerMethod(new HandlerMethodDescription(mapping.getValue()));
124128
return new DispatcherHandlerMappingDescription(mapping.getKey().toString(),
125-
mapping.getValue().toString());
129+
mapping.getValue().toString(), handlerMapping);
126130
}
127131

128132
}
@@ -145,7 +149,8 @@ public List<DispatcherHandlerMappingDescription> describe(
145149
private DispatcherHandlerMappingDescription describe(
146150
Entry<PathPattern, Object> mapping) {
147151
return new DispatcherHandlerMappingDescription(
148-
mapping.getKey().getPatternString(), mapping.getValue().toString());
152+
mapping.getKey().getPatternString(), mapping.getValue().toString(),
153+
null);
149154
}
150155

151156
}
@@ -186,8 +191,10 @@ public void endNested(RequestPredicate predicate) {
186191
@Override
187192
public void route(RequestPredicate predicate,
188193
HandlerFunction<?> handlerFunction) {
194+
DispatcherHandlerMappingDetails details = new DispatcherHandlerMappingDetails();
195+
details.setHandlerFunction(new HandlerFunctionDescription(handlerFunction));
189196
this.descriptions.add(new DispatcherHandlerMappingDescription(
190-
predicate.toString(), handlerFunction.toString()));
197+
predicate.toString(), handlerFunction.toString(), details));
191198
}
192199

193200
@Override
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright 2012-2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.actuate.web.mappings.reactive;
18+
19+
import org.springframework.web.reactive.function.server.HandlerFunction;
20+
21+
/**
22+
* Description of a {@link HandlerFunction}.
23+
*
24+
* @author Andy Wilkinson
25+
* @since 2.0.0
26+
*/
27+
public class HandlerFunctionDescription {
28+
29+
private final String className;
30+
31+
HandlerFunctionDescription(HandlerFunction<?> handlerFunction) {
32+
this.className = handlerFunction.getClass().getCanonicalName();
33+
}
34+
35+
public String getClassName() {
36+
return this.className;
37+
}
38+
39+
}

spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/mappings/servlet/DispatcherServletMappingDescription.java

+9-16
Original file line numberDiff line numberDiff line change
@@ -30,32 +30,25 @@ public class DispatcherServletMappingDescription {
3030

3131
private final String predicate;
3232

33-
/**
34-
* Creates a new {@code DispatcherServletMappingDescription} for the given
35-
* {@code handler} that will receives requests that match the given {@code predicate}.
36-
*
37-
* @param predicate the predicate
38-
* @param handler the handler
39-
*/
40-
public DispatcherServletMappingDescription(String predicate, String handler) {
33+
private final DispatcherServletMappingDetails details;
34+
35+
DispatcherServletMappingDescription(String predicate, String handler,
36+
DispatcherServletMappingDetails details) {
4137
this.handler = handler;
4238
this.predicate = predicate;
39+
this.details = details;
4340
}
4441

45-
/**
46-
* Returns the handler for the described mapping.
47-
* @return the handler
48-
*/
4942
public String getHandler() {
5043
return this.handler;
5144
}
5245

53-
/**
54-
* Returns the predicate for the described mapping.
55-
* @return the predicate
56-
*/
5746
public String getPredicate() {
5847
return this.predicate;
5948
}
6049

50+
public DispatcherServletMappingDetails getDetails() {
51+
return this.details;
52+
}
53+
6154
}

0 commit comments

Comments
 (0)