Thursday 4 June 2015

AspectJ Weaving - Finally Working !!!!!



After almost banging my head on the desk I finally get this working and thought of sharing the same in order to have a smooth run for anyone looking at it for the first time.

Before explaining the sample project I would like to set the plot that why I wanted to use AspectJ at the first place. I wanted to log a method's execution time in java. Though I would have done it a simple way by recording the difference between start time and end time of the method I chose AspectJ for not polluting the method body with the execution time logic.

In this tutorial I am not going to explain what is AspectJ? As I wanted to focus more on a working example. I have created a sample MAVEN project consists of the following java files:

SampleTarget.java:
This class contains a method fact for which we have to record the execution time.

 package com.piyush.aspect.tutorial;

/**
 * Class on which advice will be applied
 */
public class SampleTarget {
    public static void main(String[] args) {
        // Calling fact method on which 'around' advice will be applied
        new SampleTarget().fact(5);
    }

    public void fact(int num) {
        int c, fact = 1;
        if ( num < 0 )
           System.out.println("Number should be non-negative.");
        else
        {
           for ( c = 1 ; c <= num ; c++ )
              fact = fact * c;
           System.out.println("Factorial of "+num+" is = "+fact);
        }
    }
} 


SampleAspect.java: This class contains the advice implementation which has to be applied to the SampleTarget fact method.

package com.piyush.aspect.tutorial;

import org.aspectj.lang.ProceedingJoinPoint;
/**
 * A sample class annotated with @Aspect and implementing around advice
 */
@Aspect
public class SampleAspect {
    @Around("execution (* com.piyush.aspect.tutorial.SampleTarget.fact*(..))")
    public void advice(ProceedingJoinPoint joinPoint) throws Throwable {
        final long startTime = System.nanoTime();

        joinPoint.proceed();
        System.out.println("Elapsed time: ="+ (System.nanoTime() - startTime));
    }
}


Above things were not that difficult and can be found easily over the various blogs or tutorials. The key thing which has to be setup right is the pom.xml. Following is the pom.xml file which can be used without any modifications:

<?xml version="1.0" encoding="UTF-8"?>
<project
    <modelVersion>4.0.0</modelVersion>
    <groupId>SampleProject</groupId>
    <artifactId>SampleProject</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <properties>
        <targetJdk>1.7</targetJdk>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.6.12</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.6.12</version>
        </dependency>
    </dependencies>
    <build>
        <sourceDirectory>src</sourceDirectory>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>aspectj-maven-plugin</artifactId>
                <version>1.5</version>
                <dependencies>
                    <dependency>
                        <groupId>org.aspectj</groupId>
                        <artifactId>aspectjtools</artifactId>
                        <version>1.6.12</version>
                    </dependency>
                </dependencies>
                <configuration>
                    <complianceLevel>1.6</complianceLevel>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.1</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>java</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <mainClass>com.piyush.aspect.tutorial.SampleTarget</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Above pom.xml contains 3 dependencies aspectjrt, aspectjweaver and aspectjtools. It also contains aspectj maven plugin and maven execution plugin to execute SampleTarget.java having main method.

Now we have to run this project and see if the advice is applied to the fact method or not. Following command can be used to run the project from command line (make sure you are in the root directory of the project):

Command : mvn clean install OR mvn install

Following output should be displayed after running the project:

[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Building Unnamed - SampleProject:SampleProject:jar:0.0.1-SNAPSHOT
[INFO]    task-segment: [clean, install]
[INFO] ------------------------------------------------------------------------
[INFO] [clean:clean {execution: default-clean}]
[INFO] Deleting /home/piyusht/workspace/rtb/MyAspect/target
[INFO] [resources:resources {execution: default-resources}]
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 1 resource
[INFO] [compiler:compile {execution: default-compile}]
[INFO] Compiling 3 source files to /home/piyusht/workspace/rtb/MyAspect/target/classes
[WARNING] POM for 'org.apache.maven.wagon:wagon-provider-api:pom:1.0-beta-6:runtime' is invalid.
Its dependencies (if any) will NOT be available to the current build.
[INFO] [aspectj:compile {execution: default}]
[INFO] [resources:testResources {execution: default-testResources}]
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /home/piyusht/workspace/rtb/MyAspect/src/test/resources
[INFO] [compiler:testCompile {execution: default-testCompile}]
[INFO] No sources to compile
[WARNING] POM for 'org.apache.maven:maven-toolchain:pom:2.0.9:runtime' is invalid.
Its dependencies (if any) will NOT be available to the current build.
[INFO] [surefire:test {execution: default-test}]
[INFO] No tests to run.
[INFO] Surefire report directory: /home/piyusht/workspace/rtb/MyAspect/target/surefire-reports
-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Results :
Tests run: 0, Failures: 0, Errors: 0, Skipped: 0
[INFO] [jar:jar {execution: default-jar}]
[INFO] Building jar: /home/piyusht/workspace/rtb/MyAspect/target/SampleProject-0.0.1-SNAPSHOT.jar
[INFO] Preparing exec:java
[WARNING] Removing: java from forked lifecycle, to prevent recursive invocation.
[INFO] No goals needed for project - skipping
[INFO] [exec:java {execution: default}]
Factorial of 5 is = 120
Elapsed time: =119435
[INFO] [install:install {execution: default-install}]
[INFO] Installing /home/piyusht/workspace/rtb/MyAspect/target/SampleProject-0.0.1-SNAPSHOT.jar to /home/piyusht/.m2/repository/SampleProject/SampleProject/0.0.1-SNAPSHOT/SampleProject-0.0.1-SNAPSHOT.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 4 seconds
[INFO] Finished at: Wed Jun 03 18:09:31 IST 2015
[INFO] Final Memory: 31M/244M
[INFO] ------------------------------------------------------------------------

das Ende !!!! Nope it is not garbage, its THE END in german. Although it has nothing to do with this post, mentioned just to put THE END in a different way :-)

Hope it helps. Please share your feedback and questions If any.