title: How to setup a Spring Boot application with embedded tomcat session clustering?
body: <p>I want to setup a Spring Boot application with embedded tomcat session clustering.</p>
  
  <p>Since embedded tomcat does not have a <code>server.xml</code> file, I've created a TomcatEmbeddedServletContainerFactory and programmatically setup the cluster configuration. The code is as follows:</p>
  
  <pre><code>@Configuration
  public class TomcatConfig
  {
      @Bean
      public EmbeddedServletContainerFactory servletContainerFactory()
      {
          return new TomcatEmbeddedServletContainerFactory()
          {
              @Override
              protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(
                  Tomcat tomcat)
              {
                  configureCluster(tomcat);
                  return super.getTomcatEmbeddedServletContainer(tomcat);
              }
  
              private void configureCluster(Tomcat tomcat)
              {
                  // static membership cluster 
  
                  SimpleTcpCluster cluster = new SimpleTcpCluster();
                  cluster.setChannelStartOptions(3);
                  {
                      DeltaManager manager = new DeltaManager();
                      manager.setNotifyListenersOnReplication(true);
                      cluster.setManagerTemplate(manager);
                  }
                  {
                      GroupChannel channel = new GroupChannel();
                      {
                          NioReceiver receiver = new NioReceiver();
                          receiver.setPort(localClusterMemberPort);
                          channel.setChannelReceiver(receiver);
                      }
                      {
                          ReplicationTransmitter sender = new ReplicationTransmitter();
                          sender.setTransport(new PooledParallelSender());
                          channel.setChannelSender(sender);
                      }
                      channel.addInterceptor(new TcpPingInterceptor());
                      channel.addInterceptor(new TcpFailureDetector());
                      channel.addInterceptor(new MessageDispatch15Interceptor());
                      {
                          StaticMembershipInterceptor membership =
                              new StaticMembershipInterceptor();
                          String[] memberSpecs = clusterMembers.split(",", -1);
                          for (String spec : memberSpecs)
                          {
                              ClusterMemberDesc memberDesc = new ClusterMemberDesc(spec);
                              StaticMember member = new StaticMember();
                              member.setHost(memberDesc.address);
                              member.setPort(memberDesc.port);
                              member.setDomain("MyWebAppDomain");
                              member.setUniqueId(memberDesc.uniqueId);
                              membership.addStaticMember(member);
                          }
                          channel.addInterceptor(membership);
                      }
                      cluster.setChannel(channel);
                  }
                  cluster.addValve(new ReplicationValve());
                  cluster.addValve(new JvmRouteBinderValve());
                  cluster.addClusterListener(new ClusterSessionListener());
  
                  tomcat.getEngine().setCluster(cluster);
              }
          };
      }
  
      private static class ClusterMemberDesc
      {
          public String address;
          public int port;
          public String uniqueId;
  
          public ClusterMemberDesc(String spec) throws IllegalArgumentException
          {
              String[] values = spec.split(":", -1);
              if (values.length != 3)
                  throw new IllegalArgumentException("clusterMembers element " +
                      "format must be address:port:uniqueIndex");
              address = values[0];
              port = Integer.parseInt(values[1]);
              int index = Integer.parseInt(values[2]);
              if ((index &lt; 0) || (index &gt; 255))
                  throw new IllegalArgumentException("invalid unique index: must be &gt;= 0 and &lt; 256");
              uniqueId = "{";
              for (int i = 0; i &lt; 16; i++, index++)
              {
                  if (i != 0)
                      uniqueId += ',';
                  uniqueId += index % 256;
              }
              uniqueId += '}';
          }
      };
  
      // This is for example. In fact these are read from application.properties
      private int localClusterMemberPort = 9991;
      private String clusterMembers = "111.222.333.444:9992:1";
  }
  </code></pre>
  
  <p>And I tested the code with the following environment:</p>
  
  <ul>
  <li>Single Windows PC</li>
  <li>2 Spring boot application instances with different localClusterMemberPort and clusterMembers</li>
  </ul>
  
  <p>Since a cookie does not take the port in account, the cookie that contains JSESSIONID is shared between the two instances.</p>
  
  <p>When the instances are started, tomcat clustering seems to work, since the JSESSIONID value of the requests for the 2 instances are the same. But when I make a request to the second instance after I logged in using the first instance, the second instance does not find the HttpSession. It logs he following message:</p>
  
  <pre><code>w.c.HttpSessionSecurityContextRepository : No HttpSession currently exists
  w.c.HttpSessionSecurityContextRepository : No SecurityContext was available from the HttpSession: null. A new one will be created.
  </code></pre>
  
  <p>Apparently the HttpSession is not being shared. But as the second instance creates a new session, the first instance's login information is cleared and login is invalidated.</p>
  
  <p>What is happening here? Session is shared but HttpSession is not shared?</p>
  
  <p>BTW, I've read that <code>&lt;distributed /&gt;</code> tag must be specified on <code>web.xml</code> for the applications to use tomcat session clustering. But I don't know how to specify it with Spring Boot's no-xml environment. Is this the cause of the problem? Then how can it be specified?</p>
  
  <p>I've searched and found a few documents that show the clustering using Redis. But currently I don't want to add another moving part to my configuration. In my configuration 3~4 nodes are the maximum.</p>