Skip to content

FELIX-6807 - Java 25 LTS support - #433

Merged
paulrutter merged 14 commits into
masterfrom
feature/FELIX-6759-Java-25-LTS
Oct 26, 2026
Merged

paulrutter merged 14 commits into
masterfrom
feature/FELIX-6759-Java-25-LTS

Conversation

@paulrutter

@paulrutter paulrutter commented Jul 14, 2025 •

Copy link
Copy Markdown
Contributor

Implements FELIX-6807 — make the framework run on current JDKs, up to and including Java 25 — and FELIX-6870, which makes the integration tests run against the code being built (merged in from #573).

Overview

JEP 486 permanently disabled the SecurityManager in Java 24: it can no longer be installed, and -Djava.security.manager=allow is a fatal startup error. The OSGi security layer cannot work under that, so this PR removes it and everything that depended on it, then raises the build baselines so every module still compiles on a current JDK.

The framework becomes 8.0.0, a deliberately breaking release. The 7.x line stays as the Java 8 + security-layer option.

81 files changed, 654 insertions(+), 8331 deletions(-) — overwhelmingly deletions.

The bulk of it:

  • framework.security is deleted outright.
  • SecureAction loses ~1,600 lines: every doPrivileged wrapper collapses to a direct call.
  • AccessController / System.getSecurityManager() / doPrivileged usage is removed from the framework, SCR and Web Console.
  • SecurityManagerEx is reduced to a StackWalker-based call-stack lookup.

Affected bundles

Module What changed Version
framework Security layer removed; SecureAction reduced to direct calls; SecurityManagerEx now uses StackWalker; baseline 8 → 11 7.1.0-SNAPSHOT → 8.0.0-SNAPSHOT
framework.security Removed entirely (20 files) — the bundle only exists to implement the security layer was 2.9.0-SNAPSHOT
framework.tck bnd 7.1.0 → 7.4.0; runs against framework 8.0.0-SNAPSHOT —
configadmin UpdateThread.terminate() no longer calls Thread.stop() (throws since Java 20) — it interrupts instead; ConfigAdminSecurityTest excluded above JDK 17 1.9.27-SNAPSHOT → 1.10.0-SNAPSHOT
scr doPrivileged / SecurityManager removed from DependencyManager, RegionConfigurationSupport, AbstractComponentManager, BaseMethod, FieldUtils 2.2.19-SNAPSHOT → 2.3.0-SNAPSHOT
webconsole doPrivileged / SecurityManager removed from AbstractWebConsolePlugin, OsgiManager, ConfigurationSupport 5.0.19-SNAPSHOT → 5.1.0-SNAPSHOT
main felix-parent 9; launches framework 8.0.0-SNAPSHOT 7.1.0-SNAPSHOT → 8.0.0-SNAPSHOT
gogo ThreadUtils no longer consults the SecurityManager; Mockito 5.17.0 → 5.18.0 runtime 1.1.7-SNAPSHOT → 1.2.0-SNAPSHOT
http Mockito 5.17.0 → 5.18.0, Awaitility 4.2.1 → 4.3.0 (base, jetty, jetty12, sslfilter, wrappers) —
resolver, connect, bundlerepository, log.extension felix-parent bumped so they compile on the CI matrix at all (see below) —
examples compiler source/target 1.5 → 8 —
CI matrix [17, 21, 25, 27]; 23 dropped (EOL); producers built before their consumers (FELIX-6870) —

scr, webconsole and gogo.runtime take a minor bump rather than a patch: dropping the security-layer code paths is a behavioural change, not a fix.

Testing against the code being built (FELIX-6870)

Merged in from #573. Several modules pinned released Felix bundles as dependencies of their tests, so pax-exam and the bnd resolver exercised published artifacts instead of the code in this repository — which is how the FilterImpl.WrapperCapability regression fixed in #554 survived on master unnoticed.

Each pin becomes a property that still defaults to the latest release, so every module keeps building standalone. CI builds and installs a producing module whenever it or one of its consumers changed, then overrides the property with the version built here:

Producer Consumers Pin was
framework configadmin, scr, http/itest, gogo itests 7.0.5 / 7.0.1 / 6.0.1
configadmin scr 1.9.0
utils resolver 1.8.0
gogo bundles gogo/jline, itest-shell, itest-jline 1.1.2 / 1.1.4 / 1.1.8

Two cases worth calling out:

  • http/itest still pinned framework 7.0.5 — missed by FELIX-6869, which only covered jetty and jetty12.
  • The gogo itest modules resolved against none of the code in this repository: they take versions from an imported BOM that pins released bundles, and the import was itself stale (gogo.bom:1.0.3-SNAPSHOT against 1.0.7-SNAPSHOT in the reactor).

The version-resolving CI step evaluates the gogo modules inside the gogo reactor, because gogo-parent:7-SNAPSHOT is not published anywhere and a standalone help:evaluate would fail on a clean runner.

Follow-up discovered while releasing: gogo-parent had never been published above version 6, so every gogo module inherited a parent that existed only in the reactor. Releasing gogo.runtime 1.2.0 therefore required releasing gogo-parent 7 first, and the seven gogo modules were repointed from gogo-parent:7-SNAPSHOT to the released 7. That is why gogo-parent 7 appears in the JDK 25 support release alongside the bundles listed above.

framework.security stays pinned at 2.8.3: it is no longer built here, and its only consumer, ConfigAdminSecurityTest, is already excluded. ipojo is left alone, since its removal is proposed separately.

JDK baseline changes

Effective felix.java.version per module, before and after:

Module Before After Why
framework 8 11 Deliberate policy change — the only intentional baseline move in this PR
configadmin 7 8 felix-parent 6 → 9
connect 7 8 felix-parent 5 → 9
log.extension 7 8 felix-parent 5 → 9 (also maven.compiler 1.7 → 8)
resolver 6 8 felix-parent 5 → 9, explicit felix.java.version 6 dropped
bundlerepository (unset, felix-parent 2.1) 8 felix-parent 2.1 → 9
examples 1.5 8 explicit source/target
main 8 8 unchanged (parent bumped only)
scr 8 8 unchanged
webconsole 11 11 unchanged

Only the framework's 8 → 11 is a policy decision. The rest are forced: --release 7 was removed in JDK 20 and --release 6 in JDK 21, so configadmin, connect, log.extension, resolver and bundlerepository could not compile on JDK 21 or 25 at all. CI did not surface this because those steps only run when the module itself changes, and none had changed in a long time.

For the framework specifically, StackWalker requires Java 9, but 9 and 10 are long EOL, so 11 is the first sensible LTS baseline.

Breaking changes

  • The OSGi security layer is gone. framework.security is no longer built or released, and org.osgi.framework.security has no effect.
  • Require-Capability: osgi.ee moves from JavaSE 1.8 to JavaSE 11 for the framework.
  • Anyone who needs the security layer, or Java 8, stays on the 7.x line, which remains maintained.

framework/README.md documents the 7.x / 8.x split so the choice is discoverable from the repository.

@paulrutter paulrutter changed the title FELIX-6759-Java-25-LTS FELIX-6759 - Java 25 LTS support Jul 14, 2025
@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 👍

@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.

@laeubi

laeubi commented Oct 1, 2025

Copy link
Copy Markdown

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

tjwatson commented Oct 1, 2025

Copy link
Copy Markdown
Member

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

tjwatson commented Oct 1, 2025

Copy link
Copy Markdown
Member

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

paulrutter commented Oct 1, 2025 •

Copy link
Copy Markdown
Contributor Author

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

laeubi commented Oct 2, 2025

Copy link
Copy Markdown

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

mattrpav commented Oct 2, 2025 •

Copy link
Copy Markdown

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

ben-manes commented Oct 5, 2025 •

Copy link
Copy Markdown

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.

@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.

@JnRouvignac

Copy link
Copy Markdown

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.

They are using 7.0.5 indeed. Then why do they think Felix doesn't support JDK 25? 🤔
I'll investigate. Thank you for this info!

@mbien

mbien commented Oct 8, 2026

Copy link
Copy Markdown
Member

They are using 7.0.5 indeed. Then why do they think Felix doesn't support JDK 25? 🤔

IMO: "support" is an overloaded term. Some use it for "it appears to be working", others rise the bar a little higher. I personally try to use "support" only when CI of that project tests on those JDK versions (at the bare minimum). E.g NetBeans 32 is using felix atm too (supports running on JDK 21, 25 and 27) but it would fail if something uses certain features (even by accident).

-        java: [17,21,23]
+        java: [17,21,23,25]

speaking of versions: I would consider adding 27 and removing 23 (since its EOL).

@paulrutter

paulrutter commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor Author

Agreed on both counts, and on the definition — "tested in CI" is a much more useful bar than "seems to work", and it is exactly the gap here: 7.0.5 predates the framework having a CI job at all (FELIX-6765 added it for 7.1.0), so nobody could say what it was tested against.

Matrix is now [17,21,25]. 23 dropped as you suggested — its last Temurin build is 23.0.2 from January 2025, superseded by 24 that March.

Applying your own rule, 26 comes out too: 27 superseded it in September 2026, so 26.0.2.1 from August is its final update.

Updated: I first added 27 here, then took it back out. It fails, but in Felix HTTP rather than in anything this PR touches — Aries SpiFly 1.3.7 bundles ASM 9.6, which rejects class file major version 71, so its weaving hook throws and Jetty never starts. Filed as FELIX-6869, details in this comment. 27 can go back in once that is fixed.

The framework itself is fine on 26 and 27 — both green including the OSGi Core R8 TCK, checked on the plurl branch (run), where the path filters build only framework and framework.tck and so never reach the SpiFly problem.

Noting NetBeans 32 runs on 21, 25 and 27 — 27 is the one we cannot cover yet, for the reason above.

@paulrutter

Copy link
Copy Markdown
Contributor Author

Matrix is [17,21,25] — 23 dropped as discussed, and 27 taken back out.

27 does fail, but not in anything this PR touches. Aries SpiFly 1.3.7 bundles ASM 9.6, which rejects class file major version 71, so its weaving hook throws for every class it sees and Jetty never finishes starting:

org.apache.aries.spifly.dynamic.bundle [Events.Framework] ERROR : FrameworkEvent ERROR
java.lang.ClassFormatError: Weaving hook failed.
Caused by: java.lang.IllegalArgumentException: Unsupported class file major version 71

JettySniHostCheckIT then waits 30 seconds for an HttpService that never registers. Not SNI or TLS specific — the test keystore is fine and Jetty 12.1.14 starts normally up to that point.

SpiFly 1.3.8 carries ASM 9.10.1, which does declare V27 = 71, but it is not drop-in: bumping it alone leaves the test container with unresolved bundles, because its Import-Package changed. So it needs real work in the HTTP module rather than a version bump here.

While digging I also found that the Felix HTTP ITs pin org.apache.felix.framework 7.0.5 in http/jetty12/pom.xml, so pax-exam always runs them against the released 2022 framework rather than the one built in the reactor — visible in the IT logs, which attribute warnings to ~/.m2/.../7.0.5/... rather than framework/target/classes. That explains why this was not caught earlier: those ITs are not wired to the rest of the build.

Both are filed as FELIX-6869. Once the first part lands, 27 can go back in the matrix.

For the record, the framework itself is fine on 26 and 27 — both green including the OSGi Core R8 TCK, checked on the plurl branch where the path filters build only framework and framework.tck (run).

@JnRouvignac

Copy link
Copy Markdown

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.

They are using 7.0.5 indeed. Then why do they think Felix doesn't support JDK 25? 🤔 I'll investigate. Thank you for this info!

Indeed, I got things mixed up.
They can run on JDK 25, they just cannot compile with it. So we're good.

Thanks again for your comment and work!

@paulrutter

Copy link
Copy Markdown
Contributor Author

@apache/felix-committers i want to move this forward. I would like to ask all committers on this project for feedback. If there is none, i'll move ahead with merging the end of this month and put out the release vote for all impacted projects outlined in #433 (comment).

@ascertrobw

Copy link
Copy Markdown

@apache/felix-committers i want to move this forward. I would like to ask all committers on this project for feedback. If there is none, i'll move ahead with merging the end of this month and put out the release vote for all impacted projects outlined in #433 (comment).

Unfortunately don't have much useful to add from our side - we're in the camp of those running 7.0.5 under J 17, 21, and 25 but with a fairly old bundle set. Hopefully at some stage we'll get our Jetty, servlet APIs etc up to newer versions but it's tricky to carve out time with them all basically working. Appreciate your efforts on this though for when we can get to the newer versions.

@rombert

rombert commented Oct 23, 2026

Copy link
Copy Markdown
Contributor

@apache/felix-committers i want to move this forward. I would like to ask all committers on this project for feedback. If there is none, i'll move ahead with merging the end of this month and put out the release vote for all impacted projects outlined in #433 (comment).

I am also in the camp of people running on Java 25 + Felix Framework 7.0.5 with no visibile issues. I am however looking forward to this fix since this is a needed refactoring and having it released means I can run some more comprehensive tests with it.

paulrutter and others added 10 commits October 26, 2026 09:39
Java 24 permanently disabled the SecurityManager (JEP 486), so the OSGi
security layer can no longer function. Removes framework.security and the
doPrivileged/AccessController plumbing that existed to support it.
SecurityManagerEx now resolves the call stack with StackWalker.

Co-Authored-By: Claude Opus 5 <[email protected]>
StackWalker needs Java 9, but 9 and 10 are long EOL, so 11 is the first
sensible LTS baseline. Removing the security layer is breaking, hence 8.0.0.
framework/README.md documents the 7.x / 8.x split.

Co-Authored-By: Claude Opus 5 <[email protected]>
…urrent JDK

UpdateThread.terminate() called Thread.stop(), which has thrown
UnsupportedOperationException since Java 20; it now interrupts the worker.
ConfigAdminSecurityTest is excluded above JDK 17: it needs
-Djava.security.manager=allow, a fatal startup error from Java 24 on.

Co-Authored-By: Claude Opus 5 <[email protected]>
…nt JDK

JDK 20 removed --release 7 and JDK 21 removed --release 6, so configadmin,
connect, log.extension, resolver and bundlerepository could not compile on
the CI matrix at all. CI did not surface this because those steps only run
when the module itself changes.

Co-Authored-By: Claude Opus 5 <[email protected]>
Modules across the repository pin released Felix sibling bundles as
dependencies of their tests, so pax-exam and the bnd resolver exercise
released artifacts instead of the code in this repository. Regressions
introduced here can therefore pass CI unnoticed, which is how the
FilterImpl.WrapperCapability bug fixed in FELIX-6869 survived on master.

Every such pin becomes a property that still defaults to the latest
release, so each module keeps building on its own. CI now builds and
installs a producing module whenever it or one of its consumers changed,
and overrides the property with the version built here:

  framework      -> configadmin, scr, http, gogo
  configadmin    -> scr
  utils          -> resolver
  gogo bundles   -> gogo itest-shell, itest-jline, jline

The two gogo itest modules took their versions from an imported BOM that
pins released bundles, so they resolved against none of the code in this
repository; they now carry explicit, overridable versions.

http/itest kept a 7.0.5 framework pin that FELIX-6869 missed.

The step that resolves the versions evaluates the gogo modules inside the
gogo reactor, because gogo-parent is not published anywhere.

framework.security is left pinned: it is no longer built here (removed in
the 8.0.0 framework line, FELIX-6807) and its only consumer,
ConfigAdminSecurityTest, is excluded.

ipojo is left alone, since its removal is already proposed separately.

Co-Authored-By: Claude Opus 5 <[email protected]>
27 was taken out again in d347835 because Aries SpiFly 1.3.7 bundles
ASM 9.6, which rejects class file major version 71, so its weaving hook
threw for every class and Jetty never finished starting. FELIX-6869 since
provisions ASM 9.10.1 alongside the SpiFly bundle, which does know version
71, so the blocker should be gone. The framework and the TCK already passed
on 27 when that was checked separately.

Co-Authored-By: Claude Opus 5 <[email protected]>
@paulrutter
paulrutter force-pushed the feature/FELIX-6759-Java-25-LTS branch from 7a02bfa to 01aab02 Compare October 26, 2026 08:42
paulrutter and others added 4 commits October 26, 2026 09:43
FELIX-6870 - Run the integration tests against the code being built
framework, main and bundlerepository each declared an attach-sources
execution of maven-source-plugin with the jar goal. The Apache parent
declares the same execution id with jar-no-fork in its apache-release
profile, and Maven merges executions sharing an id by taking the union of
their goals, so both ran:

    <id>attach-sources</id>
    <goal>jar</goal>
    <goal>jar-no-fork</goal>

Each attached a sources jar, and the second was rejected:

    Failed to execute goal maven-source-plugin:3.3.0:jar-no-fork
    (attach-sources): We have duplicated artifacts attached.

That only surfaces under -Papache-release, which is why ordinary builds
and CI never hit it, but it fails every release of those three modules.

Use jar-no-fork in all three, as connect already does: the goals then
coincide, the union is a single goal, and sources are attached once
without forking a second lifecycle.

Co-Authored-By: Claude Opus 5 <[email protected]>
The JDK 21 doclet enforces HTML5 and rejects markup that predates it, so
maven-javadoc-plugin:jar failed under -Papache-release and blocked a
release of these two modules:

  configadmin   79 errors in 13 files, e.g. <table border="0" cellspacing="3">
  gogo.runtime  <h1> where the doclet expects a lower heading level

The framework already sets failOnError=false for the same reason. Do the
same here: the javadoc jar is still produced and attached, the markup is
left to be cleaned up separately, and no release is held up by it.

This only affects maven-javadoc-plugin; no other part of either build
changes.

Co-Authored-By: Claude Opus 5 <[email protected]>
Same as the preceding commit, for the last module that still failed under
-Papache-release: RepositoryAdmin.java has an unclosed <ol>, which the
JDK 21 doclet rejects.

bundlerepository is not part of this release, but it would have been
blocked the next time it is released, and no module should be left with a
known release-blocking javadoc failure.

Co-Authored-By: Claude Opus 5 <[email protected]>
@paulrutter
paulrutter merged commit 6b16527 into master Oct 26, 2026
4 checks passed
@paulrutter
paulrutter deleted the feature/FELIX-6759-Java-25-LTS branch October 26, 2026 15:20
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.