epubjs
Version:
Render ePub documents in the browser, across many devices
415 lines (387 loc) • 51.4 kB
HTML
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Servlet Filters</title><link rel="stylesheet" href="core.css" type="text/css"/><meta name="generator" content="DocBook XSL Stylesheets V1.74.0"/></head><body><div class="sect1" title="Servlet Filters"><div class="titlepage"><div><div><h1 class="title"><a id="learnjava3-CHP-15-SECT-4"/>Servlet Filters</h1></div></div></div><p><a id="idx10892" class="indexterm"/>The servlet Filter API generalizes the Java Servlet API to
allow modular component “filters” to operate on the servlet request and
responses in a sort of pipeline. Filters are <span class="emphasis"><em>chained</em></span>,
meaning that when more than one filter is applied, the servlet request is
passed through each filter in succession, with each having an opportunity
to act upon or modify the request before passing it to the next filter.
Similarly, upon completion, the servlet result is effectively passed back
through the chain on its return trip to the browser. Servlet filters may
operate on any requests to a web application, not just those handled by
the servlets; they may filter static content, as well. You can also
control whether filters are applied to error and welcome pages as well as
pages forwarded or included using the request dispatcher (from servlet to
servlet).</p><p>Filters can be declared and mapped to servlets in the
<span class="emphasis"><em>web.xml</em></span> file or using annotations. There are two ways
to map a filter: using a URL pattern like those used for servlets or by
specifying a servlet by its servlet name as defined in its servlet config.
Filters obey the same basic rules as servlets when it comes to URL
matching, but when multiple filters match a path, they are each
invoked.</p><p>When using <span class="emphasis"><em>web.xml</em></span>, the order of the chain is
determined by the order in which matching filter mappings appear in the
<span class="emphasis"><em>web.xml</em></span> file, with <a id="I_indexterm15_id782226" class="indexterm"/><code class="literal"><url-pattern></code>
matches taking precedence over <a id="I_indexterm15_id782240" class="indexterm"/><code class="literal"><servlet-name></code>
matches. This is contrary to the way in which servlet URL matching is
done, with specific matches taking the highest priority. Filter chains are
constructed as follows. First, each filter with a matching URL pattern is
called in the order in which it appears in the
<span class="emphasis"><em>web.xml</em></span> file; next, each filter with a matching
servlet name is called, also in order of appearance. URL patterns take a
higher priority than filters specifically associated with a servlet, so in
this case, patterns such as <code class="literal">/*</code> have
first crack at an incoming request.</p><p>Servlet filters may be declared and mapped using the <a id="I_indexterm15_id782270" class="indexterm"/><code class="literal">WebFilter</code> annotation.
There is no corresponding way to control filter ordering using
annotations; however, as always you can mix annotations and
<span class="emphasis"><em>web.xml</em></span> to minimize the XML configuration by only
declaring the filter mappings in the XML. (We’ll discuss configuration
more later in this chapter.)</p><p>The Filter API is very simple and mimics the Servlet API. A servlet
filter implements the <a id="I_indexterm15_id782293" class="indexterm"/><code class="literal">javax.servlet.Filter</code>
interface and implements three methods: <code class="literal">init()</code>, <code class="literal">doFilter()</code>, and <code class="literal">destroy()</code>. The <code class="literal">doFilter()</code> method is where the work is
performed. For each incoming request, the <code class="literal">ServletRequest</code> and <code class="literal">ServletResponse</code> objects are passed to <code class="literal">doFilter()</code>. Here, we have a chance to examine
and modify these objects—or even substitute our own objects for
them—before passing them to the next filter and, ultimately, the servlet
(or user) on the other side. Our link to the rest of the filter chain is
another parameter of <code class="literal">doFilter()</code>, the
<code class="literal">FilterChain</code> object. With <code class="literal">FilterChain</code>, we can invoke the next element in
the pipeline. The following section presents an example.</p><div class="sect2" title="A Simple Filter"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-15-SECT-4.1"/>A Simple Filter</h2></div></div></div><p><a id="idx10865" class="indexterm"/>For our first filter, we’ll do something easy but
practical: create a filter that limits the number of concurrent
connections to its URLs. We’ll simply have our filter keep a counter of
the active connections passing through it and turn away new requests
when they exceed a specified limit:</p><a id="I_15_tt953"/><pre class="programlisting"><code class="kn">import</code> <code class="nn">java.io.*</code><code class="o">;</code>
<code class="kn">import</code> <code class="nn">javax.servlet.*</code><code class="o">;</code>
<code class="kn">import</code> <code class="nn">javax.servlet.annotation.*</code><code class="o">;</code>
<code class="kn">import</code> <code class="nn">javax.servlet.http.*</code><code class="o">;</code>
<code class="kd">public</code> <code class="kd">class</code> <code class="nc">ConLimitFilter</code> <code class="kd">implements</code> <code class="n">Filter</code>
<code class="o">{</code>
<code class="kt">int</code> <code class="n">limit</code><code class="o">;</code>
<code class="kd">volatile</code> <code class="kt">int</code> <code class="n">count</code><code class="o">;</code>
<code class="kd">public</code> <code class="kt">void</code> <code class="nf">init</code><code class="o">(</code> <code class="n">FilterConfig</code> <code class="n">filterConfig</code> <code class="o">)</code>
<code class="kd">throws</code> <code class="n">ServletException</code>
<code class="o">{</code>
<code class="n">String</code> <code class="n">s</code> <code class="o">=</code> <code class="n">filterConfig</code><code class="o">.</code><code class="na">getInitParameter</code><code class="o">(</code><code class="s">"limit"</code><code class="o">);</code>
<code class="k">if</code> <code class="o">(</code> <code class="n">s</code> <code class="o">==</code> <code class="kc">null</code> <code class="o">)</code>
<code class="k">throw</code> <code class="k">new</code> <code class="nf">ServletException</code><code class="o">(</code><code class="s">"Missing init parameter: "</code><code class="o">+</code><code class="n">limit</code><code class="o">);</code>
<code class="n">limit</code> <code class="o">=</code> <code class="n">Integer</code><code class="o">.</code><code class="na">parseInt</code><code class="o">(</code> <code class="n">s</code> <code class="o">);</code>
<code class="o">}</code>
<code class="kd">public</code> <code class="kt">void</code> <code class="nf">doFilter</code> <code class="o">(</code>
<code class="n">ServletRequest</code> <code class="n">req</code><code class="o">,</code> <code class="n">ServletResponse</code> <code class="n">res</code><code class="o">,</code> <code class="n">FilterChain</code> <code class="n">chain</code> <code class="o">)</code>
<code class="kd">throws</code> <code class="n">IOException</code><code class="o">,</code> <code class="n">ServletException</code>
<code class="o">{</code>
<code class="k">if</code> <code class="o">(</code> <code class="n">count</code> <code class="o">></code> <code class="n">limit</code> <code class="o">)</code> <code class="o">{</code>
<code class="n">HttpServletResponse</code> <code class="n">httpRes</code> <code class="o">=</code> <code class="o">(</code><code class="n">HttpServletResponse</code><code class="o">)</code><code class="n">res</code><code class="o">;</code>
<code class="n">httpRes</code><code class="o">.</code><code class="na">sendError</code><code class="o">(</code> <code class="n">httpRes</code><code class="o">.</code><code class="na">SC_SERVICE_UNAVAILABLE</code><code class="o">,</code> <code class="s">"Too Busy."</code><code class="o">);</code>
<code class="o">}</code> <code class="k">else</code> <code class="o">{</code>
<code class="o">++</code><code class="n">count</code><code class="o">;</code>
<code class="n">chain</code><code class="o">.</code><code class="na">doFilter</code><code class="o">(</code> <code class="n">req</code><code class="o">,</code> <code class="n">res</code> <code class="o">);</code>
<code class="o">--</code><code class="n">count</code><code class="o">;</code>
<code class="o">}</code>
<code class="o">}</code>
<code class="kd">public</code> <code class="kt">void</code> <code class="nf">destroy</code><code class="o">()</code> <code class="o">{</code> <code class="o">}</code>
<code class="o">}</code></pre><p><code class="literal">ConLimitFilter</code> implements the
three lifecycle methods of the <code class="literal">Filter</code>
interface: <code class="literal">init()</code>, <code class="literal">doFilter()</code>, and <code class="literal">destroy()</code>. In our <code class="literal">init()</code> method, we use the <code class="literal">FilterConfig</code> object to look for an
initialization parameter named “limit” and turn it into an integer.
Users can set this value in the section of the
<span class="emphasis"><em>web.xml</em></span> file where the instance of our filter is
declared or in the annotation as shown. The <code class="literal">doFilter()</code> method implements all our logic.
First, it receives <code class="literal">ServletRequest</code> and
<code class="literal">ServletResponse</code> object pairs for
incoming requests. Depending on the counter, it then either passes them
down the chain by invoking the next <code class="literal">doFilter()</code> method on the <code class="literal">FilterChain</code> object, or rejects them by
generating its own response. We use the standard HTTP message “504
Service Unavailable” when we deny new connections.</p><p>Calling <code class="literal">doFilter()</code> on the
<code class="literal">FilterChain</code> object continues
processing by invoking the next filter in the chain or by invoking the
servlet if ours is the last filter. Alternatively, when we choose to
reject the call, we use the <code class="literal">ServletResponse</code> to generate our own response
and then simply allow <code class="literal">doFilter()</code> to
exit. This stops the processing chain at our filter, although any
filters called before us still have an opportunity to intervene as the
request effectively traverses back to the client.</p><p>Notice that <code class="literal">ConLimitFilter</code>
increments the count before calling <code class="literal">doFilter()</code> and decrements it after. Prior to
calling <code class="literal">doFilter()</code>, we can work on
the request before it reaches the rest of the chain and the servlet.
After the call to <code class="literal">doFilter()</code>, the
chain to the servlet has completed, and the request is sent back to the
client. This is our opportunity to do any post-processing of the
response.</p><p>Finally, we should mention that although we’ve been talking about
the servlet request and response as if they were <code class="literal">HttpServletRequest</code> and <code class="literal">HttpServletResponse</code>, the <code class="literal">doFilter()</code> method actually takes the more
generic <a id="I_indexterm15_id782561" class="indexterm"/><code class="literal">ServletRequest</code> and
<a id="I_indexterm15_id782571" class="indexterm"/><code class="literal">ServletResponse</code> objects
as parameters. As filter implementers, we are expected to determine when
it is safe to treat them as HTTP traffic and perform the cast as
necessary (which we do here in order to use the <code class="literal">sendError()</code> HTTP response method).<a id="I_indexterm15_id782589" class="indexterm"/></p></div><div class="sect2" title="A Test Servlet"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-15-SECT-4.2"/>A Test Servlet</h2></div></div></div><p><a id="idx10866" class="indexterm"/>Before we go on, here is a simple test servlet you can use
to try out this filter and the other filters we’ll develop in this
section. It’s called <code class="literal">WaitServlet</code> and,
as its name implies, it simply waits. You can specify how long it waits
as a number of seconds with the servlet parameter <code class="literal">time</code>. (This is the “dumb” version of the
<code class="literal">BackgroundWaitServlet</code> that we created
earlier in this chapter when discussing asynchronous servlets.)</p><a id="I_15_tt954"/><pre class="programlisting"> <code class="kn">import</code> <code class="nn">java.io.*</code><code class="o">;</code>
<code class="kn">import</code> <code class="nn">javax.servlet.*</code><code class="o">;</code>
<code class="kn">import</code> <code class="nn">javax.servlet.http.*</code><code class="o">;</code>
<code class="kd">public</code> <code class="kd">class</code> <code class="nc">WaitServlet</code> <code class="kd">extends</code> <code class="n">HttpServlet</code>
<code class="o">{</code>
<code class="kd">public</code> <code class="kt">void</code> <code class="nf">doGet</code><code class="o">(</code> <code class="n">HttpServletRequest</code> <code class="n">request</code><code class="o">,</code>
<code class="n">HttpServletResponse</code> <code class="n">response</code> <code class="o">)</code>
<code class="kd">throws</code> <code class="n">ServletException</code><code class="o">,</code> <code class="n">IOException</code>
<code class="o">{</code>
<code class="n">String</code> <code class="n">waitStr</code> <code class="o">=</code> <code class="n">request</code><code class="o">.</code><code class="na">getParameter</code><code class="o">(</code><code class="s">"time"</code><code class="o">);</code>
<code class="k">if</code> <code class="o">(</code> <code class="n">waitStr</code> <code class="o">==</code> <code class="kc">null</code> <code class="o">)</code>
<code class="k">throw</code> <code class="k">new</code> <code class="nf">ServletException</code><code class="o">(</code><code class="s">"Missing parameter: time"</code><code class="o">);</code>
<code class="kt">int</code> <code class="n">wait</code> <code class="o">=</code> <code class="n">Integer</code><code class="o">.</code><code class="na">parseInt</code><code class="o">(</code><code class="n">waitStr</code><code class="o">);</code>
<code class="k">try</code> <code class="o">{</code>
<code class="n">Thread</code><code class="o">.</code><code class="na">sleep</code><code class="o">(</code> <code class="n">wait</code> <code class="o">*</code> <code class="mi">1000</code> <code class="o">);</code>
<code class="o">}</code> <code class="k">catch</code><code class="o">(</code> <code class="n">InterruptedException</code> <code class="n">e</code> <code class="o">)</code> <code class="o">{</code>
<code class="k">throw</code> <code class="k">new</code> <code class="nf">ServletException</code><code class="o">(</code><code class="n">e</code><code class="o">);</code>
<code class="o">}</code>
<code class="n">response</code><code class="o">.</code><code class="na">setContentType</code><code class="o">(</code><code class="s">"text/html"</code><code class="o">);</code>
<code class="n">PrintWriter</code> <code class="n">out</code> <code class="o">=</code> <code class="n">response</code><code class="o">.</code><code class="na">getWriter</code><code class="o">();</code>
<code class="n">out</code><code class="o">.</code><code class="na">println</code><code class="o">(</code>
<code class="s">"<html><body><h1>WaitServlet Response</h1></body></html>"</code><code class="o">);</code>
<code class="n">out</code><code class="o">.</code><code class="na">close</code><code class="o">();</code>
<code class="o">}</code>
<code class="o">}</code></pre><p>By making multiple simultaneous requests to the <code class="literal">WaitServlet</code>, you can try out the <code class="literal">ConLimitFilter</code>. Note that some web browsers
won’t open multiple requests to the same URL or may delay opening
multiple tabs. You may have to add extraneous parameters to trick the
web browser. Alternately, you may wish to use the <a id="I_indexterm15_id782679" class="indexterm"/><span class="emphasis"><em>curl</em></span> command-line utility to make the
requests if you have it.<a id="I_indexterm15_id782688" class="indexterm"/></p></div><div class="sect2" title="Declaring and Mapping Filters"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-15-SECT-4.3"/>Declaring and Mapping Filters</h2></div></div></div><p><a id="idx10853" class="indexterm"/> <a id="idx10862" class="indexterm"/>In the <span class="emphasis"><em>web.xml</em></span> file filters are
declared and mapped much as servlets are. Like servlets, one instance of
a filter class is created for each filter declaration in the
<span class="emphasis"><em>web.xml</em></span> file. A filter declaration looks like
this:</p><a id="I_15_tt955"/><pre class="programlisting"> <code class="o"><</code><code class="n">filter</code><code class="o">></code>
<code class="o"><</code><code class="n">filter</code><code class="o">-</code><code class="n">name</code><code class="o">></code><code class="n">defaultsfilter1</code><code class="o"></</code><code class="n">filter</code><code class="o">-</code><code class="n">name</code><code class="o">></code>
<code class="o"><</code><code class="n">filter</code><code class="o">-</code><code class="n">class</code><code class="o">></code><code class="n">RequestDefaultsFilter</code><code class="o"></</code><code class="n">filter</code><code class="o">-</code><code class="n">class</code><code class="o">></code>
<code class="o"></</code><code class="n">filter</code><code class="o">></code></pre><p>It specifies a filter handle name to be used for reference within
the <span class="emphasis"><em>web.xml</em></span> file and the filter’s Java class name.
Filter declarations may also contain <a id="I_indexterm15_id782754" class="indexterm"/><code class="literal"><init-param></code>
parameter sections, just like servlet declarations.</p><p>Filters are mapped to resources with <a id="I_indexterm15_id782771" class="indexterm"/><code class="literal"><filter-mapping></code>
declarations that specify the filter handle name and either the specific
servlet handle name or a URL pattern, as we discussed earlier:</p><a id="I_15_tt956"/><pre class="programlisting"> <code class="o"><</code><code class="n">filter</code><code class="o">-</code><code class="n">mapping</code><code class="o">></code>
<code class="o"><</code><code class="n">filter</code><code class="o">-</code><code class="n">name</code><code class="o">></code><code class="n">conlimitfilter1</code><code class="o"></</code><code class="n">filter</code><code class="o">-</code><code class="n">name</code><code class="o">></code>
<code class="o"><</code><code class="n">servlet</code><code class="o">-</code><code class="n">name</code><code class="o">></code><code class="n">waitservlet1</code><code class="o"></</code><code class="n">servlet</code><code class="o">-</code><code class="n">name</code><code class="o">></code>
<code class="o"></</code><code class="n">filter</code><code class="o">-</code><code class="n">mapping</code><code class="o">></code>
<code class="o"><</code><code class="n">filter</code><code class="o">-</code><code class="n">mapping</code><code class="o">></code>
<code class="o"><</code><code class="n">filter</code><code class="o">-</code><code class="n">name</code><code class="o">></code><code class="n">conlimitfilter1</code><code class="o"></</code><code class="n">filter</code><code class="o">-</code><code class="n">name</code><code class="o">></code>
<code class="o"><</code><code class="n">url</code><code class="o">-</code><code class="n">pattern</code><code class="o">>/*</</code><code class="n">url</code><code class="o">-</code><code class="n">pattern</code><code class="o">></code>
<code class="o"></</code><code class="n">filter</code><code class="o">-</code><code class="n">mapping</code><code class="o">></code></pre><p>The corresponding <code class="literal">WebFilter</code>
annotation can declare and map filters as well as supply filter
parameters. The annotation will accept either a <a id="I_indexterm15_id782806" class="indexterm"/><code class="literal">urlPatterns</code> or a
<a id="I_indexterm15_id782816" class="indexterm"/><code class="literal">servletNames</code> attribute
for the mapping.<a id="I_indexterm15_id782828" class="indexterm"/><a id="I_indexterm15_id782835" class="indexterm"/></p><a id="I_programlisting15_id782842"/><pre class="programlisting"><code class="nd">@WebFilter</code><code class="o">(</code>
<code class="n">urlPatterns</code> <code class="o">=</code> <code class="s">"/*"</code><code class="o">,</code>
<code class="n">initParams</code> <code class="o">=</code> <code class="o">{</code>
<code class="nd">@WebInitParam</code><code class="o">(</code><code class="n">name</code><code class="o">=</code><code class="s">"limit"</code><code class="o">,</code> <code class="n">value</code><code class="o">=</code><code class="s">"3"</code><code class="o">)</code>
<code class="o">}</code>
<code class="o">)</code></pre></div><div class="sect2" title="Filtering the Servlet Request"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-15-SECT-4.4"/>Filtering the Servlet Request</h2></div></div></div><p><a id="idx10863" class="indexterm"/>Our first filter example was not very exciting because it
did not actually modify any information going to or coming from the
servlet. Next, let’s do some actual “filtering” by modifying the
incoming request before it reaches a servlet. In this example, we’ll
create a request “defaulting” filter that automatically supplies default
values for specified servlet parameters when they are not provided in
the incoming request. Here is the <code class="literal">RequestDefaultsFilter</code>:</p><a id="I_15_tt957"/><pre class="programlisting"> <code class="kn">import</code> <code class="nn">java.io.*</code><code class="o">;</code>
<code class="kn">import</code> <code class="nn">javax.servlet.*</code><code class="o">;</code>
<code class="kn">import</code> <code class="nn">javax.servlet.http.*</code><code class="o">;</code>
<code class="kd">public</code> <code class="kd">class</code> <code class="nc">RequestDefaultsFilter</code> <code class="kd">implements</code> <code class="n">Filter</code>
<code class="o">{</code>
<code class="n">FilterConfig</code> <code class="n">filterConfig</code><code class="o">;</code>
<code class="kd">public</code> <code class="kt">void</code> <code class="nf">init</code><code class="o">(</code> <code class="n">FilterConfig</code> <code class="n">filterConfig</code> <code class="o">)</code> <code class="kd">throws</code> <code class="n">ServletException</code>
<code class="o">{</code>
<code class="k">this</code><code class="o">.</code><code class="na">filterConfig</code> <code class="o">=</code> <code class="n">filterConfig</code><code class="o">;</code>
<code class="o">}</code>
<code class="kd">public</code> <code class="kt">void</code> <code class="nf">doFilter</code> <code class="o">(</code>
<code class="n">ServletRequest</code> <code class="n">req</code><code class="o">,</code> <code class="n">ServletResponse</code> <code class="n">res</code><code class="o">,</code> <code class="n">FilterChain</code> <code class="n">chain</code> <code class="o">)</code>
<code class="kd">throws</code> <code class="n">IOException</code><code class="o">,</code> <code class="n">ServletException</code>
<code class="o">{</code>
<code class="n">WrappedRequest</code> <code class="n">wrappedRequest</code> <code class="o">=</code>
<code class="k">new</code> <code class="nf">WrappedRequest</code><code class="o">(</code> <code class="o">(</code><code class="n">HttpServletRequest</code><code class="o">)</code><code class="n">req</code> <code class="o">);</code>
<code class="n">chain</code><code class="o">.</code><code class="na">doFilter</code><code class="o">(</code> <code class="n">wrappedRequest</code><code class="o">,</code> <code class="n">res</code> <code class="o">);</code>
<code class="o">}</code>
<code class="kd">public</code> <code class="kt">void</code> <code class="nf">destroy</code><code class="o">()</code> <code class="o">{</code> <code class="o">}</code>
<code class="kd">class</code> <code class="nc">WrappedRequest</code> <code class="kd">extends</code> <code class="n">HttpServletRequestWrapper</code>
<code class="o">{</code>
<code class="n">WrappedRequest</code><code class="o">(</code> <code class="n">HttpServletRequest</code> <code class="n">req</code> <code class="o">)</code> <code class="o">{</code>
<code class="kd">super</code><code class="o">(</code> <code class="n">req</code> <code class="o">);</code>
<code class="o">}</code>
<code class="kd">public</code> <code class="n">String</code> <code class="nf">getParameter</code><code class="o">(</code> <code class="n">String</code> <code class="n">name</code> <code class="o">)</code> <code class="o">{</code>
<code class="n">String</code> <code class="n">value</code> <code class="o">=</code> <code class="kd">super</code><code class="o">.</code><code class="na">getParameter</code><code class="o">(</code> <code class="n">name</code> <code class="o">);</code>
<code class="k">if</code> <code class="o">(</code> <code class="n">value</code> <code class="o">==</code> <code class="kc">null</code> <code class="o">)</code>
<code class="n">value</code> <code class="o">=</code> <code class="n">filterConfig</code><code class="o">.</code><code class="na">getInitParameter</code><code class="o">(</code> <code class="n">name</code> <code class="o">);</code>
<code class="k">return</code> <code class="n">value</code><code class="o">;</code>
<code class="o">}</code>
<code class="o">}</code>
<code class="o">}</code></pre><p>To interpose ourselves in the data flow, we must do something
drastic. We kidnap the incoming <code class="literal">HttpServletRequest</code> object and replace it with
an imposter that does our bidding. The technique, which we’ll use here
for modifying the request object and later for modifying the response,
is to wrap the real request with an adapter, allowing us to override
some of its methods. Here, we will take control of the <code class="literal">HttpServletRequest</code>’s <code class="literal">getParameter()</code> method, modifying it to look
for default values where it would otherwise return <code class="literal">null</code>.</p><p>Again, we implement the three lifecycle methods of <code class="literal">Filter</code>, but this time, before invoking
<code class="literal">doFilter()</code> on the filter chain to
continue processing, we wrap the incoming <a id="I_indexterm15_id782947" class="indexterm"/><code class="literal">HttpServletRequest</code> in
our own class, <code class="literal">WrappedRequest</code>.
<code class="literal">WrappedRequest</code> extends a special
adapter called <code class="literal">HttpServletRequestWrapper</code>. This wrapper class
is a convenience utility that extends <code class="literal">HttpServletRequest</code>. It accepts a reference to
a target <code class="literal">HttpServletRequest</code> object
and, by default, delegates all of its methods to that target. This makes
it very convenient for us to simply override one or more methods of
interest to us. All we have to do is override <code class="literal">getParameter()</code> in our <code class="literal">WrappedRequest</code> class and add our
functionality. Here, we simply call our parent’s <code class="literal">getParameter()</code>, and in the case where the
value is <code class="literal">null</code>, we try to substitute a
filter initialization parameter of the same name.</p><p>Try this example using the <code class="literal">WaitServlet</code> with a filter declaration and
mapping or annotation as follows:</p><a id="I_15_tt958"/><pre class="programlisting"><code class="o"><</code><code class="n">filter</code><code class="o">></code>
<code class="o"><</code><code class="n">filter</code><code class="o">-</code><code class="n">name</code><code class="o">></code><code class="n">defaultsfilter1</code><code class="o"></</code><code class="n">filter</code><code class="o">-</code><code class="n">name</code><code class="o">></code>
<code class="o"><</code><code class="n">filter</code><code class="o">-</code><code class="n">class</code><code class="o">></code><code class="n">RequestDefaultsFilter</code><code class="o"></</code><code class="n">filter</code><code class="o">-</code><code class="n">class</code><code class="o">></code>
<code class="o"><</code><code class="n">init</code><code class="o">-</code><code class="n">param</code><code class="o">></code>
<code class="o"><</code><code class="n">param</code><code class="o">-</code><code class="n">name</code><code class="o">></code><code class="n">time</code><code class="o"></</code><code class="n">param</code><code class="o">-</code><code class="n">name</code><code class="o">></code>
<code class="o"><</code><code class="n">param</code><code class="o">-</code><code class="n">value</code><code class="o">></code><code class="mi">3</code><code class="o"></</code><code class="n">param</code><code class="o">-</code><code class="n">value</code><code class="o">></code>
<code class="o"></</code><code class="n">init</code><code class="o">-</code><code class="n">param</code><code class="o">></code>
<code class="o"></</code><code class="n">filter</code><code class="o">></code>
<code class="o"><</code><code class="n">filter</code><code class="o">-</code><code class="n">mapping</code><code class="o">></code>
<code class="o"><</code><code class="n">filter</code><code class="o">-</code><code class="n">name</code><code class="o">></code><code class="n">defaultsfilter1</code><code class="o"></</code><code class="n">filter</code><code class="o">-</code><code class="n">name</code><code class="o">></code>
<code class="o"><</code><code class="n">servlet</code><code class="o">-</code><code class="n">name</code><code class="o">></code><code class="n">waitservlet1</code><code class="o"></</code><code class="n">servlet</code><code class="o">-</code><code class="n">name</code><code class="o">></code>
<code class="o"></</code><code class="n">filter</code><code class="o">-</code><code class="n">mapping</code><code class="o">></code>
<code class="nd">@WebFilter</code><code class="o">(</code>
<code class="n">servletNames</code> <code class="o">=</code> <code class="s">"waitservlet1"</code><code class="o">,</code>
<code class="n">initParams</code> <code class="o">=</code> <code class="o">{</code>
<code class="nd">@WebInitParam</code><code class="o">(</code><code class="n">name</code><code class="o">=</code><code class="s">"time"</code><code class="o">,</code> <code class="n">value</code><code class="o">=</code><code class="s">"3"</code><code class="o">)</code>
<code class="o">}</code>
<code class="o">)</code></pre><p>Now the <code class="literal">WaitServlet</code> receives a
default time value of three seconds even when you don’t specify
one.<a id="I_indexterm15_id783043" class="indexterm"/></p></div><div class="sect2" title="Filtering the Servlet Response"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-15-SECT-4.5"/>Filtering the Servlet Response</h2></div></div></div><p><a id="idx10864" class="indexterm"/>Filtering the request was fairly easy, and we can do
something similar with the response object using exactly the same
technique. There is a corresponding <a id="I_indexterm15_id783073" class="indexterm"/><code class="literal">HttpServletResponseWrapper</code> that we can use to
wrap the response before the servlet uses it to communicate back to the
client. By wrapping the response, we can intercept methods that the
servlet uses to write the response, just as we intercepted the <code class="literal">getParameter()</code> method that the servlet used in
reading the incoming data. For example, we could override the <code class="literal">sendError()</code> method of the <code class="literal">HttpServletResponse</code> object and modify it to
redirect to a specified page. In this way, we could create a servlet
filter that emulates the programmable error page control offered in the
<span class="emphasis"><em>web.xml</em></span> file. But the most interesting technique
available to us, and the one we’ll show here, involves actually
modifying the data written by the servlet before it reaches the client.
In order to do this, we have to pull a double “switcheroo.” We wrap the
servlet response to override the <code class="literal">getWriter()</code> method and then create our own
wrapper for the client’s <code class="literal">PrintWriter</code>
object supplied by this method, one that buffers the data written and
allows us to modify it. This is a useful and powerful technique, but it
can be tricky.</p><p>Our example, <code class="literal">LinkResponseFilter</code>, is an automatic
hyperlink-generating filter that reads HTML responses and searches them
for patterns supplied as regular expressions. When it matches a pattern,
it turns it into an HTML link. The pattern and links are specified in
the filter initialization parameters. You could extend this example with
access to a database or XML file and add more rules to make it into a
useful site-management helper. Here it is:</p><a id="I_15_tt959"/><pre class="programlisting"> <code class="kn">import</code> <code class="nn">java.io.*</code><code class="o">;</code>
<code class="kn">import</code> <code class="nn">java.util.*</code><code class="o">;</code>
<code class="kn">import</code> <code class="nn">javax.servlet.*</code><code class="o">;</code>
<code class="kn">import</code> <code class="nn">javax.servlet.http.*</code><code class="o">;</code>
<code class="kd">public</code> <code class="kd">class</code> <code class="nc">LinkResponseFilter</code> <code class="kd">implements</code> <code class="n">Filter</code>
<code class="o">{</code>
<code class="n">FilterConfig</code> <code class="n">filterConfig</code><code class="o">;</code>
<code class="kd">public</code> <code class="kt">void</code> <code class="nf">init</code><code class="o">(</code> <code class="n">FilterConfig</code> <code class="n">filterConfig</code> <code class="o">)</code>
<code class="kd">throws</code> <code class="n">ServletException</code>
<code class="o">{</code>
<code class="k">this</code><code class="o">.</code><code class="na">filterConfig</code> <code class="o">=</code> <code class="n">filterConfig</code><code class="o">;</code>
<code class="o">}</code>
<code class="kd">public</code> <code class="kt">void</code> <code class="nf">doFilter</code> <code class="o">(</code>
<code class="n">ServletRequest</code> <code class="n">req</code><code class="o">,</code> <code class="n">ServletResponse</code> <code class="n">res</code><code class="o">,</code> <code class="n">FilterChain</code> <code class="n">chain</code> <code class="o">)</code>
<code class="kd">throws</code> <code class="n">IOException</code><code class="o">,</code> <code class="n">ServletException</code>
<code class="o">{</code>
<code class="n">WrappedResponse</code> <code class="n">wrappedResponse</code> <code class="o">=</code>
<code class="k">new</code> <code class="nf">WrappedResponse</code><code class="o">(</code> <code class="o">(</code><code class="n">HttpServletResponse</code><code class="o">)</code><code class="n">res</code> <code class="o">);</code>
<code class="n">chain</code><code class="o">.</code><code class="na">doFilter</code><code class="o">(</code> <code class="n">req</code><code class="o">,</code> <code class="n">wrappedResponse</code> <code class="o">);</code>
<code class="n">wrappedResponse</code><code class="o">.</code><code class="na">close</code><code class="o">();</code>
<code class="o">}</code>
<code class="kd">public</code> <code class="kt">void</code> <code class="nf">destroy</code><code class="o">()</code> <code class="o">{</code> <code class="o">}</code>
<code class="kd">class</code> <code class="nc">WrappedResponse</code> <code class="kd">extends</code> <code class="n">HttpServletResponseWrapper</code>
<code class="o">{</code>
<code class="kt">boolean</code> <code class="n">linkText</code><code class="o">;</code>
<code class="n">PrintWriter</code> <code class="n">client</code><code class="o">;</code>
<code class="n">WrappedResponse</code><code class="o">(</code> <code class="n">HttpServletResponse</code> <code class="n">res</code> <code class="o">)</code> <code class="o">{</code>
<code class="kd">super</code><code class="o">(</code> <code class="n">res</code> <code class="o">);</code>
<code class="o">}</code>
<code class="kd">public</code> <code class="kt">void</code> <code class="nf">setContentType</code><code class="o">(</code> <code class="n">String</code> <code class="n">mime</code> <code class="o">)</code> <code class="o">{</code>
<code class="kd">super</code><code class="o">.</code><code class="na">setContentType</code><code class="o">(</code> <code class="n">mime</code> <code class="o">);</code>
<code class="k">if</code> <code class="o">(</code> <code class="n">mime</code><code class="o">.</code><code class="na">startsWith</code><code class="o">(</code><code class="s">"text/html"</code><code class="o">)</code> <code class="o">)</code>
<code class="n">linkText</code> <code class="o">=</code> <code class="kc">true</code><code class="o">;</code>
<code class="o">}</code>
<code class="kd">public</code> <code class="n">PrintWriter</code> <code class="nf">getWriter</code><code class="o">()</code> <code class="kd">throws</code>
<code class="n">IOException</code> <code class="o">{</code>
<code class="k">if</code> <code class="o">(</code> <code class="n">client</code> <code class="o">==</code> <code class="kc">null</code> <code class="o">)</code>
<code class="k">if</code> <code class="o">(</code> <code class="n">linkText</code> <code class="o">)</code>
<code class="n">client</code> <code class="o">=</code> <code class="k">new</code> <code class="n">LinkWriter</code><code class="o">(</code>
<code class="kd">super</code><code class="o">.</code><code class="na">getWriter</code><code class="o">(),</code> <code class="k">new</code> <code class="n">ByteArrayOutputStream</code><code class="o">()</code> <code class="o">);</code>
<code class="k">else</code>
<code class="n">client</code> <code class="o">=</code> <code class="kd">super</code><code class="o">.</code><code class="na">getWriter</code><code class="o">();</code>
<code class="k">return</code> <code class="n">client</code><code class="o">;</code>
<code class="o">}</code>
<code class="kt">void</code> <code class="nf">close</code><code class="o">()</code> <code class="o">{</code>
<code class="k">if</code> <code class="o">(</code> <code class="n">client</code> <code class="o">!=</code> <code class="kc">null</code> <code class="o">)</code>
<code class="n">client</code><code class="o">.</code><code class="na">close</code><code class="o">();</code>
<code class="o">}</code>
<code class="o">}</code>
<code class="kd">class</code> <code class="nc">LinkWriter</code> <code class="kd">extends</code> <code class="n">PrintWriter</code>
<code class="o">{</code>
<code class="n">ByteArrayOutputStream</code> <code class="n">buffer</code><code class="o">;</code>
<code class="n">Writer</code> <code class="n">client</code><code class="o">;</code>
<code class="n">LinkWriter</code><code class="o">(</code> <code class="n">Writer</code> <code class="n">client</code><code class="o">,</code> <code class="n">ByteArrayOutputStream</code> <code class="n">buffer</code> <code class="o">)</code> <code class="o">{</code>
<code class="kd">super</code><code class="o">(</code> <code class="n">buffer</code> <code class="o">);</code>
<code class="k">this</code><code class="o">.</code><code class="na">buffer</code> <code class="o">=</code> <code class="n">buffer</code><code class="o">;</code>
<code class="k">this</code><code class="o">.</code><code class="na">client</code> <code class="o">=</code> <code class="n">client</code><code class="o">;</code>
<code class="o">}</code>
<code class="kd">public</code> <code class="kt">void</code> <code class="nf">close</code><code class="o">()</code> <code class="o">{</code>
<code class="k">try</code> <code class="o">{</code>
<code class="n">flush</code><code class="o">();</code>
<code class="n">client</code><code class="o">.</code><code class="na">write</code><code class="o">(</code> <code class="n">linkText</code><code class="o">(</code> <code class="n">buffer</code><code class="o">.</code><code class="na">toString</code><code class="o">()</code> <code class="o">)</code> <code class="o">);</code>
<code class="n">client</code><code class="o">.</code><code class="na">close</code><code class="o">();</code>
<code class="o">}</code> <code class="k">catch</code> <code class="o">(</code> <code class="n">IOException</code> <code class="n">e</code> <code class="o">)</code> <code class="o">{</code>
<code class="n">setError</code><code class="o">();</code>
<code class="o">}</code>
<code class="o">}</code>
<code class="n">String</code> <code class="nf">linkText</code><code class="o">(</code> <code class="n">String</code> <code class="n">text</code> <code class="o">)</code> <code class="o">{</code>
<code class="n">Enumeration</code> <code class="n">en</code> <code class="o">=</code> <code class="n">filterConfig</code><code class="o">.</code><code class="na">getInitParameterNames</code><code class="o">();</code>
<code class="k">while</code> <code class="o">(</code> <code class="n">en</code><code class="o">.</code><code class="na">hasMoreElements</code><code class="o">()</code> <code class="o">)</code> <code class="o">{</code>
<code class="n">String</code> <code class="n">pattern</code> <code class="o">=</code> <code class="o">(</code><code class="n">String</code><code class="o">)</code><code class="n">en</code><code class="o">.</code><code class="na">nextElement</code><code class="o">();</code>
<code class="n">String</code> <code class="n">value</code> <code class="o">=</code> <code class="n">filterConfig</code><code class="o">.</code><code class="na">getInitParameter</code><code class="o">(</code> <code class="n">pattern</code> <code class="o">);</code>
<code class="n">text</code> <code class="o">=</code> <code class="n">text</code><code class="o">.</code><code class="na">replaceAll</code><code class="o">(</code>
<code class="n">pattern</code><code class="o">,</code> <code class="s">"<a href="</code><code class="o">+</code><code class="n">value</code><code class="o">+</code><code class="s">">$0</a>"</code> <code class="o">);</code>
<code class="o">}</code>
<code class="k">return</code> <code class="n">text</code><code class="o">;</code>
<code class="o">}</code>
<code class="o">}</code>
<code class="o">}</code></pre><p>That was a bit longer than our previous examples, but the basics
are the same. We wrapped the <code class="literal">HttpServletResponse</code> object with our own
<code class="literal">WrappedResponse</code> class using the
<code class="literal">HttpServletResponseWrapper</code> helper
class. Our <code class="literal">WrappedResponse</code> overrides
two methods: <code class="literal">getWriter()</code> and <code class="literal">setContentType()</code>. We override <code class="literal">setContentType()</code> in order to set a flag that
indicates whether the output is of type “text/html” (an HTML document).
We don’t want to be performing regular-expression replacements on binary
data such as images, for example, should they happen to match our
filter. We also override <code class="literal">getWriter()</code>
to provide our substitute writer stream, <code class="literal">LinkWriter</code>. Our <code class="literal">LinkWriter</code> class is a <code class="literal">PrintStream</code> that takes as arguments the client
<code class="literal">PrintWriter</code> and a <code class="literal">ByteArrayOutputStream</code> that serves as a buffer
for storing output data before it is written. We are careful to
substitute our <code class="literal">LinkWriter</code> only if the
<code class="literal">linkText</code> Boolean set by <code class="literal">setContent()</code> is <code class="literal">true</code>. When we do use our <code class="literal">LinkWriter</code>, we cache the stream so that any
subsequent calls to <code class="literal">getWriter()</code>
return the same object. Finally, we have added one method to the
response object: <code class="literal">close()</code>. A normal
<a id="I_indexterm15_id783312" class="indexterm"/><code class="literal">HttpServletResponse</code>
does not have a <code class="literal">close()</code> method. We
use ours on the return trip to the client to indicate that the <code class="literal">LinkWriter</code> should complete its processing and
write the actual data to the client. We do this in case the client does
not explicitly close the output stream before exiting the servlet
service methods.</p><p>This explains the important parts of our filter-writing example.
Let’s wrap up by looking at the <code class="literal">LinkWriter</code>, which does the magic in this
example. <code class="literal">LinkWriter</code> is a <code class="literal">PrintStream</code> that holds references to two other
<code class="literal">Writers</code>: the true client <code class="literal">PrintWriter</code> and a <a id="I_indexterm15_id783368" class="indexterm"/><code class="literal">ByteArrayOutputStream</code>.
The <code class="literal">LinkWriter</code> calls its superclass
constructor, passing the <code class="literal">ByteArrayOutputStream</code> as the target stream, so
all of its default functionality (its <code class="literal">print()</code> methods) writes to the byte array. Our
only real job is to intercept the <code class="literal">close()</code> method of the <code class="literal">PrintStream</code> and add our text linking before
sending the data. When <code class="literal">LinkWriter</code> is
closed, it flushes itself to force any data buffered in its superclass
out to the <code class="literal">ByteArrayOutputStream</code>. It
then retrieves the buffered data (with the <code class="literal">ByteArrayOutputStream toString()</code> metho