Friday, December 1, 2006

Using AspectJ in web applications

AspectJ is a great tool, and can make your life a lot easier when used properly. I have found several great uses for it in web application programming. Most web applications use a database connection and share that connection in a http session. Often a programmer will need to get the connection object from the session and check to make sure it isn't null, if it is try to instantiate it and catch any errors. Instead of putting this code everywhere you use the connection just use an aspect. The following example works well with a portlet application.


public aspect SanityCheck {
protected pointcut render(RenderRequest req,RenderResponse res) :
execution(void GenericPortlet.do*(RenderRequest,RenderResponse)
&& args(req,res);

around(RenderRequest req, RenderResponse res) : render(req,res)
{
Object conn = req.getPortletSession().getAttribute("conn");
if (conn == null)
{
try{
conn = new MyConnection();
req.getPortletSession().setAttribute("conn",conn,PortletSession.APPLICATION_SCOPE);
}catch(Exception e){
//handle error
}
}
proceed(req,res);
}
}


Now you have all this logic in one place and there is no need to be concerned with this problem elsewhere in your code. You also have a reusable pointcut for all renders, notice it is protected so any aspects that extend this aspect can also advise this pointcut.

Managing sessions can also be a pain sometimes. You might have one class that relies on session variables, but you want those to be cleared when a user navigates away from that page. Luckily we have the negation operator in AspectJ.


public aspect ClearSession {

private List<String> varNames = new ArrayList<String>();

protected pointcut display(MyPortlet portlet, RenderRequest req) :
execution(void MyPortlet.do*(RenderRequest,RenderResponse))
&& target(portlet)
&& args(req,*);

private pointcut notDisplay(RenderRequest req) :
execution (void GenericPortlet.do*(RenderRequest,RenderResponse)
&& !execution(void MyPortlet.do*(RenderRequest,RenderResponse))
&& args(req,*);

//only do this after returning, if it threw an exception we probably don't want to bother
after(MyPortlet portlet, RenderRequest req) returning : display(portlet,req)
{
//assumes MyPortlet has this method to retrieve a list of variable names it uses
List<String> names = portlet.getVarNames();
for (String x:names)
{
if (!varNames.contains(x))
varNames.add(x);
}
}

before(RenderRequest req) : notDisplay(req)
{
for (String x:varNames)
req.getPortletSession().removeAttribute(x,PortletSession.APPLICATION_SCOPE);
varNames.clear();
}
}


This will get a list of variables to be cleared from the portlet, and then when anything other than this portlet executes, we clear those variables.

No comments: