Commit 53d24301 authored by Phillip Webb's avatar Phillip Webb

Merge branch '1.1.x'

parents 5ce46690 f8bf0e20
...@@ -31,7 +31,7 @@ import org.springframework.web.bind.annotation.ResponseBody; ...@@ -31,7 +31,7 @@ import org.springframework.web.bind.annotation.ResponseBody;
/** /**
* Adapter to expose {@link HealthEndpoint} as an {@link MvcEndpoint}. * Adapter to expose {@link HealthEndpoint} as an {@link MvcEndpoint}.
* *
* @author Christian Dupuis * @author Christian Dupuis
* @since 1.1.0 * @since 1.1.0
*/ */
......
...@@ -24,7 +24,7 @@ import com.mongodb.CommandResult; ...@@ -24,7 +24,7 @@ import com.mongodb.CommandResult;
/** /**
* Simple implementation of a {@link HealthIndicator} returning status information for * Simple implementation of a {@link HealthIndicator} returning status information for
* Mongo data stores. * Mongo data stores.
* *
* @author Christian Dupuis * @author Christian Dupuis
* @since 1.1.0 * @since 1.1.0
*/ */
......
...@@ -53,6 +53,8 @@ import static org.springframework.util.StringUtils.trimAllWhitespace; ...@@ -53,6 +53,8 @@ import static org.springframework.util.StringUtils.trimAllWhitespace;
@ConfigurationProperties(prefix = "spring.messages") @ConfigurationProperties(prefix = "spring.messages")
public class MessageSourceAutoConfiguration { public class MessageSourceAutoConfiguration {
private static final Resource[] NO_RESOURCES = {};
private String basename = "messages"; private String basename = "messages";
private String encoding = "utf-8"; private String encoding = "utf-8";
...@@ -103,27 +105,27 @@ public class MessageSourceAutoConfiguration { ...@@ -103,27 +105,27 @@ public class MessageSourceAutoConfiguration {
String basename = context.getEnvironment().getProperty( String basename = context.getEnvironment().getProperty(
"spring.messages.basename", "messages"); "spring.messages.basename", "messages");
for (String name : commaDelimitedListToStringArray(trimAllWhitespace(basename))) { for (String name : commaDelimitedListToStringArray(trimAllWhitespace(basename))) {
Resource[] resources; for (Resource resource : getResources(context.getClassLoader(), name)) {
try {
resources = new PathMatchingResourcePatternResolver(
context.getClassLoader()).getResources("classpath*:" + name
+ "*.properties");
}
catch (IOException e) {
continue;
}
for (Resource resource : resources) {
if (resource.exists()) { if (resource.exists()) {
return ConditionOutcome return ConditionOutcome.match("Bundle found for "
.match("Bundle found for spring.messages.basename: " + "spring.messages.basename: " + name);
+ name);
} }
} }
} }
return ConditionOutcome return ConditionOutcome.noMatch("No bundle found for "
.noMatch("No bundle found for spring.messages.basename: " + basename); + "spring.messages.basename: " + basename);
} }
private Resource[] getResources(ClassLoader classLoader, String name) {
try {
return new PathMatchingResourcePatternResolver(classLoader)
.getResources("classpath*:" + name + "*.properties");
}
catch (IOException ex) {
return NO_RESOURCES;
}
}
} }
} }
...@@ -107,7 +107,8 @@ public class RabbitProperties { ...@@ -107,7 +107,8 @@ public class RabbitProperties {
} }
result.add(address); result.add(address);
} }
return result.isEmpty() ? null : StringUtils.collectionToCommaDelimitedString(result); return (result.isEmpty() ? null : StringUtils
.collectionToCommaDelimitedString(result));
} }
public void setPort(int port) { public void setPort(int port) {
......
...@@ -67,25 +67,34 @@ public class HttpMessageConverters implements Iterable<HttpMessageConverter<?>> ...@@ -67,25 +67,34 @@ public class HttpMessageConverters implements Iterable<HttpMessageConverter<?>>
/** /**
* Create a new {@link HttpMessageConverters} instance with the specified additional * Create a new {@link HttpMessageConverters} instance with the specified additional
* converters. * converters.
* @param additionalConverters additional converters to be added. New converters will * @param additionalConverters additional converters to be added. Items are added just
* be added to the front of the list, overrides will replace existing items without * before any default converter of the same type (or at the front of the list if no
* changing the order. The {@link #getConverters()} methods can be used for further * default converter is found) The {@link #getConverters()} methods can be used for
* converter manipulation. * further converter manipulation.
*/ */
public HttpMessageConverters(Collection<HttpMessageConverter<?>> additionalConverters) { public HttpMessageConverters(Collection<HttpMessageConverter<?>> additionalConverters) {
List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>(); List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>();
List<HttpMessageConverter<?>> defaultConverters = getDefaultConverters(); List<HttpMessageConverter<?>> processing = new ArrayList<HttpMessageConverter<?>>(
converters.addAll(additionalConverters); additionalConverters);
converters.addAll(defaultConverters); for (HttpMessageConverter<?> defaultConverter : getDefaultConverters()) {
Iterator<HttpMessageConverter<?>> iterator = processing.iterator();
while (iterator.hasNext()) {
HttpMessageConverter<?> candidate = iterator.next();
if (ClassUtils.isAssignableValue(defaultConverter.getClass(), candidate)) {
converters.add(candidate);
iterator.remove();
}
}
converters.add(defaultConverter);
}
converters.addAll(0, processing);
this.converters = Collections.unmodifiableList(converters); this.converters = Collections.unmodifiableList(converters);
} }
private List<HttpMessageConverter<?>> getDefaultConverters() { private List<HttpMessageConverter<?>> getDefaultConverters() {
List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>(); List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>();
if (ClassUtils if (ClassUtils.isPresent("org.springframework.web.servlet.config.annotation."
.isPresent( + "WebMvcConfigurationSupport", null)) {
"org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport",
null)) {
converters.addAll(new WebMvcConfigurationSupport() { converters.addAll(new WebMvcConfigurationSupport() {
public List<HttpMessageConverter<?>> defaultMessageConverters() { public List<HttpMessageConverter<?>> defaultMessageConverters() {
return super.getMessageConverters(); return super.getMessageConverters();
......
...@@ -16,7 +16,6 @@ ...@@ -16,7 +16,6 @@
package org.springframework.boot.autoconfigure.web; package org.springframework.boot.autoconfigure.web;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
...@@ -53,9 +52,7 @@ public class HttpMessageConvertersAutoConfiguration { ...@@ -53,9 +52,7 @@ public class HttpMessageConvertersAutoConfiguration {
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public HttpMessageConverters messageConverters() { public HttpMessageConverters messageConverters() {
List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>( return new HttpMessageConverters(this.converters);
this.converters);
return new HttpMessageConverters(converters);
} }
@Configuration @Configuration
......
...@@ -34,6 +34,7 @@ import org.springframework.http.converter.xml.SourceHttpMessageConverter; ...@@ -34,6 +34,7 @@ import org.springframework.http.converter.xml.SourceHttpMessageConverter;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
...@@ -65,27 +66,35 @@ public class HttpMessageConvertersTests { ...@@ -65,27 +66,35 @@ public class HttpMessageConvertersTests {
} }
@Test @Test
public void overrideExistingConverter() { public void addBeforeExistingConverter() {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); MappingJackson2HttpMessageConverter converter1 = new MappingJackson2HttpMessageConverter();
HttpMessageConverters converters = new HttpMessageConverters(converter); MappingJackson2HttpMessageConverter converter2 = new MappingJackson2HttpMessageConverter();
assertTrue(converters.getConverters().contains(converter)); HttpMessageConverters converters = new HttpMessageConverters(converter1,
int count = 0; converter2);
for (HttpMessageConverter<?> httpMessageConverter : converters) { assertTrue(converters.getConverters().contains(converter1));
if (httpMessageConverter instanceof MappingJackson2HttpMessageConverter) { assertTrue(converters.getConverters().contains(converter2));
count++; List<MappingJackson2HttpMessageConverter> httpConverters = new ArrayList<MappingJackson2HttpMessageConverter>();
for (HttpMessageConverter<?> candidate : converters) {
if (candidate instanceof MappingJackson2HttpMessageConverter) {
httpConverters.add((MappingJackson2HttpMessageConverter) candidate);
} }
} }
// The existing converter is still there, but with a lower priority // The existing converter is still there, but with a lower priority
assertEquals(2, count); assertEquals(3, httpConverters.size());
assertEquals(0, converters.getConverters().indexOf(converter)); assertEquals(0, httpConverters.indexOf(converter1));
assertEquals(1, httpConverters.indexOf(converter2));
assertNotEquals(0, converters.getConverters().indexOf(converter1));
} }
@Test @Test
public void addNewConverter() { public void addNewConverters() {
HttpMessageConverter<?> converter = mock(HttpMessageConverter.class); HttpMessageConverter<?> converter1 = mock(HttpMessageConverter.class);
HttpMessageConverters converters = new HttpMessageConverters(converter); HttpMessageConverter<?> converter2 = mock(HttpMessageConverter.class);
assertTrue(converters.getConverters().contains(converter)); HttpMessageConverters converters = new HttpMessageConverters(converter1,
assertEquals(converter, converters.getConverters().get(0)); converter2);
assertTrue(converters.getConverters().contains(converter1));
assertEquals(converter1, converters.getConverters().get(0));
assertEquals(converter2, converters.getConverters().get(1));
} }
} }
...@@ -20,4 +20,5 @@ class Example implements CommandLineRunner { ...@@ -20,4 +20,5 @@ class Example implements CommandLineRunner {
def world = new RESTClient("http://localhost:" + port).get(path:"/").data.text def world = new RESTClient("http://localhost:" + port).get(path:"/").data.text
print "Hello " + world print "Hello " + world
} }
} }
...@@ -2004,7 +2004,6 @@ the actual port that was allocated for the duration of the tests. ...@@ -2004,7 +2004,6 @@ the actual port that was allocated for the duration of the tests.
[[boot-features-testing-spring-boot-applications-with-spock]] [[boot-features-testing-spring-boot-applications-with-spock]]
==== Using Spock to test Spring Boot applications ==== Using Spock to test Spring Boot applications
If you wish to use Spock to test a Spring Boot application you should add a dependency If you wish to use Spock to test a Spring Boot application you should add a dependency
on Spock's `spock-spring` module to your application's build. `spock-spring` integrates on Spock's `spock-spring` module to your application's build. `spock-spring` integrates
Spring's test framework into Spock. Spring's test framework into Spock.
......
...@@ -72,65 +72,67 @@ public class PropertySourcesPropertyValues implements PropertyValues { ...@@ -72,65 +72,67 @@ public class PropertySourcesPropertyValues implements PropertyValues {
this.propertySources = propertySources; this.propertySources = propertySources;
PropertySourcesPropertyResolver resolver = new PropertySourcesPropertyResolver( PropertySourcesPropertyResolver resolver = new PropertySourcesPropertyResolver(
propertySources); propertySources);
String[] includes = patterns == null ? new String[0] : patterns String[] includes = toArray(patterns);
.toArray(new String[0]); String[] exacts = toArray(names);
String[] exacts = names == null ? new String[0] : names.toArray(new String[0]);
for (PropertySource<?> source : propertySources) { for (PropertySource<?> source : propertySources) {
processPropertySource(source, resolver, includes, exacts); processPropertySource(source, resolver, includes, exacts);
} }
} }
private String[] toArray(Collection<String> strings) {
if (strings == null) {
return new String[0];
}
return strings.toArray(new String[strings.size()]);
}
private void processPropertySource(PropertySource<?> source, private void processPropertySource(PropertySource<?> source,
PropertySourcesPropertyResolver resolver, String[] includes, String[] exacts) { PropertySourcesPropertyResolver resolver, String[] includes, String[] exacts) {
if (source instanceof EnumerablePropertySource) { if (source instanceof EnumerablePropertySource) {
EnumerablePropertySource<?> enumerable = (EnumerablePropertySource<?>) source; processEnumerablePropertySource((EnumerablePropertySource<?>) source,
if (enumerable.getPropertyNames().length > 0) { resolver, includes, exacts);
for (String propertyName : enumerable.getPropertyNames()) {
if (this.NON_ENUMERABLE_ENUMERABLES.contains(source.getName())
&& !PatternMatchUtils.simpleMatch(includes, propertyName)) {
continue;
}
Object value = source.getProperty(propertyName);
try {
value = resolver.getProperty(propertyName);
}
catch (RuntimeException ex) {
// Probably could not resolve placeholders, ignore it here
}
if (!this.propertyValues.containsKey(propertyName)) {
this.propertyValues.put(propertyName, new PropertyValue(
propertyName, value));
}
}
}
} }
else if (source instanceof CompositePropertySource) { else if (source instanceof CompositePropertySource) {
CompositePropertySource composite = (CompositePropertySource) source; processCompositePropertySource((CompositePropertySource) source, resolver,
for (PropertySource<?> nested : extractSources(composite)) { includes, exacts);
processPropertySource(nested, resolver, includes, exacts);
}
} }
else { else {
// We can only do exact matches for non-enumerable property names, but // We can only do exact matches for non-enumerable property names, but
// that's better than nothing... // that's better than nothing...
for (String propertyName : exacts) { processDefaultPropertySource(source, resolver, includes, exacts);
Object value; }
value = resolver.getProperty(propertyName); }
if (value != null && !this.propertyValues.containsKey(propertyName)) {
this.propertyValues.put(propertyName, new PropertyValue(propertyName, private void processEnumerablePropertySource(EnumerablePropertySource<?> source,
value)); PropertySourcesPropertyResolver resolver, String[] includes, String[] exacts) {
if (source.getPropertyNames().length > 0) {
for (String propertyName : source.getPropertyNames()) {
if (this.NON_ENUMERABLE_ENUMERABLES.contains(source.getName())
&& !PatternMatchUtils.simpleMatch(includes, propertyName)) {
continue; continue;
} }
value = source.getProperty(propertyName.toUpperCase()); Object value = source.getProperty(propertyName);
if (value != null && !this.propertyValues.containsKey(propertyName)) { try {
value = resolver.getProperty(propertyName);
}
catch (RuntimeException ex) {
// Probably could not resolve placeholders, ignore it here
}
if (!this.propertyValues.containsKey(propertyName)) {
this.propertyValues.put(propertyName, new PropertyValue(propertyName, this.propertyValues.put(propertyName, new PropertyValue(propertyName,
value)); value));
continue;
} }
} }
} }
} }
private void processCompositePropertySource(CompositePropertySource source,
PropertySourcesPropertyResolver resolver, String[] includes, String[] exacts) {
for (PropertySource<?> nested : extractSources(source)) {
processPropertySource(nested, resolver, includes, exacts);
}
}
private Collection<PropertySource<?>> extractSources(CompositePropertySource composite) { private Collection<PropertySource<?>> extractSources(CompositePropertySource composite) {
Field field = ReflectionUtils.findField(CompositePropertySource.class, Field field = ReflectionUtils.findField(CompositePropertySource.class,
"propertySources"); "propertySources");
...@@ -141,9 +143,24 @@ public class PropertySourcesPropertyValues implements PropertyValues { ...@@ -141,9 +143,24 @@ public class PropertySourcesPropertyValues implements PropertyValues {
.get(composite); .get(composite);
return collection; return collection;
} }
catch (Exception e) { catch (Exception ex) {
throw new IllegalStateException( throw new IllegalStateException(
"Cannot extract property sources from composite", e); "Cannot extract property sources from composite", ex);
}
}
private void processDefaultPropertySource(PropertySource<?> source,
PropertySourcesPropertyResolver resolver, String[] includes, String[] exacts) {
for (String propertyName : exacts) {
Object value = resolver.getProperty(propertyName);
if (value == null) {
value = source.getProperty(propertyName.toUpperCase());
}
if (value != null && !this.propertyValues.containsKey(propertyName)) {
this.propertyValues.put(propertyName, new PropertyValue(propertyName,
value));
continue;
}
} }
} }
......
...@@ -76,17 +76,21 @@ public class JettyEmbeddedServletContainer implements EmbeddedServletContainer { ...@@ -76,17 +76,21 @@ public class JettyEmbeddedServletContainer implements EmbeddedServletContainer {
this.server.start(); this.server.start();
} }
catch (Exception ex) { catch (Exception ex) {
try { // Ensure process isn't left running
// Ensure process isn't left running stopSilently();
this.server.stop();
}
catch (Exception e) {
}
throw new EmbeddedServletContainerException( throw new EmbeddedServletContainerException(
"Unable to start embedded Jetty servlet container", ex); "Unable to start embedded Jetty servlet container", ex);
} }
} }
private void stopSilently() {
try {
this.server.stop();
}
catch (Exception ex) {
}
}
@Override @Override
public void start() throws EmbeddedServletContainerException { public void start() throws EmbeddedServletContainerException {
this.server.setConnectors(this.connectors); this.server.setConnectors(this.connectors);
......
...@@ -95,7 +95,6 @@ public class TomcatEmbeddedServletContainer implements EmbeddedServletContainer ...@@ -95,7 +95,6 @@ public class TomcatEmbeddedServletContainer implements EmbeddedServletContainer
this.tomcat.stop(); this.tomcat.stop();
throw new IllegalStateException("Tomcat connector in failed state"); throw new IllegalStateException("Tomcat connector in failed state");
} }
} }
catch (Exception ex) { catch (Exception ex) {
throw new EmbeddedServletContainerException( throw new EmbeddedServletContainerException(
...@@ -154,15 +153,19 @@ public class TomcatEmbeddedServletContainer implements EmbeddedServletContainer ...@@ -154,15 +153,19 @@ public class TomcatEmbeddedServletContainer implements EmbeddedServletContainer
} }
// Ensure process isn't left running if it actually failed to start // Ensure process isn't left running if it actually failed to start
if (LifecycleState.FAILED.equals(this.tomcat.getConnector().getState())) { if (LifecycleState.FAILED.equals(this.tomcat.getConnector().getState())) {
try { stopSilently();
this.tomcat.stop();
}
catch (LifecycleException e) {
}
throw new IllegalStateException("Tomcat connector in failed state"); throw new IllegalStateException("Tomcat connector in failed state");
} }
} }
private void stopSilently() {
try {
this.tomcat.stop();
}
catch (LifecycleException ex) {
}
}
private void addPreviouslyRemovedConnectors() { private void addPreviouslyRemovedConnectors() {
Service[] services = this.tomcat.getServer().findServices(); Service[] services = this.tomcat.getServer().findServices();
for (Service service : services) { for (Service service : services) {
......
...@@ -77,14 +77,13 @@ class ErrorPageFilter extends AbstractConfigurableEmbeddedServletContainer imple ...@@ -77,14 +77,13 @@ class ErrorPageFilter extends AbstractConfigurableEmbeddedServletContainer imple
private final Map<Class<?>, String> exceptions = new HashMap<Class<?>, String>(); private final Map<Class<?>, String> exceptions = new HashMap<Class<?>, String>();
private final Map<Class<?>, Class<?>> subtypes = new HashMap<Class<?>, Class<?>>(); private final Map<Class<?>, Class<?>> subtypes = new HashMap<Class<?>, Class<?>>();
private final OncePerRequestFilter delegate = new OncePerRequestFilter( private final OncePerRequestFilter delegate = new OncePerRequestFilter() {
) {
@Override @Override
protected void doFilterInternal(HttpServletRequest request, protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain chain) HttpServletResponse response, FilterChain chain) throws ServletException,
throws ServletException, IOException { IOException {
ErrorPageFilter.this.doFilter(request, response, chain); ErrorPageFilter.this.doFilter(request, response, chain);
} }
...@@ -92,13 +91,13 @@ class ErrorPageFilter extends AbstractConfigurableEmbeddedServletContainer imple ...@@ -92,13 +91,13 @@ class ErrorPageFilter extends AbstractConfigurableEmbeddedServletContainer imple
@Override @Override
public void init(FilterConfig filterConfig) throws ServletException { public void init(FilterConfig filterConfig) throws ServletException {
delegate.init(filterConfig); this.delegate.init(filterConfig);
} }
@Override @Override
public void doFilter(ServletRequest request, ServletResponse response, public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException { FilterChain chain) throws IOException, ServletException {
delegate.doFilter(request, response, chain); this.delegate.doFilter(request, response, chain);
} }
private void doFilter(HttpServletRequest request, HttpServletResponse response, private void doFilter(HttpServletRequest request, HttpServletResponse response,
......
/* /*
* Copyright 2012-2013 the original author or authors. * Copyright 2012-2014 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
......
...@@ -16,11 +16,6 @@ ...@@ -16,11 +16,6 @@
package org.springframework.boot.context.web; package org.springframework.boot.context.web;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import java.io.IOException; import java.io.IOException;
import javax.servlet.RequestDispatcher; import javax.servlet.RequestDispatcher;
...@@ -38,6 +33,11 @@ import org.springframework.mock.web.MockFilterConfig; ...@@ -38,6 +33,11 @@ import org.springframework.mock.web.MockFilterConfig;
import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockHttpServletResponse;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
/** /**
* Tests for {@link ErrorPageFilter}. * Tests for {@link ErrorPageFilter}.
* *
...@@ -110,7 +110,7 @@ public class ErrorPageFilterTests { ...@@ -110,7 +110,7 @@ public class ErrorPageFilterTests {
super.doFilter(request, response); super.doFilter(request, response);
} }
}; };
filter.init(new MockFilterConfig("FILTER")); this.filter.init(new MockFilterConfig("FILTER"));
this.filter.doFilter(this.request, this.response, this.chain); this.filter.doFilter(this.request, this.response, this.chain);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment