This is an automated email from the ASF dual-hosted git repository.

gnodet pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new f6331388bf41 CAMEL-23112: Lower minimum JDK requirement from 21 to 17 
(#21991)
f6331388bf41 is described below

commit f6331388bf41e015783409843f38869b4be7dcbf
Author: Guillaume Nodet <[email protected]>
AuthorDate: Mon Mar 16 09:16:41 2026 +0100

    CAMEL-23112: Lower minimum JDK requirement from 21 to 17 (#21991)
    
    * Lower minimum JDK requirement from 21 to 17
    
    - Change jdk.version from 21 to 17 in root pom.xml
    - Create JDK 17 fallback for CamelThreadFactory using new Thread()
    - Move JDK 21 CamelThreadFactory (virtual threads) to MRJAR java21/ 
directory
    - Replace List.getLast() (JDK 21) with index-based access in JsonObject
    - Replace String.splitWithDelimiters() (JDK 21) with Pattern/Matcher in 
JsonObject
    - Add @EnabledForJreRange(min=JAVA_21) to ManagedVirtualThreadExecutorTest
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
    
    * Use reflection for JDK 19/20 SSLParameters methods
    
    SSLParameters.getNamedGroups/setNamedGroups (JDK 20) and
    getSignatureSchemes/setSignatureSchemes (JDK 19) are called
    via reflection so the code compiles and runs on JDK 17.
    On JDK 17, these SSL configuration options are silently skipped.
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
    
    * Fix remaining JDK 21+ API usages in camel-support
    
    - Replace Executors.newThreadPerTaskExecutor() (JDK 21) with
      reflection in DefaultThreadPoolFactory VIRTUAL enum
    - Replace Thread.threadId() (JDK 19) with Thread.getId() in
      ExpressionBuilder.threadIdExpression()
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
    
    * Fix PQC named groups method to use reflection for JDK 17 compat
    
    The applyPqcNamedGroupDefaults method added in main uses
    SSLParameters.getNamedGroups() (JDK 20+) directly. Use the
    reflection-based getNamedGroupsFromParams() helper instead.
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
    
    * Fix SSLContextParametersTest to compile with --release 17
    
    Use reflection-based helpers for SSLParameters.getNamedGroups() and
    getSignatureSchemes() (JDK 20/19 APIs) and add @EnabledForJreRange
    annotations to skip these tests on JDK < 21 at runtime.
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
    
    * Fix ManagedVirtualThreadExecutorTest to compile with --release 17
    
    Use reflection to call Executors.newVirtualThreadPerTaskExecutor()
    which is a JDK 21 API not available with --release 17 compilation.
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
    
    * Fix List.addFirst() usage for JDK 17 compatibility
    
    Replace List.addFirst() (JDK 21 SequencedCollection) with
    List.add(0, ...) in SpringAiImageOllamaIT.
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
    
    ---------
    
    Co-authored-by: Claude Opus 4.6 <[email protected]>
---
 .../springai/image/SpringAiImageOllamaIT.java      |   2 +-
 .../support/jsse/BaseSSLContextParameters.java     | 103 +++++++++--
 .../camel/support/jsse/SSLContextParameters.java   |   2 +-
 .../support/jsse/SSLContextParametersTest.java     | 193 ++++++++++++---------
 .../ManagedVirtualThreadExecutorTest.java          |   7 +-
 .../camel/support/DefaultThreadPoolFactory.java    |  15 +-
 .../camel/support/builder/ExpressionBuilder.java   |   2 +-
 .../camel/util/concurrent/CamelThreadFactory.java  |  35 +---
 .../camel/util/concurrent/CamelThreadFactory.java  |   0
 pom.xml                                            |   2 +-
 .../org/apache/camel/util/json/JsonObject.java     |  22 ++-
 11 files changed, 242 insertions(+), 141 deletions(-)

diff --git 
a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-image/src/test/java/org/apache/camel/component/springai/image/SpringAiImageOllamaIT.java
 
b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-image/src/test/java/org/apache/camel/component/springai/image/SpringAiImageOllamaIT.java
index bdbbb6621687..704f05155307 100644
--- 
a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-image/src/test/java/org/apache/camel/component/springai/image/SpringAiImageOllamaIT.java
+++ 
b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-image/src/test/java/org/apache/camel/component/springai/image/SpringAiImageOllamaIT.java
@@ -86,7 +86,7 @@ public class SpringAiImageOllamaIT extends CamelTestSupport {
                 java.util.List.of(MediaType.APPLICATION_JSON, 
MediaType.valueOf("application/x-ndjson")));
 
         RestClient.Builder restClientBuilder = RestClient.builder()
-                .messageConverters(converters -> 
converters.addFirst(ndjsonConverter));
+                .messageConverters(converters -> converters.add(0, 
ndjsonConverter));
 
         OpenAiImageApi imageApi = OpenAiImageApi.builder()
                 .baseUrl(OLLAMA.baseUrl())
diff --git 
a/core/camel-api/src/main/java/org/apache/camel/support/jsse/BaseSSLContextParameters.java
 
b/core/camel-api/src/main/java/org/apache/camel/support/jsse/BaseSSLContextParameters.java
index 0356459cd8f2..2107ace5366b 100644
--- 
a/core/camel-api/src/main/java/org/apache/camel/support/jsse/BaseSSLContextParameters.java
+++ 
b/core/camel-api/src/main/java/org/apache/camel/support/jsse/BaseSSLContextParameters.java
@@ -17,6 +17,7 @@
 package org.apache.camel.support.jsse;
 
 import java.io.IOException;
+import java.lang.reflect.Method;
 import java.net.InetAddress;
 import java.net.ServerSocket;
 import java.net.Socket;
@@ -96,6 +97,72 @@ public abstract class BaseSSLContextParameters extends 
JsseParameters {
     private static final String SSL_SERVER_SOCKET_SIGNATURE_SCHEME_LOG_MSG
             = createSignatureSchemeLogMessage("SSLServerSocket");
 
+    // Reflection handles for JDK 19/20 SSLParameters methods (not available 
on JDK 17)
+    private static final Method GET_NAMED_GROUPS;
+    private static final Method SET_NAMED_GROUPS;
+    private static final Method GET_SIGNATURE_SCHEMES;
+    private static final Method SET_SIGNATURE_SCHEMES;
+
+    static {
+        Method gng = null, sng = null, gss = null, sss = null;
+        try {
+            gng = SSLParameters.class.getMethod("getNamedGroups");
+            sng = SSLParameters.class.getMethod("setNamedGroups", 
String[].class);
+            gss = SSLParameters.class.getMethod("getSignatureSchemes");
+            sss = SSLParameters.class.getMethod("setSignatureSchemes", 
String[].class);
+        } catch (NoSuchMethodException e) {
+            // JDK < 19/20 — these methods don't exist, features will be 
silently skipped
+        }
+        GET_NAMED_GROUPS = gng;
+        SET_NAMED_GROUPS = sng;
+        GET_SIGNATURE_SCHEMES = gss;
+        SET_SIGNATURE_SCHEMES = sss;
+    }
+
+    static String[] getNamedGroupsFromParams(SSLParameters params) {
+        if (GET_NAMED_GROUPS == null) {
+            return null;
+        }
+        try {
+            return (String[]) GET_NAMED_GROUPS.invoke(params);
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    private static void setNamedGroupsOnParams(SSLParameters params, String[] 
namedGroups) {
+        if (SET_NAMED_GROUPS == null) {
+            return;
+        }
+        try {
+            SET_NAMED_GROUPS.invoke(params, (Object) namedGroups);
+        } catch (Exception e) {
+            // ignore
+        }
+    }
+
+    private static String[] getSignatureSchemesFromParams(SSLParameters 
params) {
+        if (GET_SIGNATURE_SCHEMES == null) {
+            return null;
+        }
+        try {
+            return (String[]) GET_SIGNATURE_SCHEMES.invoke(params);
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    private static void setSignatureSchemesOnParams(SSLParameters params, 
String[] signatureSchemes) {
+        if (SET_SIGNATURE_SCHEMES == null) {
+            return;
+        }
+        try {
+            SET_SIGNATURE_SCHEMES.invoke(params, (Object) signatureSchemes);
+        } catch (Exception e) {
+            // ignore
+        }
+    }
+
     /**
      * The optional explicitly configured cipher suites for this configuration.
      */
@@ -536,7 +603,7 @@ public abstract class BaseSSLContextParameters extends 
JsseParameters {
                         filteredSecureSocketProtocols.toArray(new String[0]));
 
                 String[] namedGroups = resolveNamedGroups(
-                        engine.getSSLParameters().getNamedGroups(),
+                        getNamedGroupsFromParams(engine.getSSLParameters()),
                         enabledNamedGroups, enabledNamedGroupsPatterns);
                 if (namedGroups != null) {
                     if (LOG.isDebugEnabled()) {
@@ -544,16 +611,16 @@ public abstract class BaseSSLContextParameters extends 
JsseParameters {
                                 engine,
                                 enabledNamedGroups,
                                 enabledNamedGroupsPatterns,
-                                engine.getSSLParameters().getNamedGroups(),
+                                
getNamedGroupsFromParams(engine.getSSLParameters()),
                                 namedGroups);
                     }
                     SSLParameters params = engine.getSSLParameters();
-                    params.setNamedGroups(namedGroups);
+                    setNamedGroupsOnParams(params, namedGroups);
                     engine.setSSLParameters(params);
                 }
 
                 String[] signatureSchemes = resolveSignatureSchemes(
-                        engine.getSSLParameters().getSignatureSchemes(),
+                        
getSignatureSchemesFromParams(engine.getSSLParameters()),
                         enabledSignatureSchemes, 
enabledSignatureSchemesPatterns);
                 if (signatureSchemes != null) {
                     if (LOG.isDebugEnabled()) {
@@ -561,11 +628,11 @@ public abstract class BaseSSLContextParameters extends 
JsseParameters {
                                 engine,
                                 enabledSignatureSchemes,
                                 enabledSignatureSchemesPatterns,
-                                
engine.getSSLParameters().getSignatureSchemes(),
+                                
getSignatureSchemesFromParams(engine.getSSLParameters()),
                                 signatureSchemes);
                     }
                     SSLParameters params = engine.getSSLParameters();
-                    params.setSignatureSchemes(signatureSchemes);
+                    setSignatureSchemesOnParams(params, signatureSchemes);
                     engine.setSSLParameters(params);
                 }
 
@@ -762,7 +829,7 @@ public abstract class BaseSSLContextParameters extends 
JsseParameters {
                         filteredSecureSocketProtocols.toArray(new String[0]));
 
                 String[] namedGroups = resolveNamedGroups(
-                        socket.getSSLParameters().getNamedGroups(),
+                        getNamedGroupsFromParams(socket.getSSLParameters()),
                         enabledNamedGroups, enabledNamedGroupsPatterns);
                 if (namedGroups != null) {
                     if (LOG.isDebugEnabled()) {
@@ -770,16 +837,16 @@ public abstract class BaseSSLContextParameters extends 
JsseParameters {
                                 socket,
                                 enabledNamedGroups,
                                 enabledNamedGroupsPatterns,
-                                socket.getSSLParameters().getNamedGroups(),
+                                
getNamedGroupsFromParams(socket.getSSLParameters()),
                                 namedGroups);
                     }
                     SSLParameters params = socket.getSSLParameters();
-                    params.setNamedGroups(namedGroups);
+                    setNamedGroupsOnParams(params, namedGroups);
                     socket.setSSLParameters(params);
                 }
 
                 String[] signatureSchemes = resolveSignatureSchemes(
-                        socket.getSSLParameters().getSignatureSchemes(),
+                        
getSignatureSchemesFromParams(socket.getSSLParameters()),
                         enabledSignatureSchemes, 
enabledSignatureSchemesPatterns);
                 if (signatureSchemes != null) {
                     if (LOG.isDebugEnabled()) {
@@ -787,11 +854,11 @@ public abstract class BaseSSLContextParameters extends 
JsseParameters {
                                 socket,
                                 enabledSignatureSchemes,
                                 enabledSignatureSchemesPatterns,
-                                
socket.getSSLParameters().getSignatureSchemes(),
+                                
getSignatureSchemesFromParams(socket.getSSLParameters()),
                                 signatureSchemes);
                     }
                     SSLParameters params = socket.getSSLParameters();
-                    params.setSignatureSchemes(signatureSchemes);
+                    setSignatureSchemesOnParams(params, signatureSchemes);
                     socket.setSSLParameters(params);
                 }
 
@@ -913,7 +980,7 @@ public abstract class BaseSSLContextParameters extends 
JsseParameters {
                         filteredSecureSocketProtocols.toArray(new String[0]));
 
                 String[] namedGroups = resolveNamedGroups(
-                        socket.getSSLParameters().getNamedGroups(),
+                        getNamedGroupsFromParams(socket.getSSLParameters()),
                         enabledNamedGroups, enabledNamedGroupsPatterns);
                 if (namedGroups != null) {
                     if (LOG.isDebugEnabled()) {
@@ -921,16 +988,16 @@ public abstract class BaseSSLContextParameters extends 
JsseParameters {
                                 socket,
                                 enabledNamedGroups,
                                 enabledNamedGroupsPatterns,
-                                socket.getSSLParameters().getNamedGroups(),
+                                
getNamedGroupsFromParams(socket.getSSLParameters()),
                                 namedGroups);
                     }
                     SSLParameters params = socket.getSSLParameters();
-                    params.setNamedGroups(namedGroups);
+                    setNamedGroupsOnParams(params, namedGroups);
                     socket.setSSLParameters(params);
                 }
 
                 String[] signatureSchemes = resolveSignatureSchemes(
-                        socket.getSSLParameters().getSignatureSchemes(),
+                        
getSignatureSchemesFromParams(socket.getSSLParameters()),
                         enabledSignatureSchemes, 
enabledSignatureSchemesPatterns);
                 if (signatureSchemes != null) {
                     if (LOG.isDebugEnabled()) {
@@ -938,11 +1005,11 @@ public abstract class BaseSSLContextParameters extends 
JsseParameters {
                                 socket,
                                 enabledSignatureSchemes,
                                 enabledSignatureSchemesPatterns,
-                                
socket.getSSLParameters().getSignatureSchemes(),
+                                
getSignatureSchemesFromParams(socket.getSSLParameters()),
                                 signatureSchemes);
                     }
                     SSLParameters params = socket.getSSLParameters();
-                    params.setSignatureSchemes(signatureSchemes);
+                    setSignatureSchemesOnParams(params, signatureSchemes);
                     socket.setSSLParameters(params);
                 }
 
diff --git 
a/core/camel-api/src/main/java/org/apache/camel/support/jsse/SSLContextParameters.java
 
b/core/camel-api/src/main/java/org/apache/camel/support/jsse/SSLContextParameters.java
index e44b5f07011d..8a86f15daace 100644
--- 
a/core/camel-api/src/main/java/org/apache/camel/support/jsse/SSLContextParameters.java
+++ 
b/core/camel-api/src/main/java/org/apache/camel/support/jsse/SSLContextParameters.java
@@ -349,7 +349,7 @@ public class SSLContextParameters extends 
BaseSSLContextParameters {
      */
     private boolean applyPqcNamedGroupDefaults(SSLContext context) {
         SSLEngine probeEngine = context.createSSLEngine();
-        String[] availableGroups = 
probeEngine.getSSLParameters().getNamedGroups();
+        String[] availableGroups = 
getNamedGroupsFromParams(probeEngine.getSSLParameters());
 
         if (availableGroups == null) {
             return false;
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/support/jsse/SSLContextParametersTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/support/jsse/SSLContextParametersTest.java
index 558e4dcb853b..077e25d68a72 100644
--- 
a/core/camel-core/src/test/java/org/apache/camel/support/jsse/SSLContextParametersTest.java
+++ 
b/core/camel-core/src/test/java/org/apache/camel/support/jsse/SSLContextParametersTest.java
@@ -16,6 +16,7 @@
  */
 package org.apache.camel.support.jsse;
 
+import java.lang.reflect.Method;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
@@ -25,11 +26,14 @@ import java.util.regex.Pattern;
 
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLParameters;
 import javax.net.ssl.SSLServerSocket;
 import javax.net.ssl.SSLSocket;
 
 import org.apache.camel.CamelContext;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.EnabledForJreRange;
+import org.junit.jupiter.api.condition.JRE;
 import org.junit.jupiter.api.parallel.Isolated;
 
 import static org.junit.jupiter.api.Assertions.*;
@@ -742,11 +746,12 @@ public class SSLContextParametersTest extends 
AbstractJsseParametersTest {
     }
 
     @Test
+    @EnabledForJreRange(min = JRE.JAVA_21)
     public void testNamedGroups() throws Exception {
         SSLContext controlContext = SSLContext.getInstance("TLSv1.3");
         controlContext.init(null, null, null);
         SSLEngine controlEngine = controlContext.createSSLEngine();
-        String[] controlNamedGroups = 
controlEngine.getSSLParameters().getNamedGroups();
+        String[] controlNamedGroups = 
getNamedGroups(controlEngine.getSSLParameters());
 
         // default - no named groups configured
         // When PQC groups are available (JDK 25+), auto-configuration 
reorders named groups
@@ -759,14 +764,14 @@ public class SSLContextParametersTest extends 
AbstractJsseParametersTest {
 
         if (Arrays.asList(controlNamedGroups).contains("X25519MLKEM768")) {
             // PQC auto-configuration reorders groups with X25519MLKEM768 first
-            assertEquals("X25519MLKEM768", 
engine.getSSLParameters().getNamedGroups()[0]);
-            assertEquals("X25519MLKEM768", 
socket.getSSLParameters().getNamedGroups()[0]);
-            assertEquals("X25519MLKEM768", 
serverSocket.getSSLParameters().getNamedGroups()[0]);
+            assertEquals("X25519MLKEM768", 
getNamedGroups(engine.getSSLParameters())[0]);
+            assertEquals("X25519MLKEM768", 
getNamedGroups(socket.getSSLParameters())[0]);
+            assertEquals("X25519MLKEM768", 
getNamedGroups(serverSocket.getSSLParameters())[0]);
         } else {
             // No PQC available, should keep JVM defaults
-            assertArrayEquals(controlNamedGroups, 
engine.getSSLParameters().getNamedGroups());
-            assertArrayEquals(controlNamedGroups, 
socket.getSSLParameters().getNamedGroups());
-            assertArrayEquals(controlNamedGroups, 
serverSocket.getSSLParameters().getNamedGroups());
+            assertArrayEquals(controlNamedGroups, 
getNamedGroups(engine.getSSLParameters()));
+            assertArrayEquals(controlNamedGroups, 
getNamedGroups(socket.getSSLParameters()));
+            assertArrayEquals(controlNamedGroups, 
getNamedGroups(serverSocket.getSSLParameters()));
         }
 
         // empty ngp - sets empty list
@@ -777,9 +782,9 @@ public class SSLContextParametersTest extends 
AbstractJsseParametersTest {
         socket = (SSLSocket) context.getSocketFactory().createSocket();
         serverSocket = (SSLServerSocket) 
context.getServerSocketFactory().createServerSocket();
 
-        assertEquals(0, engine.getSSLParameters().getNamedGroups().length);
-        assertEquals(0, socket.getSSLParameters().getNamedGroups().length);
-        assertEquals(0, 
serverSocket.getSSLParameters().getNamedGroups().length);
+        assertEquals(0, getNamedGroups(engine.getSSLParameters()).length);
+        assertEquals(0, getNamedGroups(socket.getSSLParameters()).length);
+        assertEquals(0, 
getNamedGroups(serverSocket.getSSLParameters()).length);
 
         // explicit named group
         ngp.setNamedGroup(Collections.singletonList(controlNamedGroups[0]));
@@ -788,12 +793,12 @@ public class SSLContextParametersTest extends 
AbstractJsseParametersTest {
         socket = (SSLSocket) context.getSocketFactory().createSocket();
         serverSocket = (SSLServerSocket) 
context.getServerSocketFactory().createServerSocket();
 
-        assertEquals(1, engine.getSSLParameters().getNamedGroups().length);
-        assertEquals(controlNamedGroups[0], 
engine.getSSLParameters().getNamedGroups()[0]);
-        assertEquals(1, socket.getSSLParameters().getNamedGroups().length);
-        assertEquals(controlNamedGroups[0], 
socket.getSSLParameters().getNamedGroups()[0]);
-        assertEquals(1, 
serverSocket.getSSLParameters().getNamedGroups().length);
-        assertEquals(controlNamedGroups[0], 
serverSocket.getSSLParameters().getNamedGroups()[0]);
+        assertEquals(1, getNamedGroups(engine.getSSLParameters()).length);
+        assertEquals(controlNamedGroups[0], 
getNamedGroups(engine.getSSLParameters())[0]);
+        assertEquals(1, getNamedGroups(socket.getSSLParameters()).length);
+        assertEquals(controlNamedGroups[0], 
getNamedGroups(socket.getSSLParameters())[0]);
+        assertEquals(1, 
getNamedGroups(serverSocket.getSSLParameters()).length);
+        assertEquals(controlNamedGroups[0], 
getNamedGroups(serverSocket.getSSLParameters())[0]);
 
         // explicit named groups override filter
         FilterParameters filter = new FilterParameters();
@@ -804,20 +809,21 @@ public class SSLContextParametersTest extends 
AbstractJsseParametersTest {
         socket = (SSLSocket) context.getSocketFactory().createSocket();
         serverSocket = (SSLServerSocket) 
context.getServerSocketFactory().createServerSocket();
 
-        assertEquals(1, engine.getSSLParameters().getNamedGroups().length);
-        assertEquals(controlNamedGroups[0], 
engine.getSSLParameters().getNamedGroups()[0]);
-        assertEquals(1, socket.getSSLParameters().getNamedGroups().length);
-        assertEquals(controlNamedGroups[0], 
socket.getSSLParameters().getNamedGroups()[0]);
-        assertEquals(1, 
serverSocket.getSSLParameters().getNamedGroups().length);
-        assertEquals(controlNamedGroups[0], 
serverSocket.getSSLParameters().getNamedGroups()[0]);
+        assertEquals(1, getNamedGroups(engine.getSSLParameters()).length);
+        assertEquals(controlNamedGroups[0], 
getNamedGroups(engine.getSSLParameters())[0]);
+        assertEquals(1, getNamedGroups(socket.getSSLParameters()).length);
+        assertEquals(controlNamedGroups[0], 
getNamedGroups(socket.getSSLParameters())[0]);
+        assertEquals(1, 
getNamedGroups(serverSocket.getSSLParameters()).length);
+        assertEquals(controlNamedGroups[0], 
getNamedGroups(serverSocket.getSSLParameters())[0]);
     }
 
     @Test
+    @EnabledForJreRange(min = JRE.JAVA_21)
     public void testNamedGroupsFilter() throws Exception {
         SSLContext controlContext = SSLContext.getInstance("TLSv1.3");
         controlContext.init(null, null, null);
         SSLEngine controlEngine = controlContext.createSSLEngine();
-        String[] controlNamedGroups = 
controlEngine.getSSLParameters().getNamedGroups();
+        String[] controlNamedGroups = 
getNamedGroups(controlEngine.getSSLParameters());
 
         // default - no filter, keeps defaults (or PQC-reordered defaults on 
JDK 25+)
         SSLContextParameters scp = new SSLContextParameters();
@@ -828,13 +834,13 @@ public class SSLContextParametersTest extends 
AbstractJsseParametersTest {
         SSLServerSocket serverSocket = (SSLServerSocket) 
context.getServerSocketFactory().createServerSocket();
 
         if (Arrays.asList(controlNamedGroups).contains("X25519MLKEM768")) {
-            assertEquals("X25519MLKEM768", 
engine.getSSLParameters().getNamedGroups()[0]);
-            assertEquals("X25519MLKEM768", 
socket.getSSLParameters().getNamedGroups()[0]);
-            assertEquals("X25519MLKEM768", 
serverSocket.getSSLParameters().getNamedGroups()[0]);
+            assertEquals("X25519MLKEM768", 
getNamedGroups(engine.getSSLParameters())[0]);
+            assertEquals("X25519MLKEM768", 
getNamedGroups(socket.getSSLParameters())[0]);
+            assertEquals("X25519MLKEM768", 
getNamedGroups(serverSocket.getSSLParameters())[0]);
         } else {
-            assertArrayEquals(controlNamedGroups, 
engine.getSSLParameters().getNamedGroups());
-            assertArrayEquals(controlNamedGroups, 
socket.getSSLParameters().getNamedGroups());
-            assertArrayEquals(controlNamedGroups, 
serverSocket.getSSLParameters().getNamedGroups());
+            assertArrayEquals(controlNamedGroups, 
getNamedGroups(engine.getSSLParameters()));
+            assertArrayEquals(controlNamedGroups, 
getNamedGroups(socket.getSSLParameters()));
+            assertArrayEquals(controlNamedGroups, 
getNamedGroups(serverSocket.getSSLParameters()));
         }
 
         // empty filter - no includes means no groups match
@@ -845,9 +851,9 @@ public class SSLContextParametersTest extends 
AbstractJsseParametersTest {
         socket = (SSLSocket) context.getSocketFactory().createSocket();
         serverSocket = (SSLServerSocket) 
context.getServerSocketFactory().createServerSocket();
 
-        assertEquals(0, engine.getSSLParameters().getNamedGroups().length);
-        assertEquals(0, socket.getSSLParameters().getNamedGroups().length);
-        assertEquals(0, 
serverSocket.getSSLParameters().getNamedGroups().length);
+        assertEquals(0, getNamedGroups(engine.getSSLParameters()).length);
+        assertEquals(0, getNamedGroups(socket.getSSLParameters()).length);
+        assertEquals(0, 
getNamedGroups(serverSocket.getSSLParameters()).length);
 
         // include all
         filter.getInclude().add(".*");
@@ -856,9 +862,9 @@ public class SSLContextParametersTest extends 
AbstractJsseParametersTest {
         socket = (SSLSocket) context.getSocketFactory().createSocket();
         serverSocket = (SSLServerSocket) 
context.getServerSocketFactory().createServerSocket();
 
-        assertArrayEquals(controlNamedGroups, 
engine.getSSLParameters().getNamedGroups());
-        assertArrayEquals(controlNamedGroups, 
socket.getSSLParameters().getNamedGroups());
-        assertArrayEquals(controlNamedGroups, 
serverSocket.getSSLParameters().getNamedGroups());
+        assertArrayEquals(controlNamedGroups, 
getNamedGroups(engine.getSSLParameters()));
+        assertArrayEquals(controlNamedGroups, 
getNamedGroups(socket.getSSLParameters()));
+        assertArrayEquals(controlNamedGroups, 
getNamedGroups(serverSocket.getSSLParameters()));
 
         // include all but exclude all (excludes win)
         filter.getExclude().add(".*");
@@ -867,9 +873,9 @@ public class SSLContextParametersTest extends 
AbstractJsseParametersTest {
         socket = (SSLSocket) context.getSocketFactory().createSocket();
         serverSocket = (SSLServerSocket) 
context.getServerSocketFactory().createServerSocket();
 
-        assertEquals(0, engine.getSSLParameters().getNamedGroups().length);
-        assertEquals(0, socket.getSSLParameters().getNamedGroups().length);
-        assertEquals(0, 
serverSocket.getSSLParameters().getNamedGroups().length);
+        assertEquals(0, getNamedGroups(engine.getSSLParameters()).length);
+        assertEquals(0, getNamedGroups(socket.getSSLParameters()).length);
+        assertEquals(0, 
getNamedGroups(serverSocket.getSSLParameters()).length);
 
         // include only x* groups (e.g. x25519, x448)
         filter.getInclude().clear();
@@ -880,21 +886,22 @@ public class SSLContextParametersTest extends 
AbstractJsseParametersTest {
         socket = (SSLSocket) context.getSocketFactory().createSocket();
         serverSocket = (SSLServerSocket) 
context.getServerSocketFactory().createServerSocket();
 
-        assertTrue(engine.getSSLParameters().getNamedGroups().length >= 1);
-        for (String group : engine.getSSLParameters().getNamedGroups()) {
+        assertTrue(getNamedGroups(engine.getSSLParameters()).length >= 1);
+        for (String group : getNamedGroups(engine.getSSLParameters())) {
             assertTrue(group.startsWith("x"), "Expected group starting with 
'x' but got: " + group);
         }
-        assertTrue(socket.getSSLParameters().getNamedGroups().length >= 1);
-        for (String group : socket.getSSLParameters().getNamedGroups()) {
+        assertTrue(getNamedGroups(socket.getSSLParameters()).length >= 1);
+        for (String group : getNamedGroups(socket.getSSLParameters())) {
             assertTrue(group.startsWith("x"), "Expected group starting with 
'x' but got: " + group);
         }
-        assertTrue(serverSocket.getSSLParameters().getNamedGroups().length >= 
1);
-        for (String group : serverSocket.getSSLParameters().getNamedGroups()) {
+        assertTrue(getNamedGroups(serverSocket.getSSLParameters()).length >= 
1);
+        for (String group : getNamedGroups(serverSocket.getSSLParameters())) {
             assertTrue(group.startsWith("x"), "Expected group starting with 
'x' but got: " + group);
         }
     }
 
     @Test
+    @EnabledForJreRange(min = JRE.JAVA_21)
     public void testNamedGroupsDoNotAffectCipherSuitesOrProtocols() throws 
Exception {
         SSLContext controlContext = SSLContext.getInstance("TLSv1.3");
         controlContext.init(null, null, null);
@@ -911,16 +918,17 @@ public class SSLContextParametersTest extends 
AbstractJsseParametersTest {
 
         assertArrayEquals(controlEngine.getEnabledCipherSuites(), 
engine.getEnabledCipherSuites());
         assertArrayEquals(controlEngine.getEnabledProtocols(), 
engine.getEnabledProtocols());
-        assertEquals(1, engine.getSSLParameters().getNamedGroups().length);
-        assertEquals("x25519", engine.getSSLParameters().getNamedGroups()[0]);
+        assertEquals(1, getNamedGroups(engine.getSSLParameters()).length);
+        assertEquals("x25519", getNamedGroups(engine.getSSLParameters())[0]);
     }
 
     @Test
+    @EnabledForJreRange(min = JRE.JAVA_21)
     public void testSignatureSchemes() throws Exception {
         SSLContext controlContext = SSLContext.getInstance("TLSv1.3");
         controlContext.init(null, null, null);
         SSLEngine controlEngine = controlContext.createSSLEngine();
-        String[] controlSignatureSchemes = 
controlEngine.getSSLParameters().getSignatureSchemes();
+        String[] controlSignatureSchemes = 
getSignatureSchemes(controlEngine.getSSLParameters());
 
         // default - no signature schemes configured, should keep defaults
         SSLContextParameters scp = new SSLContextParameters();
@@ -930,9 +938,9 @@ public class SSLContextParametersTest extends 
AbstractJsseParametersTest {
         SSLSocket socket = (SSLSocket) 
context.getSocketFactory().createSocket();
         SSLServerSocket serverSocket = (SSLServerSocket) 
context.getServerSocketFactory().createServerSocket();
 
-        assertArrayEquals(controlSignatureSchemes, 
engine.getSSLParameters().getSignatureSchemes());
-        assertArrayEquals(controlSignatureSchemes, 
socket.getSSLParameters().getSignatureSchemes());
-        assertArrayEquals(controlSignatureSchemes, 
serverSocket.getSSLParameters().getSignatureSchemes());
+        assertArrayEquals(controlSignatureSchemes, 
getSignatureSchemes(engine.getSSLParameters()));
+        assertArrayEquals(controlSignatureSchemes, 
getSignatureSchemes(socket.getSSLParameters()));
+        assertArrayEquals(controlSignatureSchemes, 
getSignatureSchemes(serverSocket.getSSLParameters()));
 
         // empty ssp - sets empty list
         SignatureSchemesParameters ssp = new SignatureSchemesParameters();
@@ -942,9 +950,9 @@ public class SSLContextParametersTest extends 
AbstractJsseParametersTest {
         socket = (SSLSocket) context.getSocketFactory().createSocket();
         serverSocket = (SSLServerSocket) 
context.getServerSocketFactory().createServerSocket();
 
-        assertEquals(0, 
engine.getSSLParameters().getSignatureSchemes().length);
-        assertEquals(0, 
socket.getSSLParameters().getSignatureSchemes().length);
-        assertEquals(0, 
serverSocket.getSSLParameters().getSignatureSchemes().length);
+        assertEquals(0, getSignatureSchemes(engine.getSSLParameters()).length);
+        assertEquals(0, getSignatureSchemes(socket.getSSLParameters()).length);
+        assertEquals(0, 
getSignatureSchemes(serverSocket.getSSLParameters()).length);
 
         // explicit signature scheme
         
ssp.setSignatureScheme(Collections.singletonList("rsa_pss_rsae_sha256"));
@@ -953,12 +961,12 @@ public class SSLContextParametersTest extends 
AbstractJsseParametersTest {
         socket = (SSLSocket) context.getSocketFactory().createSocket();
         serverSocket = (SSLServerSocket) 
context.getServerSocketFactory().createServerSocket();
 
-        assertEquals(1, 
engine.getSSLParameters().getSignatureSchemes().length);
-        assertEquals("rsa_pss_rsae_sha256", 
engine.getSSLParameters().getSignatureSchemes()[0]);
-        assertEquals(1, 
socket.getSSLParameters().getSignatureSchemes().length);
-        assertEquals("rsa_pss_rsae_sha256", 
socket.getSSLParameters().getSignatureSchemes()[0]);
-        assertEquals(1, 
serverSocket.getSSLParameters().getSignatureSchemes().length);
-        assertEquals("rsa_pss_rsae_sha256", 
serverSocket.getSSLParameters().getSignatureSchemes()[0]);
+        assertEquals(1, getSignatureSchemes(engine.getSSLParameters()).length);
+        assertEquals("rsa_pss_rsae_sha256", 
getSignatureSchemes(engine.getSSLParameters())[0]);
+        assertEquals(1, getSignatureSchemes(socket.getSSLParameters()).length);
+        assertEquals("rsa_pss_rsae_sha256", 
getSignatureSchemes(socket.getSSLParameters())[0]);
+        assertEquals(1, 
getSignatureSchemes(serverSocket.getSSLParameters()).length);
+        assertEquals("rsa_pss_rsae_sha256", 
getSignatureSchemes(serverSocket.getSSLParameters())[0]);
 
         // explicit signature schemes override filter
         FilterParameters filter = new FilterParameters();
@@ -969,15 +977,16 @@ public class SSLContextParametersTest extends 
AbstractJsseParametersTest {
         socket = (SSLSocket) context.getSocketFactory().createSocket();
         serverSocket = (SSLServerSocket) 
context.getServerSocketFactory().createServerSocket();
 
-        assertEquals(1, 
engine.getSSLParameters().getSignatureSchemes().length);
-        assertEquals("rsa_pss_rsae_sha256", 
engine.getSSLParameters().getSignatureSchemes()[0]);
-        assertEquals(1, 
socket.getSSLParameters().getSignatureSchemes().length);
-        assertEquals("rsa_pss_rsae_sha256", 
socket.getSSLParameters().getSignatureSchemes()[0]);
-        assertEquals(1, 
serverSocket.getSSLParameters().getSignatureSchemes().length);
-        assertEquals("rsa_pss_rsae_sha256", 
serverSocket.getSSLParameters().getSignatureSchemes()[0]);
+        assertEquals(1, getSignatureSchemes(engine.getSSLParameters()).length);
+        assertEquals("rsa_pss_rsae_sha256", 
getSignatureSchemes(engine.getSSLParameters())[0]);
+        assertEquals(1, getSignatureSchemes(socket.getSSLParameters()).length);
+        assertEquals("rsa_pss_rsae_sha256", 
getSignatureSchemes(socket.getSSLParameters())[0]);
+        assertEquals(1, 
getSignatureSchemes(serverSocket.getSSLParameters()).length);
+        assertEquals("rsa_pss_rsae_sha256", 
getSignatureSchemes(serverSocket.getSSLParameters())[0]);
     }
 
     @Test
+    @EnabledForJreRange(min = JRE.JAVA_21)
     public void testSignatureSchemesFilter() throws Exception {
         // Note: SSLParameters.getSignatureSchemes() returns null by default 
(unlike getNamedGroups()),
         // so filters operate on explicitly provided schemes rather than JDK 
defaults.
@@ -990,9 +999,9 @@ public class SSLContextParametersTest extends 
AbstractJsseParametersTest {
         SSLSocket socket = (SSLSocket) 
context.getSocketFactory().createSocket();
         SSLServerSocket serverSocket = (SSLServerSocket) 
context.getServerSocketFactory().createServerSocket();
 
-        assertNull(engine.getSSLParameters().getSignatureSchemes());
-        assertNull(socket.getSSLParameters().getSignatureSchemes());
-        assertNull(serverSocket.getSSLParameters().getSignatureSchemes());
+        assertNull(getSignatureSchemes(engine.getSSLParameters()));
+        assertNull(getSignatureSchemes(socket.getSSLParameters()));
+        assertNull(getSignatureSchemes(serverSocket.getSSLParameters()));
 
         // empty filter - no includes means no schemes match (empty array)
         FilterParameters filter = new FilterParameters();
@@ -1002,9 +1011,9 @@ public class SSLContextParametersTest extends 
AbstractJsseParametersTest {
         socket = (SSLSocket) context.getSocketFactory().createSocket();
         serverSocket = (SSLServerSocket) 
context.getServerSocketFactory().createServerSocket();
 
-        assertEquals(0, 
engine.getSSLParameters().getSignatureSchemes().length);
-        assertEquals(0, 
socket.getSSLParameters().getSignatureSchemes().length);
-        assertEquals(0, 
serverSocket.getSSLParameters().getSignatureSchemes().length);
+        assertEquals(0, getSignatureSchemes(engine.getSSLParameters()).length);
+        assertEquals(0, getSignatureSchemes(socket.getSSLParameters()).length);
+        assertEquals(0, 
getSignatureSchemes(serverSocket.getSSLParameters()).length);
 
         // explicit schemes override filter - filter ignored when schemes are 
set
         SignatureSchemesParameters ssp = new SignatureSchemesParameters();
@@ -1021,7 +1030,7 @@ public class SSLContextParametersTest extends 
AbstractJsseParametersTest {
         engine = context.createSSLEngine();
 
         // explicit schemes take precedence over filter
-        assertEquals(4, 
engine.getSSLParameters().getSignatureSchemes().length);
+        assertEquals(4, getSignatureSchemes(engine.getSSLParameters()).length);
 
         // clear explicit schemes, keep filter - now filter applies to empty 
JDK defaults
         scp.setSignatureSchemes(null);
@@ -1033,12 +1042,13 @@ public class SSLContextParametersTest extends 
AbstractJsseParametersTest {
         serverSocket = (SSLServerSocket) 
context.getServerSocketFactory().createServerSocket();
 
         // JDK defaults are null → filtering null gives empty array
-        assertEquals(0, 
engine.getSSLParameters().getSignatureSchemes().length);
-        assertEquals(0, 
socket.getSSLParameters().getSignatureSchemes().length);
-        assertEquals(0, 
serverSocket.getSSLParameters().getSignatureSchemes().length);
+        assertEquals(0, getSignatureSchemes(engine.getSSLParameters()).length);
+        assertEquals(0, getSignatureSchemes(socket.getSSLParameters()).length);
+        assertEquals(0, 
getSignatureSchemes(serverSocket.getSSLParameters()).length);
     }
 
     @Test
+    @EnabledForJreRange(min = JRE.JAVA_21)
     public void testSignatureSchemesDoNotAffectOtherSettings() throws 
Exception {
         SSLContext controlContext = SSLContext.getInstance("TLSv1.3");
         controlContext.init(null, null, null);
@@ -1056,16 +1066,16 @@ public class SSLContextParametersTest extends 
AbstractJsseParametersTest {
         assertArrayEquals(controlEngine.getEnabledCipherSuites(), 
engine.getEnabledCipherSuites());
         assertArrayEquals(controlEngine.getEnabledProtocols(), 
engine.getEnabledProtocols());
         // Named groups may be reordered by PQC auto-configuration, but same 
groups should be present
-        String[] controlGroups = 
controlEngine.getSSLParameters().getNamedGroups();
-        String[] engineGroups = engine.getSSLParameters().getNamedGroups();
+        String[] controlGroups = 
getNamedGroups(controlEngine.getSSLParameters());
+        String[] engineGroups = getNamedGroups(engine.getSSLParameters());
         if (controlGroups != null && 
Arrays.asList(controlGroups).contains("X25519MLKEM768")) {
             assertEquals("X25519MLKEM768", engineGroups[0]);
             assertEquals(controlGroups.length, engineGroups.length);
         } else {
             assertArrayEquals(controlGroups, engineGroups);
         }
-        assertEquals(1, 
engine.getSSLParameters().getSignatureSchemes().length);
-        assertEquals("rsa_pss_rsae_sha256", 
engine.getSSLParameters().getSignatureSchemes()[0]);
+        assertEquals(1, getSignatureSchemes(engine.getSSLParameters()).length);
+        assertEquals("rsa_pss_rsae_sha256", 
getSignatureSchemes(engine.getSSLParameters())[0]);
     }
 
     @Test
@@ -1154,18 +1164,19 @@ public class SSLContextParametersTest extends 
AbstractJsseParametersTest {
     }
 
     @Test
+    @EnabledForJreRange(min = JRE.JAVA_21)
     public void testPqcNamedGroupsAutoConfigured() throws Exception {
         SSLContext controlContext = SSLContext.getInstance("TLSv1.3");
         controlContext.init(null, null, null);
         SSLEngine controlEngine = controlContext.createSSLEngine();
-        String[] controlNamedGroups = 
controlEngine.getSSLParameters().getNamedGroups();
+        String[] controlNamedGroups = 
getNamedGroups(controlEngine.getSSLParameters());
         boolean pqcAvailable = controlNamedGroups != null
                 && 
Arrays.asList(controlNamedGroups).contains("X25519MLKEM768");
 
         SSLContextParameters scp = new SSLContextParameters();
         SSLContext context = scp.createSSLContext(null);
         SSLEngine engine = context.createSSLEngine();
-        String[] resultGroups = engine.getSSLParameters().getNamedGroups();
+        String[] resultGroups = getNamedGroups(engine.getSSLParameters());
 
         if (pqcAvailable) {
             // X25519MLKEM768 should be first
@@ -1182,6 +1193,7 @@ public class SSLContextParametersTest extends 
AbstractJsseParametersTest {
     }
 
     @Test
+    @EnabledForJreRange(min = JRE.JAVA_21)
     public void testPqcNamedGroupsUserConfigOverrides() throws Exception {
         // User-configured named groups should NOT be overridden by PQC 
auto-config
         SSLContextParameters scp = new SSLContextParameters();
@@ -1192,11 +1204,12 @@ public class SSLContextParametersTest extends 
AbstractJsseParametersTest {
         SSLContext context = scp.createSSLContext(null);
         SSLEngine engine = context.createSSLEngine();
 
-        assertEquals(1, engine.getSSLParameters().getNamedGroups().length);
-        assertEquals("secp256r1", 
engine.getSSLParameters().getNamedGroups()[0]);
+        assertEquals(1, getNamedGroups(engine.getSSLParameters()).length);
+        assertEquals("secp256r1", 
getNamedGroups(engine.getSSLParameters())[0]);
     }
 
     @Test
+    @EnabledForJreRange(min = JRE.JAVA_21)
     public void testPqcNamedGroupsFilterOverrides() throws Exception {
         // User-configured named groups filter should NOT be overridden by PQC 
auto-config
         SSLContextParameters scp = new SSLContextParameters();
@@ -1207,7 +1220,7 @@ public class SSLContextParametersTest extends 
AbstractJsseParametersTest {
         SSLContext context = scp.createSSLContext(null);
         SSLEngine engine = context.createSSLEngine();
 
-        for (String group : engine.getSSLParameters().getNamedGroups()) {
+        for (String group : getNamedGroups(engine.getSSLParameters())) {
             assertTrue(group.startsWith("secp"), "Expected group starting with 
'secp' but got: " + group);
         }
     }
@@ -1248,4 +1261,16 @@ public class SSLContextParametersTest extends 
AbstractJsseParametersTest {
             assertTrue(value.startsWith(prefix), value + " does not start with 
the prefix " + prefix);
         }
     }
+
+    // Reflection helpers for JDK 20+ 
SSLParameters.getNamedGroups()/getSignatureSchemes()
+    // These methods are not available with --release 17 compilation target
+    private static String[] getNamedGroups(SSLParameters params) throws 
Exception {
+        Method m = SSLParameters.class.getMethod("getNamedGroups");
+        return (String[]) m.invoke(params);
+    }
+
+    private static String[] getSignatureSchemes(SSLParameters params) throws 
Exception {
+        Method m = SSLParameters.class.getMethod("getSignatureSchemes");
+        return (String[]) m.invoke(params);
+    }
 }
diff --git 
a/core/camel-management/src/test/java/org/apache/camel/management/ManagedVirtualThreadExecutorTest.java
 
b/core/camel-management/src/test/java/org/apache/camel/management/ManagedVirtualThreadExecutorTest.java
index e6e4d500689d..f93bf97591c6 100644
--- 
a/core/camel-management/src/test/java/org/apache/camel/management/ManagedVirtualThreadExecutorTest.java
+++ 
b/core/camel-management/src/test/java/org/apache/camel/management/ManagedVirtualThreadExecutorTest.java
@@ -26,12 +26,15 @@ import org.apache.camel.CamelContext;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.spi.LifecycleStrategy;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.EnabledForJreRange;
+import org.junit.jupiter.api.condition.JRE;
 
 import static 
org.apache.camel.management.DefaultManagementObjectNameStrategy.TYPE_THREAD_POOL;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
+@EnabledForJreRange(min = JRE.JAVA_21)
 public class ManagedVirtualThreadExecutorTest extends ManagementTestSupport {
 
     private ExecutorService vte;
@@ -41,7 +44,9 @@ public class ManagedVirtualThreadExecutorTest extends 
ManagementTestSupport {
         CamelContext ctx = super.createCamelContext();
         // register the virtual thread executor during context creation 
(before it starts)
         // so that shouldRegister returns true
-        vte = Executors.newVirtualThreadPerTaskExecutor();
+        vte = (ExecutorService) Executors.class
+                .getMethod("newVirtualThreadPerTaskExecutor")
+                .invoke(null);
         for (LifecycleStrategy lifecycle : ctx.getLifecycleStrategies()) {
             lifecycle.onThreadPoolAdd(ctx, vte, "myVirtualPool", "test", null, 
null);
         }
diff --git 
a/core/camel-support/src/main/java/org/apache/camel/support/DefaultThreadPoolFactory.java
 
b/core/camel-support/src/main/java/org/apache/camel/support/DefaultThreadPoolFactory.java
index c69d555511b1..70e6f5fb5c50 100644
--- 
a/core/camel-support/src/main/java/org/apache/camel/support/DefaultThreadPoolFactory.java
+++ 
b/core/camel-support/src/main/java/org/apache/camel/support/DefaultThreadPoolFactory.java
@@ -163,7 +163,7 @@ public class DefaultThreadPoolFactory extends 
ServiceSupport implements CamelCon
         VIRTUAL {
             @Override
             ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
-                return Executors.newThreadPerTaskExecutor(threadFactory);
+                return newThreadPerTaskExecutor(threadFactory);
             }
 
             @Override
@@ -173,7 +173,7 @@ public class DefaultThreadPoolFactory extends 
ServiceSupport implements CamelCon
                     RejectedExecutionHandler rejectedExecutionHandler,
                     ThreadFactory threadFactory)
                     throws IllegalArgumentException {
-                return Executors.newThreadPerTaskExecutor(threadFactory);
+                return newThreadPerTaskExecutor(threadFactory);
             }
 
             @Override
@@ -198,6 +198,17 @@ public class DefaultThreadPoolFactory extends 
ServiceSupport implements CamelCon
                     && factoryTypeAware.isVirtual() ? 
ThreadPoolFactoryType.VIRTUAL : ThreadPoolFactoryType.PLATFORM;
         }
 
+        @SuppressWarnings("unchecked")
+        private static ExecutorService newThreadPerTaskExecutor(ThreadFactory 
threadFactory) {
+            try {
+                return (ExecutorService) Executors.class
+                        .getMethod("newThreadPerTaskExecutor", 
ThreadFactory.class)
+                        .invoke(null, threadFactory);
+            } catch (Exception e) {
+                throw new 
UnsupportedOperationException("newThreadPerTaskExecutor requires JDK 21+", e);
+            }
+        }
+
         abstract ExecutorService newCachedThreadPool(ThreadFactory 
threadFactory);
 
         abstract ExecutorService newThreadPool(
diff --git 
a/core/camel-support/src/main/java/org/apache/camel/support/builder/ExpressionBuilder.java
 
b/core/camel-support/src/main/java/org/apache/camel/support/builder/ExpressionBuilder.java
index 63a05598927a..49c0ed3132b6 100644
--- 
a/core/camel-support/src/main/java/org/apache/camel/support/builder/ExpressionBuilder.java
+++ 
b/core/camel-support/src/main/java/org/apache/camel/support/builder/ExpressionBuilder.java
@@ -1407,7 +1407,7 @@ public class ExpressionBuilder {
         return new ExpressionAdapter() {
             @Override
             public Object evaluate(Exchange exchange) {
-                return Thread.currentThread().threadId();
+                return Thread.currentThread().getId();
             }
 
             @Override
diff --git 
a/core/camel-util/src/main/java/org/apache/camel/util/concurrent/CamelThreadFactory.java
 
b/core/camel-util/src/main/java/org/apache/camel/util/concurrent/CamelThreadFactory.java
index 4bb630479974..638fcf1b399d 100644
--- 
a/core/camel-util/src/main/java/org/apache/camel/util/concurrent/CamelThreadFactory.java
+++ 
b/core/camel-util/src/main/java/org/apache/camel/util/concurrent/CamelThreadFactory.java
@@ -20,8 +20,9 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * Thread factory which creates threads supporting a naming pattern. The 
factory creates virtual threads in case the
- * System property {@code camel.threads.virtual.enabled} set to {@code true}.
+ * Thread factory which creates threads supporting a naming pattern. On JDK 
21+, this factory creates virtual threads
+ * when the System property {@code camel.threads.virtual.enabled} is set to 
{@code true}. On JDK 17, only platform
+ * threads are available.
  */
 public final class CamelThreadFactory implements ThreadFactoryTypeAware {
     private static final Logger LOG = 
LoggerFactory.getLogger(CamelThreadFactory.class);
@@ -29,25 +30,24 @@ public final class CamelThreadFactory implements 
ThreadFactoryTypeAware {
     private final String pattern;
     private final String name;
     private final boolean daemon;
-    private final ThreadFactoryType threadType;
 
     public CamelThreadFactory(String pattern, String name, boolean daemon) {
         this.pattern = pattern;
         this.name = name;
         this.daemon = daemon;
-        this.threadType = daemon ? ThreadFactoryType.current() : 
ThreadFactoryType.PLATFORM;
     }
 
     @Override
     public boolean isVirtual() {
-        return threadType == ThreadFactoryType.VIRTUAL;
+        return false;
     }
 
     @Override
     public Thread newThread(Runnable runnable) {
         String threadName = ThreadHelper.resolveThreadName(pattern, name);
 
-        Thread answer = threadType.newThread(threadName, daemon, runnable);
+        Thread answer = new Thread(runnable, threadName);
+        answer.setDaemon(daemon);
 
         LOG.trace("Created thread[{}] -> {}", threadName, answer);
         return answer;
@@ -61,27 +61,4 @@ public final class CamelThreadFactory implements 
ThreadFactoryTypeAware {
     public String toString() {
         return "CamelThreadFactory[" + name + "]";
     }
-
-    private enum ThreadFactoryType {
-        PLATFORM {
-            Thread.Builder newThreadBuilder(String threadName, boolean daemon) 
{
-                return Thread.ofPlatform().name(threadName).daemon(daemon);
-            }
-        },
-        VIRTUAL {
-            Thread.Builder newThreadBuilder(String threadName, boolean daemon) 
{
-                return Thread.ofVirtual().name(threadName);
-            }
-        };
-
-        Thread newThread(String threadName, boolean daemon, Runnable runnable) 
{
-            return newThreadBuilder(threadName, daemon).unstarted(runnable);
-        }
-
-        abstract Thread.Builder newThreadBuilder(String threadName, boolean 
daemon);
-
-        static ThreadFactoryType current() {
-            return ThreadType.current() == ThreadType.VIRTUAL ? VIRTUAL : 
PLATFORM;
-        }
-    }
 }
diff --git 
a/core/camel-util/src/main/java/org/apache/camel/util/concurrent/CamelThreadFactory.java
 
b/core/camel-util/src/main/java21/org/apache/camel/util/concurrent/CamelThreadFactory.java
similarity index 100%
copy from 
core/camel-util/src/main/java/org/apache/camel/util/concurrent/CamelThreadFactory.java
copy to 
core/camel-util/src/main/java21/org/apache/camel/util/concurrent/CamelThreadFactory.java
diff --git a/pom.xml b/pom.xml
index b8aa2a05bf5d..d9f09f6febbc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -110,7 +110,7 @@
         
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
 
         
<site-repo-url>scpexe://people.apache.org/www/camel.apache.org/maven/</site-repo-url>
-        <jdk.version>21</jdk.version>
+        <jdk.version>17</jdk.version>
         <!-- These two are here only to prevent the versions for the Apache 
parent pom from leaking-->
         <maven.compiler.source>${jdk.version}</maven.compiler.source>
         <maven.compiler.target>${jdk.version}</maven.compiler.target>
diff --git 
a/tooling/camel-util-json/src/main/java/org/apache/camel/util/json/JsonObject.java
 
b/tooling/camel-util-json/src/main/java/org/apache/camel/util/json/JsonObject.java
index d361e0b05179..a6be47747d96 100644
--- 
a/tooling/camel-util-json/src/main/java/org/apache/camel/util/json/JsonObject.java
+++ 
b/tooling/camel-util-json/src/main/java/org/apache/camel/util/json/JsonObject.java
@@ -20,12 +20,15 @@ import java.io.IOException;
 import java.io.StringWriter;
 import java.io.Writer;
 import java.math.BigDecimal;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * JsonObject is a common non-thread safe data format for string to data 
mappings. The contents of a JsonObject are only
@@ -194,7 +197,7 @@ public class JsonObject extends LinkedHashMap<String, 
Object> implements Jsonabl
             if (pos != -1 && answer instanceof List<?> arr) {
                 jo = null;
                 if (pos == Integer.MAX_VALUE) {
-                    answer = arr.getLast();
+                    answer = arr.get(arr.size() - 1);
                 } else if (pos < arr.size()) {
                     answer = arr.get(pos);
                 } else {
@@ -230,10 +233,23 @@ public class JsonObject extends LinkedHashMap<String, 
Object> implements Jsonabl
         return JsonObject.class.cast(o);
     }
 
+    private static String[] splitWithDelimiters(String input, String regex) {
+        List<String> parts = new ArrayList<>();
+        Matcher m = Pattern.compile(regex).matcher(input);
+        int lastEnd = 0;
+        while (m.find()) {
+            parts.add(input.substring(lastEnd, m.start()));
+            parts.add(m.group());
+            lastEnd = m.end();
+        }
+        parts.add(input.substring(lastEnd));
+        return parts.toArray(new String[0]);
+    }
+
     private Optional<Object> doPath(final String path) {
         Object answer = null;
 
-        String[] split = path.splitWithDelimiters("(\\?\\.|\\.)", 0);
+        String[] split = splitWithDelimiters(path, "(\\?\\.|\\.)");
         for (int i = 0; i < split.length; i = i + 2) {
             String part = split[i];
             String dot = i > 0 ? split[i - 1] : null;
@@ -262,7 +278,7 @@ public class JsonObject extends LinkedHashMap<String, 
Object> implements Jsonabl
             }
             if (pos != -1 && answer instanceof List<?> arr) {
                 if (pos == Integer.MAX_VALUE) {
-                    answer = arr.getLast();
+                    answer = arr.get(arr.size() - 1);
                 } else if (pos < arr.size()) {
                     answer = arr.get(pos);
                 } else {

Reply via email to