Enterprise Java

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:

1
2
3
  @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:

    1
    2
    <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:
    1
    2
    3
    4
    5
    6
    7
    <!-- 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:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    <repositories>
     <!-- For testing against latest Spring snapshots -->
     <repository>
      <id>org.springframework.maven.snapshot</id>
      <name>Spring Maven Snapshot Repository</name>
      <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>
      <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:
    1
    2
    3
    4
    5
    6
    7
    <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:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    <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
    1
    2
    3
    4
    5
    <dependency>
     <groupId>org.aspectj</groupId>
     <artifactId>aspectjweaver</artifactId>
     <version>${org.aspectj-version}</version>
    </dependency>
  9. Remove the AspectJ plugin reference:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    <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.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    <plugin>
     <groupId>org.codehaus.mojo</groupId>
     <artifactId>tomcat-maven-plugin</artifactId>
     <version>1.1</version>
     <configuration>
      <server>myserver</server>
     </configuration>
    </plugin>

    This leaves the following working POM file

    001
    002
    003
    004
    005
    006
    007
    008
    009
    010
    011
    012
    013
    014
    015
    016
    017
    018
    019
    020
    021
    022
    023
    024
    025
    026
    027
    028
    029
    030
    031
    032
    033
    034
    035
    036
    037
    038
    039
    040
    041
    042
    043
    044
    045
    046
    047
    048
    049
    050
    051
    052
    053
    054
    055
    056
    057
    058
    059
    060
    061
    062
    063
    064
    065
    066
    067
    068
    069
    070
    071
    072
    073
    074
    075
    076
    077
    078
    079
    080
    081
    082
    083
    084
    085
    086
    087
    088
    089
    090
    091
    092
    093
    094
    095
    096
    097
    098
    099
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    <?xml version="1.0" encoding="UTF-8"?>
     <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>
        </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
 

Do you want to know how to develop your skillset to become a Java Rockstar?
Subscribe to our newsletter to start Rocking right now!
To get you started we give you our best selling eBooks for FREE!
1. JPA Mini Book
2. JVM Troubleshooting Guide
3. JUnit Tutorial for Unit Testing
4. Java Annotations Tutorial
5. Java Interview Questions
6. Spring Interview Questions
7. Android UI Design
and many more ....
I agree to the Terms and Privacy Policy
Subscribe
Notify of
guest


This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button