WebConfig.java

package org.xandercat.pmdb.config;

import java.util.List;
import java.util.Locale;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.multipart.support.StandardServletMultipartResolver;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import org.springframework.web.servlet.i18n.SessionLocaleResolver;
import org.thymeleaf.extras.springsecurity5.dialect.SpringSecurityDialect;
import org.thymeleaf.spring5.SpringTemplateEngine;
import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver;
import org.thymeleaf.spring5.view.ThymeleafViewResolver;
import org.thymeleaf.templatemode.TemplateMode;

import com.fasterxml.jackson.databind.ObjectMapper;

import nz.net.ultraq.thymeleaf.LayoutDialect;

/**
 * Application web configuration.
 * 
 * @author Scott Arnold
 */
@EnableWebMvc
@Configuration
@ComponentScan(basePackages={"org.xandercat.pmdb"})
public class WebConfig implements WebMvcConfigurer {

	private static final Logger LOGGER = LogManager.getLogger(WebConfig.class);
	
	@Autowired
	private ApplicationContext applicationContext;
	
	/**
	 * LocaleResolver for i18n support.
	 * 
	 * @return LocaleResolver
	 */
	@Bean
	public LocaleResolver localeResolver() {
		SessionLocaleResolver localeResolver = new SessionLocaleResolver();
		localeResolver.setDefaultLocale(Locale.US);
		return localeResolver;
	}
	
	/**
	 * Interceptor for locale change and i18n support.  Defines parameter that can be used to specify what language to use.
	 * 
	 * @return interceptor for locale change
	 */
	@Bean
	public LocaleChangeInterceptor localeChangeInterceptor() {
		LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
		localeChangeInterceptor.setParamName("lang");
		return localeChangeInterceptor;
	}
	
	/**
	 * Source for application messages for i18n support.
	 * 
	 * @return message source for application messages
	 */
	@Bean
	public MessageSource messageSource() {
		ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
		messageSource.setBasename("classpath:messages");
		messageSource.setDefaultEncoding("UTF-8");
		messageSource.setUseCodeAsDefaultMessage(true); // useful for test / debugging
		return messageSource;
	}
	
	/**
	 * Template resolver for view.  Constructs Thymeleaf resolver, defines where view files are located,
	 * and what extension they use.
	 * 
	 * @return template resolver
	 */
	@Bean
	public SpringResourceTemplateResolver templateResolver() {
		LOGGER.debug("WebConfig templateResolver bean creation called");
		SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
		templateResolver.setApplicationContext(applicationContext);
		templateResolver.setTemplateMode(TemplateMode.HTML); // should be default; just being more explicit here
		templateResolver.setPrefix("/WEB-INF/view/");
		templateResolver.setSuffix(".html");
		return templateResolver;
	}
	
	/**
	 * Template engine for view.  Constructs and configures Thymeleaf template engine.
	 * 
	 * @param templateResolver Thymeleaf template resolver
	 * @return template engine
	 */
	@Bean
	public SpringTemplateEngine templateEngine(SpringResourceTemplateResolver templateResolver) {
		LOGGER.debug("WebConfig templateEngine bean creation called");
		SpringTemplateEngine templateEngine = new SpringTemplateEngine();
		templateEngine.setTemplateResolver(templateResolver);
		templateEngine.addDialect(new LayoutDialect()); // for Thymeleaf hierarchical layout
		templateEngine.addDialect(new SpringSecurityDialect());  // for Thymeleaf security extras
		templateEngine.setEnableSpringELCompiler(true);
		return templateEngine;
	}
	
	/**
	 * Bean for handling file uploads.
	 * 
	 * @return multipart resolver
	 */
	@Bean
	public MultipartResolver multipartResolver() {
		return new StandardServletMultipartResolver(); // for file upload support
	}
	
	@Override
	public void addViewControllers(ViewControllerRegistry registry) {
		LOGGER.debug("WebConfig addViewControllers called");
		//registry.addViewController("/").setViewName("home");  // can essentially set up noop controllers with this if desired
	}

	@Override
	public void configureViewResolvers(ViewResolverRegistry registry) {
		LOGGER.debug("WebConfig configureViewResolvers called");
		ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
		SpringTemplateEngine templateEngine = applicationContext.getBean(SpringTemplateEngine.class);
		if (templateEngine == null) {
			LOGGER.warn("TemplateEngine not found in applicationContext");
		}
		viewResolver.setTemplateEngine(templateEngine);
		registry.viewResolver(viewResolver);
	}
	
	@Override
	public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
		ObjectMapper objectMapper = new ObjectMapper();
		MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(objectMapper); // for JSON responses
		converters.add(converter);
	}
	
	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		LocaleChangeInterceptor localeChangeInterceptor = applicationContext.getBean(LocaleChangeInterceptor.class);
		registry.addInterceptor(localeChangeInterceptor);
	}

	@Override
	public void addResourceHandlers(ResourceHandlerRegistry registry) {
		registry
			.addResourceHandler("/css/**", "/js/**", "/fonts/**")
			.addResourceLocations("/WEB-INF/css/", "/WEB-INF/js/", "/WEB-INF/fonts/");
	}
	
}