Filters vs Aspects
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 html
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.
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
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?
