Verse Of The Day

Saturday, April 12, 2008

Dynamic Servlet Filters Using JVM Scripting

What got me on the subject of Servlet Filters in the first place was an idea that occurred to me recently. Servlet Filters aren't the sort of thing that can be easily altered at runtime. They are mapped in the web.xml file, so if there are Filters you only want for development or test, but not production (like the DebugFilter I presented in the last post), you have to jump through some hoops to make that happen. You could edit the web.xml before deployment, or have separate dev/test/prod web.xml files that ANT copies into place depending on the build, or have some sort of runtime flag (DB property, value bound in JNDI, etc.) that will either run the Filter logic, or just pass through to the next in chain.

But what if you could not only enable or disable the Filter, but also change it's behavior, on the fly at runtime without special build or deploy steps. I was pondering this idea and came up with the concept of a Servlet Filter, that by itself, does nothing.

Nothing? Well, nothing by itself.

"By itself" is the key phrase here. Instead of doing something in the Java code (getting timings, checking security, auditing calls, printing debug statements, etc.) it just instantiates a scripting engine, like BeanShell (BSH), JRuby, Groovy, etc. It starts up the scripting engine, passes the Request and/or Response objects, and runs a script that you have identified. That script does all the meat-n-potatoes work, and the Filter then calls next in chain like it normally would. And the best part is, the script could be changed at runtime, from a simple do-nothing-and-return to full blown screwing around with the Request and Response objects.

I've developed a proof-of-concept (POC) and have it running under Glassfish v2 application server. I used BeanShell for the scripting engine in the POC, but as stated above, any of the scripting engines for the JVM should work. This isn't quite ready for primetime, but it fully works.

There are actually 2 scripts called by this filter, one before the chain.doFilter() call is made, and one after. That way you have the flexibility to do operations either on the request, on the response, or both.

First, the Java code for the Filter:

Then, the 2 BSH scripts:


import java.servlet.*;
import java.servlet.http.*;
import java.util.*;

System.out.println("This is the BSHServletFilter_PRE.bsh script!!!");

StringBuffer output = new StringBuffer();
output.append("\nRequest Attributes\n");
Enumeration attrs = request.getAttributeNames();
while (attrs.hasMoreElements()) {
String attr = (String) attrs.nextElement();
output.append(attr +" - " + request.getAttribute(attr));

output.append("Request Parameters\n");
Enumeration params = request.getParameterNames();
while (params.hasMoreElements()) {
String param = (String) params.nextElement();
output.append(param +" - " + request.getParameter(param));

System.out.println("This consludes the BSHServletFilter_PRE.bsh script!!!");


System.out.println("This is the BSHServletFilter_POST.bsh script!!!");
System.out.println("About to alter the response...");
response.getWriter().print("<h1>This response is from the BSH script</h1>");
System.out.println("Done altering the response");

And the browser screenshot...

This is currently just at the POC stage. It is working as-is, running under Glassfish. But as you will immediately notice, the filenames for the scripts are hard-coded, and there is definitely some more cleanup to make this production quality code. But, you can change the behaviors of the filter, for better or worse, at runtime without any compiling, deploying, or restarting of servers. You can even "disable" the filter by writing BSH scripts that do nothing.

No comments: