diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcChildContextConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcChildContextConfiguration.java index c15421aa15..35db5b3863 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcChildContextConfiguration.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcChildContextConfiguration.java @@ -16,6 +16,7 @@ package org.springframework.boot.actuate.autoconfigure; +import java.util.Collections; import java.util.HashSet; import java.util.Set; @@ -36,6 +37,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.SearchStrategy; import org.springframework.boot.autoconfigure.web.ErrorAttributes; import org.springframework.boot.autoconfigure.web.HttpMessageConverters; +import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer; import org.springframework.boot.context.embedded.EmbeddedServletContainer; import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer; @@ -75,13 +77,23 @@ public class EndpointWebMvcChildContextConfiguration { // instances get their callback very early in the context lifecycle. private ManagementServerProperties managementServerProperties; + private ServerProperties server; + @Override public void customize(ConfigurableEmbeddedServletContainer container) { if (this.managementServerProperties == null) { this.managementServerProperties = BeanFactoryUtils .beanOfTypeIncludingAncestors(this.beanFactory, ManagementServerProperties.class); + this.server = BeanFactoryUtils + .beanOfTypeIncludingAncestors(this.beanFactory, + ServerProperties.class); } + // Customize as per the parent context first (so e.g. the access logs go to the same place) + server.customize(container); + // Then reset the error pages + container.setErrorPages(Collections.emptySet()); + // and add the management-specific bits container.setPort(this.managementServerProperties.getPort()); container.setAddress(this.managementServerProperties.getAddress()); container.setContextPath(this.managementServerProperties.getContextPath()); diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfigurationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfigurationTests.java index 21218792b8..5c1b4e13a4 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfigurationTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfigurationTests.java @@ -35,6 +35,7 @@ import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration; import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration; import org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext; +import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer; import org.springframework.boot.context.embedded.EmbeddedServletContainer; import org.springframework.boot.context.embedded.EmbeddedServletContainerInitializedEvent; import org.springframework.boot.test.EnvironmentTestUtils; @@ -222,6 +223,7 @@ public class EndpointWebMvcAutoConfigurationTests { assertThat(localServerPort, notNullValue()); assertThat(localManagementPort, notNullValue()); assertThat(localServerPort, not(equalTo(localManagementPort))); + assertThat(applicationContext.getBean(ServerPortConfig.class).getCount(), equalTo(2)); this.applicationContext.close(); assertAllClosed(); } @@ -303,10 +305,22 @@ public class EndpointWebMvcAutoConfigurationTests { @Configuration public static class ServerPortConfig { + + private int count = 0; + + public int getCount() { + return count; + } @Bean public ServerProperties serverProperties() { - ServerProperties properties = new ServerProperties(); + ServerProperties properties = new ServerProperties() { + @Override + public void customize(ConfigurableEmbeddedServletContainer container) { + count++; + super.customize(container); + } + }; properties.setPort(ports.get().server); return properties; } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/thymeleaf/ThymeleafAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/thymeleaf/ThymeleafAutoConfiguration.java index 712e5b2b1f..e403fa32cf 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/thymeleaf/ThymeleafAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/thymeleaf/ThymeleafAutoConfiguration.java @@ -29,6 +29,7 @@ import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; @@ -163,6 +164,7 @@ public class ThymeleafAutoConfiguration { @Configuration @ConditionalOnClass({ Servlet.class }) + @ConditionalOnWebApplication protected static class ThymeleafViewResolverConfiguration { @Autowired diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoPropertiesTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoPropertiesTests.java new file mode 100644 index 0000000000..60105a2a52 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoPropertiesTests.java @@ -0,0 +1,52 @@ +/* + * Copyright 2012-2014 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.autoconfigure.mongo; + +import org.junit.Test; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.test.EnvironmentTestUtils; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Configuration; + +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; + +/** + * Tests for {@link MongoProperties}. + * + * @author Phillip Webb + */ +public class MongoPropertiesTests { + + @Test + public void canBindCharArrayPassword() { + // gh-1572 + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + EnvironmentTestUtils.addEnvironment(context, "spring.data.mongodb.password:word"); + context.register(Conf.class); + context.refresh(); + MongoProperties properties = context.getBean(MongoProperties.class); + assertThat(properties.getPassword(), equalTo("word".toCharArray())); + } + + @Configuration + @EnableConfigurationProperties(MongoProperties.class) + static class Conf { + + } + +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/thymeleaf/ThymeleafAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/thymeleaf/ThymeleafAutoConfigurationTests.java index 629b300771..b28ff65c1a 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/thymeleaf/ThymeleafAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/thymeleaf/ThymeleafAutoConfigurationTests.java @@ -30,6 +30,7 @@ import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockServletContext; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; +import org.springframework.web.servlet.ViewResolver; import org.springframework.web.servlet.support.RequestContext; import org.thymeleaf.TemplateEngine; import org.thymeleaf.context.Context; @@ -38,8 +39,10 @@ import org.thymeleaf.spring4.view.ThymeleafViewResolver; import org.thymeleaf.templateresolver.ITemplateResolver; import org.thymeleaf.templateresolver.TemplateResolver; +import static org.hamcrest.Matchers.containsString; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; /** @@ -49,7 +52,7 @@ import static org.junit.Assert.assertTrue; */ public class ThymeleafAutoConfigurationTests { - private AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + private AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); @After public void close() { @@ -148,4 +151,33 @@ public class ThymeleafAutoConfigurationTests { assertEquals("", result); } + @Test + public void renderTemplate() throws Exception { + this.context.register(ThymeleafAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class); + this.context.refresh(); + TemplateEngine engine = this.context.getBean(TemplateEngine.class); + Context attrs = new Context(Locale.UK, Collections.singletonMap("foo", "bar")); + String result = engine.process("home", attrs); + assertEquals("bar", result); + } + + @Test + public void renderNonWebAppTemplate() throws Exception { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( + ThymeleafAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class); + assertEquals(0, context.getBeanNamesForType(ViewResolver.class).length); + try { + TemplateEngine engine = context.getBean(TemplateEngine.class); + Context attrs = new Context(Locale.UK, Collections.singletonMap("greeting", + "Hello World")); + String result = engine.process("message", attrs); + assertThat(result, containsString("Hello World")); + } + finally { + context.close(); + } + } + } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/BasicErrorControllerIntegrationTest.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/BasicErrorControllerIntegrationTest.java index 991b966b8c..a5ceca8673 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/BasicErrorControllerIntegrationTest.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/BasicErrorControllerIntegrationTest.java @@ -81,7 +81,8 @@ public class BasicErrorControllerIntegrationTest { "http://localhost:" + this.port + "/bind", Map.class); String resp = entity.getBody().toString(); assertThat(resp, containsString("Error count: 1")); - assertThat(resp, containsString("errors=[{codes=")); + assertThat(resp, containsString("errors=[{")); + assertThat(resp, containsString("codes=[")); assertThat(resp, containsString("org.springframework.validation.BindException")); } diff --git a/spring-boot-autoconfigure/src/test/resources/templates/home.html b/spring-boot-autoconfigure/src/test/resources/templates/home.html index e69de29bb2..8f364d3623 100644 --- a/spring-boot-autoconfigure/src/test/resources/templates/home.html +++ b/spring-boot-autoconfigure/src/test/resources/templates/home.html @@ -0,0 +1 @@ +Home \ No newline at end of file diff --git a/spring-boot-autoconfigure/src/test/resources/templates/message.html b/spring-boot-autoconfigure/src/test/resources/templates/message.html new file mode 100644 index 0000000000..53440f08e7 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/resources/templates/message.html @@ -0,0 +1 @@ +Message: Hello \ No newline at end of file diff --git a/spring-boot-samples/README.adoc b/spring-boot-samples/README.adoc index 6757696dd7..e675a89fae 100644 --- a/spring-boot-samples/README.adoc +++ b/spring-boot-samples/README.adoc @@ -10,9 +10,9 @@ -- Simple REST service with production features * link:spring-boot-sample-actuator-ui[spring-boot-sample-actuator-ui] -- A web UI example with production features -* link:spring-boot-sample-actuator-ui[spring-boot-sample-actuator-noweb] +* link:spring-boot-sample-actuator-noweb[spring-boot-sample-actuator-noweb] -- A production features sample with no web application -* link:spring-boot-sample-actuator-ui[spring-boot-sample-actuator-log4j] +* link:spring-boot-sample-actuator-log4j[spring-boot-sample-actuator-log4j] -- A production features sample using log4j for logging (instead of logback) * link:spring-boot-sample-web-ui[spring-boot-sample-web-ui] -- A thymeleaf web application @@ -48,7 +48,7 @@ -- Example showing database migrations with Liquibase * link:spring-boot-sample-amqp[spring-boot-sample-amqp] -- Example showing message-oriented application using RabbitMQ -* link:spring-boot-sample-amqp[spring-boot-sample-hornetq] +* link:spring-boot-sample-hornetq[spring-boot-sample-hornetq] -- Example showing message-oriented application using HornetQ * link:spring-boot-sample-batch[spring-boot-sample-batch] -- Define and run a Batch job in a few lines of code diff --git a/spring-boot-samples/spring-boot-sample-actuator/src/main/resources/application.properties b/spring-boot-samples/spring-boot-sample-actuator/src/main/resources/application.properties index 49672f79a1..d36c067f87 100644 --- a/spring-boot-samples/spring-boot-sample-actuator/src/main/resources/application.properties +++ b/spring-boot-samples/spring-boot-sample-actuator/src/main/resources/application.properties @@ -1,6 +1,7 @@ logging.file: /tmp/logs/app.log logging.level.org.springframework.security: INFO management.address: 127.0.0.1 +#management.port: 8181 endpoints.shutdown.enabled: true server.tomcat.basedir: target/tomcat server.tomcat.access_log_enabled: true diff --git a/spring-boot/src/main/java/org/springframework/boot/bind/RelaxedConversionService.java b/spring-boot/src/main/java/org/springframework/boot/bind/RelaxedConversionService.java index 626f704a07..a645e94cb8 100644 --- a/spring-boot/src/main/java/org/springframework/boot/bind/RelaxedConversionService.java +++ b/spring-boot/src/main/java/org/springframework/boot/bind/RelaxedConversionService.java @@ -49,6 +49,7 @@ class RelaxedConversionService implements ConversionService { this.additionalConverters = new GenericConversionService(); this.additionalConverters .addConverterFactory(new StringToEnumIgnoringCaseConverterFactory()); + this.additionalConverters.addConverter(new StringToCharArrayConverter()); } @Override diff --git a/spring-boot/src/main/java/org/springframework/boot/bind/StringToCharArrayConverter.java b/spring-boot/src/main/java/org/springframework/boot/bind/StringToCharArrayConverter.java new file mode 100644 index 0000000000..1c3e20bcbb --- /dev/null +++ b/spring-boot/src/main/java/org/springframework/boot/bind/StringToCharArrayConverter.java @@ -0,0 +1,33 @@ +/* + * Copyright 2012-2014 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.bind; + +import org.springframework.core.convert.converter.Converter; + +/** + * Converts a String to a Char Array. + * + * @author Phillip Webb + */ +class StringToCharArrayConverter implements Converter { + + @Override + public char[] convert(String source) { + return source.toCharArray(); + } + +} diff --git a/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessorTests.java b/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessorTests.java index 9ef038defe..dedf4bf375 100644 --- a/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessorTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessorTests.java @@ -188,6 +188,16 @@ public class ConfigurationPropertiesBindingPostProcessorTests { assertTrue("No init", ConfigurationPropertiesWithFactoryBean.factoryBeanInit); } + @Test + public void configurationPropertiesWithCharArray() throws Exception { + this.context = new AnnotationConfigApplicationContext(); + EnvironmentTestUtils.addEnvironment(this.context, "test.chars:word"); + this.context.register(PropertyWithCharArray.class); + this.context.refresh(); + assertThat(this.context.getBean(PropertyWithCharArray.class).getChars(), + equalTo("word".toCharArray())); + } + @Configuration @EnableConfigurationProperties public static class TestConfigurationWithValidatingSetter { @@ -302,6 +312,23 @@ public class ConfigurationPropertiesBindingPostProcessorTests { } + @Configuration + @EnableConfigurationProperties + @ConfigurationProperties(prefix = "test") + public static class PropertyWithCharArray { + + private char[] chars; + + public char[] getChars() { + return this.chars; + } + + public void setChars(char[] chars) { + this.chars = chars; + } + + } + @Configuration @EnableConfigurationProperties @ConfigurationProperties(prefix = "test")