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

davsclaus 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 4c4172410627 CAMEL-25795: camel-pqc - stopping an endpoint no longer 
removes the Bouncy Castle providers from the JVM (#28089)
4c4172410627 is described below

commit 4c417241062798d27ff1967ea841c37688ac9fad
Author: Andrea Cosentino <[email protected]>
AuthorDate: Tue Oct 20 21:02:22 2026 +0200

    CAMEL-25795: camel-pqc - stopping an endpoint no longer removes the Bouncy 
Castle providers from the JVM (#28089)
    
    Co-Authored-By: Claude Fable 5.1 <[email protected]>
    Co-Authored-By: Claude Opus 5 <[email protected]>
---
 .../apache/camel/component/pqc/PQCEndpoint.java    | 20 +++---
 .../pqc/PQCEndpointProviderLifecycleTest.java      | 81 ++++++++++++++++++++++
 .../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc    | 12 ++++
 3 files changed, 102 insertions(+), 11 deletions(-)

diff --git 
a/components/camel-pqc/src/main/java/org/apache/camel/component/pqc/PQCEndpoint.java
 
b/components/camel-pqc/src/main/java/org/apache/camel/component/pqc/PQCEndpoint.java
index 42dc26432838..3a78b651d698 100644
--- 
a/components/camel-pqc/src/main/java/org/apache/camel/component/pqc/PQCEndpoint.java
+++ 
b/components/camel-pqc/src/main/java/org/apache/camel/component/pqc/PQCEndpoint.java
@@ -60,6 +60,15 @@ public class PQCEndpoint extends DefaultEndpoint {
         return new PQCProducer(this);
     }
 
+    /**
+     * Registers the BouncyCastle providers if they are not installed yet.
+     * <p>
+     * The providers are deliberately never removed again. {@link Security} is 
JVM-global and shared with every other
+     * endpoint, every other {@code CamelContext} and all non-Camel code in 
the same JVM, and nothing records which
+     * component installed a given provider. Removing one on stop therefore 
tears it out from under unrelated users -
+     * including the sibling {@code pqc} endpoint of the same route and the 
statically initialised
+     * {@code PQCDefault*Material} classes of this very component, which also 
register BouncyCastle and never remove it.
+     */
     @Override
     public void doStart() throws Exception {
         super.doStart();
@@ -71,17 +80,6 @@ public class PQCEndpoint extends DefaultEndpoint {
         }
     }
 
-    @Override
-    public void doStop() throws Exception {
-        if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) != null) {
-            Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME);
-        }
-        if (Security.getProvider(BouncyCastlePQCProvider.PROVIDER_NAME) != 
null) {
-            Security.removeProvider(BouncyCastlePQCProvider.PROVIDER_NAME);
-        }
-        super.doStop();
-    }
-
     public PQCConfiguration getConfiguration() {
         return configuration;
     }
diff --git 
a/components/camel-pqc/src/test/java/org/apache/camel/component/pqc/PQCEndpointProviderLifecycleTest.java
 
b/components/camel-pqc/src/test/java/org/apache/camel/component/pqc/PQCEndpointProviderLifecycleTest.java
new file mode 100644
index 000000000000..bef20c0f91f1
--- /dev/null
+++ 
b/components/camel-pqc/src/test/java/org/apache/camel/component/pqc/PQCEndpointProviderLifecycleTest.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.pqc;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider;
+import org.junit.jupiter.api.Test;
+
+import static java.security.Security.getProvider;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+/**
+ * The BouncyCastle providers live in the JVM-global {@link 
java.security.Security} registry, so a PQC endpoint must
+ * never remove them on stop - other endpoints, other CamelContexts and 
unrelated code in the same JVM rely on them.
+ */
+public class PQCEndpointProviderLifecycleTest {
+
+    @Test
+    void stoppingTheContextKeepsTheProvidersRegistered() throws Exception {
+        try (CamelContext context = new DefaultCamelContext()) {
+            context.addRoutes(new RouteBuilder() {
+                @Override
+                public void configure() {
+                    
from("direct:sign").to("pqc:sign?operation=sign&signatureAlgorithm=MLDSA");
+                }
+            });
+            context.start();
+
+            assertNotNull(getProvider(BouncyCastleProvider.PROVIDER_NAME), "BC 
must be registered while running");
+            assertNotNull(getProvider(BouncyCastlePQCProvider.PROVIDER_NAME), 
"BCPQC must be registered while running");
+
+            context.stop();
+
+            assertNotNull(getProvider(BouncyCastleProvider.PROVIDER_NAME),
+                    "stopping a PQC endpoint must not de-register the 
JVM-global BC provider");
+            assertNotNull(getProvider(BouncyCastlePQCProvider.PROVIDER_NAME),
+                    "stopping a PQC endpoint must not de-register the 
JVM-global BCPQC provider");
+        }
+    }
+
+    @Test
+    void stoppingOneEndpointLeavesTheProviderForTheOther() throws Exception {
+        try (CamelContext context = new DefaultCamelContext()) {
+            context.addRoutes(new RouteBuilder() {
+                @Override
+                public void configure() {
+                    
from("direct:a").routeId("a").to("pqc:a?operation=sign&signatureAlgorithm=MLDSA");
+                    
from("direct:b").routeId("b").to("pqc:b?operation=sign&signatureAlgorithm=MLDSA");
+                }
+            });
+            context.start();
+
+            // stopping the first route disposes its endpoint. The second 
route keeps its own Signature either way,
+            // so what this pins is the provider: on main the stop 
de-registers it for the whole JVM.
+            context.getRouteController().stopRoute("a");
+            context.removeEndpoints("pqc:a*");
+
+            context.createProducerTemplate().sendBody("direct:b", "Hello");
+
+            assertNotNull(getProvider(BouncyCastleProvider.PROVIDER_NAME),
+                    "a sibling PQC endpoint must still see the BC provider");
+        }
+    }
+}
diff --git 
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
index 56e5ff0a682a..9e7a7d47dc0a 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
@@ -272,6 +272,18 @@ values such as `AES`, which are pure ASCII. A deployment 
running on a JVM whose
 name identically to UTF-8 would, however, derive a different key than the 
previous version, and could not decrypt
 material produced by it.
 
+=== camel-pqc - the BouncyCastle providers stay registered when an endpoint 
stops
+
+Stopping a `pqc` endpoint no longer removes the `BC` and `BCPQC` providers 
from `java.security.Security`.
+
+`java.security.Security` is JVM-global, and the endpoint only added a provider 
when it was absent, so stopping one
+endpoint could remove a provider it had not installed - breaking the other 
`pqc` endpoint of the same route, other
+`CamelContext` instances, components such as camel-crypto, camel-ssh and 
camel-xmlsecurity, and any non-Camel code in
+the same JVM.
+
+An application that relied on the providers being de-registered on shutdown 
must now remove them itself with
+`Security.removeProvider("BC")` and `Security.removeProvider("BCPQC")`.
+
 === camel-jbang - camel validate normalize writes one file per input file to 
an output directory
 
 `camel validate normalize <files> --output=<dir>` now writes one normalized 
file per input file into the

Reply via email to