Using FiltersS2C Home « Using Filters
In this lesson we look at filters which are Java components that allow us to receive requests and responses and manipulate them through the filtering mechanism. In many ways filters are like servlets, they have a lifecycle, access to the servlet context and can pass control to another filter or target resource, in much the same way as request dispatchers work with servlets.
The main purpose of filters is to intercept a client request before it reaches a web resource such as a servlet or JSP page and then to interpose itself after the response has been sent from the web resource and before it reaches the client. There are many reasons why you may want to use filters before a web resource gets the request or after it sends the response and some of these are listed below:
- Filtration Before Request:
- Authentication filters which can deny acesss to the requested resource.
- Caching filters that store uploaded resources.
- Logging and auditing filters that write traffic details and volumes to reports.
- MIME-type chain filters that reformat the request accordingly.
- Tokenizing filters that reformat the request from an input form.
- XSL/T filters that transform XML content
- Filtration After Response:
- Data compression filters that zip up responses before sending them back to the client.
- Encryption filters that encode the response for security purposes.
- Image conversion filters that deflate or change images before sending them back to the client.
- Reformat filters that transorm the response tailored to a particular client.
Filters are modular in nature and because of this modularity it's common practice to have several filters chained together each with a specific purpose in modifying the request or response. The sequence of filter invocation is configured via URL mappings within the DD and we will look at this in more detail when we code a filter later in the lesson. This also means that filters aren't tied to any particular servlet and in fact a servlet has no idea that the request or response is being filtered.
The following slideshow shows a filter intercepting the request and response from the client so you can see the sequence of events.
In this slide our client has clicked on a link that points to a resource that is a servlet or a static resource that is declared as requiring filtration in the DD,
so the web server directs the HTTP request to our web container.
The Filter
APITop
The Filter
API consists of three interfaces these being, the javax.servlet.Filter
interface which is analagous to the javax.servlet.Servlet
interface and supplies
the framework with which we build our filters. The javax.servlet.FilterConfig
interface which is comparable to the javax.servlet.ServletConfig
interface in that
it provides a mechanism for configuring a filter from initialisation parameters we declare in the DD. Lastly the javax.servlet.FilterChain
interface which allows us to chain
filters together by passing the request and response from one filter to the next. The tables below summarize the methods within each of the interfaces.
The Filter
InterfaceTop
Every Filter
object can access a FilterConfig
object, but to be of any use this object must be saved, generally as an instance variable from within the overidden
init()
method. The FilterConfig
object can then be used to obtain initialization parameters, as well as a reference to the ServletContext
object which
can be useful for things such as loading the resources required for filtering tasks. The doFilter()
method is where we perform the filtering tasks and the destroy()
can be useful for tidying up resources. These three methods are shown in the table below, use the links in the table to go to example code of the method in question.
Method Declaration | Description |
---|---|
void init( FilterConfig filterConfig) | Invoked by the web container to advise a filter that it is being placed into service. |
void doFilter( ServletRequest request, | Invoked by the web container each time a request/response pair is applied to the current client request for a resource. |
void destroy() | Invoked by the web container to advise a filter that it is being taken out of service. |
The FilterConfig
InterfaceTop
The FilterConfig
interface allows us to pass information to a filter during initialisation. The container provides an instance of a FilterConfig
implementation which can be saved
to an instance variable during the initialisation of a filter within the init()
method. The four methods of the FilterConfig
Interface are shown in the table
below, use the links in the table to go to example code of the method in question.
Method Declaration | Description |
---|---|
java.lang.String getServletName() | Returns name of this filter as defined in the DD. |
java.lang.String | Returns a String containing value of specified name initialization parameter, or null if the parameter does not exist. |
java.util.Enumeration getInitParameterNames() | Returns a java.util.Enumeration of String objects containing the filter's initialization parameter names, or an empty java.util.Enumeration if filter has none. |
ServletContext getServletContext() | Returns a reference to the ServletContext in which the caller is executing. |
The FilterChain
InterfaceTop
The FilterChain
interface allows a filter to invoke the next filter in the chain, or if the calling filter is the last filter in the chain, to invoke the resource at the end
of the chain. The container provides instances of FilterChain
implementation when required so there is no need to instantiate this type. The table below shows the single
method in the FilterChain
interface, use the links in the table to go to example code of the method.
Method Declaration | Description |
---|---|
void doFilter(ServletRequest request, | Causes the next filter in the chain to be invoked, or if the calling filter is the last filter in the chain, causes the resource at the end of the chain to be invoked. |
Filter LifecycleTop
We can think of an object's lifecycle as the various stages that said object goes through during its existence. Because a filter has to run within the boundaries of whatever web container we are using, there are additional factors involved with the lifecycle of a filter object.
The following slideshow shows the various stages in the filter lifecycle. Press the button below to step through the slideshow:
Our client has clicked on a link that points to a resource that is a servlet or a static resource that is declared as requiring filtration in the DD, so the web server
directs the HTTP request to our web container.
We will go into the different stages of the filter lifecycle shown in the slideshow in much more detail and answer some other questions raised by it below.
Class LoadingTop
A container can contain many web applications and each of these has a class loader that is used to search for and load classes from the /WEB-INF/classes/
directory and JAR files from
the /WEB-INF/lib/
directory. Having separate class loaders for each application allows filters in different applications to have the same name.
Because the container finds a class it doesn't necessarily load it; the filter could be lazily initialised on first use or when the container deems it necessary.
InstantiationTop
Just like any other object our filter has to be instantiated so it can be called by the container. This is achieved using the default no-args constructor which is best left to the compiler to generate. If you absolutely must have a custom constructor for your filter then you also need to explicitly code a no-args constructor so the container can instantiate your filter.
You cannot do any initialisation at this stage as our filter doesn't exist in what we can think of as a filter state until the no-args constructor has completed and the container makes it into a filter object. Any attempts to initialise the filter using other parts of the application will fail and is the reason why we have separate instantiation and intialisation steps.
InitialisationTop
We now have a fully fledged filter object waiting to be initialized to its default state. Here is a good time to talk about what value we get from a normal object being transformed into a
filter object. Well apart from the benefits we get from the container every filter also gets access to three other objects, these being FilterConfig
, FilterChain
and ServletContext
.
Each filter gets one FilterConfig
object and this is where we can put configuration information for deployment rather than in a constructor. We configure initialisation
parameters for the FilterConfig
object via the DD. This is achieved using the <init-param>
sub-level element of the top-level <filter> element, an example of which is shown below.
<filter>
<filter-name>compressionFiltre</servlet-name>
<filter-class>test.Filter</servlet-class>
<init-param>
<param-name>datasource</param-name>
<param-value>somefile.txt</param-value>
</init-param>
</filter>
Don't worry too much about the DD elements shown above for now but a point of note is that we can have multiple entries of the <init-param> sub-element.
The FilterConfig
object also allows us to access the ServletContext
object which we spoke about in detail in the ServletConfig & ServletContext lesson.
The init()
Lifecycle MethodTop
The init()
lifecycle method is the first of the three lifecycle methods associated with a filter and is run only once during the life of a servlet. You can put initialisation code required by your
filter that cannot be placed declaratively within the DD in this method, such as Java code for referencing and database connections.
The FilterConfig
object created by the container is passes as a parameter to this method and this is your one and only chance to save this object, generally to an instance variable, for later use.
We can also use methods of the FilterConfig
object to access any initialisation parameters we declared within the DD.
The init()
method can throw a ServletException
or a subclass thereof and if this happens the container bypasses the other lifecycle methods and makes the servlet available for garbage collection.
Request ServicingTop
So our one time initialisation has completed and our filter is now ready to service requests. When a request from a client comes in that points to a resource that is a servlet or a static resource that is declared
as requiring filtration in the DD, the doFilter()
method is invoked.
The doFilter()
Lifecycle MethodTop
The doFilter() lifecycle method is the second of the three lifecycle methods associated with a servlet and is run every time a client requests a resource that is a servlet or a static resource that is declared
as requiring filtration in the DD. As you can see from the signature doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
method uses the ServletRequest
and ServletResponse
objects and is where all our filtration takes place. It also allows us to chain to the next filter, or if the calling filter is the last filter in the chain, to invoke the
resource at the end of the chain using the chain
method parameter, an example of which we will see shortly when we write some code.
The doFilter()
method can throw a ServletException
or a subclass thereof and if this happens and the exception is permanent the container returns a 404('Not Found')
HTTP
status code and removes the filter from service. If the exception is temporary the container returns a 503('Service Unavailable')
HTTP status code and makes the filter temporarily unavailable. After a
period of time the container will put the filter back into service, making it available to filter more requests. The doFilter()
method can also throw an IOException
as we are accessing
writers which can cause exceptions.
DestructionTop
The final phase of a filters lifecycle is its destruction and can happen for a number of reasons including server shutdown and an application being undeployed. When such an event happens the container will call
the filters destroy()
method which gives us an opportunity to free resources and tidy up before the filter is garbage collected.
The destroy()
Lifecycle MethodTop
The destroy()
lifecycle method is the last of the three lifecycle methods associated with a filter and is run only once during the life of a filter. This method is invoked by the container
when a filter is going to be destroyed and gives us a chance to clean up and housekeep before the filter is garbage collected.
Filter StackingTop
We have spoken about chaining filters within the DD but what does this mean in terms of Java and the stack? The following slideshow illustrates two filters that are declared in the DD and a final chain to a servlet which shows what happens with the stack.
Example Of Using FiltersTop
We will enhance the code from the slideshow above for our filtering example and just write some lines to the Tomcat log so you can see each filter and then the servlet getting invoked.
Within the _ServletsAdv
folder create a new folder for this lesson called filtering
Within the filtering
folder we will create separate folders to hold our DD, source files and our compiled byte code and these will be called dd
, src
and classes
.
Within the src
folder create a subfolders called controller
.
So after creating these folders your directory structure should look something like the following screenshot:
Coding The FiltersTop
We are now ready to code our filters which will consist of implementations of the Filter
interface.
Coding ImplFilter1
The ImplFilter1
class implements the int()
, doFilter()
and destroy()
methods of the Filter
interface. We output some
messages to the console so you can see the lifecycle and invocation flow through the filter and how we get initialisation parameters from the DD using the FilterConfig
object.
Cut and paste the following code into your text editor and save in the c:\_ServletsAdv\filtering\src\controller directory as ImplFilter1.java
.
package controller;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.*;
public class ImplFilter1 implements Filter {
private FilterConfig fc;
@Override
public void init(FilterConfig fc) throws ServletException {
// Write to console
System.out.println("Within init() method of Filter1");
// Save FilterConfig
this.fc = fc;
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws ServletException, IOException {
// Write to console
System.out.println("Within doFilter() method of Filter1");
// Get init parameter and write to console
String initParam = fc.getInitParameter("f1param");
System.out.println("Filter1 init param: f1param, value: " + initParam);
// Invoke next filter or resource (Filter2)
System.out.println("Leaving Filter1");
chain.doFilter(req, resp);
System.out.println("Back in Filter1");
// Create response
resp.setContentType("text/html");
PrintWriter writer = resp.getWriter();
writer.print("<p>Filter1 Response</p>");
}
@Override
public void destroy() {
// Write to console
System.out.println("Within the destroy() method of Filter1");
}
}
Compiling ImplFilter1
Open your command line editor:
Change to directory c:\_ServletsAdv\filtering\src\controller
Compile ImplFilter1.java
using the java compiler with the -cp
and -d
options as below, making sure you change apache-tomcat-6.0.37
to wherever you downloaded Tomcat to.
javac -cp c:\apache-tomcat-6.0.37\lib\servlet-api.jar -d ..\..\classes ImplFilter1.java
The following screenshot shows that we get a clean compile and also the ImplFilter1
class now compiled into the classes\controller
directory.
Coding ImplFilter2
The ImplFilter2
class implements the int()
, doFilter()
and destroy()
methods of the Filter
interface. We output some
messages to the console so you can see the lifecycle and invocation flow through the filter.
Cut and paste the following code into your text editor and save in the c:\_ServletsAdv\filtering\src\controller directory as ImplFilter2.java
.
package controller;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.*;
public class ImplFilter2 implements Filter {
private FilterConfig fc;
@Override
public void init(FilterConfig fc) throws ServletException {
// Write to console
System.out.println("Within init() method of Filter2");
// Save FilterConfig
this.fc = fc;
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws ServletException, IOException {
// Write to console
System.out.println("Within doFilter() method of Filter2");
// Invoke next filter or resource (AServlet)
System.out.println("Leaving Filter2");
chain.doFilter(req, resp);
// response code
System.out.println("Back in Filter2");
// Create response
resp.setContentType("text/html");
PrintWriter writer = resp.getWriter();
writer.print("<p>Filter2 Response</p>");
}
@Override
public void destroy() {
// Write to console
System.out.println("Within the destroy() method of Filter2");
}
}
Compiling ImplFilter2
Open your command line editor:
Change to directory c:\_ServletsAdv\filtering\src\controller
Compile ImplFilter2.java
using the java compiler with the -cp
and -d
options as below, making sure you change apache-tomcat-6.0.37
to wherever you downloaded Tomcat to.
javac -cp c:\apache-tomcat-6.0.37\lib\servlet-api.jar -d ..\..\classes ImplFilter2.java
The following screenshot shows that we get a clean compile and also the ImplFilter2
class now compiled into the classes\controller
directory.
Coding The AServlet
ClassTop
This simple servlet just creates a paragraph for HTML output and writes to the Tomat 6 logs.
Cut and paste the following code into your text editor and save it in the c:\_ServletsAdv\filtering\src\controller directory as AServlet.java
.
package controller;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.*;
import javax.servlet.http.*;
public class AServlet extends HttpServlet {
private static final long serialVersionUID = 871964L;
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// Write to console
System.out.println("Within doGet() method of AServlet");
resp.setContentType("text/html");
PrintWriter writer = resp.getWriter();
writer.print("<html><head></head><body><p>AServlet Response</p></body></html>");
writer.flush();
writer.close();
}
}
Compiling The AServlet
Class
Open your command line editor:
Change to directory c:\_ServletsAdv\filtering\src\controller
Compile AServlet.java
using the java compiler with the -cp
and -d
options as below, making sure you change apache-tomcat-6.0.37
to wherever you downloaded Tomcat to.
javac -cp c:\apache-tomcat-6.0.37\lib\servlet-api.jar -d ..\..\classes AServlet.java
The following screenshot shows that we get a clean compile and also the AServlet
class now compiled into the classes\controller
directory.
Coding The DDTop
<?xml version="1.0" encoding="UTF-8"?>
<web-app xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" version="2.5">
<filter>
<filter-name>FirstFilter</filter-name>
<filter-class>controller.ImplFilter1</filter-class>
<init-param>
<param-name>f1param</param-name>
<param-value>f1paramvalue</param-value>
</init-param>
</filter>
<filter>
<filter-name>SecondFilter</filter-name>
<filter-class>controller.ImplFilter2</filter-class>
</filter>
<filter-mapping>
<filter-name>FirstFilter</filter-name>
<url-pattern>/example</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>SecondFilter</filter-name>
<url-pattern>/example</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>UsingFilters</servlet-name>
<servlet-class>controller.AServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>UsingFilters</servlet-name>
<url-pattern>/example</url-pattern>
</servlet-mapping>
</web-app>
Save the web.xml
file in the DD
folder.
The <filter> ElementTop
Ok lets look at the new DD elements we have entered starting with the top-level <filter> element which is used to describe and point to our filter class. The <filter> element contains several sub-level elements which themselves can contain sub-level elements. All of which are explained in detail in the DD Elements At A Glance quick reference so we won't go into them here. What we will explain are the sub-level elements within our DD above. The sub-level <filter-name> and <filter-class> elements are mandatory and can only appear once within the top-level <filter> element.
The <filter-name> sub-level element must come before the <filter-class> sub-level element. The value of the filter name you give this element must be unique within the web application and be at least one character long. The <filter> top-level element is used in conjunction with the <filter-mapping> top-level element, which we look at below.
The <filter-class> sub-level element defines the fully qualifed name of a Java Servlet class, delimited by dots for the packages our servlet is within. The value you give this class doesn't have to be unique within a web application.
The <init-param> sub-level element is optional and can appear many times within the top-level
<filter> element. Within <init-param>
the <description> sub-level element is optional and the <param-name> and
<param-value> sub-level elements are both mandatory, must appear only once and represent a key/value pair. The values
within these elements can be retrieved using methods of the FilterConfig
interface.
The <filter-mapping> ElementTop
The <filter-mapping> element is used to map an incoming resource to a filter and is used in conjunction with the <filter> top-level element to allow controlled access to resources. The <filter-mapping> element has three sub-level elements described below.
The <url-pattern> and <servlet-name> sub-level elements can be placed in any order and there must be at least one of either present. The value of the servlet names must be unique within the web application and be at least one character long.
When a request comes in for a servlet or a static resource that is declared as requiring filtration in the DD the part of the address supplied by the client after the web application name,
is used to match against the <url-pattern> and <servlet-name>
sub-level elements within the DD. When one is found the <filter-name> sub-level element of the
<filter-mapping> top-level element is used to find the <filter-name>
sub-level element within the top-level <filter> element. From here we can get the location of the filter via the
<filter-class> sub-level element. This is how the mapping from the request to the filter, which is hidden from public
view in the WEB-INF
directory is achieved.
A filter chain is based on the order of the <url-pattern> and <servlet-name> sub-level elements declared in the DD. When more than one resource is mapped to a given resource all filters with matching URL patterns are located and are applied to the chain in the order they were declared in the DD. Once this is completed the same process is repeated for filters that have a matching <servlet-name>.
The <dispatcher> sub-level element is optional and can appear up to four times; the values are listed below:
- REQUEST - This is the default and is a direct client request for a resource.
- FORWARD - Internal request on the web server for a resource via the
forward()
method of a Request Dispatcher. - INCLUDE - Internal request on the web server for a resource via the
include()
method of a Request Dispatcher. - ERROR - Internal request on the web server for a resource that has been created as an
error-page
.
A discussion on URL mapping takes place in the Servlets Glossary - URL Mapping section.
Tomcat DeploymentTop
Go to your Tomcat installation which in my case is:
C:\apache-tomcat-6.0.37\webapps\
Within the webapps
folder create a folder for our web application called filtering
Within the filtering
folder create the WEB-INF
folder.
Copy the web.xml
file and the classes
directory and contents from our development environment into the WEB-INF
folder.
After creating these folders and copying the files from development your Tomcat directory structure within the webapps\filtering
folder should look something like the following screenshot:
Testing Our ServletTop
Open a command prompt and change the directory to the location of our Tomcat bin directory and then type in startup
(Windows 7) or startup.sh
(Linux/Unix/OSX).
Press Enter and the Tomcat server should open up in a new window.
You can also start up Tomcat by double-clicking the startup
batch file within your Tomcat /bin
folder.
With Tomcat up and running type the following into your web browser address bar and press enter:
http://localhost:8080/filtering/example
The following screenshot shows the results of typing the above into your web browser address bar and pressing enter.
So the AServlet
servlet seemed to run fine and render some HTML to our browser window, but what happened to the HTML paragraphs we sent from the ImplFilter1
and ImplFilter2
classes?. Lets check the entries in the Tomcat logs (at the bottom) to make sure everything worked ok.
The following screenshot shows the System.out.println
messages we sent to the console from the ImplFilter1
, ImplFilter2
and AServlet
classes. The relevant
lines are highlighted in yellow so you can see them easier.
So from looking at the logs everything has run as expected but still doesn't answer where the paragraphs from the ImplFilter1
and ImplFilter2
classes has gone. Well the reason that we don't
see these lines is that we closed the response in the AServlet
servlet using the writer.close();
statement. This commits the response and so anything we do after that is like closing
the stable door after the horse has bolted, it's too late. Admittedly we are adding the two paragraphs after we have closed the HTML so we are not coding well formed HTML. Of course in the real world this is not
acceptable but makes no difference to the point we are making, in fact if you remove the writer.close();
statement from the AServlet
servlet and recompile, redeploy and run the code the two paragraphs will then appear.
How we get around this problem with the response being committed is the discussion of the next lesson when we look at wrappers.
Lesson 4 Complete
In this lesson we looked at the three filter interfaces and how we can use filters to intercept our requests and responses.
What's Next?
In the next lesson we look at decorating our requests and responses using wrappers.