2

I'm using aspectj to target methods using a third-party annotation. However, I can't guarantee that this annotation will be available on the classpath. Is there a way to target an annotation from an optional dependency?

As an example, I might want to target JUnit 5's @ParameterizedTest annotation. My .aj file would look something like this:

public aspect Example {
  pointcut beforeTest(): @annotation(ParamterizedTest);

  before(): beforeTest() {
    System.out.println("This is a Parameterized Test!");
  }
}

However, if my project is using JUnit 4, or doesn't include the junit-jupiter-params library, then Maven will fail to weave since it can't find the class:

2019-02-04 16:37:37.649 [ERROR] Failed to execute goal org.codehaus.mojo:aspectj-maven-plugin:1.11:test-compile (default) on project ExampleProject: AJC compiler errors:
2019-02-04 16:37:37.650 [ERROR] error at (no source information available)
2019-02-04 16:37:37.656 [ERROR] /jenkins/workspace/exampleProject/src/test/java/com/example/ExampleTest.java:0::0 can't determine annotations of missing type org.junit.jupiter.params.ParameterizedTest
2019-02-04 16:37:37.657 [ERROR] when weaving type com.example.ExampleTest
2019-02-04 16:37:37.657 [ERROR] when weaving classes
2019-02-04 16:37:37.657 [ERROR] when weaving
2019-02-04 16:37:37.658 [ERROR] when batch building BuildConfig[null] #Files=21 AopXmls=#0
2019-02-04 16:37:37.658 [ERROR] [Xlint:cantFindType]

I've tried adding the library to the aspectj-maven-plugin's <dependencies> section like this:

<plugin>
  <groupId>org.codehaus.mojo</groupId>
  <artifactId>aspectj-maven-plugin</artifactId>
  <version>1.11</version>
  <configuration>
    <source>1.8</source>
    <target>1.8</target>
    <complianceLevel>1.8</complianceLevel>
    <aspectLibraries>
      <aspectLibrary>
        <groupId>com.example</groupId>
        <artifactId>example-aspects</artifactId>
      </aspectLibrary>
    </aspectLibraries>
  </configuration>
  <executions>
    <execution>
      <goals>
        <goal>test-compile</goal>
      </goals>
    </execution>
  </executions>
  <dependencies>
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjrt</artifactId>
      <version>1.8.13</version>
    </dependency>
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjtools</artifactId>
      <version>1.8.13</version>
    </dependency>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-params</artifactId>
      <version>5.1.1</version>
    </dependency>
  </dependencies>
</plugin>

... but that makes no difference.

Is there a way to make this work without requiring the addition of the dependency? Pretty much I want the pointcut to work if there is a method annotated with the third-party annotation, if present, and to be ignored otherwise.

(For the purposes of the junit example, which I built out to confirm it works the same as my real problem, my aspect library does declare the dependency on junit-jupiter-params.)

Roddy of the Frozen Peas
  • 14,380
  • 9
  • 49
  • 99

1 Answers1

0

Your aspect uses the class and even though your sample code does not show it, there must be an import for it on top of your aspect. This means that it is definitely a dependency - and not an optional one - for your aspect library. Thus you have to define it as such in your library's Maven POM. Anything else just does not make sense. What is the big deal in defining it anyway? Proper dependency management is what Maven was made for.


Update: Thanks for the clarifying comment.

You could use annotation-based syntax instead of native syntax because it does not require imports. The pointcut would just not match, as another Xlint warning would then show.

If you have the option to compile the other projects with the AspectJ compiler, I also recommend you to look into @DeclareError and @DeclareWarning. It would help you enforce the policy not to use parametrised tests during compilation instead of throwing errors or logging something during runtime. The AspectJ compiler would just not compile the tests using that annotation.

kriegaex
  • 63,017
  • 15
  • 111
  • 202
  • My aspect uses the class. The completely separate project which inherits the aspect library may or may not use the class. What I'm trying to do is to detect the usage of an annotation my employer does not want us using. If the annotation isn't used -- great. If the annotation isn't even included in the project's dependencies, great. I'm using JUnit 5 as an example -- if that aspect library identifying the Junit 5 class were to be used in a separate project that has no JUnit 5 dependency because it's using 4, the aspectJ compiler would fail to compile the project. – Roddy of the Frozen Peas Feb 10 '19 at 03:52