Using AspectJ annotations without Spring - java

I am fairly new to AOP. I am trying to create annotations in a maven project without Spring using AspectJ. However my the method I am trying to call using the #Aspect is not being called.
This is what my pom looks like :
<?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>test</groupId>
<artifactId>tanvi</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!-- https://mvnrepository.com/artifact/aspectj/aspectjrt -->
<dependency>
<groupId>aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.5.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/aspectj/aspectjweaver -->
<dependency>
<groupId>aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.5.3</version>
</dependency>
<dependency>
<groupId>aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>1.5.3</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>
<executions>
<execution>
<phase>process-sources</phase>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
The annotation looks like this :
#Target({ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
public #interface HasAccess {
Access[] accesses();
String message() default "You are not allowed to perform this operation";
}
I created an annotation processor for my annotation :
#Aspect
public class HasAccessAdvice {
// #Before("execution(* *.*(..)) && #annotation(testAnnotation) ")
#Before("execution(* *.*(..)) && #annotation(hasAccess)")
public void myBeforeLogger(JoinPoint joinPoint, HasAccess hasAccess) {
System.out.println("Okay - we're in the before handler...");
System.out.println("The test annotation value is: " + hasAccess.accesses().toString());
Signature signature = joinPoint.getSignature();
String methodName = signature.getName();
String stuff = signature.toString();
String arguments = Arrays.toString(joinPoint.getArgs());
System.out.println("Write something in the log... We are just about to call method: "
+ methodName + " with arguments " + arguments + "\nand the full toString: "
+ stuff);
}
}
I am calling it in this call :
public class TestMe {
#HasAccess(accesses = {Access.CREATE_PURCHASE})
public void createPurchase(BigDecimal bigDecimal) {
System.out.println("create Purchase called");
}
}
I created an aop.xml file and placed it in the same folder as pom.xml.
<aspectj>
<aspects>
<aspect name="HasAccessAdvice"/>
</aspects>
</aspectj>
When I call the method createPurchase, it runs without the #Before method being called first. Please help me with what I am missing. Most of the documentation/answers I found were Spring aligned. Any pointers to any tutorial or even another way of creating simple annotations without Spring would be greatly appreciated.

First, since you are using aop.xml, I assume you want to do load time weaving. See Load Time Weaving docs and docs on different weaving types.
Second, in your aop.xml file, you define which <aspect> to use, but you also need to define which class files / packages you want to weave:
<aspectj>
<aspects>
<aspect name="HasAccessAdvice"/>
</aspects>
<weaver options="-verbose">
<!-- weave anything -->
<include within="*" />
<!-- weave specific packages only -->
<include within="my.package..*" />
</weaver>
</aspectj>
Either use "*" to run your aspect on any classes or replace my.package with the package of TestMe. Note that double-dot .. includes sub-packages too.
Also note that <aspect name="..."> asks for fully-qualified Aspect-name with package. Did you create HasAccessibleAdvice in default-package? Add your package otherwise.
Third, aop.xml must be readable in META-INF/aop.xml on your classpath.
If you are running your test via CLI (using java -javaagent...), check your classpath settings (-cp).
If you are writing JUnit tests, you can put META-INF/aop.xml in src/test/resources and adjust the <build><plugins>-section of your pom.xml to include the load time weaver like this:
<properties>
<aspectj.version>1.8.9</aspectj.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.9</version>
<configuration>
<argLine>
-javaagent:${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar
</argLine>
</configuration>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectj.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>

Related

Groovy Mocked methods return null when called inside code from a Library

I'm developing a Java project that has several modules. I decided to create a Module just for testing, where the "java" folder is empty and the "test" folder has all the project's unit and integration tests.
I'm having trouble with a test where I mock MyClass1's getText() method. It works fine if I call getText() somewhere within the Groovy test files, but it's returning null when it gets called from within MyClass2's methods. I suspect it has to do with how the Groovy files get compiled to allow method mocking, whereas MyClass1 and MyClass2 are already compiled since they're from a library. Here's somewhat what my code looks like:
Classes from Module1
public final class MyClass1 {
private String text;
public MyClass1() {
text = "Hello there!";
}
public String getText() {
return text;
}
}
public class MyClass2 {
private MyClass1 myObject1;
public MyClass2(MyClass1 myObject1) {
this.myObject1 = myObject1;
}
public String toLowerCase() {
return myObject1.getText().toLowerCase();
}
}
Tests:
def assertTextNotNull(MyClass1 myObject1) {
assert myObject1.getText() != null;
}
def "successful test"() {
given:
MyClass1 myMock1 = GroovyMock(MyClass1);
myMock1.getText() >> "ABC";
assertTextNotNull(myMock1);
}
def "unsuccessful test"() {
given:
MyClass1 myMock1 = GroovyMock(MyClass1); // I need to use GroovyMock here because the actual class I'm mocking is final
MyClass2 myObject2 = new MyClass2(myMock1);
myMock1.getText() >> "ABC";
when:
assertTextNotNull(myMock1);
then:
myObject2.toLowerCase() == "abc"; // Fails here because myObject1.getText() returns an empty String ""
}
My test module's 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">
<parent>
<groupId>myGroupId</groupId>
<artifactId>myParentArtifactId</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>myArtifactId-tests</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<release>17</release>
</configuration>
<dependencies>
</dependencies>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M7</version>
<configuration>
<excludes>
<exclude>**/*IT.*</exclude>
<exclude>**/*IntegrationTest.*</exclude>
</excludes>
<includes>
<include>**/*UT.*</include>
<include>**/*UnitTest.*</include>
<include>**/*UT.groovy</include>
</includes>
</configuration>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>3.0.0-M7</version>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
<configuration>
<excludes>
<exclude>**/*UT.*</exclude>
<exclude>**/*UnitTest.*</exclude>
</excludes>
<includes>
<include>**/*IT.*</include>
<include>**/*IntegrationTest.*</include>
</includes>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<!-- The gmavenplus plugin is used to compile Groovy code. To learn
more about this plugin, visit https://github.com/groovy/GMavenPlus/wiki -->
<groupId>org.codehaus.gmavenplus</groupId>
<artifactId>gmavenplus-plugin</artifactId>
<version>1.13.1</version>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compileTests</goal>
<goal>addSources</goal>
<goal>addTestSources</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
<repositories>
<repository>
<id>papermc</id>
<url>https://repo.papermc.io/repository/maven-public/</url>
</repository>
</repositories>
<dependencies>
<!-- Paper/Waterfall -->
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>1.18.2-R0.1-SNAPSHOT</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.github.waterfallmc</groupId>
<artifactId>waterfall-api</artifactId>
<version>1.18-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<!-- All other modules -->
<dependency>
<groupId>myGroupId</groupId>
<artifactId>myArtifactId-module1</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>test</scope>
</dependency>
<!-- Testing related -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
<!-- Enables mocking of classes without default constructor (together with CGLIB) -->
<dependency>
<groupId>org.objenesis</groupId>
<artifactId>objenesis</artifactId>
<version>3.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-core</artifactId>
<version>2.2-M1-groovy-4.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>3.3.0</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
I've searched far and wide on StackOverflow. It seems like people only have issues with mocked methods returning null when they're using their own code, and not library code. Hence, I found no solution.
What can I do to make sure getText() returns the mocked reply independently from where it's called?
EDIT:
There has been a development. I basically copy-pasted the code I pasted here into a new module and made MyClass1 final just like it is in the library. It didn't work, as expected. By removing the final on the class declaration, the tests suddenly worked. So, the conclusion I can take from this is that Mocking final class's methods only works inside groovy code. Those methods can be called from within library/other module's code but will return a default value (in my case, null. In the posted code's case, an empty String).
You cannot mock final classes for Java with Spock. And GroovyMock only works for code written in (dynamic) Groovy as you suspected.
However, there is spock-mockable, it is an extension, that will use a java agent to transparently remove the final modifier from the configured classes, methods, and fields. Using this extension, you can use the standard Mock method from Spock to mock the now non-final class.
#Mockable(MyClass1)
class ASpec extends Specification {
def "unsuccessful test"() {
given:
MyClass1 myMock1 = Mock() // I need to use GroovyMock here because the actual class I'm mocking is final
MyClass2 myObject2 = new MyClass2(myMock1)
myMock1.getText() >> "ABC"
when:
assertTextNotNull(myMock1)
then:
myObject2.toLowerCase() == "abc" // Fails here because myObject1.getText() returns an empty String ""
}
}

HystrixCommandAspect Resulting into NoSuchMethodError in plain Java application

I am trying to use Hystrix in my Java Application, its a Non spring java application.
Used following Maven Dependencies in POM to enable Hystrix commands :
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-javanica</artifactId>
<version>1.5.8</version>
</dependency>
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-core</artifactId>
<version>1.5.12</version>
</dependency>
<dependency>
<groupId>com.netflix.rxjava</groupId>
<artifactId>rxjava-core</artifactId>
<version>0.20.7</version>
</dependency>
Used following Dependencies to enable AspectJ :
<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.7</version>
</dependency>
Created a aop.xml in META-INF with following configuration :
<aspectj>
<aspects>
<aspect name="com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect" />
</aspects>
<weaver options="-verbose">
<include within="*" />
</weaver>
</aspectj>
Used Hystrix Command in my Service Class :
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Service;
#Component
#Service
public class TestHystrix
#HystrixCommand(commandKey = "testHystrix", threadPoolKey = "testHystrix", commandProperties = {
#HystrixProperty(name = "hystrix.command.testHystrix.execution.isolation.thread.timeoutInMilliseconds", value = "30") }, threadPoolProperties = {
#HystrixProperty(name = "hystrix.threadpool.testHystrix.maximumSize", value = "3") })
public void testHystrix() {
Added following JVM Argument :
-DWeavingMode=compile
But at both Junit testing and application Runtime, its resulting into following error :
java.lang.NoSuchMethodError: com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect.aspectOf()Lcom/netflix/hystrix/contrib/javanica/aop/aspectj/HystrixCommandAspect;
Please help.
Before asking a question, you should first consult the manual of any tool you like to use. I am just quoting form there:
Aspect weaving
Javanica supports two weaving modes: compile and runtime. (...)
CTW. To use CTW mode you need to use specific jar version: hystrix-javanica-ctw-X.Y.Z. This jar is assembled with aspects compiled with using AJC compiler. If you will try to use regular hystrix-javanica-X.Y.Z with CTW then you get NoSuchMethodError aspectOf() at runtime from building with iajc. Also, you need to start your app with using java property: -DWeavingMode=compile. (...)
So maybe you want to switch your library.
BTW, if you use compile-time weaving (CTW), you should not need aop.xml because AspectJ only uses it for load-time weaving (LTW).
I was able to fix the issue by using following AspectJ Plugin configuration along with above maven dependencies :
<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>
<!-- Provide the Source information. -->
<!-- <aspectLibraries>
<aspectLibrary>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-javanica</artifactId>
</aspectLibrary>
</aspectLibraries> -->
<!--Weaving already compiled JAR artifacts -->
<weaveDependencies>
<weaveDependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-javanica</artifactId>
</weaveDependency>
</weaveDependencies>
</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>
This plugin will enable Post Compile weaving, for more details refer a very good article # http://www.baeldung.com/aspectj https://www.mojohaus.org/aspectj-maven-plugin/examples/weaveJars.html
With this plugin, aop.xml and -DWeavingMode=compile are also not required

Spring AOP + AspectJ maven plugin - Internal method call doesn't work

Java + Spring + Maven application.
Unable to make Internal call from annotation based public method.
Prerequisite
Java-Version: 1.7.
Project: AspectProject > Post build it will create jar file.
Client: AspectClient : which has dependency of "AspectProject".
AspectProject
pom.xml
<properties>
<java.version>1.7</java.version>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<springframework.version>4.1.2.RELEASE</springframework.version>
<org.aspectj-version>1.7.0</org.aspectj-version>
</properties>
<dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${springframework.version}</version>
</dependency>
<!-- AspectJ dependencies -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${org.aspectj-version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${org.aspectj-version}</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>src/main/java</sourceDirectory>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<!-- compile for Java 1.7 -->
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.4</version>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${org.aspectj-version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${org.aspectj-version}</version>
</dependency>
</dependencies>
<executions>
<execution>
<phase>process-sources</phase>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
<configuration>
<complianceLevel>${maven.compiler.source}</complianceLevel>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
AspectProvider
#Aspect
public class AspectProvider {
/**
* This is the point cut for all get Method with #TestAnnotation annotation
*/
#Pointcut("execution(* get*()) && #annotation(aTestAnnotation)")
public void getMethodPointcut(TestAnnotation aTestAnnotation) {}
#Around("getMethodPointcut(aTestAnnotation)")
public Object getConfiguration(ProceedingJoinPoint iJoinPoint, TestAnnotation aTestAnnotation) throws Throwable {
return getValueFromISTCompositeConfiguration(iJoinPoint, aTestAnnotation);
}
private Object getValueFromISTCompositeConfiguration(final ProceedingJoinPoint iProceedingJoinPoint, TestAnnotation aTestAnnotation) throws Throwable {
Object aReturnValue = null;
if (aTestAnnotation.value() != null) {
System.out.println("ASPECT: Returning annotation value.");
aReturnValue = aTestAnnotation.value();
} else {
System.out.println("MISSING_GETTER_PROPERTY");
}
if(aReturnValue == null){
aReturnValue = iProceedingJoinPoint.proceed();
}
return aReturnValue;
}
}
Annotation "TestAnnotation"
#Component
#Target({ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface TestAnnotation {
String value();
}
AspectClient
pom.xml
<properties>
<java.version>1.7</java.version>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<aspectProject.version>0.0.1-SNAPSHOT</aspectProject.version>
<spring-framework.version>4.1.2.RELEASE</spring-framework.version>
<org.aspectj-version>1.7.0</org.aspectj-version>
</properties>
<dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<!-- AspectProject dependencies -->
<dependency>
<groupId>com.example.aop</groupId>
<artifactId>AspectProject</artifactId>
<version>${aspectProject.version}</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>src/main/java/</sourceDirectory>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<!-- compile for Java 1.7 -->
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.4</version>
<configuration>
<showWeaveInfo>true</showWeaveInfo>
<aspectLibraries>
<aspectLibrary>
<groupId>com.example.aop</groupId>
<artifactId>AspectProject</artifactId>
</aspectLibrary>
</aspectLibraries>
<complianceLevel>${maven.compiler.source}</complianceLevel>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
<executions>
<execution>
<phase>process-sources</phase>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${org.aspectj-version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${org.aspectj-version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
Service Class
#Component
public class TestService {
private String value;
public void internalCall() {
System.out.println("INTERNAL_CALL :"+ getValue());
}
#TestAnnotation("RETURNED_FROM_ASPECT_CALL")
public String getValue() {
return value;
}
public void setValue(String iValue) {
this.value = iValue;
}
}
Spring context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<!-- Enable AspectJ style of Spring AOP -->
<context:component-scan base-package="com.example.aop.client" />
<aop:aspectj-autoproxy />
<bean name="TestService" class="com.example.aop.client.service.TestService" />
<!-- Configure Aspect Beans, without this Aspects advice wont execute -->
<bean name="aspectProvider" class="com.example.aop.aspect.AspectProvider"/>
</beans>
Main class
public class SpringMain {
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");
TestService aTestService = ctx.getBean("TestService", TestService.class);
System.out.println("EXTERNAL_CALL: "+aTestService.getValue());
aTestService.internalCall();
ctx.close();
}
}
OUTPUT:
ASPECT: Returning annotation value.
EXTERNAL_CALL:RETURNED_FROM_ASPECT_CALL
INTERNAL_CALL: **null**
Expected:
ASPECT: Returning annotation value.
EXTERNAL_CALL: RETURNED_FROM_ASPECT_CALL
INTERNAL_CALL: **RETURNED_FROM_ASPECT_CALL**
Please advice in case if I am missing any entry or required to change configuration.Thanks in advance for your valuable time.
What you do is a bit strange because on the one hand you configure Spring to use (auto) proxy-based Spring AOP, on the other hand you use AspectJ Maven Plugin to use native AspectJ and do compile-time weaving. Please decide which way you want to go:
For Spring AOP you do not need the AspectJ compiler, but then you are stuck with the proxy-based "AOP lite" approach which comes at the cost of internal calls not being intercepted by aspects because they do not go through proxies but through this (the original object).
For full-blown AspectJ you can configure Spring to use LTW (load-time weaving) as described in manual chapter Using AspectJ with Spring applications. Alternatively, you can also use compile-time weaving, but this is not necessary unless you have performance problems during application start-up.

Cannot find parent aspect for concrete aspect

I have trying to test load time weaving in simple hello world normal Servlet based example in wildfly8.2
I have below Aspect code
package com.test.aspects;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import com.test.helloworld.HelloService;
#Aspect
public abstract class FieldAspect {
#Pointcut
public abstract void getField();
#Pointcut
public abstract void setField();
#Around("getField()")
public HelloService getFieldValue() {
System.out.println("In FieldAspect.getFieldValue() - Applying around advice - getting the value (Andy) for field annotated variable");
return new HelloService();
}
#Around("setField()")
public void setFieldValue() {
System.out
.println("In FieldAspect.setFieldValue() - Applying around advice - throw RuntimeException if field annotated variable is set");
throw new RuntimeException();
}
}
The below run time field annotation
package com.test.aspects;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Target({ ElementType.FIELD })
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface Field {
}
Test Servlet:
package com.test.helloworld;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.test.aspects.Field;
#SuppressWarnings("serial")
#WebServlet("/HelloWorld")
public class HelloWorldServlet extends HttpServlet {
static String PAGE_HEADER = "<html><head><title>helloworld</title></head><body>";
static String PAGE_FOOTER = "</body></html>";
#Field
public HelloService helloService;
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html");
PrintWriter writer = resp.getWriter();
writer.println(PAGE_HEADER);
writer.println("<h1>" + helloService.createHelloMessage("World") + "</h1>");
writer.println(PAGE_FOOTER);
writer.close();
}
}
Service class:
package com.test.helloworld;
public class HelloService {
String createHelloMessage(String name) {
return "Hello " + name + "!";
}
}
Aop.xml under web-inf:
<?xml version="1.0" encoding="UTF-8"?>
<aspectj>
<aspects>
<!-- Field annotation example -->
<concrete-aspect name="com.test.aspects.MyFieldAspect"
extends="com.test.aspects.FieldAspect">
<pointcut name="getField" expression="get(#com.test.aspects.Field * *)" />
<pointcut name="setField" expression="set(#com.test.aspects.Field * *)" />
</concrete-aspect>
</aspects>
<weaver options="-verbose -showWeaveInfo" />
</aspectj>
And the POM.xml:
<?xml version="1.0"?>
<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>org.wildfly.quickstarts</groupId>
<artifactId>wildfly-helloworld</artifactId>
<version>8.0.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>WildFly : Helloworld</name>
<description>WildFly : Helloworld</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<version.wildfly.maven.plugin>1.0.2.Final</version.wildfly.maven.plugin>
<version.jboss.spec.javaee.7.0>1.0.0.Final</version.jboss.spec.javaee.7.0>
<!-- other plugin versions -->
<version.compiler.plugin>3.1</version.compiler.plugin>
<version.war.plugin>2.1.1</version.war.plugin>
<!-- maven-compiler-plugin -->
<maven.compiler.target>1.7</maven.compiler.target>
<maven.compiler.source>1.7</maven.compiler.source>
</properties>
<dependencyManagement>
<dependencies>
<!-- Define the version of JBoss' Java EE 7 APIs we want to use -->
<!-- JBoss distributes a complete set of Java EE 7 APIs including
a Bill of Materials (BOM). A BOM specifies the versions of a "stack" (or
a collection) of artifacts. We use this here so that we always get the correct
versions of artifacts. Here we use the jboss-javaee-7.0 stack (you can
read this as the JBoss stack of the Java EE 7 APIs). You can actually
use this stack with any version of WildFly that implements Java EE 7, not
just WildFly 8! -->
<dependency>
<groupId>org.jboss.spec</groupId>
<artifactId>jboss-javaee-7.0</artifactId>
<version>1.0.2</version>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.2</version>
</dependency>
<!-- Import the CDI API, we use provided scope as the API is included in JBoss WildFly -->
<dependency>
<groupId>javax.enterprise</groupId>
<artifactId>cdi-api</artifactId>
<version>1.2</version>
</dependency>
<!-- Import the Common Annotations API (JSR-250), we use provided scope
as the API is included in JBoss WildFly -->
<dependency>
<groupId>org.jboss.spec.javax.annotation</groupId>
<artifactId>jboss-annotations-api_1.2_spec</artifactId>
<version>1.0.0.Final</version>
</dependency>
<!-- Import the Servlet API, we use provided scope as the API is included in JBoss WildFly -->
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.1_spec</artifactId>
<version>1.0.0.Final</version>
</dependency>
</dependencies>
<build>
<!-- Set the name of the war, used as the context root when the app
is deployed -->
<finalName>wildfly-helloworld</finalName>
<pluginManagement>
<plugins>
<!--This plugin's configuration is used to store Eclipse m2e settings
only. It has no influence on the Maven build itself. -->
<plugin>
<groupId>org.eclipse.m2e</groupId>
<artifactId>lifecycle-mapping</artifactId>
<version>1.0.0</version>
<configuration>
<lifecycleMappingMetadata>
<pluginExecutions>
<pluginExecution>
<pluginExecutionFilter>
<groupId>
org.apache.maven.plugins
</groupId>
<artifactId>
maven-compiler-plugin
</artifactId>
<versionRange>
[2.5.1,)
</versionRange>
<goals>
<goal>compile</goal>
</goals>
</pluginExecutionFilter>
<action>
<ignore></ignore>
</action>
</pluginExecution>
<pluginExecution>
<pluginExecutionFilter>
<groupId>
org.codehaus.mojo
</groupId>
<artifactId>
aspectj-maven-plugin
</artifactId>
<versionRange>
[1.7,)
</versionRange>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</pluginExecutionFilter>
<action>
<ignore></ignore>
</action>
</pluginExecution>
</pluginExecutions>
</lifecycleMappingMetadata>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<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>
<artifactId>maven-war-plugin</artifactId>
<version>2.6</version>
<configuration>
<!-- Java EE 7 doesn't require web.xml, Maven needs to catch up! -->
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
<!-- WildFly plugin to deploy war -->
<plugin>
<groupId>org.wildfly.plugins</groupId>
<artifactId>wildfly-maven-plugin</artifactId>
<version>1.0.2.Final</version>
<configuration>
<version>1.0.2.Final</version>
<jvmArgs>${the-whole-jvm-args}</jvmArgs>
</configuration>
</plugin>
<!-- Compiler plugin enforces Java 1.6 compatibility and activates
annotation processors -->
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
Used below link to configure the Load time weaving: http://wiki.eclipse.org/LTWJboss7
So after above if I start wildfly, it shows below error stack trace.
[AppClassLoader#1912a56] info AspectJ Weaver Version 1.8.5 built on Thursday Jan 29, 2015 at 01:03:58 GMT
[AppClassLoader#1912a56] info register classloader sun.misc.Launcher$AppClassLoader#1912a56
[AppClassLoader#1912a56] info using configuration file:/helloworld/target/wildfly-helloworld.war!/WEB-INF/aop.xml
[AppClassLoader#1912a56] info define aspect com.test.aspects.MyFieldAspect
[AppClassLoader#1912a56] error Cannot find parent aspect for: <concrete-aspect name='com.test.aspects.MyFieldAspect' extends='com.test.aspects.FieldAspect' perclause='null'/> in aop.xml
[AppClassLoader#1912a56] error Concrete-aspect 'com.test.aspects.MyFieldAspect' could not be registered
[AppClassLoader#1912a56] warning failure(s) registering aspects. Disabling weaver for class loader sun.misc.Launcher$AppClassLoader#1912a56
I am confused here what has been done wrong. Can someone help me to run my test program. I am learning AspectJ and LTW with wildfly and new to this all.
Thanks,
classpath was wrong.
Place the aspectjWeaver jar in WEB-INF/lib folder.
Also package aop module as create Aspects inside war module , create aspects in different module and package in jar.

AspectJ & Maven warning: "Advice defined in ... has not been applied?"

I'm trying to weave some aspects at compile time into a project that becomes a WAR. The aspects advise classes that are within the same project (though in different packages).
I get the warning:
Advice not applied
My aspect is not being executed. Here's my setup:
annotation FooAnnotation.java:
package a;
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface FooAnnotation {}
aspect FooAdvice.aj:
package a;
public aspect FooAdvice {
private static final Log log = LogFactory.getLog(FooAdvice.class);
Object around() : call( #FooAnnotation * *(..)) {
log.info(String.format("Testing around"));
return proceed();
}
}
I also tried the annotation:
#Around("call( #FooAnnotation * *(..))")
public Object checkFoo( ProceedingJoinPoint joinPoint) throws Throwable {
As far as I can tell, my pointcut specification is correct, but for some reason the ajc compiler isn't playing ball.
Class FooClass.java:
package b;
#ApplicationPath("/service")
#Path("/{param}")
#Produces("application/json")
#Provider
public class FooClass {
#POST
#PUT
#Path("/{context}/{resource}")
#FooAnnotation
public String updateResource(...) {}
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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>zzz.group</groupId>
<artifactId>yyy.artifact</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>yyy.name</name>
<properties>
<endorsed.dir>${project.build.directory}/endorsed</endorsed.dir>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<javaVersion>1.6</javaVersion>
<org.aspectj-version>1.7.2</org.aspectj-version>
</properties>
<dependencies>
<dependency>
... stuff ...
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${org.aspectj-version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.0</version>
<configuration>
<source>${javaVersion}</source>
<target>${javaVersion}</target>
<compilerArguments>
<endorseddirs>${endorsed.dir}</endorseddirs>
</compilerArguments>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.1.1</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<!--
Have to use version 1.2 since version 1.3 does not appear to work
with ITDs
-->
<version>1.4</version>
<dependencies>
<!--
You must use Maven 2.0.9 or above or these are ignored (see
MNG-2972)
-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${org.aspectj-version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${org.aspectj-version}</version>
</dependency>
</dependencies>
<executions>
<execution>
<goals>
<goal>compile</goal>
<!-- <goal>test-compile</goal> -->
</goals>
</execution>
</executions>
<configuration>
<outxml>true</outxml>
<source>${javaVersion}</source>
<target>${javaVersion}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
Aha! Found the answer here:
aspectj: why advice cannot be applied?
It has nothing to do with maven.
The reason is that in my example, the method is being called from within the jax-rs framework by indirect loading. The call() advice wants to weave the caller, but the ajc compiler can't know where the caller is. The fix is to change the advice to execution() thusly:
Object around() : execution(#FooAnnotation * *(..)) {...}
This differs because it weaves around the execution (which ajc can find), rather than the caller.
Done.

Categories