1

i am using spring boot version 1.3.1 and i want to add tomcat session replication in my application for embedded tomcat. I have google a lot but nowhere i got solution. I have generated my spring boot application using jhipster and my generated code of WebConfigurer file is:

import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.servlet.InstrumentedFilter;
import com.codahale.metrics.servlets.MetricsServlet;
import in.megacabs.web.filter.CachingHttpHeadersFilter;
import in.megacabs.web.filter.StaticResourcesProductionFilter;
import in.megacabs.web.rest.filter.megacabs.ServerModifiedFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.boot.context.embedded.MimeMappings;
import org.springframework.boot.context.embedded.ServletContextInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

import java.util.*;
import javax.inject.Inject;
import javax.servlet.*;

/**
 * Configuration of web application with Servlet 3.0 APIs.
 */
@Configuration
@AutoConfigureAfter(CacheConfiguration.class)
public class WebConfigurer extends WebMvcConfigurerAdapter implements ServletContextInitializer, EmbeddedServletContainerCustomizer {

    private final Logger log = LoggerFactory.getLogger(WebConfigurer.class);

    @Inject
    private Environment env;

    @Inject
    private JHipsterProperties props;

    @Autowired(required = false)
    private MetricRegistry metricRegistry;

    @Inject
    private JasperReportConfiguration reportConfiguration;

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        super.addResourceHandlers(registry);
        registry.addResourceHandler(reportConfiguration.getUploadUrl()+"*//**").addResourceLocations("file://"+ reportConfiguration.getUploadPath()+"/");
    }

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        log.info("Web application configuration, using profiles: {}", Arrays.toString(env.getActiveProfiles()));
        EnumSet<DispatcherType> disps = EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.ASYNC);
        if (!env.acceptsProfiles(Constants.SPRING_PROFILE_FAST)) {
            initMetrics(servletContext, disps);
        }
        if (env.acceptsProfiles(Constants.SPRING_PROFILE_PRODUCTION)) {
            initCachingHttpHeadersFilter(servletContext, disps);
            initStaticResourcesProductionFilter(servletContext, disps);
        }
        initServerModifiedFilter(servletContext, disps);

        log.info("Web application fully configured");
    }

    /**
     * Set up Mime types.
     */
    @Override
    public void customize(ConfigurableEmbeddedServletContainer container) {
        MimeMappings mappings = new MimeMappings(MimeMappings.DEFAULT);
        // IE issue, see https://github.com/jhipster/generator-jhipster/pull/711
        mappings.add("html", "text/html;charset=utf-8");
        // CloudFoundry issue, see https://github.com/cloudfoundry/gorouter/issues/64
        mappings.add("json", "text/html;charset=utf-8");
        container.setMimeMappings(mappings);
    }

    /**
     * Initializes the static resources production Filter.
     */
    private void initStaticResourcesProductionFilter(ServletContext servletContext,
                                                     EnumSet<DispatcherType> disps) {

        log.debug("Registering static resources production Filter");
        FilterRegistration.Dynamic staticResourcesProductionFilter =
            servletContext.addFilter("staticResourcesProductionFilter",
                new StaticResourcesProductionFilter());

        staticResourcesProductionFilter.addMappingForUrlPatterns(disps, true, "/");
        staticResourcesProductionFilter.addMappingForUrlPatterns(disps, true, "/index.html");
        staticResourcesProductionFilter.addMappingForUrlPatterns(disps, true, "/assets/*");
        staticResourcesProductionFilter.addMappingForUrlPatterns(disps, true, "/scripts/*");
        staticResourcesProductionFilter.setAsyncSupported(true);
    }

    /**
     * Initializes the caching HTTP Headers Filter.
     */
    private void initCachingHttpHeadersFilter(ServletContext servletContext,
                                              EnumSet<DispatcherType> disps) {
        log.debug("Registering Caching HTTP Headers Filter");
        FilterRegistration.Dynamic cachingHttpHeadersFilter =
            servletContext.addFilter("cachingHttpHeadersFilter",
                new CachingHttpHeadersFilter(env));

        cachingHttpHeadersFilter.addMappingForUrlPatterns(disps, true, "/dist/assets/*");
        cachingHttpHeadersFilter.addMappingForUrlPatterns(disps, true, "/dist/scripts/*");
        cachingHttpHeadersFilter.setAsyncSupported(true);
    }

    /**
     * Initializes Metrics.
     */
    private void initMetrics(ServletContext servletContext, EnumSet<DispatcherType> disps) {
        log.debug("Initializing Metrics registries");
        servletContext.setAttribute(InstrumentedFilter.REGISTRY_ATTRIBUTE,
            metricRegistry);
        servletContext.setAttribute(MetricsServlet.METRICS_REGISTRY,
            metricRegistry);

        log.debug("Registering Metrics Filter");
        FilterRegistration.Dynamic metricsFilter = servletContext.addFilter("webappMetricsFilter",
            new InstrumentedFilter());

        metricsFilter.addMappingForUrlPatterns(disps, true, "/*");
        metricsFilter.setAsyncSupported(true);

        log.debug("Registering Metrics Servlet");
        ServletRegistration.Dynamic metricsAdminServlet =
            servletContext.addServlet("metricsServlet", new MetricsServlet());

        metricsAdminServlet.addMapping("/metrics/metrics/*");
        metricsAdminServlet.setAsyncSupported(true);
        metricsAdminServlet.setLoadOnStartup(2);
    }

    /**
     * Initializes the serverModified Filter.
     */
    private void initServerModifiedFilter(ServletContext servletContext,
                                          EnumSet<DispatcherType> disps) {
        log.debug("Registering static resources production Filter");
        FilterRegistration.Dynamic serverModifiedFilter =
            servletContext.addFilter("serverModifiedFilter",
                new ServerModifiedFilter());

        serverModifiedFilter.addMappingForUrlPatterns(disps, true, "/api/*");
        serverModifiedFilter.setAsyncSupported(true);
    }

    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = props.getCors();
        if (config.getAllowedOrigins() != null && !config.getAllowedOrigins().isEmpty()) {
            source.registerCorsConfiguration("/api/**", config);
            source.registerCorsConfiguration("/v2/api-docs", config);
            source.registerCorsConfiguration("/oauth/**", config);
        }
        return new CorsFilter(source);
    }

}

I have added above code to just give you clue that what i am doing in my web configuration file. I have found this link How to setup a Spring Boot application with embedded tomcat session clustering? but it does not help me because some of the classes was not in my classpath for embedded tomcat.

Please help me if anyone setup tomcat session replication before.

Community
  • 1
  • 1
Qasim
  • 9,058
  • 8
  • 36
  • 50

1 Answers1

0

We use Hazelcast to session replication/clustering within our enterprise applications.

You need to add the relevant dependencies to your build.gradle file:

dependencies {
    //Main dependencies
    compile 
        'com.hazelcast:hazelcast:3.3.5',
        'com.hazelcast:hazelcast-wm:3.3.5',
        'com.hazelcast:hazelcast-spring:3.3.5'
}

We then create a Hazelcast configuration class:

import com.hazelcast.config.Config
import com.hazelcast.config.JoinConfig
import com.hazelcast.config.MulticastConfig
import com.hazelcast.config.NetworkConfig
import com.hazelcast.core.Hazelcast
import com.hazelcast.core.HazelcastInstance
import com.hazelcast.web.SessionListener
import com.hazelcast.web.WebFilter
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.boot.context.embedded.FilterRegistrationBean
import org.springframework.boot.context.embedded.ServletListenerRegistrationBean
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.DependsOn

import javax.servlet.DispatcherType

@Configuration
@ConfigurationProperties(prefix="hzl")
@ConditionalOnProperty(value= "hzl.enabled")
class HazelcastConfig {
    String address
    Integer port
    Integer multicastPort
    String instanceName
    Integer multicastTimeoutSeconds

    @Bean
    public hazelcastInstance(){
        Config config = new Config()
        config.instanceName = instanceName

        config.setProperty("hazelcast.jmx", "true")

        NetworkConfig network = config.networkConfig
        network.port = port ?: network.port

        JoinConfig join = network.join
        join.tcpIpConfig.enabled = false
        join.awsConfig.enabled = false

        MulticastConfig multicastConfig = join.multicastConfig
        multicastConfig.enabled = true
        multicastConfig.multicastGroup = address

        multicastConfig.multicastPort = multicastPort ?: multicastConfig.multicastPort
        multicastConfig.multicastTimeoutSeconds = multicastTimeoutSeconds ?: multicastConfig.multicastTimeoutSeconds

        HazelcastInstance instance = Hazelcast.newHazelcastInstance(config)
        instance
    }

    @Bean
    @DependsOn(["hazelcastInstance"])
    public FilterRegistrationBean hazelcastFilter() {
        FilterRegistrationBean registration = new FilterRegistrationBean()
        WebFilter filter = new WebFilter()

        registration.filter = filter
        registration.order = 0
        registration.name = "hazelcastFilter"
        registration.addUrlPatterns("/*")
        registration.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.INCLUDE)

        registration.addInitParameter("instance-name", instanceName)
//        registration.addInitParameter("session-ttl-seconds", "3600")
        registration.addInitParameter("sticky-session", "false")
        registration.addInitParameter("deferred-write", "false")
        registration.addInitParameter("sticky-session", "true")

        registration
    }

    @Bean
    public ServletListenerRegistrationBean hazelcastSessionListener() {
        return new ServletListenerRegistrationBean(new SessionListener());
    }
}

Which depends on specific properties prefixed with hzl. in the application.properties:

#Hazelcast Session Replication
hzl.enabled=true
hzl.address=224.2.2.110
hzl.multicastTimeoutSeconds=60

And that should do it. Now Hazelcast will replicate/copy all sessions to each server in a cluster that is broadcast and listening to the IP address 224.2.2.110.

For more information on Hazelcast see the documentation at Hazelcast Download and Documentation for Java

pczeus
  • 7,709
  • 4
  • 36
  • 51
  • 1
    Thanks for your reply but I need solution without hazelcast as we do not want to use hazelcast. Any one knows about tomcat session replication with spring boot configuring tomcat only then please help – Qasim Sep 27 '16 at 07:52