Allow endpoint paths to be configured via endpoint.<name>.path

Support for configuring an endpoint’s path separately from its id was
introduced in 97255785, but it didn’t work for a variety of reasons:

 1. Some custom MVC endpoints did not have configuration properties
    bound to them
 2. Some generic endpoints rejected the path property as they were
    configured not to ignore unknown fields
 3. The property used to configure the path was dependent on the id
    of the endpoint. This meant that the path property’s name would
    change if the endpoint’s id was changed

This commit addresses these problems:

 1. @ConfigurationProperties has been added to custom MvcEndpoints where
    it was missing
 2. Generic endpoints have been updated to ignore unknown fields,
    allowing the path of their MVC adapter to be configured
 3. Rather than using the id of a generic endpoint to determine the name
    of its path property, the prefix or value of the endpoint’s
    @ConfigurationProperties annotation is used instead. Any generic
    endpoint that is not annotated with @ConfigurationProperties is
    ignored, making its path unconfigurable.

Closes gh-5105
This commit is contained in:
Andy Wilkinson
2016-02-22 17:55:27 +00:00
parent c4205d04b3
commit 617c97322d
25 changed files with 277 additions and 38 deletions

View File

@@ -0,0 +1,173 @@
/*
* Copyright 2012-2016 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.autoconfigure;
import liquibase.integration.spring.SpringLiquibase;
import org.flywaydb.core.Flyway;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.actuate.endpoint.AutoConfigurationReportEndpoint;
import org.springframework.boot.actuate.endpoint.BeansEndpoint;
import org.springframework.boot.actuate.endpoint.ConfigurationPropertiesReportEndpoint;
import org.springframework.boot.actuate.endpoint.DumpEndpoint;
import org.springframework.boot.actuate.endpoint.FlywayEndpoint;
import org.springframework.boot.actuate.endpoint.InfoEndpoint;
import org.springframework.boot.actuate.endpoint.LiquibaseEndpoint;
import org.springframework.boot.actuate.endpoint.RequestMappingEndpoint;
import org.springframework.boot.actuate.endpoint.ShutdownEndpoint;
import org.springframework.boot.actuate.endpoint.TraceEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.DocsMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter;
import org.springframework.boot.actuate.endpoint.mvc.EnvironmentMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.HalJsonMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.HealthMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.JolokiaMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.LogFileMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.ManagementServletContext;
import org.springframework.boot.actuate.endpoint.mvc.MetricsMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoints;
import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport;
import org.springframework.boot.autoconfigure.test.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration;
import org.springframework.boot.test.EnvironmentTestUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.mock.web.MockServletContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
/**
* Tests for configuring the path of an MVC endpoint.
*
* @author Andy Wilkinson
*/
@RunWith(Parameterized.class)
public class MvcEndpointPathConfigurationTests {
private final String endpointName;
private final Class<?> endpointClass;
private AnnotationConfigWebApplicationContext context;
@After
public void cleanUp() {
if (this.context != null) {
this.context.close();
}
}
@Parameters(name = "{0}")
public static Object[] parameters() {
return new Object[] { new Object[] { "actuator", HalJsonMvcEndpoint.class },
new Object[] { "autoconfig", AutoConfigurationReportEndpoint.class },
new Object[] { "beans", BeansEndpoint.class },
new Object[] { "configprops",
ConfigurationPropertiesReportEndpoint.class },
new Object[] { "docs", DocsMvcEndpoint.class },
new Object[] { "dump", DumpEndpoint.class },
new Object[] { "env", EnvironmentMvcEndpoint.class },
new Object[] { "flyway", FlywayEndpoint.class },
new Object[] { "health", HealthMvcEndpoint.class },
new Object[] { "info", InfoEndpoint.class },
new Object[] { "jolokia", JolokiaMvcEndpoint.class },
new Object[] { "liquibase", LiquibaseEndpoint.class },
new Object[] { "logfile", LogFileMvcEndpoint.class },
new Object[] { "mappings", RequestMappingEndpoint.class },
new Object[] { "metrics", MetricsMvcEndpoint.class },
new Object[] { "shutdown", ShutdownEndpoint.class },
new Object[] { "trace", TraceEndpoint.class } };
}
public MvcEndpointPathConfigurationTests(String endpointName,
Class<?> endpointClass) {
this.endpointName = endpointName;
this.endpointClass = endpointClass;
}
@Test
public void pathCanBeConfigured() {
this.context = new AnnotationConfigWebApplicationContext();
this.context.register(TestConfiguration.class);
this.context.setServletContext(new MockServletContext());
EnvironmentTestUtils.addEnvironment(this.context,
"endpoints." + this.endpointName + ".path" + ":/custom/path",
"endpoints." + this.endpointName + ".enabled:true",
"logging.file:target/test.log");
this.context.refresh();
assertThat(getConfiguredPath(), is(equalTo("/custom/path")));
}
private String getConfiguredPath() {
if (MvcEndpoint.class.isAssignableFrom(this.endpointClass)) {
return ((MvcEndpoint) this.context.getBean(this.endpointClass)).getPath();
}
for (MvcEndpoint endpoint : this.context.getBean(MvcEndpoints.class)
.getEndpoints()) {
if (endpoint instanceof EndpointMvcAdapter && this.endpointClass
.isInstance(((EndpointMvcAdapter) endpoint).getDelegate())) {
return ((EndpointMvcAdapter) endpoint).getPath();
}
}
throw new IllegalStateException(
"Could not get configured path for " + this.endpointClass);
}
@Configuration
@ImportAutoConfiguration({ EndpointAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
ManagementServerPropertiesAutoConfiguration.class,
ServerPropertiesAutoConfiguration.class,
EndpointWebMvcAutoConfiguration.class, JolokiaAutoConfiguration.class,
EndpointAutoConfiguration.class })
protected static class TestConfiguration {
@Bean
public ConditionEvaluationReport conditionEvaluationReport(
ConfigurableListableBeanFactory beanFactory) {
return ConditionEvaluationReport.get(beanFactory);
}
@Bean
public FlywayEndpoint flyway() {
return new FlywayEndpoint(new Flyway());
}
@Bean
public LiquibaseEndpoint liquibase() {
return new LiquibaseEndpoint(new SpringLiquibase());
}
@Bean
public DocsMvcEndpoint docs(ManagementServletContext managementServletContext) {
return new DocsMvcEndpoint(managementServletContext);
}
}
}

View File

@@ -19,6 +19,7 @@ package org.springframework.boot.actuate.endpoint.mvc;
import org.junit.Test;
import org.springframework.boot.actuate.endpoint.AbstractEndpoint;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.test.EnvironmentTestUtils;
import org.springframework.context.support.StaticApplicationContext;
@@ -77,6 +78,7 @@ public class MvcEndpointsTests {
this.endpoints.getEndpoints().iterator().next().getPath());
}
@ConfigurationProperties("endpoints.test")
protected static class TestEndpoint extends AbstractEndpoint<String> {
public TestEndpoint() {