Skip to content

Commit cec6015

Browse files
committed
Cope with null server or management port when creating curie provider
Previously, a NullPointerException would occur if endpoints.docs.curies.enabled was true and the default value was being used for either server.port or management.port. EndpointWebMvcHypermediaManagementContextConfiguration has been restructured to ensure that the DocsMvcEndpoint bean is defined before the condition on its existence is evaluated. Previously this was dependant on the class’s bean methods being processed in a particular ordering, something that would be ok when using ASM but would vary when using reflection. Closes gh-6584
1 parent f186008 commit cec6015

File tree

3 files changed

+163
-9
lines changed

3 files changed

+163
-9
lines changed

spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcHypermediaManagementContextConfiguration.java

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
import org.springframework.boot.context.properties.EnableConfigurationProperties;
5555
import org.springframework.context.annotation.Bean;
5656
import org.springframework.context.annotation.Conditional;
57+
import org.springframework.context.annotation.Configuration;
5758
import org.springframework.core.MethodParameter;
5859
import org.springframework.core.annotation.AnnotationUtils;
5960
import org.springframework.core.io.ResourceLoader;
@@ -117,14 +118,6 @@ public HalJsonMvcEndpoint halJsonMvcEndpoint(
117118
return new HalJsonMvcEndpoint(managementServletContext);
118119
}
119120

120-
@Bean
121-
@ConditionalOnEnabledEndpoint("docs")
122-
@ConditionalOnResource(resources = "classpath:/META-INF/resources/spring-boot-actuator/docs/index.html")
123-
public DocsMvcEndpoint docsMvcEndpoint(
124-
ManagementServletContext managementServletContext) {
125-
return new DocsMvcEndpoint(managementServletContext);
126-
}
127-
128121
@Bean
129122
@ConditionalOnBean(DocsMvcEndpoint.class)
130123
@ConditionalOnMissingBean(CurieProvider.class)
@@ -133,12 +126,33 @@ public DefaultCurieProvider curieProvider(ServerProperties server,
133126
ManagementServerProperties management, DocsMvcEndpoint endpoint) {
134127
String path = management.getContextPath() + endpoint.getPath()
135128
+ "/#spring_boot_actuator__{rel}";
136-
if (server.getPort().equals(management.getPort()) && management.getPort() != 0) {
129+
if (serverAndManagementPortsAreTheSame(server, management)) {
137130
path = server.getPath(path);
138131
}
139132
return new DefaultCurieProvider("boot", new UriTemplate(path));
140133
}
141134

135+
private boolean serverAndManagementPortsAreTheSame(ServerProperties server,
136+
ManagementServerProperties management) {
137+
if (server.getPort() == null) {
138+
return management.getPort() == null;
139+
}
140+
return server.getPort().equals(management.getPort()) && management.getPort() != 0;
141+
}
142+
143+
@Configuration
144+
static class DocsMvcEndpointConfiguration {
145+
146+
@Bean
147+
@ConditionalOnEnabledEndpoint("docs")
148+
@ConditionalOnResource(resources = "classpath:/META-INF/resources/spring-boot-actuator/docs/index.html")
149+
public DocsMvcEndpoint docsMvcEndpoint(
150+
ManagementServletContext managementServletContext) {
151+
return new DocsMvcEndpoint(managementServletContext);
152+
}
153+
154+
}
155+
142156
/**
143157
* Controller advice that adds links to the actuator endpoint's path.
144158
*/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
/*
2+
* Copyright 2012-2016 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.autoconfigure;
18+
19+
import java.net.URL;
20+
21+
import org.junit.After;
22+
import org.junit.Test;
23+
24+
import org.springframework.boot.actuate.endpoint.mvc.DocsMvcEndpoint;
25+
import org.springframework.boot.actuate.endpoint.mvc.HalJsonMvcEndpoint;
26+
import org.springframework.boot.actuate.endpoint.mvc.ManagementServletContext;
27+
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoints;
28+
import org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration;
29+
import org.springframework.boot.autoconfigure.web.ServerProperties;
30+
import org.springframework.boot.context.properties.EnableConfigurationProperties;
31+
import org.springframework.boot.test.EnvironmentTestUtils;
32+
import org.springframework.context.annotation.Bean;
33+
import org.springframework.context.annotation.Configuration;
34+
import org.springframework.hateoas.Link;
35+
import org.springframework.hateoas.hal.DefaultCurieProvider;
36+
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
37+
38+
import static org.hamcrest.Matchers.equalTo;
39+
import static org.hamcrest.Matchers.is;
40+
import static org.junit.Assert.assertThat;
41+
42+
/**
43+
* Tests for {@link EndpointWebMvcHypermediaManagementContextConfigurationTests}.
44+
*
45+
* @author Andy Wilkinson
46+
*/
47+
public class EndpointWebMvcHypermediaManagementContextConfigurationTests {
48+
49+
private AnnotationConfigWebApplicationContext context;
50+
51+
@After
52+
public void closeContext() {
53+
this.context.close();
54+
}
55+
56+
@Test
57+
public void basicConfiguration() {
58+
load();
59+
assertThat(this.context.getBeansOfType(ManagementServletContext.class).size(),
60+
is(equalTo(1)));
61+
assertThat(this.context.getBeansOfType(HalJsonMvcEndpoint.class).size(),
62+
is(equalTo(1)));
63+
assertThat(this.context.getBeansOfType(DocsMvcEndpoint.class).size(),
64+
is(equalTo(1)));
65+
assertThat(this.context.getBeansOfType(DefaultCurieProvider.class).size(),
66+
is(equalTo(0)));
67+
}
68+
69+
@Test
70+
public void curiesEnabledWithDefaultPorts() {
71+
load("endpoints.docs.curies.enabled:true");
72+
assertThat(getCurieHref(), is(equalTo("/docs/#spring_boot_actuator__{rel}")));
73+
}
74+
75+
@Test
76+
public void curiesEnabledWithRandomPorts() {
77+
load("endpoints.docs.curies.enabled:true", "server.port:0", "management.port:0");
78+
assertThat(getCurieHref(), is(equalTo("/docs/#spring_boot_actuator__{rel}")));
79+
}
80+
81+
@Test
82+
public void curiesEnabledWithSpecificServerPort() {
83+
load("endpoints.docs.curies.enabled:true", "server.port:8080");
84+
assertThat(getCurieHref(), is(equalTo("/docs/#spring_boot_actuator__{rel}")));
85+
}
86+
87+
@Test
88+
public void curiesEnabledWithSpecificManagementPort() {
89+
load("endpoints.docs.curies.enabled:true", "management.port:8081");
90+
assertThat(getCurieHref(), is(equalTo("/docs/#spring_boot_actuator__{rel}")));
91+
}
92+
93+
@Test
94+
public void curiesEnabledWithSpecificManagementAndServerPorts() {
95+
load("endpoints.docs.curies.enabled:true", "server.port:8080",
96+
"management.port:8081");
97+
assertThat(getCurieHref(), is(equalTo("/docs/#spring_boot_actuator__{rel}")));
98+
}
99+
100+
private void load(String... properties) {
101+
this.context = new AnnotationConfigWebApplicationContext();
102+
this.context.setClassLoader(new ClassLoader(getClass().getClassLoader()) {
103+
104+
@Override
105+
public URL getResource(String name) {
106+
if ("META-INF/resources/spring-boot-actuator/docs/index.html"
107+
.equals(name)) {
108+
return super.getResource("actuator-docs-index.html");
109+
}
110+
return super.getResource(name);
111+
}
112+
113+
});
114+
EnvironmentTestUtils.addEnvironment(this.context, properties);
115+
this.context.register(TestConfiguration.class,
116+
HttpMessageConvertersAutoConfiguration.class,
117+
EndpointWebMvcHypermediaManagementContextConfiguration.class);
118+
this.context.refresh();
119+
}
120+
121+
private String getCurieHref() {
122+
DefaultCurieProvider curieProvider = this.context
123+
.getBean(DefaultCurieProvider.class);
124+
Link link = (Link) curieProvider.getCurieInformation(null).iterator().next();
125+
return link.getHref();
126+
}
127+
128+
@Configuration
129+
@EnableConfigurationProperties({ ManagementServerProperties.class,
130+
ServerProperties.class })
131+
static class TestConfiguration {
132+
133+
@Bean
134+
public MvcEndpoints mvcEndpoints() {
135+
return new MvcEndpoints();
136+
}
137+
138+
}
139+
140+
}

spring-boot-actuator/src/test/resources/actuator-docs-index.html

Whitespace-only changes.

0 commit comments

Comments
 (0)