The <filter-mapping> block
Now we need to tell the servlet container when to apply the filter, which is
done with a <filter-mapping> block. The filter-mapping block includes
the filter-name, which must match the name given in a <filter> block,
and either a URL pattern or a servlet name. Most often you will use a URL
pattern, but you can also specify the name of a servlet as defined in a
<servlet> block.
URL patterns for filter-mappings are the same as for the <servlet-mapping>
block, normally either a subpath such as "/secure/*", or a file ending like
"*.gif". Exact paths may also be used, like "/filtered_page.html".
To have our filter handle all requests, we'll use the following filter-mapping
block:
<filter-mapping>
<filter-name>Timer</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
This filter will run for every single request handled by the servlet engine.
In most cases this would put an unnecessary load on the server, since few
filters really need to execute for every image file as well as flat HTML,
JSP, and servlet calls.
Using the FilterConfig object
When the servlet container starts it initializes each filter by calling the
init() method, passing in a FilterConfig object. This object has two main uses,
obtaining initialization parameters and accessing the container's ServletContext.
Initialization parameters are set in the web.xml file, in the <filter>
block, and are accessed by calling FilterConfig.getInitParameter(). The names
of all parameters can be retrieved with getInitParameterNames().
The ServletContext is useful for accessing data and services relating to the
servlet container and the web application. For instance, it is generally
better to send output to the container's log file rather than printing to
standard output, as the TimerFilter code earlier in this article does.
The original ExampleFilter code from the Tomcat project does this by saving
a reference to FilterConfig in a class variable, and then gets the servlet
context to call its log() method.
Let's look at the original Tomcat ExampleFilter code to see this in action.
Not only does it use ServletContext, it also uses a configuration parameter
to create a request attribute that could be retrieved by a servlet, JSP, or
another filter. Although not obviously useful here, this is a handy technique
for passing data between the components of a web application.
In its init() method the filter saves the FilterConfig object in an object
variable, along with the "attribute" initialization parameter.
private FilterConfig filterConfig = null;
private String attribute = null;
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
this.attribute = filterConfig.getInitParameter("attribute");
}
Then the doFilter() method uses the FilterConfig to grab the ServletContext
and use its log() method to write to the log file. It also sets an attribute
in the request named according to the init-parameter, whose value is the
ExampleFilter object itself. This could be used in a JSP page or servlet,
or a later Filter.
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
// Store ourselves as a request attribute (if requested)
if (attribute != null)
request.setAttribute(attribute, this);
// Time and log the subsequent processing
long startTime = System.currentTimeMillis();
chain.doFilter(request, response);
long stopTime = System.currentTimeMillis();
filterConfig.getServletContext().log
(this.toString() + ": " + (stopTime - startTime) +
" milliseconds");
}
Overriding the request or response
In most cases filters need to affect what happens later in the request process,
even if later components or the resource being accessed don't know anything
about the filter. A powerful way to do this is to substitute your own request
or response object when calling the FilterChain.doFilter() method. The servlet
specification offers special wrapper classes for this purpose, which can be
subclassed to override their default behavior. The replacement class is
instantiated passing in the original request or response object, so methods
which aren't overridden can still be used.
This technique is particularly useful for filters that need to process the raw
output of the request, for example encrypting, converting, or compressing the
response data. Once again the Tomcat folks offer a good example of how to do
this with CompressionFilter. Since most web browsers can automatically handle
compressed pages, CompressionFilter compresses large pages on the fly. This
can be applied not only to static HTML pages, but also HTML (or other format)
output generated by servlets or JSP pages.
If CompressionFilter determines that the browser supports compression, it
wraps the response with a CompressionServletResponseWrapper, which is a
subclass of HttpServletResponseWrapper. Whatever final resource is accessed
by the request will be sent to the browser by calling
ServletResponse.getOutputStream() or ServletResponse.getWriter() to obtain a
writer or stream to feed the output into. So the response wrapper overrides
these methods, replacing the stream with a custom CompressionResponseStream
that uses java.util.zip.GZIPOutputStream to compress whatever is written
to it, or returning a writer which employs that same stream.
Here is the portion of CompressionFilter.doFilter() which actually wraps
the response.
if (response instanceof HttpServletResponse) {
CompressionServletResponseWrapper wrappedResponse =
new CompressionServletResponseWrapper((HttpServletResponse)response);
wrappedResponse.setCompressionThreshold(compressionThreshold);
if (debug > 0) {
System.out.println("doFilter gets called with compression");
}
try {
chain.doFilter(request, wrappedResponse);
} finally {
wrappedResponse.finishResponse();
}
return;
}
The implementation of the custom output stream is actually more sophisticated
than this. It waits to ensure the returned content is large enough to be worth
compressing, and then uses the compression stream or a normal stream accordingly.
New on the Java Boutique:
New Review:
Time Management Made Easy with the Quartz Enterprise Job Scheduler
Why not just use the Java timer API? This open source scheduling
API boasts simplicity, ease-of-integration, a well-rounded feature
set, and it's free!
New Applet:
Reverse Complement
Reverse Complement is a simple applet that converts DNA or RNA
sequences into three useful formats.
Elsewhere on internet.com:
WebDeveloper Java
Lots of Java information on webdeveloper.com
WDVL Java
Thorough Java resource at the Web Developer's Virtual Library.
ScriptSearch Java
Hundreds of free Java code files to download.
jGuru: Your View of the Java Universe
Customizable portal with online training, FAQs, regular news updates, and tutorials.
|