1. Servlet filter
1.1 What is a filter
A filter is a program that runs on the server before the servlet or JSP page associated with it. Filters may be attached to one or more servlets or JSP pages and may check request information entering these resources. After this, the filter can be selected as follows:
① Call resources in a regular way (i.e., call servlets or JSP pages).
② Use the modified request information to call the resource.
③Call the resource, but modify it before sending the response to the client.
④Block the resource call, and instead go to another resource, return a specific status code or generate replacement output.
1.2 Basic principles of Servlet filter
When a servlet is used as a filter, it can process customer requests. After the processing is completed, it will be handed over to the next filter for processing, so that the client's request is processed one by one in the filter chain until the request is sent to the target. For example, a website has a web page that submits "modified registration information". After the user fills in the modified information and submits it, the server needs to do two tasks when processing: determine whether the client's session is valid; and uniformly encode the submitted data. These two tasks can be processed in a filter chain composed of two filters. When the filter process is successful, the submitted data is sent to the final target; if the filter process is unsuccessful, the view will be distributed to the specified error page.
2. Servlet filter development steps
The steps for developing a Servlet filter are as follows:
① Write a Servlet class that implements the Filter interface.
②Configure Filter in web.xml.
Developing a filter requires implementing the Filter interface. The Filter interface defines the following methods:
① Destory() is called by the web container to initialize this Filter.
② init (FilterConfig filterConfig) is called by the web container to initialize this Filter.
③ doFilter (ServletRequest request, ServletResponse response, FilterChain chain) specific filtering processing code.
3. An example of a filter framework
SimpleFilter1.java
package com.zj.sample;import java.io.IOException;import javax.servlet.Filter;import javax.servlet.FilterChain;import javax.servlet.FilterConfig;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse; public class SimpleFilter1 implements Filter { @SuppressWarnings("unused") private FilterConfig filterConfig; public void init(FilterConfig config) throws ServletException { this.filterConfig = config; } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) { try { System.out.println("Within SimpleFilter1:Filtering the Request..."); chain.doFilter(request, response);// Send the processing to the next filter System.out .println("Within SimpleFilter1:Filtering the Response..."); } catch (IOException ioe) { ioe.printStackTrace(); } catch (ServletException se) { se.printStackTrace(); } } public void destroy() { this.filterConfig = null; }}
SimpleFilter2.java
package com.zj.sample;import java.io.IOException;import javax.servlet.Filter;import javax.servlet.FilterChain;import javax.servlet.FilterConfig;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse; public class SimpleFilter2 implements Filter { @SuppressWarnings("unused") private FilterConfig filterConfig; public void init(FilterConfig config) throws ServletException { this.filterConfig = config; } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) { try { System.out.println("Within SimpleFilter2:Filtering the Request..."); chain.doFilter(request, response); // Send the processing to the next filter System.out.println("Within SimpleFilter2:Filtering the Response..."); } catch (IOException ioe) { ioe.printStackTrace(); } catch (ServletException se) { se.printStackTrace(); } } public void destroy() { this.filterConfig = null; }}
web.xml
<filter> <filter-name>filter1</filter-name> <filter-class>com.zj.sample.SimpleFilter1</filter-class></filter><filter-mapping> <filter-name>filter1</filter-name> <url-pattern>/*</url-pattern>//Filter</filter-mapping> <filter> <filter-name>filter2</filter-name> <filter-class>com.zj.sample.SimpleFilter2</filter-class></filter><filter-mapping> <filter-name>filter2</filter-name> <url-pattern>/*</url-pattern>//Filter for all visits</filter-mapping>
Open any page in the web container to output the result: (Note the request/response order executed by the filter)
Within SimpleFilter1:Filtering the Request...Within SimpleFilter2:Filtering the Request...Within SimpleFilter2:Filtering the Response...Within SimpleFilter1:Filtering the Response...
4. Report filter
Let's experiment with a simple filter that prints a message to standard output by calling the relevant servlet or JSP page. To implement this function, filtering behavior is performed in the doFilter method. Whenever a servlet or JSP page associated with this filter is called, the doFilter method generates a printout that lists the requested host and the URL of the call. Because the getRequestURL method is located in the HttpServletRequest instead of the ServletRequest, the ServletRequest object is constructed as the HttpServletRequest type. Let's change the SimpleFilter1.java in Chapter 3.
SimpleFilter1.java
package com.zj.sample;import java.io.IOException;import java.util.Date;import javax.servlet.Filter;import javax.servlet.FilterChain;import javax.servlet.FilterConfig;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.http.HttpServletRequest; public class SimpleFilter1 implements Filter { @SuppressWarnings("unused") private FilterConfig filterConfig; public void init(FilterConfig config) throws ServletException { this.filterConfig = config; } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) { try { System.out.println("Within SimpleFilter1:Filtering the Request..."); HttpServletRequest req = (HttpServletRequest) request; System.out.println(req.getRemoteHost() + " tried to access " + req.getRequestURL() + " on " + new Date() + "."); chain.doFilter(request, response); System.out.println("Within SimpleFilter1:Filtering the Response..."); } catch (IOException ioe) { ioe.printStackTrace(); } catch (ServletException se) { se.printStackTrace(); } } public void destroy() { this.filterConfig = null; }}
The web.xml settings remain unchanged, in the same chapter 3.
test:
Enter [url]http://localhost:8080/Test4Jsp/login.jsp[/url]
result:
Within SimpleFilter1:Filtering the Request...0:0:0:0:0:0:0:0:0:0:0:0:0:0:1 tried to access [url]http://localhost:8080/Test4Jsp/login.jsp[/url] on Sun Mar 04 17:01:37 CST 2007.Within SimpleFilter2:Filtering the Request...Within SimpleFilter2:Filtering the Response...Within SimpleFilter1:Filtering the Response...
5. Filters at access (using servlets to initialize parameters in filters)
The following is to set a normal access time range using init to record accesses that are not in this time period. Let's change the SimpleFilter2.java in Chapter 3.
SimpleFilter2.java.
package com.zj.sample;import java.io.IOException;import java.text.DateFormat;import java.util.Calendar;import java.util.GregorianCalendar;import javax.servlet.Filter;import javax.servlet.FilterChain;import javax.servlet.FilterConfig;import javax.servlet.ServletContext;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.http.HttpServletRequest; public class SimpleFilter2 implements Filter { @SuppressWarnings("unused") private FilterConfig config; private ServletContext context; private int startTime, endTime; private DateFormat formatter; public void init(FilterConfig config) throws ServletException { this.config = config; context = config.getServletContext(); formatter = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM); try { startTime = Integer.parseInt(config.getInitParameter("startTime"));// web.xml endTime = Integer.parseInt(config.getInitParameter("endTime"));// web.xml } catch (NumberFormatException nfe) { // Malformed or null // Default: access at or after 10 pm but before 6 am is // considered unusual. startTime = 22; // 10:00 pm endTime = 6; // 6:00 am } } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) { try { System.out.println("Within SimpleFilter2:Filtering the Request..."); HttpServletRequest req = (HttpServletRequest) request; GregorianCalendar calendar = new GregorianCalendar(); int currentTime = calendar.get(Calendar.HOUR_OF_DAY); if (isUnusualTime(currentTime, startTime, endTime)) { context.log("WARNING: " + req.getRemoteHost() + " accessed " + req.getRequestURL() + " on " + formatter.format(calendar.getTime())); // The log file is under <CATALINA_HOME>/logs.One log per day. } chain.doFilter(request, response); System.out .println("Within SimpleFilter2:Filtering the Response..."); } catch (IOException ioe) { ioe.printStackTrace(); } catch (ServletException se) { se.printStackTrace(); } } public void destroy() {} // Is the current time between the start and end // times that are marked as absolute access times? private boolean isUnusualTime(int currentTime, int startTime, int endTime) { // If the start time is less than the end time (ie, // they are two times on the same day), then the // current time is considered unusual if it is // between the start and end times. if (startTime < endTime) { return ((currentTime >= startTime) && (currentTime < endTime)); } // If the start time is greater than or equal to the // end time (ie, the start time is on one day and // the end time is on the next day), then the current // time is considered unusual if it is NOT between // the end and start times. else { return (!isUnusualTime(currentTime, endTime, startTime)); } }}
The web.xml settings remain unchanged.
Regarding Tomcat log processing, here is a further introduction. config.getServletContext().log ("log message") will write log information to the <CATALINA_HOME>/logs folder. The file name should be localhost_log.2007-03-04.txt (one is generated per day by date, and can be seen the next day). To get such a log file, you should have:
<Logger className="org.apache.catalina.logger.FileLogger" prefix="catalina_log." suffix=".txt" timestamp="true"/>
6. Site filters are prohibited
If you want to interrupt the subsequent filtering process midway when your filter detects an abnormal exception, you can do this:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse res = (HttpServletResponse) response; if (isUnusualCondition(req)) { res.sendRedirect("http://www.somesite.com"); } else { chain.doFilter(req, res); }} The following example is a prohibited site filter. If you do not want some sites to access your website, you can list its site in the param-value of web.xml, and then apply the above principle to jump out of the regular filtering and give the prohibited page.
BannedAccessFilter.java
package com.zj.sample;import java.io.IOException;import java.io.PrintWriter;import java.net.MalformedURLException;import java.net.URL;import java.util.HashSet;import java.util.StringTokenizer;import javax.servlet.Filter;import javax.servlet.FilterChain;import javax.servlet.FilterConfig;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.http.HttpServletRequest; public class BannedAccessFilter implements Filter { private HashSet<String> bannedSiteTable; /*** Deny access if the request comes from a bannered site or is referred here* by a bannered site. */ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException { System.out.println("Within BannedAccessFilter:Filtering the Request..."); HttpServletRequest req = (HttpServletRequest) request; String requestingHost = req.getRemoteHost(); String referringHost = getReferringHost(req.getHeader("Referer")); String bannedSite = null; boolean isBanned = false; if (bannedSiteTable.contains(requestingHost)) { bannedSite = requestingHost; isBanned = true; } else if (bannedSiteTable.contains(referringHost)) { bannedSite = referringHost; isBanned = true; } if (isBanned) { showWarning(response, bannedSite); } else { chain.doFilter(request, response); } System.out.println("Within BannedAccessFilter:Filtering the Response..."); } /*** Create a table of bannered sites based on initialization parameters.* Remember that version 2.3 of the servlet API mandates the use of the* Java 2 Platform. Thus, it is safe to use HashSet (which determines* whether a given key exists) rather than the clumsier Hashtable* (which has a value for each key).*/ public void init(FilterConfig config) throws ServletException { banneredSiteTable = new HashSet<String>(); String bannedSites = config.getInitParameter("bannedSites"); // Default token set: white space. StringTokenizer tok = new StringTokenizer(bannedSites); while (tok.hasMoreTokens()) { String bannedSite = tok.nextToken(); bannedSiteTable.add(bannedSite); System.out.println("Banned " + bannedSite); } } public void destroy() {} private String getReferringHost(String refererringURLString) { try { URL referringURL = new URL(refererringURLString); return (referringURL.getHost()); } catch (MalformedURLException mue) { // Malformed or null return (null); } } // Replacement response that is returned to users // who are from or referred here by a banner site. private void showWarning(ServletResponse response, String bannedSite) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter out = response.getWriter(); String docType = "<!DOCTYPE HTML PUBLIC /"-//W3C//DTD HTML 4.0 " + "Transitional//EN/">/n"; out.println(docType + "<HTML>/n" + "<HEAD><TITLE>Access Prohibited</TITLE></HEAD>/n" + "<BODY BGCOLOR=/"WHITE/">/n" + "<H1>Access Prohibited</H1>/n" + "Sorry, access from or via " + bannedSite + "/n" + "is not allowed./n" + "</BODY></HTML>"); }}
web.xml
<filter> <filter-name>BannedAccessFilter</filter-name> <filter-class>com.zj.sample.BannedAccessFilter</filter-class> <init-param> <param-name>bannedSites</param-name> <param-value> [url]www.competingsite.com[/url] [url]www.bettersite.com[/url] [url]www.moreservlets.com[/url] 127.0.0.1//We test this</param-value> </init-param></filter><filter-mapping> <filter-name>BannedAccessFilter</filter-name> <url-pattern>/*</url-pattern></filter-mapping>
test:
[url]http://localhost:8080/Test4Jsp/[/url]
result:
7. Replace filter
7.1 Modify the response
Filters can block access to resources or prevent them from being activated. But if the filter wants to change the response generated by the resource. What to do? There seems to be no way to access the response generated by a resource. The second parameter of DoFilter (ServletResponse) provides a way to send new output to the client, but does not provide the filter with a way to access the servlet or JSP page output. Why is this happening? Because the servlet or JSP page has not even been executed when the doFilter method is called for the first time. Once the doFilter method in the FilterChain object is called, it seems too late to modify the response, which is that the data has been sent to the client.
However, there is a way, that is, modify the response object of the doFilter method passed to the FilterChain object. Generally, build a cache of all output versions generated by a servlet or JSP page. Servlet API version 2.3 provides a useful resource for this, namely the HttpServletResponseWrapper class. The use of this class includes the following five steps:
1) Create a response wrapper. Extend javax.servlet.http.HttpServletResponseWrapper.
2) Provide a PrintWriter that caches output. Overload the getWriter method, return a PrintWriter that saves everything sent to it, and saves the result into a field that can be accessed later.
3) Pass this wrapper to doFilter. This call is legal because HttpServletResponseWrapper implements HttpServletResponse.
4) Extract and modify the output. After calling the doFilter method of FilterChain, the output of the original resource can be obtained by using the mechanism provided in step 2. You can modify or replace it as long as it is suitable for your application.
5) Send the modified output to the client. Because the original resource no longer sends outputs to the client (these outputs are already stored in your response wrapper), these outputs must be sent. This way, your filter needs to get a PrintWriter or OutputStream from the original response object and pass the modified output into the stream.
7.2 A reusable response wrapper
The following example gives a wrapper that can be used in most applications where the filter wants to modify the output of the resource. The CharArrayWrapper class overloads the getWriter method to return a PrintWriter which accumulates everything in a large character array. Developers can get this result by using toCharArray (original char[]) or toString (a String derived from char[]).
CharArrayWrapper.java
package com.zj.sample;import java.io.CharArrayWriter;import java.io.PrintWriter;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpServletResponseWrapper; /** * A response wrapper that takes everything the client would normally * output and saves it in one big character array. */public class CharArrayWrapper extends HttpServletResponseWrapper { private CharArrayWriter charWriter; /** * Initializes wrapper. * <P> * First, this constructor calls the parent constructor. That call *is cruel so that the response is stored and thus setHeader, *setStatus, addCookie, and so forth work normally. * <P> * Second, this constructor creates a CharArrayWriter that will* be used to accumulate the response. */ public CharArrayWrapper(HttpServletResponse response) { super(response); charWriter = new CharArrayWriter(); } /** * When servlets or JSP pages ask for the Writer, don't give them* the real one. Instead, give them a version that writes into* the character array. * The filter needs to send the contents of the array to the* client (perhaps after modifying it). */ public PrintWriter getWriter() { return (new PrintWriter(charWriter)); } /** * Get a String representation of the entire buffer. * <P> * Be sure <B>not</B> to call this method multiple times on the same * wrapper. The API for CharArrayWriter does not guarantee that it * "remembers" the previous value, so the call is likely to make* a new String every time. */ public String toString() { return (charWriter.toString()); } /** Get the underlying character array. */ public char[] toCharArray() { return (charWriter.toCharArray()); }}
7.3 Replace filter
Here is a common application of CharArrayWrapper given in the previous section: changing a filter for a multiple-occurring target string to a substitute string.
7.3.1 General replacement filter
ReplaceFilter.java gives a filter that wraps the response in CharArraryWrapper, passes the wrapper into the doFilter method of the FilterChain object, extracts a String-type value giving the output of all resources, replaces all occurrences of a target string with a substitution string, and sends the modified result to the client.
There are two things to note about this filter. First, it is an abstract class. To use it, you must create a subclass that provides the implementation of getTargetString and getReplacementString methods. An example of this treatment is given in the next subsection. Second, it utilizes a smaller utility class (see FilterUtils.java) for actual string replacement. You can use new regular expression packages instead of using low-level and tedious methods in String and StringTokenizer.
ReplaceFilter.java
package com.zj.sample;import java.io.IOException;import java.io.PrintWriter;import javax.servlet.Filter;import javax.servlet.FilterChain;import javax.servlet.FilterConfig;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.http.HttpServletResponse; /** * Filter that replaces all occurrences of a given string with a* replacement. * This is an abstract class: you <I>must</I> override the getTargetString* and getReplacementString methods in a subclass.* The first of these methods specify the string in the response* that should be replaced. The second of these specifications the string* that should replace each occurrence of the target string. */public abstract class ReplaceFilter implements Filter { private FilterConfig config; public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException { CharArrayWrapper responseWrapper = new CharArrayWrapper( (HttpServletResponse) response); // Invoke resource, accumulating output in the wrapper. chain.doFilter(request, responseWrapper); // Turn entire output into one big String. String responseString = responseWrapper.toString(); // In output, replace all occurrences of target string with replacement // string. responseString = FilterUtils.replace(responseString, getTargetString(), getReplacementString()); // Update the Content-Length header. updateHeaders(response, responseString); PrintWriter out = response.getWriter(); out.write(responseString); } /** * Store the FilterConfig object in case subclasses want it. */ public void init(FilterConfig config) throws ServletException { this.config = config; } protected FilterConfig getFilterConfig() { return (config); } public void destroy() { } /** * The string that needs replacement.*Override this method in your subclass. */ public abstract String getTargetString(); /** * The string that replaces the target. Override this method in * your subclass. */ public abstract String getReplacementString(); /** * Updates the response headers. This simple version just sets*the Content-Length header, assuming that we are using a*character set that uses 1 byte per character.* For other character sets, override this method to use* different logic or to give up on persistent HTTP connections.* In this latter case, have this method set the Connection header* to "close". */ public void updateHeaders(ServletResponse response, String responseString) { response.setContentLength(responseString.length()); }}
FilterUtils.java
package com.zj.sample; /** * Small utility to assist with response wrappers that return strings. */public class FilterUtils { /** * Change all occurrences of orig in mainString to replace. */ public static String replace(String mainString, String orig, String replacement) { String result = ""; int oldIndex = 0; int index = 0; int origLength = orig.length(); while ((index = mainString.indexOf(orig, oldIndex)) != -1) { result = result + mainString.substring(oldIndex, index) + replacement; oldIndex = index + origLength; } result = result + mainString.substring(oldIndex); return (result); }} 7.3.2 Implement a character replacement filter. Assume that Baidu acquired Google (just a hypothesis), all texts with the word Google on all pages must be replaced by Baidu! ReplaceSiteNameFilter.java inherits the ReplaceFilter.java above to implement this function. ReplaceSiteNameFilter.javapackage com.zj.sample; public class ReplaceSiteNameFilter extends ReplaceFilter { public String getTargetString() { return ("google.com.cn"); } public String getReplacementString() { return ("baidu.com"); }}
web.xml
<filter> <filter-name>ReplaceSiteNameFilter</filter-name> <filter-class>com.zj.sample.ReplaceSiteNameFilter</filter-class></filter><filter-mapping> <filter-name>ReplaceSiteNameFilter</filter-name> <url-pattern>/login.jsp</url-pattern></filter-mapping>
Test results:
Before filtering
After filtering
8. Compression filter
There are several latest browsers that can handle compressed content, automatically unpack the compressed file with gzip as the Content-Encoding response header value, and then process the results just like the original document. Sending such compressed content can save a lot of time, because the time it takes to compress a document on the server and then undo the document on the client is trivial compared to the time it takes to download the file. The program LongServlet.java gives a servlet with long, duplicate plain text output, a mature servlet for compression. If you use gzip, it can compress the output to 1/300!
When the browser supports this compression capability, the compression filter can use the CharArrayWrapper introduced in Chapter 7 to compress the content. The following content is required to complete this task:
1) Class that implements the Filter interface. This class is named CompressionFIlter. The init method stores the FilterConfig object in a field in case the subclass needs to access the servlet environment or filter name. The body of the destory method is empty.
2) The wrapped response object. The DoFilter method wraps the ServletResponse object in a CharArrayWrapper and passes this wrapper onto the doFilter method of the FilterChain object. After this call is complete, all other filters and final resources have been executed and the output is within the wrapper. In this way, the original doFilter extracts an array of characters representing the output of all resources. If the client states that it supports compression (i.e., taking gzip as a value for the Accept-Encoding header), the filter appends a GZIPOutputStream to the ByteArrayOutputStream, copy the character array into this stream, and sets the Content-Encoding response header to gzip. If the client does not support gzip, copy the unmodified character array to ByteArrayOutputStream. Finally, doFilter sends the result to the client by writing the entire character array (probably compressed) into the OutputStream associated with the original response.
3) Register the LongServlet.
CompressionFilter.java
package com.zj.sample;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.OutputStream;import java.io.OutputStreamWriter;import java.util.zip.GZIPOutputStream;import javax.servlet.Filter;import javax.servlet.FilterChain;import javax.servlet.FilterConfig;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse; /** * Filter that compresses output with gzip (assuming that browser supports * gzip). */public class CompressionFilter implements Filter { private FilterConfig config; /** * If browser does not support gzip, invoke resource normally. If browser * <I>does</I> support gzip, set the Content-Encoding response header and * invoke resource with a wrapped response that collects all the output. * Extract the output and write it into a gzipped byte array. Finally, write * that array to the client's output stream. */ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse res = (HttpServletResponse) response; if (!isGzipSupported(req)) { // Invoke resource normally. chain.doFilter(req, res); } else { // Tell browser we are sending it gzipped data. res.setHeader("Content-Encoding", "gzip"); // Invoke resource, accumulating output in the wrapper. CharArrayWrapper responseWrapper = new CharArrayWrapper(res); chain.doFilter(req, responseWrapper); // Get character array representing output. char[] responseChars = responseWrapper.toCharArray(); // Make a writer that compresses data and puts it into a byte array. ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); GZIPOutputStream zipOut = new GZIPOutputStream(byteStream); OutputStreamWriter tempOut = new OutputStreamWriter(zipOut); // Compress original output and put it into byte array. tempOut.write(responseChars); // Gzip streams must be explicitly closed. tempOut.close(); // Update the Content-Length header. res.setContentLength(byteStream.size()); // Send compressed result to client. OutputStream realOut = res.getOutputStream(); byteStream.writeTo(realOut); } } /** * Store the FilterConfig object in case subclasses want it. */ public void init(FilterConfig config) throws ServletException { this.config = config; } protected FilterConfig getFilterConfig() { return (config); } public void destroy() {} private boolean isGzipSupported(HttpServletRequest req) { String browserEncodings = req.getHeader("Accept-Encoding"); return ((browserEncodings != null) && (browserEncodings.indexOf("gzip") != -1)); }} LongServlet.javapackage com.zj.sample;import java.io.IOException;import java.io.PrintWriter;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse; /** * Servlet with <B>long</B> output. Used to test the effect of the compression * filter of Chapter 9. */ public class LongServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter out = response.getWriter(); String docType = "<!DOCTYPE HTML PUBLIC /"-//W3C//DTD HTML 4.0 " + "Transitional//EN/">/n"; String title = "Long Page"; out.println(docType + "<HTML>/n" + "<HEAD><TITLE>" + title + "</TITLE></HEAD>/n" + "<BODY BGCOLOR=/"#FDF5E6/">/n" + "<H1 ALIGN=/"CENTER/">" + title + "</H1>/n"); String line = "Blah, blah, blah, blah, blah. " + "Yadda, yadda, yadda, yadda."; for (int i = 0; i < 10000; i++) { out.println(line); } out.println("</BODY></HTML>"); }}
web.xml
<filter> <filter-name>CompressionFilter</filter-name> <filter-class>com.zj.sample.CompressionFilter</filter-class></filter><filter-mapping> <filter-name>CompressionFilter</filter-name> <servlet-name>LongServlet</servlet-name></filter-mapping> <servlet> <servlet-name>LongServlet</servlet-name> <servlet-class>com.zj.sample.LongServlet</servlet-class></servlet><servlet-mapping> <servlet-name>LongServlet</servlet-name> <url-pattern>/LongServlet</url-pattern></servlet-mapping>