Commit ae249f75 authored by Phillip Webb's avatar Phillip Webb

Merge branch '1.3.x'

parents 725ae2c8 a1284bce
......@@ -30,11 +30,14 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.boot.actuate.endpoint.Endpoint;
import org.springframework.boot.actuate.endpoint.mvc.ManagementServletContext;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
......@@ -55,6 +58,7 @@ import org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfigurat
import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext;
import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.EmbeddedWebApplicationContext;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
......@@ -104,6 +108,8 @@ public class EndpointWebMvcAutoConfiguration
private static final Log logger = LogFactory
.getLog(EndpointWebMvcAutoConfiguration.class);
private static final ConfigurableListableBeanFactory BeanDefinitionRegistry = null;
private ApplicationContext applicationContext;
private BeanFactory beanFactory;
......@@ -164,7 +170,7 @@ public class EndpointWebMvcAutoConfiguration
}
private void createChildManagementContext() {
final AnnotationConfigEmbeddedWebApplicationContext childContext = new AnnotationConfigEmbeddedWebApplicationContext();
AnnotationConfigEmbeddedWebApplicationContext childContext = new AnnotationConfigEmbeddedWebApplicationContext();
childContext.setParent(this.applicationContext);
childContext.setNamespace("management");
childContext.setId(this.applicationContext.getId() + ":management");
......@@ -172,12 +178,30 @@ public class EndpointWebMvcAutoConfiguration
PropertyPlaceholderAutoConfiguration.class,
EmbeddedServletContainerAutoConfiguration.class,
DispatcherServletAutoConfiguration.class);
registerEmbeddedServletContainerFactory(childContext);
CloseEventPropagationListener.addIfPossible(this.applicationContext,
childContext);
childContext.refresh();
managementContextResolver().setApplicationContext(childContext);
}
private void registerEmbeddedServletContainerFactory(
AnnotationConfigEmbeddedWebApplicationContext childContext) {
try {
EmbeddedServletContainerFactory servletContainerFactory = this.applicationContext
.getBean(EmbeddedServletContainerFactory.class);
ConfigurableListableBeanFactory beanFactory = childContext.getBeanFactory();
if (beanFactory instanceof BeanDefinitionRegistry) {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
registry.registerBeanDefinition("embeddedServletContainerFactory",
new RootBeanDefinition(servletContainerFactory.getClass()));
}
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore and assume auto-configuration
}
}
/**
* Add an alias for 'local.management.port' that actually resolves using
* 'local.server.port'.
......
/*
* Copyright 2012-2015 the original author or authors.
* 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.
......@@ -41,6 +41,7 @@ import org.springframework.messaging.MessageChannel;
public class MetricsChannelAutoConfiguration {
@Bean
@ExportMetricWriter
@ConditionalOnMissingBean
public MessageChannelMetricWriter messageChannelMetricWriter(
@Qualifier("metricsChannel") MessageChannel channel) {
......
......@@ -58,7 +58,9 @@ import org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebAppl
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerException;
import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.EmbeddedServletContainerInitializedEvent;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import org.springframework.boot.context.web.ServerPortInfoApplicationContextInitializer;
import org.springframework.boot.test.util.EnvironmentTestUtils;
import org.springframework.boot.testutil.Matched;
......@@ -176,6 +178,35 @@ public class EndpointWebMvcAutoConfigurationTests {
assertAllClosed();
}
@Test
public void onDifferentPortWithSpecificContainer() throws Exception {
this.applicationContext.register(SpecificContainerConfig.class, RootConfig.class,
DifferentPortConfig.class, EndpointConfig.class, BaseConfiguration.class,
EndpointWebMvcAutoConfiguration.class, ErrorMvcAutoConfiguration.class);
this.applicationContext.refresh();
assertContent("/controller", ports.get().server, "controlleroutput");
assertContent("/endpoint", ports.get().server, null);
assertContent("/controller", ports.get().management, null);
assertContent("/endpoint", ports.get().management, "endpointoutput");
assertContent("/error", ports.get().management, startsWith("{"));
ApplicationContext managementContext = this.applicationContext
.getBean(ManagementContextResolver.class).getApplicationContext();
List<?> interceptors = (List<?>) ReflectionTestUtils.getField(
managementContext.getBean(EndpointHandlerMapping.class), "interceptors");
assertThat(interceptors).hasSize(1);
EmbeddedServletContainerFactory parentContainerFactory = this.applicationContext
.getBean(EmbeddedServletContainerFactory.class);
EmbeddedServletContainerFactory managementContainerFactory = managementContext
.getBean(EmbeddedServletContainerFactory.class);
assertThat(parentContainerFactory)
.isInstanceOf(SpecificEmbeddedServletContainerFactory.class);
assertThat(managementContainerFactory)
.isInstanceOf(SpecificEmbeddedServletContainerFactory.class);
assertThat(managementContainerFactory).isNotSameAs(parentContainerFactory);
this.applicationContext.close();
assertAllClosed();
}
@Test
public void onDifferentPortAndContext() throws Exception {
this.applicationContext.register(RootConfig.class, EndpointConfig.class,
......@@ -609,6 +640,16 @@ public class EndpointWebMvcAutoConfigurationTests {
}
@Configuration
public static class SpecificContainerConfig {
@Bean
public SpecificEmbeddedServletContainerFactory embeddedServletContainerFactory() {
return new SpecificEmbeddedServletContainerFactory();
}
}
@Configuration
@Import(ServerPortConfig.class)
public static class DifferentPortConfig {
......@@ -636,6 +677,7 @@ public class EndpointWebMvcAutoConfigurationTests {
}
protected static class TestInterceptor extends HandlerInterceptorAdapter {
private int count = 0;
@Override
......@@ -648,6 +690,7 @@ public class EndpointWebMvcAutoConfigurationTests {
public int getCount() {
return this.count;
}
}
}
......@@ -728,4 +771,9 @@ public class EndpointWebMvcAutoConfigurationTests {
}
private static class SpecificEmbeddedServletContainerFactory
extends TomcatEmbeddedServletContainerFactory {
}
}
......@@ -75,6 +75,7 @@ public class MetricExportAutoConfigurationTests {
PropertyPlaceholderAutoConfiguration.class);
MetricExporters exporter = this.context.getBean(MetricExporters.class);
assertThat(exporter).isNotNull();
assertThat(exporter.getExporters()).containsKey("messageChannelMetricWriter");
}
@Test
......
......@@ -961,14 +961,30 @@ public class ServerProperties
void customizeUndertow(ServerProperties serverProperties,
UndertowEmbeddedServletContainerFactory factory) {
factory.setBufferSize(this.bufferSize);
factory.setBuffersPerRegion(this.buffersPerRegion);
factory.setIoThreads(this.ioThreads);
factory.setWorkerThreads(this.workerThreads);
factory.setDirectBuffers(this.directBuffers);
factory.setAccessLogDirectory(this.accesslog.dir);
factory.setAccessLogPattern(this.accesslog.pattern);
factory.setAccessLogEnabled(this.accesslog.enabled);
if (this.bufferSize != null) {
factory.setBufferSize(this.bufferSize);
}
if (this.buffersPerRegion != null) {
factory.setBuffersPerRegion(this.buffersPerRegion);
}
if (this.ioThreads != null) {
factory.setIoThreads(this.ioThreads);
}
if (this.workerThreads != null) {
factory.setWorkerThreads(this.workerThreads);
}
if (this.directBuffers != null) {
factory.setDirectBuffers(this.directBuffers);
}
if (this.accesslog.dir != null) {
factory.setAccessLogDirectory(this.accesslog.dir);
}
if (this.accesslog.pattern != null) {
factory.setAccessLogPattern(this.accesslog.pattern);
}
if (this.accesslog.enabled != null) {
factory.setAccessLogEnabled(this.accesslog.enabled);
}
factory.setUseForwardHeaders(serverProperties.getOrDeduceUseForwardHeaders());
}
......@@ -977,7 +993,7 @@ public class ServerProperties
/**
* Enable access log.
*/
private boolean enabled = false;
private Boolean enabled;
/**
* Format pattern for access logs.
......@@ -989,11 +1005,11 @@ public class ServerProperties
*/
private File dir = new File("logs");
public boolean isEnabled() {
public Boolean getEnabled() {
return this.enabled;
}
public void setEnabled(boolean enabled) {
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
......
......@@ -48,6 +48,7 @@ import org.springframework.mock.env.MockEnvironment;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
......@@ -418,6 +419,14 @@ public class ServerPropertiesTests {
verify(container).setSessionStoreDir(new File("myfolder"));
}
@Test
public void skipNullElementsForUndertow() throws Exception {
UndertowEmbeddedServletContainerFactory container = mock(
UndertowEmbeddedServletContainerFactory.class);
this.properties.customize(container);
verify(container, never()).setAccessLogEnabled(anyBoolean());
}
private void bindProperties(Map<String, String> map) {
new RelaxedDataBinder(this.properties, "server")
.bind(new MutablePropertyValues(map));
......
/*
* 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.context.embedded.jetty;
import java.io.IOException;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.ErrorHandler;
/**
* Variation of Jetty's {@link ErrorHandler} that supports all {@link HttpMethod
* HttpMethods} rather than just {@code GET}, {@code POST} and {@code HEAD}. Jetty
* <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=446039">intentionally only
* supports a limited set of HTTP methods</a> for error pages, however, Spring Boot
* prefers Tomcat, Jetty and Undertow to all behave in the same way.
*
* @author Phillip Webb
*/
class JettyEmbeddedErrorHandler extends ErrorHandler {
private final ErrorHandler delegate;
JettyEmbeddedErrorHandler(ErrorHandler delegate) {
this.delegate = delegate;
}
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request,
HttpServletResponse response) throws IOException {
String method = request.getMethod();
if (!HttpMethod.GET.is(method) && !HttpMethod.POST.is(method)
&& !HttpMethod.HEAD.is(method)) {
request = new ErrorHttpServletRequest(request);
}
this.delegate.handle(target, baseRequest, request, response);
}
private static class ErrorHttpServletRequest extends HttpServletRequestWrapper {
private boolean simulateGetMethod = true;
ErrorHttpServletRequest(HttpServletRequest request) {
super(request);
}
@Override
public String getMethod() {
return (this.simulateGetMethod ? HttpMethod.GET.toString()
: super.getMethod());
}
@Override
public ServletContext getServletContext() {
this.simulateGetMethod = false;
return super.getServletContext();
}
}
}
......@@ -410,11 +410,14 @@ public class JettyEmbeddedServletContainerFactory
*/
private Configuration getErrorPageConfiguration() {
return new AbstractConfiguration() {
@Override
public void configure(WebAppContext context) throws Exception {
ErrorHandler errorHandler = context.getErrorHandler();
context.setErrorHandler(new JettyEmbeddedErrorHandler(errorHandler));
addJettyErrorPages(errorHandler, getErrorPages());
}
};
}
......@@ -424,6 +427,7 @@ public class JettyEmbeddedServletContainerFactory
*/
private Configuration getMimeTypeConfiguration() {
return new AbstractConfiguration() {
@Override
public void configure(WebAppContext context) throws Exception {
MimeTypes mimeTypes = context.getMimeTypes();
......@@ -432,6 +436,7 @@ public class JettyEmbeddedServletContainerFactory
mapping.getMimeType());
}
}
};
}
......
......@@ -334,6 +334,19 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests {
assertThat(getResponse(getLocalUrl("/bang"))).isEqualTo("Hello World");
}
@Test
public void errorPageFromPutRequest() throws Exception {
AbstractEmbeddedServletContainerFactory factory = getFactory();
factory.addErrorPages(new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/hello"));
this.container = factory.getEmbeddedServletContainer(exampleServletRegistration(),
errorServletRegistration());
this.container.start();
assertThat(getResponse(getLocalUrl("/hello"), HttpMethod.PUT),
equalTo("Hello World"));
assertThat(getResponse(getLocalUrl("/bang"), HttpMethod.PUT),
equalTo("Hello World"));
}
@Test
public void basicSslFromClassPath() throws Exception {
testBasicSslWithKeyStore("classpath:test.jks");
......@@ -869,7 +882,12 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests {
protected String getResponse(String url, String... headers)
throws IOException, URISyntaxException {
ClientHttpResponse response = getClientResponse(url, headers);
return getResponse(url, HttpMethod.GET, headers);
}
protected String getResponse(String url, HttpMethod method, String... headers)
throws IOException, URISyntaxException {
ClientHttpResponse response = getClientResponse(url, method, headers);
try {
return StreamUtils.copyToString(response.getBody(), Charset.forName("UTF-8"));
}
......@@ -881,7 +899,14 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests {
protected String getResponse(String url,
HttpComponentsClientHttpRequestFactory requestFactory, String... headers)
throws IOException, URISyntaxException {
ClientHttpResponse response = getClientResponse(url, requestFactory, headers);
return getResponse(url, HttpMethod.GET, requestFactory, headers);
}
protected String getResponse(String url, HttpMethod method,
HttpComponentsClientHttpRequestFactory requestFactory, String... headers)
throws IOException, URISyntaxException {
ClientHttpResponse response = getClientResponse(url, method, requestFactory,
headers);
try {
return StreamUtils.copyToString(response.getBody(), Charset.forName("UTF-8"));
}
......@@ -892,21 +917,27 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests {
protected ClientHttpResponse getClientResponse(String url, String... headers)
throws IOException, URISyntaxException {
return getClientResponse(url, new HttpComponentsClientHttpRequestFactory() {
return getClientResponse(url, HttpMethod.GET, headers);
}
@Override
protected HttpContext createHttpContext(HttpMethod httpMethod, URI uri) {
return AbstractEmbeddedServletContainerFactoryTests.this.httpClientContext;
}
protected ClientHttpResponse getClientResponse(String url, HttpMethod method,
String... headers) throws IOException, URISyntaxException {
return getClientResponse(url, method,
new HttpComponentsClientHttpRequestFactory() {
@Override
protected HttpContext createHttpContext(HttpMethod httpMethod,
URI uri) {
return AbstractEmbeddedServletContainerFactoryTests.this.httpClientContext;
}
}, headers);
}, headers);
}
protected ClientHttpResponse getClientResponse(String url,
protected ClientHttpResponse getClientResponse(String url, HttpMethod method,
HttpComponentsClientHttpRequestFactory requestFactory, String... headers)
throws IOException, URISyntaxException {
ClientHttpRequest request = requestFactory.createRequest(new URI(url),
HttpMethod.GET);
ClientHttpRequest request = requestFactory.createRequest(new URI(url), method);
request.getHeaders().add("Cookie", "JSESSIONID=" + "123");
for (String header : headers) {
String[] parts = header.split(":");
......
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