/*
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.web.servlet;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.LinkedHashSet;
import java.util.Set;
import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import javax.servlet.FilterRegistration;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.Assert;
/**
* Abstract base {@link ServletContextInitializer} to register {@link Filter}s in a
* Servlet 3.0+ container.
*
* @param <T> the type of {@link Filter} to register
* @author Phillip Webb
*/
abstract class AbstractFilterRegistrationBean<T extends Filter> extends RegistrationBean {
/**
* Filters that wrap the servlet request should be ordered less than or equal to this.
*/
protected static final int REQUEST_WRAPPER_FILTER_MAX_ORDER = 0;
private final Log logger = LogFactory.getLog(getClass());
private static final String[] DEFAULT_URL_MAPPINGS = { "/*" };
private Set<ServletRegistrationBean<?>> servletRegistrationBeans = new LinkedHashSet<>();
private Set<String> servletNames = new LinkedHashSet<>();
private Set<String> urlPatterns = new LinkedHashSet<>();
private EnumSet<DispatcherType> dispatcherTypes;
private boolean matchAfter = false;
/**
* Create a new instance to be registered with the specified
* {@link ServletRegistrationBean}s.
* @param servletRegistrationBeans associate {@link ServletRegistrationBean}s
*/
AbstractFilterRegistrationBean(
ServletRegistrationBean<?>... servletRegistrationBeans) {
Assert.notNull(servletRegistrationBeans,
"ServletRegistrationBeans must not be null");
Collections.addAll(this.servletRegistrationBeans, servletRegistrationBeans);
}
/**
* Set {@link ServletRegistrationBean}s that the filter will be registered against.
* @param servletRegistrationBeans the Servlet registration beans
*/
public void setServletRegistrationBeans(
Collection<? extends ServletRegistrationBean<?>> servletRegistrationBeans) {
Assert.notNull(servletRegistrationBeans,
"ServletRegistrationBeans must not be null");
this.servletRegistrationBeans = new LinkedHashSet<>(servletRegistrationBeans);
}
/**
* Return a mutable collection of the {@link ServletRegistrationBean} that the filter
* will be registered against. {@link ServletRegistrationBean}s.
* @return the Servlet registration beans
* @see #setServletNames
* @see #setUrlPatterns
*/
public Collection<ServletRegistrationBean<?>> getServletRegistrationBeans() {
return this.servletRegistrationBeans;
}
/**
* Add {@link ServletRegistrationBean}s for the filter.
* @param servletRegistrationBeans the servlet registration beans to add
* @see #setServletRegistrationBeans
*/
public void addServletRegistrationBeans(
ServletRegistrationBean<?>... servletRegistrationBeans) {
Assert.notNull(servletRegistrationBeans,
"ServletRegistrationBeans must not be null");
Collections.addAll(this.servletRegistrationBeans, servletRegistrationBeans);
}
/**
* Set servlet names that the filter will be registered against. This will replace any
* previously specified servlet names.
* @param servletNames the servlet names
* @see #setServletRegistrationBeans
* @see #setUrlPatterns
*/
public void setServletNames(Collection<String> servletNames) {
Assert.notNull(servletNames, "ServletNames must not be null");
this.servletNames = new LinkedHashSet<>(servletNames);
}
/**
* Return a mutable collection of servlet names that the filter will be registered
* against.
* @return the servlet names
*/
public Collection<String> getServletNames() {
return this.servletNames;
}
/**
* Add servlet names for the filter.
* @param servletNames the servlet names to add
*/
public void addServletNames(String... servletNames) {
Assert.notNull(servletNames, "ServletNames must not be null");
this.servletNames.addAll(Arrays.asList(servletNames));
}
/**
* Set the URL patterns that the filter will be registered against. This will replace
* any previously specified URL patterns.
* @param urlPatterns the URL patterns
* @see #setServletRegistrationBeans
* @see #setServletNames
*/
public void setUrlPatterns(Collection<String> urlPatterns) {
Assert.notNull(urlPatterns, "UrlPatterns must not be null");
this.urlPatterns = new LinkedHashSet<>(urlPatterns);
}
/**
* Return a mutable collection of URL patterns that the filter will be registered
* against.
* @return the URL patterns
*/
public Collection<String> getUrlPatterns() {
return this.urlPatterns;
}
/**
* Add URL patterns that the filter will be registered against.
* @param urlPatterns the URL patterns
*/
public void addUrlPatterns(String... urlPatterns) {
Assert.notNull(urlPatterns, "UrlPatterns must not be null");
Collections.addAll(this.urlPatterns, urlPatterns);
}
/**
* Convenience method to {@link #setDispatcherTypes(EnumSet) set dispatcher types}
* using the specified elements.
* @param first the first dispatcher type
* @param rest additional dispatcher types
*/
public void setDispatcherTypes(DispatcherType first, DispatcherType... rest) {
this.dispatcherTypes = EnumSet.of(first, rest);
}
/**
* Sets the dispatcher types that should be used with the registration. If not
* specified the types will be deduced based on the value of
* {@link #isAsyncSupported()}.
* @param dispatcherTypes the dispatcher types
*/
public void setDispatcherTypes(EnumSet<DispatcherType> dispatcherTypes) {
this.dispatcherTypes = dispatcherTypes;
}
/**
* Set if the filter mappings should be matched after any declared filter mappings of
* the ServletContext. Defaults to {@code false} indicating the filters are supposed
* to be matched before any declared filter mappings of the ServletContext.
* @param matchAfter if filter mappings are matched after
*/
public void setMatchAfter(boolean matchAfter) {
this.matchAfter = matchAfter;
}
/**
* Return if filter mappings should be matched after any declared Filter mappings of
* the ServletContext.
* @return if filter mappings are matched after
*/
public boolean isMatchAfter() {
return this.matchAfter;
}
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
Filter filter = getFilter();
Assert.notNull(filter, "Filter must not be null");
String name = getOrDeduceName(filter);
if (!isEnabled()) {
this.logger.info("Filter " + name + " was not registered (disabled)");
return;
}
FilterRegistration.Dynamic added = servletContext.addFilter(name, filter);
if (added == null) {
this.logger.info("Filter " + name + " was not registered "
+ "(possibly already registered?)");
return;
}
configure(added);
}
/**
* Return the {@link Filter} to be registered.
* @return the filter
*/
public abstract T getFilter();
/**
* Configure registration settings. Subclasses can override this method to perform
* additional configuration if required.
* @param registration the registration
*/
protected void configure(FilterRegistration.Dynamic registration) {
super.configure(registration);
EnumSet<DispatcherType> dispatcherTypes = this.dispatcherTypes;
if (dispatcherTypes == null) {
dispatcherTypes = EnumSet.of(DispatcherType.REQUEST);
}
Set<String> servletNames = new LinkedHashSet<>();
for (ServletRegistrationBean<?> servletRegistrationBean : this.servletRegistrationBeans) {
servletNames.add(servletRegistrationBean.getServletName());
}
servletNames.addAll(this.servletNames);
if (servletNames.isEmpty() && this.urlPatterns.isEmpty()) {
this.logger.info("Mapping filter: '" + registration.getName() + "' to: "
+ Arrays.asList(DEFAULT_URL_MAPPINGS));
registration.addMappingForUrlPatterns(dispatcherTypes, this.matchAfter,
DEFAULT_URL_MAPPINGS);
}
else {
if (!servletNames.isEmpty()) {
this.logger.info("Mapping filter: '" + registration.getName()
+ "' to servlets: " + servletNames);
registration.addMappingForServletNames(dispatcherTypes, this.matchAfter,
servletNames.toArray(new String[servletNames.size()]));
}
if (!this.urlPatterns.isEmpty()) {
this.logger.info("Mapping filter: '" + registration.getName()
+ "' to urls: " + this.urlPatterns);
registration.addMappingForUrlPatterns(dispatcherTypes, this.matchAfter,
this.urlPatterns.toArray(new String[this.urlPatterns.size()]));
}
}
}
}