/*
* Copyright (c) 2015. Bearchoke
*/
package com.bearchoke.platform.server.common;
import com.bearchoke.platform.server.common.web.config.WebMvcConfig;
import com.bearchoke.platform.server.common.web.filter.JsonHttpRequestFilter;
import com.bearchoke.platform.server.common.web.filter.SimpleCORSFilter;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.Cloud;
import org.springframework.cloud.CloudException;
import org.springframework.cloud.CloudFactory;
import org.springframework.cloud.service.ServiceInfo;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.request.RequestContextListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.filter.DelegatingFilterProxy;
import org.springframework.web.filter.HiddenHttpMethodFilter;
import org.springframework.web.filter.HttpPutFormContentFilter;
import org.springframework.web.filter.ShallowEtagHeaderFilter;
import org.springframework.web.servlet.DispatcherServlet;
import javax.servlet.DispatcherType;
import javax.servlet.MultipartConfigElement;
import javax.servlet.ServletContext;
import javax.servlet.ServletRegistration;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
/**
* Created by Bjorn Harvold
* Date: 8/19/15
* Time: 15:32
* Responsibility:
*/
@Log4j2
public abstract class AbstractWebApplicationInitializer {
private static final String SPRING_PROFILES_ACTIVE = "spring.profiles.active";
private static final String LOCAL = "local";
private static final String CLOUD = "cloud";
private static final String MONGODB_LOCAL = "mongodb-local";
private static final String MONGODB_CLOUD = "mongodb-cloud";
private static final String JPA = "jpa";
private static final String JPA_LOCAL = "jpa-local";
private static final String JPA_CLOUD = "jpa-cloud";
private static final String REDIS_LOCAL = "redis-local";
private static final String REDIS_CLOUD = "redis-cloud";
private static final String RABBIT_LOCAL = "rabbit-local";
private static final String RABBIT_CLOUD = "rabbit-cloud";
private static final String ELASTICSEARCH_CLOUD = "elasticsearch-cloud";
private static final String ELASTICSEARCH_LOCAL = "elasticsearch-local";
private static final String FILTER_MAPPING = "/*";
private static final String REDIS_SERVICE_ID = "traveliko-redis";
private static final String RABBIT_SERVICE_ID = "traveliko-rabbit";
private static final String MONGODB_SERVICE_ID = "traveliko-mongodb";
private static final String JPA_SERVICE_ID = "traveliko-jpa";
private static final String ELASTICSEARCH_SERVICE_ID = "traveliko-elasticsearch";
protected void createWebApplicationContext(ServletContext servletContext, Class clazz) {
log.info("Creating Web Application Context started");
List<Class> configClasses = new ArrayList<>();
configClasses.add(clazz);
// let's determine if this is a cloud based server
Cloud cloud = getCloud();
String activeProfiles = System.getProperty(SPRING_PROFILES_ACTIVE);
if (StringUtils.isEmpty(activeProfiles)) {
if (cloud == null) {
// if no active profiles are specified, we default to these profiles
activeProfiles = String.format("%s,%s,%s,%s,%s", MONGODB_LOCAL,REDIS_LOCAL,RABBIT_LOCAL,ELASTICSEARCH_LOCAL,LOCAL);
} else {
activeProfiles = String.format("%s,%s,%s,%s,%s", MONGODB_CLOUD,REDIS_CLOUD,RABBIT_CLOUD,ELASTICSEARCH_CLOUD,CLOUD);
}
}
log.info("Active spring profiles: " + activeProfiles);
// load local or cloud based configs
if (cloud != null) {
// list available service - fail servlet initializing if we are missing one that we require below
printAvailableCloudServices(cloud.getServiceInfos());
}
AnnotationConfigWebApplicationContext appContext = new AnnotationConfigWebApplicationContext();
appContext.register(configClasses.toArray(new Class[configClasses.size()]));
servletContext.addListener(new ContextLoaderListener(appContext));
servletContext.addListener(new RequestContextListener());
// log.info("Creating Web Application Context completed");
}
protected void createSpringServlet(ServletContext servletContext) {
log.info("Creating Spring Servlet started....");
AnnotationConfigWebApplicationContext appContext = new AnnotationConfigWebApplicationContext();
appContext.register(
WebMvcConfig.class
);
DispatcherServlet sc = new DispatcherServlet(appContext);
ServletRegistration.Dynamic appServlet = servletContext.addServlet("DispatcherServlet", sc);
appServlet.setLoadOnStartup(1);
appServlet.addMapping("/");
// for serving up asynchronous events in tomcat
appServlet.setInitParameter("dispatchOptionsRequest", "true");
appServlet.setAsyncSupported(true);
// enable multipart file upload support
// file size limit is 10Mb
// max file request size = 20Mb
appServlet.setMultipartConfig(new MultipartConfigElement("/tmp", 10000000l, 20000000l, 0));
// log.info("Creating Spring Servlet completed");
}
protected void createFilters(ServletContext ctx) {
CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("UTF-8");
SimpleCORSFilter corsFilter = new SimpleCORSFilter();
corsFilter.setCorsAllowCredentials("true");
corsFilter.setCorsAllowMethods("GET, POST, PUT, PATCH, DELETE, OPTIONS");
corsFilter.setCorsAllowHeaders("content-type, x-requested-with, origin, accept, authorization, username, password, x-app-type, x-app-version, x-auth-token, soapaction");
corsFilter.setCorsExposeHeaders("content-type, cookie, x-requested-with, origin, accept, username, password, x-app-type, x-app-version, x-auth-token, soapaction");
corsFilter.setCorsMaxAge("3600");
ctx.addFilter("springSessionRepositoryFilter", DelegatingFilterProxy.class).addMappingForUrlPatterns(
EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD), false, FILTER_MAPPING);
ctx.addFilter("simpleCORSFilter", DelegatingFilterProxy.class).addMappingForUrlPatterns(
EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD), false, FILTER_MAPPING);
// ctx.addFilter("SimpleCorsFilter", corsFilter).addMappingForUrlPatterns(
// EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD), false, FILTER_MAPPING);
ctx.addFilter("JsonHttpRequestFilter", new JsonHttpRequestFilter()).addMappingForUrlPatterns(
EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD), false, "/api/authenticate");
ctx.addFilter("springSecurityFilterChain", DelegatingFilterProxy.class).addMappingForUrlPatterns(
EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD), false, FILTER_MAPPING);
ctx.addFilter("CharacterEncodingFilter", characterEncodingFilter).addMappingForUrlPatterns(
EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD), false, FILTER_MAPPING);
ctx.addFilter("HiddenHttpMethodFilter", new HiddenHttpMethodFilter()).addMappingForUrlPatterns(
EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD), false, FILTER_MAPPING);
ctx.addFilter("HttpPutFormContentFilter", new HttpPutFormContentFilter()).addMappingForUrlPatterns(
EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD), false, FILTER_MAPPING);
ctx.addFilter("ShallowEtagHeaderFilter", new ShallowEtagHeaderFilter()).addMappingForUrlPatterns(
EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD), false, FILTER_MAPPING);
}
protected void printAvailableCloudServices(List<ServiceInfo> serviceInfos) {
if (serviceInfos != null && serviceInfos.size() > 0) {
log.info("Available Cloud Foundry Services are:");
for (ServiceInfo si : serviceInfos) {
log.info("Service ID: " + si.getId());
}
}
}
protected boolean isCloudServiceAvailable(Cloud cloud, String id) {
if (cloud.getServiceInfo(id) != null) {
return true;
} else {
String error = "Required cloud service: " + id + " not available";
log.error(error);
throw new RuntimeException(error);
}
}
protected Cloud getCloud() {
try {
CloudFactory cloudFactory = new CloudFactory();
return cloudFactory.getCloud();
} catch (CloudException ce) {
return null;
}
}
}