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 426f41ab35ab CAMEL-24052: Fix model dumpers permanently destroying
EndpointDSL builders on RouteDefinition
426f41ab35ab is described below
commit 426f41ab35ab4cb256f96d5df20c099e10048f0c
Author: Federico Mariani <[email protected]>
AuthorDate: Mon Jul 13 21:35:35 2026 +0200
CAMEL-24052: Fix model dumpers permanently destroying EndpointDSL builders
on RouteDefinition
resolveEndpointDslUris() in all four model dumpers called setUri() on live
FromDefinition and SendDefinition objects before serialization. setUri()
internally calls clear(), which permanently nulls the endpoint builder
fields,
causing Camel to lose pre-configured bean references (e.g. custom S3Client)
and fall back to default unconfigured clients. Fix: save builder references
before setUri() and restore them in a finally block after serialization.
Closes #24662
---
.../org/apache/camel/java/LwModelToJavaDumper.java | 118 +++++++++-------
.../org/apache/camel/xml/LwModelToXMLDumper.java | 35 +++--
.../java/org/apache/camel/xml/jaxb/JaxbHelper.java | 25 ++--
.../camel/xml/jaxb/JaxbModelToXMLDumper.java | 15 ++-
.../org/apache/camel/yaml/LwModelToYAMLDumper.java | 76 ++++++-----
dsl/camel-endpointdsl/pom.xml | 5 +
.../AdviceWithEndpointDslMutationTest.java | 149 +++++++++++++++++++++
7 files changed, 318 insertions(+), 105 deletions(-)
diff --git
a/core/camel-java-io/src/main/java/org/apache/camel/java/LwModelToJavaDumper.java
b/core/camel-java-io/src/main/java/org/apache/camel/java/LwModelToJavaDumper.java
index ef9705f33c16..e2e4131f829d 100644
---
a/core/camel-java-io/src/main/java/org/apache/camel/java/LwModelToJavaDumper.java
+++
b/core/camel-java-io/src/main/java/org/apache/camel/java/LwModelToJavaDumper.java
@@ -16,11 +16,15 @@
*/
package org.apache.camel.java;
+import java.util.ArrayList;
import java.util.Collection;
+import java.util.List;
import java.util.Properties;
import org.apache.camel.CamelContext;
import org.apache.camel.NamedNode;
+import org.apache.camel.builder.EndpointConsumerBuilder;
+import org.apache.camel.builder.EndpointProducerBuilder;
import org.apache.camel.model.FromDefinition;
import org.apache.camel.model.RouteConfigurationDefinition;
import org.apache.camel.model.RouteConfigurationsDefinition;
@@ -60,75 +64,80 @@ public class LwModelToJavaDumper implements
ModelToJavaDumper {
throws Exception {
Properties properties = new Properties();
+ List<Runnable> restorers = new ArrayList<>();
if (definition instanceof RouteTemplatesDefinition templates) {
templates.getRouteTemplates().forEach(template -> {
- resolveEndpointDslUris(template.getRoute());
+ restorers.add(resolveEndpointDslUris(template.getRoute()));
collectTemplateProperties(template, properties);
});
} else if (definition instanceof RouteTemplateDefinition template) {
- resolveEndpointDslUris(template.getRoute());
+ restorers.add(resolveEndpointDslUris(template.getRoute()));
collectTemplateProperties(template, properties);
} else if (definition instanceof RoutesDefinition routes) {
-
routes.getRoutes().forEach(LwModelToJavaDumper::resolveEndpointDslUris);
+ routes.getRoutes().forEach(r ->
restorers.add(resolveEndpointDslUris(r)));
} else if (definition instanceof RouteDefinition route) {
- resolveEndpointDslUris(route);
+ restorers.add(resolveEndpointDslUris(route));
}
org.apache.camel.java.out.JavaDslModelWriter writer = new
org.apache.camel.java.out.JavaDslModelWriter();
StringBuilder sb = new StringBuilder();
- if (definition instanceof RoutesDefinition rd) {
- for (RouteDefinition route : rd.getRoutes()) {
- if (!sb.isEmpty()) {
- sb.append("\n\n");
+ try {
+ if (definition instanceof RoutesDefinition rd) {
+ for (RouteDefinition route : rd.getRoutes()) {
+ if (!sb.isEmpty()) {
+ sb.append("\n\n");
+ }
+ sb.append(writer.writeRouteDefinition(route));
}
+ } else if (definition instanceof RouteDefinition route) {
sb.append(writer.writeRouteDefinition(route));
- }
- } else if (definition instanceof RouteDefinition route) {
- sb.append(writer.writeRouteDefinition(route));
- } else if (definition instanceof RouteTemplatesDefinition rtd) {
- for (RouteTemplateDefinition template : rtd.getRouteTemplates()) {
- if (!sb.isEmpty()) {
- sb.append("\n\n");
+ } else if (definition instanceof RouteTemplatesDefinition rtd) {
+ for (RouteTemplateDefinition template :
rtd.getRouteTemplates()) {
+ if (!sb.isEmpty()) {
+ sb.append("\n\n");
+ }
+ sb.append(writer.writeRouteTemplateDefinition(template));
}
+ } else if (definition instanceof RouteTemplateDefinition template)
{
sb.append(writer.writeRouteTemplateDefinition(template));
- }
- } else if (definition instanceof RouteTemplateDefinition template) {
- sb.append(writer.writeRouteTemplateDefinition(template));
- } else if (definition instanceof RestsDefinition rd) {
- for (RestDefinition rest : rd.getRests()) {
- if (!sb.isEmpty()) {
- sb.append("\n\n");
+ } else if (definition instanceof RestsDefinition rd) {
+ for (RestDefinition rest : rd.getRests()) {
+ if (!sb.isEmpty()) {
+ sb.append("\n\n");
+ }
+ sb.append(writer.writeRestDefinition(rest));
}
+ } else if (definition instanceof RestDefinition rest) {
sb.append(writer.writeRestDefinition(rest));
- }
- } else if (definition instanceof RestDefinition rest) {
- sb.append(writer.writeRestDefinition(rest));
- } else if (definition instanceof RouteConfigurationsDefinition rcd) {
- for (RouteConfigurationDefinition config :
rcd.getRouteConfigurations()) {
- if (!sb.isEmpty()) {
- sb.append("\n\n");
+ } else if (definition instanceof RouteConfigurationsDefinition
rcd) {
+ for (RouteConfigurationDefinition config :
rcd.getRouteConfigurations()) {
+ if (!sb.isEmpty()) {
+ sb.append("\n\n");
+ }
+
sb.append(writer.writeRouteConfigurationDefinition(config));
}
+ } else if (definition instanceof RouteConfigurationDefinition
config) {
sb.append(writer.writeRouteConfigurationDefinition(config));
- }
- } else if (definition instanceof RouteConfigurationDefinition config) {
- sb.append(writer.writeRouteConfigurationDefinition(config));
- } else if (definition instanceof RestConfigurationDefinition
restConfig) {
- sb.append(writer.writeRestConfigurationDefinition(restConfig));
- } else if (definition instanceof TransformersDefinition td) {
- for (TransformerDefinition t : td.getTransformers()) {
- if (!sb.isEmpty()) {
- sb.append("\n\n");
+ } else if (definition instanceof RestConfigurationDefinition
restConfig) {
+ sb.append(writer.writeRestConfigurationDefinition(restConfig));
+ } else if (definition instanceof TransformersDefinition td) {
+ for (TransformerDefinition t : td.getTransformers()) {
+ if (!sb.isEmpty()) {
+ sb.append("\n\n");
+ }
+ sb.append(writer.writeTransformer(t));
}
- sb.append(writer.writeTransformer(t));
- }
- } else if (definition instanceof ValidatorsDefinition vd) {
- for (ValidatorDefinition v : vd.getValidators()) {
- if (!sb.isEmpty()) {
- sb.append("\n\n");
+ } else if (definition instanceof ValidatorsDefinition vd) {
+ for (ValidatorDefinition v : vd.getValidators()) {
+ if (!sb.isEmpty()) {
+ sb.append("\n\n");
+ }
+ sb.append(writer.writeValidator(v));
}
- sb.append(writer.writeValidator(v));
}
+ } finally {
+ restorers.forEach(Runnable::run);
}
String result = sb.toString();
@@ -149,26 +158,35 @@ public class LwModelToJavaDumper implements
ModelToJavaDumper {
}
@SuppressWarnings("rawtypes")
- private static void resolveEndpointDslUris(RouteDefinition route) {
+ private static Runnable resolveEndpointDslUris(RouteDefinition route) {
if (route == null) {
- return;
+ return () -> {
+ };
}
+ List<Runnable> restorers = new ArrayList<>();
FromDefinition from = route.getInput();
if (from != null && from.getEndpointConsumerBuilder() != null) {
- from.setUri(from.getEndpointConsumerBuilder().getRawUri());
+ EndpointConsumerBuilder builder =
from.getEndpointConsumerBuilder();
+ from.setUri(builder.getRawUri());
+ restorers.add(() -> from.setEndpointConsumerBuilder(builder));
}
Collection<SendDefinition> col =
filterTypeInOutputs(route.getOutputs(), SendDefinition.class);
for (SendDefinition<?> to : col) {
if (to.getEndpointProducerBuilder() != null) {
- to.setUri(to.getEndpointProducerBuilder().getRawUri());
+ EndpointProducerBuilder builder =
to.getEndpointProducerBuilder();
+ to.setUri(builder.getRawUri());
+ restorers.add(() -> to.setEndpointProducerBuilder(builder));
}
}
Collection<ToDynamicDefinition> col2 =
filterTypeInOutputs(route.getOutputs(), ToDynamicDefinition.class);
for (ToDynamicDefinition to : col2) {
if (to.getEndpointProducerBuilder() != null) {
- to.setUri(to.getEndpointProducerBuilder().getRawUri());
+ EndpointProducerBuilder builder =
to.getEndpointProducerBuilder();
+ to.setUri(builder.getRawUri());
+ restorers.add(() -> to.setUri(null));
}
}
+ return () -> restorers.forEach(Runnable::run);
}
private static String resolvePlaceholders(CamelContext context, String
text, Properties properties) {
diff --git
a/core/camel-xml-io/src/main/java/org/apache/camel/xml/LwModelToXMLDumper.java
b/core/camel-xml-io/src/main/java/org/apache/camel/xml/LwModelToXMLDumper.java
index eb0edb17e547..11f8aa7cb7a7 100644
---
a/core/camel-xml-io/src/main/java/org/apache/camel/xml/LwModelToXMLDumper.java
+++
b/core/camel-xml-io/src/main/java/org/apache/camel/xml/LwModelToXMLDumper.java
@@ -31,6 +31,8 @@ import org.apache.camel.CamelContext;
import org.apache.camel.CamelContextAware;
import org.apache.camel.Expression;
import org.apache.camel.NamedNode;
+import org.apache.camel.builder.EndpointConsumerBuilder;
+import org.apache.camel.builder.EndpointProducerBuilder;
import org.apache.camel.model.BasicExpressionNode;
import org.apache.camel.model.BeanFactoryDefinition;
import org.apache.camel.model.DataFormatDefinition;
@@ -74,12 +76,13 @@ public class LwModelToXMLDumper implements ModelToXMLDumper
{
Properties properties = new Properties();
Map<String, String> namespaces = new LinkedHashMap<>();
Map<String, KeyValueHolder<Integer, String>> locations = new
HashMap<>();
+ List<Runnable> restorers = new ArrayList<>();
Consumer<RouteDefinition> extractor = route -> {
extractNamespaces(route, namespaces);
if (sourceLocation || context.isDebugging()) {
extractSourceLocations(route, locations);
}
- resolveEndpointDslUris(route);
+ restorers.add(resolveEndpointDslUris(route));
if (Boolean.TRUE.equals(route.isTemplate())) {
Map<String, Object> parameters = route.getTemplateParameters();
if (parameters != null) {
@@ -178,7 +181,11 @@ public class LwModelToXMLDumper implements
ModelToXMLDumper {
} else if (definition instanceof RouteDefinition route) {
extractor.accept(route);
}
-
writer.writeOptionalIdentifiedDefinitionRef((OptionalIdentifiedDefinition)
definition);
+ try {
+
writer.writeOptionalIdentifiedDefinitionRef((OptionalIdentifiedDefinition)
definition);
+ } finally {
+ restorers.forEach(Runnable::run);
+ }
return buffer.toString();
}
@@ -282,29 +289,37 @@ public class LwModelToXMLDumper implements
ModelToXMLDumper {
/**
* If the route has been built with endpoint-dsl, then the model will not
have uri set which then cannot be included
- * in the JAXB model dump
+ * in the JAXB model dump. Returns a {@link Runnable} that restores the
original endpoint DSL builders after the XML
+ * has been written, so that the live {@link RouteDefinition} is not
permanently mutated by the dump.
*/
@SuppressWarnings("rawtypes")
- private static void resolveEndpointDslUris(RouteDefinition route) {
+ private static Runnable resolveEndpointDslUris(RouteDefinition route) {
+ List<Runnable> restorers = new ArrayList<>();
FromDefinition from = route.getInput();
if (from != null && from.getEndpointConsumerBuilder() != null) {
- String uri = from.getEndpointConsumerBuilder().getRawUri();
- from.setUri(uri);
+ EndpointConsumerBuilder builder =
from.getEndpointConsumerBuilder();
+ from.setUri(builder.getRawUri());
+ restorers.add(() -> from.setEndpointConsumerBuilder(builder));
}
Collection<SendDefinition> col =
filterTypeInOutputs(route.getOutputs(), SendDefinition.class);
for (SendDefinition<?> to : col) {
if (to.getEndpointProducerBuilder() != null) {
- String uri = to.getEndpointProducerBuilder().getRawUri();
- to.setUri(uri);
+ EndpointProducerBuilder builder =
to.getEndpointProducerBuilder();
+ to.setUri(builder.getRawUri());
+ restorers.add(() -> to.setEndpointProducerBuilder(builder));
}
}
Collection<ToDynamicDefinition> col2 =
filterTypeInOutputs(route.getOutputs(), ToDynamicDefinition.class);
for (ToDynamicDefinition to : col2) {
if (to.getEndpointProducerBuilder() != null) {
- String uri = to.getEndpointProducerBuilder().getRawUri();
- to.setUri(uri);
+ EndpointProducerBuilder builder =
to.getEndpointProducerBuilder();
+ to.setUri(builder.getRawUri());
+ // ToDynamicDefinition.setUri does not call clear(), so the
builder is preserved;
+ // only the uri field needs to be reset.
+ restorers.add(() -> to.setUri(null));
}
}
+ return () -> restorers.forEach(Runnable::run);
}
private static NamespaceAware
getNamespaceAwareFromExpression(ExpressionNode expressionNode) {
diff --git
a/core/camel-xml-jaxb/src/main/java/org/apache/camel/xml/jaxb/JaxbHelper.java
b/core/camel-xml-jaxb/src/main/java/org/apache/camel/xml/jaxb/JaxbHelper.java
index b6714f4f3b5b..236c95cb9a9a 100644
---
a/core/camel-xml-jaxb/src/main/java/org/apache/camel/xml/jaxb/JaxbHelper.java
+++
b/core/camel-xml-jaxb/src/main/java/org/apache/camel/xml/jaxb/JaxbHelper.java
@@ -41,6 +41,8 @@ import org.apache.camel.CamelContext;
import org.apache.camel.Expression;
import org.apache.camel.NamedNode;
import org.apache.camel.TypeConversionException;
+import org.apache.camel.builder.EndpointConsumerBuilder;
+import org.apache.camel.builder.EndpointProducerBuilder;
import org.apache.camel.converter.jaxp.XmlConverter;
import org.apache.camel.model.BasicExpressionNode;
import org.apache.camel.model.ExpressionNode;
@@ -132,28 +134,35 @@ public final class JaxbHelper {
/**
* If the route has been built with endpoint-dsl, then the model will not
have uri set which then cannot be included
- * in the JAXB model dump
+ * in the JAXB model dump. Returns a {@link Runnable} that restores the
original endpoint DSL builders after
+ * serialization, so that the live {@link RouteDefinition} is not
permanently mutated.
*/
- public static void resolveEndpointDslUris(RouteDefinition route) {
+ @SuppressWarnings("rawtypes")
+ public static Runnable resolveEndpointDslUris(RouteDefinition route) {
+ List<Runnable> restorers = new ArrayList<>();
FromDefinition from = route.getInput();
if (from != null && from.getEndpointConsumerBuilder() != null) {
- String uri = from.getEndpointConsumerBuilder().getRawUri();
- from.setUri(uri);
+ EndpointConsumerBuilder builder =
from.getEndpointConsumerBuilder();
+ from.setUri(builder.getRawUri());
+ restorers.add(() -> from.setEndpointConsumerBuilder(builder));
}
Collection<SendDefinition> col =
filterTypeInOutputs(route.getOutputs(), SendDefinition.class);
for (SendDefinition<?> to : col) {
if (to.getEndpointProducerBuilder() != null) {
- String uri = to.getEndpointProducerBuilder().getRawUri();
- to.setUri(uri);
+ EndpointProducerBuilder builder =
to.getEndpointProducerBuilder();
+ to.setUri(builder.getRawUri());
+ restorers.add(() -> to.setEndpointProducerBuilder(builder));
}
}
Collection<ToDynamicDefinition> col2 =
filterTypeInOutputs(route.getOutputs(), ToDynamicDefinition.class);
for (ToDynamicDefinition to : col2) {
if (to.getEndpointProducerBuilder() != null) {
- String uri = to.getEndpointProducerBuilder().getRawUri();
- to.setUri(uri);
+ EndpointProducerBuilder builder =
to.getEndpointProducerBuilder();
+ to.setUri(builder.getRawUri());
+ restorers.add(() -> to.setUri(null));
}
}
+ return () -> restorers.forEach(Runnable::run);
}
private static NamespaceAware
getNamespaceAwareFromExpression(ExpressionNode expressionNode) {
diff --git
a/core/camel-xml-jaxb/src/main/java/org/apache/camel/xml/jaxb/JaxbModelToXMLDumper.java
b/core/camel-xml-jaxb/src/main/java/org/apache/camel/xml/jaxb/JaxbModelToXMLDumper.java
index 7c8499494932..8d469589b7f4 100644
---
a/core/camel-xml-jaxb/src/main/java/org/apache/camel/xml/jaxb/JaxbModelToXMLDumper.java
+++
b/core/camel-xml-jaxb/src/main/java/org/apache/camel/xml/jaxb/JaxbModelToXMLDumper.java
@@ -85,6 +85,7 @@ public class JaxbModelToXMLDumper implements ModelToXMLDumper
{
final JAXBContext jaxbContext = getJAXBContext(context);
final Map<String, String> namespaces = new LinkedHashMap<>();
final Map<String, KeyValueHolder<Integer, String>> locations = new
HashMap<>();
+ final List<Runnable> restorers = new ArrayList<>();
// gather all namespaces from the routes or route which is stored on
the
// expression nodes
@@ -95,14 +96,14 @@ public class JaxbModelToXMLDumper implements
ModelToXMLDumper {
if (sourceLocation || context.isDebugging()) {
extractSourceLocations(route.getRoute(), locations);
}
- resolveEndpointDslUris(route.getRoute());
+ restorers.add(resolveEndpointDslUris(route.getRoute()));
}
} else if (definition instanceof RouteTemplateDefinition template) {
extractNamespaces(template.getRoute(), namespaces);
if (sourceLocation || context.isDebugging()) {
extractSourceLocations(template.getRoute(), locations);
}
- resolveEndpointDslUris(template.getRoute());
+ restorers.add(resolveEndpointDslUris(template.getRoute()));
} else if (definition instanceof RoutesDefinition routesDefinition) {
List<RouteDefinition> routes = routesDefinition.getRoutes();
for (RouteDefinition route : routes) {
@@ -110,21 +111,25 @@ public class JaxbModelToXMLDumper implements
ModelToXMLDumper {
if (sourceLocation || context.isDebugging()) {
extractSourceLocations(route, locations);
}
- resolveEndpointDslUris(route);
+ restorers.add(resolveEndpointDslUris(route));
}
} else if (definition instanceof RouteDefinition route) {
extractNamespaces(route, namespaces);
if (sourceLocation || context.isDebugging()) {
extractSourceLocations(route, locations);
}
- resolveEndpointDslUris(route);
+ restorers.add(resolveEndpointDslUris(route));
}
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
StringWriter buffer = new StringWriter();
- marshaller.marshal(definition, buffer);
+ try {
+ marshaller.marshal(definition, buffer);
+ } finally {
+ restorers.forEach(Runnable::run);
+ }
XmlConverter xmlConverter = newXmlConverter(context);
String xml = buffer.toString();
diff --git
a/core/camel-yaml-io/src/main/java/org/apache/camel/yaml/LwModelToYAMLDumper.java
b/core/camel-yaml-io/src/main/java/org/apache/camel/yaml/LwModelToYAMLDumper.java
index 21d0ac6830fd..589c62e2713d 100644
---
a/core/camel-yaml-io/src/main/java/org/apache/camel/yaml/LwModelToYAMLDumper.java
+++
b/core/camel-yaml-io/src/main/java/org/apache/camel/yaml/LwModelToYAMLDumper.java
@@ -31,6 +31,8 @@ import org.apache.camel.CamelContext;
import org.apache.camel.CamelContextAware;
import org.apache.camel.Expression;
import org.apache.camel.NamedNode;
+import org.apache.camel.builder.EndpointConsumerBuilder;
+import org.apache.camel.builder.EndpointProducerBuilder;
import org.apache.camel.model.BeanFactoryDefinition;
import org.apache.camel.model.DataFormatDefinition;
import org.apache.camel.model.ExpressionNode;
@@ -76,12 +78,13 @@ public class LwModelToYAMLDumper implements
ModelToYAMLDumper {
Properties properties = new Properties();
Map<String, String> namespaces = new LinkedHashMap<>();
Map<String, KeyValueHolder<Integer, String>> locations = new
HashMap<>();
+ List<Runnable> restorers = new ArrayList<>();
Consumer<RouteDefinition> extractor = route -> {
extractNamespaces(route, namespaces);
if (sourceLocation || context.isDebugging()) {
extractSourceLocations(route, locations);
}
- resolveEndpointDslUris(route);
+ restorers.add(resolveEndpointDslUris(route));
if (Boolean.TRUE.equals(route.isTemplate())) {
Map<String, Object> parameters = route.getTemplateParameters();
if (parameters != null) {
@@ -154,35 +157,39 @@ public class LwModelToYAMLDumper implements
ModelToYAMLDumper {
writer.setCamelContext(context);
List<JsonObject> roots = new ArrayList<>();
- if (definition instanceof RoutesDefinition rd) {
- for (RouteDefinition route : rd.getRoutes()) {
+ try {
+ if (definition instanceof RoutesDefinition rd) {
+ for (RouteDefinition route : rd.getRoutes()) {
+ roots.add(writer.writeRouteDefinition(route));
+ }
+ } else if (definition instanceof RouteDefinition route) {
roots.add(writer.writeRouteDefinition(route));
- }
- } else if (definition instanceof RouteDefinition route) {
- roots.add(writer.writeRouteDefinition(route));
- } else if (definition instanceof RouteTemplatesDefinition rtd) {
- for (RouteTemplateDefinition template : rtd.getRouteTemplates()) {
+ } else if (definition instanceof RouteTemplatesDefinition rtd) {
+ for (RouteTemplateDefinition template :
rtd.getRouteTemplates()) {
+ roots.add(writer.writeRouteTemplateDefinition(template));
+ }
+ } else if (definition instanceof RouteTemplateDefinition template)
{
roots.add(writer.writeRouteTemplateDefinition(template));
- }
- } else if (definition instanceof RouteTemplateDefinition template) {
- roots.add(writer.writeRouteTemplateDefinition(template));
- } else if (definition instanceof RestsDefinition rd) {
- for (RestDefinition rest : rd.getRests()) {
+ } else if (definition instanceof RestsDefinition rd) {
+ for (RestDefinition rest : rd.getRests()) {
+ roots.add(writer.writeRestDefinition(rest));
+ }
+ } else if (definition instanceof RestDefinition rest) {
roots.add(writer.writeRestDefinition(rest));
- }
- } else if (definition instanceof RestDefinition rest) {
- roots.add(writer.writeRestDefinition(rest));
- } else if (definition instanceof RouteConfigurationsDefinition rcd) {
- for (RouteConfigurationDefinition config :
rcd.getRouteConfigurations()) {
+ } else if (definition instanceof RouteConfigurationsDefinition
rcd) {
+ for (RouteConfigurationDefinition config :
rcd.getRouteConfigurations()) {
+
roots.add(writer.writeRouteConfigurationDefinition(config));
+ }
+ } else if (definition instanceof RouteConfigurationDefinition
config) {
roots.add(writer.writeRouteConfigurationDefinition(config));
+ } else {
+ JsonObject jo =
writer.writeOptionalIdentifiedDefinitionRef((OptionalIdentifiedDefinition)
definition);
+ if (jo != null) {
+ roots.add(jo);
+ }
}
- } else if (definition instanceof RouteConfigurationDefinition config) {
- roots.add(writer.writeRouteConfigurationDefinition(config));
- } else {
- JsonObject jo =
writer.writeOptionalIdentifiedDefinitionRef((OptionalIdentifiedDefinition)
definition);
- if (jo != null) {
- roots.add(jo);
- }
+ } finally {
+ restorers.forEach(Runnable::run);
}
return writer.printAsYaml(roots);
@@ -289,26 +296,31 @@ public class LwModelToYAMLDumper implements
ModelToYAMLDumper {
* in the model dump
*/
@SuppressWarnings("rawtypes")
- private static void resolveEndpointDslUris(RouteDefinition route) {
+ private static Runnable resolveEndpointDslUris(RouteDefinition route) {
+ List<Runnable> restorers = new ArrayList<>();
FromDefinition from = route.getInput();
if (from != null && from.getEndpointConsumerBuilder() != null) {
- String uri = from.getEndpointConsumerBuilder().getRawUri();
- from.setUri(uri);
+ EndpointConsumerBuilder builder =
from.getEndpointConsumerBuilder();
+ from.setUri(builder.getRawUri());
+ restorers.add(() -> from.setEndpointConsumerBuilder(builder));
}
Collection<SendDefinition> col =
filterTypeInOutputs(route.getOutputs(), SendDefinition.class);
for (SendDefinition<?> to : col) {
if (to.getEndpointProducerBuilder() != null) {
- String uri = to.getEndpointProducerBuilder().getRawUri();
- to.setUri(uri);
+ EndpointProducerBuilder builder =
to.getEndpointProducerBuilder();
+ to.setUri(builder.getRawUri());
+ restorers.add(() -> to.setEndpointProducerBuilder(builder));
}
}
Collection<ToDynamicDefinition> col2 =
filterTypeInOutputs(route.getOutputs(), ToDynamicDefinition.class);
for (ToDynamicDefinition to : col2) {
if (to.getEndpointProducerBuilder() != null) {
- String uri = to.getEndpointProducerBuilder().getRawUri();
- to.setUri(uri);
+ EndpointProducerBuilder builder =
to.getEndpointProducerBuilder();
+ to.setUri(builder.getRawUri());
+ restorers.add(() -> to.setUri(null));
}
}
+ return () -> restorers.forEach(Runnable::run);
}
private static NamespaceAware
getNamespaceAwareFromExpression(ExpressionNode expressionNode) {
diff --git a/dsl/camel-endpointdsl/pom.xml b/dsl/camel-endpointdsl/pom.xml
index 466b21e1944a..a61afe43ca2b 100644
--- a/dsl/camel-endpointdsl/pom.xml
+++ b/dsl/camel-endpointdsl/pom.xml
@@ -140,6 +140,11 @@
<artifactId>camel-test-junit6</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-xml-io</artifactId>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-xml-jaxb</artifactId>
diff --git
a/dsl/camel-endpointdsl/src/test/java/org/apache/camel/builder/endpoint/AdviceWithEndpointDslMutationTest.java
b/dsl/camel-endpointdsl/src/test/java/org/apache/camel/builder/endpoint/AdviceWithEndpointDslMutationTest.java
new file mode 100644
index 000000000000..1674924d01aa
--- /dev/null
+++
b/dsl/camel-endpointdsl/src/test/java/org/apache/camel/builder/endpoint/AdviceWithEndpointDslMutationTest.java
@@ -0,0 +1,149 @@
+/*
+ * 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.builder.endpoint;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.AdviceWith;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.model.FromDefinition;
+import org.apache.camel.model.RouteDefinition;
+import org.apache.camel.model.SendDefinition;
+import org.apache.camel.model.ToDefinition;
+import org.apache.camel.spi.ExceptionHandler;
+import org.apache.camel.spi.ModelToXMLDumper;
+import org.apache.camel.support.PluginHelper;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+/**
+ * Verifies that calling {@code adviceWith()} with {@code logXml=true} (the
default) does not permanently mutate the
+ * live {@link RouteDefinition} by destroying the {@code
EndpointConsumerBuilder}/{@code EndpointProducerBuilder}
+ * references on {@link FromDefinition}/{@link SendDefinition}.
+ *
+ * <p>
+ * Before the fix, {@code LwModelToXMLDumper.resolveEndpointDslUris()} called
{@code from.setUri()} which internally
+ * calls {@code clear()}, permanently nulling the {@code
endpointConsumerBuilder} and replacing it with a plain URI
+ * string that may contain only a {@code hash=XXXXXXXX} parameter for
non-primitive endpoint properties. This caused
+ * Camel to ignore pre-configured beans (e.g. AWS S3 clients) and fall back to
default (unconfigured) clients.
+ */
+public class AdviceWithEndpointDslMutationTest extends BaseEndpointDslTest {
+
+ private final ExceptionHandler myExceptionHandler = new ExceptionHandler()
{
+ @Override
+ public void handleException(Throwable exception) {
+ }
+
+ @Override
+ public void handleException(String message, Throwable exception) {
+ }
+
+ @Override
+ public void handleException(String message, Exchange exchange,
Throwable exception) {
+ }
+ };
+
+ @Test
+ public void testDumpModelAsXmlDoesNotDestroyEndpointConsumerBuilder()
throws Exception {
+ RouteDefinition route = context.getRouteDefinitions().get(0);
+ FromDefinition from = route.getInput();
+
+ // before dump: the builder must be set (not yet resolved to a plain
URI)
+ assertNotNull(from.getEndpointConsumerBuilder(),
+ "endpointConsumerBuilder should be set before dumpModelAsXml");
+ assertNull(from.getUri(),
+ "uri should be null before dumpModelAsXml (endpoint is held as
a builder)");
+
+ // directly invoke the XML dumper — this is what adviceWith calls
internally for logging
+ ModelToXMLDumper dumper =
PluginHelper.getModelToXMLDumper(context.getCamelContextExtension());
+ assertNotNull(dumper, "LwModelToXMLDumper must be on the classpath");
+ dumper.dumpModelAsXml(context, route);
+
+ // after dump: the builder must still be intact — the dump must be
non-destructive
+ assertNotNull(from.getEndpointConsumerBuilder(),
+ "endpointConsumerBuilder must survive dumpModelAsXml — it was
permanently destroyed before the fix");
+ assertNull(from.getUri(),
+ "uri must remain null after dumpModelAsXml — it was set to a
'hash=XXXX' URI before the fix");
+ }
+
+ @Test
+ public void testAdviceWithDoesNotDestroyEndpointConsumerBuilder() throws
Exception {
+ RouteDefinition route = context.getRouteDefinitions().get(0);
+
+ // before adviceWith: the builder must be set (not yet resolved to a
plain URI)
+ FromDefinition from = route.getInput();
+ assertNotNull(from.getEndpointConsumerBuilder(),
+ "endpointConsumerBuilder should be set before adviceWith");
+ assertNull(from.getUri(),
+ "uri should be null before adviceWith (endpoint is held as a
builder)");
+
+ // adviceWith with logXml=true (the default) — this triggers the XML
dump that used to mutate the route
+ AdviceWith.adviceWith(context, "test-route", a ->
a.weaveAddLast().to("mock:advised"));
+
+ // after adviceWith: the builder must still be intact
+ assertNotNull(from.getEndpointConsumerBuilder(),
+ "endpointConsumerBuilder must survive adviceWith — it was
permanently destroyed before the fix");
+ assertNull(from.getUri(),
+ "uri must remain null after adviceWith — it was set to a
'hash=XXXX' URI before the fix");
+ }
+
+ @Test
+ public void testAdviceWithDoesNotDestroyEndpointProducerBuilder() throws
Exception {
+ RouteDefinition route = context.getRouteDefinitions().get(0);
+
+ // find the seda("sink") ToDefinition — built via EndpointDSL, so
endpointProducerBuilder is set
+ ToDefinition toDef = route.getOutputs().stream()
+ .filter(ToDefinition.class::isInstance)
+ .map(ToDefinition.class::cast)
+ .filter(d -> d.getEndpointProducerBuilder() != null)
+ .findFirst()
+ .orElseThrow(() -> new AssertionError("No ToDefinition with an
endpointProducerBuilder found"));
+
+ assertNotNull(toDef.getEndpointProducerBuilder(),
+ "endpointProducerBuilder should be set before adviceWith");
+ assertNull(toDef.getUri(),
+ "uri should be null before adviceWith");
+
+ AdviceWith.adviceWith(context, "test-route", a ->
a.weaveAddLast().to("mock:advised2"));
+
+ assertNotNull(toDef.getEndpointProducerBuilder(),
+ "endpointProducerBuilder must survive adviceWith — it was
permanently destroyed before the fix");
+ assertNull(toDef.getUri(),
+ "uri must remain null after adviceWith");
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new EndpointRouteBuilder() {
+ @Override
+ public void configure() {
+ // Use a non-primitive ExceptionHandler property so that
getRawUri() without a CamelContext
+ // would produce "timer://tick?hash=XXXXXXXX" — reproducing
the case where the URI carries
+ // only a hash parameter and the original bean reference is
lost after mutation.
+ from(timer("tick")
+ .period(10_000)
+ .repeatCount(1)
+ .advanced().exceptionHandler(myExceptionHandler))
+ .routeId("test-route")
+ .autoStartup(false)
+ .to(seda("sink"))
+ .to("mock:result");
+ }
+ };
+ }
+}