001/* 002 * Copyright 2012-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 * http://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.boot.web.servlet; 018 019import java.util.Arrays; 020import java.util.Collection; 021import java.util.LinkedHashSet; 022import java.util.Set; 023 024import javax.servlet.MultipartConfigElement; 025import javax.servlet.Servlet; 026import javax.servlet.ServletContext; 027import javax.servlet.ServletRegistration; 028 029import org.springframework.util.Assert; 030import org.springframework.util.ObjectUtils; 031import org.springframework.util.StringUtils; 032 033/** 034 * A {@link ServletContextInitializer} to register {@link Servlet}s in a Servlet 3.0+ 035 * container. Similar to the {@link ServletContext#addServlet(String, Servlet) 036 * registration} features provided by {@link ServletContext} but with a Spring Bean 037 * friendly design. 038 * <p> 039 * The {@link #setServlet(Servlet) servlet} must be specified before calling 040 * {@link #onStartup}. URL mapping can be configured used {@link #setUrlMappings} or 041 * omitted when mapping to '/*' (unless 042 * {@link #ServletRegistrationBean(Servlet, boolean, String...) alwaysMapUrl} is set to 043 * {@code false}). The servlet name will be deduced if not specified. 044 * 045 * @param <T> the type of the {@link Servlet} to register 046 * @author Phillip Webb 047 * @since 1.4.0 048 * @see ServletContextInitializer 049 * @see ServletContext#addServlet(String, Servlet) 050 */ 051public class ServletRegistrationBean<T extends Servlet> 052 extends DynamicRegistrationBean<ServletRegistration.Dynamic> { 053 054 private static final String[] DEFAULT_MAPPINGS = { "/*" }; 055 056 private T servlet; 057 058 private Set<String> urlMappings = new LinkedHashSet<>(); 059 060 private boolean alwaysMapUrl = true; 061 062 private int loadOnStartup = -1; 063 064 private MultipartConfigElement multipartConfig; 065 066 /** 067 * Create a new {@link ServletRegistrationBean} instance. 068 */ 069 public ServletRegistrationBean() { 070 } 071 072 /** 073 * Create a new {@link ServletRegistrationBean} instance with the specified 074 * {@link Servlet} and URL mappings. 075 * @param servlet the servlet being mapped 076 * @param urlMappings the URLs being mapped 077 */ 078 public ServletRegistrationBean(T servlet, String... urlMappings) { 079 this(servlet, true, urlMappings); 080 } 081 082 /** 083 * Create a new {@link ServletRegistrationBean} instance with the specified 084 * {@link Servlet} and URL mappings. 085 * @param servlet the servlet being mapped 086 * @param alwaysMapUrl if omitted URL mappings should be replaced with '/*' 087 * @param urlMappings the URLs being mapped 088 */ 089 public ServletRegistrationBean(T servlet, boolean alwaysMapUrl, 090 String... urlMappings) { 091 Assert.notNull(servlet, "Servlet must not be null"); 092 Assert.notNull(urlMappings, "UrlMappings must not be null"); 093 this.servlet = servlet; 094 this.alwaysMapUrl = alwaysMapUrl; 095 this.urlMappings.addAll(Arrays.asList(urlMappings)); 096 } 097 098 /** 099 * Sets the servlet to be registered. 100 * @param servlet the servlet 101 */ 102 public void setServlet(T servlet) { 103 Assert.notNull(servlet, "Servlet must not be null"); 104 this.servlet = servlet; 105 } 106 107 /** 108 * Return the servlet being registered. 109 * @return the servlet 110 */ 111 public T getServlet() { 112 return this.servlet; 113 } 114 115 /** 116 * Set the URL mappings for the servlet. If not specified the mapping will default to 117 * '/'. This will replace any previously specified mappings. 118 * @param urlMappings the mappings to set 119 * @see #addUrlMappings(String...) 120 */ 121 public void setUrlMappings(Collection<String> urlMappings) { 122 Assert.notNull(urlMappings, "UrlMappings must not be null"); 123 this.urlMappings = new LinkedHashSet<>(urlMappings); 124 } 125 126 /** 127 * Return a mutable collection of the URL mappings, as defined in the Servlet 128 * specification, for the servlet. 129 * @return the urlMappings 130 */ 131 public Collection<String> getUrlMappings() { 132 return this.urlMappings; 133 } 134 135 /** 136 * Add URL mappings, as defined in the Servlet specification, for the servlet. 137 * @param urlMappings the mappings to add 138 * @see #setUrlMappings(Collection) 139 */ 140 public void addUrlMappings(String... urlMappings) { 141 Assert.notNull(urlMappings, "UrlMappings must not be null"); 142 this.urlMappings.addAll(Arrays.asList(urlMappings)); 143 } 144 145 /** 146 * Sets the {@code loadOnStartup} priority. See 147 * {@link ServletRegistration.Dynamic#setLoadOnStartup} for details. 148 * @param loadOnStartup if load on startup is enabled 149 */ 150 public void setLoadOnStartup(int loadOnStartup) { 151 this.loadOnStartup = loadOnStartup; 152 } 153 154 /** 155 * Set the {@link MultipartConfigElement multi-part configuration}. 156 * @param multipartConfig the multi-part configuration to set or {@code null} 157 */ 158 public void setMultipartConfig(MultipartConfigElement multipartConfig) { 159 this.multipartConfig = multipartConfig; 160 } 161 162 /** 163 * Returns the {@link MultipartConfigElement multi-part configuration} to be applied 164 * or {@code null}. 165 * @return the multipart config 166 */ 167 public MultipartConfigElement getMultipartConfig() { 168 return this.multipartConfig; 169 } 170 171 @Override 172 protected String getDescription() { 173 Assert.notNull(this.servlet, "Servlet must not be null"); 174 return "servlet " + getServletName(); 175 } 176 177 @Override 178 protected ServletRegistration.Dynamic addRegistration(String description, 179 ServletContext servletContext) { 180 String name = getServletName(); 181 return servletContext.addServlet(name, this.servlet); 182 } 183 184 /** 185 * Configure registration settings. Subclasses can override this method to perform 186 * additional configuration if required. 187 * @param registration the registration 188 */ 189 @Override 190 protected void configure(ServletRegistration.Dynamic registration) { 191 super.configure(registration); 192 String[] urlMapping = StringUtils.toStringArray(this.urlMappings); 193 if (urlMapping.length == 0 && this.alwaysMapUrl) { 194 urlMapping = DEFAULT_MAPPINGS; 195 } 196 if (!ObjectUtils.isEmpty(urlMapping)) { 197 registration.addMapping(urlMapping); 198 } 199 registration.setLoadOnStartup(this.loadOnStartup); 200 if (this.multipartConfig != null) { 201 registration.setMultipartConfig(this.multipartConfig); 202 } 203 } 204 205 /** 206 * Returns the servlet name that will be registered. 207 * @return the servlet name 208 */ 209 public String getServletName() { 210 return getOrDeduceName(this.servlet); 211 } 212 213 @Override 214 public String toString() { 215 return getServletName() + " urls=" + getUrlMappings(); 216 } 217 218}