0

Each time I run a test from my IDE I get the error

java.lang.IllegalStateException: Missing the '-javaagent' JVM argument. 

If I create a run config and add the jvm arg everything is fine. However, the next time I run a test on the fly that is not a pre-configured run config I get the error again.

I thought maybe I could use @BeforeClass to load the java agent dynamically. e.g.

@BeforeClass
public void loadAgent() {
    String nameOfRunningVM = ManagementFactory.getRuntimeMXBean().getName();
    String pid = nameOfRunningVM.substring(0, nameOfRunningVM.indexOf('@'));
    VirtualMachine vm = VirtualMachine.attach(pid);

    Class klass = JavaAgent.class;
    CodeSource codeSource = klass.getProtectionDomain().getCodeSource();
    String agentJar = codeSource.getLocation().getPath();

    vm.loadAgent(agentJar, "");
    vm.detach();
}

(maybe using a test listener might mean I don't copy and paste the code into every test class)

This works but it doesn't seem quite right.

Is there a way to do this without needing the pid?

Or is there a fundamentally better alternative to doing this?

opticyclic
  • 7,412
  • 12
  • 81
  • 155
  • "Missing the '-javaagent' JVM argument" seems like the tests need to be run with a java agent. What can't you specify -javaagent when running the tests? – Alan Bateman Mar 17 '18 at 12:33
  • I don't have a run config set up for every test. Often I will run the test by right clicking on the test name in code and then it will fail. If I change the default config template to include the javaagent it will affect my other projects. – opticyclic Mar 17 '18 at 15:58

2 Answers2

0

This code is not portable, but for test cases, this might be acceptable. There is no other way to load a Java Agent into an already running HotSpot/OpenJDK environment.

This answer shows a fallback for the case that attaching via PID doesn’t work (as the actual name pattern of the RuntimeMXBean is unspecified). It boils down to setting a special “magic” system property and iterate over all local JVMs, query their system properties to find the one you’ve just defined:

String magic=UUID.randomUUID().toString()+'/'+System.nanoTime();
System.setProperty("magic", magic);
VirtualMachine vm;
for(VirtualMachineDescriptor vd:VirtualMachine.list()) try {
    vm=VirtualMachine.attach(vd);
    if(magic.equals(vm.getSystemProperties().getProperty("magic"))) break;
    vm.detach();
}

but obviously, attaching directly to the desired process is much more efficient.

Starting with Java 9, you don’t need to rely on the unspecified name pattern of the RuntimeMXBean anymore:

VirtualMachine vm = VirtualMachine.attach(Long.toString(ProcessHandle.current().pid()));

On the other hand, self-attach now generally requires you to set the system property jdk.attach.allowAttachSelf on the command line or use an even deeper hack like discussed on this page.

Holger
  • 285,553
  • 42
  • 434
  • 765
0

For JDK8 the EA Agent Loader is a better choice and doesn't require the pid. However, it is discontinued for JDK9.

Kotlin example where my agent happens to be called JavaAgent!

AgentLoader.loadAgentClass(JavaAgent::class.java.name, "")
opticyclic
  • 7,412
  • 12
  • 81
  • 155