Maven, Scala and AspectJ(AOP)

4 Minutes reading time

Recently i was thinking about combining AspectJ and Scala in one project. Of course we can argue that functional programming addresses a lot of issues we are trying to solve with AspectJ, but that is not the point. AspectJ is a Bytecode manipulation framework, not a Java code manipulation thing. So why not implement cross cutting concerns like security or transaction handling with AOP/AspectJ in a Scala project? For this short example, i am using AspectJ 1.6.11 and Scala 2.10.1.

The good news are that Maven, Scala and AspectJ fit pretty smooth together. The bad news are that support for Scala together with AspectJ is not very good in Eclipse and even IntelliJ. There are some workarounds available, but this only works with annotation-style aspects, and not the more powerful .aj syntax. The AspectJ compiler currently only works together with the Java compiler, basically it is a wrapper around it. The Scala compiler itself only supports Java and Scala code, but no .aj AspectJ files. The only common language is Java, so we also need to write our Aspects in Java using AspectJs annotation style syntax.

To make it working, we have to setup the project as described in this article. Finally, we have to write our aspects using Java annotation style, this will create the following compilation order:

1) compile Scala code and Java code with annotation style aspects

2) use AspectJ weaver to weave pre compiled aspects into project output

The AspectJ weaving can be done using compile time or load time weaving. Due to lack of good IDE support, load time weaving might be the option for development environment and compile time weaving for production environment. But this decision is up to you:-)

Let’s start with a short example of what i mean. This is the aspect annotation style Java code:

package de.mirkosertic.java;

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

@Aspect
public class TestAspect {

    @Around("execution(@de.mirkosertic.java.Loggable * *.*(..))")
    public Object invoke(ProceedingJoinPoint joinPoint) throws Throwable {
        try {
            System.out.println("Invoking logic");
            return joinPoint.proceed();
        } finally {
            System.out.println("Invocation done");
        }
    }
}

This aspect will print the message “Invoking logic” before a method is entered annotated with the @Loggable annotation and print the message “Invocation done” when it’s finished. Let’s see the @Loggable annotation Java code:

package de.mirkosertic.java;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Loggable {
}

Straight forward. Please note the RetentionPolicy.RUNTIME meta annotation. We need the annotation available at runtime, so inside the generated byte code, because the AspectJ weaving process is another compile step, and AspectJ needs to be able to discover the annotation, as it modifies the byte code, and not the original source.

Now let’s add the aspect to some Java and Scala code. Here is the Java main method:

package de.mirkosertic.java;

import de.mirkosertic.scala.TestScala;

public class Main {

    @Loggable
    public static void main(String[] args) {
        TestScala theTest = new TestScala();
        theTest.out("Hallo");
    }
}

And finally the Scala aspect usage:

package de.mirkosertic.scala

import de.mirkosertic.java.TestJava
import de.mirkosertic.java.Loggable

class TestScala {

  @Loggable
  def out(aValue: String) {
    val theJava = new TestJava()
    theJava.out(aValue)
  }
}

Now we need something to glue it all together. Here is the Maven pom.xml with all relevant pieces:

<?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/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>MavenScalaMixed</groupId>
    <artifactId>MavenScalaMixed</artifactId>
    <version>1.0-SNAPSHOT</version>

    <repositories>
        <repository>
            <id>scala-tools.org</id>
            <name>Scala-tools Maven2 Repository</name>
            <url>http://scala-tools.org/repo-releases</url>
        </repository>
    </repositories>
    <pluginRepositories>
        <pluginRepository>
            <id>scala-tools.org</id>
            <name>Scala-tools Maven2 Repository</name>
            <url>http://scala-tools.org/repo-releases</url>
        </pluginRepository>
    </pluginRepositories>

    <build>
        <plugins>
            <plugin>
                <groupId>org.scala-tools</groupId>
                <artifactId>maven-scala-plugin</artifactId>
                <executions>
                    <execution>
                        <id>compile</id>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                        <phase>compile</phase>
                    </execution>
                    <execution>
                        <id>test-compile</id>
                        <goals>
                            <goal>testCompile</goal>
                        </goals>
                        <phase>test-compile</phase>
                    </execution>
                    <execution>
                        <phase>process-resources</phase>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>aspectj-maven-plugin</artifactId>
                <version>1.4</version>
                <executions>
                    <execution>
                        <phase>process-classes</phase>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <complianceLevel>1.6</complianceLevel>
                    <weaveDirectories>
                        <weaveDirectory>${project.build.directory}/classes</weaveDirectory>
                    </weaveDirectories>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>org.scala-lang</groupId>
            <artifactId>scala-library</artifactId>
            <version>2.10.1</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.6.11</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

Easy:-) We just need good IDE support, but that’s another topic. There is an example project available demonstrating IntelliJ Scala AspectJ integration. You can checkout the project here. Unfortunately there is a bug inside IntelliJ 12 preventing the project from being compiled inside the IDE. For further details, checkout the bug status at youtrack.jetbrains.com/issue/SCL-5483.

Git revision: 63a36b0

Loading comments...