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