söndag 7 oktober 2012

Using Sonar to monitor a JAspect-based application

I started a small project a while ago to learn some more about AspectJ and aspect oriented programming in Java. Some days ago Recently I decided to try to apply Sonar on it to get some nice quality measures such as code complexity, test coverage and static code analysis to detect potential bugs.

This is a small writeup of my experiences from that experiment.

Installation

Installation was quite straight forward. I fetched the latest version Sonarsource and followed the installation instructions. I've opted not to install Sonar as a background service yet but instead start it manually whenever I need it. If I later find that I use it alot then installing it as a service should be as simple as running a batch script.

To run Sonar you must install a SQL database in which the analysis results are stored. I opted for the community version of MySQL since there was an example on the sonarsource web site for that particular DB. As I wanted to get started quickly that was my choice. In hindsight I don't think it would have been any more complicated to use another DB.

Installation of MySQL was equally straight forward. I just ran the supplied installer using the default installation.

To get Sonar working with MySQL was a simple two step procedure described below. I will do it only briefly as the installation instructions provided on the Sonar website are very good.

Create DB and sonar user in MySQL

Doing this was as simple as running the following lines (found through the installation instructions on the Sonar web page) in the mysql command shell (mysql -u root -p):
CREATE DATABASE sonar CHARACTER SET utf8 COLLATE utf8_general_ci;
CREATE USER 'sonar' IDENTIFIED BY 'sonar';
GRANT ALL ON sonar.* TO 'sonar'@'%' IDENTIFIED BY 'sonar';
GRANT ALL ON sonar.* TO 'sonar'@'localhost' IDENTIFIED BY 'sonar';
FLUSH PRIVILEGES;

Configure Sonar

The sonar configuration file must be updated with parameters to connect to the DB and port and address conlfiguration. It is found under <SONAR INSTALLATION DIR>/conf/sonar.properties.

# Listen host/port and context path (for example / or /sonar). Default values are 0.0.0.0:9000/.
sonar.web.host:                           0.0.0.0
sonar.web.port:                           9000
sonar.web.context:                        /
.
.
.

#----- Credentials
# Permissions to create tables and indexes must be granted to JDBC user.
# The schema must be created first.
sonar.jdbc.username:                       sonar
sonar.jdbc.password:                       sonar
.
.
.
#----- MySQL 5.x/6.x
# Comment the embedded database and uncomment the following line to use MySQL
sonar.jdbc.url:jdbc:mysql://localhost:3306/sonar?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true

# Optional properties
sonar.jdbc.driverClassName:                com.mysql.jdbc.Driver

Setting up the project for sonar analyze

My initial plan was to use the Sonar Runner which is the recommended way to run a sonar analyze according to the Sonar website. Doing this proved to be easy. I just followed the instructions found here. Running the analyze was equally easy, see the instructions here. After running the analyze the results were browsable at the URL and port specified above (e.g. http://localhost:9000).
There was one cavaet using the Sonar runner though. It doesn't execute any code and can hence not be used for execution of automated tests and test coverage analysis. For this another method had to be used. The recommended approach is to use targets adjusted for your build system. Both Maven plugins and Ant tasks are available to run the analyze. For this particular project I use Maven which made the choice obvious.

Using a maven plugin to analyze the project

I'll start by dropping the entire POM here and will then discuss the details of it.

<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns="http://maven.apache.org/POM/4.0.0"
   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>datalogger</groupId>
   <artifactId>DataLogger</artifactId>
   <packaging>jar</packaging>
   <version>1.0-SNAPSHOT</version>
   <name>DataLogger</name>
   <url>http://maven.apache.org</url>
   <build>
      <plugins>
         <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.7.2</version>
            <configuration>
               <test>**/*.java</test>
            </configuration>
         </plugin>
         <plugin>
            <groupId>org.jacoco</groupId>
            <artifactId>jacoco-maven-plugin</artifactId>
            <version>0.5.10.201208310627</version>
            <executions>
               <execution>
                  <id>jacoco-initialize</id>
                  <goals>
                     <goal>prepare-agent</goal>
                  </goals>
               </execution>
            </executions>
         </plugin>
         <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>aspectj-maven-plugin</artifactId>
            <version>1.4</version>
            <executions>
               <execution>
                  <goals>
                     <goal>compile</goal>
                     <!-- use this goal to weave all your main classes -->
                     <goal>test-compile</goal>
                     <!-- use this goal to weave all your test classes -->
                  </goals>
               </execution>
            </executions>
            <configuration>
               <complianceLevel>1.6</complianceLevel>
            </configuration>
         </plugin>
         <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>sonar-maven-plugin</artifactId>
            <version>2.0</version>
         </plugin>
         <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <version>1.7</version>
            <executions>
               <execution>
                  <phase>package</phase>
                  <goals>
                     <goal>shade</goal>
                  </goals>
                  <configuration>
                     <artifactSet>
                        <excludes>
                           <exclude>junit:junit</exclude>
                           <exclude>org.aspectj:aspectjrt</exclude>
                           <exclude>org.slf4j:slf4j-simple</exclude>
                        </excludes>
                     </artifactSet>
                  </configuration>
               </execution>
            </executions>
         </plugin>
      </plugins>
   </build>

   <properties>
      <coverage.reports.dir>${basedir}/target/coverage-reports</coverage.reports.dir>
      <sonar.core.codeCoveragePlugin>jacoco</sonar.core.codeCoveragePlugin>
      <sonar.dynamicAnalysis>reuseReports</sonar.dynamicAnalysis>
   </properties>
   <dependencies>
      <dependency>
         <groupId>org.aspectj</groupId>
         <artifactId>aspectjrt</artifactId>
         <version>1.6.11</version>
      </dependency>
      <dependency>
         <groupId>junit</groupId>
         <artifactId>junit</artifactId>
         <version>4.10</version>
      </dependency>
      <dependency>
         <groupId>com.google.code.gson</groupId>
         <artifactId>gson</artifactId>
         <version>1.7.1</version>
      </dependency>
      <dependency>
         <groupId>org.slf4j</groupId>
         <artifactId>slf4j-api</artifactId>
         <version>1.6.4</version>
      </dependency>
      <dependency>
         <groupId>org.slf4j</groupId>
         <artifactId>slf4j-simple</artifactId>
         <version>1.6.4</version>
      </dependency>
   </dependencies>
</project>

There are four plugins of interest here. The surefire plugin which is used to execute the test cases. The jacoco plugin which is used to instrument the classes for coverage measurement (using a java agent). The sonar plugin which performs the actual analyse and the aspectj plugin which performs compile time weaving of my aspects.
I initially had some problems getting any coverage reports since the code I wanted to test was actually the aspects and they weren't woven into the code the way I wanted. The problem was that I never managed to get the load time weaving that I initially used (using a java agent) to work together with the code coverage agent of jacoco. Switching to compile time veawing using the AspectJ plugin did the trick.
Also, to get the AspectJ plugin to work properly I had to explicitly set the compliance level to 1.6.

With the above configuration and pom I now have full blown quality measures of my project without having to set up and configure every tool individually.