Redirecting a request
Another use for filters is to restrict access to resources - if a filter doesn't
call FilterChain.doFilter(), the resource won't be loaded. Simply returning
would send an empty document to the browser, so it's better to redirect or
forward the request to a different resource. As with a servlet, there are two
ways to do this. HttpServletReponse.sendRedirect() actually sends a response
to the browser giving it a URL to fetch instead of the original one, which
may be on a different server. The other option is to use a RequestDispatcher
to load a different resource on the server instead of the resource which would
have been loaded, transparently to the browser.
Unfortunately the Tomcat project doesn't provide any useful examples for this,
so I'm reduced to writing my own code. SecureFilter checks the current session
to see whether there is a User object; if not, it forwards the request to a
login form. While hardly a robust component, you can see how a more useful
system could be implemented.
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain)
throws IOException, ServletException
{
boolean authorized = false;
if (request instanceof HttpServletRequest) {
HttpSession session = ((HttpServletRequest)request).getSession(false);
if (session != null) {
User user = (User) session.getAttribute("user");
if (user != null)
authorized = true;
}
}
if (authorized) {
chain.doFilter(request, response);
return;
} else if (filterConfig != null) {
String login_page = filterConfig.getInitParameter("login_page");
if (login_page != null && !"".equals(login_page)) {
filterConfig.getServletContext().getRequestDispatcher(login_page).
forward(request, response);
return;
}
}
throw new ServletException
("Unauthorized access, unable to forward to login page");
}
Threading issues
As with servlets, it's important to remember that filters are used in a
threaded environment. A single instance of the filter class is used to handle
all of the requests it applies to (well, one instance for each <filter>
block), so more than one request may be using the object at the same time. Each
request will execute methods on the same filter object in different threads,
so you need to take care to write your filters in a threadsafe manner.
A common mistake by programmers who aren't familiar with threads is to use
object variables to store data that applies only to one thread. If you have
data which you are only using for a particular request, create variables for
it inside the method so each thread will have its own copy, not outside the
methods where all threads will share the same copy.
Here is a contrived example of making this error. This filter counts the number
of times it is invoked, storing the number in an integer object variable
named hitCount.
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain)
throws IOException, ServletException
{
hitCount++;
chain.doFilter(request, response);
context.log("This filter has been hit = " + hitCount + " times");
}
The problem is that if two requests hit the server at about the same time,
both threads may increment the hitCount before either thread reaches the
line that prints the count to the log file, so both threads will print the
same value. Combining the increment with the log line is less obviously
wrong, but it is still not threadsafe:
context.log("This filter has been hit = " + (++hitCount) + " times");
Although the incrementation happens on the same line, the line itself
is compiled into multiple bytecode instructions which aren't guaranteed
to be atomic. That is, the JVM doesn't guarantee to complete the
operation all at once, it may switch to another thread in the middle,
which might then execute the same operation (or part of the operation).
If you want to demonstrate this, use UnsafeFilter2, which inserts a 5 second
pause inside the danger area of the code. Open a page filtered by UnsafeFilter2
in two browser windows and reload them both quickly, then see the standard
output (which may be your servlet engine's console, or a log file).
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain)
throws IOException, ServletException
{
hitCount++;
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
}
chain.doFilter(request, response);
System.out.println("This filter has been hit = " + hitCount + " times");
}
On my Tomcat server this prints the following:
This filter has been hit = 2 times
This filter has been hit = 2 times
Another issue is with data that actually does need to be shared between
threads, for example caching data in a Map. If one thread modifies the
shared object while another accesses it the results may not be what you
expect; even if the second thread is only retrieving data, if the object
being accessed isn't threadsafe its internal data may be in an inconsistent
state while it is being updated by the first thread. The key here is to
use threadsafe classes, for example Hashtable instead of HashMap, to
synchronize appropriate blocks of code, and generally to be aware of
threadsafe programming techniques. Doug Lea's book "Concurrent Programming
in Java" is an excellent reference.
Conclusion
By now you should have an idea of the kinds of things filters can do, and
how to write your own filters. For more information, see the Servlet
specifications available at Java.sun.com. Filters were introduced in the
2.3 version of the specification, available at
java.sun.com, and so aren't available on
older servlet containers. The proposed 2.4 version of the specification, also
available from Sun, adds the capability to configure how a filter applies to
forwarded requests.
Download the source code from
this tutorial.
New on the Java Boutique:
New Review:
Time Management Made Easy with the Quartz Enterprise Job Scheduler
Why not just use the Java timer API? This open source scheduling
API boasts simplicity, ease-of-integration, a well-rounded feature
set, and it's free!
New Applet:
Reverse Complement
Reverse Complement is a simple applet that converts DNA or RNA
sequences into three useful formats.
Elsewhere on internet.com:
WebDeveloper Java
Lots of Java information on webdeveloper.com
WDVL Java
Thorough Java resource at the Web Developer's Virtual Library.
ScriptSearch Java
Hundreds of free Java code files to download.
jGuru: Your View of the Java Universe
Customizable portal with online training, FAQs, regular news updates, and tutorials.
|