I have this simple Stateful session bean (a single op stack calculator):
package beans;
import java.util.EmptyStackException;
import java.util.Stack;
import javax.ejb.LocalBean;
import javax.ejb.Stateful;
@Stateful
@LocalBean
public class StackCalcBean {
private static int instanceCounter = 0;
private int instanceID;
private Stack<Double> stack;
public StackCalcBean() {
instanceCounter++;
instanceID = instanceCounter;
stack = new Stack<>();
}
public void push(double d) {
stack.push(d);
}
public String plus() {
try {
double d = stack.pop() + stack.pop();
stack.push(d);
return Double.toString(d);
} catch (EmptyStackException e) {
return "Empty Stack !";
}
}
public String myToString() {
return "StackCalc [instanceID=" + instanceID + ", stack=" + stack + "]";
}
}
That works fine with this servlet:
package servlets;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.EmptyStackException;
import java.util.Scanner;
import javax.ejb.EJB;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import beans.StackCalcBean;
@WebServlet("/StackCalc")
public class StackCalcServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@EJB
private StackCalcBean calc;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("In doPost");
String input = request.getParameter("input");
boolean newResult = false;
String value = "no value";
String boxStyle = "font-size:18pt;padding:30px;width:650px;margin:auto;"
+ "border:solid;border-width:1px;border-radius:15px;"
+ "margin-top:40px;";
if (input != null) {
Scanner in = new Scanner(input);
if (in.hasNextDouble()) {
double n = Double.parseDouble(input);
calc.push(n);
} else {
switch (input) {
case "+" : value = calc.plus(); break;
}
newResult = true;
}
in.close();
}
response.setContentType( "text/html" );
try ( PrintWriter out = response.getWriter() ) {
out.println("<!DOCTYPE html>");
out.println("<html><head><title>StackCalc</title></head><body>");
out.println("<div style='"+boxStyle+"'>Session: "+
request.getSession() + " " + request.getSession().getId() + "</div>");
out.println("<div style='"+boxStyle+"'>StackCalcBean: "+ calc.myToString() + "</div>");
out.println( "<div style='"+boxStyle+"'><form method='POST' action='StackCalc'>" );
if (newResult) {
out.println("<div>"+value+"</div>");
}
out.println( "Input :" );
out.println( "<input name='input' type='text' autofocus />" );
out.println( "<input name='btnSubmit' type='submit' value='Send' /><br/>" );
out.println( "</div></body></html>" );
}
}
}
That works well except that the same bean is given by the container in different sessions. I know sessions are not the same because I print the session ID and I use different clients on different machines. I know the bean is the same because it has the same instanceID value and the same stack contents.
I was expecting that I would get a new calculator instance if running a client on a different machine.
I tried with WildFly 21 and Glassfish 5 and I get the same behavior.
Clearly, I am missing something.
Edit: A solution may consist in associating explicitly the calc instance to the web session:
StackCalcBean calc = null;
if (request.getSession().getAttribute("calc") == null) {
try {
InitialContext ctx= new InitialContext();
calc = (StackCalcBean) ctx.lookup("java:module/StackCalcBean!beans.StackCalcBean");
request.getSession().setAttribute("calc", calc);
} catch (NamingException e) {
e.printStackTrace();
}
} else {
calc = (StackCalcBean) request.getSession().getAttribute("calc");
}