0

I have an unusual "bug". I've configured Tomcat Session Replication Manager (Delta). The SessionID and Session attributes are replicated, but not the content of the attributes.

my cluster-tag in server.xml

<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"
                 channelSendOptions="6">

         <Manager className="org.apache.catalina.ha.session.DeltaManager"
                   expireSessionsOnShutdown="false"
                   notifyListenersOnReplication="true"/>
                   

          <Channel className="org.apache.catalina.tribes.group.GroupChannel">
            <Membership className="org.apache.catalina.tribes.membership.McastService"
                        address="228.0.0.4"
                        port="45564"
                        frequency="500"
                        dropTime="3000"/>
            <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
                      address="auto"
                      port="4001"
                      autoBind="100"
                      selectorTimeout="5000"
                      maxThreads="6"/>

            <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
              <Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
            </Sender>
            <Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
            <Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatchInterceptor"/>
          </Channel>

          <Valve className="org.apache.catalina.ha.tcp.ReplicationValve"
                 filter=""/>
          <Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/>

         
          <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
        </Cluster>

../webapps/ROOT/WEB-INF/web.xml on all other machines:

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                      http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
  version="3.1"
  metadata-complete="true">

  <display-name>Welcome to Tomcat</display-name>
  <description>
     Welcome to Tomcat
  </description>
<distributable/>
</web-app>

../webapps/ROOT/index.jsp for all instances (1, 2, .. )

<%@page import="java.util.ArrayList"%>
<%@page import="java.util.Date"%>


<%@page import="java.util.List"%>
<%@page contentType="text/html" pageEncoding="UTF-8"%> 
<%@ page import="javax.servlet.http.HttpUtils.*" %>

<!DOCTYPE html>

<html>

<style type="text/css"> 
     th {
        font-weight:bold;
        text-align:left;
    }
</style> 



    <head>
        <meta http-equiv="Content-Type" content"text/html"; charset=UTF-8">
        <title> JSP PAGE </title>
    </head>
    
    <body>
    
    <FONT size = 5 COLOR="#0000FF">
    
    Instance 1 <br/> <br/>
    
    </FONT>
    
    
    
    <hr/>
    
    
    <br/>
    
    <table cellpadding="10" >
    <tr> <th> Session Id </th> <td> <%=request.getSession().getId()%> </td> </tr>
    <tr> <th> Is it New Session </th> <td> <%=request.getSession().isNew()%> </td> </tr>
    <tr> <th> Session Create Date </th> <td> <%=new Date(request.getSession().getCreationTime())%>  </td> </tr>
    <tr> <th> Session Access Date </th> <td> <%=new Date(request.getSession().getLastAccessedTime())%> </td> </tr>
    </table>
    
    <br/><br/><br/><br/>
    
    <b> Cart List </b> <br/>
    
    <hr/>

    <ul>
    
    <%
        String bookName = request.getParameter("bookName");
        List<String> listOfBooks = (List<String>) request.getSession().getAttribute("Books");
        
        if (listOfBooks == null) {
            listOfBooks = new ArrayList<String>();
            request.getSession().setAttribute("Books", listOfBooks);
        }
        
        if (bookName != null) {
            listOfBooks.add(bookName);
        }
        
        for (String book : listOfBooks ) { 
            out.println("<li>"+book + "</li>");
        }
    %>
    </ul>
<form action="index.jsp" method="POST" >
         <input type= "text" name="bookName" placeholder="Enter bookname" required /> 
         <input type = "submit" value = "add to cart" > <br/> 
         
</form>

Result after adding some session attributes/values via index.jsp

on INSTANCE1 (Primary)

enter image description here

on INSTANCE2 (Backup) (Attributes will not be replicated!)

enter image description here

In the log files I see that the instances recognize each other and send signals. But

  1. when I send ping -t 1 -c 2 228.0.0.4, I get following: 2 packets transmitted, 0 received, 100% packet loss, time 5852ms

  2. Why are the SessionID and Attribute replicated, but not the value of the Attribute?

ng-User
  • 243
  • 1
  • 6
  • 18

1 Answers1

1

I was finally able to solve this problem, and nobody, not even the official java documentation helped when I typed keywords, which is a DISGRACE in the world of documentation!!!!!! You see, I don't think java has an out-of-the-box way to notify the other instances whenever the contents of a list change. There is no DatasetChangedNotify method to notify the other tomcat instances that the list contents have been changed, only if a session attribute itself is changed.

That's right. I know you're following Ramki's tutorial; so am I. Don't follow the nutty suggestions you find on the internet because they don't work; I've tried them. Follow Ramki's tutorial EXACTLY; whatever he does on the video, you do it, BUT CHANGE your JSP scriptlet to:

String bookName = request.getParameter("bookName");
List<String> listOfBooks = (ArrayList<String>) request.getSession().getAttribute("Books");
if(listOfBooks == null) {
  listOfBooks = new ArrayList<String>();                                      
}
if(bookName != null) {
    listOfBooks.add(bookName);
}
request.getSession().setAttribute("Books", listOfBooks);
for(String book: listOfBooks) {
    out.println("<li>" + book + "</li><br />");
}           

You've probably understood it by now, but in case someone else needs more assistance, this is what's happening: in the tutorial, Ramki sets the listOfBooks as the object to the Books session attribute only at the beginning. It should be setting it every time.

Whenever the setAttribute method is called, all the tomcat instances will get notified that the session attribute with the name Books has been changed and, if it's a list, the list will get scanned, so all instances will know the contents have been changed and will display them. Don't worry that you'll always be setting the Books attribute over itself because it'll just be overwritten.

If setAttribute is only called once, whenever listOfBooks is null, when the list is changed, only the tomcat instance that's running the scriptlet will be notified and act accordingly with respect to the session attribute, but all the other instances won't rescan the list they have in their Books session attributes, so their lists are still empty to them.

That's why you're having a full list in the first instance, but all the other instances have an empty list.