ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

JSP 1.2: Great news for the JSP Community, Part 1
Pages: 1, 2, 3, 4

Tag Library Event Listeners

The Servlet 2.3 specification extends the event listener capability of previous versions. Previously, you've only been able to receive notifications about changes to session attributes, but starting with Servlet 2.3 you can implement listeners for servlet context and session life cycle events, servlet context attribute changes, and session activation and passivation events (used by a container that temporarily saves session state to disk or migrates a session to another server).



All new types of listeners follow the standard Java event model. In other words, a listener is a class that implements one or more of the new listener interfaces. The interfaces define methods that correspond to events. Your listener class is registered with the container when the application starts, and the container calls the event methods at the appropriate times.

You can bundle one or more event listeners with a tag library. To get the listeners registered, you just define the listener implementation classes in the Tag Library Descriptor (TLD) for your library using the new <listener> element:

<listener> 
  <listener-class>com.foo.MyListener</listener-class> 
</listener>

When the container loads the Web application, it looks through all TLDs for listener definitions and registers them.

You can use listeners for a number of tasks. For instance, a servlet context lifecycle event listener can initialize resources used by the application (such as a connection pool) when the application starts and shut them down gracefully when it stops. A session lifecycle listener can initialize new sessions or keep track of the number of active sessions.

Here's an example of a session lifecycle listener that keeps track of active sessions:

  package com.foo; 
 
import javax.servlet.*; 
import javax.servlet.http.*; 
public class MyListener implements HttpSessionListener { 
  public void sessionCreated(HttpSessionEvent hse) { 
        int[] counter = getCounter(hse); 
        counter[0]++; 
  } 
 
  public void sessionDestroyed(HttpSessionEvent hse) { 
    int[] counter = getCounter(hse); 
      counter[0]--; 
  } 
 
  private int[] getCounter(HttpSessionEvent hse) { 
    HttpSession session = hse.getSession(); 
    ServletContext context = session.getServletContext(); 
    int[] counter = (int[])  
                  context.getAttribute("com.foo.counter"); 
    if (counter == null) { 
        counter = new int[1]; 
        context.setAttribute("com.foo.counter", counter); 
    } 
    return counter; 
  } 
}

For every new session, a counter maintained as a servlet context attribute is incremented. When a session ends, the counter is decremented. Tags in the library can be used to simply display the current number of active session, reject new users if a certain number of sessions are active (to ensure optimal performance), or whatever makes sense in your application.

Validators

A brand new feature in JSP 1.2 is the tag library validator, represented by the javax.servlet.jsp.tagext.TagLibraryValidator class. The Web container uses the validator defined for a tag library when it converts a JSP page that uses the tag library to a servlet, after performing validation of the page based on the information available in the TLD (mandatory attributes, empty bodies). You implement a validator as a subclass of TagLibraryValidator and override the validate() method:

public ValidationMessage[] validate(String prefix,  
       String uri, PageData pageData)

The validator's validate() method is invoked with an instance of PageData. Through the PageData instance, the validator can get the XML representation of the page. This is either an XML View for a page written in the JSP Page format or a JSP Document (written from scratch using the JSP XML syntax). With access to the XML representation of the complete page, a validator can perform validation that would be impossible using just TLD information or the information about a single custom action element available to a TagExtraInfo class. For instance, a validator can verify that a custom action that must be used as a subelement of another action element is not used anywhere else, or that action elements are used in the appropriate order.

A validator is associated with a tag library through the new <validator> element in the TLD:

<validator> 
  <validator-class>com.foo.MyValidator</validator-class> 
</validator>

The <validator-class> element is used to specify the validator class name. Optional <init-param> elements can be used to configure a generic validator for a specific tag library.

Validation in Action

Here's an example of a validator that verifies that a <param> custom action element is only used within the body of a <redirect> action element from the same tag library. We look at the code piece by piece, starting with the class declaration:

package com.foo; 
 
import java.util.*; 
import javax.servlet.jsp.tagext.*; 
import org.jdom.*; 
import org.jdom.input.*; 
public class MyValidator extends TagLibraryValidator { 
 private SAXBuilder builder = new SAXBuilder(); 
 private Namespace jspNamespace = Namespace.getNamespace("jsp", 
   "http://java.sun.com/JSP/Page");

As you can see, the validator class extends the TagLibraryValidator class that is part of the JSP API. In this particular validator, I use JDOM to work with the XML representation of the page. I therefore import the JDOM packages containing classes for parsing and the JDOM tree. You can, of course, use any XML parser and validation tools you want. For instance, the Jakarta Taglibs project is experimenting with a lot of different XML tools in their validators.

I create an instance of the JDOM SAXBuilder class and save it as an instance variable. If the container caches instances of validators, this saves me from having to create this object for every page that's validated. I also create a JDOM Namespace for the JSP namespace as an instance variable. More about that later.

The validator must override the validate() method:

public ValidationMessage[] validate(String prefix, String uri, PageData pd) {
  
  ValidationMessage[] vms = null;
   ArrayList msgs = new ArrayList();
   try {
   Document doc = builder.build(pd.getInputStream());
   Element root = doc.getRootElement();
   validateElement(root, prefix, msgs);
   }
   catch (Exception e) {
   vms = new ValidationMessage[1];
   vms[0] = new ValidationMessage(null, e.getMessage()); }
  
   if (msgs.size() != 0) {
   vms = new ValidationMessage[msgs.size()];
   msgs.toArray(vms);
   }
   return vms;
  }

The validator() method gets the XML representation of the page and uses JDOM to parse the document. It then calls the validateElement() method with the document's root element, the prefix used for this tag library, and an ArrayList used to collect error messages. If the validateElement() method finds any errors, the message list is converted to an array of ValidationMessage instances, used as the return value.

As you will see later, a ValidationMessage instance contains the error message itself and possibly information about where the error was found in the JSP source file. The fact that the validate() method returns an array of ValidationMessage instances means that all errors found in the page can be presented in one shot, allowing the page author to fix them all at once instead of one by one.

The validateElement() method is a dispatcher to methods that validate specific elements:

private void validateElement(Element e, String ns, ArrayList msgs) {
  
  if (ns.equals(e.getNamespace().getPrefix())) {
    if (e.getName().equals("param")) {
        validateParam(e, ns, msgs);
    }
  }
  if (e.hasChildren()) {
    List kids = e.getChildren();
    Iterator i = kids.iterator();
    while(i.hasNext()) {
        validateElement((Element) i.next(), ns, msgs);
    }
  }
}

It's a recursive method that is called for all elements in the document tree. First it checks if the current element is part of the namespace for this tag library. It then checks if it's an element that needs to be validated, and if so, calls the appropriate method.

In this example I only validate elements of type param, but you can see how you could extend it to validate other elements as well.

Pages: 1, 2, 3, 4

Next Pagearrow