[bs-115] Add EmbeddedServletContainerCustomizer as a callback

* All instances are called before the container is started in
 a bean post processor
* Users still have to be careful because the customizer is
called very early in the ApplicationContext lifecycle (e.g.
might have to do a lookup for some dependencies instead of
@Autowired)

[Fixes #49671463] User-hook for customizing embedded servlet container
This commit is contained in:
Dave Syer 2013-05-10 11:51:21 +01:00
parent 56865ab260
commit ef5c8aa304
16 changed files with 494 additions and 93 deletions

View File

@ -25,6 +25,7 @@ import org.springframework.bootstrap.actuate.error.ErrorEndpoint;
import org.springframework.bootstrap.actuate.properties.ManagementServerProperties;
import org.springframework.bootstrap.context.annotation.ConditionalOnBean;
import org.springframework.bootstrap.context.embedded.AbstractEmbeddedServletContainerFactory;
import org.springframework.bootstrap.context.embedded.ConfigurableEmbeddedServletContainerFactory;
import org.springframework.bootstrap.context.embedded.EmbeddedServletContainerFactory;
import org.springframework.bootstrap.context.embedded.ErrorPage;
import org.springframework.bootstrap.context.embedded.jetty.JettyEmbeddedServletContainerFactory;
@ -100,7 +101,7 @@ public class ManagementServerConfiguration implements BeanPostProcessor {
if (bean instanceof AbstractEmbeddedServletContainerFactory
&& !this.initialized) {
AbstractEmbeddedServletContainerFactory factory = (AbstractEmbeddedServletContainerFactory) bean;
ConfigurableEmbeddedServletContainerFactory factory = (ConfigurableEmbeddedServletContainerFactory) bean;
factory.setPort(this.configuration.getPort());
factory.setAddress(this.configuration.getAddress());
factory.setContextPath(this.configuration.getContextPath());

View File

@ -20,17 +20,15 @@ import javax.servlet.Servlet;
import org.apache.catalina.valves.AccessLogValve;
import org.apache.catalina.valves.RemoteIpValve;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.bootstrap.actuate.error.ErrorEndpoint;
import org.springframework.bootstrap.actuate.properties.ServerProperties;
import org.springframework.bootstrap.actuate.properties.ServerProperties.Tomcat;
import org.springframework.bootstrap.context.annotation.ConditionalOnClass;
import org.springframework.bootstrap.context.embedded.AbstractEmbeddedServletContainerFactory;
import org.springframework.bootstrap.context.embedded.EmbeddedServletContainerFactory;
import org.springframework.bootstrap.context.embedded.ConfigurableEmbeddedServletContainerFactory;
import org.springframework.bootstrap.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.bootstrap.context.embedded.ErrorPage;
import org.springframework.bootstrap.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import org.springframework.context.annotation.Bean;
@ -50,66 +48,34 @@ import org.springframework.util.StringUtils;
@ConditionalOnClass({ Servlet.class })
@Order(Integer.MIN_VALUE)
@Import(InfoConfiguration.class)
public class ServerConfiguration implements BeanPostProcessor, BeanFactoryAware {
public class ServerConfiguration implements EmbeddedServletContainerCustomizer {
@Autowired
private BeanFactory beanFactory;
// Don't do this! We don't get a callback for our own dependencies (lifecycle).
// @Autowired
// private AbstractEmbeddedServletContainerFactory factory;
private boolean initialized = false;
@Value("${endpoints.error.path:/error}")
private String errorPath = "/error";
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
@Bean
public ErrorEndpoint errorEndpoint() {
return new ErrorEndpoint();
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
public void customize(ConfigurableEmbeddedServletContainerFactory factory) {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
// Need to do a look up here to make it lazy
ServerProperties server = this.beanFactory.getBean(ServerProperties.class);
if (bean instanceof EmbeddedServletContainerFactory) {
if (bean instanceof AbstractEmbeddedServletContainerFactory
&& !this.initialized) {
// Cannot use @Autowired because the injection happens too early
ServerProperties server = this.beanFactory
.getBean(ServerProperties.class);
AbstractEmbeddedServletContainerFactory factory = (AbstractEmbeddedServletContainerFactory) bean;
factory.setPort(server.getPort());
factory.setAddress(server.getAddress());
factory.setContextPath(server.getContextPath());
if (factory instanceof TomcatEmbeddedServletContainerFactory) {
configureTomcat((TomcatEmbeddedServletContainerFactory) factory,
server);
}
factory.addErrorPages(new ErrorPage(this.errorPath));
this.initialized = true;
}
factory.setPort(server.getPort());
factory.setAddress(server.getAddress());
factory.setContextPath(server.getContextPath());
if (factory instanceof TomcatEmbeddedServletContainerFactory) {
configureTomcat((TomcatEmbeddedServletContainerFactory) factory, server);
}
return bean;
factory.addErrorPages(new ErrorPage(this.errorPath));
}

View File

@ -0,0 +1,89 @@
/*
* Copyright 2012-2013 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.bootstrap.autoconfigure.web;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.servlet.Servlet;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.bootstrap.context.annotation.ConditionalOnClass;
import org.springframework.bootstrap.context.annotation.EnableAutoConfiguration;
import org.springframework.bootstrap.context.embedded.ConfigurableEmbeddedServletContainerFactory;
import org.springframework.bootstrap.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.bootstrap.context.embedded.jetty.JettyEmbeddedServletContainerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
/**
* {@link EnableAutoConfiguration Auto-configuration} for
* {@link JettyEmbeddedServletContainerFactory}.
*
* @author Dave Syer
*/
@Configuration
@ConditionalOnClass({ Servlet.class })
public class EmbeddedContainerCustomizerConfiguration {
@Autowired(required = false)
private Set<EmbeddedServletContainerCustomizer> customizers = new HashSet<EmbeddedServletContainerCustomizer>();
@Bean
public BeanPostProcessor embeddedContainerCustomizerBeanPostProcessor() {
return new EmbeddedContainerCustomizerBeanPostProcessor(this.customizers);
}
private static final class EmbeddedContainerCustomizerBeanPostProcessor implements
BeanPostProcessor {
private List<EmbeddedServletContainerCustomizer> customizers;
public EmbeddedContainerCustomizerBeanPostProcessor(
Set<EmbeddedServletContainerCustomizer> customizers) {
final List<EmbeddedServletContainerCustomizer> list = new ArrayList<EmbeddedServletContainerCustomizer>(
customizers);
Collections.sort(list, new AnnotationAwareOrderComparator());
this.customizers = list;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
if (bean instanceof ConfigurableEmbeddedServletContainerFactory) {
ConfigurableEmbeddedServletContainerFactory factory = (ConfigurableEmbeddedServletContainerFactory) bean;
for (EmbeddedServletContainerCustomizer customizer : this.customizers) {
customizer.customize(factory);
}
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
}
}

View File

@ -36,7 +36,7 @@ import org.springframework.util.Assert;
* @since 4.0
*/
public abstract class AbstractEmbeddedServletContainerFactory implements
EmbeddedServletContainerFactory {
ConfigurableEmbeddedServletContainerFactory {
private final Log logger = LogFactory.getLog(getClass());
@ -170,6 +170,7 @@ public abstract class AbstractEmbeddedServletContainerFactory implements
* @see #setInitializers
* @see #getInitializers
*/
@Override
public void addInitializers(ServletContextInitializer... initializers) {
Assert.notNull(initializers, "Initializers must not be null");
this.initializers.addAll(Arrays.asList(initializers));
@ -181,6 +182,7 @@ public abstract class AbstractEmbeddedServletContainerFactory implements
* parameters.
* @return the initializers
*/
@Override
public List<ServletContextInitializer> getInitializers() {
return this.initializers;
}
@ -190,6 +192,7 @@ public abstract class AbstractEmbeddedServletContainerFactory implements
* files.
* @param documentRoot the document root or {@code null} if not required
*/
@Override
public void setDocumentRoot(File documentRoot) {
this.documentRoot = documentRoot;
}
@ -198,6 +201,7 @@ public abstract class AbstractEmbeddedServletContainerFactory implements
* Returns the document root which will be used by the web context to serve static
* files.
*/
@Override
public File getDocumentRoot() {
return this.documentRoot;
}
@ -206,6 +210,7 @@ public abstract class AbstractEmbeddedServletContainerFactory implements
* Sets the error pages that will be used when handling exceptions.
* @param errorPages the error pages
*/
@Override
public void setErrorPages(Set<ErrorPage> errorPages) {
Assert.notNull(errorPages, "ErrorPages must not be null");
this.errorPages = new LinkedHashSet<ErrorPage>(errorPages);
@ -215,6 +220,7 @@ public abstract class AbstractEmbeddedServletContainerFactory implements
* Adds error pages that will be used when handling exceptions.
* @param errorPages the error pages
*/
@Override
public void addErrorPages(ErrorPage... errorPages) {
Assert.notNull(this.initializers, "ErrorPages must not be null");
this.errorPages.addAll(Arrays.asList(errorPages));
@ -224,6 +230,7 @@ public abstract class AbstractEmbeddedServletContainerFactory implements
* Returns a mutable set of {@link ErrorPage}s that will be used when handling
* exceptions.
*/
@Override
public Set<ErrorPage> getErrorPages() {
return this.errorPages;
}
@ -233,6 +240,7 @@ public abstract class AbstractEmbeddedServletContainerFactory implements
* files from the {@link #setDocumentRoot(File) document root} will be served.
* @param registerDefaultServlet if the default servlet should be registered
*/
@Override
public void setRegisterDefaultServlet(boolean registerDefaultServlet) {
this.registerDefaultServlet = registerDefaultServlet;
}
@ -243,7 +251,8 @@ public abstract class AbstractEmbeddedServletContainerFactory implements
*
* @return true if the JSP servlet is to be registered
*/
protected boolean getRegisterJspServlet() {
@Override
public boolean isRegisterJspServlet() {
return this.registerJspServlet;
}
@ -253,6 +262,7 @@ public abstract class AbstractEmbeddedServletContainerFactory implements
* will be served.
* @param registerJspServlet if the JSP servlet should be registered
*/
@Override
public void setRegisterJspServlet(boolean registerJspServlet) {
this.registerJspServlet = registerJspServlet;
}
@ -262,7 +272,8 @@ public abstract class AbstractEmbeddedServletContainerFactory implements
*
* @return true if the default servlet is to be registered
*/
protected boolean getRegisterDefaultServlet() {
@Override
public boolean isRegisterDefaultServlet() {
return this.registerDefaultServlet;
}
@ -275,6 +286,7 @@ public abstract class AbstractEmbeddedServletContainerFactory implements
*
* @param jspServletClassName the class name for the JSP servlet if used
*/
@Override
public void setJspServletClassName(String jspServletClassName) {
this.jspServletClassName = jspServletClassName;
}

View File

@ -16,6 +16,7 @@
package org.springframework.bootstrap.context.embedded;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.context.annotation.AnnotatedBeanDefinitionReader;
import org.springframework.context.annotation.AnnotationConfigUtils;
@ -55,6 +56,10 @@ public class AnnotationConfigEmbeddedWebApplicationContext extends
private final ClassPathBeanDefinitionScanner scanner;
private Class<?>[] annotatedClasses;
private String[] basePackages;
/**
* Create a new {@link AnnotationConfigEmbeddedWebApplicationContext} that needs to be
* populated through {@link #register} calls and then manually {@linkplain #refresh
@ -149,9 +154,9 @@ public class AnnotationConfigEmbeddedWebApplicationContext extends
* @see #refresh()
*/
public void register(Class<?>... annotatedClasses) {
this.annotatedClasses = annotatedClasses;
Assert.notEmpty(annotatedClasses,
"At least one annotated class must be specified");
this.reader.register(annotatedClasses);
}
/**
@ -162,8 +167,8 @@ public class AnnotationConfigEmbeddedWebApplicationContext extends
* @see #refresh()
*/
public void scan(String... basePackages) {
this.basePackages = basePackages;
Assert.notEmpty(basePackages, "At least one base package must be specified");
this.scanner.scan(basePackages);
}
@Override
@ -172,4 +177,15 @@ public class AnnotationConfigEmbeddedWebApplicationContext extends
super.prepareRefresh();
}
@Override
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
super.postProcessBeanFactory(beanFactory);
if (this.basePackages != null && this.basePackages.length > 0) {
this.scanner.scan(this.basePackages);
}
if (this.annotatedClasses != null && this.annotatedClasses.length > 0) {
this.reader.register(this.annotatedClasses);
}
}
}

View File

@ -0,0 +1,72 @@
/*
* Copyright 2002-2013 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.bootstrap.context.embedded;
import java.io.File;
import java.net.InetAddress;
import java.util.List;
import java.util.Set;
/**
* Simple interface that represents customizations to an
* {@link EmbeddedServletContainerFactory}.
*
* @author Dave Syer
* @see EmbeddedServletContainerFactory
*/
public interface ConfigurableEmbeddedServletContainerFactory extends
EmbeddedServletContainerFactory {
void setContextPath(String contextPath);
String getContextPath();
void setPort(int port);
int getPort();
void setAddress(InetAddress address);
InetAddress getAddress();
void setInitializers(List<? extends ServletContextInitializer> initializers);
void setJspServletClassName(String jspServletClassName);
boolean isRegisterDefaultServlet();
void setRegisterJspServlet(boolean registerJspServlet);
boolean isRegisterJspServlet();
void setRegisterDefaultServlet(boolean registerDefaultServlet);
Set<ErrorPage> getErrorPages();
void addErrorPages(ErrorPage... errorPages);
void setErrorPages(Set<ErrorPage> errorPages);
File getDocumentRoot();
void setDocumentRoot(File documentRoot);
List<ServletContextInitializer> getInitializers();
void addInitializers(ServletContextInitializer... initializers);
}

View File

@ -17,7 +17,7 @@
package org.springframework.bootstrap.context.embedded;
/**
* Simple interface that represents a fully configure embedded servlet container (for
* Simple interface that represents a fully configured embedded servlet container (for
* example Tomcat or Jetty). Allows the container to be {@link #stop() stopped}.
*
* <p>

View File

@ -0,0 +1,33 @@
/*
* Copyright 2012-2013 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.bootstrap.context.embedded;
/**
* Strategy interface for customizing auto-configured embedded servlet containers. Any
* beans of this type will get a callback with the container factory before the container
* itself is started, so you can set the port, address, error pages etc. Beware: will be
* called from a BeanPostProcessor (so very early in the ApplicationContext lifecycle), so
* it might be safer to lookup dependencies lazily in the enclosing BeanFactory rather
* than injecting them with <code>@Autowired</code>.
*
* @author Dave Syer
*
*/
public interface EmbeddedServletContainerCustomizer {
void customize(ConfigurableEmbeddedServletContainerFactory factory);
}

View File

@ -110,10 +110,10 @@ public class JettyEmbeddedServletContainerFactory extends
this.context.setContextPath(StringUtils.hasLength(contextPath) ? contextPath
: "/");
configureDocumentRoot(this.context);
if (getRegisterDefaultServlet()) {
if (isRegisterDefaultServlet()) {
addDefaultServlet(this.context);
}
if (getRegisterJspServlet()
if (isRegisterJspServlet()
&& ClassUtils.isPresent(getJspServletClassName(), getClass()
.getClassLoader())) {
addJspServlet(this.context);

View File

@ -144,10 +144,10 @@ public class TomcatEmbeddedServletContainerFactory extends
if (this.resourceLoader != null) {
context.setParentClassLoader(this.resourceLoader.getClassLoader());
}
if (getRegisterDefaultServlet()) {
if (isRegisterDefaultServlet()) {
addDefaultServlet(context);
}
if (getRegisterJspServlet()
if (isRegisterJspServlet()
&& ClassUtils.isPresent(getJspServletClassName(), getClass()
.getClassLoader())) {
addJspServlet(context);

View File

@ -4,6 +4,7 @@ org.springframework.bootstrap.autoconfigure.data.JpaRepositoriesAutoConfiguratio
org.springframework.bootstrap.autoconfigure.jdbc.EmbeddedDatabaseAutoConfiguration,\
org.springframework.bootstrap.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.bootstrap.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.bootstrap.autoconfigure.web.EmbeddedContainerCustomizerConfiguration,\
org.springframework.bootstrap.autoconfigure.web.EmbeddedJettyAutoConfiguration,\
org.springframework.bootstrap.autoconfigure.web.EmbeddedTomcatAutoConfiguration,\
org.springframework.bootstrap.autoconfigure.web.WebMvcAutoConfiguration

View File

@ -0,0 +1,121 @@
/*
* Copyright 2012-2013 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.bootstrap.autoconfigure.web;
import javax.servlet.Servlet;
import org.junit.Test;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.bootstrap.context.annotation.ConditionalOnExpression;
import org.springframework.bootstrap.context.embedded.AnnotationConfigEmbeddedWebApplicationContext;
import org.springframework.bootstrap.context.embedded.ConfigurableEmbeddedServletContainerFactory;
import org.springframework.bootstrap.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.bootstrap.context.embedded.EmbeddedServletContainerFactory;
import org.springframework.bootstrap.context.embedded.MockEmbeddedServletContainerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.verify;
/**
* @author Dave Syer
*/
public class WebMvcAutoConfigurationTests {
private AnnotationConfigEmbeddedWebApplicationContext context;
@Test
public void createFromConfigClass() throws Exception {
this.context = new AnnotationConfigEmbeddedWebApplicationContext(
WebMvcAutoConfiguration.class, EmbeddedContainerConfiguration.class);
verifyContext();
}
@Test
public void containerHasNoServletContext() throws Exception {
this.context = new AnnotationConfigEmbeddedWebApplicationContext(
WebMvcAutoConfiguration.class, EmbeddedContainerConfiguration.class,
EnsureContainerHasNoServletContext.class);
verifyContext();
}
@Test
public void customizeContainerThroughCallback() throws Exception {
this.context = new AnnotationConfigEmbeddedWebApplicationContext(
WebMvcAutoConfiguration.class, EmbeddedContainerConfiguration.class,
EmbeddedContainerCustomizerConfiguration.class,
CallbackEmbeddedContainerCustomizer.class);
verifyContext();
assertEquals(9000, getContainerFactory().getPort());
}
private void verifyContext() {
MockEmbeddedServletContainerFactory containerFactory = getContainerFactory();
Servlet servlet = this.context.getBean(Servlet.class);
verify(containerFactory.getServletContext()).addServlet("dispatcherServlet",
servlet);
}
private MockEmbeddedServletContainerFactory getContainerFactory() {
return this.context.getBean(MockEmbeddedServletContainerFactory.class);
}
@Configuration
@ConditionalOnExpression("true")
public static class EmbeddedContainerConfiguration {
@Bean
public EmbeddedServletContainerFactory containerFactory() {
return new MockEmbeddedServletContainerFactory();
}
}
@Component
public static class EnsureContainerHasNoServletContext implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
if (bean instanceof ConfigurableEmbeddedServletContainerFactory) {
MockEmbeddedServletContainerFactory containerFactory = (MockEmbeddedServletContainerFactory) bean;
assertNull(containerFactory.getServletContext());
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
return bean;
}
}
@Component
public static class CallbackEmbeddedContainerCustomizer implements
EmbeddedServletContainerCustomizer {
@Override
public void customize(ConfigurableEmbeddedServletContainerFactory factory) {
factory.setPort(9000);
}
}
}

View File

@ -79,7 +79,7 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests {
@Test
public void startServlet() throws Exception {
AbstractEmbeddedServletContainerFactory factory = getFactory();
ConfigurableEmbeddedServletContainerFactory factory = getFactory();
this.container = factory
.getEmbdeddedServletContainer(exampleServletRegistration());
assertThat(getResponse("http://localhost:8080/hello"), equalTo("Hello World"));
@ -87,7 +87,7 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests {
@Test
public void emptyServer() throws Exception {
AbstractEmbeddedServletContainerFactory factory = getFactory();
ConfigurableEmbeddedServletContainerFactory factory = getFactory();
factory.setPort(0);
this.container = factory
.getEmbdeddedServletContainer(exampleServletRegistration());
@ -98,7 +98,7 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests {
@Test
public void stopServlet() throws Exception {
AbstractEmbeddedServletContainerFactory factory = getFactory();
ConfigurableEmbeddedServletContainerFactory factory = getFactory();
this.container = factory
.getEmbdeddedServletContainer(exampleServletRegistration());
this.container.stop();
@ -109,7 +109,7 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests {
@Test
@Ignore
public void restartWithKeepAlive() throws Exception {
AbstractEmbeddedServletContainerFactory factory = getFactory();
ConfigurableEmbeddedServletContainerFactory factory = getFactory();
this.container = factory
.getEmbdeddedServletContainer(exampleServletRegistration());
@ -130,7 +130,7 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests {
@Test
public void startServletAndFilter() throws Exception {
AbstractEmbeddedServletContainerFactory factory = getFactory();
ConfigurableEmbeddedServletContainerFactory factory = getFactory();
this.container = factory.getEmbdeddedServletContainer(
exampleServletRegistration(), new FilterRegistrationBean(
new ExampleFilter()));
@ -140,7 +140,7 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests {
@Test
public void startBlocksUntilReadyToServe() throws Exception {
// FIXME Assume.group(TestGroup.LONG_RUNNING);
AbstractEmbeddedServletContainerFactory factory = getFactory();
ConfigurableEmbeddedServletContainerFactory factory = getFactory();
final Date[] date = new Date[1];
this.container = factory
.getEmbdeddedServletContainer(new ServletContextInitializer() {
@ -160,7 +160,7 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests {
@Test
public void specificPort() throws Exception {
AbstractEmbeddedServletContainerFactory factory = getFactory();
ConfigurableEmbeddedServletContainerFactory factory = getFactory();
factory.setPort(8081);
this.container = factory
.getEmbdeddedServletContainer(exampleServletRegistration());
@ -169,7 +169,7 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests {
@Test
public void specificContextRoot() throws Exception {
AbstractEmbeddedServletContainerFactory factory = getFactory();
ConfigurableEmbeddedServletContainerFactory factory = getFactory();
factory.setContextPath("/say");
this.container = factory
.getEmbdeddedServletContainer(exampleServletRegistration());
@ -200,7 +200,7 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests {
@Test
public void doubleStop() throws Exception {
AbstractEmbeddedServletContainerFactory factory = getFactory();
ConfigurableEmbeddedServletContainerFactory factory = getFactory();
this.container = factory
.getEmbdeddedServletContainer(exampleServletRegistration());
this.container.stop();
@ -209,7 +209,7 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests {
@Test
public void multipleConfigurations() throws Exception {
AbstractEmbeddedServletContainerFactory factory = getFactory();
ConfigurableEmbeddedServletContainerFactory factory = getFactory();
ServletContextInitializer[] initializers = new ServletContextInitializer[6];
for (int i = 0; i < initializers.length; i++) {
initializers[i] = mock(ServletContextInitializer.class);

View File

@ -17,12 +17,18 @@
package org.springframework.bootstrap.context.embedded;
import javax.servlet.Servlet;
import javax.servlet.ServletContext;
import org.junit.Test;
import org.springframework.bootstrap.context.embedded.AnnotationConfigEmbeddedWebApplicationContext;
import org.springframework.bootstrap.context.embedded.config.ExampleEmbeddedWebApplicationConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.ServletContextAware;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import static org.mockito.Mockito.*;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.verify;
/**
* Tests for {@link AnnotationConfigEmbeddedWebApplicationContext}.
@ -64,10 +70,95 @@ public class AnnotationConfigEmbeddedWebApplicationContextTests {
verifyContext();
}
@Test
public void createAndInitializeCyclic() throws Exception {
this.context = new AnnotationConfigEmbeddedWebApplicationContext(
ServletContextAwareEmbeddedConfiguration.class);
verifyContext();
// You can't initialize the application context and inject the servlet context
// because of a cycle - we'd like this to be not null but it never will be
assertNull(this.context.getBean(ServletContextAwareEmbeddedConfiguration.class)
.getServletContext());
}
@Test
public void createAndInitializeWithRoot() throws Exception {
AnnotationConfigEmbeddedWebApplicationContext parent = new AnnotationConfigEmbeddedWebApplicationContext(
EmbeddedContainerConfiguration.class);
this.context = new AnnotationConfigEmbeddedWebApplicationContext();
this.context.register(ServletContextAwareConfiguration.class);
this.context.setParent(parent);
this.context.setServletContext(parent.getServletContext());
this.context.refresh();
verifyContext();
assertNotNull(this.context.getBean(ServletContextAwareConfiguration.class)
.getServletContext());
}
private void verifyContext() {
MockEmbeddedServletContainerFactory containerFactory = this.context
.getBean(MockEmbeddedServletContainerFactory.class);
Servlet servlet = this.context.getBean(Servlet.class);
verify(containerFactory.getServletContext()).addServlet("servlet", servlet);
}
@Configuration
@EnableWebMvc
public static class ServletContextAwareEmbeddedConfiguration implements
ServletContextAware {
private ServletContext servletContext;
@Bean
public EmbeddedServletContainerFactory containerFactory() {
return new MockEmbeddedServletContainerFactory();
}
@Bean
public Servlet servlet() {
return new MockServlet();
}
@Override
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
public ServletContext getServletContext() {
return this.servletContext;
}
}
@Configuration
public static class EmbeddedContainerConfiguration {
@Bean
public EmbeddedServletContainerFactory containerFactory() {
return new MockEmbeddedServletContainerFactory();
}
}
@Configuration
@EnableWebMvc
public static class ServletContextAwareConfiguration implements ServletContextAware {
private ServletContext servletContext;
@Bean
public Servlet servlet() {
return new MockServlet();
}
@Override
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
public ServletContext getServletContext() {
return this.servletContext;
}
}
}

View File

@ -16,12 +16,6 @@
package org.springframework.bootstrap.context.embedded;
import static org.mockito.BDDMockito.given;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
@ -29,6 +23,7 @@ import java.util.NoSuchElementException;
import javax.servlet.Filter;
import javax.servlet.FilterRegistration;
import javax.servlet.RequestDispatcher;
import javax.servlet.Servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
@ -36,25 +31,27 @@ import javax.servlet.ServletRegistration;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.bootstrap.context.embedded.EmbeddedServletContainer;
import org.springframework.bootstrap.context.embedded.EmbeddedServletContainerFactory;
import static org.mockito.BDDMockito.given;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
/**
* Mock {@link EmbeddedServletContainerFactory}.
*
* @author Phillip Webb
*/
public class MockEmbeddedServletContainerFactory implements
EmbeddedServletContainerFactory {
public class MockEmbeddedServletContainerFactory extends
AbstractEmbeddedServletContainerFactory {
private MockEmbeddedServletContainer container;
private int port;
@Override
public EmbeddedServletContainer getEmbdeddedServletContainer(
ServletContextInitializer... initializers) {
this.container = spy(new MockEmbeddedServletContainer(initializers, port));
this.container = spy(new MockEmbeddedServletContainer(initializers, getPort()));
return this.container;
}
@ -63,19 +60,17 @@ public class MockEmbeddedServletContainerFactory implements
}
public ServletContext getServletContext() {
return getContainer().servletContext;
return getContainer() == null ? null : getContainer().servletContext;
}
public RegisteredServlet getRegisteredServlet(int index) {
return getContainer().getRegisteredServlets().get(index);
return getContainer() == null ? null : getContainer().getRegisteredServlets()
.get(index);
}
public RegisteredFilter getRegisteredFilter(int index) {
return getContainer().getRegisteredFilters().get(index);
}
public void setPort(int port) {
this.port = port;
return getContainer() == null ? null : getContainer().getRegisteredFilters().get(
index);
}
public static class MockEmbeddedServletContainer implements EmbeddedServletContainer {
@ -128,6 +123,8 @@ public class MockEmbeddedServletContainerFactory implements
MockEmbeddedServletContainer.<String> emptyEnumeration());
given(this.servletContext.getAttributeNames()).willReturn(
MockEmbeddedServletContainer.<String> emptyEnumeration());
given(this.servletContext.getNamedDispatcher("default")).willReturn(
mock(RequestDispatcher.class));
for (ServletContextInitializer initializer : this.initializers) {
initializer.onStartup(this.servletContext);
}
@ -178,7 +175,7 @@ public class MockEmbeddedServletContainerFactory implements
}
public int getPort() {
return port;
return this.port;
}
}

View File

@ -13,13 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.bootstrap.context.annotation;
package org.springframework.bootstrap.context.test;
import java.util.Properties;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.bootstrap.context.annotation.ConfigurationProperties;
import org.springframework.bootstrap.context.annotation.EnableConfigurationProperties;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;