001/* 002 * Copyright 2002-2015 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.servlet.support; 018 019import java.util.EnumSet; 020import javax.servlet.DispatcherType; 021import javax.servlet.Filter; 022import javax.servlet.FilterRegistration; 023import javax.servlet.FilterRegistration.Dynamic; 024import javax.servlet.ServletContext; 025import javax.servlet.ServletException; 026import javax.servlet.ServletRegistration; 027 028import org.springframework.context.ApplicationContextInitializer; 029import org.springframework.core.Conventions; 030import org.springframework.util.Assert; 031import org.springframework.util.ObjectUtils; 032import org.springframework.web.context.AbstractContextLoaderInitializer; 033import org.springframework.web.context.WebApplicationContext; 034import org.springframework.web.servlet.DispatcherServlet; 035import org.springframework.web.servlet.FrameworkServlet; 036 037/** 038 * Base class for {@link org.springframework.web.WebApplicationInitializer} 039 * implementations that register a {@link DispatcherServlet} in the servlet context. 040 * 041 * <p>Concrete implementations are required to implement 042 * {@link #createServletApplicationContext()}, as well as {@link #getServletMappings()}, 043 * both of which get invoked from {@link #registerDispatcherServlet(ServletContext)}. 044 * Further customization can be achieved by overriding 045 * {@link #customizeRegistration(ServletRegistration.Dynamic)}. 046 * 047 * <p>Because this class extends from {@link AbstractContextLoaderInitializer}, concrete 048 * implementations are also required to implement {@link #createRootApplicationContext()} 049 * to set up a parent "<strong>root</strong>" application context. If a root context is 050 * not desired, implementations can simply return {@code null} in the 051 * {@code createRootApplicationContext()} implementation. 052 * 053 * @author Arjen Poutsma 054 * @author Chris Beams 055 * @author Rossen Stoyanchev 056 * @author Juergen Hoeller 057 * @author Stephane Nicoll 058 * @since 3.2 059 */ 060public abstract class AbstractDispatcherServletInitializer extends AbstractContextLoaderInitializer { 061 062 /** 063 * The default servlet name. Can be customized by overriding {@link #getServletName}. 064 */ 065 public static final String DEFAULT_SERVLET_NAME = "dispatcher"; 066 067 068 @Override 069 public void onStartup(ServletContext servletContext) throws ServletException { 070 super.onStartup(servletContext); 071 registerDispatcherServlet(servletContext); 072 } 073 074 /** 075 * Register a {@link DispatcherServlet} against the given servlet context. 076 * <p>This method will create a {@code DispatcherServlet} with the name returned by 077 * {@link #getServletName()}, initializing it with the application context returned 078 * from {@link #createServletApplicationContext()}, and mapping it to the patterns 079 * returned from {@link #getServletMappings()}. 080 * <p>Further customization can be achieved by overriding {@link 081 * #customizeRegistration(ServletRegistration.Dynamic)} or 082 * {@link #createDispatcherServlet(WebApplicationContext)}. 083 * @param servletContext the context to register the servlet against 084 */ 085 protected void registerDispatcherServlet(ServletContext servletContext) { 086 String servletName = getServletName(); 087 Assert.hasLength(servletName, "getServletName() must not return empty or null"); 088 089 WebApplicationContext servletAppContext = createServletApplicationContext(); 090 Assert.notNull(servletAppContext, 091 "createServletApplicationContext() did not return an application " + 092 "context for servlet [" + servletName + "]"); 093 094 FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext); 095 dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers()); 096 097 ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet); 098 Assert.notNull(registration, 099 "Failed to register servlet with name '" + servletName + "'." + 100 "Check if there is another servlet registered under the same name."); 101 102 registration.setLoadOnStartup(1); 103 registration.addMapping(getServletMappings()); 104 registration.setAsyncSupported(isAsyncSupported()); 105 106 Filter[] filters = getServletFilters(); 107 if (!ObjectUtils.isEmpty(filters)) { 108 for (Filter filter : filters) { 109 registerServletFilter(servletContext, filter); 110 } 111 } 112 113 customizeRegistration(registration); 114 } 115 116 /** 117 * Return the name under which the {@link DispatcherServlet} will be registered. 118 * Defaults to {@link #DEFAULT_SERVLET_NAME}. 119 * @see #registerDispatcherServlet(ServletContext) 120 */ 121 protected String getServletName() { 122 return DEFAULT_SERVLET_NAME; 123 } 124 125 /** 126 * Create a servlet application context to be provided to the {@code DispatcherServlet}. 127 * <p>The returned context is delegated to Spring's 128 * {@link DispatcherServlet#DispatcherServlet(WebApplicationContext)}. As such, 129 * it typically contains controllers, view resolvers, locale resolvers, and other 130 * web-related beans. 131 * @see #registerDispatcherServlet(ServletContext) 132 */ 133 protected abstract WebApplicationContext createServletApplicationContext(); 134 135 /** 136 * Create a {@link DispatcherServlet} (or other kind of {@link FrameworkServlet}-derived 137 * dispatcher) with the specified {@link WebApplicationContext}. 138 * <p>Note: This allows for any {@link FrameworkServlet} subclass as of 4.2.3. 139 * Previously, it insisted on returning a {@link DispatcherServlet} or subclass thereof. 140 */ 141 protected FrameworkServlet createDispatcherServlet(WebApplicationContext servletAppContext) { 142 return new DispatcherServlet(servletAppContext); 143 } 144 145 /** 146 * Specify application context initializers to be applied to the servlet-specific 147 * application context that the {@code DispatcherServlet} is being created with. 148 * @since 4.2 149 * @see #createServletApplicationContext() 150 * @see DispatcherServlet#setContextInitializers 151 * @see #getRootApplicationContextInitializers() 152 */ 153 protected ApplicationContextInitializer<?>[] getServletApplicationContextInitializers() { 154 return null; 155 } 156 157 /** 158 * Specify the servlet mapping(s) for the {@code DispatcherServlet} — 159 * for example {@code "/"}, {@code "/app"}, etc. 160 * @see #registerDispatcherServlet(ServletContext) 161 */ 162 protected abstract String[] getServletMappings(); 163 164 /** 165 * Specify filters to add and map to the {@code DispatcherServlet}. 166 * @return an array of filters or {@code null} 167 * @see #registerServletFilter(ServletContext, Filter) 168 */ 169 protected Filter[] getServletFilters() { 170 return null; 171 } 172 173 /** 174 * Add the given filter to the ServletContext and map it to the 175 * {@code DispatcherServlet} as follows: 176 * <ul> 177 * <li>a default filter name is chosen based on its concrete type 178 * <li>the {@code asyncSupported} flag is set depending on the 179 * return value of {@link #isAsyncSupported() asyncSupported} 180 * <li>a filter mapping is created with dispatcher types {@code REQUEST}, 181 * {@code FORWARD}, {@code INCLUDE}, and conditionally {@code ASYNC} depending 182 * on the return value of {@link #isAsyncSupported() asyncSupported} 183 * </ul> 184 * <p>If the above defaults are not suitable or insufficient, override this 185 * method and register filters directly with the {@code ServletContext}. 186 * @param servletContext the servlet context to register filters with 187 * @param filter the filter to be registered 188 * @return the filter registration 189 */ 190 protected FilterRegistration.Dynamic registerServletFilter(ServletContext servletContext, Filter filter) { 191 String filterName = Conventions.getVariableName(filter); 192 Dynamic registration = servletContext.addFilter(filterName, filter); 193 if (registration == null) { 194 int counter = -1; 195 while (counter == -1 || registration == null) { 196 counter++; 197 registration = servletContext.addFilter(filterName + "#" + counter, filter); 198 Assert.isTrue(counter < 100, 199 "Failed to register filter '" + filter + "'." + 200 "Could the same Filter instance have been registered already?"); 201 } 202 } 203 registration.setAsyncSupported(isAsyncSupported()); 204 registration.addMappingForServletNames(getDispatcherTypes(), false, getServletName()); 205 return registration; 206 } 207 208 private EnumSet<DispatcherType> getDispatcherTypes() { 209 return (isAsyncSupported() ? 210 EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.INCLUDE, DispatcherType.ASYNC) : 211 EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.INCLUDE)); 212 } 213 214 /** 215 * A single place to control the {@code asyncSupported} flag for the 216 * {@code DispatcherServlet} and all filters added via {@link #getServletFilters()}. 217 * <p>The default value is "true". 218 */ 219 protected boolean isAsyncSupported() { 220 return true; 221 } 222 223 /** 224 * Optionally perform further registration customization once 225 * {@link #registerDispatcherServlet(ServletContext)} has completed. 226 * @param registration the {@code DispatcherServlet} registration to be customized 227 * @see #registerDispatcherServlet(ServletContext) 228 */ 229 protected void customizeRegistration(ServletRegistration.Dynamic registration) { 230 } 231 232}