WebInitializer.java

/*
 * Copyright 2017 Global Crop Diversity Trust
 *
 * 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.genesys.spring.web;

import java.util.EnumSet;

import javax.annotation.Priority;
import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import javax.servlet.FilterRegistration;
import javax.servlet.MultipartConfigElement;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;

import org.genesys.server.servlet.filter.LocaleURLFilter;
import org.genesys.server.servlet.filter.SuppressRequestRejectedExceptionFilter;
import org.genesys.spring.config.ApplicationConfig;
import org.sitemesh.builder.SiteMeshFilterBuilder;
import org.sitemesh.config.ConfigurableSiteMeshFilter;
import org.sitemesh.webapp.contentfilter.BasicSelector;
import org.springframework.core.annotation.Order;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.filter.CommonsRequestLoggingFilter;
import org.springframework.web.filter.DelegatingFilterProxy;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import org.tuckey.web.filters.urlrewrite.UrlRewriteFilter;

//import com.hazelcast.web.SessionListener;

/**
 * Boots up Spring MVC and registers additional servlet filters
 */
@Order(1)
@Priority(0)
public class WebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

	private static final long MAX_FILE_SIZE = 1024 * 1024 * 500; // 500M
	private static final long MAX_REQUEST_SIZE = MAX_FILE_SIZE * 2;
	private static final int FILE_SIZE_THRESHOLD = 1024 * 1024 * 1; // 1M;

	@Override
	protected Class<?>[] getRootConfigClasses() {
		return null;
	}

	@Override
	protected Class<?>[] getServletConfigClasses() {
		return new Class<?>[] { ApplicationConfig.class };
	}

	@Override
	protected String[] getServletMappings() {
		return new String[] { "/" };
	}

	@Override
	protected Filter[] getServletFilters() {
		final CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
		characterEncodingFilter.setEncoding("UTF-8");
		return new Filter[] { characterEncodingFilter };
	}

	@Override
	protected DispatcherServlet createDispatcherServlet(final WebApplicationContext servletAppContext) {
		final DispatcherServlet dispatcherServlet = (DispatcherServlet) super.createDispatcherServlet(servletAppContext);
		dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
		return dispatcherServlet;
	}

	@Override
	public void onStartup(final ServletContext servletContext) throws ServletException {
		super.onStartup(servletContext);
//		servletContext.setSessionTrackingModes(EnumSet.of(SessionTrackingMode.COOKIE));

		registerFilters(servletContext);

//		 Hazelcast session listener
//		servletContext.addListener(new SessionListener());
	}

	public CommonsRequestLoggingFilter requestLoggingFilter() {
		CommonsRequestLoggingFilter loggingFilter = new CommonsRequestLoggingFilter();
		loggingFilter.setIncludeClientInfo(true);
		loggingFilter.setIncludeQueryString(true);
		loggingFilter.setIncludePayload(true);
		loggingFilter.setMaxPayloadLength(100);
		loggingFilter.setBeforeMessagePrefix("START ");
		loggingFilter.setAfterMessagePrefix("DONE ");
		return loggingFilter;
	}

	private void registerFilters(final ServletContext servletContext) {
		final FilterRegistration.Dynamic loggingFilter = servletContext.addFilter("requestLoggingFilter", requestLoggingFilter());
		loggingFilter.addMappingForUrlPatterns(null, false, "/*");

		final FilterRegistration.Dynamic customLogFilter = servletContext.addFilter("suppressRequestRejectedExceptionFilter", SuppressRequestRejectedExceptionFilter.class);
		customLogFilter.addMappingForUrlPatterns(null, false, "/*");

		// UrlRewrite filter configuration
		final FilterRegistration.Dynamic urlRewriteFilter = servletContext.addFilter("urlRewriteFilter", UrlRewriteFilter.class);
		urlRewriteFilter.setInitParameter("confReloadCheckInterval", "10");
		// urlRewriteFilter.setInitParameter("logLevel", "sysout:DEBUG");
		urlRewriteFilter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD), false, "/*");

		// Encoding filter configuration
		final FilterRegistration.Dynamic encodingFilter = servletContext.addFilter("encodingFilter", CharacterEncodingFilter.class);
		encodingFilter.setInitParameter("encoding", "UTF-8");
		encodingFilter.setInitParameter("forceEncoding", "true");
		encodingFilter.addMappingForUrlPatterns(null, false, "/*");

		// Locale URL filter configuration
		final FilterRegistration.Dynamic localeURLFilter = servletContext.addFilter("localeURLFilter", LocaleURLFilter.class);
		localeURLFilter.setInitParameter("defaultLocale", "en");
		localeURLFilter.setInitParameter("allowedLocales", "en,es,de,fr,fa,ar,ru,zh,pt,cs");
		localeURLFilter.setInitParameter("excludePaths", "/html,/login-attempt");
		localeURLFilter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST, DispatcherType.INCLUDE, DispatcherType.FORWARD), false, "/*");

		// Then the spring sessions
		final DelegatingFilterProxy springSessionFilter = new DelegatingFilterProxy("springSessionRepositoryFilter");
		springSessionFilter.setContextAttribute("org.springframework.web.servlet.FrameworkServlet.CONTEXT.dispatcher");
		servletContext.addFilter("springSessionRepositoryFilter", springSessionFilter).addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST, DispatcherType.ERROR, DispatcherType.ASYNC), false, "/*");

		// Then the spring security
		final DelegatingFilterProxy filter = new DelegatingFilterProxy("springSecurityFilterChain");
		filter.setContextAttribute("org.springframework.web.servlet.FrameworkServlet.CONTEXT.dispatcher");
		servletContext.addFilter("springSecurityFilterChain", filter).addMappingForUrlPatterns(null, false, "/*");

		// http://stackoverflow.com/a/22409634
		final ConfigurableSiteMeshFilter sitemeshWithErrors = new ConfigurableSiteMeshFilter() {
			@Override
			protected void applyCustomConfiguration(final SiteMeshFilterBuilder builder) {
				builder.setCustomSelector(new BasicSelector(true, "text/html"));
			}
		};

		// Sitemesh filter configuration
		final FilterRegistration.Dynamic sitemeshFilter = servletContext.addFilter("sitemesh", sitemeshWithErrors);
		sitemeshFilter.addMappingForUrlPatterns(null, false, "/*");

		// GZip filter configuration
//		final FilterRegistration.Dynamic gZIPFilter = servletContext.addFilter("gZIPFilter", GzipFilter.class); // Never enable gzipFilter!
	}

	@Override
	protected void customizeRegistration(final ServletRegistration.Dynamic registration) {
		registration.setMultipartConfig(getMultipartConfigElement());
	}

	private MultipartConfigElement getMultipartConfigElement() {
		final MultipartConfigElement multipartConfigElement = new MultipartConfigElement("", MAX_FILE_SIZE, MAX_REQUEST_SIZE, FILE_SIZE_THRESHOLD);
		return multipartConfigElement;
	}
}