aspects.jcabi: #Loggable doesn't add logs to the output - java

I am bored of adding manual logs for debugging each and every method that I write.
I came to know about #Loggable annotation of jcabi but am not successful in implementing that and your help is highly appreciated.
Below is the code that I have tried.
import com.jcabi.aspects.Loggable;
import lombok.extern.slf4j.Slf4j;
#Slf4j
public class Jcabi {
#Loggable
private static String checkJcabi(String stringToPrint) {
log.info("Print Successfull");
return stringToPrint;
}
public static void main(String[] args) {
checkJcabi("Hello World!");
}
}
IDE console prints this:
[main] INFO com.amazon.optimus.cpamutil.utils.Jcabi - Print Successfull
This is the log for the log.info() I have added in the method and there is no log for the #Loggable annotation something like this (below) as mentioned in this post
[INFO] com.example.Foo #power(2, 10): 1024 in 12μs
[INFO] com.example.Foo #power(3, 3): 27 in 4μs
Below are the dependencies packages that I use:
JCabiAspects = 1.0;
AspectJ = 6.0;
Slf4j = 1.7;
Slf4j_Simple = 1.7;
Let me know if you need more details. Thanks.

You need to do weaving of your binaries, as explained here: http://aspects.jcabi.com/example-weaving.html

You need to do the binary weaving. In the post that you linked, it says so and it also says that you can use the jcabi-maven-plugin to do the weaving for you, given that you are using maven.
Just add a few dependencies to your classpath and configure jcabi-maven-plugin for aspect weaving (get their latest versions in Maven Central)
<project>
<dependencies>
<dependency>
<groupId>com.jcabi</groupId>
<artifactId>jcabi-aspects</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>com.jcabi</groupId>
<artifactId>jcabi-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>ajc</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
If you are using gradle, you can use this gradle-aspectj-binary plugin

Related

AspectJ Aspects not getting triggered in Maven Project

I am trying to build a POC project using AspectJ without using Spring AOP. I am using an annotation based approach where I want to run the aspect #Around the method which has been annotated with an annotation. For some reason my aspects don't get triggered. Below is my code:
pom.xml
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.7</version>
<configuration>
<complianceLevel>1.8</complianceLevel>
<source>1.8</source>
<target>1.8</target>
<showWeaveInfo>true</showWeaveInfo>
<verbose>true</verbose>
<Xlint>ignore</Xlint>
<encoding>UTF-8</encoding>
</configuration>
<executions>
<execution>
<goals>
<!-- use this goal to weave all your main classes -->
<goal>compile</goal>
<!-- use this goal to weave all your test classes -->
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
resources/META-INF/aop.xml
<aspectj>
<aspects>
<aspect name="com.aditya.personal.aspects.MetricsAspect"/>
<weaver options="-verbose -showWeaveInfo">
<include within="com.carrot.personal.aspects.*"/>
</weaver>
</aspects>
</aspectj>
My Aspect:
#Aspect
public class DataTrackAspect {
#Around("#annotation(com.carrot.personal.aspects.DataTrackEnabled)")
public Object performAspect(ProceedingJoinPoint joinPoint, DataTrackEnabled dataTrackEnabled) throws Throwable {
Object result = joinPoint.proceed();
DataTrackHelper dataTrackHelper = new DataTrackHelper();
dataTrackHelper.recordInstanceCount(dataTrackEnabled.dataClass(), dataTrackEnabled.dataName(), dataTrackEnabled.instance());
return result;
}
}
The annotated method
#DataTrackEnabled(dataClass = "Hey", dataName = "There", instance = "How are you?")
public Map<String, String> fetchUser() {
Map<String, String> returnable = new HashMap<>();
returnable.put("firstName", "carrot");
returnable.put("lastName", "radish");
return returnable;
}
I can't seem to figure it out as to what am I missing. I have uploaded the sample code on GitHub here.
You are using AspectJ Maven Plugin, i.e. you are going to use compile-time weaving. Therefore, you do not need aop.xml because that one is used for load-time weaving. So you can delete it.
During compilation you should get a compile error:
Unbound pointcut parameter 'dataTrackEnabled'
That tells you that your advice method has a parameter dataTrackEnabled which does not occur anywhere in the pointcut as it should. So just change the pointcut from
#Around("#annotation(com.carrot.personal.aspects.DataTrackEnabled)")
to
#Around("#annotation(dataTrackEnabled)")
As you see, I am referring to the method parameter name from the pointcut. If you would not bind the annotation to a parameter, then you would need the fully qualified class name as you used it before.
Also, your Maven POM should cause this error:
diamond operator is not supported in -source 1.5
This is because using AspectJ Maven does not automatically deactivate or replace Maven Compiler Plugin and the latter complains that you did not set source and target versions for it. So you have two options:
Deactivate Maven Compiler, let AspectJ Maven compile your Java and aspect classes.
Set the compiler level to 1.8 (or simply 8) for Maven Compiler and use the same settings for AspectJ Maven.
I will choose option no. 2 in this case. Maven knows properties named maven.compiler.source and maven.compiler.target. Let us define and use them:
<!-- (...) -->
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<!-- (...) -->
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.7</version>
<configuration>
<complianceLevel>${maven.compiler.target}</complianceLevel>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
<!-- (...) -->
Now there are a few more warnings:
Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent!
bad version number found in C:\Users\alexa\.m2\repository\org\aspectj\aspectjrt\1.8.9\aspectjrt-1.8.9.jar expected 1.8.2 found 1.8.9
The first two are just because you forgot to set <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>. While you are at it, you can set the encoding for generated reports, too: <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>.
The last one is aspect-related and due to the fact that AspectJ Maven 1.7 depends on AspectJ 1.8.2, not 1.8.9. But be careful! It depends on aspectjtools (for using the compiler), not on aspectjweaver (for load-time weaving). You can either upgrade the version or override the dependency inside the plugin. I am going to show you both at the same time, just in case.
Last but not least, the latest version of AspectJ 1.8 is 1.8.13. But you can just use the very latest version 1.9.6, it supports up to Java 14 (the next version for Java 15+16 is almost ready) and can still produce byte code for Java 8.
How about this POM?
<?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>com.carrot</groupId>
<artifactId>SO_AJ_MavenProjectNotWorking_66734262</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<aspectj.version>1.9.6</aspectj.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.7</version>
<configuration>
<complianceLevel>${maven.compiler.target}</complianceLevel>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
<showWeaveInfo>true</showWeaveInfo>
<verbose>true</verbose>
<Xlint>ignore</Xlint>
<encoding>UTF-8</encoding>
</configuration>
<executions>
<execution>
<goals>
<!-- use this goal to weave all your main classes -->
<goal>compile</goal>
<!-- use this goal to weave all your test classes -->
<goal>test-compile</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${aspectj.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
</dependencies>
</project>
Now when you run the program, you should see something like:
Recording: dataClass = Hey, dataName = There, instance = How are you?
Recording: dataClass = Hey, dataName = There, instance = How are you?
You may ask why the advice is triggered twice. Well, just because #annotation(dataTrackEnabled) captures both the method call() and the method execution() pointcut. So let us limit the match to just executions.
The full solution looks like this (do not forget RUNTIME retention for your annotation):
package com.carrot.personal.aspects;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#Retention(RetentionPolicy.RUNTIME)
public #interface DataTrackEnabled {
String dataClass();
String dataName();
String instance();
}
package com.carrot.personal.app;
public class DataTrackHelper {
public void recordInstanceCount(String dataClass, String dataName, String instance) {
System.out.println("Recording: dataClass = " + dataClass + ", dataName = " + dataName + ", instance = " + instance);
}
}
package com.carrot.personal.app;
import com.carrot.personal.aspects.DataTrackEnabled;
import java.util.HashMap;
import java.util.Map;
public class Application {
public static void main(String[] args) {
System.out.println(new Application().fetchUser());
}
#DataTrackEnabled(dataClass = "Hey", dataName = "There", instance = "How are you?")
public Map<String, String> fetchUser() {
Map<String, String> returnable = new HashMap<>();
returnable.put("firstName", "carrot");
returnable.put("lastName", "radish");
return returnable;
}
}
package com.carrot.personal.aspects;
import com.carrot.personal.app.DataTrackHelper;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
#Aspect
public class DataTrackAspect {
#Around("#annotation(dataTrackEnabled) && execution(* *(..))")
public Object performAspect(ProceedingJoinPoint joinPoint, DataTrackEnabled dataTrackEnabled) throws Throwable {
System.out.println(joinPoint);
Object result = joinPoint.proceed();
DataTrackHelper dataTrackHelper = new DataTrackHelper();
dataTrackHelper.recordInstanceCount(dataTrackEnabled.dataClass(), dataTrackEnabled.dataName(), dataTrackEnabled.instance());
return result;
}
}
I added some more log output, so let us run the application again:
execution(Map com.carrot.personal.app.Application.fetchUser())
Recording: dataClass = Hey, dataName = There, instance = How are you?
{firstName=carrot, lastName=radish}

Program runs fine when I run in VSCode, but the package the maven extension builds doesn't run

Background: I am using VS Code and the VS Code Java extension pack (which includes a maven extension) to write a project that uses htmlunit. Currently, I'm just using someone else's code just to see if I can get my environment working. I am a total noob to maven and VS Code and a semi-noob to Java.
When I run my program using the VS Code run tab, it runs as expected*. When I use the maven package command to build an executable jar, the jar builds fine but when I run it with java -jar ___.jar I get an error:
Exception in thread "main" java.lang.NoClassDefFoundError: com/gargoylesoftware/htmlunit/FailingHttpStatusCodeException
at reliant.Main.main(Main.java:44)
Caused by: java.lang.ClassNotFoundException: com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
... 1 more
Here is the code that uses htmlunit:
package reliant; //I know now this is not how package naming conventions work, but I don't think it causes a problem
import java.io.IOException;
import com.gargoylesoftware.htmlunit.BrowserVersion;
import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException; //the import that seems to be buggy
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.HtmlForm;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
public class RedditClient {
private final WebClient CLIENT = new WebClient(BrowserVersion.CHROME);
private final String username;
private char[] password;
public RedditClient(String username, char[] password) {
this.username = username;
this.password = password;
CLIENT.getCookieManager().setCookiesEnabled(true);
}
public void checkLogin() {
System.out.println(username + ", " + new String(password));
}
public void login() {
String loginURL = "https://www.reddit.com/login";
try {
HtmlPage loginPage = CLIENT.getPage(loginURL);
HtmlForm loginForm = loginPage.getFirstByXPath("//form[#id='login-form']");
//System.out.println(loginPage.getWebResponse().getContentAsString());
loginForm.getInputByName("user").setValueAttribute(username);
loginForm.getInputByName("passwd").setValueAttribute(new String(password));
loginForm.getElementsByTagName("button").get(0).click();
} catch (FailingHttpStatusCodeException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public String getHTML(String url) {
try {
return CLIENT.getPage(url).getWebResponse().getContentAsString();
} catch (FailingHttpStatusCodeException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public void close() {
CLIENT.close();
}
}
And here is line 44 of Main.java:
RedditClient c = new RedditClient(username, password);
This seems to look right to me, I added htmlunit to my maven dependencies. I can show you a screeenshot of the maven dependencies if you want, but I omitted it for now since it's kind of long/hard to read.
Here is the pom file:
<?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>reliant</groupId>
<artifactId>redditlogin</artifactId>
<version>2.0</version>
<name>redditlogin</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>htmlunit</groupId>
<artifactId>htmlunit</artifactId>
<version>1.14</version>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.13.1</version>
</dependency>
<dependency>
<groupId>net.sourceforge.htmlunit</groupId>
<artifactId>htmlunit</artifactId>
<version>2.41.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>reliant.Main</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
<!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.7.1</version>
</plugin>
<plugin>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.0.0</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
The fact that the jar file errors out but running in VS Code proceeds as expected* makes no sense to me whatsoever.
Also, on an unrelated note, why is a "class not found" error a run-time error? Why is maven able to successfully build the package?
*I say "as expected" because there is a bug in the code I'm using, but it is a very-specific runtime error. Basically, the code I'm using was designed to log in to reddit before reddit got the OAuth thing so now the line that tries to get the login form returns null and that breaks the rest of it. The point is, I see no reason for why running in VS Code vs running the jar should have any different outcomes. I don't even know where to begin with something like this.
Thank you for help, please let me know if there is anything I can add to make this question easier to answer.
Ok so turns out this was pretty silly. Basically, the classpath listed my dependencies (because of how I set up the pom) but that doesn't mean that it specified the path to those dependencies. In fact, it didn't specify a path at all, so whenever java tried to run the jar, it looked for the dependencies in the current directory. I know that because when I put the dependencies in the same directory as the jar, it worked. The better solution here would be to modify the pom to add a prefix to the classpath so that the java knows where to find the dependencies. Unfortunately, my dependencies are in different places so that's not really an option for me; instead, I'm going to try to find out how to make a fat/uber jar.

Using object from other module in aspect

I have one aspect with using aspectJ as below:
public aspect TestAspect {
pointcut classicPointcut(PersistenceManagerImpl object) : execution(manager.PersistenceManagerImpl.new(..)) && target(object);
after(PersistenceManagerImpl object) : classicPointcut(object){
System.err.println(object.getClass().getSimpleName());
}
}
this aspect is in module aspect. this module is packaking as jar. PersistenceManagerImpl is in other module but i need use it in module aspect. For dependency management i use maven. But here is of course problem with cyclic reference. Exists some way how can a resolve this problem ?
----------EDIT----------
I get only this error:
java.lang.NoSuchMethodError:TestAspect.ajc$after$TestAspect$1$cc149106(Ljava/lang/Object;)V
When i move my aspect to same module, when is PersistenceManagerImpl i obtain correct solution(of course). But this is not, what i wanted.
Could you put the error result of the compiling code? You could try to put another module as dependency first then later put the dependency on the aspectj maven plugin at weaveDependency in pom.xml as follow:
....
<dependency>
<groupId>com.maventest</groupId>
<artifactId>mytest</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
....
....
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.8</version>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
<showWeaveInfo>true</showWeaveInfo>
<complianceLevel>${maven.compiler.target}</complianceLevel>
<encoding>${project.build.sourceEncoding}</encoding>
<weaveDependencies>
<weaveDependency>
<groupId>com.maventest</groupId>
<artifactId>mytest</artifactId>
</weaveDependency>
</weaveDependencies>
</configuration>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${aspectj.version}</version>
</dependency>
</dependencies>
</plugin>
ps: You could see my question post asking the same thing here
Your aspect seems to be specific for the module in which PersistenceManagerImpl is located, so that module should be a dependency of the aspect module. On the other hand, that module depends on the aspect module because it needs it as an <aspectLibrary> in the AspectJ Maven configuration. Indeed a circular dependency, but an unnecessary one. You have two options:
Move the aspect to the application module which it is specific for because IMO it belongs there if it explicitly uses specific classes from there. Only aspects which implement cross-cutting concerns in a way applicable to multiple modules should be in their own aspect library.
Following the previous thought, you could make your aspect more general, e.g. do something like this:
public aspect TestAspect {
pointcut classicPointcut(Object object) :
execution(*..PersistenceManagerImpl.new(..)) &&
target(object);
after(Object object) : classicPointcut(object){
System.err.println(object.getClass().getSimpleName());
}
}

Java + Spring + Maven - AspectJ implementation instead of SpringAOP

Java + Spring + Maven application:
Can somebody provide me with the link or advise me on a pure AspectJ implementation without proxy-based Spring AOP?
My application is purely Spring + Maven based. I have currently implemented aspects with Spring AOP which is not solving my problems.
If I try to access a private method2() from public method1() within the same class A this is not supported.
I would like to know :
1) How to write an aspectj with pointcut that supports intraclass method calls?
2) How to configure that into my current Spring,maven project with AspectJ load-time weaving?
3) how to configure AspectJ Maven Plugin for compile-time weaving in Tomcat server + eclipse.
#Controller
class A {
public void method1() {
method2("foo");
}
private String method2(String text) {
return text;
}
}
Expected output:
log.entering(method1)
log.entering(method2)
print abc
log.exiting(method2)
log.exiting(method1)
My recommendation for you is to make a dummy project with Spring Roo (so you can see how the Maven pom.xml and Spring applicationContext.xml file looks) and download Spring STS version of Eclipse which has AspectJ setup correctly.
You can use Maven and AspectJ project together by converting Maven project into AspectJ project by Right Click - Configuration - Convert to AspectJ project. From there you can create Aspect class without annotation or Java class of Aspect using annotation.
As for the result that you want, you can use Around method like below:
#Around("execution ( * A.method1(..))")
public void captureMethodOne(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("log.entering(method1)");
joinPoint.proceed();
System.out.println("log.exiting(method1)");
}
Also do not forget to input aspectj and aspectj maven plugin in the pom.xml, such as
<properties>
<maven.compiler.plugin.version>3.5.1</maven.compiler.plugin.version>
</properties>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.7</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.8</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.8</version>
<configuration>
<complianceLevel>1.8</complianceLevel>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven.compiler.plugin.version}</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
Hope it helps.
PS Check AspectJ tutorial first, plenty around in the internet.

Weaving production classes with AspectJ aspects in Maven for tests execution only

I have a project with Maven build and need to add some basic performance tracing to methods. I decided to use AspectJ for this. Main requirement is to weave tracing aspect into production classes but only for unit tests execution phase.
I was able to configure weaving in Maven however after execution of tests same production classes with aspect applied go to packaged war.
The case looks like pretty common nevertheless I wasn't able to find solution for it in web.
You can put your aspects in the test directory and set the weaveMainSourceFolder flag to true in the test-compile configuration
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.4</version>
<configuration>
<showWeaveInfo>true</showWeaveInfo>
<source>1.7</source>
<target>1.7</target>
</configuration>
<executions>
<execution>
<id>test-compile</id>
<configuration>
<weaveMainSourceFolder>true</weaveMainSourceFolder>
</configuration>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${aspectj.version}</version>
</dependency>
</dependencies>
</plugin>
See http://mojo.codehaus.org/aspectj-maven-plugin/test-compile-mojo.html
I solved this with load-time weaving. That way the weaving happens when your unit-tests are run (via a command line argument when running unit-tests), but your aspects are not woven into the published artifacts.
For example, I wanted to fake out the system clock in my unit-tests, but obviously not mess with it in the live code. Here's my aspect class:
#Aspect
public class TweakSystemAspects {
private static long timeOffsetMillis = 0;
public static void advanceTime(int amount, TimeUnit unit) {
timeOffsetMillis += unit.toMillis(amount);
}
#Around("call (long System.currentTimeMillis())")
public long aroundSystemTime(ProceedingJoinPoint joinPoint) throws Throwable {
return ((Long) joinPoint.proceed()) + timeOffsetMillis;
}
}
Obviously, this is used in unit-tests by calling the TweakSystemAspects.advanceTime() mehtod to fake the passage of time in the system. To accomplish the load time weaving, I just had to make an aop.xml file that defined my aspects (and that weaving should happen in all classes):
<aspectj>
<aspects>
<aspect name="com.mypackage.TweakSystemAspects"/>
</aspects>
<weaver options="-nowarn -Xlint:ignore"/>
<!-- During testing this was useful, but I didn't want all that output normally. -->
<!--<weaver options="-verbose -showWeaveInfo"/>-->
</aspectj>
Finally, I made changes in my pom file to declare the AspectJ runtime dependency and tell surefire to do run-time weaving.
<project ...>
...
<properties>
...
<version.aspectj>1.8.10</version.aspectj>
<properties>
<dependencies>
...
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${version.aspectj}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
...
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<!-- For Load Time Weaving of our AspectJ helper code -->
<argLine>-javaagent:${settings.localRepository}/org/aspectj/aspectjweaver/${version.aspectj}/aspectjweaver-${version.aspectj}.jar</argLine>
...
</configuration>
</plugin>
</plugins>
</build>
...
</project>
I would do that in a dedicated module, use the Maven Dependency Plugin to unpack the artifact "under test" during the generate-test-sources phase, then weave the classes and finally run the tests.
Let me try to illustrate what I mean. Let's imagine the following project structure:
.
|-- pom.xml
`-- some-module // this is the module that we want to weave
|-- pom.xml // but only for testing purpose
`-- ...
So my suggestion is to do something like this:
.
|-- pom.xml
|-- some-module
| |-- pom.xml
| `-- ...
`-- test-module // we're going to weave the classes here because we don't want
|-- pom.xml // the tracing aspect to be packaged in the "production" jar
`-- ...
The idea is to have an additional "test-module" where we would unpack the artifact that we want to test so that we can weave its classes without affecting the "real" production jar.
To do so, declare a dependency on the module under test and use dependency:unpack to unpack the classes into target/classes before invoking the AspectJ plugin to weave the "main" classes.
Based on the sample provided in AspectJ compiler Maven Plugin - Usage something like the following should work:
<project>
...
<build>
<plugins>
...
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
...
</plugins>
<build>
...
</project>

Categories

Resources