Saturday, October 5, 2024

Taming your Tomcat: Filtering tricks for Tomcat 5

Designing maintainable high-performance systems

The new Tomcat 5 server takes filters to a new level of deployment flexibility. Tomcat 5’s support for the upcoming Servlet 2.4 and JSP 2.0 specifications gives filter writers a new way to integrate and deploy these flexible components — tapping directly into the request dispatcher’s operations. In this article, Sing Li takes you on a guided tour of the new enhancement and gives you some hands-on training. See how Tomcat 5 can benefit Web application frameworks and lead ultimately to the design of maintainable high-performance systems.

Back in June 2001, I wrote an article that previewed the (then) brand-new Servlet 2.3 feature called filtering (see Resources). The Tomcat 4.x server supported this feature, and filters enabled Web application developers to create flexible application components that could be inserted and chained into the application server’s request flow — before and after the actual request was processed. And because filters are packaged as application-level components, on a par with JSP pages and servlets, they can easily be bundled in the same WAR file for deployment on any Servlet 2.3-compliant server. Since then, major application server vendors have embraced the Servlet 2.3 standard to support filtering, and filters have become a cornerstone component of many Web applications.

Readers who are unfamiliar with filtering operations — or who would like a refresher on how to construct filters and how filters work — are encouraged to read the first Tomcat filtering article.

Servlet 2.4 filter enhancements

Based on actual “in-the-field” feedback from the Web development community, the Servlet 2.4 specification — now being finalized — gives filters a single, highly important new capability. This new capability finally enables filters to play on completely level ground with servlets and JSP pages, enabling Web application designers to add major application functionality using filters. This new capability also facilitates a highly popular new style of Web application design called JSP Model 2, or Model View Controller (MVC) Web application design. (See Resources for more information on JSP Model 2 or MVC-styled Web application design.)

Filters in the request dispatcher’s processing flow

The new filtering feature in Servlet 2.4 basically alleviates the restriction that filters can only operate in the request flow before and after the actual request processing by the application server. Instead, Servlet 2.4 filters can now interact with the request dispatcher at every dispatch point. This means that when a Web resource forwards a request to another resource (for instance, a servlet forwarding the request to a JSP page in the same application), a filter can be operating before the request is handled by the targeted resource. It also means that should a Web resource include the output or function from other Web resources (for instance, a JSP page including the output from multiple other JSP pages), Servlet 2.4 filters can work before and after each of the included resources. Figure 1 illustrates the difference between Servlet 2.3 and Servlet 2.4’s dispatcher interactions.

Figure 1. Servlet 2.3 and Servlet 2.4 filter differences
Servlet 2.3 and Servlet 2.4 Filter Differences

Controlling filter dispatcher interactions

The interaction of filters with the container’s request processing flow is configured through the element definition in the deployment descriptor (web.xml file). This is the convention established in the Servlet 2.3 specification, and it has not changed in Servlet 2.4. Instead, the new ability to interact with the dispatcher is controlled through a new optional sub-element. Listing 1 shows a element in a deployment descriptor that uses the sub-element to apply a filter called “Blue Filter” to any JSP page that may be included (assuming that all the JSP pages in the application are mapped to the /jsp/* URL pattern):

Listing 1. Servlet 2.4-styled filter mapping



           Blue Filter
           /jsp/*
           INCLUDE
 

To apply the same filter to dispatcher-forwarded resources as well as included resources, we simply add another sub-element, as shown in Listing 2:

Listing 2. Mapping forwards and includes



           Blue Filter
           /jsp/*
           INCLUDE
           FORWARD
 

The values that a sub-element can contain are shown in Table 1:

Table 1. Possible values for a sub-element

ValueDescriptionREQUESTApply filter to request originating from the client — exactly the same as the default behavior in Servlet 2.3-compliant containers.FORWARDApply filter to request as it is being forwarded using the Dispatcher.forward() call between Web resources.INCLUDEApply filter to request as Web resources are being included through Dispatcher.include() calls.ERRORApply filter to an error processing resource (essentially a container-managed forward mechanism when an error occurs).

All of the values in Table 1, of course, are still subjected to the match specified by the sub-element of . Essentially, only the URLs that match the and the sub-element will be filtered.

To better appreciate the operation of the new filter/dispatcher interactions, let’s put some filters to work in a Tomcat server.

Downloading and installing Tomcat 5 plus JSTL
All code has been fully tested under Tomcat 5.0 alpha, the latest release build available at the time this article was written. In addition to installing Tomcat, you need to download the JSTL 1.0 binaries. Place standard.jar and jstl.jar into the webapps/dvworks/web-inf/lib directory, and all the TLD files under the webapps/dvworks/web-inf directory. (See Resources) for more information on installing Tomcat and downloading the JSTL 1.0 binaries.)

Configuring Tomcat 5 filters

Assuming you have Tomcat 5 installed and running with the JSP Standard Tag Library 1.0 (see the sidebar “Downloading and installing Tomcat 5 plus JSTL”), we can now work with some actual filter examples. Download the source code to get our sample Web application.

Let’s look at the coding of a couple of JSP pages and filters. The resources we’ll work with initially are:

  • /jsp/filtercount.jsp
  • /jsp/included.jsp
  • AddAttributesFilter
  • RedFilter

The filtercount.jsp resource is quite simple, and consists of the code shown in Listing 3:

Listing 3. Code for filtercount.jsp







developerWorks Tomcat 5 Filters


The BlueFilter has been activated ${requestScope.BlueCounter} times. The RedFilter has been activated ${requestScope.RedCounter} times.

This JSP page calls the dispatcher to include another JSP page called /jsp/included.jsp. It also uses the JSTL Expression Language to print out the value of two counters (RedCounter and BlueCounter) that are attached as attributes to the incoming request, if the counter exists, and if the count is greater than zero.

In fact, each counter is used to count the number of times that either RedCounter or BlueCounter is called during request processing. Table 2 shows the filters we will use and what they actually do.

Table 2. Names and functions of filters in experiment scenarios

Filter NameDescriptionAddAttributesFilterAdd the BlueCounter and RedCounter attributes to the incoming request.RedFilterIncrement the value of the RedCounter attribute attached to the request.BlueFilterIncrement the value of the BlueCounter attribute attached to the request.

All of the filters in Table 2 are part of the com.ibm.dvworks.filters package.

The code for com.ibm.dvworks.filters.AddAttributesFilter is presented in Listing 4. Note that it simply creates the two counter attributes as java.lang.Integer type, which has a value of 0.

Listing 4. Code for AddAttributesFilter.java


package com.ibm.devworks.filters;

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


public final class AddAttributesFilter implements Filter {

    public static final String RED_COUNTER_ATTRIB = "RedCounter";
    public static final String BLUE_COUNTER_ATTRIB = "BlueCounter";


    private FilterConfig filterConfig = null;

    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain)
	throws IOException, ServletException {

        request.setAttribute(RED_COUNTER_ATTRIB, new Integer(0));
        request.setAttribute(BLUE_COUNTER_ATTRIB, new Integer(0));
        filterConfig.getServletContext().log("Inside AddAttributesFilter");
        chain.doFilter(request, response);

    }


    public void destroy() {
        this.filterConfig = null;
    }


    public void init(FilterConfig filterConfig) {
	this.filterConfig = filterConfig;

    }

}

The construction of this filter follows the basic filter coding pattern. Again, refer to the first article for more details on coding filters.

Similarly, com.ibm.dvworks.filters.RedFilter has the code shown in Listing 5. Note that the code simply looks for the RedCounter attribute and increments it if found.

Listing 5. Code for RedFilter.java


package com.ibm.devworks.filters;

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


public final class RedFilter implements Filter {

    public static final String COUNTER_ATTRIB = "RedCounter";
    private FilterConfig filterConfig = null;

    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain)
	throws IOException, ServletException {

    filterConfig.getServletContext().log("Inside RedFilter");
    Integer curCount = (Integer) request.getAttribute(COUNTER_ATTRIB);
    if (curCount != null)
        request.setAttribute(COUNTER_ATTRIB, 
            new Integer(curCount.intValue() + 1));

      chain.doFilter(request, response);

    }


    public void destroy() {
        this.filterConfig = null;
    }


    public void init(FilterConfig filterConfig) {
	this.filterConfig = filterConfig;

    }

}

The com.ibm.dvworks.filters.BlueFilter class has identical code to RedFilter, except the constant COUNTER_ATTRIB is defined to be BlueFilter, as shown in Listing 6:

Listing 6. Code change for BlueFilter


public static final String COUNTER_ATTRIB = "BlueCounter";

The filtercount.jsp file uses the dispatcher to include the included.jsp file. The included.jsp file simply creates a table that is filled with the color specified by the input parameter called BoxColor. Note the use of the Expression Language (EL) in rendering the color in the table, as shown in Listing 7:

Listing 7. Code for included.jsp



       
      this page is included in-line using dispatcher
      

With these filters and JSP pages, we are ready to begin our exploration of five different filter configuration scenarios.

Scenario 1: Standard Servlet 2.3 filter interaction — REQUEST only
In this first scenario, we will configure our filters to work in a manner supported by older Servlet 2.3 containers.

The element that we will use is shown in Listing 8:

Listing 8. Filter mapping for Standard Servlet 2.3 compatibility



  Add Attributes Filter
  /jsp/*


  Red Filter
  /jsp/*

Edit your web.xml to reflect the Listing 8 configuration. Here, we are not specifying a sub-element. This means that the filters will be applied on a REQUEST-only basis, as it did in Servlet 2.3 containers. The configuration in Listing 8 is operationally identical to the code in Listing 9:

Listing 9. Equivalent Servlet 2.4 filter mapping



  Add Attributes Filter
  /jsp/*
  REQUEST


  Red Filter
  /jsp/*
  REQUEST

Start Tomcat 5, and access the Web application using the http://:8080/dvworks/jsp/filtercount.jsp URL. Figure 2 shows the response generated by filtercount.jsp:

Figure 2. Servlet 2.3-compatible REQUEST filtering
Servlet 2.3 Compatible REQUEST Filtering

According to the filtercount.jsp response, RedFilter has been called only once in this scenario. Figure 3 is a detailed interaction diagram showing the request flow through the request processors:

Figure 3. Servlet 2.3 filtering request flow
Servlet 2.3 Filtering Request Flow

In Figure 3, the filter chain is called only once — as the request enters the system from the client.
The filter chain, of course, consists of AddAttributesFilter and RedFilter, as shown in Figure 4:

Figure 4. Details of the filter chain
Details of the filter chain

In this Servlet 2.3-styled configuration, RedFilter is applied only once on the incoming request directly from the client.

Scenario 2: Combining REQUEST and INCLUDE interactions
The filter mapping we will use for this scenario is shown in Listing 10. Note the use of the Servlet 2.4 syntax and the addition of the INCLUDE dispatcher sub-element to RedFilter.

Listing 10. Filter mapping for combined REQUEST and INCLUDE interactions



  Add Attributes Filter
  /jsp/*
  REQUEST


  Red Filter
  /jsp/*
  REQUEST
  INCLUDE

Restart Tomcat 5 and access the application again with the http://:8080/dvworks/jsp/filtercount.jsp URL. The response shown in Figure 5 displays the filter count.

Figure 5. REQUEST and INCLUDE filtering

REQUEST and INCLUDE Filtering

Now RedFilter has been called twice — once during the incoming request, and the second time with the inclusion of included.jsp. The two calls are detailed in the interaction diagram shown in Figure 6. Note that we have simplified the diagram by showing only the flow of the request into the system (and not the detailed return flow as was done previously).

Figure 6. Combined REQUEST and INCLUDE interaction
Combined REQUEST and INCLUDE Interaction

Scenario 3: Combining REQUEST, INCLUDE, and FORWARD interactions
Next, we will bring in another simple JSP page, called forwarder.jsp, shown in Listing 11. It simply uses the dispatcher to forward the incoming request to filtercount.jsp.

Listing 11. Code for forwarder.jsp



Now we can add BlueFilter to the deployment descriptor, as shown in Listing 12:

Listing 12. Filter mapping for combined REQUEST, INCLUDE, and FORWARD interactions



  Add Attributes Filter

  /jsp/*
  REQUEST


  Red Filter
  /jsp/*
  REQUEST
  INCLUDE


  Blue Filter
  /jsp/*
  FORWARD

BlueFilter is configured to handle forwarded requests only, while the rest of the filter mapping remains unchanged.

Restart Tomcat 5 and access the application with the http://:8080/dvworks/jsp/forwarder.jsp URL. Figure 7 shows the response.

Figure 7. REQUEST, INCLUDE, and FORWARD filtering

REQUEST, INCLUDE, and FORWARD Filtering

In this case, we see that BlueFilter is actually called once — during the forwarding of the request from forwarder.jsp to filtercount.jsp. The interaction diagram in Figure 8 shows the flow.

Figure 8. Combined REQUEST, INCLUDE, and FORWARD interactions
Combined REQUEST, INCLUDE, and FORWARD Interactions

Scenario 4: Combining REQUEST, INCLUDE, FORWARD, and legacy Servlet 2.3 interactions
In this scenario, we are adding a filter from the previous filtering article (see Resources). You can use ReplaceTextFilter to replace text in the response. We configure it using initial parameters in the definition shown in Listing 13:

Listing 13. Filter definition for ReplaceTextFilter with initialization parameters


    
        Replace Text Filter
        com.ibm.devworks.filters.ReplaceTextFilter
        
         
	    search
	    color="red"
	  
        
	    replace
	    color="green"
	  
    

We then add the following to the ones used in the previous scenario, as shown in Listing 14:

Listing 14. Filter mapping for combined REQUEST, INCLUDE, FORWARD, and legacy Servlet 2.3 filter interactions



  Replace Text Filter
  /jsp/*

...

Note that we are not specifying a sub-element here, and therefore this filter is defaulting to REQUEST-only processing.

Restart Tomcat 5 and access the http://:8080/dvworks/jsp/forwarder.jsp URL. You should see something similar to Figure 9. Unlike the previous configuration, now the included box is green.

Figure 9. Combined REQUEST, INCLUDE, FORWARD, and legacy filtering

Combined REQUEST, INCLUDE, FORWARD and Legacy Filtering

The interaction diagram shown in Figure 10 illustrates how ReplaceTextFilter is handed the response for final processing before passing it back to the client. It is here that the color of the table is changed from red to green.

Figure 10. Combined REQUEST, INCLUDE, FORWARD, and legacy interactions
Combined REQUEST, INCLUDE, FORWARD and Legacy Interactions

Servlet 2.4 containers are completely backward compatible with Servlet 2.3 filters, definitions, and mappings.

Scenario 5: Filtering on ERROR dispatch
For the final scenario, we will briefly examine the application of a filter when dispatching to an error-handling page. The filter mapping that we will use is in Listing 15. Make sure you remove all other filter mappings in web.xml.

Listing 15. Filtering on ERROR



  Red Filter
  /jsp/*
  REQUEST
  INCLUDE
  ERROR

To purposely generate an error, we create a JSP page, called errorgen.jsp, which sets up a page called errorhdlr.jsp to handle an error, and then throws an exception, as shown in Listing 16:

Listing 16. Code for errorgen.jsp






When an error is caught by the page, the container forwards the request to the /jsp/errorhdlr.jsp page, as specified in the @page directive. The errorhdlr.jsp page contains the code shown in Listing 17:

Listing 17. Code for errorhdlr.jsp








developerWorks Tomcat 5 Error Page


The BlueFilter has been activated ${requestScope.BlueCounter} times.
The RedFilter has been activated ${requestScope.RedCounter} times.

Error information:
${pageContext.errorData.throwable.message}

The errorhdlr.jsp prints out the filter counter values, then prints out the message of the exception being caught using the EL.

Restart Tomcat 5 and access the http://:8080/dvworks/jsp/errorgen.jsp URL. Even though you attempt to access errorgen.jsp, it is errorhdlr.jsp that will be displayed (because an exception is explicitly thrown). The error page that you will see is shown in Figure 11:

Figure 11. REQUEST and ERROR filtering

REQUEST and ERROR Filtering

Note that RedFilter has been called twice — once at the REQUEST level and a second time when the container forwards to the error page.

You can apply Servlet 2.4 filters just before the processing of a custom error-handling Web resource.

Filters and Model 2 Web application frameworks
The enhanced filtering capability in Tomcat 5 provides new deployment opportunities when using popular application frameworks. Frameworks such as Struts and Turbine (see Resources) use a JSP Model 2 architecture to separate data operations (the Model in an MVC pattern) from the business logic operations, and the presentation operations (the View). Their functionality centers around the use of a switching servlet (the Controller servlet) that forwards incoming requests to different user-created Action components, either according to a static XML directive file or using runtime introspection. These requests are typically further forwarded between components, until the response is complete. The Model 2 architecture enables designers to componentize their server-side coding for adaptability to change and easier long-term maintenance.

Because the dispatcher is used extensively to forward requests between the controller servlet and the application components, as well as between application components, you can readily add filters to the processing flow. With the Servlet 2.4 enhancements, filters can now become an integral part of a Web application’s components mix — when using Model 2 application frameworks.

An architectural pattern from the hardware domain
A novel way of thinking of a cluster of servers running a maximally scaled Web application is through the analogy of a microprocessor. Incoming requests span a finite set of possible operations like a CPU’s instruction set. Processing of requests by the servers is analogous to the execution of instructions by the hardware core.

Pipelining your application toward a high-performance future
Pipelining is a request flow-centric view of componentized request processing. It focuses on the multistage processing of a single request as it flows through the application server.

In the pipeline model, a request flows through an assembly line (the pipeline) while at each stage being modified/transformed/processed by a series of processors, until finally the response is generated and expedited back to the client. Popular Web application frameworks such as Jakarta Struts and Turbine, through the virtue of their JSP Model 2 architecture, already componentize the server-side logic in a pipelined fashion.

Figure 12 shows the typical pipeline stages that a Web application or service request may flow through when displaying a page of data:

Figure 12. Typical stages of the pipeline model

Typical Stages of the Pipeline Model

In summary, the request is passed through the three pipeline stages shown in Table 3:

Table 3. Pipeline model stages

Name of pipeline stageDescription of typical operation performedData fetchFetches data from external systems (such as JDBC datasource), based on information from the incoming requestProcessingValidates or transforms data elements using the value of the data element to look up further data, such as from internal or external sources
RenderingCreates the output response based on the processed data, typically in a visual format (such as HTML) or an interchange format (such as XML)

By placing design focus on the request processing pipeline, Web application design is entering a new era of performance tuning and optimization possibilities. Many of the same techniques being exploited in modern microprocessor hardware design — in the optimization of pipelines — can be leveraged in the near future for optimization of Web application server software and hardware.

For example, in a Web application that has similar requirements for every request flowing through the data fetch stage of processing, the optimized application server/hardware platform can prefetch and locally cache the most commonly accessed data — completely eliminating the data fetch overhead for most incoming requests. Another example — for requests that demand significant CPU processing resources in the rendering stage, such as XSLT transformation or other XML parsing — a parallel bank of hardware-accelerated XML processors can be configured in the rendering stage of the pipeline. While these optimizations are still slightly futuristic in terms of practical ability to implement them today, designing Web applications with consideration for the pipeline model is a necessary element to make them reality.

Conclusions

Tomcat 5’s new filtering feature enables us to tap into every stage of the Web application’s request-processing flow. This capability facilitates the use of filters with application frameworks and opens up new performance optimization and tuning possibilities.

Resources

Sing Li is the author of Early Adopter JXTA and Professional Jini, as well as numerous other books with Wrox Press. He is a regular contributor to technical magazines and is an active evangelist of the P2P evolution. Sing is a consultant and freelance writer and can be reached at westmakaha@yahoo.com.

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Latest Articles