001/*
002 * Copyright 2002-2016 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 *      https://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.web;
018
019import java.lang.reflect.Modifier;
020import java.util.LinkedList;
021import java.util.List;
022import java.util.ServiceLoader;
023import java.util.Set;
024import javax.servlet.ServletContainerInitializer;
025import javax.servlet.ServletContext;
026import javax.servlet.ServletException;
027import javax.servlet.annotation.HandlesTypes;
028
029import org.springframework.core.annotation.AnnotationAwareOrderComparator;
030
031/**
032 * Servlet 3.0 {@link ServletContainerInitializer} designed to support code-based
033 * configuration of the servlet container using Spring's {@link WebApplicationInitializer}
034 * SPI as opposed to (or possibly in combination with) the traditional
035 * {@code web.xml}-based approach.
036 *
037 * <h2>Mechanism of Operation</h2>
038 * This class will be loaded and instantiated and have its {@link #onStartup}
039 * method invoked by any Servlet 3.0-compliant container during container startup assuming
040 * that the {@code spring-web} module JAR is present on the classpath. This occurs through
041 * the JAR Services API {@link ServiceLoader#load(Class)} method detecting the
042 * {@code spring-web} module's {@code META-INF/services/javax.servlet.ServletContainerInitializer}
043 * service provider configuration file. See the
044 * <a href="https://download.oracle.com/javase/6/docs/technotes/guides/jar/jar.html#Service%20Provider">
045 * JAR Services API documentation</a> as well as section <em>8.2.4</em> of the Servlet 3.0
046 * Final Draft specification for complete details.
047 *
048 * <h3>In combination with {@code web.xml}</h3>
049 * A web application can choose to limit the amount of classpath scanning the Servlet
050 * container does at startup either through the {@code metadata-complete} attribute in
051 * {@code web.xml}, which controls scanning for Servlet annotations or through an
052 * {@code <absolute-ordering>} element also in {@code web.xml}, which controls which
053 * web fragments (i.e. jars) are allowed to perform a {@code ServletContainerInitializer}
054 * scan. When using this feature, the {@link SpringServletContainerInitializer}
055 * can be enabled by adding "spring_web" to the list of named web fragments in
056 * {@code web.xml} as follows:
057 *
058 * <pre class="code">
059 * {@code
060 * <absolute-ordering>
061 *   <name>some_web_fragment</name>
062 *   <name>spring_web</name>
063 * </absolute-ordering>
064 * }</pre>
065 *
066 * <h2>Relationship to Spring's {@code WebApplicationInitializer}</h2>
067 * Spring's {@code WebApplicationInitializer} SPI consists of just one method:
068 * {@link WebApplicationInitializer#onStartup(ServletContext)}. The signature is intentionally
069 * quite similar to {@link ServletContainerInitializer#onStartup(Set, ServletContext)}:
070 * simply put, {@code SpringServletContainerInitializer} is responsible for instantiating
071 * and delegating the {@code ServletContext} to any user-defined
072 * {@code WebApplicationInitializer} implementations. It is then the responsibility of
073 * each {@code WebApplicationInitializer} to do the actual work of initializing the
074 * {@code ServletContext}. The exact process of delegation is described in detail in the
075 * {@link #onStartup onStartup} documentation below.
076 *
077 * <h2>General Notes</h2>
078 * In general, this class should be viewed as <em>supporting infrastructure</em> for
079 * the more important and user-facing {@code WebApplicationInitializer} SPI. Taking
080 * advantage of this container initializer is also completely <em>optional</em>: while
081 * it is true that this initializer will be loaded and invoked under all Servlet 3.0+
082 * runtimes, it remains the user's choice whether to make any
083 * {@code WebApplicationInitializer} implementations available on the classpath. If no
084 * {@code WebApplicationInitializer} types are detected, this container initializer will
085 * have no effect.
086 *
087 * <p>Note that use of this container initializer and of {@code WebApplicationInitializer}
088 * is not in any way "tied" to Spring MVC other than the fact that the types are shipped
089 * in the {@code spring-web} module JAR. Rather, they can be considered general-purpose
090 * in their ability to facilitate convenient code-based configuration of the
091 * {@code ServletContext}. In other words, any servlet, listener, or filter may be
092 * registered within a {@code WebApplicationInitializer}, not just Spring MVC-specific
093 * components.
094 *
095 * <p>This class is neither designed for extension nor intended to be extended.
096 * It should be considered an internal type, with {@code WebApplicationInitializer}
097 * being the public-facing SPI.
098 *
099 * <h2>See Also</h2>
100 * See {@link WebApplicationInitializer} Javadoc for examples and detailed usage
101 * recommendations.<p>
102 *
103 * @author Chris Beams
104 * @author Juergen Hoeller
105 * @author Rossen Stoyanchev
106 * @since 3.1
107 * @see #onStartup(Set, ServletContext)
108 * @see WebApplicationInitializer
109 */
110@HandlesTypes(WebApplicationInitializer.class)
111public class SpringServletContainerInitializer implements ServletContainerInitializer {
112
113        /**
114         * Delegate the {@code ServletContext} to any {@link WebApplicationInitializer}
115         * implementations present on the application classpath.
116         * <p>Because this class declares @{@code HandlesTypes(WebApplicationInitializer.class)},
117         * Servlet 3.0+ containers will automatically scan the classpath for implementations
118         * of Spring's {@code WebApplicationInitializer} interface and provide the set of all
119         * such types to the {@code webAppInitializerClasses} parameter of this method.
120         * <p>If no {@code WebApplicationInitializer} implementations are found on the classpath,
121         * this method is effectively a no-op. An INFO-level log message will be issued notifying
122         * the user that the {@code ServletContainerInitializer} has indeed been invoked but that
123         * no {@code WebApplicationInitializer} implementations were found.
124         * <p>Assuming that one or more {@code WebApplicationInitializer} types are detected,
125         * they will be instantiated (and <em>sorted</em> if the @{@link
126         * org.springframework.core.annotation.Order @Order} annotation is present or
127         * the {@link org.springframework.core.Ordered Ordered} interface has been
128         * implemented). Then the {@link WebApplicationInitializer#onStartup(ServletContext)}
129         * method will be invoked on each instance, delegating the {@code ServletContext} such
130         * that each instance may register and configure servlets such as Spring's
131         * {@code DispatcherServlet}, listeners such as Spring's {@code ContextLoaderListener},
132         * or any other Servlet API componentry such as filters.
133         * @param webAppInitializerClasses all implementations of
134         * {@link WebApplicationInitializer} found on the application classpath
135         * @param servletContext the servlet context to be initialized
136         * @see WebApplicationInitializer#onStartup(ServletContext)
137         * @see AnnotationAwareOrderComparator
138         */
139        @Override
140        public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
141                        throws ServletException {
142
143                List<WebApplicationInitializer> initializers = new LinkedList<WebApplicationInitializer>();
144
145                if (webAppInitializerClasses != null) {
146                        for (Class<?> waiClass : webAppInitializerClasses) {
147                                // Be defensive: Some servlet containers provide us with invalid classes,
148                                // no matter what @HandlesTypes says...
149                                if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
150                                                WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
151                                        try {
152                                                initializers.add((WebApplicationInitializer) waiClass.newInstance());
153                                        }
154                                        catch (Throwable ex) {
155                                                throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
156                                        }
157                                }
158                        }
159                }
160
161                if (initializers.isEmpty()) {
162                        servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
163                        return;
164                }
165
166                servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
167                AnnotationAwareOrderComparator.sort(initializers);
168                for (WebApplicationInitializer initializer : initializers) {
169                        initializer.onStartup(servletContext);
170                }
171        }
172
173}