CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 3 of 3
  1. #1
    Join Date
    Sep 2017
    Posts
    15

    Servlet thread safety of a local session attribute

    Good day, all.

    I have a servlet, in which I have local variables declared, initiated and assigned to session attributes.

    I need the servlet to be thread-safe, such that multiple browser windows on same computer can have the servlet running and you could perform calculations in each window, but such that the operations in the various threads would not influence each other, that is, that the account balance variable was not shared among the threads, causing inconsistent states of it for any thread at any time.

    The below is my code:

    Code:
    // Import servlet and HTTP functionality packages.
        import javax.servlet.*;
        import javax.servlet.http.*;
        
        // Import packages to: handle user inputs and outputs, enable usage of decimal formatting of numbers.
        import java.io.*;
        import java.util.*;
        import java.text.DecimalFormat;
        
        public class SessionBank extends HttpServlet	// Define concrete class and extend HTTP functionality.
        {
        	public void init() throws ServletException	// Initialise variables at start of the servlet. Possible exception.
        	{
        
        	}
        
        	// The method to output the initial HTML form to the screen, addressing also possible exceptions.
        	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
        	{
        		// Declare and initiate local variables.
        		double balance = 0;						// The current balance variable, un-formatted number, initiate to zero.
        		String formattedBal = "";				// The current balance variable, formatted to show as currency amount, initially blank.
        
        		// Set balance and formatted balance as session attributes.
        		request.getSession().setAttribute("balance", balance);
        		request.getSession().setAttribute("formattedBal", formattedBal);
        
        		showBalance(request, response);	// Call custom-defined method to display initial page.
        	}
        
        	// Method to respond to user's button inputs - output relevant HTML back to the screen. Possible exceptions.
        	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
        	{
        		HttpSession session = request.getSession(true);		// Establish session object.
        
        		response.setContentType("text/html");		// Set response type to text/HTML.
        		response.setHeader("Expires", "Tues, 01 Jan 1980 00:00:00 GMT");	// Set past date to forbid cache.
        
        		// If user clicks "Deposit" button:
        		if(request.getParameter("depButton") != null && request.getParameter("depButton").equals("Deposit"))
        		{
        			if(verifyAmount(request))	// If entered amount passes verification.
        			{
        				addToBalance(request, session);			// Add the amount to current balance.
        				redisplayCurrentPage(response);		// Redisplay initial page.
        			}
        			else		// If entered amount does not pass verification.
        			{
        				showErrorPage(response);	// Display error page.
        			}
        		}
        
        		// If user clicks "Withdraw" button:
        		if(request.getParameter("withdrButton") != null && request.getParameter("withdrButton").equals("Withdraw"))
        		{
        			if(verifyAmount(request))	// If entered amount passes verification.
        			{
        				subtractFromBalance(request, session);	// Subtract the amount from current balance.
        				redisplayCurrentPage(response);		// Redisplay initial page.
        			}
        			else		// If entered amount does not pass verification.
        			{
        				showErrorPage(response);	// Display error page.
        			}
        		}
        
        		// If user clicks "Balance" button:
        		if(request.getParameter("balButton") != null && request.getParameter("balButton").equals("Balance"))
        		{
        			showBalance(request, response);		// Display current formatted balance on page.
        		}
        	}
        
        	private boolean verifyAmount(HttpServletRequest request)	// Method to verify entered amount, based on textbook criteria.
        	{
        		boolean amountValid = false;	// Declare and initiate a validity variable.
        
        		// If entered amount is not blank and is greater than zero, return validity as true. Else, return false.
        		if(request.getParameter("amount") != "" && Double.parseDouble(request.getParameter("amount")) > 0)
        			amountValid = true;
        		else
        			amountValid = false;
        
        		return amountValid;		// Return validity variable.
        	}
        
        	// Method to add amount to balance, addressing possible exception.
        	private void addToBalance(HttpServletRequest request, HttpSession session) throws IOException
        	{
        		double userAmount = Double.parseDouble(request.getParameter("amount"));	// Declare and assign entered amount variable.
        
        		// Down-cast session attribute object to String, then parse into double type variable.
        		double balOld = Double.parseDouble(String.valueOf(session.getAttribute("balance")));
        
        		double balNew = balOld + userAmount;		// Add the amount to current balance and save the value.
        
        		session.setAttribute("balance", balNew);	// Assign new balance to the session attribute.
        	}
        
        	// Method to subtract from balance. Possible exception.
        	private void subtractFromBalance(HttpServletRequest request, HttpSession session) throws IOException
        	{
        		double userAmount = Double.parseDouble(request.getParameter("amount"));	// Declare and assign entered amount value.
        
        		// Down-cast session attribute object to String, then parse into a double type variable.
        		double balOld = Double.parseDouble(String.valueOf(session.getAttribute("balance")));
        
        		double balNew = balOld - userAmount;		// Subtract the amount from the balance and save the value.
        
        		session.setAttribute("balance", balNew);	// Assign new balance value to the session attribute.
        	}
        
        	private void showBalance(HttpServletRequest request, HttpServletResponse response) throws IOException	// Method to output balance HTML page. Possible exception.
        	{
        		PrintWriter out = response.getWriter();		// Establish HTML writer object.
        
        		formatBalance(request);	// Format current balance for displaying.
        
        		out.println("<html>");
        			out.println("<hr>");		// Horizontal line.
        			out.println("<title>Online Bank ATM Simulator</title>");		// Title to show on browser title bar.
        			out.println("<h1 align = \"center\">Bank ATM Simulation</h1>");	// Page heading, centered on page.
        			out.println("<body onLoad = \"amount.focus()\">");				// Set focus to the text-field.
        				out.println("<form method = \"POST\" action = \"../servlet/SessionBank\">");	// Form method and submission address.
        					out.println("<center>");		// Tag to center the following output on page.
        					out.println("Amount: ");
        					out.println("<input type = \"text\" name = \"amount\" id = \"amount\" size = \"20\"><br><br>");	// Amount text field.
        					out.println("Balance: ");
        					out.println(request.getSession().getAttribute("formattedBal") + "<br><br>");	// Current formatted balance shown.
        					out.println("<button name = \"balButton\" value = \"Balance\">Balance</button>");	// "Balance" button.
        					out.println("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;");	// Spacers.
        					out.println("<button name = \"depButton\" value = \"Deposit\">Deposit</button>");	// "Deposit" button.
        					out.println("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;");	// Spacers.
        					out.println("<button name = \"withdrButton\" value = \"Withdraw\">Withdraw</button>");	// "Withdraw" button.
        					out.println("</center>");		// Tag to end centering of output on page.
        				out.println("</form>");		// End of form.
        			out.println("</body>");
        			out.println("<br>");
        			out.println("<hr>");		// Horizontal line.
        		out.println("</html>");
        	}
        
        	// Method to redisplay form after deposit/withdrawal.
        	private void redisplayCurrentPage(HttpServletResponse response) throws IOException
        	{
        		PrintWriter out = response.getWriter();		// Establish HTML writer object.
        
        		out.println("<html>");
        			out.println("<hr>");		// Horizontal line.
        			out.println("<title>Online Bank ATM Simulator</title>");		// Title to show on browser title bar.
        			out.println("<h1 align = \"center\">Bank ATM Simulation</h1>");	// Page heading, centered on page.
        			out.println("<body onLoad = \"amount.focus()\">");				// Set focus to the text-field.
        				out.println("<form method = \"POST\" action = \"../servlet/SessionBank\">");	// Form method and submission address.
        					out.println("<center>");		// Tag to center the following output on page.
        					out.println("Amount: ");
        					out.println("<input type = \"text\" name = \"amount\" id = \"amount\" size = \"20\"><br><br>");	// Amount text field.
        					out.println("Balance: ");
        					out.println("<br><br>");	// No formatted balance value shown.
        					out.println("<button name = \"balButton\" value = \"Balance\">Balance</button>");	// "Balance" button.
        					out.println("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;");	// Spacers.
        					out.println("<button name = \"depButton\" value = \"Deposit\">Deposit</button>");	// "Deposit" button.
        					out.println("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;");	// Spacers.
        					out.println("<button name = \"withdrButton\" value = \"Withdraw\">Withdraw</button>");	// "Withdraw" button.
        					out.println("</center>");		// Tag to end centering of output on page.
        				out.println("</form>");		// End of form.
        			out.println("</body>");
        			out.println("<br>");
        			out.println("<hr>");		// Horizontal line.
        		out.println("</html>");
        	}
        
        	private void formatBalance(HttpServletRequest request)	// Method to format the current balance number to a currency amount.
        	{
        		DecimalFormat dollars = new DecimalFormat("$###,###.###");	// Construct new decimal format.
        
        		// Down-cast session attribute to String, parse to double, then format using the above decimal format and save value.
        		String formattedBal = dollars.format(Double.parseDouble(String.valueOf(request.getSession().getAttribute("balance"))));
        
        		request.getSession().setAttribute("formattedBal", formattedBal);	// Assign new formatted balance to session attribute.
        	}
        
        	// Method to output error HTML page, if entered amount does not pass verification. Possible exception.
        	private void showErrorPage(HttpServletResponse response) throws IOException
        	{
        		PrintWriter out = response.getWriter();		// Establish HTML writer object.
        
        		out.println("<html>");
        			out.println("<head>");
        				out.println("<title>Amount Input Error</title>");	// Title to show in browser title bar.
        			out.println("</head>");
        
        			out.println("<body>");
        				out.println("<h1>Error processing the input.</h1><br>");	// Heading text.
        				out.println("Please ensure your input:<br><br>");
        				out.println("- Is not blank.<br>");
        				out.println("- Is strictly a number.<br>");
        				out.println("- Is a positive, non-zero amount.");
        			out.println("</body>");
        		out.println("</html>");
        	}
        
        	public void destroy()	// Method to terminate the servlet.
        	{
        
        	}
        }
    Now, what happens instead is, when using multiple threads (browser windows running the servlet), the balance that one thread overwrites, becomes the balance that another thread reads. Which is of course incorrect. I need each simultaneous thread to have its own copy of this variable for consistent results.

    As far as I know, unlike storing the balance as a class (instance) variable, storing it as local and assigning to session attribute is thread-safe. But if that is so, why do the threads update one another's variable? What is coded incorrectly?

    Thanks a lot!

  2. #2
    2kaud's Avatar
    2kaud is online now Super Moderator Power Poster
    Join Date
    Dec 2012
    Location
    England
    Posts
    7,824

    Re: Servlet thread safety of a local session attribute

    [Thread moved to Java forum]
    All advice is offered in good faith only. All my code is tested (unless stated explicitly otherwise) with the latest version of Microsoft Visual Studio (using the supported features of the latest standard) and is offered as examples only - not as production quality. I cannot offer advice regarding any other c/c++ compiler/IDE or incompatibilities with VS. You are ultimately responsible for the effects of your programs and the integrity of the machines they run on. Anything I post, code snippets, advice, etc is licensed as Public Domain https://creativecommons.org/publicdomain/zero/1.0/ and can be used without reference or acknowledgement. Also note that I only provide advice and guidance via the forums - and not via private messages!

    C++23 Compiler: Microsoft VS2022 (17.6.5)

  3. #3
    Join Date
    Aug 2017
    Posts
    36

    Re: Servlet thread safety of a local session attribute

    The problem was indeed that one browser (Chrome in my case) shared the session between all running tabs. Shared the cookie, as you explained. I tested now with rather running the servlet in Chrome and another in Internet Explorer and they ran perfectly independently from each other, not affecting each other's "balance" variable.

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured