This section shows how to
- use Session Tracking capabilities
Session Tracking allows a Servlet to associate a request with a
user. A session can extend across requests and connections of the
stateless
HTTP.
Sessions can be maintained in two ways:
-
By using Cookies. A Cookie is a string (in this case that
string is the session ID) which is
sent to a client to start a session. If the client wants to
continue the session it sends back the Cookie with subsequent
requests. This is the most common way to implement session
tracking.
-
By rewriting URLs. All links and redirections which are
created by a Servlet have to be encoded to include the session
ID. This is a less elegant solution (both, for Servlet implementors
and users) because the session
cannot be maintained by requesting a well-known URL oder selecting
a URL which was created in a different (or no) session. It also
does not allow the use of static pages. All HTML pages which
are sent within a session have to be created dynamically.
Our next Servlet manages a virtual shopping cart. Users can add various
items to their shopping cart via HTML forms. The shopping cart contents
are stored on the server and each user gets his own shopping cart
which is selected automatically whenever he makes a request to the
Servlet.
In the simplified version that we implement in class
ShoppingCartServlet there are only two kinds of
items, named FOO and BAR. By pressing a button
in an HTML form a single FOO or BAR item can be put
into the shopping cart. There's another button to see the
current contents of the shopping cart and a button to order
the selected items, thus clearing the shopping cart.
The first version of the Servlet, called
ShoppingCartServlet, which works with Cookie-style
sessions only, consists of the two standard methods, doGet
and doPost:
-
A form with the buttons is created by the Servlet's doGet
method.
7: protected void doGet(HttpServletRequest req, HttpServletResponse res)
8: throws ServletException, IOException
9: {
10: res.setContentType("text/html");
11: PrintWriter out = res.getWriter();
12: out.print("<HTML><HEAD><TITLE>Online Shop</TITLE>"+
13: "</HEAD><BODY><FORM METHOD=POST>"+
14: "<INPUT TYPE=SUBMIT NAME=foo VALUE="+
15: "\"Put a FOO into the shopping cart\">"+
16: "<INPUT TYPE=SUBMIT NAME=bar VALUE="+
17: "\"Put a BAR into the shopping cart\">"+
18: "<INPUT TYPE=SUBMIT NAME=see VALUE="+
19: "\"See the shopping cart contents\">"+
20: "<INPUT TYPE=SUBMIT NAME=buy VALUE="+
21: "\"Buy the shopping cart contents\">"+
22: "</FORM></BODY></HTML>");
23: out.close();
24: }
Alternatively, it could also come from a static HTML page.
-
The doPost method processes the form data which
is sent by a client in response to the form created by
doGet.
26: protected void doPost(HttpServletRequest req, HttpServletResponse res)
27: throws ServletException, IOException
28: {
29: String msg;
30:
31: HttpSession session = req.getSession(true);
32: if(session.isNew())
33: {
34: session.putValue("foo", new int[] { 0 });
35: session.putValue("bar", new int[] { 0 });
36: }
37:
38: int[] foo = (int[])session.getValue("foo");
39: int[] bar = (int[])session.getValue("bar");
40:
41: if(req.getParameter("foo") != null)
42: {
43: foo[0]++;
44: msg = "Bought a FOO. You now have "+foo[0]+".";
45: }
46: else if(req.getParameter("bar") != null)
47: {
48: bar[0]++;
49: msg = "Bought a BAR. You now have "+bar[0]+".";
50: }
51: else if(req.getParameter("buy") != null)
52: {
53: session.invalidate();
54: msg = "Your order for "+foo[0]+" FOOs and "+bar[0]+
55: " BARs has been accepted. Your shopping cart is empty now.";
56: }
57: else
58: {
59: msg = "You have "+foo[0]+" FOOs and "+bar[0]+
60: " BARs in your shopping cart.";
61: }
62:
63: res.setContentType("text/html");
64: res.setHeader("pragma", "no-cache");
65: PrintWriter out = res.getWriter();
66: out.print("<HTML><HEAD><TITLE>Shopping Cart</TITLE></HEAD><BODY>");
67: out.print(msg);
68: out.print("<HR><A HREF=\"");
69: out.print(req.getRequestURI());
70: out.print("\">Back to the shop</A></BODY></HTML>");
71: out.close();
72: }
First we get the HttpSession object which is
associated with the request by calling req.getSession.
The argument true forces the creation of a new
session if the request doesn't contain a valid session key.
Note: Although getSession is a
method of HttpServletRequest and not of
HttpServletResponse it may modify the response
header and therefore needs to be called before a
ServletOutputStream or PrintWriter is
requested.
If the session is indeed new (determined by calling
HttpSession's isNew() method)
we add some custom data to the session: Two counters, one for
the FOOs and one for the BARs in the shopping cart.
The session object can be used like a Dictionary.
That means we can only add Objects, not instances of
primitive types like int. We could use an instance of
java.lang.Integer for each counter, but these
objects are immutable which makes incrementing inefficient
and difficult to implement. Instead we use an array of int
(int[]) with only one element as a mutable wrapper
object. The element is initialized to 0.
Next we retrieve the values for "foo" and "bar" from the
session, no matter if they were just added or carried over
from a previous request.
In the ListManagerServlet both buttons had the same
name but different values so we could use getParameter
to retrieve the value from the request and then do a string
compare to the possible values. This time we use a different
approach which can be implemented more efficiently. All buttons
have different names and we can find out which button was used
to submit the form by checking which name has a non-null
value.
A new FOO or BAR item can be put into the shopping
cart by simply incrementing the counter in the array. Note that
the array does not need to be put back into the session because
it has not changed itself, only the contents have been
modified.
When the user chooses to buy the contents of the shopping cart
we call session.invalidate() to delete the
session on the server side and tell the client to remove
the session ID Cookie. The session data is lost and
when a new POST request is made to the Servlet, a new
session will be created.
The rest of the doPost method is basically the same
as in the ListManagerServlet.
Here is the full source code of the ShoppingCartServlet:
1: import java.io.*;
2: import javax.servlet.*;
3: import javax.servlet.http.*;
4:
5: public class ShoppingCartServlet extends HttpServlet
6: {
7: protected void doGet(HttpServletRequest req, HttpServletResponse res)
8: throws ServletException, IOException
9: {
10: res.setContentType("text/html");
11: PrintWriter out = res.getWriter();
12: out.print("<HTML><HEAD><TITLE>Online Shop</TITLE>"+
13: "</HEAD><BODY><FORM METHOD=POST>"+
14: "<INPUT TYPE=SUBMIT NAME=foo VALUE="+
15: "\"Put a FOO into the shopping cart\">"+
16: "<INPUT TYPE=SUBMIT NAME=bar VALUE="+
17: "\"Put a BAR into the shopping cart\">"+
18: "<INPUT TYPE=SUBMIT NAME=see VALUE="+
19: "\"See the shopping cart contents\">"+
20: "<INPUT TYPE=SUBMIT NAME=buy VALUE="+
21: "\"Buy the shopping cart contents\">"+
22: "</FORM></BODY></HTML>");
23: out.close();
24: }
25:
26: protected void doPost(HttpServletRequest req, HttpServletResponse res)
27: throws ServletException, IOException
28: {
29: String msg;
30:
31: HttpSession session = req.getSession(true);
32: if(session.isNew())
33: {
34: session.putValue("foo", new int[] { 0 });
35: session.putValue("bar", new int[] { 0 });
36: }
37:
38: int[] foo = (int[])session.getValue("foo");
39: int[] bar = (int[])session.getValue("bar");
40:
41: if(req.getParameter("foo") != null)
42: {
43: foo[0]++;
44: msg = "Bought a FOO. You now have "+foo[0]+".";
45: }
46: else if(req.getParameter("bar") != null)
47: {
48: bar[0]++;
49: msg = "Bought a BAR. You now have "+bar[0]+".";
50: }
51: else if(req.getParameter("buy") != null)
52: {
53: session.invalidate();
54: msg = "Your order for "+foo[0]+" FOOs and "+bar[0]+
55: " BARs has been accepted. Your shopping cart is empty now.";
56: }
57: else
58: {
59: msg = "You have "+foo[0]+" FOOs and "+bar[0]+
60: " BARs in your shopping cart.";
61: }
62:
63: res.setContentType("text/html");
64: res.setHeader("pragma", "no-cache");
65: PrintWriter out = res.getWriter();
66: out.print("<HTML><HEAD><TITLE>Shopping Cart</TITLE></HEAD><BODY>");
67: out.print(msg);
68: out.print("<HR><A HREF=\"");
69: out.print(req.getRequestURI());
70: out.print("\">Back to the shop</A></BODY></HTML>");
71: out.close();
72: }
73:
74: public String getServletInfo()
75: {
76: return "ShoppingCartServlet 1.0 by Stefan Zeiger";
77: }
78: }
Note that req.getSession is called for
every POST request. It is important that the session object
is requested on a regular basis because the Web Server may set
a session timeout. Requesting the session ensures that the session's
time-to-live is reset. The only reason for not calling
req.getSession in the doGet method of this
version of the Servlet is to make the response to doGet
cachable.
Adding Support for URL Rewriting
To make the Servlet usable with URL rewriting (for clients without
Cookie support or with Cookie support turned off) we have to make some
modifications.
The formerly static "Online Shop" page which is created by doGet
needs to be modified to include an ACTION URL which contains an
encoded session ID in the HTML form. This is done with the
encodeUrl method of HttpServletResponse. We also
need to call req.getSession to keep the session alive.
The additional code to check for a new session can be avoided by using
getSession(false). If there is no session or an invalid
session we do not force the creation of a new session. This is
deferred until the doPost method is called.
Finally, the response has to be marked as not cachable by
calling res.setHeader("pragma", "no-cache"), as usual.
These changes lead us to the following revised implementation of
doGet:
protected void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException
{
HttpSession session = req.getSession(false);
res.setContentType("text/html");
res.setHeader("pragma", "no-cache");
PrintWriter out = res.getWriter();
out.print("<HTML><HEAD><TITLE>Online Shop</TITLE>"+
"</HEAD><BODY><FORM METHOD=POST ACTION="+
out.print(res.encodeUrl(req.getRequestURI()));
out.print("><INPUT TYPE=SUBMIT NAME=foo VALUE="+
"\"Put a FOO into the shopping cart\">"+
"<INPUT TYPE=SUBMIT NAME=bar VALUE="+
"\"Put a BAR into the shopping cart\">"+
"<INPUT TYPE=SUBMIT NAME=see VALUE="+
"\"See the shopping cart contents\">"+
"<INPUT TYPE=SUBMIT NAME=buy VALUE="+
"\"Buy the shopping cart contents\">"+
"</FORM></BODY></HTML>");
out.close();
}
The doPost method requires only a minor change. The Servlet's
URI which is used to link back to the "Online Shop" page needs to be
encoded with res.encodeUrl, as in the doGet
method.
[API 2.1]
Beginning with version 2.1 of the Servlet API, the abbrevation
URL is consistently spelled in upper case in all method names.
The old method names have been deprecated but are still supported in 2.1.
The method encodeUrl which is used in the shopping cart
Servlet should be replaced by encodeURL when writing a
2.1-compliant Servlet.
The full source code of the revised Shopping Cart Servlet is available as
ShoppingCartServlet2.java.