001/* 002 * Copyright 2012-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.boot.web.servlet.support; 018 019import java.util.Collections; 020 021import javax.servlet.Filter; 022import javax.servlet.Servlet; 023import javax.servlet.ServletContext; 024import javax.servlet.ServletContextEvent; 025import javax.servlet.ServletException; 026 027import org.apache.commons.logging.Log; 028import org.apache.commons.logging.LogFactory; 029 030import org.springframework.boot.SpringApplication; 031import org.springframework.boot.builder.ParentContextApplicationContextInitializer; 032import org.springframework.boot.builder.SpringApplicationBuilder; 033import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; 034import org.springframework.boot.web.servlet.ServletContextInitializer; 035import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext; 036import org.springframework.context.ApplicationContext; 037import org.springframework.context.ApplicationListener; 038import org.springframework.context.annotation.Configuration; 039import org.springframework.core.Ordered; 040import org.springframework.core.annotation.AnnotationUtils; 041import org.springframework.core.env.ConfigurableEnvironment; 042import org.springframework.util.Assert; 043import org.springframework.web.WebApplicationInitializer; 044import org.springframework.web.context.ConfigurableWebEnvironment; 045import org.springframework.web.context.ContextLoaderListener; 046import org.springframework.web.context.WebApplicationContext; 047 048/** 049 * An opinionated {@link WebApplicationInitializer} to run a {@link SpringApplication} 050 * from a traditional WAR deployment. Binds {@link Servlet}, {@link Filter} and 051 * {@link ServletContextInitializer} beans from the application context to the server. 052 * <p> 053 * To configure the application either override the 054 * {@link #configure(SpringApplicationBuilder)} method (calling 055 * {@link SpringApplicationBuilder#sources(Class...)}) or make the initializer itself a 056 * {@code @Configuration}. If you are using {@link SpringBootServletInitializer} in 057 * combination with other {@link WebApplicationInitializer WebApplicationInitializers} you 058 * might also want to add an {@code @Ordered} annotation to configure a specific startup 059 * order. 060 * <p> 061 * Note that a WebApplicationInitializer is only needed if you are building a war file and 062 * deploying it. If you prefer to run an embedded web server then you won't need this at 063 * all. 064 * 065 * @author Dave Syer 066 * @author Phillip Webb 067 * @author Andy Wilkinson 068 * @since 2.0.0 069 * @see #configure(SpringApplicationBuilder) 070 */ 071public abstract class SpringBootServletInitializer implements WebApplicationInitializer { 072 073 protected Log logger; // Don't initialize early 074 075 private boolean registerErrorPageFilter = true; 076 077 /** 078 * Set if the {@link ErrorPageFilter} should be registered. Set to {@code false} if 079 * error page mappings should be handled via the server and not Spring Boot. 080 * @param registerErrorPageFilter if the {@link ErrorPageFilter} should be registered. 081 */ 082 protected final void setRegisterErrorPageFilter(boolean registerErrorPageFilter) { 083 this.registerErrorPageFilter = registerErrorPageFilter; 084 } 085 086 @Override 087 public void onStartup(ServletContext servletContext) throws ServletException { 088 // Logger initialization is deferred in case an ordered 089 // LogServletContextInitializer is being used 090 this.logger = LogFactory.getLog(getClass()); 091 WebApplicationContext rootAppContext = createRootApplicationContext( 092 servletContext); 093 if (rootAppContext != null) { 094 servletContext.addListener(new ContextLoaderListener(rootAppContext) { 095 @Override 096 public void contextInitialized(ServletContextEvent event) { 097 // no-op because the application context is already initialized 098 } 099 }); 100 } 101 else { 102 this.logger.debug("No ContextLoaderListener registered, as " 103 + "createRootApplicationContext() did not " 104 + "return an application context"); 105 } 106 } 107 108 protected WebApplicationContext createRootApplicationContext( 109 ServletContext servletContext) { 110 SpringApplicationBuilder builder = createSpringApplicationBuilder(); 111 builder.main(getClass()); 112 ApplicationContext parent = getExistingRootWebApplicationContext(servletContext); 113 if (parent != null) { 114 this.logger.info("Root context already created (using as parent)."); 115 servletContext.setAttribute( 116 WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, null); 117 builder.initializers(new ParentContextApplicationContextInitializer(parent)); 118 } 119 builder.initializers( 120 new ServletContextApplicationContextInitializer(servletContext)); 121 builder.contextClass(AnnotationConfigServletWebServerApplicationContext.class); 122 builder = configure(builder); 123 builder.listeners(new WebEnvironmentPropertySourceInitializer(servletContext)); 124 SpringApplication application = builder.build(); 125 if (application.getAllSources().isEmpty() && AnnotationUtils 126 .findAnnotation(getClass(), Configuration.class) != null) { 127 application.addPrimarySources(Collections.singleton(getClass())); 128 } 129 Assert.state(!application.getAllSources().isEmpty(), 130 "No SpringApplication sources have been defined. Either override the " 131 + "configure method or add an @Configuration annotation"); 132 // Ensure error pages are registered 133 if (this.registerErrorPageFilter) { 134 application.addPrimarySources( 135 Collections.singleton(ErrorPageFilterConfiguration.class)); 136 } 137 return run(application); 138 } 139 140 /** 141 * Returns the {@code SpringApplicationBuilder} that is used to configure and create 142 * the {@link SpringApplication}. The default implementation returns a new 143 * {@code SpringApplicationBuilder} in its default state. 144 * @return the {@code SpringApplicationBuilder}. 145 * @since 1.3.0 146 */ 147 protected SpringApplicationBuilder createSpringApplicationBuilder() { 148 return new SpringApplicationBuilder(); 149 } 150 151 /** 152 * Called to run a fully configured {@link SpringApplication}. 153 * @param application the application to run 154 * @return the {@link WebApplicationContext} 155 */ 156 protected WebApplicationContext run(SpringApplication application) { 157 return (WebApplicationContext) application.run(); 158 } 159 160 private ApplicationContext getExistingRootWebApplicationContext( 161 ServletContext servletContext) { 162 Object context = servletContext.getAttribute( 163 WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE); 164 if (context instanceof ApplicationContext) { 165 return (ApplicationContext) context; 166 } 167 return null; 168 } 169 170 /** 171 * Configure the application. Normally all you would need to do is to add sources 172 * (e.g. config classes) because other settings have sensible defaults. You might 173 * choose (for instance) to add default command line arguments, or set an active 174 * Spring profile. 175 * @param builder a builder for the application context 176 * @return the application builder 177 * @see SpringApplicationBuilder 178 */ 179 protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { 180 return builder; 181 } 182 183 private static final class WebEnvironmentPropertySourceInitializer 184 implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered { 185 186 private final ServletContext servletContext; 187 188 private WebEnvironmentPropertySourceInitializer(ServletContext servletContext) { 189 this.servletContext = servletContext; 190 } 191 192 @Override 193 public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) { 194 ConfigurableEnvironment environment = event.getEnvironment(); 195 if (environment instanceof ConfigurableWebEnvironment) { 196 ((ConfigurableWebEnvironment) environment) 197 .initPropertySources(this.servletContext, null); 198 } 199 } 200 201 @Override 202 public int getOrder() { 203 return Ordered.HIGHEST_PRECEDENCE; 204 } 205 206 } 207 208}