Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The new ThreadLocalUtil requires the surefire/failsafe JVM's to add --add-opens=java.base/java.lang=ALL-UNNAMED #578

Closed
jamezp opened this issue Jul 2, 2024 · 3 comments
Assignees

Comments

@jamezp
Copy link
Collaborator

jamezp commented Jul 2, 2024

Issue Overview

The ThreadLocalUtil utility introduced in #501 requires the JVM argument --add-opens=java.base/java.lang=ALL-UNNAMED because uses a package-private method ThreadLocal.getMap(). An exception like the following is always logged:

Jul 01, 2024 7:25:57 PM org.jboss.arquillian.core.spi.ThreadLocalUtil cleanThreadLocals
SEVERE: Arquillian failed to cleanup threadlocals - did the Java API change?
java.lang.reflect.InaccessibleObjectException: Unable to make java.lang.ThreadLocal$ThreadLocalMap java.lang.ThreadLocal.getMap(java.lang.Thread) accessible: module java.base does not "opens java.lang" to unnamed module @679d4c1c
	at java.base/java.lang.reflect.AccessibleObject.throwInaccessibleObjectException(AccessibleObject.java:391)
	at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:367)
	at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:315)
	at java.base/java.lang.reflect.Method.checkCanSetAccessible(Method.java:203)
	at java.base/java.lang.reflect.Method.setAccessible(Method.java:197)
	at org.jboss.arquillian.core.spi.ThreadLocalUtil.cleanThreadLocals(ThreadLocalUtil.java:48)
	at org.jboss.arquillian.core.spi.ThreadLocalUtil.forceCleanupThreadLocal(ThreadLocalUtil.java:33)
	at org.jboss.arquillian.core.impl.ManagerImpl.shutdown(ManagerImpl.java:285)
	at org.jboss.arquillian.test.impl.EventTestRunnerAdaptor.shutdown(EventTestRunnerAdaptor.java:163)
	at org.jboss.arquillian.junit5.JUnitJupiterTestClassLifecycleManager.close(JUnitJupiterTestClassLifecycleManager.java:55)
	at org.junit.jupiter.engine.descriptor.AbstractExtensionContext.lambda$static$0(AbstractExtensionContext.java:45)
	at org.junit.platform.engine.support.store.NamespacedHierarchicalStore$EvaluatedValue.close(NamespacedHierarchicalStore.java:333)
	at org.junit.platform.engine.support.store.NamespacedHierarchicalStore$EvaluatedValue.access$800(NamespacedHierarchicalStore.java:317)
	at org.junit.platform.engine.support.store.NamespacedHierarchicalStore.lambda$close$3(NamespacedHierarchicalStore.java:98)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.store.NamespacedHierarchicalStore.lambda$close$4(NamespacedHierarchicalStore.java:98)
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
	at java.base/java.util.stream.SortedOps$RefSortingSink.end(SortedOps.java:395)
	at java.base/java.util.stream.Sink$ChainedReference.end(Sink.java:261)
	at java.base/java.util.stream.Sink$ChainedReference.end(Sink.java:261)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:510)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
	at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151)
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174)
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596)
	at org.junit.platform.engine.support.store.NamespacedHierarchicalStore.close(NamespacedHierarchicalStore.java:98)
	at org.junit.jupiter.engine.descriptor.AbstractExtensionContext.close(AbstractExtensionContext.java:87)
	at org.junit.jupiter.engine.execution.JupiterEngineExecutionContext.close(JupiterEngineExecutionContext.java:53)
	at org.junit.jupiter.engine.descriptor.JupiterEngineDescriptor.cleanUp(JupiterEngineDescriptor.java:70)
	at org.junit.jupiter.engine.descriptor.JupiterEngineDescriptor.cleanUp(JupiterEngineDescriptor.java:31)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$cleanUp$10(NodeTestTask.java:167)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.cleanUp(NodeTestTask.java:167)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:98)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:198)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:169)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:93)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:58)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:141)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:57)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:103)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:85)
	at org.junit.platform.launcher.core.DelegatingLauncher.execute(DelegatingLauncher.java:47)
	at org.apache.maven.surefire.junitplatform.LazyLauncher.execute(LazyLauncher.java:56)
	at org.apache.maven.surefire.junitplatform.JUnitPlatformProvider.execute(JUnitPlatformProvider.java:184)
	at org.apache.maven.surefire.junitplatform.JUnitPlatformProvider.invokeAllTests(JUnitPlatformProvider.java:148)
	at org.apache.maven.surefire.junitplatform.JUnitPlatformProvider.invoke(JUnitPlatformProvider.java:122)
	at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:385)
	at org.apache.maven.surefire.booter.ForkedBooter.execute(ForkedBooter.java:162)
	at org.apache.maven.surefire.booter.ForkedBooter.run(ForkedBooter.java:507)
	at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:495)

In general, this feels fairly fragile as a change could result in this not working in future JVM's.

Expected Behaviour

Users should not have to add the opens to the JVM arguments.

Current Behaviour

Users are required to add --add-opens=java.base/java.lang=ALL-UNNAMED to the JVM which launches the test.

Additional Information
$mvn --version Maven home: /home/jperkins/apps/maven Java version: 21.0.3, vendor: Red Hat, Inc., runtime: /usr/lib/jvm/java-21-openjdk-21.0.3.0.9-1.fc40.x86_64 Default locale: en_US, platform encoding: UTF-8 OS name: "linux", version: "6.9.6-200.fc40.x86_64", arch: "amd64", family: "unix"
@WolfgangHG
Copy link
Contributor

WolfgangHG commented Jul 6, 2024

At the time when I developed the fix, I used only Java 1.8, so I did not notice the problem. Is there a way to handle this?

I think for arquillian-warp, the tests are run inside the server, and WildFly already opens "java.base/java.lang", so the error might not happen. But I don't know whether other supported servers (Glassfish, TomEE) also set this option.

I could at least add a check to "ThreadLocalUtil" that prints a warning only once instead of messing the console with a lot of errors:

Class<?> threadLocalClass = Class.forName("java.lang.ThreadLocal");
Module declaringModule = threadLocalClass.getModule();
Module currentModule = ThreadLocalUtil.class.getModule();
if (declaringModule.isOpen( "java.lang", currentModule) == false) {
  log.log(Level.SEVERE, "ThreadLocal cleanup is not possible, as module java.base/java.lang is not opened");
}

What do you think? Do you have better ideas?

But I don't know whether I can test this with arquillian-extension-warp - the release process seems to be stuck. I would have liked to do a final Java 1.8 release before switching to JakartaEE10 and Java 11, and currently there is nobody capable of doing a release.

@WolfgangHG
Copy link
Contributor

I created another pull request with a different approach. What do you think about this one? Could it work?

@WolfgangHG
Copy link
Contributor

WolfgangHG commented Jul 26, 2024

Could this issue be closed, as my reworked pull request was accepted?

I hope that my pull request does not cause memory leaks "from the other" side in cases where a thread does not perform proper cleanup. But I assume this would have happened with ThreadLocal, too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants