11.2. Bundling Listeners with Tag Libraries

Application life-cycle event listeners are described in Chapter 10. They provide a powerful new capability that lets you respond to the creation and deletion of the servlet context and HttpSession objects and lets you monitor changes in servlet context and session attributes. In most cases, listeners are declared with the listener element of the deployment descriptor (web.xml).

However, suppose that the behavior of a tag library depends upon an event listener. In such a case, you would want to be certain that the listeners were available in all Web applications that used the tag library.

If the listener and listener-class elements of web.xml were the only option for declaring listeners, tag library maintenance would be difficult. Normally, the user of a tag library deploys it by simply dropping a JAR file in WEB-INF/lib and putting a TLD file in WEB-INF. Users of the tag libraries need no knowledge of the individual classes within the library, only of the tags that the library defines. But if the tag libraries used listeners, users of the libraries would need to discover the name of the listener classes and make web.xml entries for each one. This would be less flexible and harder to maintain.

Fortunately, the JSP 1.2 specification lets you put the listener declarations in the tag library descriptor file instead of in the deployment descriptor. “Hold on!” you say, “Event listeners need to run when the Web application is first loaded, not just the first time a JSP page that uses a custom library is accessed. I thought TLD files were only loaded the first time a user requests a page that refers to it. How can this work?” Good question. JSP 1.2 introduced a new TLD search mechanism to handle this situation. When a Web application is loaded, the system automatically searches WEB-INF and its subdirectories for files with.tld extensions and uses all listener declarations that it finds in them. This means that your TLD files must be in the WEB-INF directory or a subdirectory thereof. In fact, although few servers enforce the restriction, the JSP 1.2 specification requires all TLD files to be in WEB-INF anyhow. Besides, putting the TLD files in WEB-INF is a good strategy to prevent users from retrieving them. So, you should make WEB-INF the standard TLD file location, regardless of whether your libraries use event handlers.

Core Approach

Always put your TLD files in the WEB-INF directory or a subdirectory thereof.


Unfortunately, there is a problem with this approach: Tomcat 4.0 ignores TLD files at Web application startup time unless there is a taglib entry in web.xml of the following form:

<taglib> 
  <taglib-uri>/someName.tld</taglib-uri> 
  <taglib-location>/WEB-INF/realName.tld</taglib-location> 
</taglib> 

As discussed in Section 5.13 (Locating Tag Library Descriptors), this entry is a good idea when the name of your tag library changes frequently. However, the JSP 1.2 specification does not require its use, and servers such as ServletExec 4.1 properly handle listener declarations in TLD files when there is no such entry. Nevertheless, Tomcat 4.0 requires it.

Core Warning

Tomcat 4.0 reads listener declarations only from TLD files that have taglib entries in web.xml.


Tracking Active Sessions

At busy sites, a significant portion of the server’s memory can be spent storing HttpSession objects. You might like to track session usage so that you can decide if you should lower the session timeout, increase the server’s memory allotment, or even use a database instead of the servlet session API.

Listing 11.3 shows a session listener that keeps a running count of the number of sessions in memory. The count is incremented each time a session is created; it is decremented whenever a session is destroyed (regardless of whether the session destruction is from an explicit call to invalidate or from timing out).

Listing 11.4 gives a custom tag that simply prints the count of active sessions. Listing 11.5 presents a related tag that prints a large, red warning if the number of sessions in memory exceeds a predefined maximum. Nothing is printed if the number of active sessions is within bounds.

Since it doesn’t make sense to use these tags unless the listener is in effect, the TLD file that declares the tags (Listing 11.6) also declares the listener. Finally, an alias for the TLD file is created with the taglib element of the web.xml deployment descriptor (Listing 11.7) to ensure that Tomcat will read the TLD file when the Web application is loaded and to allow developers to change the name of the TLD file without modifying the JSP pages that use it.

Listing 11.8 shows a JSP page that uses both of the custom tags. Figures 11-1 and 11-2 show the results when the number of sessions is below and above the predefined limit, respectively.

Figure 11-1. When the number of sessions in memory is less than the limit, the sessionCountWarning tag does not generate any output.


Figure 11-2. When the number of sessions in memory exceeds the limit, the sessionCountWarning tag generates a warning.


Listing 11.3. ActiveSessionCounter.java
package moreservlets.listeners; 

import java.io.*; 
import javax.servlet.*; 
import javax.servlet.http.*; 
import java.util.*; 

/** Listener that keeps track of the number of sessions 
 *  that the Web application is currently using. 
 */ 

public class ActiveSessionCounter 
                             implements HttpSessionListener {
  private static int sessionCount = 0; 
  private static int sessionLimit = 1000; 
  private ServletContext context = null; 

  /** Each time a session is created, increment the 
   *  running count. If the count exceeds the limit, 
   *  print a warning in the log file. 
   */ 
  public void sessionCreated(HttpSessionEvent event) {
							sessionCount++;
							if (context == null) {
							recordServletContext(event);
							}
							String warning = getSessionCountWarning();
							if (warning != null) {
							context.log(warning);
							}
							} 

  /** Each time a session is destroyed, decrement the 
   *  running count. A session can be destroyed when a 
   *  servlet makes an explicit call to invalidate, but it 
   *  is more commonly destroyed by the system when the time 
   *  since the last client access exceeds a limit. 
   */ 

  public void sessionDestroyed(HttpSessionEvent event) {
							sessionCount--;
							} 

  /** The number of sessions currently in memory. */ 

  public static int getSessionCount() {
							return(sessionCount);
							} 

  /** The limit on the session count. If the number of 
   *  sessions in memory exceeds this value, a warning 
   *  should be issued. 
   */ 

  public static int getSessionLimit() {
    return(sessionLimit); 
  } 

  /** If the number of active sessions is over the limit, 
   *  this returns a warning string. Otherwise, it returns 
   *  null. 
   */ 
  public static String getSessionCountWarning() {
							String warning = null;
							if (sessionCount > sessionLimit) {
							warning = "WARNING: the number of sessions in memory " +
							"(" + sessionCount + ") exceeds the limit " +
							"(" + sessionLimit + "). Date/time: " +
							new Date();
							}
							return(warning);
							} 
  private void recordServletContext(HttpSessionEvent event) {
    HttpSession session = event.getSession(); 
    context = session.getServletContext(); 
  } 
} 

Listing 11.4. SessionCountTag.java
package moreservlets.tags; 

import javax.servlet.*; 
import javax.servlet.jsp.*; 
import javax.servlet.jsp.tagext.*; 
import java.io.*; 
import moreservlets.listeners.*; 

/** Prints out the number of active sessions. */ 

public class SessionCountTag extends TagSupport {
  public int doStartTag() {
    try {
      JspWriter out = pageContext.getOut(); 
      out.print(ActiveSessionCounter.getSessionCount()); 
    } catch(IOException ioe) {
      System.out.println("Error printing session count."); 
    } 
    return(SKIP_BODY); 
  } 
} 

Listing 11.5. SessionCountWarningTag.java
package moreservlets.tags; 

import javax.servlet.*; 
import javax.servlet.jsp.*; 
import javax.servlet.jsp.tagext.*; 
import java.io.*; 
import moreservlets.listeners.*; 

/** If the number of active sessions is above the limit, 
 *  this prints a warning. Otherwise, it does nothing. 
 */ 

public class SessionCountWarningTag extends TagSupport {
  public int doStartTag() {
    try {
      String warning =
							ActiveSessionCounter.getSessionCountWarning();
							if (warning != null) {
							JspWriter out = pageContext.getOut();
							out.println("<H1><FONT COLOR="RED">");
							out.println(warning);
							out.println("</FONT></H1>");
							} 
    } catch(IOException ioe) {
      System.out.println("Error printing session warning."); 
    } 
    return(SKIP_BODY); 
  } 
} 

Listing 11.6. session-count-taglib-0.9-beta.tld
<?xml version="1.0" encoding="ISO-8859-1" ?> 
<!DOCTYPE taglib 
 PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN" 
 "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd"> 

<taglib> 
  <tlib-version>0.9</tlib-version> 
  <jsp-version>1.2</jsp-version> 
  <short-name>company-name-tags</short-name> 
  <description> 
    A tag library that lets you print out the number of 
    sessions currently in memory and/or a warning about 
    the session count exceeding the limit. 

    The tlib-version number and the TLD filename are intended 
    to suggest that this tag library is in development. In 
    such a situation, you want to use the web.xml taglib 
    element to define an alias for the TLD filename. You 
    would want to do so even if you weren't trying 
    to accommodate Tomcat 4.0, which only reads listener 
    declarations from TLD files that are declared that way. 
  </description> 

  <!-- Register the listener that records the counts. --> 
  <listener>
							<listener-class>
							moreservlets.listeners.ActiveSessionCounter
							</listener-class>
							</listener> 

  <!-- Define a tag that prints out the session count. --> 
  <tag>
							<name>sessionCount</name>
							<tag-class>
							moreservlets.tags.SessionCountTag
							</tag-class>
							<body-content>empty</body-content>
							<description>The number of active sessions.</description>
							</tag> 

  <!-- Define a tag that prints out an optional 
       session-count warning. --> 
  <tag>
							<name>sessionCountWarning</name>
							<tag-class>
							moreservlets.tags.SessionCountWarningTag
							</tag-class>
							<body-content>empty</body-content>
							<description>
							If the number of sessions exceeds the limit,
							this prints a warning. Otherwise, it does nothing.
							</description>
							</tag> 
</taglib> 

Listing 11.7. web.xml (For session-counting tags)
<?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> 
  <!-- If URL gives a directory but no filename, try index.jsp 
       first and index.html second. If neither is found, 
       the result is server specific (e.g., a directory 
       listing).  Order of elements in web.xml matters. 
       welcome-file-list needs to come after servlet but 
       before error-page. 
  --> 
  <welcome-file-list> 
    <welcome-file>index.jsp</welcome-file> 
    <welcome-file>index.html</welcome-file> 
  </welcome-file-list> 

  <!-- Register the company-name tag library. Declare an alias 
       for the TLD filename since the tag library is under 
       development and thus the TLD filename might change. 
       You don't want to change all the JSP files each time 
       the TLD file changes. Besides, Tomcat 4.0 won't pick 
       up listener declarations from TLD files unless they 
       are declared this way. 
  --> 
  <taglib>
							<taglib-uri>
							/session-count-taglib.tld
							</taglib-uri>
							<taglib-location>
							/WEB-INF/session-count-taglib-0.9-beta.tld
							</taglib-location>
							</taglib> 
</web-app> 

Listing 11.8. index.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> 
<HTML> 
<HEAD> 
<TITLE>VIP</TITLE> 
<LINK REL=STYLESHEET 
      HREF="styles.css" 
      TYPE="text/css"> 
</HEAD> 

<BODY> 
<TABLE BORDER=5 ALIGN="CENTER"> 
  <TR><TH CLASS="TITLE"> 
      Very Important Page 
</TABLE> 
<P> 
Blah, blah, blah. 
<P> 
Yadda, yadda, yadda. 
<HR> 
<!-- Note that the uri refers to the location defined by 
     the taglib element of web.xml, not to the real 
     location of the TLD file. --> 
<%@ taglib uri="/session-count-taglib.tld" prefix="counts" %> 
Number of sessions in memory:  <counts:sessionCount/>
							<counts:sessionCountWarning/> 
</BODY> 
</HTML> 

Testing Session Counts

On a development system, it is often difficult to test session usage because few (if any) outside users are accessing the server. So, it is helpful to generate sessions manually. The simplest way to do this is to disable cookies in your browser and access a framed page that loads the same JSP page multiple times. For details on the process, see Section 10.7 (Recognizing Session Creation and Destruction).

To test the session-counting page shown earlier in this section, I used the same JSP page as in Section 10.7 —a blank page with a randomized background color (Listing 11.9; uses the utility class of Listing 11.10). Listing 11.11 shows a frame-based page that, each time it is loaded, loads 49 copies of the JSP page. Figure 11-3 shows a typical result.

Figure 11-3. The page used to force the system to create new sessions.


Listing 11.9. 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 11.10. 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)); 
  } 
} 

Listing 11.11. make-sessions.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN"> 
<HTML> 
<HEAD> 
  <TITLE>Session Testing...</TITLE> 
</HEAD> 
<!-- Load the same JSP page 49 times. If cookies are 
     disabled and the server is using cookies for session 
     tracking (the default), loading this HTML page causes 
     the system to create 49 new sessions. --> 
<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"> 
  <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"> 
  <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"> 
  <FRAME SRC="test.jsp"> 
  <NOFRAMES><BODY> 
    This example requires a frame-capable browser. 
  </BODY></NOFRAMES> 
</FRAMESET> 
</HTML> 

..................Content has been hidden....................

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