![]() |
May 2001 Issue |
![]() |
Headlines:
Java Technical Insight of the
Month
Filters in the Servlet 2.3 API
Visit The Java News Brief
Archive for past issues of the JNB.
OCI Educational Services
Java Technical Insight of the
Month
Filters in the Servlet
2.3 API
by Dan Troesser, Senior
Software Engineer, Object Computing, Inc. (OCI)
Java Servlets have been around for several years now and have
proven themselves in delivering robust and portable web applications. Since their introduction, the Java web
development front hasn't slowed down. Along came JSPs
introducing an easier way to generate HTML. XML and XSLT
entered the picture making it easier to separate presentation
from data, among many other benefits. The Servlet API hasn't
stood still either. The Servlet 2.3 API, now a proposed final
draft, will be released soon and will bring with it another
powerful feature called filters.
Filters allow declarative pre-processing and post-processing
of requests and responses handled by web resources. Web
resources in this case are considered servlets, JSPs, static
content (HTML files), and even other filters in the web
application. Declarative means the Filter can be applied in the
deployment descriptor of the web application rather than
programmatically in a Servlet or JSP.
Filters are very similar to the concept of Servlet chaining
introduced and implemented by some Servlet containers years
ago. They could also be applied declaratively usually in a web
administration tool. However, declarative Servlet chaining was
not in the specification and thus was not implemented widely or
in any standard way by the containers. In the interest of
portability, their use somewhat disappeared.


import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.xml.transform.*;
import javax.xml.transform.stream.*;
public class ImageRemovalFilter implements Filter {
// Could also use an initialization parameter for this.
private static final String STYLESHEET = "removeImages.xslt";
private FilterConfig filterConfig;
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
boolean doRemove = req.getParameter("removeImages") != null;
HttpServletResponse httpRes;
if (res instanceof HttpServletResponse) {
httpRes = (HttpServletResponse) res;
} else {
throw new ServletException("ImageRemovalFilter only accepts"
+ " HTTP requests");
}
OutputCaptureResponseWrapper resWrapper
= new OutputCaptureResponseWrapper(httpRes);
chain.doFilter(req, resWrapper);
resWrapper.flushBuffer();
String contentType = resWrapper.getContentType();
if (doRemove && contentType != null
&& contentType.indexOf("html") != -1) {
String initialXHTML = resWrapper.getOutputAsString();
String finalXHTML = removeImages(initialXHTML);
res.setContentLength(finalXHTML.length());
res.getWriter().print(finalXHTML);
} else {
// Let all other content types pass through.
res.getOutputStream().write(resWrapper.getOutputAsByteArray());
}
}
public void init(FilterConfig filterConfig) {
this.filterConfig = filterConfig;
}
// Required by Filter interface. Just providing empty implementation.
public void destroy() { }
/**
* Performs an XSLT transformation using JAXP 1.1 to replace img elements
* in the input XHTML document with links (<a href="...).
*/
private String removeImages(String initialXHTML)
throws ServletException {
StringWriter finalXHTMLWriter = new StringWriter();
TransformerFactory factory = TransformerFactory.newInstance();
ServletContext context = filterConfig.getServletContext();
try {
InputStream xsltStream = context.getResourceAsStream(STYLESHEET);
StreamSource xsltSource = new StreamSource(xsltStream);
StringReader xmlSourceReader
= new StringReader(initialXHTML);
StreamSource xmlSource = new StreamSource(xmlSourceReader);
StreamResult xmlResult = new StreamResult(finalXHTMLWriter);
Transformer transformer = factory.newTransformer(xsltSource);
transformer.transform(xmlSource, xmlResult);
} catch (Exception ex) {
throw new ServletException("Error performing transformation to remove"
+ " images", ex);
}
return finalXHTMLWriter.toString();
}
}
When the Filter is loaded, the init() method is called. We use
the opportunity to save a reference to the FilterConfig object.
We will need this later to retrieve an instance of the
ServletContext object so we can gain access to the XSLT
stylesheet, which will perform the content transformation.
Click here to view or download
the stylesheet. The stylesheet filename is defined as a static
final String. It could also have been specified as a Filter
initialization parameter in the web application's deployment
descriptor (web.xml). If that were the case, the init() method
would have been a good place to retrieve the value using
getInitParameter() on the FilterConfig object. Once loaded and
the init() method is called, the Filter can handle requests.
Each request that should be filtered calls the doFilter()
method.
<?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/j2ee/dtds/web-app_2_3.dtd">
<web-app>
<filter>
<filter-name>Image Removal Filter</filter-name>
<filter-class>ImageRemovalFilter</filter-class>
</filter>
<!-- Will cause the Filter to be applied to Viewer Servlet because it
has a matching url-pattern. One could have instead specified the
Servlet directly using a servlet-name element.-->
<filter-mapping>
<filter-name>Image Removal Filter</filter-name>
<url-pattern>/view/*</url-pattern>
</filter-mapping>
<!-- Will cause the Filter to be applied to all resources in the web
application, including static content and JSPs. This makes the
previous filter-mapping a bit redundant.-->
<filter-mapping>
<filter-name>Image Removal Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>Viewer Servlet</servlet-name>
<servlet-class>ViewerServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Viewer Servlet</servlet-name>
<url-pattern>/view/*</url-pattern>
</servlet-mapping>
</web-app>
The <filter> element declares a Filter in the web
application. It expects <filter-name> and
<filter-class> child elements that define the logical
name and class of the Filter, respectively, similar to the
definitions for servlets shown farther down in the file.
Initialization parameters can be defined in the same way for
filters as far servlets using <init-params> elements. We
also have <filter-mapping> elements that look similar to
<servlet-mapping> elements. They can contain
<url-pattern> elements that define when a Filter should
be called. Filters have critical differences, however, when it
comes to mapping. For one, the <filter-mapping> element
can take a <servlet-name> element instead of a
<url-pattern> element. This allows the Filter to be
mapped to an individual Servlet using its logical name defined
elsewhere in the deployment descriptor. Another critical
difference in mapping is realized when filters are chained
together. When chaining multiple filters, the order the
mappings appear in the deployment descriptor is significant.
First, the filters will be called in the order the
<filter-mapping> elements appear for <url-pattern>
elements that match. Then filters will be called in the order
the <filter-mapping> elements appear for matching
<servlet-name> elements.