You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2026/07/17 08:58:00 UTC

(camel) branch fix/CAMEL-24150 created (now 45ab56b132c0)

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

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


      at 45ab56b132c0 CAMEL-24150: Do not stop shared saga service from individual route processor

This branch includes the following new commits:

     new 45ab56b132c0 CAMEL-24150: Do not stop shared saga service from individual route processor

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



(camel) 01/01: CAMEL-24150: Do not stop shared saga service from individual route processor

Posted by da...@apache.org.
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 <cl...@gmail.com>
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 <no...@anthropic.com>
    Signed-off-by: Claus Ibsen <cl...@gmail.com>
---
 .../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");
+            }
+        };
+    }
+}