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