Skip to content

Conversation

@paulrutter
Copy link
Contributor

Try-out building framework and HTTP subprojects against java 25 to see what will break

- Try-out building framework and HTTP subprojects against java 25 to see what will break
@paulrutter paulrutter changed the title FELIX-6759-Java-25-LTS FELIX-6759 - Java 25 LTS support Jul 14, 2025
- Use 25-ea (Early access)
- Update mockito-core to a version that has jdk 25 support via byte-buddy
- Update awaitility
- Disable jetty bundle
- Don't rely on snapshot build for jetty
@stbischof
Copy link
Contributor

can you also do a change in scr to trigger this test

@stbischof
Copy link
Contributor

stbischof commented Jul 14, 2025

2025-07-14T15:00:46.1938964Z [�[1;34mINFO�[m] Results:
2025-07-14T15:00:46.1939060Z [�[1;34mINFO�[m] 
2025-07-14T15:00:46.1939191Z [�[1;31mERROR�[m] �[1;31mErrors: �[m
2025-07-14T15:00:46.1939647Z [�[1;31mERROR�[m] �[1;31m  JakartaSpecificWebsocketIT.testWebSocketConversation » NumberFormat For input string: "25-beta"�[m
2025-07-14T15:00:46.1940070Z [�[1;31mERROR�[m] �[1;31m  JettySpecificWebsocketIT.testWebSocketConversation » NumberFormat For input string: "25-beta"�[m
2025-07-14T15:00:46.1940535Z [�[1;31mERROR�[m] �[1;31m  JettySpecificWebsocketIT.testWebSocketServletHttpService » NumberFormat For input string: "25-beta"�[m
2025-07-14T15:00:46.1940981Z [�[1;31mERROR�[m] �[1;31m  JettySpecificWebsocketIT.testWebSocketServletWhiteboard » NumberFormat For input string: "25-beta"�[m
2025-07-14T15:00:46.1941078Z [�[1;34mINFO�[m] 
2025-07-14T15:00:46.1941405Z [�[1;31mERROR�[m] �[1;31mTests run: 24, Failures: 0, Errors: 4, Skipped: 0�[m
2025-07-14T15:00:46.1941502Z [�[1;34mINFO�[m] 
2025-07-14T15:00:46.1941596Z [�[1;34mINFO�[m] 
2025-07-14T15:00:46.1941942Z [�[1;34mINFO�[m] �[1m--- �[0;32mfailsafe:3.2.2:verify�[m �[1m(verify)�[m @ �[36morg.apache.felix.http.jetty12�[0;1m ---�[m
2025-07-14T15:00:46.1942198Z [�[1;34mINFO�[m] �[1m------------------------------------------------------------------------�[m
2025-07-14T15:00:46.1942329Z [�[1;34mINFO�[m] �[1mReactor Summary:�[m
2025-07-14T15:00:46.1942423Z [�[1;34mINFO�[m] 
2025-07-14T15:00:46.1942724Z [�[1;34mINFO�[m] Apache Felix Http Base 5.1.17-SNAPSHOT ............. �[1;32mSUCCESS�[m [ 11.340 s]
2025-07-14T15:00:46.1943010Z [�[1;34mINFO�[m] Apache Felix Http Bridge 6.0.1-SNAPSHOT ............ �[1;32mSUCCESS�[m [  2.062 s]
2025-07-14T15:00:46.1943310Z [�[1;34mINFO�[m] Apache Felix Http Inventory Printer 1.0.3-SNAPSHOT . �[1;32mSUCCESS�[m [  0.612 s]
2025-07-14T15:00:46.1943613Z [�[1;34mINFO�[m] Apache Felix Http Integration Tests 0.0.3-SNAPSHOT . �[1;32mSUCCESS�[m [07:37 min]
2025-07-14T15:00:46.1943897Z [�[1;34mINFO�[m] Apache Felix Http Wrappers 6.1.1-SNAPSHOT .......... �[1;32mSUCCESS�[m [  2.700 s]
2025-07-14T15:00:46.1944173Z [�[1;34mINFO�[m] Apache Felix Http Jetty 12.x 1.0.37-SNAPSHOT ....... �[1;31mFAILURE�[m [ 35.428 s]
2025-07-14T15:00:46.1944437Z [�[1;34mINFO�[m] Apache Felix Http Proxy 6.0.0-SNAPSHOT ............. �[1;33mSKIPPED�[m
2025-07-14T15:00:46.1944698Z [�[1;34mINFO�[m] Apache Felix Http Samples - Whiteboard 3.0.0-SNAPSHOT �[1;33mSKIPPED�[m
2025-07-14T15:00:46.1944951Z [�[1;34mINFO�[m] Apache Felix Servlet API 6.1.1-SNAPSHOT ............ �[1;33mSKIPPED�[m
2025-07-14T15:00:46.1945274Z [�[1;34mINFO�[m] Apache Felix Http SSL Filter 2.0.3-SNAPSHOT ........ �[1;33mSKIPPED�[m
2025-07-14T15:00:46.1945584Z [�[1;34mINFO�[m] Apache Felix Http Webconsole Plugin 1.2.3-SNAPSHOT . �[1;33mSKIPPED�[m
2025-07-14T15:00:46.1945875Z [�[1;34mINFO�[m] Apache Felix Http Reactor TODO remove this change 7-SNAPSHOT �[1;33mSKIPPED�[m

on detail

46.1910860Z java.lang.NumberFormatException: For input string: "25-beta"
2025-07-14T15:00:46.1911133Z 	at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:67)
2025-07-14T15:00:46.1911388Z 	at java.base/java.lang.Integer.parseInt(Integer.java:565)
2025-07-14T15:00:46.1911536Z 	at java.base/java.lang.Integer.parseInt(Integer.java:662)
2025-07-14T15:00:46.1911820Z 	at org.awaitility.core.JavaVersionDetector.getJavaMajorVersion(JavaVersionDetector.java:21)
2025-07-14T15:00:46.1912213Z 	at org.awaitility.core.LambdaErrorMessageGenerator.getLambdaDetectionClassName(LambdaErrorMessageGenerator.java:86)
2025-07-14T15:00:46.1912538Z 	at org.awaitility.core.LambdaErrorMessageGenerator.isLambdaClass(LambdaErrorMessageGenerator.java:30)
2025-07-14T15:00:46.1912935Z 	at org.awaitility.core.CallableCondition$ConditionEvaluationWrapper.generateDescriptionPrefix(CallableCondition.java:120)
2025-07-14T15:00:46.1913278Z 	at org.awaitility.core.CallableCondition$ConditionEvaluationWrapper.getMatchMessage(CallableCondition.java:110)
2025-07-14T15:00:46.1913584Z 	at org.awaitility.core.CallableCondition$ConditionEvaluationWrapper.eval(CallableCondition.java:101)
2025-07-14T15:00:46.1913833Z 	at org.awaitility.core.ConditionAwaiter$ConditionPoller.call(ConditionAwaiter.java:248)
2025-07-14T15:00:46.1914076Z 	at org.awaitility.core.ConditionAwaiter$ConditionPoller.call(ConditionAwaiter.java:235)
2025-07-14T15:00:46.1914265Z 	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:328)
2025-07-14T15:00:46.1914543Z 	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1090)
2025-07-14T15:00:46.1914800Z 	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:614)
2025-07-14T15:00:46.1914930Z 	at java.base/java.lang.Thread.run(Thread.java:1474)
2025-07-14T15:00:46.1914939Z 
2025-07-14T15:00:46.1915486Z [�[1;31mERROR�[m] org.apache.felix.http.jetty.it.JettySpecificWebsocketIT.testWebSocketServletWhiteboard -- Time elapsed: 0.269 s <<< ERROR!
2025-07-14T15:00:46.1915727Z java.lang.NumberFormatException: For input string: "25-beta"
2025-07-14T15:00:46.1915992Z 	at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:67)
2025-07-14T15:00:46.1916199Z 	at java.base/java.lang.Integer.parseInt(Integer.java:565)
2025-07-14T15:00:46.1916341Z 	at java.base/java.lang.Integer.parseInt(Integer.java:662)
2025-07-14T15:00:46.1916617Z 	at org.awaitility.core.JavaVersionDetector.getJavaMajorVersion(JavaVersionDetector.java:21)
2025-07-14T15:00:46.1917013Z 	at org.awaitility.core.LambdaErrorMessageGenerator.getLambdaDetectionClassName(LambdaErrorMessageGenerator.java:86)
2025-07-14T15:00:46.1917331Z 	at org.awaitility.core.LambdaErrorMessageGenerator.isLambdaClass(LambdaErrorMessageGenerator.java:30)
2025-07-14T15:00:46.1917826Z 	at org.awaitility.core.CallableCondition$ConditionEvaluationWrapper.generateDescriptionPrefix(CallableCondition.java:120)
2025-07-14T15:00:46.1918179Z 	at org.awaitility.core.CallableCondition$ConditionEvaluationWrapper.getMatchMessage(CallableCondition.java:110)
2025-07-14T15:00:46.1918479Z 	at org.awaitility.core.CallableCondition$ConditionEvaluationWrapper.eval(CallableCondition.java:101)
2025-07-14T15:00:46.1918727Z 	at org.awaitility.core.ConditionAwaiter$ConditionPoller.call(ConditionAwaiter.java:248)
2025-07-14T15:00:46.1918976Z 	at org.awaitility.core.ConditionAwaiter$ConditionPoller.call(ConditionAwaiter.java:235)
2025-07-14T15:00:46.1919161Z 	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:328)
2025-07-14T15:00:46.1919429Z 	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1090)
2025-07-14T15:00:46.1919695Z 	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:614)
2025-07-14T15:00:46.1919825Z 	at java.base/java.lang.Thread.run(Thread.java:1474)
2025-07-14T15:00:46.1919831Z 
2025-07-14T15:00:46.1920371Z [�[1;31mERROR�[m] org.apache.felix.http.jetty.it.JettySpecificWebsocketIT.testWebSocketServletHttpService -- Time elapsed: 0.263 s <<< ERROR!
2025-07-14T15:00:46.1920535Z java.lang.NumberFormatException: For input string: "25-beta"
2025-07-14T15:00:46.1920799Z 	at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:67)
2025-07-14T15:00:46.1920940Z 	at java.base/java.lang.Integer.parseInt(Integer.java:565)
2025-07-14T15:00:46.1921084Z 	at java.base/java.lang.Integer.parseInt(Integer.java:662)
2025-07-14T15:00:46.1921463Z 	at org.awaitility.core.JavaVersionDetector.getJavaMajorVersion(JavaVersionDetector.java:21)
2025-07-14T15:00:46.1921853Z 	at org.awaitility.core.LambdaErrorMessageGenerator.getLambdaDetectionClassName(LambdaErrorMessageGenerator.java:86)
2025-07-14T15:00:46.1922174Z 	at org.awaitility.core.LambdaErrorMessageGenerator.isLambdaClass(LambdaErrorMessageGenerator.java:30)
2025-07-14T15:00:46.1922573Z 	at org.awaitility.core.CallableCondition$ConditionEvaluationWrapper.generateDescriptionPrefix(CallableCondition.java:120)
2025-07-14T15:00:46.1922916Z 	at org.awaitility.core.CallableCondition$ConditionEvaluationWrapper.getMatchMessage(CallableCondition.java:110)
2025-07-14T15:00:46.1923208Z 	at org.awaitility.core.CallableCondition$ConditionEvaluationWrapper.eval(CallableCondition.java:101)
2025-07-14T15:00:46.1923457Z 	at org.awaitility.core.ConditionAwaiter$ConditionPoller.call(ConditionAwaiter.java:248)
2025-07-14T15:00:46.1923696Z 	at org.awaitility.core.ConditionAwaiter$ConditionPoller.call(ConditionAwaiter.java:235)
2025-07-14T15:00:46.1923876Z 	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:328)
2025-07-14T15:00:46.1924147Z 	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1090)
2025-07-14T15:00:46.1924402Z 	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:614)
2025-07-14T15:00:46.1924526Z 	at java.base/java.lang.Thread.run(Thread.java:1474)
2025-07-14T15:00:46.1924531Z 

@stbischof
Copy link
Contributor

awaitility/awaitility#296

@paulrutter
Copy link
Contributor Author

can you also do a change in scr to trigger this test

Will do tomorrow 👍

- continue-on-error: true to allow building other modules after a failed one
- Change SCR to trigger CI
@stbischof
Copy link
Contributor

might be interesting to du parallel build for matrix java-version x module

like here in osgi repo
https://github.com/osgi/osgi/blob/0b278b1d315138602cd480072e77bd5a3b07fb10/.github/workflows/cibuild.yml#L182

@paulrutter
Copy link
Contributor Author

Results of the latest run, including SCR:

SCR
Due to failure in framework.

WARNING: A terminally deprecated method in sun.misc.Unsafe has been called
WARNING: sun.misc.Unsafe::staticFieldOffset has been called by org.apache.felix.framework.util.SecureAction (file:/home/runner/.m2/repository/org/apache/felix/org.apache.felix.framework/7.0.1/org.apache.felix.framework-7.0.1.jar)
WARNING: Please consider reporting this to the maintainers of class org.apache.felix.framework.util.SecureAction
WARNING: sun.misc.Unsafe::staticFieldOffset will be removed in a future release

Framework

WARNING: sun.misc.Unsafe::staticFieldOffset has been called by org.apache.felix.framework.util.SecureAction (file:/home/runner/.m2/repository/org/apache/felix/org.apache.felix.framework/7.0.5/org.apache.felix.framework-7.0.5.jar)
WARNING: Please consider reporting this to the maintainers of class org.apache.felix.framework.util.SecureAction
WARNING: sun.misc.Unsafe::staticFieldOffset will be removed in a future release
org.osgi.framework.BundleException: Unable to resolve slf4j.api [13](R 13.0): missing requirement [slf4j.api [13](R 13.0)] osgi.extender; (&(osgi.extender=osgi.serviceloader.processor)(version>=1.0.0)(!(version>=2.0.0))) Unresolved requirements: [[slf4j.api [13](R 13.0)] osgi.extender; (&(osgi.extender=osgi.serviceloader.processor)(version>=1.0.0)(!(version>=2.0.0)))]
	at org.apache.felix.framework.Felix.resolveBundleRevision(Felix.java:4398)
	at org.apache.felix.framework.Felix.startBundle(Felix.java:2308)
	at org.apache.felix.framework.Felix.setActiveStartLevel(Felix.java:1566)
	at org.apache.felix.framework.FrameworkStartLevelImpl.run(FrameworkStartLevelImpl.java:297)
	at java.base/java.lang.Thread.run(Thread.java:1474)

HTTP
Warning:

Mockito is currently self-attaching to enable the inline-mock-maker. This will no longer work in future releases of the JDK. Please add Mockito as an agent to your build as described in Mockito's documentation: https://javadoc.io/doc/org.mockito/mockito-core/latest/org.mockito/org/mockito/Mockito.html#0.3
WARNING: A Java agent has been loaded dynamically (/home/runner/.m2/repository/net/bytebuddy/byte-buddy-agent/1.17.5/byte-buddy-agent-1.17.5.jar)
WARNING: If a serviceability tool is in use, please run with -XX:+EnableDynamicAgentLoading to hide this warning
WARNING: If a serviceability tool is not in use, please run with -Djdk.instrument.traceUsage for more information
WARNING: Dynamic loading of agents will be disallowed by default in a future release

@tjwatson
Copy link
Member

WARNING: sun.misc.Unsafe::staticFieldOffset has been called by org.apache.felix.framework.util.SecureAction (file:/home/runner/.m2/repository/org/apache/felix/org.apache.felix.framework/7.0.5/org.apache.felix.framework-7.0.5.jar)
WARNING: Please consider reporting this to the maintainers of class org.apache.felix.framework.util.SecureAction
WARNING: sun.misc.Unsafe::staticFieldOffset will be removed in a future release

I discussed this at https://www.mail-archive.com/dev%40felix.apache.org/msg57202.html

But I didn't have any luck getting feedback on using a common solution between Equinox and Felix (and maybe others). Therefore I only integrated it into Equinox to get rid of the use of Unsafe for the URL singleton management. If someone from Felix would like to adopt the same strategy as I did in Equinox I think it would be good so the two framework's can live in the same JVM without cratering the URL singletons.

@paulrutter
Copy link
Contributor Author

Thanks @tjwatson, from my point of view we should indeed consider adopting the same approach as you mentioned in osgi/osgi#226 (comment).
Hopefully others can weigh in on this as well, given that Java 25 will be LTS soon-ish.

@stbischof
Copy link
Contributor

common solution between Equinox and Felix .... And springboot would be really best way to solve.

@tjwatson
Copy link
Member

tjwatson commented Jul 15, 2025

common solution between Equinox and Felix .... And springboot would be really best way to solve.

I don't disagree but I have my doubts the SpringBoot URL factories will be open to play well with others. Worth a try if you already have a good contribution relationship with the Spring project to bring up the issue.

But I first suggest we get Felix and Equinox to play well with each other to prove out the approach.

@nroduit
Copy link
Contributor

nroduit commented Aug 10, 2025

In addition to the changes of removing the use of sun.misc.Unsafe, all code in Framework using SecurityManager should be removed, since SecurityManager has been completely disabled from Java 24 onwards.

@stbischof
Copy link
Contributor

I disagree with your comment in Security manager.
There are users that use lower Versions if java and need Security Management.

In all cases where Security Managemer is uses we check Existense before we use that. So no execution in Versions where Security Manager is remived.

@mattrpav
Copy link

mattrpav commented Oct 1, 2025

With the SecurityManager removal, it makes it difficult to support wide JDK LTS version ranges. I suggest making two supported branch streams with JDK LTS version alignment.

There are two issues with trying to support a wide range of JDK versions:

  1. SecurityManager class is not present in JDK 25
  2. JAAS API permanently changes in JDK 25, and is not backward compatible in JDK 17
    a. No Subject.current() present in JDK 11/17 due to the ThreadLocal to ScopedValue conv

The approach we are looking at taking in Apache ActiveMQ and Apache Karaf is to have branches with JDK supported ranges::

branch-a: Supported Java: JDK 17 to 21 (Apache Karaf is able to do JDK 11 to JDK 21)
branch-b: Supported Java: JDK 21 to 25

It does to appear that it is physically possible to mismatch JAAS API across JDK 11-25 or JDK 17-25 b/c of the JAAS API change. One side sets a ThreadLocal and uses doAs() and the newer API uses ScopedValue and runAs() methods on the Subject class.

see: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/javax/security/auth/Subject.html

@tjwatson
Copy link
Member

tjwatson commented Oct 1, 2025

  1. SecurityManager class is not present in JDK 25

Is the class completely gone? I would be surprised because I would have expected loads of class not founds when running Equinox on Java 25, but we don't observe that.

@laeubi
Copy link

laeubi commented Oct 1, 2025

@mattrpav Just wanted to note that JDT has now new support for multi-release jars that maps nicely to what we have in maven.

Multi-Release Jars are a perfect fit for such kind of support such JDK dependent changes, then one only need a SecurityManagerFacade that is a multi-release type where depending on the JVM version either use security manager or make it a no-op on JDK25+

@mattrpav
Copy link

mattrpav commented Oct 1, 2025

@tjwatson I mispoke-- JDK 25 disables the SecurityManager by default and custom SecurityManages cannot be installed.

@laeubi MRJ is a good idea, will check that out. Have you solved for how to do JDK-version-specific unit tests in a single Maven module? If so, I'd love to see a sample configuration.

@laeubi
Copy link

laeubi commented Oct 1, 2025

MRJ is a good idea, will check that out. Have you solved for how to do JDK-version-specific unit tests in a single Maven module? If so, I'd love to see a sample configuration.

Let me know if you need any pointer or support, we would need something similar for equinox on the long run.

Regarding testing the most useful these days is a matrix build what uses different native JVMs... of course one can write MR-Test cases as well its just a bit more setup.

@tjwatson
Copy link
Member

tjwatson commented Oct 1, 2025

Let me know if you need any pointer or support, we would need something similar for equinox on the long run.

I have a distaste for MRJ. If we create a SecurityManagerFacade then I would prefer that to just load the right class depending on the Java version behind that instead of being hidden behind class loader MRJ stuff.

@mattrpav
Copy link

mattrpav commented Oct 1, 2025

Regarding testing the most useful these days is a matrix build what uses different native JVMs... of course one can write MR-Test cases as well its just a bit more setup.

Yeah, the how-to-execute-tests is the issue I'm running into. Creating a MR-jar for compiling and packaging test classes seems straight-forward. The hang-up comes in as far as how to instruct the surefire plugin to execute those tests. ASFAIK, would require two separate Maven profiles (kludgy-- as the surefire configurations would need to be kept in sync b/w the two profiles) for listing include/exclude of class test names based on JDK version. Punting to use separate Maven modules by JDK version seems less than ideal.

@mattrpav
Copy link

mattrpav commented Oct 1, 2025

I have a distaste for MRJ

Anything specific? I'm looking at using a MRJ for activemq-client to use that for the Virtual Thread classes that need JDK 21+.

@tjwatson
Copy link
Member

tjwatson commented Oct 1, 2025

I have a distaste for MRJ

Anything specific? I'm looking at using a MRJ for activemq-client to use that for the Virtual Thread classes that need JDK 21+.

It has been a while since I looked into them seriously. But the first blocker from me was source code debugging and what the source JAR looks like for the release. It was a nightmare to debug. Maybe all that is fixed by now. But it seemed far more simple to just choose the class to load myself in code.

@laeubi
Copy link

laeubi commented Oct 1, 2025

I have a distaste for MRJ. If we create a SecurityManagerFacade then I would prefer that to just load the right class depending on the Java version behind that instead of being hidden behind class loader MRJ stuff.

But this will only work if the API is not gone (or non existent) if you not choose reflection ... what is a nightmare on its own of course.

But the first blocker from me was source code debugging and what the source JAR looks like for the release. It was a nightmare to debug. Maybe all that is fixed by now.

As said MR support in JDT is quite new and there is obviously a lot to improve, but if anything is missing it could be linked here:

so I can only invite everyone to participate there :-)

But it seemed far more simple to just choose the class to load myself in code.

While this might work in this case (see above) this is simply not manageable for any more complex API as it would require you to use reflection all the time, that has no compile time checking, it has no real debugging support or anything so not really something I would call "more simple".

and what the source JAR looks like for the release.

The source jar?!? Do you mean the final build jar or the source code in the project?

@tjwatson
Copy link
Member

tjwatson commented Oct 1, 2025

But this will only work if the API is not gone (or non existent) if you not choose reflection ... what is a nightmare on its own of course.

This is all theoretical since SecurityManager is still there. I would never recommend going to MRJs if the class is still there.

I also would never recommend using a compiler that doesn't understand the release you are targeting where the class still exists that your code wants to use on old Java versions. Even if/when they really remove the class the --release option must continue to know about the removed class for the java version you are targeting and must allow your code to compile against it. If no compiler exists that can do the --release option to the java version you are targeting then it is really finally time you move onto a new Java version.

Since Oracle just extended their Java 8 support way out to 2030 (and other vendors will follow) I don't see that happening any earlier for the new Java versions being released through 2030. I suspect they will continue to support --release 8 for a long time yet.

The source jar?!? Do you mean the final build jar or the source code in the project?

I mean the source jar, typically published to maven central. As in org.eclipse.osgi-3.23.200-sources.jar

Even if that all works out and all IDEs handle it in a useful way. I often am handed an error log and asked to analyze some stack trace with methods and line numbers. At that point it is nice to just know the version I am dealing with to find the code for the class/method/line-number in the stack trace without having to worry about which "version" of the class within the MRJ got loaded.

I am doubtful anyone can convince me that MRJs are a perfect solution for me.

@tjwatson
Copy link
Member

tjwatson commented Oct 1, 2025

Anything specific? I'm looking at using a MRJ for activemq-client to use that for the Virtual Thread classes that need JDK 21+.

Even for that I would use a separate project that holds the Java 21+ code that uses Virftual Threads and then pull the classes into the project that still targets the old Java release. Then have code that knows what class to load based on Java versions. That is what we do for the Virtual threads. We then created an org.osgi.service.condition.Condition ID for the Java version so that we can enable the correct service component based on the Java version.

For example, this component only gets enabled for Java versions < 21:
https://github.com/OpenLiberty/open-liberty/blob/f8638a00c65c9dec748d21253571b2d0acd9fcab/dev/com.ibm.ws.threading/src/com/ibm/ws/threading/internal/VirtualThreadDisallowed.java#L34

And this one gets enabled for Java versions >= 21

https://github.com/OpenLiberty/open-liberty/blob/f8638a00c65c9dec748d21253571b2d0acd9fcab/dev/io.openliberty.threading.virtual.internal/src/io/openliberty/threading/virtual/internal/VirtualThreadOperations.java#L40

The rest of the system gets injected with the right component implementation without having to worry about what Java version is running.

@paulrutter
Copy link
Contributor Author

paulrutter commented Oct 1, 2025

Anything specific? I'm looking at using a MRJ for activemq-client to use that for the Virtual Thread classes that need JDK 21+.

Even for that I would use a separate project that holds the Java 21+ code that uses Virftual Threads and then pull the classes into the project that still targets the old Java release. Then have code that knows what class to load based on Java versions. That is what we do for the Virtual threads. We then created an org.osgi.service.condition.Condition ID for the Java version so that we can enable the correct service component based on the Java version.

For example, this component only gets enabled for Java versions < 21: https://github.com/OpenLiberty/open-liberty/blob/f8638a00c65c9dec748d21253571b2d0acd9fcab/dev/com.ibm.ws.threading/src/com/ibm/ws/threading/internal/VirtualThreadDisallowed.java#L34

And this one gets enabled for Java versions >= 21

https://github.com/OpenLiberty/open-liberty/blob/f8638a00c65c9dec748d21253571b2d0acd9fcab/dev/io.openliberty.threading.virtual.internal/src/io/openliberty/threading/virtual/internal/VirtualThreadOperations.java#L40

The rest of the system gets injected with the right component implementation without having to worry about what Java version is running.

That's an elegant solution to the problem indeed. In Felix, we have resorted to reflection to determine java 21 specific features, see for example

Method newVirtualThreadPerTaskExecutorMethod = null;
. But this is pretty isolated.

To get back to the problem of the SecurityManager in Felix, i feel we've only scratched the surface and are not aligned on the approach to take. The options as i see them:

  1. use reflection or a similar approach outlined above to go about distinguishing java features and thus remain to have one main branch
  2. Maintain two branches, depending on java version support or remain to have one and increase minimum java version to 25 (probably undesired) in next release
  3. Investigate MRJ feasibility
  4. Other ideas?

I don't have a strong preference yet, but i do think we should come up with a plan forward, while also respecting the OSGi spec on the security aspects.

@laeubi
Copy link

laeubi commented Oct 2, 2025

I am doubtful anyone can convince me that MRJs are a perfect solution for me.

I don't want to convince anyone, just mentioning that its maybe worth to consider and most of what you describe more feels like limitations of the tooling that could probably be better enhanced there.

Compared to that your solution seem to add a lot of complexity both on the build time as well as dev time just to emulate a feature the JVM can already offer to you (you then need to make sure to have at least a MR OSGi-Manifest), but again I don't want to convince anyone and there are obviously multiple ways.

The problem of multi-release source jars is discussed here so you maybe want to leave a not if you thing its not feasible (even though you not plan to use it) as likely others might still adopt:

@mattrpav
Copy link

mattrpav commented Oct 2, 2025

I solved for MRJ usage in ActiveMQ for Virtual Thread support without adding additional Maven modules based on JDK version.

  1. Use a MRJ for activemq-client (thread factory types lives here)

  2. In the unit test project, use a Maven profile that activates on JDK version to add an additional test sources folder to the build. The result is that matrix builds using JDK 17 and 21 will pass, and the JDK 21-only test classes will be included in the test run used by JDK 21.

ref: apache/activemq#1504

@ben-manes
Copy link

ben-manes commented Oct 5, 2025

I am trying to get a basic pax-exam unit test to work on jdk 25 and it seems to fail due to Felix not being compatible, if I understand correctly. ops4j/org.ops4j.pax.exam#1131

org.osgi.framework.BundleException: Unable to resolve com.github.ben-manes.caffeine [15](R 15.0): missing requirement [com.github.ben-manes.caffeine [15](R 15.0)] osgi.ee; (osgi.ee=UNKNOWN) Unresolved requirements: [[com.github.ben-manes.caffeine [15](R 15.0)] osgi.ee; (osgi.ee=UNKNOWN)]
	at org.apache.felix.framework.Felix.resolveBundleRevision(Felix.java:4398)
	at org.apache.felix.framework.Felix.startBundle(Felix.java:2308)
	at org.apache.felix.framework.Felix.setActiveStartLevel(Felix.java:1566)
	at org.apache.felix.framework.FrameworkStartLevelImpl.run(FrameworkStartLevelImpl.java:297)
	at java.base/java.lang.Thread.run(Thread.java:1474)

I tried passing in org.osgi.framework.executionenvironment as JavaSE-11 but no luck.


Nevermind! The problem is the BND plugin does not yet support JDK-25 and emits an invalid Require-Capability. Setting it explicitly fixes my unit test.

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

Successfully merging this pull request may close these issues.

7 participants