Cannot attach BB agent in connection with PowerMock on JDK 8
Hi Rafael.
I found several similar issues, maybe this is related to #813, but I am not sure. I did not want to hijack a long closed issue, asking there.
My situation is:
- I use Spock 2.
- For Spock, I have a global extension which is able to successfully attach my agent Sarek in the extension's
startmethod. - Dependency
org.powermock:powermock-module-junit4-rule-agentand@Rule PowerMockRuleare also in the mix together with Spock and my own BB-based agent. - Maven Surefire (2.2.22, 3.0.0-M5, 3.0.0-M6-SNAPSHOT), all versions show the same behaviour.
Now the problem is that Surefire starts the already attached agent again. The agent tries to call ByteBuddyAgent.install again, and this time I am getting this exception on JDK 8 (JDKs 9-17 work nicely):
org.junit.platform.commons.JUnitException: TestEngine with ID 'spock' failed to discover tests
at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discoverEngineRoot(EngineDiscoveryOrchestrator.java:160)
at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discoverSafely(EngineDiscoveryOrchestrator.java:134)
at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discover(EngineDiscoveryOrchestrator.java:108)
at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discover(EngineDiscoveryOrchestrator.java:80)
at org.junit.platform.launcher.core.DefaultLauncher.discover(DefaultLauncher.java:110)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86)
at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86)
at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:53)
at org.apache.maven.surefire.junitplatform.JUnitPlatformProvider.invokeAllTests(JUnitPlatformProvider.java:150)
at org.apache.maven.surefire.junitplatform.JUnitPlatformProvider.invoke(JUnitPlatformProvider.java:120)
at org.apache.maven.surefire.booter.ForkedBooter.invokeProviderInSameClassLoader(ForkedBooter.java:384)
at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:345)
at org.apache.maven.surefire.booter.ForkedBooter.execute(ForkedBooter.java:126)
at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:418)
Caused by: java.lang.ExceptionInInitializerError
at dev.sarek.spock.SarekSpockExtension.start(SarekSpockExtension.java:15)
at org.spockframework.runtime.GlobalExtensionRegistry.startGlobalExtensions(GlobalExtensionRegistry.java:138)
at org.spockframework.runtime.RunContext.start(RunContext.java:62)
at org.spockframework.runtime.RunContext.get(RunContext.java:167)
at org.spockframework.runtime.SpockEngine.discover(SpockEngine.java:21)
at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discoverEngineRoot(EngineDiscoveryOrchestrator.java:152)
... 13 more
Caused by: java.lang.IllegalStateException: Error during attachment using: net.bytebuddy.agent.ByteBuddyAgent$AttachmentProvider$Compound@31610302
at net.bytebuddy.agent.ByteBuddyAgent.install(ByteBuddyAgent.java:639)
at net.bytebuddy.agent.ByteBuddyAgent.install(ByteBuddyAgent.java:612)
at net.bytebuddy.agent.ByteBuddyAgent.install(ByteBuddyAgent.java:564)
at net.bytebuddy.agent.ByteBuddyAgent.install(ByteBuddyAgent.java:541)
at dev.sarek.attach.AgentAttacher.<clinit>(AgentAttacher.java:19)
... 19 more
Caused by: java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at net.bytebuddy.agent.Attacher.install(Attacher.java:102)
at net.bytebuddy.agent.ByteBuddyAgent.install(ByteBuddyAgent.java:634)
... 23 more
Caused by: java.lang.NullPointerException
at com.sun.tools.attach.VirtualMachine.attach(VirtualMachine.java:182)
... 29 more
I think this used to work before, but it was a while ago since I last opened that playground project. I updated to the lastest Spock 2.1-M2 milestone and the latest BB version, but the behaviour stays the same. If I deactivate org.powermock:powermock-module-junit4-rule-agent and comment PowerMock imports in some tests in order to make everything compile, my Spock extension works as expected.
BTW, I locally built https://github.com/powermock/powermock/pull/1090, which is linked to #813 via https://github.com/powermock/powermock/issues/1034. In my case, it is not helping, but it was worth a shot.
The JVM in versions 8- is a bit particular when it comes to attachment. It is only possible a single time for the entire VM. If there are multiple class loaders running the same attachment, it will fail for the second.
There's not much you can do about it but upgrade, I am afraid. It was only fixed in Java 9.
The JVM in versions 8- is a bit particular when it comes to attachment. It is only possible a single time for the entire VM. If there are multiple class loaders running the same attachment, it will fail for the second.
Yes, I was able to work around that in my proof-of-concept project by switching Sarek from dynamically attaching its transformation agent (there are JUnit 4/5, TestNG and Spock modules for that) to simply putting it on the command line via -javaagent. Then only PowerMock remains as an entity dynamically attaching an agent to the VM. The other way around does not work for some reason, PowerMock is not very flexible in that respect.
There's not much you can do about it but upgrade, I am afraid. It was only fixed in Java 9.
Actually, it is just tedious to create extra Maven modules for different types of tests and multiply that by custom profile settings for JDK 8, 9-15 and 16+, when it comes to --add-opens and optionally prepending pre-transformed JDK classes to the boot classpath -Xbootclasspath/p (JDK 8) vs. --patch-module (JDK 9+) or permission for dynamically attaching an agent via -Djdk.attach.allowAttachSelf=true (JDK 9+). It would just be nice to have everything in one Maven module, but in practice probably people would rather use Sarek instead of PowerMock, if PM's limits are reached. Using them together is not necessary, because they do similar things, only in different ways.