Showing posts with label AspectJ. Show all posts
Showing posts with label AspectJ. Show all posts

Tuesday, May 25, 2010

AspectJ Load-Time Weaving

AspectJ has two weaving models, build-time and load-time. Here I'm gonna talk about binary load-time weaving. The good thing about it is that we can create a separate Java project and AspectJ project and weave them at runtime. This can be achieved thanks to JVMTI. Hence, it requires Java 5 and above.

Let's say we have a Java project called my-java-project.

HelloWorld.java
package myproject;

public class HelloWorld {
    
    public void sayHello(String message) {
        System.out.println(message);
    }
}

Main.java
package myproject;

public class Main {

    public static void main(String[] args) {
        HelloWorld helloWorld = new HelloWorld();
        helloWorld.sayHello("Hello World");
    }
}

And an AspectJ project called my-aspectj-project.

HelloWorldAspect.java
package myproject;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class HelloWorldAspect {

    @Around("execution(* myproject.HelloWorld.sayHello(..))")
    public Object aroundAspect(ProceedingJoinPoint joinPoint) {
        System.out.println("Before sayHello()");
        Object obj = null;
        try {
            obj = joinPoint.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        System.out.println("After sayHello()");
        return obj;
    }
}

Here we also need to add META-INF/aop.xml inside this AspectJ jar.

aop.xml
<?xml version="1.0" encoding="UTF-8"?>
<aspectj>
    <aspects>
        <aspect name="myproject.HelloWorldAspect" />
    </aspects>
</aspectj>

To run, execute this from the commaand line.
java -javaagent:aspectjweaver-1.5.4.jar -cp my-aspectj-project-0.0.1-SNAPSHOT.jar:my-java-project-0.0.1-SNAPSHOT.jar:aspectjrt-1.6.7.jar myproject.Main

Monday, May 24, 2010

Mixin in Groovy and Java

Mixin is the ability to mix certain functionalities into other classes, i.e. to add new methods into other classes.

Because of the dynamic nature of Groovy, it's very straightforward to use mixin in Groovy. All we need to do is to call mixin in a class that we want to mix in as shown in the example below.

class Laughable {
    def laugh() {
        println "Hahaha"
    }
}

class Person {
}

Person.mixin(Laughable)

Person person = new Person()
person.laugh()

In Java, however, it's not that straightfoward to implement mixin. Luckily, we have AspectJ that can help to do that. Here I'm gonna show you how to use mixin using @AspectJ syntax.

Laughable.java
package myproject;

public interface Laughable {

    public void laugh();
}

LaughableImpl.java
package myproject;

public class LaughableImpl implements Laughable {

    @Override
    public void laugh() {
        System.out.println("Hahaha");
    }
}

Person.java
package myproject;

public class Person {
}

All we need here is to declare @DeclareMixin annotation.

LaughableAspect.java
package myproject;

import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.DeclareMixin;

@Aspect
public class LaughableAspect {

    @DeclareMixin("myproject.Person")
    public Laughable identifiable() {
        return new LaughableImpl();
    }
}


The ugly part here is that we need to typecast the person object into Laughable interface.
Main.java
package myproject;

public class Main {

    public static void main(String[] args) {
        Person person = new Person();
        Laughable laughable = (Laughable) person;
        laughable.laugh();
    }
}

Wednesday, May 19, 2010

AspectJ Inter-type Declaration

AspectJ is a very powerful AOP framework. It's most commonly used for logging, security, transaction, etc. Besides that, AspectJ has a powerful feature that allows you to add a method or a field to a class, or declare a type that implement an interface. I will show you how it can be done in AspectJ.

Here, we intend to add a new method, called sayBye(String message) into HelloWorld.
HelloWorld.java
package myproject;

public class HelloWorld {

    public void sayHello(String message) {
        System.out.println(message);
    }
}

We create a new interface called ByeWorld and make HelloWorld class to implement it.
ByeWorldAspect.aj
package myproject;

public aspect ByeWorldAspect {

    declare parents: HelloWorld implements ByeWorld;

    public void ByeWorld.sayBye(String message) {
        System.out.println(message);
    }

    before(ByeWorld byeWorld)
        : execution(* ByeWorld+.*(..))
            && !execution(* ByeWorld.*(..))
            && this(byeWorld) {
    }

    private static interface ByeWorld {
    }
}

Main.java
package myproject;

public class Main {

    public static void main(String[] args) {
        HelloWorld helloWorld = new HelloWorld();
        helloWorld.sayHello("Hello World!");
        helloWorld.sayBye("Bye World!"); // There is no sayBye() in HelloWorld class!
    }
}

Make sure you add failOnError to true in the maven-compiler-plugin.
pom.xml
<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>myproject</groupId>
    <artifactId>aspectj-project</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>
    <dependencies>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.6.7</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.6<source>
                    <target>1.6</target>
                    <failOnError>false</failOnError>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>aspectj-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                        <configuration>
                            <source>1.6</source>
                            <target>1.6</target>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>java</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <mainClass>myproject.Main</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Although this feature is very powerful, my advice is not to overuse it because it can lead your code into an unmaintainable state. Remember, great power comes with great responsibility :)

Tuesday, May 11, 2010

AspectJ and @AspectJ without Spring using Maven

AspectJ offers two syntaxes. The first one is with the AspectJ syntax, another programming language. The second one is the @Aspect, which is set of annotations. With @AspectJ, our plain Java IDE will work just fine. There is no need to install AspectJ plugin or anything of that sort. For those obvious reasons, my preference is to use @AspectJ, the only catch is that it needs to run on Java 5 and above.

Here is a brief introduction how to setup AspectJ and @AspectJ without Spring using Maven.

Using AspectJ

LoggingAspect.aj
package myproject;

public aspect LoggingAspect {

    pointcut logging() : execution(* HelloWorld.sayHello(..));

    Object around() : logging() {
        System.out.println("Before " + thisJoinPointStaticPart.getSignature());
        Object ret = proceed();
        System.out.println("After " + thisJoinPointStaticPart.getSignature());

        return ret;
    }
}
The thisJoinPointStaticPart is one of the variables available in AspectJ.

or

Using @AspectJ

LoggingAspect.java
package myproject;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class LoggingAspect {

    @Pointcut("execution(* HelloWorld.sayHello(..))")
    public void logging() {}

    @Around("logging()")
    public Object logging(ProceedingJoinPoint thisJoinPoint) throws Throwable {
        System.out.println("Before " + thisJoinPoint.getSignature());
        Object ret = thisJoinPoint.proceed();
        System.out.println("After " + thisJoinPoint.getSignature());

        return ret;
    }
}
Unlike the AspectJ syntax, we need to include ProceedingJoinPoint argument to get something similar to thisJoinPointStaticPart in AspectJ syntax.

The Maven project requires Maven AspectJ plugin.
pom.xml
<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>myproject</groupId>
<artifactId>aspectj-project</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.6.8</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
<configuration>
<mainclass>myproject.Main</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>

HelloWorld.java
package myproject;

public class HelloWorld {

    public void sayHello(String message) {
        System.out.println(message);
    }
}

Main.java
package myproject;

public class Main {

    public static void main(String[] args) {
        HelloWorld helloWorld = new HelloWorld();
        helloWorld.sayHello("Hello World!");
        helloWorld.sayHello("Bye World!");
    }
}

To run, just execute 'mvn clean package exec:java'.