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

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

commit 8006b8b3a265a7db1b530c99b4ce0a03c4dbdbb4
Author: Luigi De Masi <[email protected]>
AuthorDate: Wed Sep 30 12:56:25 2026 +0200

    CAMEL-24988: Restore explicit Switch destination preparation
    
    Keep the model getters side-effect-free and prepare endpoint nodes during 
reification. Restore regression coverage for stable prepared nodes and 
independent copies.
    
    Co-authored-by: Codex <[email protected]>
    Signed-off-by: Luigi De Masi <[email protected]>
---
 .../apache/camel/model/SwitchCaseDefinition.java   |  9 ++-
 .../org/apache/camel/model/SwitchDefinition.java   | 32 +++++++----
 .../camel/model/SwitchOtherwiseDefinition.java     | 22 ++++++-
 .../apache/camel/model/SwitchDefinitionTest.java   | 67 ++++++++++++++++++++++
 .../org/apache/camel/reifier/SwitchReifier.java    |  2 +
 5 files changed, 119 insertions(+), 13 deletions(-)

diff --git 
a/core/camel-core-model/src/main/java/org/apache/camel/model/SwitchCaseDefinition.java
 
b/core/camel-core-model/src/main/java/org/apache/camel/model/SwitchCaseDefinition.java
index f875ec051401..fbbabe0c9845 100644
--- 
a/core/camel-core-model/src/main/java/org/apache/camel/model/SwitchCaseDefinition.java
+++ 
b/core/camel-core-model/src/main/java/org/apache/camel/model/SwitchCaseDefinition.java
@@ -78,9 +78,15 @@ public class SwitchCaseDefinition extends 
OptionalIdentifiedDefinition<SwitchCas
     /** The destination node shared by reification and management 
instrumentation. */
     @XmlTransient
     public ToDefinition getToDefinition() {
+        return toDefinition;
+    }
+
+    /** Prepare the destination node before processor creation and management 
instrumentation. */
+    public void prepareToDefinition() {
         if (toDefinition == null) {
-            toDefinition = new ToDefinition(uri);
+            toDefinition = new ToDefinition();
         }
+        toDefinition.setUri(uri);
         toDefinition.setParent(parent);
         if (hasCustomIdAssigned()) {
             toDefinition.setId(getId());
@@ -91,7 +97,6 @@ public class SwitchCaseDefinition extends 
OptionalIdentifiedDefinition<SwitchCas
         toDefinition.setNote(getNote());
         toDefinition.setLocation(getLocation());
         toDefinition.setLineNumber(getLineNumber());
-        return toDefinition;
     }
 
     @Override
diff --git 
a/core/camel-core-model/src/main/java/org/apache/camel/model/SwitchDefinition.java
 
b/core/camel-core-model/src/main/java/org/apache/camel/model/SwitchDefinition.java
index 61b56043cd93..944d6e059e50 100644
--- 
a/core/camel-core-model/src/main/java/org/apache/camel/model/SwitchDefinition.java
+++ 
b/core/camel-core-model/src/main/java/org/apache/camel/model/SwitchDefinition.java
@@ -49,8 +49,6 @@ public class SwitchDefinition extends 
NoOutputDefinition<SwitchDefinition> {
     @XmlElement
     @Metadata(description = "Fixed fallback URI for null or unmatched selector 
results. Without a fallback processing continues.")
     private SwitchOtherwiseDefinition otherwise;
-    @XmlTransient
-    private ToDefinition otherwiseDefinition;
 
     public SwitchDefinition() {
     }
@@ -62,7 +60,7 @@ public class SwitchDefinition extends 
NoOutputDefinition<SwitchDefinition> {
     protected SwitchDefinition(SwitchDefinition source) {
         super(source);
         this.selector = source.selector != null ? 
source.selector.copyDefinition() : null;
-        this.otherwise = source.otherwise != null ? 
source.otherwise.copyDefinition() : null;
+        setOtherwise(source.otherwise != null ? 
source.otherwise.copyDefinition() : null);
         for (SwitchCaseDefinition c : source.cases) {
             SwitchCaseDefinition copy = c.copyDefinition();
             copy.setParent(this);
@@ -106,19 +104,34 @@ public class SwitchDefinition extends 
NoOutputDefinition<SwitchDefinition> {
 
     public void setOtherwise(SwitchOtherwiseDefinition otherwise) {
         this.otherwise = otherwise;
-        this.otherwiseDefinition = null;
+        prepareOtherwiseDefinition();
+    }
+
+    @Override
+    public void setId(String id) {
+        super.setId(id);
+        prepareOtherwiseDefinition();
+    }
+
+    @Override
+    public void setGeneratedId(String id) {
+        super.setGeneratedId(id);
+        prepareOtherwiseDefinition();
     }
 
     /** The fallback send node, used by route traversal and processor 
creation. */
     @XmlTransient
     public ToDefinition getOtherwiseDefinition() {
-        if (otherwise == null) {
-            return null;
-        }
+        return otherwise != null ? otherwise.getToDefinition() : null;
+    }
+
+    /** Prepare the fallback node before processor creation and management 
instrumentation. */
+    public void prepareOtherwiseDefinition() {
+        ToDefinition otherwiseDefinition = getOtherwiseDefinition();
         if (otherwiseDefinition == null) {
-            otherwiseDefinition = new ToDefinition();
-            otherwiseDefinition.setParent(this);
+            return;
         }
+        otherwiseDefinition.setParent(this);
         otherwiseDefinition.setUri(otherwise.getUri());
         if (getId() != null) {
             if (hasCustomIdAssigned()) {
@@ -127,7 +140,6 @@ public class SwitchDefinition extends 
NoOutputDefinition<SwitchDefinition> {
                 otherwiseDefinition.setGeneratedId(getId() + "-otherwise");
             }
         }
-        return otherwiseDefinition;
     }
 
     /** Add a scalar literal and its destination. */
diff --git 
a/core/camel-core-model/src/main/java/org/apache/camel/model/SwitchOtherwiseDefinition.java
 
b/core/camel-core-model/src/main/java/org/apache/camel/model/SwitchOtherwiseDefinition.java
index 0c5e79d282bd..d5cd511b2089 100644
--- 
a/core/camel-core-model/src/main/java/org/apache/camel/model/SwitchOtherwiseDefinition.java
+++ 
b/core/camel-core-model/src/main/java/org/apache/camel/model/SwitchOtherwiseDefinition.java
@@ -16,10 +16,12 @@
  */
 package org.apache.camel.model;
 
+import jakarta.xml.bind.Unmarshaller;
 import jakarta.xml.bind.annotation.XmlAccessType;
 import jakarta.xml.bind.annotation.XmlAccessorType;
 import jakarta.xml.bind.annotation.XmlAttribute;
 import jakarta.xml.bind.annotation.XmlRootElement;
+import jakarta.xml.bind.annotation.XmlTransient;
 
 import org.apache.camel.spi.Metadata;
 
@@ -31,10 +33,12 @@ public class SwitchOtherwiseDefinition implements 
EndpointRequiredDefinition {
     @XmlAttribute(required = true)
     @Metadata(description = "The fixed destination URI. Supports property 
placeholders, but not Simple expressions.")
     private String uri;
+    @XmlTransient
+    private final ToDefinition toDefinition = new ToDefinition();
 
     public SwitchOtherwiseDefinition copyDefinition() {
         SwitchOtherwiseDefinition copy = new SwitchOtherwiseDefinition();
-        copy.uri = uri;
+        copy.setUri(uri);
         return copy;
     }
 
@@ -44,9 +48,25 @@ public class SwitchOtherwiseDefinition implements 
EndpointRequiredDefinition {
 
     public void setUri(String uri) {
         this.uri = uri;
+        toDefinition.setUri(uri);
+    }
+
+    /** The destination node, available for endpoint discovery before route 
startup. */
+    @XmlTransient
+    public ToDefinition getToDefinition() {
+        return toDefinition;
+    }
+
+    // JAXB populates fields directly, so initialize the traversal node after 
loading.
+    private void afterUnmarshal(Unmarshaller unmarshaller, Object parent) {
+        toDefinition.setUri(uri);
+        if (parent instanceof SwitchDefinition sw) {
+            sw.setOtherwise(this);
+        }
     }
 
     @Override
+    @XmlTransient
     public String getEndpointUri() {
         return uri;
     }
diff --git 
a/core/camel-core-model/src/test/java/org/apache/camel/model/SwitchDefinitionTest.java
 
b/core/camel-core-model/src/test/java/org/apache/camel/model/SwitchDefinitionTest.java
new file mode 100644
index 000000000000..715e1a31075d
--- /dev/null
+++ 
b/core/camel-core-model/src/test/java/org/apache/camel/model/SwitchDefinitionTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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.model;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotSame;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+
+class SwitchDefinitionTest {
+    @Test
+    void readingDestinationsDoesNotOverwritePreparedNodes() {
+        SwitchDefinition sw = new SwitchDefinition().id("dispatch")
+                .doCase("billing", 
"{{case.uri}}").otherwise("{{fallback.uri}}");
+        SwitchCaseDefinition c = sw.getCases().get(0);
+        c.setId("billing");
+        assertNull(c.getToDefinition());
+        sw.getChildren();
+        assertNull(c.getToDefinition());
+
+        c.prepareToDefinition();
+        sw.prepareOtherwiseDefinition();
+        ToDefinition caseSend = c.getToDefinition();
+        ToDefinition fallbackSend = sw.getOtherwiseDefinition();
+        caseSend.setUri("mock:billing");
+        caseSend.setId("prepared-billing");
+        fallbackSend.setUri("mock:review");
+        fallbackSend.setId("prepared-fallback");
+
+        assertSame(caseSend, c.getToDefinition());
+        assertSame(fallbackSend, sw.getOtherwiseDefinition());
+        assertSame(fallbackSend, sw.getChildren().get(1));
+        assertEquals("mock:billing", caseSend.getUri());
+        assertEquals("prepared-billing", caseSend.getId());
+        assertEquals("mock:review", fallbackSend.getUri());
+        assertEquals("prepared-fallback", fallbackSend.getId());
+    }
+
+    @Test
+    void copiedFallbackHasIndependentDestinationAndParent() {
+        SwitchDefinition original = new 
SwitchDefinition().otherwise("mock:original");
+        SwitchDefinition copy = original.copyDefinition();
+        copy.getOtherwise().setUri("mock:copy");
+
+        assertNotSame(original.getOtherwiseDefinition(), 
copy.getOtherwiseDefinition());
+        assertSame(original, original.getOtherwiseDefinition().getParent());
+        assertSame(copy, copy.getOtherwiseDefinition().getParent());
+        assertEquals("mock:original", 
original.getOtherwiseDefinition().getUri());
+        assertEquals("mock:copy", copy.getOtherwiseDefinition().getUri());
+    }
+}
diff --git 
a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/SwitchReifier.java
 
b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/SwitchReifier.java
index d5d1caef8d0f..77adf6751f4c 100644
--- 
a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/SwitchReifier.java
+++ 
b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/SwitchReifier.java
@@ -67,6 +67,7 @@ public class SwitchReifier extends 
ProcessorReifier<SwitchDefinition> {
             c.setParent(definition);
             c.setCamelContext(camelContext);
             c.idOrCreate(ids);
+            c.prepareToDefinition();
             ToDefinition send = c.getToDefinition();
             send.setUri(uris.get(i));
             cases.put(caseKeys.get(i), createSend(send));
@@ -74,6 +75,7 @@ public class SwitchReifier extends 
ProcessorReifier<SwitchDefinition> {
         Processor otherwise = null;
         if (otherwiseUri != null) {
             definition.idOrCreate(ids);
+            definition.prepareOtherwiseDefinition();
             ToDefinition send = definition.getOtherwiseDefinition();
             send.setUri(otherwiseUri);
             otherwise = createSend(send);

Reply via email to