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

davsclaus pushed a commit to branch fix/CAMEL-24150
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 45ab56b132c019feba0325b4b8a43930520d6046
Author: Claus Ibsen <[email protected]>
AuthorDate: Fri Jul 17 10:57:45 2026 +0200

    CAMEL-24150: Do not stop shared saga service from individual route processor
    
    The SagaProcessor.doStop() was calling ServiceHelper.stopService() on
    the context-level CamelSagaService. Since this service is shared across
    all saga routes, stopping one route would shut down the saga service
    for the entire CamelContext, breaking compensation on all other routes.
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../apache/camel/processor/saga/SagaProcessor.java | 12 ---
 .../processor/SagaSharedServiceRouteStopTest.java  | 86 ++++++++++++++++++++++
 2 files changed, 86 insertions(+), 12 deletions(-)

diff --git 
a/core/camel-core-processor/src/main/java/org/apache/camel/processor/saga/SagaProcessor.java
 
b/core/camel-core-processor/src/main/java/org/apache/camel/processor/saga/SagaProcessor.java
index d88c5965dc40..6f4e85e90fb5 100644
--- 
a/core/camel-core-processor/src/main/java/org/apache/camel/processor/saga/SagaProcessor.java
+++ 
b/core/camel-core-processor/src/main/java/org/apache/camel/processor/saga/SagaProcessor.java
@@ -30,7 +30,6 @@ import org.apache.camel.saga.CamelSagaStep;
 import org.apache.camel.spi.IdAware;
 import org.apache.camel.spi.RouteIdAware;
 import org.apache.camel.spi.StepIdAware;
-import org.apache.camel.support.service.ServiceHelper;
 import org.apache.camel.util.ObjectHelper;
 
 /**
@@ -171,15 +170,4 @@ public abstract class SagaProcessor extends 
BaseDelegateProcessorSupport
         }
     }
 
-    @Override
-    protected void doStart() throws Exception {
-        super.doStart();
-        ServiceHelper.startService(sagaService);
-    }
-
-    @Override
-    protected void doStop() throws Exception {
-        super.doStop();
-        ServiceHelper.stopService(sagaService);
-    }
 }
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/processor/SagaSharedServiceRouteStopTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/processor/SagaSharedServiceRouteStopTest.java
new file mode 100644
index 000000000000..0eb76a7fa2e7
--- /dev/null
+++ 
b/core/camel-core/src/test/java/org/apache/camel/processor/SagaSharedServiceRouteStopTest.java
@@ -0,0 +1,86 @@
+/*
+ * 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.processor;
+
+import java.util.concurrent.TimeUnit;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.saga.InMemorySagaService;
+import org.apache.camel.support.service.ServiceHelper;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class SagaSharedServiceRouteStopTest extends ContextTestSupport {
+
+    @Test
+    public void testStoppingOneSagaRouteKeepsSharedServiceRunning() throws 
Exception {
+        InMemorySagaService sagaService = 
context.hasService(InMemorySagaService.class);
+
+        assertTrue(ServiceHelper.isStarted(sagaService), "Saga service should 
be running");
+
+        context.getRouteController().stopRoute("route-a");
+
+        assertTrue(ServiceHelper.isStarted(sagaService),
+                "Saga service should still be running after stopping one 
route");
+    }
+
+    @Test
+    public void 
testCompensationOnOtherRouteStillWorksAfterStoppingOneSagaRoute() throws 
Exception {
+        MockEndpoint compensated = getMockEndpoint("mock:compensate-b");
+        compensated.expectedMessageCount(1);
+
+        context.getRouteController().stopRoute("route-a");
+
+        try {
+            template.sendBody("direct:route-b", "trigger-fail");
+        } catch (Exception e) {
+            // expected — the saga processor throws after compensation is 
triggered
+        }
+
+        MockEndpoint.assertIsSatisfied(context, 10, TimeUnit.SECONDS);
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                InMemorySagaService sagaService = new InMemorySagaService();
+                context.addService(sagaService);
+
+                from("direct:route-a").routeId("route-a")
+                        .saga().compensation("direct:compensate-a")
+                        .log("Route A");
+
+                from("direct:compensate-a")
+                        .log("Compensating A");
+
+                from("direct:route-b").routeId("route-b")
+                        .saga().compensation("direct:compensate-b")
+                        .process(e -> {
+                            throw new RuntimeException("forced failure");
+                        });
+
+                from("direct:compensate-b")
+                        .to("mock:compensate-b");
+            }
+        };
+    }
+}

Reply via email to