From 20cce0c69c53d996bb6e9c919b0755a2860a7817 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Mon, 10 Jun 2013 18:11:11 +0100 Subject: [PATCH] [bs-52] Added support for SpringApplication in a ServletContextInitializer WAR applications should extend SpringServletInitializer to take advantage of Servlet 3.0 initialization and SpringApplication context loading features. [#48386505] [bs-52] Support for running "traditional" webapps in place --- .../bootstrap/SpringApplication.java | 14 +++- .../EmbeddedWebApplicationContext.java | 22 ++++-- .../web/BootstrapServletInitializer.java | 55 ------------- .../web/SpringServletInitializer.java | 77 +++++++++++++++++++ 4 files changed, 105 insertions(+), 63 deletions(-) delete mode 100644 spring-bootstrap/src/main/java/org/springframework/bootstrap/web/BootstrapServletInitializer.java create mode 100644 spring-bootstrap/src/main/java/org/springframework/bootstrap/web/SpringServletInitializer.java diff --git a/spring-bootstrap/src/main/java/org/springframework/bootstrap/SpringApplication.java b/spring-bootstrap/src/main/java/org/springframework/bootstrap/SpringApplication.java index fba9646b7b8..ccb6db30f1c 100644 --- a/spring-bootstrap/src/main/java/org/springframework/bootstrap/SpringApplication.java +++ b/spring-bootstrap/src/main/java/org/springframework/bootstrap/SpringApplication.java @@ -391,9 +391,8 @@ public class SpringApplication { * @param sources the sources to load */ protected void load(ApplicationContext context, Object[] sources) { - Assert.isInstanceOf(BeanDefinitionRegistry.class, context); BeanDefinitionLoader loader = createBeanDefinitionLoader( - (BeanDefinitionRegistry) context, sources); + getBeanDefinitionRegistry(context), sources); if (this.beanNameGenerator != null) { loader.setBeanNameGenerator(this.beanNameGenerator); } @@ -406,6 +405,17 @@ public class SpringApplication { loader.load(); } + /** + * @param context the application context + * @return the BeanDefinitionRegistry if it can be determined + */ + private BeanDefinitionRegistry getBeanDefinitionRegistry(ApplicationContext context) { + if (context instanceof BeanDefinitionRegistry) { + return (BeanDefinitionRegistry) context; + } + throw new IllegalStateException("Could not locate BeanDefinitionRegistry"); + } + /** * Factory method used to create the {@link BeanDefinitionLoader}. * @param registry the bean definition registry diff --git a/spring-bootstrap/src/main/java/org/springframework/bootstrap/context/embedded/EmbeddedWebApplicationContext.java b/spring-bootstrap/src/main/java/org/springframework/bootstrap/context/embedded/EmbeddedWebApplicationContext.java index 742c7150815..1ce93e8caa0 100644 --- a/spring-bootstrap/src/main/java/org/springframework/bootstrap/context/embedded/EmbeddedWebApplicationContext.java +++ b/spring-bootstrap/src/main/java/org/springframework/bootstrap/context/embedded/EmbeddedWebApplicationContext.java @@ -127,16 +127,25 @@ public class EmbeddedWebApplicationContext extends GenericWebApplicationContext } private synchronized void createAndStartEmbeddedServletContainer() { - if (this.embeddedServletContainer == null) { + if (this.embeddedServletContainer == null && getServletContext() == null) { EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory(); this.embeddedServletContainer = containerFactory .getEmbdeddedServletContainer(getServletContextInitializers()); - WebApplicationContextUtils.registerWebApplicationScopes(getBeanFactory(), - getServletContext()); - WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), - getServletContext()); - initPropertySources(); + } else if (getServletContext() != null) { + for (ServletContextInitializer initializer : getServletContextInitializers()) { + try { + initializer.onStartup(getServletContext()); + } catch (ServletException e) { + throw new ApplicationContextException( + "Cannot initialize servlet context", e); + } + } } + WebApplicationContextUtils.registerWebApplicationScopes(getBeanFactory(), + getServletContext()); + WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), + getServletContext()); + initPropertySources(); } /** @@ -339,4 +348,5 @@ public class EmbeddedWebApplicationContext extends GenericWebApplicationContext public EmbeddedServletContainer getEmbeddedServletContainer() { return this.embeddedServletContainer; } + } diff --git a/spring-bootstrap/src/main/java/org/springframework/bootstrap/web/BootstrapServletInitializer.java b/spring-bootstrap/src/main/java/org/springframework/bootstrap/web/BootstrapServletInitializer.java deleted file mode 100644 index 07313856105..00000000000 --- a/spring-bootstrap/src/main/java/org/springframework/bootstrap/web/BootstrapServletInitializer.java +++ /dev/null @@ -1,55 +0,0 @@ -package org.springframework.bootstrap.web; - -import javax.servlet.Filter; - -import org.springframework.util.ClassUtils; -import org.springframework.web.WebApplicationInitializer; -import org.springframework.web.filter.DelegatingFilterProxy; -import org.springframework.web.servlet.FrameworkServlet; -import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; - -/** - *

- * A handy opinionated {@link WebApplicationInitializer} for applications that only have - * one Spring servlet, and no more than a single filter (which itself is only enabled when - * Spring Security is detected). If your application is more complicated consider using - * one of the other WebApplicationInitializers. - *

- * - *

- * Note that a WebApplicationInitializer is only needed if you are building a war file and - * deploying it. If you prefer to run an embedded container (we do) then you won't need - * this at all. - *

- * - * @author Dave Syer - * - */ -public abstract class BootstrapServletInitializer extends - AbstractAnnotationConfigDispatcherServletInitializer { - - @Override - protected Class[] getRootConfigClasses() { - return null; - } - - @Override - protected String[] getServletMappings() { - return new String[] { "/" }; - } - - @Override - protected Filter[] getServletFilters() { - if (ClassUtils.isPresent( - "org.springframework.security.config.annotation.web.EnableWebSecurity", - null)) { - DelegatingFilterProxy filter = new DelegatingFilterProxy( - "springSecurityFilterChain"); - filter.setContextAttribute(FrameworkServlet.SERVLET_CONTEXT_PREFIX - + "dispatcher"); - return new Filter[] { filter }; - } - return new Filter[0]; - } - -} diff --git a/spring-bootstrap/src/main/java/org/springframework/bootstrap/web/SpringServletInitializer.java b/spring-bootstrap/src/main/java/org/springframework/bootstrap/web/SpringServletInitializer.java new file mode 100644 index 00000000000..ba7c51d31ef --- /dev/null +++ b/spring-bootstrap/src/main/java/org/springframework/bootstrap/web/SpringServletInitializer.java @@ -0,0 +1,77 @@ +package org.springframework.bootstrap.web; + +import javax.servlet.ServletContext; +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.bootstrap.SpringApplication; +import org.springframework.bootstrap.context.embedded.AnnotationConfigEmbeddedWebApplicationContext; +import org.springframework.context.ApplicationContext; +import org.springframework.web.WebApplicationInitializer; +import org.springframework.web.context.ContextLoaderListener; +import org.springframework.web.context.WebApplicationContext; + +/** + *

+ * A handy opinionated {@link WebApplicationInitializer} for applications that only have + * one Spring servlet, and no more than a single filter (which itself is only enabled when + * Spring Security is detected). If your application is more complicated consider using + * one of the other WebApplicationInitializers. + *

+ * + *

+ * Note that a WebApplicationInitializer is only needed if you are building a war file and + * deploying it. If you prefer to run an embedded container (we do) then you won't need + * this at all. + *

+ * + * @author Dave Syer + * + */ +public abstract class SpringServletInitializer implements WebApplicationInitializer { + + /** Logger available to subclasses. */ + protected final Log logger = LogFactory.getLog(getClass()); + + public void onStartup(ServletContext servletContext) throws ServletException { + WebApplicationContext rootAppContext = this + .createRootApplicationContext(servletContext); + if (rootAppContext != null) { + servletContext.addListener(new ContextLoaderListener(rootAppContext) { + @Override + public void contextInitialized(ServletContextEvent event) { + // no-op because the application context is already initialized + } + }); + } else { + this.logger + .debug("No ContextLoaderListener registered, as " + + "createRootApplicationContext() did not return an application context"); + } + } + + protected WebApplicationContext createRootApplicationContext( + ServletContext servletContext) { + ApplicationContext parent = null; + Object object = servletContext + .getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE); + if (object != null && object instanceof ApplicationContext) { + this.logger.info("Root context already created (using as parent)."); + parent = (ApplicationContext) object; + servletContext.setAttribute( + WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, null); + } + SpringApplication application = new SpringApplication( + (Object[]) getConfigClasses()); + AnnotationConfigEmbeddedWebApplicationContext context = new AnnotationConfigEmbeddedWebApplicationContext(); + context.setParent(parent); + context.setServletContext(servletContext); + application.setApplicationContext(context); + return (WebApplicationContext) application.run(); + } + + protected abstract Class[] getConfigClasses(); + +}