Skip to content
Advertisement

How to modify the command line arguments of the Maven test execution phase of a project from my own Maven library?

I’d like to create a Maven library which will be a helper tool for testing so it will only be used during the testing phase, just like Hamcrest, AssertJ etc. This library needs a Java agent to properly function and this Java agent needs to be loaded at startup and not during runtime (so via the command line option -javaagent)?

So let’s say there is a (third-party) application using my Maven library as follows:

 <dependency>
    <groupId>my.org</groupId>
    <artifactId>my.library</artifactId>
    <version>1.0</version>
    <scope>test</scope>
 </dependency>

Inside my library there will be my agent packaged in a jar, but also there will be some other code to help load the agent. So when tests are run in the third-party application, my Java agent from the library should be automatically loaded without any user intervention (so the only necessary step is to add my library as a dependency).

How can I load this agent at load time? Is it possible? If it is not possible with a simple Maven library, will it be possible if I create a Maven plugin instead (so the third party application includes my plugin in it’s pom.xml)?

The reason why I want to load the agent during load-time and not runtime is that after Java 9, -Djdk.attach.allowAttachSelf=true needs to be added to the command line to allow the agent to be loaded during runtime, so why add that argument if I can just add -javaagent

Answer

You cannot. Imagine the security implications if without the user’s knowlege and without any other action than adding your dependency, you would transform her application byte code!

It is one thing to add a dependency, but another one to activate it by calling a method or starting a Java agent. This should be under the control of the client application. It is possible to attach a Java agent during runtime without using -javaagent:, but the client application would have to explicitly do that by calling an initialisation method in your agent provider. If that contract holds between you and your users, i.e. if the user cooperates by calling that hypothetical initialisation method (early enough before the classes you want to modify are loaded), you could do it.

What is the big deal? You want to offer agent functionality to your users. Why can they not start the agent as usual? Is it such an effort to add a parameter to the JVM command line?


Update after the OP also edited the question: For Maven Surefire (unit tests) or its twin Maven Failsafe (integration tests), you want to use the argLine parameter in order to start the target JVM with the Java agent on the command line. For an example, please see my answer here. It is about starting tests with the AspectJ weaving agent on the command line, but the principle is the same. Basically, it is something like:

<properties>
  <myDependency.version>1.2.3</myDependency.version>
</properties>

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-surefire-plugin</artifactId>
  <version>2.9</version>
  <configuration>
    <argLine>
      -javaagent:${settings.localRepository}/my/group-id/my-dependency/${myDependency.version}/my-dependency-${myDependency.version}.jar
    </argLine>
  </configuration>
  <dependencies>
    <dependency>
      <groupId>my.group-id</groupId>
      <artifactId>my-dependency</artifactId>
      <version>${myDependency.version}</version>
    </dependency>
  </dependencies>
</plugin>

You just need to document how to use your testing agent, so all your users down-stream know about it.

If you do not wish to specify the path to your agent semi-manually, you can use Maven Dependency Plugin’s dependency:properties goal in order to generate for all your dependencies automatically, containing path names. But this is not necessary, just icing on the cake.

Advertisement