package fr.ippon.tatami.web.init; import com.yammer.metrics.reporting.AdminServlet; import com.yammer.metrics.web.DefaultWebappMetricsFilter; import fr.ippon.tatami.config.ApplicationConfiguration; import fr.ippon.tatami.config.Constants; import fr.ippon.tatami.config.DispatcherServletConfig; import org.atmosphere.cache.UUIDBroadcasterCache; import org.atmosphere.cpr.AtmosphereServlet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.env.Environment; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; import org.springframework.web.filter.DelegatingFilterProxy; import org.springframework.web.servlet.DispatcherServlet; import javax.servlet.*; import java.util.EnumSet; /** * Configuration of web application with Servlet 3.0 APIs.<br> * <p/> * This class is to be used as a standard listener within a web.xml. * To optimise startup time, you can completely disable classpath scanning : * <ul> * <li>with metadata-complete="true" global attribute * <li>with an empty <absolute-ordering> ( it's necessary with Jetty at least <b>TODO</b> : test with other * containers ) * </ul> * <p/> * See web.xml : * <p/> * <pre> * <web-app xmlns="http://java.sun.com/xml/ns/javaee" * xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" * xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" * version="3.0" * metadata-complete="true"> * * <!-- * Remove classpath scanning (from servlet 3.0) in order to speed jetty startup : * metadata-complete="true" above + empty absolute ordering below * --> * <absolute-ordering> * <!-- * Empty absolute ordering is necessary to completely desactivate classpath scanning * --> * </absolute-ordering> * * <display-name>Tatami</display-name> * * <!-- All the Servlets and Filters are configured by this ServletContextListener : --> * <listener> * <listener-class>fr.ippon.tatami.web.init.WebConfigurer</listener-class> * </listener> * * </web-app> * </pre> * * @author Fabien Arrault */ public class WebConfigurer implements ServletContextListener { private final Logger log = LoggerFactory.getLogger(WebConfigurer.class); @Override public void contextInitialized(ServletContextEvent sce) { ServletContext servletContext = sce.getServletContext(); log.info("Web application configuration"); log.debug("Configuring Spring root application context"); AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext(); rootContext.register(ApplicationConfiguration.class); rootContext.refresh(); servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, rootContext); EnumSet<DispatcherType> disps = EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.ASYNC); log.debug("Configuring Spring Web application context"); AnnotationConfigWebApplicationContext dispatcherServletConfig = new AnnotationConfigWebApplicationContext(); dispatcherServletConfig.setParent(rootContext); dispatcherServletConfig.register(DispatcherServletConfig.class); log.debug("Registering Spring MVC Servlet"); ServletRegistration.Dynamic dispatcherServlet = servletContext.addServlet("dispatcher", new DispatcherServlet( dispatcherServletConfig)); dispatcherServlet.addMapping("/tatami/*"); dispatcherServlet.setLoadOnStartup(1); log.debug("Registering Spring Security Filter"); FilterRegistration.Dynamic springSecurityFilter = servletContext.addFilter("springSecurityFilterChain", new DelegatingFilterProxy()); springSecurityFilter.setAsyncSupported(true); initAtmosphereServlet(servletContext); Environment env = rootContext.getBean(Environment.class); if (env.acceptsProfiles(Constants.SPRING_PROFILE_METRICS)) { initMetricsServlet(servletContext, disps, dispatcherServlet); springSecurityFilter.addMappingForServletNames(disps, true, "dispatcher", "atmosphereServlet", "metricsAdminServlet"); } else { springSecurityFilter.addMappingForServletNames(disps, true, "dispatcher", "atmosphereServlet"); } log.debug("Web application fully configured"); } private void initMetricsServlet(ServletContext servletContext, EnumSet<DispatcherType> disps, ServletRegistration.Dynamic dispatcherServlet) { log.debug("Setting Metrics profile for the Web ApplicationContext"); log.debug("Registering Metrics Filter"); FilterRegistration.Dynamic metricsFilter = servletContext.addFilter("webappMetricsFilter", new DefaultWebappMetricsFilter()); metricsFilter.addMappingForUrlPatterns(disps, true, "/*"); log.debug("Registering Metrics Admin Servlet"); ServletRegistration.Dynamic metricsAdminServlet = servletContext.addServlet("metricsAdminServlet", new AdminServlet()); metricsAdminServlet.addMapping("/metrics/*"); dispatcherServlet.setLoadOnStartup(2); } private void initAtmosphereServlet(ServletContext servletContext) { log.debug("Registering Atmosphere Servlet"); ServletRegistration.Dynamic atmosphereServlet = servletContext.addServlet("atmosphereServlet", new AtmosphereServlet()); atmosphereServlet.setAsyncSupported(true); atmosphereServlet.setInitParameter("org.atmosphere.cpr.packages", "fr.ippon.tatami.web.atmosphere"); atmosphereServlet.setInitParameter("org.atmosphere.cpr.broadcasterCacheClass", UUIDBroadcasterCache.class.getName()); atmosphereServlet.setInitParameter("org.atmosphere.cpr.broadcaster.shareableThreadPool", "true"); atmosphereServlet.setInitParameter("org.atmosphere.cpr.broadcaster.maxProcessingThreads", "10"); atmosphereServlet.setInitParameter("org.atmosphere.cpr.broadcaster.maxAsyncWriteThreads", "10"); atmosphereServlet.setLoadOnStartup(3); atmosphereServlet.addMapping("/realtime/*"); } @Override public void contextDestroyed(ServletContextEvent sce) { log.info("Destroying Web application"); WebApplicationContext ac = WebApplicationContextUtils.getRequiredWebApplicationContext(sce.getServletContext()); AnnotationConfigWebApplicationContext gwac = (AnnotationConfigWebApplicationContext) ac; gwac.close(); log.debug("Web application destroyed"); } }