Skip to content

FELIX-6807 - Java 25 LTS support - #433

Open
paulrutter wants to merge 54 commits into
masterfrom
feature/FELIX-6759-Java-25-LTS
Open

paulrutter wants to merge 54 commits into
masterfrom
feature/FELIX-6759-Java-25-LTS

Conversation

@paulrutter

@paulrutter paulrutter commented Jul 14, 2025 •

Copy link
Copy Markdown
Contributor

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

https://issues.apache.org/jira/browse/FELIX-6807

- 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
Copy Markdown
Contributor

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

@stbischof

stbischof commented Jul 14, 2025 •

Copy link
Copy Markdown
Contributor
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
Copy Markdown
Contributor

awaitility/awaitility#296

@paulrutter

Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
Contributor

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

@tjwatson

tjwatson commented Jul 15, 2025 •

Copy link
Copy Markdown
Member

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

nroduit commented Aug 10, 2025

Copy link
Copy Markdown
Contributor

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
Copy Markdown
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

mattrpav commented Oct 1, 2025 •

Copy link
Copy Markdown

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

tjwatson commented Oct 1, 2025

Copy link
Copy Markdown
Member
  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

laeubi commented Oct 1, 2025

Copy link
Copy Markdown

@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

mattrpav commented Oct 1, 2025 •

Copy link
Copy Markdown

@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

laeubi commented Oct 1, 2025

Copy link
Copy Markdown

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

tjwatson commented Oct 1, 2025

Copy link
Copy Markdown
Member

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

mattrpav commented Oct 1, 2025 •

Copy link
Copy Markdown

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

mattrpav commented Oct 1, 2025

Copy link
Copy Markdown

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

tjwatson commented Oct 1, 2025

Copy link
Copy Markdown
Member

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.

@paulrutter
paulrutter marked this pull request as ready for review August 29, 2026 22:41
paulrutter and others added 5 commits August 30, 2026 00:46
main is changed by this branch, its dependency on the framework moves to
8.0.0-SNAPSHOT, and it embeds the framework, yet nothing verified it: there was no
main path filter and no build step, so a break would only have surfaced at release
time.

Building it locally first showed it does not build on a modern JDK at all, for the
same reason as the modules in the split-out build repair change: it declares
felix-parent 6, which does not match the local pom, so Maven resolves the released
parent from Central. That parent brings in ianal-maven-plugin 1.0-alpha-1, which
reflects into java.io and is blocked from Java 16 on:

    Unable to make private java.io.File(java.lang.String,java.io.File) accessible:
    module java.base does not "opens java.io" to unnamed module

Moving to felix-parent 9 removes ianal but pins maven-antrun-plugin 3.1.0, which
rejects the legacy tasks element:

    You are using 'tasks' which has been removed from the maven-antrun-plugin

Both antrun executions now use target instead.

main has no code change, so its version stays at 7.1.0-SNAPSHOT. The step runs after
the framework step, which installs the framework it embeds.

Verified on JDK 25: main builds, the jar embeds the framework, and launching it
starts the framework and keeps running, with no Security Manager error. The only
output is the known sun.misc.Unsafe warning from URLHandlers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…uild output

main bundles the framework, so shipping main 7.1.0 containing framework 8.0.0 would
be misleading. Its version moves to 8.0.0-SNAPSHOT alongside the framework.

main.distribution is unaffected: it pins framework.version to the released 7.0.5 and
consumes published artifacts rather than this build.

Also removes six jars totalling 1.7 MB that were committed by mistake in the previous
commit. main/bundle is populated by the build, is not tracked on master, and was not
ignored, so a plain add picked it up. main/bundle, main/bin and main/conf are now in
.gitignore, all three being build output of this module.

Verified on JDK 25: main builds at 8.0.0-SNAPSHOT and the build no longer leaves
untracked artifacts behind.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
main.distribution copies bundles into its basedir the same way main does, so building
it leaves untracked jars behind in bundle/ and bin/. Both are now ignored.

main.distribution/conf is tracked and is deliberately not ignored.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…modern-jdk

# Conflicts:
#	.github/workflows/maven-ci.yml
@paulrutter

Copy link
Copy Markdown
Contributor Author

@apache/felix-committers please take a look at #433 (comment) and let me know your thoughts on this. I would like to move this JDK 25 thing forward, given it's released for a while now.

I does bring the baseline for framework to JDK 9, but given we can still maintain a JDK 8 branch if required, i would think that's acceptable.

…stances

Util.loadDefaultProperties returned the shared static DEFAULTS instance, and its
callers write per framework values into the result: initializeJPMSEE stores
felix.detect.java.version, and ExtensionManager stores the resolved system package
lists with that version baked into each entry.

So the first framework created in a JVM stamped its own Java version onto every
framework created after it. Two frameworks in one JVM configured with different
java.specification.version values would share the first one's system packages.

This showed up as ExtensionManagerTest.systemBundleHeaders failing on some JDKs and
not others: MultiReleaseVersionTest creates a framework with
java.specification.version 9, and when it ran first the later test saw
java.lang exported as 0.0.0.JavaSE_009 rather than the running JDK's version. Test
order varies between JVMs, which is why it failed on 21 and 23 while passing on 17
and 25.

loadDefaultProperties now returns a copy, so each framework computes its own values.

Verified on JDK 21 and 25: 121 tests with no errors, and systemBundleHeaders passes
when run together with MultiReleaseVersionTest in one JVM.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@mbien

mbien commented Sep 3, 2026

Copy link
Copy Markdown
Member

I does bring the baseline for framework to JDK 9, but given we can still maintain a JDK 8 branch if required, i would think that's acceptable.

JDK 11 as lower bound would be an option too - it will receive updates till next year at least. The libs might be able to use a few new APIs and it might be easier to test, given that running CI against EOL JDK 9 is always a bit awkward.

Projects stuck on EOL JDKs typically don't care about dependency updates, otherwise they wouldn't be there in the first place. So the scenario that someone is running JDK 9 && wants to update felix to the latest version is quite unlikely IMO. (I am no committer)

@paulrutter

Copy link
Copy Markdown
Contributor Author

I does bring the baseline for framework to JDK 9, but given we can still maintain a JDK 8 branch if required, i would think that's acceptable.

JDK 11 as lower bound would be an option too - it will receive updates till next year at least. The libs might be able to use a few new APIs and it might be easier to test, given that running CI against EOL JDK 9 is always a bit awkward.

Projects stuck on EOL JDKs typically don't care about dependency updates, otherwise they wouldn't be there in the first place. So the scenario that someone is running JDK 9 && wants to update felix to the latest version is quite unlikely IMO. (I am no committer)

That's a fair point, but given what i know from other PRs where the baseline was proposed to be lifted, we got some pushback on it. I agree that consumers on older JDK versions can remain to use the 7.x line, while 8.x should lift the baseline.

@mbien

mbien commented Sep 4, 2026

Copy link
Copy Markdown
Member

That's a fair point, but given what i know from other PRs where the baseline was proposed to be lifted, we got some pushback on it.

I think it will depend on how the lower bound changes though. If its in the next major version while the last major version is still maintained - i can't see how this would cause pushback.

My main point was that bumping the lower bound from LTS 8 to EOL 9 (on the next major version) is not much different to bumping it from LTS 8 to LTS 11 - since it isn't 8 anymore in both cases ;)

@paulrutter paulrutter changed the title FELIX-6759 - Java 25 LTS support FELIX-6807 - Java 25 LTS support Sep 4, 2026
paulrutter and others added 6 commits September 4, 2026 08:44
FELIX-6759 Follow-up: Make the remaining module builds work on modern JDKs
FELIX-6759 Follow-up: Replace Thread.stop() with interrupt() in configadmin UpdateThread
…Java-25-LTS

# Conflicts:
#	.github/workflows/maven-ci.yml
#	utils/pom.xml
StackWalker, which replaces SecurityManager.getClassContext(), is the only
thing that forced the baseline above 8, and it arrived in 9. Nothing in the
framework uses anything from 10 or 11, so the choice of floor is free.

9 and 10 are non-LTS and long EOL, so landing on 11 drops no supported JVM
while giving a maintained LTS floor. This is already a major version, so the
baseline move is paid for once either way.

The bundle now requires osgi.ee JavaSE 11 instead of 9.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The pom comment and the framework README still described 9 as the floor and
the declared execution environment.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@paulrutter

Copy link
Copy Markdown
Contributor Author

Agreed, and done — the framework baseline is now 11 rather than 9.

Your framing is the right one: the cost is in leaving 8, and that's paid either way. StackWalker is the only thing forcing the floor above 8, and it arrived in 9, but 9 and 10 are non-LTS and long EOL — so going to 11 drops no supported JVM over 9 while landing on a maintained LTS. Since this is already a major version, there's nothing extra to defend.

The bundle now declares Require-Capability: osgi.ee ... JavaSE 11 instead of 9. Nothing in the framework uses anything from 10 or 11, so it was a one-line change; the pom comment and the framework README are updated to match.

One thing I left alone: adding 11 to the CI matrix. That matrix is the build JDK, not the baseline — framework.tck uses bnd 7.4.0, which needs 17+, so an 11 entry would fail for reasons unrelated to the framework. The floor is already enforced by compiling at --release 11 and by the osgi.ee requirement bnd emits. Verifying it at runtime on 11 would need a job that builds on 17+ and runs the tests under an 11 VM via toolchains, which seems worth doing separately rather than in this PR.

@laeubi

laeubi commented Oct 7, 2026

Copy link
Copy Markdown

by the way in PDE (and likely soon in Equinox as well) we are using multi-release jars to support possibly degraded support for lower JVM versions - might be an option for felix as well. JDT+m2e got recently improved to better support MR development in the IDE as well but feedback is always appreciated.

@paulrutter

paulrutter commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor Author

by the way in PDE (and likely soon in Equinox as well) we are using multi-release jars to support possibly degraded support for lower JVM versions - might be an option for felix as well. JDT+m2e got recently improved to better support MR development in the IDE as well but feedback is always appreciated.

There is quite some discussion on this in this PR, see #433 (comment). What i'm proposing after all of this is that we maintain two branches instead of using MRJ's.
Since there hasn't been a lot happening in framework over the years, i doubt that it will be a high maintenance burden.
master would then keep the "latest and greatest" baseline, while we keep a framework-7.x branch if we really need it.

@JnRouvignac

Copy link
Copy Markdown

Thanks for the update Paul.

I am wondering if there is an associated timeline for having a released version of Apache Felix supporting JDK 25? A very rough non-binding ETA if you prefer.

@paulrutter

paulrutter commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor Author

Thanks for the update Paul.

I am wondering if there is an associated timeline for having a released version of Apache Felix supporting JDK 25? A very rough non-binding ETA if you prefer.

I would love to answer that, but this PR hasn't gotten a lot of traction from the community yet. Ideally i would get some more feedback before we merge it. If that doesn't happen, i will merge this it at some point (let's say end of october this year) and send out a release vote so we can force movement either way.

@laeubi

laeubi commented Oct 7, 2026

Copy link
Copy Markdown

Fully agree! I just wanted to give some feedback here (since the comments are spanning a long time here) that it is at least when using in dedicated focused parts not that bad at all.

UpdateThread.terminate() no longer calls Thread.stop(), which has thrown
UnsupportedOperationException since Java 20, and interrupts the worker instead.
The class is in the non-exported org.apache.felix.cm.impl, so no exported API
changes, but the shutdown behaviour does: what used to throw now interrupts.

The same change moves the module to felix-parent 9 and excludes
ConfigAdminSecurityTest above JDK 17, since it sets org.osgi.framework.security
and so needs -Djava.security.manager=allow, which is itself a fatal startup
error from Java 24 on (JEP 486). That leaves a shipped capability the build can
no longer verify above 17.

Together that is more than a patch, so 1.9.27-SNAPSHOT becomes 1.10.0-SNAPSHOT.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@paulrutter

Copy link
Copy Markdown
Contributor Author

Artifacts that would need releasing

If this PR and #552 both land, these are the modules with production code changes. All versions are bumped in the branch.

artifact released today would release
org.apache.felix.framework 7.0.5 8.0.0
org.apache.felix.main 7.0.5 8.0.0
org.apache.felix.scr 2.2.18 2.3.0
org.apache.felix.webconsole 5.0.18 5.1.0
org.apache.felix.gogo.runtime 1.1.6 1.2.0
org.apache.felix.configadmin 1.9.26 1.10.0

framework and main go major because the execution environment changes — Require-Capability: osgi.ee moves from JavaSE 1.8 to JavaSE 11 — and the OSGi security layer is removed. The rest are minor: code changes, no removed API.

configadmin is minor rather than patch because UpdateThread.terminate() replaces Thread.stop() (which has thrown UnsupportedOperationException since Java 20) with interrupt(), so shutdown behaviour changes; and ConfigAdminSecurityTest is now excluded above JDK 17, since it sets org.osgi.framework.security and so needs -Djava.security.manager=allow, which is itself a fatal startup error from Java 24 on (JEP 486). The changed class is in the non-exported org.apache.felix.cm.impl, so no exported API moves.

org.apache.felix.framework.security is deleted, not released. It is a currently published artifact that would cease to exist — worth calling out explicitly in a release vote.

Changed but deliberately not released

  • framework.tck — tck.bndrun only; not a published artifact
  • http/jetty, http/jetty12 — test-only (AbstractJettyTestSupport)
  • bundlerepository, connect, resolver, log.extension, examples/*, gogo/jline, gogo-parent, remaining http/* — pom-only parent and plugin fixes so the modules build on a current JDK, no code change

On plurl

#552 vendors the plurl sources under org.apache.felix.framework.plurl because plurl is not on Maven Central yet (no tags, no releases upstream). The package is private, so once a release appears it becomes a provided dependency embedded with Private-Package — invisible outside the bundle, and no re-release needed on that account.

Missed when the baseline moved from 9 to 11.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@JnRouvignac

Copy link
Copy Markdown

Thanks for the update Paul.
I am wondering if there is an associated timeline for having a released version of Apache Felix supporting JDK 25? A very rough non-binding ETA if you prefer.

I would love to answer that, but this PR hasn't gotten a lot of traction from the community yet.

I have noticed too.

I cannot give any feedback personnally since I am not using Felix. It's another team which does and they are extremely busy right now. We have some dependencies on them, so I am eager to see them move to JDK 25 so they unblock us 😄 Hence why I am monitoring this PR.

Many thanks for all your efforts !

@paulrutter

Copy link
Copy Markdown
Contributor Author

Thanks for the update Paul.
I am wondering if there is an associated timeline for having a released version of Apache Felix supporting JDK 25? A very rough non-binding ETA if you prefer.

I would love to answer that, but this PR hasn't gotten a lot of traction from the community yet.

I have noticed too.

I cannot give any feedback personnally since I am not using Felix. It's another team which does and they are extremely busy right now. We have some dependencies on them, so I am eager to see them move to JDK 25 so they unblock us 😄 Hence why I am monitoring this PR.

Many thanks for all your efforts !

Btw, we're already running JDK 25 in production with the current Felix framework 7.0.5. As long as you don't use the security manager, you'll only see a few deprecation warnings, but it still runs and works ok. Might be good to keep in mind.

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.

9 participants