ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Web Server Java -- Servlets and JSP
Pages: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11

Session Tracking

Problem:
You want to keep track of one user across several servlet invocations within the same browser session.

Solution:
Use an HttpSession object.

Discussion

HTTP was designed to be a stateless protocol: you would connect to a server, download a laboratory report, and that would be the end of it. Then people started getting clever, and began using it for interactive applications. For such purposes as a shopping cart in an online mall, and tracking answers during an online quiz or moves in an online game, the notion of an HTTP session has evolved to keep track of a particular browser. Sessions can be identified either by use of a cookie (see Cookies) or by a Session Identifier that is added to the URL. In either case the session ends when the user's browser program exits, but will otherwise stick around for a long time (there is probably a major denial-of-service attack hidden in here, so beware).

Using a session is fairly simple within the Servlet API. You request the HttpSession object from the HttpRequest that is passed into your service( ) or doGet( )/doPost( ) method. The session object behaves rather like a Hashtable except that the method names are putValue( ) and getValue( ). This allows you to store an arbitrary number of objects in the session and retrieve them later.

This program uses an HttpSession to keep track of a user's responses during a quiz about Java. There are some 20 categories; once you pick a category, you can answer all the multiple-choice questions in that topic. The first question looks like Figure 18-4.

Screen shot.
Figure 18-4. Quiz servlet starting.

After you've answered a few questions, it may look like Figure 18-5.

Screen shot.
Figure 18-5. Quiz servlet several questions later.

At the end of the quiz, you'll see the total number of questions that you answered correctly.

Debugging Tip for Servlets Using an HttpSession

Objects (such as Exam and Progress in the last example) are stored in the server for as long as your session lasts. If you change any such class so as to make it incompatible with the previous version, you will get mysterious "class cast errors" with the name of the class you changed. In these cases, you can simply close the browser (use File->Exit if using Netscape), and a new session object will be created. See also for another way to avoid these ClassCastException errors.

The Exam object (an object containing all the questions and answers, along with the number of correct answers) is loaded using an XamDataAccessor (the code for these two classes is not shown) and stored in a Progress object. Progress, an inner class inside the servlet, is a tiny data structure used to monitor your progress through one quiz. When you change topics, the Progress object is discarded and a new one created. The bulk of the code in Example 18-7 is taken up in checking and tracking your answers and in generating the HTML to show the results of your previous question (if any), as well as the question and possible answers for the current question.


Example 18-7: DoTestServlet.java

/** A Java Servlet to administer the tests over the Web.
*  Saves exam and status session object to avoid having to reload it,
* but also to keep the exam constant during a session!
*/
public class DoTestServlet extends HttpServlet {
 
  /** Where to find the exams du jour */
  protected final static String DIRECTORY =
     "/home/ian/webs/daroadweb/quizzes-";
  /** The body color */
  protected final static String BGCOLOR = "white";
 
  /** An Inner Class to track the student's progress */
  class Progress {
    Exam exam;      // exam being taken
    boolean done;    // exam is finished.
    String category;  // name of exam, in effect
    int curQuest;    // Question number working on, 0-origin
    int correct;    // number gotten right on first try
  }
 
  /** Service is used to service each request. */
  public void service(HttpServletRequest request,
    HttpServletResponse response) throws IOException, ServletException {
 
    PrintWriter out = response.getWriter( );
    HttpSession session;
    Progress progress;
    String reqCategory;
 
    // Set response type to HTML. Print the HTML header.
    response.setContentType("text/html");
    out.println("<HTML>");
 
    // Find the requested category
    reqCategory = request.getParameter("category");
    reqSubject = request.getParameter("subject");  // unix or java
 
    // Request the user's session, creating it if new.
    session = request.getSession(true);
    if (session.isNew( )) {
      // out.println("<B>NEW SESSION</B>");
      progress = new Progress( );
      progress.category = reqCategory;
      session.putValue("progress", progress);
    } else {
      progress = (Progress) session.getValue("progress");
    }
 
    if (reqCategory != null && progress.category != null &&
      !reqCategory.equals(progress.category)) {
      
      // CHANGE OF CATEGORIES
      // out.println("<B>NEW PROGRESS CUZ " +
      //  reqCategory + " != " +progress.category + "</B>");
      progress = new Progress( );
      progress.category = reqCategory;
      session.putValue("progress", progress);
    }
    if (progress.exam == null) {
      XamDataAccessor ls = new XamDataAccessor( );
      try {
        progress.exam = ls.load(DIRECTORY + subject + "/" +
          progress.category + ".xam");
      } catch (IOException ex) {
        eHandler(out, ex, "We had some problems loading that exam!");
      } catch (NullPointerException ex) {
        eHandler(out, ex, "Hmmm, that exam file seems to be corrupt!");
      }
    }
 
    // Now that we have "exam", use it to get Title.
    out.print("<TITLE>Questions on ");
    out.print(progress.exam.getCourseTitle( )); out.println("</TITLE>");
    out.print("<BODY BGCOLOR=\""); out.print(BGCOLOR); out.println("\">");
    out.print("<H1>");
    out.print(progress.exam.getCourseTitle( ));
    out.println("</h2>");
 
    // Guard against reloading last page
    if (progress.done) {
      out.println("<HR><a href=\"/quizzes/\">Another Quiz?</a>");
      out.flush( );
      return;
    }
 
    // Are we asking a question, or marking it?
    out.println("<P>");
    String answer =request.getParameter("answer");
    int theirAnswer = -1;
    if (answer != null) {
      // MARK IT.
      Q q = progress.exam.getQuestion(progress.curQuest);
      theirAnswer = Integer.parseInt(answer);
      if (theirAnswer == q.getAns( )) {
 
        // WE HAVE A RIGHT ANSWER -- HURRAH!
        if (!q.tried) {
          out.println("<P><B>Right first try!</B>");
          progress.correct++;
        } else
          out.println("<P><B>Right. Knew you'd get it.</B>");
        q.tried = true;      // "Tried and true..."
 
        if (++progress.curQuest >= progress.exam.getNumQuestions( )) {
          out.print("<P>END OF EXAM.");
          if (progress.correct == progress.curQuest) {
            out.println("<P><B>Awesome!</B> You got 100% right.");
          } else {
            out.print("You got ");
            out.print(progress.correct);
            out.print(" correct out of ");
            out.print(progress.curQuest);
            out.println(".");
          }
          out.println("<HR><a href=\"/quizzes/\">Another Quiz?</a>");
 
          // todo invalidate "progress" in case user retries
          progress.done = true;
 
          // Return, so we don't try to print the next question!
          return;
 
        } else {
          out.print("Going on to next question");
          theirAnswer = -1;
        }
      } else {
        out.print("<B>Wrong answer</B>. Please try again.");
        q.tried = true;
      }
    }
 
    // Progress?
    out.print("<P>Question ");
    out.print(progress.curQuest+1);
    out.print(" of ");
    out.print(progress.exam.getNumQuestions( ));
    out.print(". ");
    if (progress.curQuest >= 2) {
      out.print(progress.correct);
      out.print(" correct out of ");
      out.print(progress.curQuest);
      out.print(" tried so far (");
      double pct = 100.0 * progress.correct / progress.curQuest;
      out.print((int) pct);
      out.println("%).");
    }
 
    // Now generate a form for the next (or same) question
    out.print("<FORM ACTION=/servlet/DoTestServlet METHOD=POST>");
    out.print("<INPUT TYPE=hidden NAME=category VALUE=");
      out.print(progress.category); out.println(">");
    out.println("<HR>");
 
    Q q = progress.exam.getQuestion(progress.curQuest);
    out.println(q.getQText( ));
 
    for (int j=0; j<q.getNumAnswers( ); j++) {
        out.print("<BR><INPUT TYPE=radio NAME=answer VALUE=\"");
        out.print(j);
        out.print("\"");
        if (j==theirAnswer)
          out.print(" CHECKED");
        out.print(">");
        out.print(q.getAnsText(j));
        out.println("</INPUT>");
      }
    out.println("<HR>");
 
    out.println("<INPUT TYPE=SUBMIT VALUE=\"Mark it!\"");
    out.println("</FORM>");
    out.println("</HTML>");
    out.close( );
  }
 
  void eHandler(PrintWriter out, Exception ex, String msg) {
    out.println("<H1>Error!</h2>");
    out.print("<B>");
    out.print(msg);
    out.println("</B>");
    out.println("<pre>");
    ex.printStackTrace(out);
    out.flush( );
    out.close( );
  }
}


Pages: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11

Next Pagearrow