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

gnodet pushed a commit to branch backport/28522-to-camel-4.18.x
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 79f2d7437c14d1b9aa1e83f99fa37bb01f25d4e4
Author: Guillaume Nodet <[email protected]>
AuthorDate: Tue Oct 27 13:42:59 2026 +0000

    [backport camel-4.18.x] CAMEL-26323: camel-shiro - ShiroSecurityProcessor 
should authenticate against policy security manager
---
 .../shiro/security/ShiroSecurityPolicy.java        |   3 -
 .../shiro/security/ShiroSecurityProcessor.java     |  71 ++++-
 .../security/ShiroMultiplePolicyIsolationTest.java | 303 +++++++++++++++++++++
 .../ROOT/pages/camel-4x-upgrade-guide-4_18.adoc    |  18 ++
 4 files changed, 388 insertions(+), 7 deletions(-)

diff --git 
a/components/camel-shiro/src/main/java/org/apache/camel/component/shiro/security/ShiroSecurityPolicy.java
 
b/components/camel-shiro/src/main/java/org/apache/camel/component/shiro/security/ShiroSecurityPolicy.java
index 423c304c1f1d..93e794bac6bf 100644
--- 
a/components/camel-shiro/src/main/java/org/apache/camel/component/shiro/security/ShiroSecurityPolicy.java
+++ 
b/components/camel-shiro/src/main/java/org/apache/camel/component/shiro/security/ShiroSecurityPolicy.java
@@ -23,7 +23,6 @@ import org.apache.camel.NamedNode;
 import org.apache.camel.Processor;
 import org.apache.camel.Route;
 import org.apache.camel.spi.AuthorizationPolicy;
-import org.apache.shiro.SecurityUtils;
 import org.apache.shiro.authz.Permission;
 import org.apache.shiro.config.Ini;
 import org.apache.shiro.crypto.cipher.AesCipherService;
@@ -58,13 +57,11 @@ public class ShiroSecurityPolicy implements 
AuthorizationPolicy {
         Ini ini = new Ini();
         ini.loadFromPath(iniResourcePath);
         securityManager = new BasicIniEnvironment(ini).getSecurityManager();
-        SecurityUtils.setSecurityManager(securityManager);
     }
 
     public ShiroSecurityPolicy(Ini ini) {
         this();
         securityManager = new BasicIniEnvironment(ini).getSecurityManager();
-        SecurityUtils.setSecurityManager(securityManager);
     }
 
     public ShiroSecurityPolicy(String iniResourcePath, byte[] passPhrase) {
diff --git 
a/components/camel-shiro/src/main/java/org/apache/camel/component/shiro/security/ShiroSecurityProcessor.java
 
b/components/camel-shiro/src/main/java/org/apache/camel/component/shiro/security/ShiroSecurityProcessor.java
index 98f18bec1221..f620c9d0433c 100644
--- 
a/components/camel-shiro/src/main/java/org/apache/camel/component/shiro/security/ShiroSecurityProcessor.java
+++ 
b/components/camel-shiro/src/main/java/org/apache/camel/component/shiro/security/ShiroSecurityProcessor.java
@@ -32,6 +32,9 @@ import org.apache.shiro.crypto.cipher.ByteSourceBroker;
 import org.apache.shiro.lang.codec.Base64;
 import org.apache.shiro.lang.util.ByteSource;
 import org.apache.shiro.subject.Subject;
+import org.apache.shiro.subject.support.DelegatingSubject;
+import org.apache.shiro.subject.support.SubjectThreadState;
+import org.apache.shiro.util.ThreadContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -42,6 +45,9 @@ import org.slf4j.LoggerFactory;
 public class ShiroSecurityProcessor extends DelegateAsyncProcessor {
 
     private static final Logger LOG = 
LoggerFactory.getLogger(ShiroSecurityProcessor.class);
+    private static final String MANAGED_SUBJECT = 
ShiroSecurityProcessor.class.getName() + ".MANAGED_SUBJECT";
+    private static final String MANAGED_EXCHANGE = 
ShiroSecurityProcessor.class.getName() + ".MANAGED_EXCHANGE";
+
     private final ShiroSecurityPolicy policy;
 
     public ShiroSecurityProcessor(Processor processor, ShiroSecurityPolicy 
policy) {
@@ -114,18 +120,75 @@ public class ShiroSecurityProcessor extends 
DelegateAsyncProcessor {
         ByteSourceBroker decryptedToken = 
policy.getCipherService().decrypt(encryptedToken.getBytes(), 
policy.getPassPhrase());
         ShiroSecurityToken securityToken = 
ShiroSecurityHelper.deserialize(decryptedToken.getClonedBytes());
 
-        Subject currentUser = SecurityUtils.getSubject();
+        SubjectThreadState threadState = null;
+        Subject currentUser = null;
+        boolean success = false;
 
-        // Authenticate user if not authenticated
         try {
+            Subject threadSubject = ThreadContext.getSubject();
+            if (!policy.isAlwaysReauthenticate()
+                    && threadSubject instanceof DelegatingSubject 
delegatingSubject
+                    && policy.getSecurityManager() != null
+                    && delegatingSubject.getSecurityManager() == 
policy.getSecurityManager()) {
+                currentUser = threadSubject;
+            } else {
+                cleanUpManagedSubject(exchange);
+
+                if (policy.getSecurityManager() != null) {
+                    currentUser = new 
Subject.Builder(policy.getSecurityManager()).buildSubject();
+                    threadState = new SubjectThreadState(currentUser);
+                    threadState.bind();
+                } else {
+                    currentUser = SecurityUtils.getSubject();
+                }
+            }
+
+            // Authenticate user if not authenticated
             authenticateUser(currentUser, securityToken);
 
             // Test whether user's role is authorized to perform functions in 
the permissions list
             authorizeUser(currentUser, exchange);
+            success = true;
         } finally {
-            if (policy.isAlwaysReauthenticate()) {
-                currentUser.logout();
+            if (policy.isAlwaysReauthenticate() || !success) {
+                try {
+                    if (currentUser != null) {
+                        currentUser.logout();
+                    }
+                } finally {
+                    ThreadContext.remove(MANAGED_SUBJECT);
+                    ThreadContext.remove(MANAGED_EXCHANGE);
+                    if (threadState != null) {
+                        threadState.restore();
+                    } else {
+                        ThreadContext.unbindSubject();
+                        ThreadContext.unbindSecurityManager();
+                    }
+                }
+            } else {
+                // When alwaysReauthenticate=false and the exchange succeeds, 
the subject remains bound for subsequent
+                // exchanges on this thread. cleanUpManagedSubject will log it 
out when a different exchange arrives.
+                ThreadContext.put(MANAGED_SUBJECT, currentUser);
+                ThreadContext.put(MANAGED_EXCHANGE, exchange);
+            }
+        }
+    }
+
+    private void cleanUpManagedSubject(Exchange currentExchange) {
+        Object managedExchange = ThreadContext.get(MANAGED_EXCHANGE);
+        // Use reference inequality (!=) to skip cleanup during synchronous 
nested routing on the same exchange
+        // (such as direct: routes), avoiding premature logout of the outer 
subject.
+        if (managedExchange != null && managedExchange != currentExchange) {
+            Object managed = ThreadContext.get(MANAGED_SUBJECT);
+            if (managed instanceof Subject oldSubject) {
+                try {
+                    oldSubject.logout();
+                } catch (Exception e) {
+                    LOG.debug("Error logging out previous managed subject", e);
+                }
             }
+            ThreadContext.remove(MANAGED_SUBJECT);
+            ThreadContext.remove(MANAGED_EXCHANGE);
         }
     }
 
diff --git 
a/components/camel-shiro/src/test/java/org/apache/camel/component/shiro/security/ShiroMultiplePolicyIsolationTest.java
 
b/components/camel-shiro/src/test/java/org/apache/camel/component/shiro/security/ShiroMultiplePolicyIsolationTest.java
new file mode 100644
index 000000000000..c155be582737
--- /dev/null
+++ 
b/components/camel-shiro/src/test/java/org/apache/camel/component/shiro/security/ShiroMultiplePolicyIsolationTest.java
@@ -0,0 +1,303 @@
+/*
+ * 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.shiro.security;
+
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.camel.CamelAuthorizationException;
+import org.apache.camel.EndpointInject;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.apache.shiro.SecurityUtils;
+import org.apache.shiro.authc.AuthenticationException;
+import org.apache.shiro.config.Ini;
+import org.apache.shiro.subject.Subject;
+import org.apache.shiro.util.ThreadContext;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class ShiroMultiplePolicyIsolationTest extends CamelTestSupport {
+
+    private static final byte[] KEY_A = 
"0123456789abcdef".getBytes(StandardCharsets.US_ASCII);
+    private static final byte[] KEY_B = 
"fedcba9876543210".getBytes(StandardCharsets.US_ASCII);
+
+    @EndpointInject("mock:successA")
+    protected MockEndpoint successEndpointA;
+
+    @EndpointInject("mock:failureA")
+    protected MockEndpoint failureEndpointA;
+
+    @EndpointInject("mock:successB")
+    protected MockEndpoint successEndpointB;
+
+    @EndpointInject("mock:failureB")
+    protected MockEndpoint failureEndpointB;
+
+    private ShiroSecurityPolicy policyA;
+    private ShiroSecurityPolicy policyB;
+    private ShiroSecurityPolicy policyNoReauthA;
+    private ShiroSecurityPolicy policyNoReauthB;
+
+    @EndpointInject("mock:successNoReauthA")
+    protected MockEndpoint successEndpointNoReauthA;
+
+    @EndpointInject("mock:failureNoReauthA")
+    protected MockEndpoint failureEndpointNoReauthA;
+
+    @EndpointInject("mock:successNoReauthB")
+    protected MockEndpoint successEndpointNoReauthB;
+
+    @EndpointInject("mock:failureNoReauthB")
+    protected MockEndpoint failureEndpointNoReauthB;
+
+    @EndpointInject("mock:successAuthB")
+    protected MockEndpoint successEndpointAuthB;
+
+    @EndpointInject("mock:failureAuthB")
+    protected MockEndpoint failureEndpointAuthB;
+
+    @EndpointInject("mock:nestedSuccess")
+    protected MockEndpoint nestedSuccessEndpoint;
+
+    @EndpointInject("mock:innerSuccess")
+    protected MockEndpoint innerSuccessEndpoint;
+
+    private ShiroSecurityPolicy policyAuthB;
+
+    @AfterEach
+    public void tearDownThreadContext() {
+        ThreadContext.remove();
+    }
+
+    @Test
+    public void testPolicyIsolationBetweenDifferentRealms() throws Exception {
+        successEndpointA.expectedMessageCount(1);
+        failureEndpointA.expectedMessageCount(1);
+        successEndpointB.expectedMessageCount(1);
+        failureEndpointB.expectedMessageCount(1);
+
+        // 1. Valid user for policy A should succeed on route A
+        Map<String, Object> headersAlice = new HashMap<>();
+        headersAlice.put(ShiroSecurityConstants.SHIRO_SECURITY_USERNAME, 
"alice");
+        headersAlice.put(ShiroSecurityConstants.SHIRO_SECURITY_PASSWORD, 
"passwordA");
+        template.sendBodyAndHeaders("direct:routeA", "payloadA", headersAlice);
+
+        // 2. Valid user for policy B should fail on route A
+        Map<String, Object> headersBobOnA = new HashMap<>();
+        headersBobOnA.put(ShiroSecurityConstants.SHIRO_SECURITY_USERNAME, 
"bob");
+        headersBobOnA.put(ShiroSecurityConstants.SHIRO_SECURITY_PASSWORD, 
"passwordB");
+        template.sendBodyAndHeaders("direct:routeA", "payloadA", 
headersBobOnA);
+
+        // 3. Valid user for policy B should succeed on route B
+        Map<String, Object> headersBobOnB = new HashMap<>();
+        headersBobOnB.put(ShiroSecurityConstants.SHIRO_SECURITY_USERNAME, 
"bob");
+        headersBobOnB.put(ShiroSecurityConstants.SHIRO_SECURITY_PASSWORD, 
"passwordB");
+        template.sendBodyAndHeaders("direct:routeB", "payloadB", 
headersBobOnB);
+
+        // 4. User for policy A should fail on route B
+        Map<String, Object> headersAliceOnB = new HashMap<>();
+        headersAliceOnB.put(ShiroSecurityConstants.SHIRO_SECURITY_USERNAME, 
"alice");
+        headersAliceOnB.put(ShiroSecurityConstants.SHIRO_SECURITY_PASSWORD, 
"passwordA");
+        template.sendBodyAndHeaders("direct:routeB", "payloadB", 
headersAliceOnB);
+
+        successEndpointA.assertIsSatisfied();
+        failureEndpointA.assertIsSatisfied();
+        successEndpointB.assertIsSatisfied();
+        failureEndpointB.assertIsSatisfied();
+    }
+
+    @Test
+    public void testPolicyIsolationWithReauthenticateFalse() throws Exception {
+        successEndpointNoReauthA.expectedMessageCount(1);
+        failureEndpointNoReauthA.expectedMessageCount(1);
+        successEndpointNoReauthB.expectedMessageCount(1);
+        failureEndpointNoReauthB.expectedMessageCount(1);
+
+        // 1. Valid user for policy A on route A
+        Map<String, Object> headersAlice = new HashMap<>();
+        headersAlice.put(ShiroSecurityConstants.SHIRO_SECURITY_USERNAME, 
"alice");
+        headersAlice.put(ShiroSecurityConstants.SHIRO_SECURITY_PASSWORD, 
"passwordA");
+        template.sendBodyAndHeaders("direct:routeNoReauthA", "payloadA", 
headersAlice);
+
+        // 2. Valid user for policy B on route B
+        Map<String, Object> headersBob = new HashMap<>();
+        headersBob.put(ShiroSecurityConstants.SHIRO_SECURITY_USERNAME, "bob");
+        headersBob.put(ShiroSecurityConstants.SHIRO_SECURITY_PASSWORD, 
"passwordB");
+        template.sendBodyAndHeaders("direct:routeNoReauthB", "payloadB", 
headersBob);
+
+        // 3. User for policy B should fail on route A even after B succeeded
+        Map<String, Object> headersBobOnA = new HashMap<>();
+        headersBobOnA.put(ShiroSecurityConstants.SHIRO_SECURITY_USERNAME, 
"bob");
+        headersBobOnA.put(ShiroSecurityConstants.SHIRO_SECURITY_PASSWORD, 
"passwordB");
+        template.sendBodyAndHeaders("direct:routeNoReauthA", "payloadA", 
headersBobOnA);
+
+        // 4. User for policy A should fail on route B
+        Map<String, Object> headersAliceOnB = new HashMap<>();
+        headersAliceOnB.put(ShiroSecurityConstants.SHIRO_SECURITY_USERNAME, 
"alice");
+        headersAliceOnB.put(ShiroSecurityConstants.SHIRO_SECURITY_PASSWORD, 
"passwordA");
+        template.sendBodyAndHeaders("direct:routeNoReauthB", "payloadB", 
headersAliceOnB);
+
+        successEndpointNoReauthA.assertIsSatisfied();
+        failureEndpointNoReauthA.assertIsSatisfied();
+        successEndpointNoReauthB.assertIsSatisfied();
+        failureEndpointNoReauthB.assertIsSatisfied();
+    }
+
+    @Test
+    public void testThreadContextCleanupOnFailureWithReauthenticateFalse() 
throws Exception {
+        failureEndpointNoReauthA.expectedMessageCount(1);
+
+        Map<String, Object> invalidHeaders = new HashMap<>();
+        invalidHeaders.put(ShiroSecurityConstants.SHIRO_SECURITY_USERNAME, 
"alice");
+        invalidHeaders.put(ShiroSecurityConstants.SHIRO_SECURITY_PASSWORD, 
"wrongpassword");
+        template.sendBodyAndHeaders("direct:routeNoReauthA", "payloadA", 
invalidHeaders);
+
+        failureEndpointNoReauthA.assertIsSatisfied();
+        assertNull(ThreadContext.getSubject(), "ThreadContext subject should 
be cleaned up on failure");
+        assertNull(ThreadContext.getSecurityManager(), "ThreadContext security 
manager should be cleaned up on failure");
+    }
+
+    @Test
+    public void testRoleAuthorizationIsolation() throws Exception {
+        successEndpointAuthB.expectedMessageCount(1);
+        failureEndpointAuthB.expectedMessageCount(1);
+
+        // 1. Bob has roleB in realm B, passes authorization
+        Map<String, Object> headersBob = new HashMap<>();
+        headersBob.put(ShiroSecurityConstants.SHIRO_SECURITY_USERNAME, "bob");
+        headersBob.put(ShiroSecurityConstants.SHIRO_SECURITY_PASSWORD, 
"passwordB");
+        template.sendBodyAndHeaders("direct:routeAuthB", "payload", 
headersBob);
+
+        // 2. Charlie is in realm B with roleA (from realm A's permission 
model), fails authorization on policy B
+        Map<String, Object> headersCharlie = new HashMap<>();
+        headersCharlie.put(ShiroSecurityConstants.SHIRO_SECURITY_USERNAME, 
"charlie");
+        headersCharlie.put(ShiroSecurityConstants.SHIRO_SECURITY_PASSWORD, 
"passwordB");
+        template.sendBodyAndHeaders("direct:routeAuthB", "payload", 
headersCharlie);
+
+        successEndpointAuthB.assertIsSatisfied();
+        failureEndpointAuthB.assertIsSatisfied();
+    }
+
+    @Test
+    public void testNestedRouteSubjectThreadStateRestoration() throws 
Exception {
+        nestedSuccessEndpoint.expectedMessageCount(1);
+        innerSuccessEndpoint.expectedMessageCount(1);
+
+        Map<String, Object> headersAlice = new HashMap<>();
+        headersAlice.put(ShiroSecurityConstants.SHIRO_SECURITY_USERNAME, 
"alice");
+        headersAlice.put(ShiroSecurityConstants.SHIRO_SECURITY_PASSWORD, 
"passwordA");
+        template.sendBodyAndHeaders("direct:outerRoute", "payload", 
headersAlice);
+
+        nestedSuccessEndpoint.assertIsSatisfied();
+        innerSuccessEndpoint.assertIsSatisfied();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        Ini iniA = new Ini();
+        iniA.setSectionProperty("users", "alice", "passwordA, roleA");
+        policyA = new ShiroSecurityPolicy(iniA, KEY_A);
+        policyNoReauthA = new ShiroSecurityPolicy(iniA, KEY_A, false);
+
+        Ini iniB = new Ini();
+        iniB.setSectionProperty("users", "bob", "passwordB, roleB");
+        iniB.setSectionProperty("users", "charlie", "passwordB, roleA");
+        policyB = new ShiroSecurityPolicy(iniB, KEY_B);
+        policyNoReauthB = new ShiroSecurityPolicy(iniB, KEY_B, false);
+
+        policyAuthB = new ShiroSecurityPolicy(iniB, KEY_B);
+        policyAuthB.setRolesList(List.of("roleB"));
+
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                from("direct:routeA")
+                        .onException(AuthenticationException.class)
+                        .handled(true)
+                        .to("mock:failureA")
+                        .end()
+                        .policy(policyA)
+                        .to("mock:successA");
+
+                from("direct:routeB")
+                        .onException(AuthenticationException.class)
+                        .handled(true)
+                        .to("mock:failureB")
+                        .end()
+                        .policy(policyB)
+                        .to("mock:successB");
+
+                from("direct:routeNoReauthA")
+                        .onException(AuthenticationException.class)
+                        .handled(true)
+                        .to("mock:failureNoReauthA")
+                        .end()
+                        .policy(policyNoReauthA)
+                        .to("mock:successNoReauthA");
+
+                from("direct:routeNoReauthB")
+                        .onException(AuthenticationException.class)
+                        .handled(true)
+                        .to("mock:failureNoReauthB")
+                        .end()
+                        .policy(policyNoReauthB)
+                        .to("mock:successNoReauthB");
+
+                from("direct:routeAuthB")
+                        .onException(CamelAuthorizationException.class)
+                        .handled(true)
+                        .to("mock:failureAuthB")
+                        .end()
+                        .policy(policyAuthB)
+                        .to("mock:successAuthB");
+
+                from("direct:outerRoute")
+                        .policy(policyNoReauthA)
+                        .process(exchange -> {
+                            Subject subject = SecurityUtils.getSubject();
+                            assertEquals("alice", subject.getPrincipal());
+                            assertTrue(subject.isAuthenticated());
+                        })
+                        .to("direct:innerRoute")
+                        .process(exchange -> {
+                            Subject restored = SecurityUtils.getSubject();
+                            assertEquals("alice", restored.getPrincipal(),
+                                    "Outer subject should be restored after 
calling inner route");
+                            assertTrue(restored.isAuthenticated(),
+                                    "Restored subject must still be 
authenticated");
+                        })
+                        .to("mock:nestedSuccess");
+
+                from("direct:innerRoute")
+                        .process(exchange -> {
+                            
exchange.getIn().setHeader(ShiroSecurityConstants.SHIRO_SECURITY_USERNAME, 
"bob");
+                            
exchange.getIn().setHeader(ShiroSecurityConstants.SHIRO_SECURITY_PASSWORD, 
"passwordB");
+                        })
+                        .policy(policyB)
+                        .to("mock:innerSuccess");
+            }
+        };
+    }
+}
diff --git 
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_18.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_18.adoc
index 4cb7a34343d5..768b96916558 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_18.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_18.adoc
@@ -71,6 +71,24 @@ Use `0.0.0.0` as the host to accept connections on all 
network interfaces.
 
 `AvroRpcHttpServerFactory` has a new method `create(SpecificResponder, String, 
int)` that receives the host. A custom factory should implement it.
 
+=== camel-shiro - ShiroSecurityPolicy scopes SecurityManager to policy instance
+
+The `ShiroSecurityPolicy` constructors that load configuration from an INI 
file or object no longer set the resulting
+`SecurityManager` as Shiro's global security manager via 
`SecurityUtils.setSecurityManager(...)`. `ShiroSecurityProcessor`
+now creates and authenticates the caller identity (principal) using the route 
policy's `SecurityManager` directly.
+Applications configuring multiple `ShiroSecurityPolicy` instances in the same 
JVM now have their realms evaluated
+independently per policy.
+
+Code that accesses the ambient security context or calls 
`SecurityUtils.getSecurityManager()` outside the policy and
+previously relied on the policy constructor setting the global security 
manager will now receive
+`UnavailableSecurityManagerException`. Such applications should explicitly 
configure
+`SecurityUtils.setSecurityManager(policy.getSecurityManager())` at startup.
+
+Additionally:
+- A `ShiroSecurityPolicy` created with the no-arg constructor must be 
configured with `setSecurityManager(...)`.
+- When `alwaysReauthenticate=true` (the default), no caller identity stays 
bound to the processing thread after the
+  policy check completes.
+
 == Upgrading from 4.18.4 to 4.18.5
 
 === camel-docling - output path handling

Reply via email to