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 Section 18.4) 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 (see Section 7.7) 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.

Quiz servlet starting

Figure 18-4. Quiz servlet starting

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

Quiz servlet several questions later

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.

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("</H1>");

        // 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!</H1>");
        out.print("<B>");
        out.print(msg);
        out.println("</B>");
        out.println("<pre>");
        ex.printStackTrace(out);
        out.flush(  );
        out.close(  );
    }
}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.15.182.159