You want to keep track of one user across several servlet invocations within the same browser session.
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.
After you’ve answered a few questions, it may look like Figure 18-5.
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( ); } }
3.15.182.159