- Monitoring Creation and Destruction of the Servlet Context
- Example: Initializing Commonly Used Data
- Detecting Changes in Servlet Context Attributes
- Example: Monitoring Changes to Commonly Used Data
- Packaging Listeners with Tag Libraries
- Example: Packaging the Company Name Listeners
- Recognizing Session Creation and Destruction
- Example: A Listener That Counts Sessions
- Watching for Changes in Session Attributes
- Example: Monitoring Yacht Orders
- Using Multiple Cooperating Listeners
- The Complete Events Deployment Descriptor
10.8 Example: A Listener That Counts Sessions
Session tracking can significantly increase the server's memory load. For example, if a site that uses session tracking has 1,000 unique visitors per hour and the server uses a two-hour session timeout, the system will have approximately 2,000 sessions in memory at any one time. Reducing the timeout to one hour would cut the session memory requirements in half but would risk having active sessions prematurely time out. You need to track typical usage before you can decide on the appropriate solution.
So, you need a listener that will keep track of how many sessions are created, how many are destroyed, and how many are in memory at any one time. Assuming that you have no explicit calls to invalidate, the session destructions correspond to expired timeouts.
The following steps summarize a listener that accomplishes this task.
Implement the HttpSessionListenerinterface. Listing 10.20 shows a class (SessionCounter) that implements this interface.
Override sessionCreatedand sessionDestroyed. The first of these (sessionCreated) increments two counters: totalSession-Countand currentSessionCount. If the current count is greater than the previous maximum count, the method also increments the maxSessionCountvariable. The second method (session-Destroyed) decrements the currentSessionCountvariable.
Obtain and use the servlet context. In this application, no specific use is made of the session object. The only thing that matters is the fact that a session was created or destroyed, not any details about the session itself. But, the session counts have to be placed in a location that is easily accessible to servlets and JSP pages that will display the counts. So, the first time sessionCreatedis called, it obtains the session object, calls getServletContexton it, and then calls set-Attributeto store the listener object in the servlet context.
Declare the listener. Listing 10.21 shows the web.xml file. It declares the listener with the listenerand listener-classele-ments, as below.
<listener> <listener-class> moreservlets.listeners.SessionCounter </listener-class> </listener>
Listing 10.22 shows a JSP page that displays the session counts. Figures 1011 shows a typical result.
Figures 1011 The SessionCounter listener keeps track of the sessions used in the Web application.
In order to test session creation and timeout, I made three temporary changes. First, I disabled cookies in my browser. Since my servers are set to use cookies for session tracking, this had the result of making each request be a new session. See the following subsection for information on disabling cookies in Netscape and Internet Explorer.
Second, I created an HTML page (Listing 10.23, Figures 1012) that used frames with four rows and four columns to request the same JSP page (Listing 10.24) 16 times. In an environment that has cookies disabled, a request for the framed page results in 16 new sessions being created on the server (recall that JSP pages perform session tracking automatically unless the session attribute of the page directive is set to falsesee Section 3.4).
Figures 1012 Session management was tested with a frame-based page that was invoked after cookies were disabled. So, each request resulted in 16 different sessions.
Third, I chose an extremely low session timeout: two minutes. This saved me from waiting for hours to test the session-counting listener. Changing the default session timeout is discussed in Section 5.10, but it simply amounts to creating a session-config entry in web.xml, as follows.
<session-config> <session-timeout>2</session-timeout> </session-config>
Listing 10.20 SessionCounter.java
package moreservlets.listeners; import java.io.*; import javax.servlet.*; import javax.servlet.http.*; /** Listener that keeps track of the number of sessions * that the Web application is currently using and has * ever used in its life cycle. */ public class SessionCounter implements HttpSessionListener { private int totalSessionCount = 0; private int currentSessionCount = 0; private int maxSessionCount = 0; private ServletContext context = null; public void sessionCreated(HttpSessionEvent event) { totalSessionCount++; currentSessionCount++; if (currentSessionCount > maxSessionCount) { maxSessionCount = currentSessionCount; } if (context == null) { storeInServletContext(event); } } public void sessionDestroyed(HttpSessionEvent event) { currentSessionCount--; } /** The total number of sessions created. */ public int getTotalSessionCount() { return(totalSessionCount); } /** The number of sessions currently in memory. */ public int getCurrentSessionCount() { return(currentSessionCount); } /** The largest number of sessions ever in memory * at any one time. */ public int getMaxSessionCount() { return(maxSessionCount); } // Register self in the servlet context so that // servlets and JSP pages can access the session counts. private void storeInServletContext(HttpSessionEvent event) { HttpSession session = event.getSession(); context = session.getServletContext(); context.setAttribute("sessionCounter", this); } }
Listing 10.21 web.xml (Excerpt for session counting listener)
<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app> <!-- ... --> <!-- Register the session counting event listener. --> <listener> <listener-class> moreservlets.listeners.SessionCounter </listener-class> </listener> <!-- ... --> <!-- Set the default session timeout to two minutes. --> <session-config> <session-timeout>2</session-timeout> </session-config> <!-- ... --> </web-app>
Listing 10.22 session-counts.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <HTML> <HEAD> <TITLE>Session Info</TITLE> <LINK REL=STYLESHEET HREF="events-styles.css" TYPE="text/css"> </HEAD> <BODY> <TABLE BORDER=5 ALIGN="CENTER"> <TR><TH CLASS="TITLE">Session Info</TABLE> <P> <jsp:useBean class="moreservlets.listeners.SessionCounter" id="sessionCounter" scope="application" /> <UL> <LI>Total number of sessions in the life of this Web application: <jsp:getProperty name="sessionCounter" property="totalSessionCount" />. <LI>Number of sessions currently in memory: <jsp:getProperty name="sessionCounter" property="currentSessionCount" />. <LI>Maximum number of sessions that have ever been in memory at any one time: <jsp:getProperty name="sessionCounter" property="maxSessionCount" />. </UL> </BODY> </HTML>
Listing 10.23 make-sessions.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN"> <HTML> <HEAD> <TITLE>Session Testing...</TITLE> </HEAD> <FRAMESET ROWS="*,*,*,*" COLS="*,*,*,*"> <FRAME SRC="test.jsp"> <FRAME SRC="test.jsp"> <FRAME SRC="test.jsp"> <FRAME SRC="test.jsp"> <FRAME SRC="test.jsp"> <FRAME SRC="test.jsp"> <FRAME SRC="test.jsp"> <FRAME SRC="test.jsp"> <FRAME SRC="test.jsp"> <FRAME SRC="test.jsp"> <FRAME SRC="test.jsp"> <FRAME SRC="test.jsp"> <FRAME SRC="test.jsp"> <FRAME SRC="test.jsp"> <FRAME SRC="test.jsp"> <FRAME SRC="test.jsp"> <NOFRAMES><BODY> This example requires a frame-capable browser. </BODY></NOFRAMES> </FRAMESET> </HTML>
Listing 10.24 test.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <!-- The purpose of this page is to force the system to create a session. --> <HTML> <HEAD><TITLE>Test</TITLE></HEAD> <%@ page import="moreservlets.*" %> <BODY BGCOLOR="<%= ColorUtils.randomColor() %>"> </BODY></HTML>
Listing 10.25 ColorUtils.java
package moreservlets; /** Small utility to generate random HTML color names. */ public class ColorUtils { // The official HTML color names. private static String[] htmlColorNames = { "AQUA", "BLACK", "BLUE", "FUCHSIA", "GRAY", "GREEN", "LIME", "MAROON", "NAVY", "OLIVE", "PURPLE", "RED", "SILVER", "TEAL", "WHITE", "YELLOW" }; public static String randomColor() { int index = randomInt(htmlColorNames.length); return(htmlColorNames[index]); } // Returns a random number from 0 to n-1 inclusive. private static int randomInt(int n) { return((int)(Math.random() * n)); } }
Disabling Cookies
Figures 1013 through 1015 summarize the approach to disabling cookies in Netscape 4, Netscape 6, and Internet Explorer 5. As discussed in the previous subsection, temporarily disabling cookies is useful for testing session usage.
Figures 1013 To disable cookies in Netscape 4, choose the Edit menu, then Preferences, then Advanced. Select "Disable cookies." Reset the browser after you are done testing; my preferred setting is "Accept only cookies that get sent back to the originating server."
Figures 1014 To disable cookies in Netscape 6, choose the Edit menu, then Preferences, then Privacy and Security, then Cookies. Select "Disable cookies." Reset the browser after you are done testing; my preferred setting is "Enable cookies for the originating web site only."
Figures 1015 To disable cookies in Internet Explorer 5, choose the Tools menu, then Internet Options, then Security, then Custom Level. Since the goal here is to generate multiple sessions, you only need to disable per-session cookies. When done testing, reset the browser by changing the setting back to Enable.