|
Introduction | Servlets & HTTP | Life-Cycle | Sessions | Summary | Examples Sessions: HTTP Session Tracking | Cookies | Servlet Sessions | Sessions At Work | ||
Working with SessionsStoring information with sessionsA session is used to store information particular to a client. For example a session can be used to: hold
the contents of a customer's shopping cart In the previous section we dealt exclusively with the life-time aspects of an HttpSession object. In this section we focus upon the remaining HttpSession methods, which are responsible for adding, retrieving and removing resources from a servlet session, namely: Object getValue(String theName); void putValue(String theName, Object theValue); void removeValue(String theName); String[] getValueNames(); Think of a session in much the same way as you do Java Properties, Dictionary and Hashtable objects. Sessions store named resources. Like typical Java collections, session comprise of a series name/value pairs, where the name is a String and the value can be any Java object. For future reference, bear in mind that their are reloading considerations for object values stored within servlet sessions that are discussed in the section on servlet reloading. In the following example, we keep a count of the number of times you submit the word neo to the SimpleSession servlet. This counter is kept as a property of the session: package com.ack.servlets; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpSession; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.*; public class SimpleSession extends HttpServlet { private static final String kNEO_COUNT = "neo.count"; private static final String kEXIT = "exit"; private static final String kNEO = "neo"; public void handleRequest(HttpServletRequest req, HttpServletResponse res, boolean isPost) throws ServletException, IOException { res.setContentType("text/html"); PrintWriter pw = res.getWriter(); pw.println("<HTML><BODY>"); pw.println("<FORM method=\"POST\" action=\"http://localhost:9999" + "/servlet/com.ack.servlets.SimpleSession\">"); pw.println("<INPUT type=\"text\" name=\"sessionValue\">"); pw.println("<INPUT type=\"submit\" >"); pw.println("</FORM>"); HttpSession theSession = req.getSession(true); Integer neoCount = (Integer)theSession.getValue(kNEO_COUNT); if( isPost ) { String textStr = req.getParameter("sessionValue"); if( kNEO.equals(textStr) ) { if( neoCount == null ) neoCount = new Integer(1); else neoCount = new Integer( neoCount.intValue() + 1 ); theSession.putValue(kNEO_COUNT, neoCount); pw.println("<P><B><I>Welcome to the real world...</I></B>"); } } pw.println("<P><P><H1>Number of neo's: " + ((neoCount == null) ? 0 : neoCount.intValue()) ); pw.println("</BODY></HTML>"); pw.close(); } public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { handleRequest(req,res,false); } public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { handleRequest(req,res,true); } } Note the use of the handleRequest() method. The SimpleSession servlet needs to respond to both HTTP POSTs and GETs. Instead of replicating code in both methods, handleRequest() provides a single implementation that both doGet() and doPost() delegate to upon receiving HTTP requests. The above example does not cater for the situation where cookies are disabled. If they are, then the session API needs to fall back upon URL rewriting to pass the sessionid between client and server as part of the URL. The session API cannot do this alone, and needs the servlet developer to do some additional coding to maintain session information using URL rewriting. To recap, the main headache with servlet rewriting is that you have to rewrite every URL that is passed to the client that participates in the session. As soon as the client clicks on URL that has not been rewritten to include the session id, the session is lost. The Servlet Session Tracing API provides two helper methods that do the URL rewriting on your behalf. you just have to remember to call them for all the URLs you pass back to the client. These helper methods are: String encodeURL(String theURLToEncode); String encodeRedirectURL(String theURLToEncode); Session EventsSometimes objects need to be informed when they are participating in a session. For example, a book object would like to know when it has been added to a customer's shopping trolley and when it has been removed. When such an object is added (bound) or removed (unbound) from a session they can receive an event informing them of the fact. In true Java style, objects that are interested receiving a particular event category implement the appropriate listener interface. For servlet session programming the two key entities are:
HttpSessionBindingListener An object that wants to be notified when it is being added/removed from a session implements HttpSessionBindingListener overriding its valueBound() and valueUnbound() methods. Here is an example of a NeoCounter object that wants to informaed when it is added to and remove from a session: class NeoCounter implements HttpSessionBindingListener { private int fNeoCount = 1; public int getValue() { return fNeoCount; } public void increment() { fNeoCount++; } public void valueBound(HttpSessionBindingEvent event) { System.out.println("ADDED NeoCounter " + event.getName() + " for session " + event.getSession().getId() ); } public void valueUnbound(HttpSessionBindingEvent event) { System.out.println("REMOVED NeoCounter " + event.getName() + " for session " + event.getSession().getId() ); } } The valueBound() and valueUnbound() methods simply dump informational text (object name and session id) to the web servers stdout. And that's it, NeoCounter are treated in exactly the same way as other objects that are stored within sessions, except that they receive session binding events. In the following example, whenever neo is entered in the form text box, we use putValue() to replace the old neoCount. This causes an valueUnbound event to be raised for the old neoCount followed by a valueBound() for the new neoCount. public class EventSession extends HttpServlet { private static final String kACTION_URL = "/servlet/com.ack.servlets.EventSession"; private static final String kNEO_COUNT = "neo.count"; private static final String kEXIT = "exit"; private static final String kNEO = "neo"; public void handleRequest(HttpServletRequest req, HttpServletResponse res, boolean isPost) throws ServletException, IOException { res.setContentType("text/html"); PrintWriter pw = res.getWriter(); // always display the form pw.println("<HTML><BODY>"); pw.println("<H1>" + kACTION_URL + "</H1>" ); pw.println("<FORM method=\"POST\" action=\"" + kACTION_URL + "\">"); pw.println("<INPUT type=\"text\" name=\"sessionValue\">"); pw.println("<INPUT type=\"submit\" >"); pw.println("</FORM>"); // get the current session and neoCount HttpSession theSession = req.getSession(true); NeoCounter neoCount = (NeoCounter)theSession.getValue(kNEO_COUNT); // on posts if( isPost ) { // check if neo was entered String textStr = req.getParameter("sessionValue"); if( textStr != null && kNEO.equals(textStr.trim()) ) { // if so update neo session counter if( neoCount == null ) neoCount = new NeoCounter(); else neoCount.increment(); theSession.putValue(kNEO_COUNT, neoCount); // and let the world not its a better place... pw.println("<P><B><I>Welcome to the real world...</I></B>"); } } // display current number of neo's entered pw.println("<P><P><H1>Number of neo's: " + ((neoCount == null) ? 0 : neoCount.getValue()) ); pw.println("</BODY></HTML>"); pw.close(); } // catches HTTP GETs, and delegates to handleRequest public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { handleRequest(req,res,false); } // catches HTTP POSTs, and delegates to handleRequest public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { handleRequest(req,res,true); } } Note that the code in the above example could be written as follows: if( neoCount == null ) { neoCount = new NeoCounter(); theSession.putValue(kNEO_COUNT, neoCount); } else { neoCount.increment(); } In doing so, we are no longer doing an unbound and bound on every neo text submission. This is because we add the NeoCounter only once to the session, and reference on all subsequent submissions of neo. Note now that the unbound occurs only when the session is invalidated. Moving to Servlet API version 2.2In version 2.2 of the Servlet API the methods for adding, removing and retrieving information from HttpSessions have changed. In effect all the methods xxxValuexxx() have been renamed so that they follow the xxxAttributexxx() naming convention. Also, the new getAttributeNames() does the right thing and returns an Enumeration and not an array of Strings. In summary, HttpSession objects have deprecated the Value methods: Object getValue(String theName); void putValue(String theName, Object theValue); void removeValue(String theName); String[] getValueNames(); and replaced them with Attribute methods: Object getAttribute(String theName); void putAttribute(String theName, Object theValue); void removeAttribute(String theName); Enumeration getAttributeNames(); But functionally, they do the same thing! | |||
(c) Zameer's Online Education 2006 |