1 /* 2 * Copyright 2002-2011 the original author or authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package org.springframework.web; 18 19 import java.lang.reflect.Modifier; 20 import java.util.Collections; 21 import java.util.LinkedList; 22 import java.util.List; 23 import java.util.ServiceLoader; 24 import java.util.Set; 25 import javax.servlet.ServletContainerInitializer; 26 import javax.servlet.ServletContext; 27 import javax.servlet.ServletException; 28 import javax.servlet.annotation.HandlesTypes; 29 30 import org.springframework.core.annotation.AnnotationAwareOrderComparator; 31 32 /** 33 * Servlet 3.0 {@link ServletContainerInitializer} designed to support code-based 34 * configuration of the servlet container using Spring's {@link WebApplicationInitializer} 35 * SPI as opposed to (or possibly in combination with) the traditional 36 * {@code web.xml}-based approach. 37 * 38 * <h2>Mechanism of Operation</h2> 39 * This class will be loaded and instantiated and have its {@link #onStartup onStartup} 40 * method invoked by any Servlet 3.0-compliant container during container startup assuming 41 * that the {@code spring-web} module JAR is present on the classpath. This occurs through 42 * the JAR Services API {@link ServiceLoader#load(Class)} method detecting the 43 * {@code spring-web} module's {@code META-INF/services/javax.servlet.ServletContainerInitializer} 44 * service provider configuration file. See the 45 * <a href="http://download.oracle.com/javase/6/docs/technotes/guides/jar/jar.html#Service%20Provider"> 46 * JAR Services API documentation</a> as well as section <em>8.2.4</em> of the Servlet 3.0 47 * Final Draft specification for complete details. 48 * 49 * <h3>when in combination with {@code web.xml}</h3> 50 * <p>If a web application does include a {@code WEB-INF/web.xml} file, it is important to 51 * understand that neither this nor any other {@code ServletContextInitializer} will be 52 * processed unless the {@code <web-app>} element's {@code version} attribute is >= "3.0" 53 * and the {@code xsi:schemaLocation} for "http://java.sun.com/xml/ns/javaee" is set to 54 * "http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd". 55 * 56 * <h2>Relationship to Spring's {@code WebApplicationInitializer}</h2> 57 * Spring's {@code WebApplicationInitializer} SPI consists of just one method: 58 * {@link WebApplicationInitializer#onStartup(ServletContext)}. The signature is intentionally 59 * quite similar to {@link ServletContainerInitializer#onStartup(Set, ServletContext)}: 60 * simply put, {@code SpringServletContainerInitializer} is responsible for instantiating 61 * and delegating the {@code ServletContext} to any user-defined 62 * {@code WebApplicationInitializer} implementations. It is then the responsibility of 63 * each {@code WebApplicationInitializer} to do the actual work of initializing the 64 * {@code ServletContext}. The exact process of delegation is described in detail in the 65 * {@link #onStartup onStartup} documentation below. 66 * 67 * <h2>General Notes</h2> 68 * In general, this class should be viewed as <em>supporting infrastructure</em> for 69 * the more important and user-facing {@code WebApplicationInitializer} SPI. Taking 70 * advantage of this container initializer is also completely <em>optional</em>: while 71 * it is true that this initializer will be loaded and invoked under all Servlet 3.0+ 72 * runtimes, it remains the user's choice whether to make any 73 * {@code WebApplicationInitializer} implementations available on the classpath. If no 74 * {@code WebApplicationInitializer} types are detected, this container initializer will 75 * have no effect. 76 * 77 * <p>Note that use of this container initializer and of {@code WebApplicationInitializer} 78 * is not in any way "tied" to Spring MVC other than the fact that the types are shipped 79 * in the {@code spring-web} module JAR. Rather, they can be considered general-purpose 80 * in their ability to facilitate convenient code-based configuration of the 81 * {@code ServletContext}. Said another way, any servlet, listener, or filter may be 82 * registered within a {@code WebApplicationInitializer}, not just Spring MVC-specific 83 * components. 84 * 85 * <p>This class is not designed for nor intended to be extended. It should be considered 86 * an internal type, with {@code WebApplicationInitializer} being the public-facing SPI. 87 * 88 * <h2>See Also</h2> 89 * See {@link WebApplicationInitializer} Javadoc for examples and detailed usage 90 * recommendations.<p> 91 * 92 * @author Chris Beams 93 * @author Juergen Hoeller 94 * @since 3.1 95 * @see #onStartup(Set, ServletContext) 96 * @see WebApplicationInitializer 97 */ 98 @HandlesTypes(WebApplicationInitializer.class) 99 public class SpringServletContainerInitializer implements ServletContainerInitializer { 100 101 /** 102 * Delegate the {@code ServletContext} to any {@link WebApplicationInitializer} 103 * implementations present on the application classpath. 104 * 105 * <p>Because this class declares @{@code HandlesTypes(WebApplicationInitializer.class)}, 106 * Servlet 3.0+ containers will automatically scan the classpath for implementations 107 * of Spring's {@code WebApplicationInitializer} interface and provide the set of all 108 * such types to the {@code webAppInitializerClasses} parameter of this method. 109 * 110 * <p>If no {@code WebApplicationInitializer} implementations are found on the 111 * classpath, this method is effectively a no-op. An INFO-level log message will be 112 * issued notifying the user that the {@code ServletContainerInitializer} has indeed 113 * been invoked, but that no {@code WebApplicationInitializer} implementations were 114 * found. 115 * 116 * <p>Assuming that one or more {@code WebApplicationInitializer} types are detected, 117 * they will be instantiated (and <em>sorted</em> if the @{@link 118 * org.springframework.core.annotation.Order Order} annotation is present or 119 * the {@link org.springframework.core.Ordered Ordered} interface has been 120 * implemented). Then the {@link WebApplicationInitializer#onStartup(ServletContext)} 121 * method will be invoked on each instance, delegating the {@code ServletContext} such 122 * that each instance may register and configure servlets such as Spring's 123 * {@code DispatcherServlet}, listeners such as Spring's {@code ContextLoaderListener} 124 * or any other Servlet API componentry such as filters. 125 * 126 * @param webAppInitializerClasses all implementations of 127 * {@link WebApplicationInitializer} found on the application classpath 128 * @param servletContext the servlet context to be initialized 129 * @see WebApplicationInitializer#onStartup(ServletContext) 130 * @see AnnotationAwareOrderComparator 131 */ onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)132 public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) 133 throws ServletException { 134 135 List<WebApplicationInitializer> initializers = new LinkedList<WebApplicationInitializer>(); 136 137 if (webAppInitializerClasses != null) { 138 for (Class<?> waiClass : webAppInitializerClasses) { 139 // Be defensive: Some servlet containers provide us with invalid classes, 140 // no matter what @HandlesTypes says... 141 if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) && 142 WebApplicationInitializer.class.isAssignableFrom(waiClass)) { 143 try { 144 initializers.add((WebApplicationInitializer) waiClass.newInstance()); 145 } 146 catch (Throwable ex) { 147 throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex); 148 } 149 } 150 } 151 } 152 153 if (initializers.isEmpty()) { 154 servletContext.log("No Spring WebApplicationInitializer types detected on classpath"); 155 return; 156 } 157 158 Collections.sort(initializers, new AnnotationAwareOrderComparator()); 159 servletContext.log("Spring WebApplicationInitializers detected on classpath: " + initializers); 160 161 for (WebApplicationInitializer initializer : initializers) { 162 initializer.onStartup(servletContext); 163 } 164 } 165 166 } 167