diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfiguration.java index fb381ea0064..5ce278f5919 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfiguration.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfiguration.java @@ -26,6 +26,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.actuate.endpoint.Endpoint; @@ -85,8 +86,14 @@ public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware, private ApplicationContext applicationContext; - @Autowired(required = false) - private ManagementServerProperties managementServerProperties = new ManagementServerProperties(); + @Autowired + private ManagementServerProperties managementServerProperties; + + @Override + public void setApplicationContext(ApplicationContext applicationContext) + throws BeansException { + this.applicationContext = applicationContext; + } @Bean @ConditionalOnMissingBean @@ -98,12 +105,6 @@ public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware, return mapping; } - @Override - public void setApplicationContext(ApplicationContext applicationContext) - throws BeansException { - this.applicationContext = applicationContext; - } - @Override public void onApplicationEvent(ContextRefreshedEvent event) { if (event.getApplicationContext() == this.applicationContext) { @@ -114,18 +115,23 @@ public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware, } } - @Bean - public Filter applicationContextIdFilter(ApplicationContext context) { - final String id = context.getId(); - return new OncePerRequestFilter() { - @Override - protected void doFilterInternal(HttpServletRequest request, - HttpServletResponse response, FilterChain filterChain) - throws ServletException, IOException { - response.addHeader("X-Application-Context", id); - filterChain.doFilter(request, response); - } - }; + // Put Servlets and Filters in their own nested class so they don't force early + // instantiation of ManagementServerProperties. + @Configuration + protected static class ApplicationContextFilterConfiguration { + @Bean + public Filter applicationContextIdFilter(ApplicationContext context) { + final String id = context.getId(); + return new OncePerRequestFilter() { + @Override + protected void doFilterInternal(HttpServletRequest request, + HttpServletResponse response, FilterChain filterChain) + throws ServletException, IOException { + response.addHeader("X-Application-Context", id); + filterChain.doFilter(request, response); + } + }; + } } @Bean @@ -181,11 +187,11 @@ public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware, childContext.refresh(); } - private enum ManagementServerPort { + protected static enum ManagementServerPort { DISABLE, SAME, DIFFERENT; - public static ManagementServerPort get(ApplicationContext beanFactory) { + public static ManagementServerPort get(BeanFactory beanFactory) { ServerProperties serverProperties; try { @@ -208,7 +214,7 @@ public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware, return DISABLE; } if (!(beanFactory instanceof WebApplicationContext)) { - // Current context is no a a webapp + // Current context is not a webapp return DIFFERENT; } return managementServerProperties.getPort() == null diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/JolokiaAutoConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/JolokiaAutoConfiguration.java index 9e4e5eb6cc4..d00e28df68e 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/JolokiaAutoConfiguration.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/JolokiaAutoConfiguration.java @@ -17,23 +17,20 @@ package org.springframework.boot.actuate.autoconfigure; import java.util.Map; +import java.util.Properties; import org.jolokia.http.AgentServlet; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.actuate.endpoint.mvc.JolokiaMvcEndpoint; -import org.springframework.boot.actuate.properties.ManagementServerProperties; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration; import org.springframework.boot.bind.RelaxedPropertyResolver; -import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory; -import org.springframework.boot.context.embedded.ServletRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; @@ -54,11 +51,11 @@ import org.springframework.core.env.Environment; * supported configuration parameters. * * @author Christian Dupuis + * @author Dave Syer */ @Configuration @ConditionalOnWebApplication @ConditionalOnClass({ AgentServlet.class }) -@ConditionalOnBean(EmbeddedServletContainerFactory.class) @AutoConfigureBefore(ManagementSecurityAutoConfiguration.class) @AutoConfigureAfter(EmbeddedServletContainerAutoConfiguration.class) @ConditionalOnExpression("${endpoints.jolokia.enabled:true}") @@ -66,40 +63,28 @@ public class JolokiaAutoConfiguration { private RelaxedPropertyResolver environment; - @Autowired - private ManagementServerProperties management; - @Autowired public void setEnvironment(Environment environment) { this.environment = new RelaxedPropertyResolver(environment); } - @Bean - @ConditionalOnMissingBean({ AgentServlet.class }) - public AgentServlet jolokiaServlet() { - return new AgentServlet(); - } - - @Bean - public ServletRegistrationBean jolokiaServletRegistration(AgentServlet servlet) { - ServletRegistrationBean registrationBean = new ServletRegistrationBean(servlet, - this.management.getContextPath() + jolokiaEndpoint().getPath() + "/*"); - addInitParameters(registrationBean); - return registrationBean; - } - @Bean @ConditionalOnMissingBean public JolokiaMvcEndpoint jolokiaEndpoint() { - return new JolokiaMvcEndpoint(); + JolokiaMvcEndpoint endpoint = new JolokiaMvcEndpoint(); + endpoint.setInitParameters(getInitParameters()); + return endpoint; } - protected void addInitParameters(ServletRegistrationBean registrationBean) { + private Properties getInitParameters() { + Properties properties = new Properties(); Map configParameters = this.environment .getSubProperties("jolokia.config."); for (Map.Entry configParameter : configParameters.entrySet()) { - registrationBean.addInitParameter(configParameter.getKey(), configParameter - .getValue().toString()); + properties.setProperty(configParameter.getKey(), configParameter.getValue() + .toString()); } + return properties; } + } diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/JolokiaMvcEndpoint.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/JolokiaMvcEndpoint.java index 1d0473cdcb4..b8564a4cefc 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/JolokiaMvcEndpoint.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/JolokiaMvcEndpoint.java @@ -16,11 +16,26 @@ package org.springframework.boot.actuate.endpoint.mvc; +import java.util.Properties; + +import javax.servlet.ServletContext; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import javax.servlet.http.HttpServletResponse; import javax.validation.constraints.NotNull; import javax.validation.constraints.Pattern; +import org.jolokia.http.AgentServlet; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.InitializingBean; import org.springframework.boot.actuate.endpoint.Endpoint; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.context.ServletContextAware; +import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.servlet.mvc.ServletWrappingController; /** * {@link Endpoint} implementation to register the Jolokia infrastructure with the Boot @@ -29,7 +44,8 @@ import org.springframework.boot.context.properties.ConfigurationProperties; * @author Christian Dupuis */ @ConfigurationProperties(name = "endpoints.jolokia", ignoreUnknownFields = false) -public class JolokiaMvcEndpoint implements MvcEndpoint { +public class JolokiaMvcEndpoint implements MvcEndpoint, InitializingBean, + ApplicationContextAware, ServletContextAware { @NotNull @Pattern(regexp = "/[^/]*", message = "Path must start with /") @@ -39,8 +55,30 @@ public class JolokiaMvcEndpoint implements MvcEndpoint { private boolean enabled = true; + private ServletWrappingController controller = new ServletWrappingController(); + public JolokiaMvcEndpoint() { this.path = "/jolokia"; + this.controller.setServletClass(AgentServlet.class); + this.controller.setServletName("jolokia"); + } + + @Override + public void afterPropertiesSet() throws Exception { + this.controller.afterPropertiesSet(); + } + + public void setServletContext(ServletContext servletContext) { + this.controller.setServletContext(servletContext); + } + + public void setInitParameters(Properties initParameters) { + this.controller.setInitParameters(initParameters); + } + + public final void setApplicationContext(ApplicationContext context) + throws BeansException { + this.controller.setApplicationContext(context); } public boolean isEnabled() { @@ -74,4 +112,37 @@ public class JolokiaMvcEndpoint implements MvcEndpoint { return null; } + @RequestMapping("/**") + public ModelAndView handle(HttpServletRequest request, HttpServletResponse response) + throws Exception { + return this.controller.handleRequest(new PathStripper(request, getPath()), + response); + } + + private static class PathStripper extends HttpServletRequestWrapper { + + private String path; + + public PathStripper(HttpServletRequest request, String path) { + super(request); + this.path = path; + } + + @Override + public String getPathInfo() { + String value = super.getRequestURI(); + if (value.startsWith(this.path)) { + value = value.substring(this.path.length()); + } + int index = value.indexOf("?"); + if (index > 0) { + value = value.substring(0, index); + } + while (value.startsWith("/")) { + value = value.substring(1); + } + return value; + } + + } } diff --git a/spring-boot-actuator/src/main/resources/META-INF/spring.factories b/spring-boot-actuator/src/main/resources/META-INF/spring.factories index 9993bce5f2a..524341d7e0f 100644 --- a/spring-boot-actuator/src/main/resources/META-INF/spring.factories +++ b/spring-boot-actuator/src/main/resources/META-INF/spring.factories @@ -4,8 +4,8 @@ org.springframework.boot.actuate.autoconfigure.CrshAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.EndpointMBeanExportAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration,\ -org.springframework.boot.actuate.autoconfigure.ErrorMvcAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.JolokiaAutoConfiguration,\ +org.springframework.boot.actuate.autoconfigure.ErrorMvcAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.ManagementServerPropertiesAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.MetricFilterAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.MetricRepositoryAutoConfiguration,\ diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/JolokiaAutoConfigurationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/JolokiaAutoConfigurationTests.java index de66bf45d46..53d37779cb0 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/JolokiaAutoConfigurationTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/JolokiaAutoConfigurationTests.java @@ -16,26 +16,21 @@ package org.springframework.boot.actuate.autoconfigure; -import javax.servlet.Servlet; -import javax.servlet.ServletRegistration; - -import org.jolokia.http.AgentServlet; import org.junit.After; import org.junit.Test; import org.springframework.boot.TestUtils; +import org.springframework.boot.actuate.endpoint.mvc.JolokiaMvcEndpoint; import org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration; import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration; import org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext; import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizerBeanPostProcessor; import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory; import org.springframework.boot.context.embedded.MockEmbeddedServletContainerFactory; -import org.springframework.boot.context.embedded.MockEmbeddedServletContainerFactory.RegisteredServlet; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; /** * Tests for {@link JolokiaAutoConfiguration}. @@ -64,7 +59,7 @@ public class JolokiaAutoConfigurationTests { HttpMessageConvertersAutoConfiguration.class, JolokiaAutoConfiguration.class); this.context.refresh(); - assertEquals(1, this.context.getBeanNamesForType(AgentServlet.class).length); + assertEquals(1, this.context.getBeanNamesForType(JolokiaMvcEndpoint.class).length); } @Test @@ -76,29 +71,7 @@ public class JolokiaAutoConfigurationTests { HttpMessageConvertersAutoConfiguration.class, JolokiaAutoConfiguration.class); this.context.refresh(); - assertEquals(0, this.context.getBeanNamesForType(AgentServlet.class).length); - } - - @Test - public void agentServletRegisteredWithServletContainer() throws Exception { - this.context = new AnnotationConfigEmbeddedWebApplicationContext(); - this.context.register(Config.class, WebMvcAutoConfiguration.class, - ManagementServerPropertiesAutoConfiguration.class, - HttpMessageConvertersAutoConfiguration.class, - JolokiaAutoConfiguration.class); - this.context.refresh(); - - Servlet servlet = null; - ServletRegistration.Dynamic registration = null; - for (RegisteredServlet registeredServlet : Config.containerFactory.getContainer() - .getRegisteredServlets()) { - if (registeredServlet.getServlet() instanceof AgentServlet) { - servlet = registeredServlet.getServlet(); - registration = registeredServlet.getRegistration(); - } - } - assertNotNull(servlet); - assertNotNull(registration); + assertEquals(0, this.context.getBeanNamesForType(JolokiaMvcEndpoint.class).length); } @Configuration diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/EnvironmentMvcEndpointTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/EnvironmentMvcEndpointTests.java index 79a9f4cd6e2..b60824dbe57 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/EnvironmentMvcEndpointTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/EnvironmentMvcEndpointTests.java @@ -22,6 +22,7 @@ import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.TestUtils; import org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration; +import org.springframework.boot.actuate.autoconfigure.ManagementServerPropertiesAutoConfiguration; import org.springframework.boot.actuate.endpoint.EnvironmentEndpoint; import org.springframework.boot.actuate.endpoint.mvc.EnvironmentMvcEndpointTests.TestConfiguration; import org.springframework.boot.test.SpringApplicationConfiguration; @@ -73,7 +74,8 @@ public class EnvironmentMvcEndpointTests { .andExpect(content().string(equalToIgnoringCase("bar"))); } - @Import(EndpointWebMvcAutoConfiguration.class) + @Import({ EndpointWebMvcAutoConfiguration.class, + ManagementServerPropertiesAutoConfiguration.class }) @EnableWebMvc @Configuration public static class TestConfiguration { diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/JolokiaEndpointTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/JolokiaEndpointTests.java index 318a83adb70..fc09993edf0 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/JolokiaEndpointTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/JolokiaEndpointTests.java @@ -16,21 +16,35 @@ package org.springframework.boot.actuate.endpoint.mvc; +import java.util.Set; + +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.TestUtils; import org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration; +import org.springframework.boot.actuate.autoconfigure.JolokiaAutoConfiguration; +import org.springframework.boot.actuate.autoconfigure.ManagementServerPropertiesAutoConfiguration; import org.springframework.boot.actuate.endpoint.mvc.JolokiaEndpointTests.Config; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.test.SpringApplicationConfiguration; -import org.springframework.context.annotation.Bean; +import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import static org.hamcrest.Matchers.containsString; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; /** * @author Christian Dupuis @@ -44,21 +58,49 @@ public class JolokiaEndpointTests { @Autowired private MvcEndpoints endpoints; + @Autowired + private WebApplicationContext context; + + private MockMvc mvc; + + @Before + public void setUp() { + this.mvc = MockMvcBuilders.webAppContextSetup(this.context).build(); + TestUtils.addEnviroment((ConfigurableApplicationContext) this.context, "foo:bar"); + } + @Test public void endpointRegistered() throws Exception { - assertEquals(1, this.endpoints.getEndpoints().size()); + Set values = this.endpoints.getEndpoints(); + assertEquals(1, values.size()); + assertTrue(values.iterator().next() instanceof JolokiaMvcEndpoint); + } + + @Test + public void search() throws Exception { + this.mvc.perform(get("/jolokia/search/java.lang:*")).andExpect(status().isOk()) + .andExpect(content().string(containsString("GarbageCollector"))); + } + + @Test + public void read() throws Exception { + this.mvc.perform(get("/jolokia/read/java.lang:type=Memory")) + .andExpect(status().isOk()) + .andExpect(content().string(containsString("NonHeapMemoryUsage"))); + } + + @Test + public void list() throws Exception { + this.mvc.perform(get("/jolokia/list/java.lang/type=Memory/attr")) + .andExpect(status().isOk()) + .andExpect(content().string(containsString("NonHeapMemoryUsage"))); } @Configuration @EnableConfigurationProperties @EnableWebMvc - @Import(EndpointWebMvcAutoConfiguration.class) + @Import({ EndpointWebMvcAutoConfiguration.class, JolokiaAutoConfiguration.class, + ManagementServerPropertiesAutoConfiguration.class }) public static class Config { - - @Bean - public JolokiaMvcEndpoint endpoint() { - return new JolokiaMvcEndpoint(); - } - } } diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/web/BasicErrorControllerSpecialIntegrationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/web/BasicErrorControllerSpecialIntegrationTests.java index efbc69ff7f4..5e358c6751b 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/web/BasicErrorControllerSpecialIntegrationTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/web/BasicErrorControllerSpecialIntegrationTests.java @@ -112,7 +112,6 @@ public class BasicErrorControllerSpecialIntegrationTests { @EnableAutoConfiguration(exclude = { SecurityAutoConfiguration.class, ManagementSecurityAutoConfiguration.class }) protected static class ChildConfiguration { - // For manual testing public static void main(String[] args) { new SpringApplicationBuilder(ParentConfiguration.class).child( diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/MultipartAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/MultipartAutoConfigurationTests.java index dbb1ded3d3b..018d9d4935c 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/MultipartAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/MultipartAutoConfigurationTests.java @@ -139,12 +139,13 @@ public class MultipartAutoConfigurationTests { } @Test - public void containerWithAutomatedMultipartTomcatConfiguration() { + public void containerWithAutomatedMultipartTomcatConfiguration() throws Exception { this.context = new AnnotationConfigEmbeddedWebApplicationContext( ContainerWithEverythingTomcat.class, EmbeddedServletContainerAutoConfiguration.class, DispatcherServletAutoConfiguration.class, MultipartAutoConfiguration.class); + new RestTemplate().getForObject("http://localhost:8080/", String.class); this.context.getBean(MultipartConfigElement.class); assertSame(this.context.getBean(DispatcherServlet.class).getMultipartResolver(), this.context.getBean(StandardServletMultipartResolver.class)); diff --git a/spring-boot-samples/spring-boot-sample-actuator-ui/pom.xml b/spring-boot-samples/spring-boot-sample-actuator-ui/pom.xml index 805f4a2c2f2..3f66493a44e 100644 --- a/spring-boot-samples/spring-boot-sample-actuator-ui/pom.xml +++ b/spring-boot-samples/spring-boot-sample-actuator-ui/pom.xml @@ -36,6 +36,10 @@ org.thymeleaf thymeleaf-spring4 + + org.jolokia + jolokia-core + diff --git a/spring-boot/src/main/java/org/springframework/boot/bind/PropertiesConfigurationFactory.java b/spring-boot/src/main/java/org/springframework/boot/bind/PropertiesConfigurationFactory.java index 6551b9bdb8d..8ccf9ac5b56 100644 --- a/spring-boot/src/main/java/org/springframework/boot/bind/PropertiesConfigurationFactory.java +++ b/spring-boot/src/main/java/org/springframework/boot/bind/PropertiesConfigurationFactory.java @@ -249,23 +249,30 @@ public class PropertiesConfigurationFactory implements FactoryBean, customizeBinder(dataBinder); Set names = new HashSet(); + Set patterns = new HashSet(); if (this.target != null) { PropertyDescriptor[] descriptors = BeanUtils .getPropertyDescriptors(this.target.getClass()); + String[] prefixes = this.targetName != null ? new String[] { + this.targetName + ".", this.targetName + "_" } : new String[] { "" }; + String[] suffixes = new String[] { ".*", "_*" }; for (PropertyDescriptor descriptor : descriptors) { String name = descriptor.getName(); if (!name.equals("class")) { - names.add(name); - names.add(name + ".*"); - names.add(name + "_*"); - names.add("*_"+name); + for (String prefix : prefixes) { + names.add(prefix + name); + patterns.add(prefix + name); + for (String suffix : suffixes) { + patterns.add(prefix + name + suffix); + } + } } } } PropertyValues propertyValues = (this.properties != null ? new MutablePropertyValues( this.properties) : new PropertySourcesPropertyValues( - this.propertySources, names)); + this.propertySources, patterns, names)); dataBinder.bind(propertyValues); if (this.validator != null) { diff --git a/spring-boot/src/main/java/org/springframework/boot/bind/PropertySourcesPropertyValues.java b/spring-boot/src/main/java/org/springframework/boot/bind/PropertySourcesPropertyValues.java index c30daa9fcc3..618fead9a40 100644 --- a/spring-boot/src/main/java/org/springframework/boot/bind/PropertySourcesPropertyValues.java +++ b/spring-boot/src/main/java/org/springframework/boot/bind/PropertySourcesPropertyValues.java @@ -45,37 +45,39 @@ public class PropertySourcesPropertyValues implements PropertyValues { private PropertySources propertySources; - private Collection NON_ENUMERABLES = Arrays.asList( + private Collection NON_ENUMERABLE_ENUMERABLES = Arrays.asList( StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, - StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME);; + StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME); /** * Create a new PropertyValues from the given PropertySources * @param propertySources a PropertySources instance */ public PropertySourcesPropertyValues(PropertySources propertySources) { - this(propertySources, null); + this(propertySources, null, null); } /** * Create a new PropertyValues from the given PropertySources * @param propertySources a PropertySources instance - * @param systemPropertyNames property names to include from system properties and + * @param patterns property name patterns to include from system properties and * environment variables + * @param names exact property names to include */ public PropertySourcesPropertyValues(PropertySources propertySources, - Collection systemPropertyNames) { + Collection patterns, Collection names) { this.propertySources = propertySources; PropertySourcesPropertyResolver resolver = new PropertySourcesPropertyResolver( propertySources); - String[] includes = systemPropertyNames == null ? new String[0] - : systemPropertyNames.toArray(new String[0]); + String[] includes = patterns == null ? new String[0] : patterns + .toArray(new String[0]); + String[] exacts = names == null ? new String[0] : names.toArray(new String[0]); for (PropertySource source : propertySources) { if (source instanceof EnumerablePropertySource) { EnumerablePropertySource enumerable = (EnumerablePropertySource) source; if (enumerable.getPropertyNames().length > 0) { for (String propertyName : enumerable.getPropertyNames()) { - if (this.NON_ENUMERABLES.contains(source.getName()) + if (this.NON_ENUMERABLE_ENUMERABLES.contains(source.getName()) && !PatternMatchUtils.simpleMatch(includes, propertyName)) { continue; } @@ -91,6 +93,25 @@ public class PropertySourcesPropertyValues implements PropertyValues { } } } + else { + // We can only do exact matches for non-enumerable property names, but + // that's better than nothing... + for (String propertyName : exacts) { + Object value; + value = source.getProperty(propertyName); + if (value != null) { + this.propertyValues.put(propertyName, new PropertyValue( + propertyName, value)); + continue; + } + value = source.getProperty(propertyName.toUpperCase()); + if (value != null) { + this.propertyValues.put(propertyName, new PropertyValue( + propertyName, value)); + continue; + } + } + } } } diff --git a/spring-boot/src/main/java/org/springframework/boot/context/embedded/ServletRegistrationBean.java b/spring-boot/src/main/java/org/springframework/boot/context/embedded/ServletRegistrationBean.java index e99c7b86f89..38d92062273 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/embedded/ServletRegistrationBean.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/embedded/ServletRegistrationBean.java @@ -52,7 +52,7 @@ public class ServletRegistrationBean extends RegistrationBean { private Set urlMappings = new LinkedHashSet(); - private int loadOnStartup = 1; + private int loadOnStartup = -1; private MultipartConfigElement multipartConfig; diff --git a/spring-boot/src/test/java/org/springframework/boot/bind/PropertySourcesPropertyValuesTests.java b/spring-boot/src/test/java/org/springframework/boot/bind/PropertySourcesPropertyValuesTests.java index c8bb7153f59..c8a1273c237 100644 --- a/spring-boot/src/test/java/org/springframework/boot/bind/PropertySourcesPropertyValuesTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/bind/PropertySourcesPropertyValuesTests.java @@ -90,6 +90,15 @@ public class PropertySourcesPropertyValuesTests { assertEquals("bar", target.getName()); } + @Test + public void testPlaceholdersBindingNonEnumerable() { + FooBean target = new FooBean(); + DataBinder binder = new DataBinder(target); + binder.bind(new PropertySourcesPropertyValues(this.propertySources, null, + Collections.singleton("foo"))); + assertEquals("bar", target.getFoo()); + } + @Test public void testPlaceholdersBindingWithError() { TestBean target = new TestBean(); @@ -112,4 +121,16 @@ public class PropertySourcesPropertyValuesTests { } } + public static class FooBean { + private String foo; + + public String getFoo() { + return this.foo; + } + + public void setFoo(String foo) { + this.foo = foo; + } + } + } diff --git a/spring-boot/src/test/java/org/springframework/boot/test/SpringApplicationContextLoader.java b/spring-boot/src/test/java/org/springframework/boot/test/SpringApplicationContextLoader.java index 9160ce82c6f..4653c14500a 100644 --- a/spring-boot/src/test/java/org/springframework/boot/test/SpringApplicationContextLoader.java +++ b/spring-boot/src/test/java/org/springframework/boot/test/SpringApplicationContextLoader.java @@ -88,8 +88,7 @@ public class SpringApplicationContextLoader extends AbstractContextLoader { private Map getArgs(MergedContextConfiguration mergedConfig) { Map args = new LinkedHashMap(); // Not running an embedded server, just setting up web context - args.put("server.port", "0"); - args.put("management.port", "0"); + args.put("server.port", "-1"); return args; }