Java After Hours: 10 Projects Youll Never Do at Work
A lot of filter technology is needed to build WebLogger, so to get familiar with that, let's take a look at an example. WebLogger works with both the request object holding the data sent from the browser and the response object sending data back to the browser. So how do you work with, for example, the response object to send data back to the browser? Say that you want to filter access to a JSP page named simple.jsp and that you want a filter to add some text to the output of this JSP sent back to the browser. Here's what that JSP page looks likeas it stands, all this page does is to display the text "Using a filter" in an <H1> HTML header: <HTML> <HEAD> <TITLE>Using a filter</TITLE> </HEAD> <BODY> <H1>Using a filter</H1> <BR> </BODY> </HTML>
Because you have access to the data sent to and from this JSP page in a filter, you can display your own text in the web page, as you can see in Figure 6.2. In this figure, a filter attached to simple.jsp wrote the text "The filter wrote this," just as WebLogger added the warning about logging user access shown at the bottom of Figure 6.1. Note that the URL still points to simple.jsp; there's nothing to indicate that the page is being filtered. Very cool. Figure 6.2. Adding text to a web page with a filter.
Writing the Code
So how do you create the filter to make this work? The filter here will be called Simple.java, and it will implement the Filter interface: import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class Simple implements Filter { . . . }
To implement this interface, you need the doFilter, init, and destroy methods: import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class Simple implements Filter { public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { . . . } public void destroy() { . . . } public void init(FilterConfig filterConfig) { . . . } }
The init method is called when the filter is first initialized, and the destroy method is called when it's destroyed. The doFilter method is where the real action is; when the user is trying to access the web resource you're filtering, this is the method that will be called. The doFilter method is passed the request and response objects that Java uses to work with web resources; the request object holds the data sent to the web resource by the browser, and the response object holds the data the web resource sends back to the browser. These objects are of the ServletRequest class (not the HttpServletRequest class you saw in Chapter 5, "Chatting on the Internet with the Chat Room") and the ServletResponse class, respectively, and you can see the significant methods of these classes in Table 6.2 and 6.3.
Besides the request and response objects, you're also passed a FilterChain object in doFilter. This object provides you with access to the filtered resourceor to other filters that have been added between the current filter and the filtered resource. In Java-enabled web servers, you can stack filters in a filter chain, where control goes to the first filter, then the next, and the next, and so on, all the way to the filtered resource, such as a JSP page, servlet, or an HTML page. You don't have to know about any other possible filters, though; you can simply call the doFilter method of the FilterChain object passed to you. You pass this method both the request and response objects that were passed to you, as shown here: import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class Simple implements Filter { public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { chain.doFilter(request, response); . . . } public void destroy() { } public void init(FilterConfig filterConfig) { } }
When you call the FilterChain object's doFilter method, the next filter, if there is one, is called, followed by any other filters, and then the filtered resource itself. Control returns in the reverse order back to your filter, where you get your hands on the response object again. And that lets you write text in the response sent back to the browser, as WebLogger does. To start, you configure the MIME type of text you're going to send to the browser, setting it to "text/html": import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class Simple implements Filter { public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { chain.doFilter(request, response); response.setContentType("text/html"); . . . } public void destroy() { } public void init(FilterConfig filterConfig) { } }
Now you can add your own text to the web page being sent back to the browser, using the response object. In particular, you use the getWriter method to get a PrintWriter object, and then you can use the println method of that object to send the message "The filter wrote this.": import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class Simple implements Filter { public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { chain.doFilter(request, response); response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.println("The filter wrote this."); } public void destroy() { } public void init(FilterConfig filterConfig) { } }
Configuring the Web Server
That completes the code for this filter. To compile this file, add a new directory named logger for this chapter's project to Tomcat's webapps directory, then add a directory named WEB-INF under logger, and two empty directories, classes and lib, under WEB-INF: webapps | | -logger | | -WEB-INF | | -classes | -lib Place simple.jsp (the resource to be filtered) in the logger directory, and place Simple.java (the filter) in the classes directory. Next, copy jsp-api.jar and servlet-api.jar, the needed JAR files for this project, from the Tomcat common\lib directory to the classes directory and add them to the classpath: %set classpath=jsp-api.jar;servlet-api.jar;.
Then compile Simple.java (add the necessary path to javac.exe if needed): %javac Simple.java
This creates Simple.class, the compiled filter, ready to be used. Next, you have to connect the filter to the resource you want to filter, which you do with the deployment descriptor file, web.xml. This file connects the filtered resource to the filter you want to use, and you store this file in the WEB-INF directory. Because it's an XML document, web.xml starts with an XML declaration, followed by a <!DOCTYPE> element that indicates where software can find the syntax used in web.xml files to check what you've written. This part is standard, and it starts all web.xml files: <?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"> . . .
To group the filter together with the resource it filters, you use a <web-app> element in web.xml: <?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> . . . </web-app>
In the <web-app> element, you install a filter with the <filter> element, which encloses two other elements, <filter-name> and <filter-class>: <filter> <filter-name> <filter-class> </filter>
The <filter-name> element holds the name you want to give to the filter ("Simple Filter" in this example) and connects that name to the Java .class file for the filter, Simple.class, in the <filter-class> element. It all looks like this in web.xml: <?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>Simple Filter</filter-name> <filter-class>Simple</filter-class> </filter> . . . </web-app>
Having told the web server which Java class to use for the filter, you have to tell it which web resource you want to filter access to. That's simple.jsp here, and you connect this newly declared filter, "Simple Filter," to simple.jps using a <filter-mapping> element, which encloses these elements: <filter-mapping> <filter-name> <url-pattern> </filter-mapping> Here, <filter-name> is the name of a filter you've declared in a <filter> element (the <filter> element must come before the <filter-mapping> element), and <url-pattern> is the URL of the resource you want to filter: <?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>Simple Filter</filter-name> <filter-class>Simple</filter-class> </filter> <filter-mapping> <filter-name>Simple Filter</filter-name> <url-pattern>/simple.jsp</url-pattern> </filter-mapping> </web-app>
Note especially that the URL in <url-pattern> can use the * wildcard, so you can filter multiple pages with the same filter. For example, <url-pattern>/*</url-pattern> will filter all the web resources in the logger directory. That's all you need. Because you've changed web.xml and created a new .class file in the webapps directory, you need to restart Tomcat before it'll be able to use simple.jsp and apply your new filter. After you restart Tomcat, navigate to http://localhost:8080/logger/simple.jsp. You should see the results displayed in Figure 6.2. After testing a filter like this, you can install it on any Java-enabled web server, ready for use by users around the world. |