Auditing a Spring MVC Webapp with AspectJ. Part 1

If you’re like me, then you’ll have those kinds of programming days where everything seems to go incredibly well. You write the code and the tests and it just works. And then and there are those other kinds of days, the really bad ones, where you know that everything you’ve written is as right as it can be and the code refuses to work: something is obviously wrong, but you’ve no idea what. I had one of these kinds of days when writing the code for this blog. The idea was to demonstrate how to use Spring and Aspectj to audit a user’s visits to a screen.

Auditing a user’s visits to a screen is one of those few cross-cutting concerns that Aspect Oriented Programming (AOP) solves very well. The idea in the case of my demo code, is that you add an annotation to the appropriate controllers and every time a user visits a page, then that visit is recorded. Using this technique you can construct a picture of the most popular screens and therefore the most popular chunks of functionality in your application. Knowing these details makes it easier to decide where to aim your development effort as it doesn’t pay to develop those chunks of your application that hardly anyone ever uses.

I’ve talked about AspectJ and AOP before in the following blogs, which is great as they demonstrate the basics, but they aren’t a real working Spring MVC application. My previous blogs were:

This time I thought that I’d come up will a fully functioning Spring MVC application using a useful AOP cross-cutting concern.

For the demo-code I created a simple Spring MVC application that has two screens: a home page and a help page. On top of this I’ve created a simple annotation: @Audit, which is used to mark a controller as one that needs auditing (not all of them will, especially if you choose to audit function points rather than individual screens) and to tell the advice object the screen id as demonstrated in the snippet of code below:

  @Audit("Home") 
  @RequestMapping(value = "/", method = RequestMethod.GET) 
  public String home(Locale locale, Model model) {

That is, until it all went pear shaped…

The plan of attack was to write my simple @Audit annotation and handle it using a simple AuditAdvice class with a method annotated with Aspectj’s @Before annotation. I would then pretend this was a real advice class, which meant delegating the actual auditing to an autowired AuditService object.

I started by creating a sample Spring MVC application using the Spring project template:

I then put all the code together and expected it to just work, except it didn’t: Spring would not, no matter what I tried, autowire the AuditService into the AuditAdvice class. This meant that when my @Before annotated method was called, it threw a NullPointerException

When you’re in the situation where you strongly suspect your code is right and it just won’t work, then one of the areas for investigation is the project POM file and setup.

The thing is, when you use someone else’s API, project setup or other tool, you tend to trust it more than you would trust your own code. I guess that the reasons for this are that it’s usually written by a highly respectable organization, which makes you kind of think that they have some magical way of writing really good code, plus it usually contains a whole bunch of stuff that you don’t really understand.

This is really irrational as the API, tool, configuration file etc have been written by programmers like you and me, who probably make as many mistakes as we do.

This fact usually worries me every time I step on to an airplane, as a software bug at 30,000ft is not something you want to happen.

The problem with the Spring MVC project POM is that it’s slightly obsolete and full of stuff that you just don’t need for a standard Spring MVC Java application, plus, there’s no links to any of the plugin documentation, so finding out what it all does and what the different settings mean is difficult.

I did write a blog called Dissecting Spring’s MVC Project POM some time ago, which tries to explain how the Spring MVC template application POM works.

These are the changes I had to make to the standard Spring MVC template POM file to get my code working.

  1. Update the version of Spring used by the project to the latest: 3.2.3.RELEASE
  2. Update the version of AspectJ to 1.7.1
  3. Remove the Spring Roo version number.

    This will create the create the following version properties:

    <org.springframework-version>3.2.3.RELEASE</org.springframework-version>
    <org.aspectj-version>1.7.1</org.aspectj-version>
  4. Remove other Spring Roo dependencies:
    <!-- Roo dependencies -->
    <dependency>
     <groupId>org.springframework.roo</groupId>
     <artifactId>org.springframework.roo.annotations</artifactId>
     <version>${org.springframework.roo-version}</version>
     <scope>provided</scope>
    </dependency>

    This is not a Roo project and I hate unnecessary configuration.

  5. Remove references to the Spring repositories:
    <repositories>
     <!-- For testing against latest Spring snapshots -->
     <repository>
      <id>org.springframework.maven.snapshot</id>
      <name>Spring Maven Snapshot Repository</name>
      <url>http://maven.springframework.org/snapshot</url>
      <releases><enabled>false</enabled></releases>
      <snapshots><enabled>true</enabled></snapshots>
     </repository>
     <!-- For developing against latest Spring milestones -->
     <repository>
      <id>org.springframework.maven.milestone</id>
      <name>Spring Maven Milestone Repository</name>
      <url>http://maven.springframework.org/milestone</url>
      <snapshots><enabled>false</enabled></snapshots>
     </repository>
    </repositories>
  6. This example uses the default WAR file name, so remove the reference to the maven-war-plugin:
    <plugin>
     <groupId>org.apache.maven.plugins</groupId>
     <artifactId>maven-war-plugin</artifactId>
     <configuration>
      <warName>abc</warName>
     </configuration>
    </plugin>
  7. Update the Surefire plugin to remove the Roo references:
    <plugin>
     <groupId>org.apache.maven.plugins</groupId>
     <artifactId>maven-surefire-plugin</artifactId>
     <configuration>
      <junitArtifactName>junit:junit</junitArtifactName>
      <!-- Remove the excludes -->
      <excludes>
       <exclude>**/*_Roo_*</exclude>
      </excludes>
     </configuration>
    </plugin>
  8. Add aspectjweaver as a dependency below aspectjrt
    <dependency>
     <groupId>org.aspectj</groupId>
     <artifactId>aspectjweaver</artifactId>
     <version>${org.aspectj-version}</version>
    </dependency>
  9. Remove the AspectJ plugin reference:
    <plugin>
     <groupId>org.codehaus.mojo</groupId>
     <artifactId>aspectj-maven-plugin</artifactId>
     <!-- Have to use version 1.2 since version 1.3 does not appear to work with ITDs -->
     <version>1.2</version>
     <dependencies>
      <!-- You must use Maven 2.0.9 or above or these are ignored (see MNG-2972) -->
      <dependency>
       <groupId>org.aspectj</groupId>
       <artifactId>aspectjrt</artifactId>
       <version>${org.aspectj-version}</version>
      </dependency>
      <dependency>
       <groupId>org.aspectj</groupId>
       <artifactId>aspectjtools</artifactId>
       <version>${org.aspectj-version}</version>
      </dependency>
     </dependencies>
     <executions>
      <execution>
       <goals>
        <goal>compile</goal>
        <goal>test-compile</goal>
       </goals>
      </execution>
     </executions>
     <configuration>
      <outxml>true</outxml>
      <source>${java-version}</source>
      <target>${java-version}</target>
     </configuration>
    </plugin>

    This is the reference that causes all the problems. Without it, default values are used and the application works.

  10. Update the tomcat-maven-plugin for automatic deployment.
    <plugin>
     <groupId>org.codehaus.mojo</groupId>
     <artifactId>tomcat-maven-plugin</artifactId>
     <version>1.1</version>
     <configuration>
      <server>myserver</server>
      <url>http://localhost:8080/manager/text</url>
     </configuration>
    </plugin>

    This leaves the following working POM file

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
     <modelVersion>4.0.0</modelVersion>
     <groupId>com.captaindebug</groupId>
     <artifactId>audit</artifactId>
     <packaging>war</packaging>
     <version>1.0.0-BUILD-SNAPSHOT</version>
     <properties>
      <java-version>1.7</java-version>
      <org.springframework-version>3.2.3.RELEASE</org.springframework-version>
      <org.aspectj-version>1.7.1</org.aspectj-version>
      <org.slf4j-version>1.5.10</org.slf4j-version>
     </properties>
     <dependencies>
      <!-- Spring -->
      <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-context</artifactId>
       <version>${org.springframework-version}</version>
       <exclusions>
        <!-- Exclude Commons Logging in favor of SLF4j -->
        <exclusion>
         <groupId>commons-logging</groupId>
         <artifactId>commons-logging</artifactId>
        </exclusion>
       </exclusions>
      </dependency>
      <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-webmvc</artifactId>
       <version>${org.springframework-version}</version>
      </dependency>
    
      <!-- AspectJ -->
      <dependency>
       <groupId>org.aspectj</groupId>
       <artifactId>aspectjrt</artifactId>
       <version>${org.aspectj-version}</version>
      </dependency>
      <dependency>
       <groupId>org.aspectj</groupId>
       <artifactId>aspectjweaver</artifactId>
       <version>${org.aspectj-version}</version>
      </dependency>
    
      <!-- Logging -->
      <dependency>
       <groupId>org.slf4j</groupId>
       <artifactId>slf4j-api</artifactId>
       <version>${org.slf4j-version}</version>
      </dependency>
      <dependency>
       <groupId>org.slf4j</groupId>
       <artifactId>jcl-over-slf4j</artifactId>
       <version>${org.slf4j-version}</version>
       <scope>runtime</scope>
      </dependency>
      <dependency>
       <groupId>org.slf4j</groupId>
       <artifactId>slf4j-log4j12</artifactId>
       <version>${org.slf4j-version}</version>
       <scope>runtime</scope>
      </dependency>
      <dependency>
       <groupId>log4j</groupId>
       <artifactId>log4j</artifactId>
       <version>1.2.15</version>
       <exclusions>
        <exclusion>
         <groupId>javax.mail</groupId>
         <artifactId>mail</artifactId>
        </exclusion>
        <exclusion>
         <groupId>javax.jms</groupId>
         <artifactId>jms</artifactId>
        </exclusion>
        <exclusion>
         <groupId>com.sun.jdmk</groupId>
         <artifactId>jmxtools</artifactId>
        </exclusion>
        <exclusion>
         <groupId>com.sun.jmx</groupId>
         <artifactId>jmxri</artifactId>
        </exclusion>
       </exclusions>
       <scope>runtime</scope>
      </dependency>
    
      <!-- @Inject -->
      <dependency>
       <groupId>javax.inject</groupId>
       <artifactId>javax.inject</artifactId>
       <version>1</version>
      </dependency>
    
      <!-- Servlet -->
      <dependency>
       <groupId>javax.servlet</groupId>
       <artifactId>servlet-api</artifactId>
       <version>2.5</version>
       <scope>provided</scope>
      </dependency>
      <dependency>
       <groupId>javax.servlet.jsp</groupId>
       <artifactId>jsp-api</artifactId>
       <version>2.1</version>
       <scope>provided</scope>
      </dependency>
      <dependency>
       <groupId>javax.servlet</groupId>
       <artifactId>jstl</artifactId>
       <version>1.2</version>
      </dependency>
    
      <!-- Test -->
      <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
       <version>4.7</version>
       <scope>test</scope>
      </dependency>
    
     </dependencies>
     <build>
      <plugins>
       <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
         <source>${java-version}</source>
         <target>${java-version}</target>
        </configuration>
       </plugin>
       <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <configuration>
         <junitArtifactName>junit:junit</junitArtifactName>
        </configuration>
       </plugin>
       <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>tomcat-maven-plugin</artifactId>
        <version>1.1</version>
        <configuration>
         <server>myserver</server>
         <url>http://localhost:8080/manager/text</url>
        </configuration>
       </plugin>
    
      </plugins>
     </build>
    </project>

Finally… having got the project setup correct, the next thing to do is to move on the code, which is something I’ll be covering next time.

The code for this and the next blog is available on github: https://github.com/roghughe/captaindebug/tree/master/audit-aspectj
 

Related Whitepaper:

Introduction to Web Applications Development

Kick start your web apps development with this introductory ebook!

This 376 page eBook 'Introduction to Web Applications Development', starts with an introduction to the internet, including a brief history of the TCT/IP protocol and World Wide Web. It defines the basic concepts for web servers and studies the case of Apache, the most used webserver, while other free software webservers are not forgotten. It continues with webpage design focusing on HTML and JavaScript. XML Schemas, their validation and transformation are covered as well as dynamic webpages built with CGI, PHP or JSP and database access.

Get it Now!  

Leave a Reply


5 + one =



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.

Sign up for our Newsletter

15,153 insiders are already enjoying weekly updates and complimentary whitepapers! Join them now to gain exclusive access to the latest news in the Java world, as well as insights about Android, Scala, Groovy and other related technologies.

As an extra bonus, by joining you will get our brand new e-books, published by Java Code Geeks and their JCG partners for your reading pleasure! Enter your info and stay on top of things,

  • Fresh trends
  • Cases and examples
  • Research and insights
  • Two complimentary e-books