Web Application Filters were added to the 2.3 Servlet specification and have been
enhanced in the just finalised 2.4 spec. Filters
allow cross cutting concerns to be applied to a web application, which is
exactly the gig of Aspect Oriented Programming (AOP).
Being born and breed with OOP, I have always viewed AOP in the cool but
useless category. Thus I’ve never given myself the opportunity to
use them for real. On the other hand, I view webapp Filters as useful
but ugly. It would be great if AOP could replace/enhance Filters so we
could get into the cool and useful territory.
So I downloaded AspectJ with the intent of seeing how AOP cross cutting
concerns could help. The example I considered was the CompressionFilter, which can be applied to
a web applications to gzip the content generated on the fly. A compression
filter can be declared in web.xml with :
<filter> <filter-name>Compression</filter-name> <filter-class>com.acme.CompressionFilter </filter-class> </filter> <filter-mapping> <filter-name>Compression</filter-name> <url-pattern>*.html</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> <dispatcher>ERROR</dispatcher> </filter-mapping>
This CompressionFilter will apply to any request to a URL ending with
that is not an
include request. The filter wraps the HttpServletResponse object with a facade, that
in turn wraps any OutputStream or Writer provided by the response with a version capable
of compression the output. The wrappers also allow HTTP headers to be intercepted and
adjusted. The multiple wrappers involved have always stuck me as a bit over complex, error
prone and in need of a better way. But it does allow the compression concern to be applied
to a web application without considering how the html content is generated.
So how would aspectJ help applying such a cross cutting concern to a webapplication?
Reading the tutorial I determined that the basic approach needed was to define a
CompressionAspect that contains:
- PointCut definitions that identified the getOutputStream and getWriter calls
of the response object.
- Advice to wrap the response stream/writer in a compressing stream/writer
- PointCut definitions that identified the header setting methods of the response object.
- Advice to intercept and adjust the HTTP headers and other meta data.
AspectJ certainly provides the Join Points and Advice types required to create this
aspect, but unfortunately I was unable to define PointCut that capture the equivalent
semantics of the CompressionFilter declaration above. I believe that the Point Cut semantics
required are something along the lines of:
calls to getOutputStream, getWriter & setContentLength methods
on objects that implement the HttpServletResponse interface
when RequestDispatcher.include is not on the calling stack
and the associated request.getRequestURI() ending with '*.html'
and the request is being handled by my webapplication/classloader
It was looking oh-so-good up to those last two clauses! The ending with ‘*.html’ could
be done with an if() point cut, except that there is no way to navigate from a response
object to the associated request. I couldn’t find any point cuts that would assist me
with restricting the aspect to a particular web application or classloader.
My next thought was to get around this problem by creating a no-op Filter and hanging the
PointCut off calls that go via it. I think this approach would have allowed me to
specified the required PointCuts, but then I realized another problem. In order for
these point cuts to work, I would need to use the AspectJ compiler to modify the response
classes used by the web container and passed into the web application. This breaking of
web application encapsulation was not something that I am prepared to do. It would
modify my infrustructure and aspects from one webapplication to be passed if not
executred to other webapplications.
AOP or at least AspectJ, does not appear to be a good replacement for Filters, as:
- PointCuts are defined in the implementation domain (java calls and classes),
while Filters are defined closer to the application domain (URLs, requests, responses). Using
the implementation domain events to trigger application level concerns may be impossible or
at the very least devoid of the application abstractions we so carefully build in OOP.
- The technology of AspectJ does not appear appropriate for Container/Infrastructure
based deployment. It is not appropriate for the container/infrastructure classes to
be modified in order to support the application concerns of a particular application.
- The declarative nature of aspects means that PointCuts needs to do a lot of work to
reduce a global scope to a particular instance. It would be great if procedural semantics
were available so you could say in code: “wrap that object with this aspect” or “apply this
advice to that Point Cut”. Such programatic code would also assist with the tracebility
concerns of AOP.
So my first attempt at AOP has not been successful and I’m still left with the “cool but useless”
feeling. The OOP design of Filters does allow cross cutting concerns to be implemented
in a modular fashion, so useful if not cool applies.
Maybe I’m still missing something or am trying the wrong problem. I hope that an AOP
guru reading this will be able to correct the error of my ways?