Link to home
Start Free TrialLog in
Avatar of Fushi
Fushi

asked on

Servlets, performance, threads and synchronization issues

Hello Experts,

We have an application that uses the JSP/Servlet model.  Info from the jsp is submitted to a servlet that deals with business objects and DAO's. We keep  some information in HttpSession objects and recently we have discovered that the sessions can get mixed up if there are multiple request coming into the servlet at close time intervals.  For example if three of us try to log in, I will log in as somone else and someone will log in as me, etc....This is due to the fact that from my understanding sessions are application wide and all threads issued by the servlet have access to those resources.  Therefore when a few threads are running concurrently they can “get confused” as to which session belongs to who.  

We have found 2 solutions to this problem.

1.      synchronizing the servlets service(). The problem with this is that it lock the service method until the first thread has finished working with it, therefore with many concurrent users the queue will grow significantly and that will hurt the performance
2.      Having the servlet implement SingleThreadModel interface.  Not sure how this works but from what I understand it causes the servlet container to create a pool of servlet instances and execute one thread in each instance for each request… This seems to have a better performance then synchronizing the service method (as per the tests that we ran) but I can see how this can soon become very costly if the server has to have 500 servlet instances in the pool.  The other thing that worries me is that I am not sure how and why this really works…

From the research done it seems that both the methods mentioned are not very good and synchronizing access to the shared resource should be the way to go but putting a synchronized scriplet around accessing the session doesn’t do jack….No one really mentions a working solution.

Please let me know if any of you have encountered a similar problem and how you dealt with it.  Also if anyone could shine some light on the singleThreadModel interface, I would greatly appreciate it!


Thanks,

-w
Avatar of gloverkcn
gloverkcn

The SingleThreadModel interface basically tells the appserver that only one instance of the  app can be run at once.  So if you get 20 connections they have to wait their  turn  to use the application.  I wouldn't recommend using it since it will really hamper performance.

I haven't seen the issue with multiple log-ins before.  How are you handling the log-in process?  Are you using roles?  Form-Based Authentication? Is a servlet handling the log-in log-out?
Avatar of Fushi

ASKER

We ran tests (showing execution speed with miliseconds ) and you are not correct . With singleThreadModel there are multiple instances of the servlet (not threads)  being executed at the same time. What you are sayiong happens if you synchronize the service method.  So the problem with SingleThreadModel is not the queue, it is the amount of resources that it incolves to create and keep track of a pool of servlet instances.

This doesn't only happen with logins...It happens when accessing any shared (session) resource.  

The process is as follows.

Jsp form is submited to a servlet's service()  method that issues a call to let's say a login() that calls a DAO that accesses a database and returns some valus that are the returned to the login() which put's it in a session object.  The that session object is used by multiple jsp's
Avatar of Mick Barry
> This is due to the fact that from my understanding sessions are application wide

Each user has there own session so this should not be a problem.

Does your servlet have any member variables, if so you should probably get rid of them as servlets are best kept stateless.
Avatar of Fushi

ASKER

that is also what I thought, but after further research as I explained sessions are application scope variables and since servlets are multithreaded, if multiple threads try to access different sessions in close time intervals different threads grab different sessions and that is why there is a need for synchronization....as I said this is not a simply theoretic problem, we were testing the app and were able to log in as different people if the 3 of us clicked submit at the same time....

When you say member variables, do you mean gloabal class variables? I do have them, and my HttpSession object is actually declared (but not initialized) as a global variable....I think I see where you are going with this....So where should I declare my session, in my service method and any other methods that use it? why wouldn't I just want to declare it as a gloabl variable and then reinitialize it whenever I need to?
.  

you can have application scope variable, and also session scope variables.

can u post your servlet code.
Avatar of Fushi

ASKER

Please keep in mind this is work in progresss....please concentrate on case 0 of the service()

package edu.asu.map.controllers;

//import Servlet packages
import javax.servlet.*;
import javax.servlet.http.*;
import edu.asu.map.containers.appUser;
import com.sybase.jdbc2.jdbc.*;


import edu.asu.map.common.*;

//import io package
import java.io.*;

import java.net.*;

import java.sql.*;
import java.util.*;
import java.util.Vector.*;

//begin mainMenuLogic Servlet Class
public class controllerMainMenu
    extends HttpServlet implements SingleThreadModel{

//create the session object
HttpSession session;
mainDAO myDAO;
public static Vector myStates;
//Declare an empty error message
String errorMsg="";

//Set response content type
  static final private String CONTENT_TYPE = "text/html";
//Declare and initialize class variables
  boolean loginSuccess=false;


//default init()
  public void init() throws ServletException {
  //load state values and put them in a static variable
  loadStates();
 
  }

//default destroy()
  public void destroy() {}

//begin service method declaration
  public void service(HttpServletRequest request, HttpServletResponse response) throws
      ServletException, IOException {
java.util.Date myDate;
long mili;
    //declare and instantiate the session object
    session = request.getSession(true);


    response.setContentType(CONTENT_TYPE);
    PrintWriter out = response.getWriter();

    //clear the error message
   
    session.setAttribute("errorMsg", errorMsg);
    //get the selection parameter
    String mySelection = request.getParameter("selection");

    //convert to int
    int menuSelection = myUtility.stringToInt(mySelection);

    //declare and initialize variable to hold the path of the redirect
    String myForwardPath = "";

    switch (menuSelection) { //start switch
      //login request received, call login
      case 0:
        if(session.getAttribute("appUser")!=null)
        {
           myForwardPath = "/stepMenu.jsp";
        }
        else
        {
        errorMsg = login(request, response, 1);
        if (errorMsg.equals(""))
        {
          myDate = new java.util.Date();
         
          appUser myuse = (appUser)session.getAttribute("appUser");
           mili = myDate.getTime();
          System.out.println(myuse.getAppId() + "Time: " + mili);
              myForwardPath = "/stepMenu.jsp";

        }
        else {
          myForwardPath = "/login.jsp";
          }
        }
           break;
           //end case 0 - login requested


      //new login request call newLogin()
      case 1:
        errorMsg = login(request, response, 0);
        if (errorMsg.equals("")) {

          if(verify(request).equals(""))
          {
              myForwardPath = "/stepMenu.jsp";
          }
          else
          {
              myForwardPath = "/error.jsp";
          }

        }
        else {
          myForwardPath = "/newLogin.jsp";
        }

        break;
      //end case 1 - new login requested

      //app deadlines request
      case 2:

      if (session.getAttribute("deadlines")!=null)
      {
         myForwardPath = "/mainMenu.jsp";
      }
      else
      {
        errorMsg = lookupDeadlines(request, response);
        if (errorMsg.equals("")) {
          myForwardPath = "/mainMenu.jsp";
        }
        else {
          myForwardPath = "/errorPage.jsp";
        }
      }

      break;//end app deadlines request

      //log out
      case 3:
      errorMsg = logout(request, response);
      if (errorMsg.equals("")) {
        myForwardPath = "/logout.jsp";
        }
      else {
        myForwardPath = "/errorPage.jsp";
        }

      break;




      default:
        out.println("Unknown request");
        break;

    } //end switch


    session.setAttribute("errorMsg", errorMsg);

     //move to next object using the myForwardPath variable
    RequestDispatcher dispatch = this.getServletContext().getRequestDispatcher(
        myForwardPath);



    dispatch.forward(request, response);


//end service method declaration
  }

  public String login(HttpServletRequest request, HttpServletResponse response, int flag)
  {
    String errorMsg="";
    session = request.getSession(true);
    appUser myUser;
    myDAO = new mainDAO();
 java.util.Date myDate;
 long mili;
 
        try
        {
          myUser=(myDAO.getUser(request.getParameter("username"),
                                                        request.getParameter("password"),
                                                        request.getHeader("user-agent"),
                                                        flag));
          myDate = new java.util.Date();
          mili = myDate.getTime();
          System.out.println(myUser.getAppId() + "Time: " + mili);
          if(myUser.getErrorCode()!=3)
          {
            //problem with login
            errorMsg="Wrong username or password";
          }
          else
          {
             session.setAttribute("appUser", myUser);


          }

        }
        catch (Exception e)
        {
          e.printStackTrace();
          errorMsg="Unable to access user profile";
        }
 
    myDate = new java.util.Date();
    appUser myuse = (appUser)session.getAttribute("appUser");
    mili = myDate.getTime();
    System.out.println(myuse.getAppId() + "Time: " + mili);

    return errorMsg;

  }

public String lookupDeadlines(HttpServletRequest request, HttpServletResponse response)
{
  String errorMsg="";
  session = request.getSession(true);
  myDAO = new mainDAO();

  try
  {
  session.setAttribute("deadlines", myDAO.getDeadlines());
  }
  catch(Exception e)
  {
    e.printStackTrace();
    errorMsg="Unable to retrieve Application deadlines...";

  }

  return errorMsg;
}

public String logout(HttpServletRequest request, HttpServletResponse response)
{
  session = request.getSession(true);
  String errorMsg="";

  try
  {

    if (session.getAttribute("appUser") == null) {
      //do nothin...user profile is empty.
      errorMsg =
          "You did not log in, or you have already terminated your session";
    }
    else {
      session.removeAttribute("appUser");

      //session.setAttribute("appUser", null);
    }
  }
  catch (Exception e)
  {
    e.printStackTrace();
    errorMsg =
          "You did not log in, or you have already terminated your session";
  }

  return errorMsg;
}
//end mainMenuLogic Servlet Class

public String verify(HttpServletRequest request)
{
  session = request.getSession(true);
  String errorMsg = "";
  try
  {
    appUser myUser = (appUser) session.getAttribute("appUser");

    if (myUser.getAppId()>0)
    {
      errorMsg="";
    }
    else
    {
      errorMsg="You are not authenticated. Please authenticate to view this page";
    }

  }
  catch(Exception e)
  {
    e.printStackTrace();
    errorMsg="You are not authenticated. Please authenticate to view this page";
  }


  return errorMsg;
}
     
public void loadStates()
{
  myDAO = new mainDAO();
  try
  {
  myStates = myDAO.getStates();
  System.out.println("weeeeeeeeeeeeeeeeeeeeeeeeeeee");
  }
  catch(Exception e)
  {
    e.printStackTrace();
  }


}

}


ASKER CERTIFIED SOLUTION
Avatar of Mick Barry
Mick Barry
Flag of Australia image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
>  implements SingleThreadModel{

thats also unnecessary
Avatar of Fushi

ASKER

well as I said implements SingleThreadModel will be unnecessary if I won't get any resource conflicts (this interface makes sure that there are multiple instances of the servlet created for each request)....and when you say to "store them in the session values", I am storring in a session....the HttpSession object...
I have no problems with my aplication wide variables...however as I said my session variables get mixed up....I will try to get rid of my member variables tomorrow as I need more people in the office to test this simoltaneously.

So why do member variables couse conflicting access to  session resources...especially since I am really not even initializing any of them (well except for errorMessgae)? I mean we tested this over and over agian with 3 people. We had 3 people try to log into the app ion close time intervals and eveything was correct...meaning the righ info for each session was being pulled from the database and then back to the servlet and it had the correct info in the session object up yuntill the request was forwarde to the jsp...That is when things got mixed up (I would get sombody elses session info, etc). This has seriously confused me since up untill that moment I was sure session objects are session (user) specific....any explanations? thanks,
> since I am really not even initializing any of them (well except for errorMessgae)?

then why have them at all :)
They are unnecessary freom what I can see. Just use local vars instead.
> So why do member variables couse conflicting access to  session resources

you get a request from user A, which sets the member session var to refernce their session
before that request is finished another request comes from user B which sets the member session var to reference their session.
user A's requests then access the member session var which is now actually User B's session.
Avatar of Fushi

ASKER

> then why have them at all :)

I guess I'm just lazy so if I just declare the vars then  I can just re-initialize them in the methods I'm using it in...I thought that was ok (or am I just being too lazy?)

Avatar of Fushi

ASKER

> user A's requests then access the member session var which is now actually User B's session.

That makes sense... I will try to get rid of member vars tomorrow and let you know if this helped...Thanks for hanging with me on this

-w
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Fushi

ASKER

Sure enough, it worked...Thanks to both of you for the solution as well as the explanation, I defenietly feel like I have a better understanding (or more like at least a beggining of an understanding) of memmory allocation....Thanks!