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 3320dcdc37a1 CAMEL-25255: camel-yaml-io / camel-java-io - model
writers write rests, route templates, beans and route error handlers as their
DSL loads them (#27254)
3320dcdc37a1 is described below
commit 3320dcdc37a163e5fd3e71cb7aea2cbca3337c1e
Author: Claus Ibsen <[email protected]>
AuthorDate: Fri Oct 2 09:28:37 2026 +0200
CAMEL-25255: camel-yaml-io / camel-java-io - model writers write rests,
route templates, beans and route error handlers as their DSL loads them (#27254)
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
---
.../apache/camel/model/ResequenceDefinition.java | 3 +-
.../org/apache/camel/model/RouteDefinition.java | 6 +
.../camel/java/out/JavaDslModelWriterSupport.java | 81 +++++---
.../org/apache/camel/java/in/RoundTripTest.java | 53 +++++
.../apache/camel/java/out/JavaDslCompileTest.java | 1 +
.../org/apache/camel/yaml/out/YamlModelWriter.java | 16 +-
.../org/apache/camel/yaml/LwModelToYAMLDumper.java | 73 ++-----
.../camel/yaml/out/YamlModelWriterSupport.java | 32 +++
.../apache/camel/yaml/out/YamlBeansDumpTest.java | 74 +++++++
.../src/test/resources/yaml-beans-with-route.yaml | 4 +-
.../src/test/resources/yaml-beans.yaml | 7 +-
.../src/test/resources/yaml-rest.yaml | 3 +-
.../src/test/resources/yaml-route-template.yaml | 2 +-
.../ROOT/pages/camel-4x-upgrade-guide-4_23.adoc | 20 ++
.../src/test/resources/blueprint-out.yaml | 2 +-
.../validator/YamlRestTemplateRoundTripTest.java | 221 +++++++++++++++++++++
.../main/resources/velocity/model-yaml-writer.vm | 19 +-
17 files changed, 515 insertions(+), 102 deletions(-)
diff --git
a/core/camel-core-model/src/main/java/org/apache/camel/model/ResequenceDefinition.java
b/core/camel-core-model/src/main/java/org/apache/camel/model/ResequenceDefinition.java
index a66234e573c5..ab7c64b3af12 100644
---
a/core/camel-core-model/src/main/java/org/apache/camel/model/ResequenceDefinition.java
+++
b/core/camel-core-model/src/main/java/org/apache/camel/model/ResequenceDefinition.java
@@ -147,7 +147,8 @@ public class ResequenceDefinition extends
OutputDefinition<ResequenceDefinition>
* @return the builder
*/
public ResequenceDefinition timeout(long timeout) {
- return timeout(Duration.ofMillis(timeout));
+ // the millis as given (not 2s0ms for 2000), so the model reads as the
route was written
+ return timeout(String.valueOf(timeout));
}
/**
diff --git
a/core/camel-core-model/src/main/java/org/apache/camel/model/RouteDefinition.java
b/core/camel-core-model/src/main/java/org/apache/camel/model/RouteDefinition.java
index 3193295489d7..3e94918b6868 100644
---
a/core/camel-core-model/src/main/java/org/apache/camel/model/RouteDefinition.java
+++
b/core/camel-core-model/src/main/java/org/apache/camel/model/RouteDefinition.java
@@ -580,6 +580,12 @@ public class RouteDefinition extends
OutputDefinition<RouteDefinition>
*/
public RouteDefinition errorHandler(ErrorHandlerFactory
errorHandlerBuilder) {
setErrorHandlerFactory(errorHandlerBuilder);
+ // kept in the model as well (as the xml and yaml dsl have it), so
dumping the route writes its error handler:
+ // as its definition (deadLetterChannel() gives a
DeadLetterChannelBuilder, which the model writers do not know)
+ ErrorHandlerDefinition def = new ErrorHandlerDefinition();
+ def.setErrorHandlerType(errorHandlerBuilder instanceof
CopyableDefinition<?> copyable
+ && copyable.copyDefinition() instanceof ErrorHandlerFactory
copy ? copy : errorHandlerBuilder);
+ this.errorHandler = def;
// we are now using a route scoped error handler
contextScopedErrorHandler = false;
return this;
diff --git
a/core/camel-java-io/src/main/java/org/apache/camel/java/out/JavaDslModelWriterSupport.java
b/core/camel-java-io/src/main/java/org/apache/camel/java/out/JavaDslModelWriterSupport.java
index 43a77f1eaba4..1a83c60b8693 100644
---
a/core/camel-java-io/src/main/java/org/apache/camel/java/out/JavaDslModelWriterSupport.java
+++
b/core/camel-java-io/src/main/java/org/apache/camel/java/out/JavaDslModelWriterSupport.java
@@ -24,9 +24,11 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.StringJoiner;
import java.util.function.BiConsumer;
import org.apache.camel.builder.ExpressionClause;
+import org.apache.camel.builder.LanguageBuilderFactory;
import org.apache.camel.model.BeanDefinition;
import org.apache.camel.model.BeanFactoryDefinition;
import org.apache.camel.model.ConvertBodyDefinition;
@@ -81,7 +83,9 @@ import org.apache.camel.model.language.ExpressionDefinition;
import org.apache.camel.model.language.HeaderExpression;
import org.apache.camel.model.language.JqExpression;
import org.apache.camel.model.language.JsonPathExpression;
+import org.apache.camel.model.language.LanguageExpression;
import org.apache.camel.model.language.MethodCallExpression;
+import org.apache.camel.model.language.NamespaceAwareExpression;
import org.apache.camel.model.language.RefExpression;
import org.apache.camel.model.language.SimpleExpression;
import org.apache.camel.model.language.SingleInputTypedExpressionDefinition;
@@ -262,12 +266,6 @@ public abstract class JavaDslModelWriterSupport {
}
}
- // extract inlined error handler — RouteBuilder-level in Java DSL
- if (def.getErrorHandler() != null) {
- writeErrorHandler(sb, def.getErrorHandler());
- handledAttributes.add("errorHandler");
- }
-
if (def.getInput() != null) {
sb.append("from(").append(quote(def.getInput().getUri())).append(")");
if (sourceLocation) {
@@ -293,6 +291,12 @@ public abstract class JavaDslModelWriterSupport {
handledAttributes.add("routeProperty");
handledAttributes.add("routeProperties");
}
+ // the error handler of the route only: errorHandler() of the route
builder would apply to all its routes
+ if (def.getErrorHandler() != null) {
+
sb.append(NL).append(indent()).append(".errorHandler(").append(errorHandlerDsl(def.getErrorHandler()))
+ .append(")");
+ }
+ handledAttributes.add("errorHandler");
indentLevel--;
doWriteRouteDefinition(sb, def);
indentLevel++;
@@ -363,6 +367,10 @@ public abstract class JavaDslModelWriterSupport {
}
handledAttributes.add("routeProperty");
handledAttributes.add("routeProperties");
+ if (route.getErrorHandler() != null) {
+ sb.append(NL).append(indent()).append(".errorHandler(")
+
.append(errorHandlerDsl(route.getErrorHandler())).append(")");
+ }
handledAttributes.add("errorHandler");
indentLevel--;
doWriteRouteDefinition(sb, route);
@@ -482,18 +490,7 @@ public abstract class JavaDslModelWriterSupport {
}
private void writeChainedErrorHandler(StringBuilder sb,
ErrorHandlerDefinition errorHandler) {
- if (errorHandler.getErrorHandlerType() instanceof
DeadLetterChannelDefinition dlc) {
- sb.append(NL).append(indent()).append("
.errorHandler(deadLetterChannel(")
- .append(quote(dlc.getDeadLetterUri())).append(")");
- appendErrorHandlerOptions(sb, dlc);
- sb.append(")");
- } else if (errorHandler.getErrorHandlerType() instanceof
DefaultErrorHandlerDefinition deh) {
- sb.append(NL).append(indent()).append("
.errorHandler(defaultErrorHandler()");
- appendErrorHandlerOptions(sb, deh);
- sb.append(")");
- } else {
- sb.append(NL).append(indent()).append("
.errorHandler(noErrorHandler())");
- }
+ sb.append(NL).append(indent()).append("
.errorHandler(").append(errorHandlerDsl(errorHandler)).append(")");
}
private void writeTemplateParameter(StringBuilder sb,
RouteTemplateParameterDefinition param) {
@@ -1225,18 +1222,19 @@ public abstract class JavaDslModelWriterSupport {
sb.append(";").append(NL).append(NL);
}
- private void writeErrorHandler(StringBuilder sb, ErrorHandlerDefinition
errorHandler) {
+ /** deadLetterChannel("uri").maximumRedeliveries(3), defaultErrorHandler()
or noErrorHandler(). */
+ private String errorHandlerDsl(ErrorHandlerDefinition errorHandler) {
+ StringBuilder sb = new StringBuilder();
if (errorHandler.getErrorHandlerType() instanceof
DeadLetterChannelDefinition dlc) {
-
sb.append("errorHandler(deadLetterChannel(").append(quote(dlc.getDeadLetterUri())).append(")");
+
sb.append("deadLetterChannel(").append(quote(dlc.getDeadLetterUri())).append(")");
appendErrorHandlerOptions(sb, dlc);
- sb.append(");").append(NL).append(NL);
} else if (errorHandler.getErrorHandlerType() instanceof
DefaultErrorHandlerDefinition deh) {
- sb.append("errorHandler(defaultErrorHandler()");
+ sb.append("defaultErrorHandler()");
appendErrorHandlerOptions(sb, deh);
- sb.append(");").append(NL).append(NL);
} else {
- sb.append("errorHandler(noErrorHandler());").append(NL).append(NL);
+ sb.append("noErrorHandler()");
}
+ return sb.toString();
}
private void appendErrorHandlerOptions(StringBuilder sb,
DefaultErrorHandlerDefinition def) {
@@ -1507,6 +1505,11 @@ public abstract class JavaDslModelWriterSupport {
if (expr instanceof RefExpression) {
return "ref(" + quotedValue + ")";
}
+ String factoryMethod = languageFactoryMethod(expr);
+ if (factoryMethod != null) {
+ // groovy, ognl, ...: as their own expression, not
language("groovy", ...)
+ return "expression()." + factoryMethod + "(" + quotedValue +
").end()";
+ }
String lang = expr.getLanguage();
if (lang != null && !lang.isEmpty()) {
return "language(" + quote(lang) + ", " + quotedValue + ")";
@@ -1557,6 +1560,10 @@ public abstract class JavaDslModelWriterSupport {
if (expr instanceof WasmExpression) {
return "wasm";
}
+ String factoryMethod = languageFactoryMethod(expr);
+ if (factoryMethod != null) {
+ return factoryMethod;
+ }
String lang = expr.getLanguage();
if (lang != null && !lang.isEmpty()) {
return "language";
@@ -1564,6 +1571,23 @@ public abstract class JavaDslModelWriterSupport {
return null;
}
+ /**
+ * The method of expression() that builds this kind of expression from its
text (groovy("..."), ognl("...")), or
+ * null when there is none.
+ */
+ private static String languageFactoryMethod(ExpressionDefinition expr) {
+ String lang = expr.getLanguage();
+ if (lang == null || lang.isEmpty() || expr instanceof
LanguageExpression) {
+ return null;
+ }
+ try {
+ Method m = LanguageBuilderFactory.class.getMethod(lang,
String.class);
+ return m.getReturnType().getEnclosingClass() == expr.getClass() ?
lang : null;
+ } catch (NoSuchMethodException e) {
+ return null;
+ }
+ }
+
private String expressionBuilderOptions(ExpressionDefinition expr) {
StringBuilder opts = new StringBuilder();
@@ -1576,6 +1600,15 @@ public abstract class JavaDslModelWriterSupport {
appendOption(opts, "source", single.getSource());
}
+ // the xml namespaces of xpath, xquery and xtokenize (those of the xml
document the route was read from)
+ if (expr instanceof NamespaceAwareExpression nae &&
nae.getNamespaces() != null
+ && !nae.getNamespaces().isEmpty()) {
+ opts.append(".namespaces(Map.of(");
+ StringJoiner pairs = new StringJoiner(", ");
+ nae.getNamespaces().forEach((prefix, uri) ->
pairs.add(quote(prefix) + ", " + quote(uri)));
+ opts.append(pairs).append("))");
+ }
+
// type-specific options
if (expr instanceof SimpleExpression se) {
appendNonDefaultOption(opts, "trimResult", se.getTrimResult(),
"false");
diff --git
a/core/camel-java-io/src/test/java/org/apache/camel/java/in/RoundTripTest.java
b/core/camel-java-io/src/test/java/org/apache/camel/java/in/RoundTripTest.java
index e05dbad37745..459de6a366f4 100644
---
a/core/camel-java-io/src/test/java/org/apache/camel/java/in/RoundTripTest.java
+++
b/core/camel-java-io/src/test/java/org/apache/camel/java/in/RoundTripTest.java
@@ -42,6 +42,7 @@ import org.apache.camel.impl.DefaultCamelContext;
import org.apache.camel.java.LwModelToJavaDumper;
import org.apache.camel.model.RouteDefinition;
import org.apache.camel.model.RoutesDefinition;
+import org.apache.camel.xml.LwModelToXMLDumper;
import org.apache.camel.xml.in.ModelParser;
import org.junit.jupiter.api.Test;
@@ -130,6 +131,58 @@ class RoundTripTest {
String.join("\n\n", failures)).isEmpty();
}
+ /**
+ * Routes whose Java reads back to a model that differs from the XML one,
and why: none of them loses what the route
+ * does.
+ */
+ private static final Map<String, String> KNOWN_XML = Map.of(
+ "barInterceptorRoute.xml", "Java has intercept() for all the
routes of a route builder, not of one route",
+ "interceptFrom.xml", "Java has interceptFrom() for all the routes
of a route builder, not of one route",
+ "interceptFromAndSendTo.xml",
+ "Java has interceptSendToEndpoint() for all the routes of a route
builder, not of one route",
+ "routeInlinedErrorHandler.xml",
+ "the redelivery policy of deadLetterChannel() in Java says
logExhausted=false, the default of a dead letter channel",
+ "circuitBreakerResilience4j.xml", "failureRateThreshold(float)
keeps 30 as 30.0: the same value, written apart");
+
+ /**
+ * The routes of the XML corpus read back from the Java the dumper writes
as the XML routes they were (CAMEL-25255):
+ * the Java writer leaves nothing of the route out, such as its error
handler or the namespaces of an xpath.
+ */
+ @Test
+ void xmlCorpusReadsBackThroughJava() throws Exception {
+ assumeTrue(Files.isDirectory(CORPUS.get(0)), "the core test routes are
next to this module");
+ List<Path> files = new ArrayList<>();
+ for (Path dir : CORPUS) {
+ try (Stream<Path> s = Files.list(dir)) {
+ files.addAll(s.filter(p ->
p.toString().endsWith(".xml")).sorted().toList());
+ }
+ }
+ List<String> failures = new ArrayList<>();
+ int routes = 0;
+ for (Path file : files) {
+ if (KNOWN_XML.containsKey(file.getFileName().toString())) {
+ continue;
+ }
+ for (RouteDefinition route : routes(file)) {
+ routes++;
+ try (DefaultCamelContext context = new DefaultCamelContext()) {
+ String xml = new
LwModelToXMLDumper().dumpModelAsXml(context, route);
+ String java = new
LwModelToJavaDumper().dumpModelAsJava(context, route);
+ JavaParseResult parsed = new LwJavaParser().parse(java +
";");
+ String back = parsed.routes().getRoutes().isEmpty()
+ ? "" : new
LwModelToXMLDumper().dumpModelAsXml(context,
parsed.routes().getRoutes().get(0));
+ if (!back.equals(xml)) {
+ failures.add(file.getFileName() + "\n" + java + "\n---
was ---\n" + xml + "\n--- read back as ---\n"
+ + back);
+ }
+ }
+ }
+ }
+ assertThat(routes).isGreaterThan(100);
+ assertThat(failures).as("%d of %d routes do not read back:%n%s",
failures.size(), routes,
+ String.join("\n\n", failures)).isEmpty();
+ }
+
/** The Java the dumper is tested to write (java-dsl-*.txt) reads back
into the same Java. */
@Test
void dumperGoldenFiles() throws Exception {
diff --git
a/core/camel-java-io/src/test/java/org/apache/camel/java/out/JavaDslCompileTest.java
b/core/camel-java-io/src/test/java/org/apache/camel/java/out/JavaDslCompileTest.java
index edb798a0291f..af32449f2765 100644
---
a/core/camel-java-io/src/test/java/org/apache/camel/java/out/JavaDslCompileTest.java
+++
b/core/camel-java-io/src/test/java/org/apache/camel/java/out/JavaDslCompileTest.java
@@ -444,6 +444,7 @@ public class JavaDslCompileTest {
private static String wrapInRouteBuilder(String className, List<String>
routeSnippets) {
StringBuilder sb = new StringBuilder();
sb.append("package test.generated;\n\n");
+ sb.append("import java.util.Map;\n\n");
sb.append("import org.apache.camel.ExchangePattern;\n");
sb.append("import org.apache.camel.LoggingLevel;\n");
sb.append("import org.apache.camel.Predicate;\n");
diff --git
a/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/YamlModelWriter.java
b/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/YamlModelWriter.java
index 55f7b50c48ca..3aeb60ef3950 100644
---
a/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/YamlModelWriter.java
+++
b/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/YamlModelWriter.java
@@ -870,8 +870,8 @@ public class YamlModelWriter extends YamlModelWriterSupport
{
doWriteAttribute(jo, "builderMethod", def.getBuilderMethod(), "build");
doWriteAttribute(jo, "scriptLanguage", def.getScriptLanguage(), null);
doWriteAttribute(jo, "scriptPropertyPlaceholders",
def.getScriptPropertyPlaceholders(), "true");
- doWriteChildElement(jo, "constructors", new
BeanConstructorsAdapter().marshal(def.getConstructors()),
this::doWriteBeanConstructorsDefinition);
- doWriteChildElement(jo, "properties", new
BeanPropertiesAdapter().marshal(def.getProperties()),
this::doWriteBeanPropertiesDefinition);
+ doWriteBeanMap(jo, "constructors", def.getConstructors());
+ doWriteBeanMap(jo, "properties", def.getProperties());
if (def.getScript() != null) {
jo.put("script", def.getScript());
}
@@ -1667,8 +1667,8 @@ public class YamlModelWriter extends
YamlModelWriterSupport {
protected JsonObject
doWriteRouteTemplateDefinition(RouteTemplateDefinition def) {
JsonObject jo = new JsonObject();
doWriteOptionalIdentifiedDefinitionAttributes(jo, def);
- doWriteChildList(jo, null, "templateParameter",
def.getTemplateParameters(), this::doWriteRouteTemplateParameterDefinition);
- doWriteChildList(jo, null, "templateBean", def.getTemplateBeans(),
this::doWriteBeanFactoryDefinition);
+ doWriteChildList(jo, null, "parameters", def.getTemplateParameters(),
this::doWriteRouteTemplateParameterDefinition);
+ doWriteChildList(jo, null, "beans", def.getTemplateBeans(),
this::doWriteBeanFactoryDefinition);
doWriteChildElement(jo, "route", def.getRoute(),
this::doWriteRouteDefinition);
return jo;
}
@@ -2061,7 +2061,7 @@ public class YamlModelWriter extends
YamlModelWriterSupport {
}
protected JsonObject doWriteValueDefinition(ValueDefinition def) {
JsonObject jo = new JsonObject();
- doWriteValue(jo, def.getValue());
+ doWriteAttribute(jo, "value", def.getValue(), null);
return jo;
}
protected JsonObject doWriteWhenDefinition(WhenDefinition def) {
@@ -3411,7 +3411,7 @@ public class YamlModelWriter extends
YamlModelWriterSupport {
doWriteAttribute(jo, "enableNoContentResponse",
def.getEnableNoContentResponse(), "false");
doWriteAttribute(jo, "apiDocs", def.getApiDocs(), "true");
doWriteAttribute(jo, "tag", def.getTag(), null);
- doWriteChildList(jo, "verbs", "verbs", def.getVerbs(),
this::doWriteVerbDefinition);
+ doWriteVerbs(jo, def.getVerbs(), this::doWriteVerbDefinition);
doWriteChildElement(jo, "openApi", def.getOpenApi(),
this::doWriteOpenApiDefinition);
doWriteChildElement(jo, "securityDefinitions",
def.getSecurityDefinitions(), this::doWriteRestSecuritiesDefinition);
doWriteChildList(jo, null, "securityRequirements",
def.getSecurityRequirements(), this::doWriteSecurityDefinition);
@@ -3480,9 +3480,9 @@ public class YamlModelWriter extends
YamlModelWriterSupport {
doWriteAttribute(jo, "consumes", def.getConsumes(), null);
}
protected void doWriteVerbDefinitionElements(JsonObject jo, VerbDefinition
def) {
- doWriteChildList(jo, "params", "params", def.getParams(),
this::doWriteParamDefinition);
+ doWriteChildList(jo, null, "param", def.getParams(),
this::doWriteParamDefinition);
doWriteChildList(jo, "security", "security", def.getSecurity(),
this::doWriteSecurityDefinition);
- doWriteChildList(jo, "responseMsgs", "responseMsgs",
def.getResponseMsgs(), this::doWriteResponseMessageDefinition);
+ doWriteChildList(jo, null, "responseMessage", def.getResponseMsgs(),
this::doWriteResponseMessageDefinition);
doWriteChildElement(jo, "to", def.getTo(), this::doWriteToDefinition);
}
protected JsonObject doWriteVerbDefinition(VerbDefinition def) {
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 c5db9efafe79..22510f4548b7 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
@@ -24,7 +24,6 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
-import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import org.apache.camel.CamelContext;
@@ -407,63 +406,25 @@ public class LwModelToYAMLDumper implements
ModelToYAMLDumper {
if (beans.isEmpty()) {
return;
}
- buffer.write("- beans:\n");
+ // written by the model writer, so nested properties come out as
nested maps, as the yaml dsl loads them
+ var writer = new YamlModelWriter() {
+ @Override
+ public JsonObject
doWriteBeanFactoryDefinition(BeanFactoryDefinition<?> def) {
+ return super.doWriteBeanFactoryDefinition(def);
+ }
+ };
+ writer.setCamelContext(camelContext);
+ JsonArray list = new JsonArray();
for (BeanFactoryDefinition<?> b : beans) {
- doWriteBeanFactoryDefinition(b);
- }
- }
-
- private void doWriteBeanFactoryDefinition(BeanFactoryDefinition<?> b) {
- String type = b.getType();
- if (type.startsWith("#class:")) {
- type = type.substring(7);
- }
- buffer.write(String.format(" - name: %s%n", b.getName()));
- buffer.write(String.format(" type: \"%s\"%n", type));
- if (b.getFactoryBean() != null) {
- buffer.write(String.format(" factoryBean: \"%s\"%n",
b.getFactoryBean()));
- }
- if (b.getFactoryMethod() != null) {
- buffer.write(String.format(" factoryMethod: \"%s\"%n",
b.getFactoryMethod()));
- }
- if (b.getBuilderClass() != null) {
- buffer.write(String.format(" builderClass: \"%s\"%n",
b.getBuilderClass()));
- }
- if (b.getBuilderMethod() != null) {
- buffer.write(String.format(" builderMethod: \"%s\"%n",
b.getBuilderMethod()));
- }
- if (b.getInitMethod() != null) {
- buffer.write(String.format(" initMethod: \"%s\"%n",
b.getInitMethod()));
- }
- if (b.getDestroyMethod() != null) {
- buffer.write(String.format(" destroyMethod: \"%s\"%n",
b.getDestroyMethod()));
- }
- if (b.getScriptLanguage() != null) {
- buffer.write(String.format(" scriptLanguage: \"%s\"%n",
b.getScriptLanguage()));
- }
- if (b.getScript() != null) {
- buffer.write(String.format(" script: \"%s\"%n",
b.getScript()));
- }
- if (b.getConstructors() != null && !b.getConstructors().isEmpty())
{
- buffer.write(String.format(" constructors:%n"));
- final AtomicInteger counter = new AtomicInteger();
- b.getConstructors().forEach((key, value) -> {
- if (key == null) {
- key = counter.getAndIncrement();
- }
- buffer.write(String.format(" %d: \"%s\"%n", key,
value));
- });
- }
- if (b.getProperties() != null && !b.getProperties().isEmpty()) {
- buffer.write(String.format(" properties:%n"));
- b.getProperties().forEach((key, value) -> {
- if (value instanceof String) {
- buffer.write(String.format(" %s: \"%s\"%n",
key, value));
- } else {
- buffer.write(String.format(" %s: %s%n", key,
value));
- }
- });
+ JsonObject bean = writer.doWriteBeanFactoryDefinition(b);
+ if (bean.get("type") instanceof String type &&
type.startsWith("#class:")) {
+ bean.put("type", type.substring(7));
+ }
+ list.add(bean);
}
+ JsonObject root = new JsonObject();
+ root.put("beans", list);
+ buffer.write(writer.printAsYaml(List.of(root)));
}
}
diff --git
a/core/camel-yaml-io/src/main/java/org/apache/camel/yaml/out/YamlModelWriterSupport.java
b/core/camel-yaml-io/src/main/java/org/apache/camel/yaml/out/YamlModelWriterSupport.java
index 4fe49a127205..6af0454bb719 100644
---
a/core/camel-yaml-io/src/main/java/org/apache/camel/yaml/out/YamlModelWriterSupport.java
+++
b/core/camel-yaml-io/src/main/java/org/apache/camel/yaml/out/YamlModelWriterSupport.java
@@ -25,6 +25,7 @@ import java.util.function.Function;
import org.apache.camel.CamelContext;
import org.apache.camel.catalog.RuntimeCamelCatalog;
+import org.apache.camel.model.rest.VerbDefinition;
import org.apache.camel.util.URISupport;
import org.apache.camel.util.json.JsonArray;
import org.apache.camel.util.json.JsonObject;
@@ -143,6 +144,37 @@ public abstract class YamlModelWriterSupport {
}
}
+ /**
+ * The verbs of a rest as the YAML DSL has them: a list per kind (get,
post, ...), not one list of all verbs.
+ */
+ protected <T extends VerbDefinition> void doWriteVerbs(JsonObject jo,
List<T> verbs, Function<T, JsonObject> writer) {
+ if (verbs != null) {
+ for (T verb : verbs) {
+ JsonObject child = writer.apply(verb);
+ if (child != null) {
+ JsonArray arr = (JsonArray)
jo.computeIfAbsent(verb.asVerb(), k -> new JsonArray());
+ arr.add(child);
+ }
+ }
+ }
+ }
+
+ /**
+ * The properties or constructor arguments of a bean as the YAML DSL has
them: a map, with a nested map for nested
+ * properties.
+ */
+ protected void doWriteBeanMap(JsonObject jo, String key, Map<?, ?> map) {
+ if (map != null && !map.isEmpty()) {
+ jo.put(key, beanMap(map));
+ }
+ }
+
+ private static JsonObject beanMap(Map<?, ?> map) {
+ JsonObject answer = new JsonObject();
+ map.forEach((k, v) -> answer.put(String.valueOf(k), v instanceof
Map<?, ?> m ? beanMap(m) : v));
+ return answer;
+ }
+
@SuppressWarnings("unchecked")
protected void doWriteStringList(JsonObject jo, String wrapperKey, String
itemKey, List<String> list) {
if (list != null && !list.isEmpty()) {
diff --git
a/core/camel-yaml-io/src/test/java/org/apache/camel/yaml/out/YamlBeansDumpTest.java
b/core/camel-yaml-io/src/test/java/org/apache/camel/yaml/out/YamlBeansDumpTest.java
new file mode 100644
index 000000000000..28e23450a3e4
--- /dev/null
+++
b/core/camel-yaml-io/src/test/java/org/apache/camel/yaml/out/YamlBeansDumpTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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.yaml.out;
+
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.camel.model.BeanFactoryDefinition;
+import org.apache.camel.model.app.BeansDefinition;
+import org.apache.camel.xml.in.ModelParser;
+import org.apache.camel.yaml.LwModelToYAMLDumper;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * The beans dumped as YAML have their properties as the YAML DSL loads them:
nested properties as a nested map
+ * (CAMEL-25255).
+ */
+class YamlBeansDumpTest {
+
+ @Test
+ void nestedPropertiesAreANestedMap() throws Exception {
+ BeansDefinition beans;
+ try (InputStream in =
Files.newInputStream(Paths.get("../camel-xml-io/src/test/resources/beansWithProperties.xml")))
{
+ beans = new ModelParser(in,
"http://camel.apache.org/schema/xml-io").parseBeansDefinition().orElseThrow();
+ }
+ String yaml = new LwModelToYAMLDumper().dumpBeansAsYaml(null, new
ArrayList<>(beans.getBeans()));
+ assertThat(yaml).isEqualTo("""
+ - beans:
+ - name: b1
+ type: org.apache.camel.xml.in.ModelParserTest.MyBean
+ properties:
+ p1: v1
+ p2: v2
+ nested:
+ p1: v1a
+ p2: v2a
+ - name: b2
+ type: org.apache.camel.xml.in.ModelParserTest.MyBean
+ properties:
+ p1: v1
+ p2: v2
+ nested.p1: v1a
+ nested.p2: v2a
+ """);
+ }
+
+ @Test
+ void theClassPrefixOfTheTypeIsLeftOut() throws Exception {
+ BeanFactoryDefinition<?> bean = new BeanFactoryDefinition<>();
+ bean.setName("greeter");
+ bean.setType("#class:com.foo.Greeter");
+ String yaml = new LwModelToYAMLDumper().dumpBeansAsYaml(null,
List.of(bean));
+ assertThat(yaml).contains("type:
com.foo.Greeter").doesNotContain("#class");
+ }
+}
diff --git a/core/camel-yaml-io/src/test/resources/yaml-beans-with-route.yaml
b/core/camel-yaml-io/src/test/resources/yaml-beans-with-route.yaml
index 64db76b553c7..02359c8af424 100644
--- a/core/camel-yaml-io/src/test/resources/yaml-beans-with-route.yaml
+++ b/core/camel-yaml-io/src/test/resources/yaml-beans-with-route.yaml
@@ -20,9 +20,7 @@
- name: myService
type: com.example.MyService
properties:
- property:
- - key: url
- value: http://localhost:8080
+ url: http://localhost:8080
route:
- customId: true
id: myRoute
diff --git a/core/camel-yaml-io/src/test/resources/yaml-beans.yaml
b/core/camel-yaml-io/src/test/resources/yaml-beans.yaml
index 3e50198f4fdc..841c9322fc89 100644
--- a/core/camel-yaml-io/src/test/resources/yaml-beans.yaml
+++ b/core/camel-yaml-io/src/test/resources/yaml-beans.yaml
@@ -19,11 +19,8 @@
name: myBean
type: com.example.MyBean
properties:
- property:
- - key: greeting
- value: Hello
- - key: count
- value: 5
+ greeting: Hello
+ count: "5"
- beanFactory:
name: myFactory
type: com.example.MyFactory
diff --git a/core/camel-yaml-io/src/test/resources/yaml-rest.yaml
b/core/camel-yaml-io/src/test/resources/yaml-rest.yaml
index fee9da12aaaa..9ee93c87a537 100644
--- a/core/camel-yaml-io/src/test/resources/yaml-rest.yaml
+++ b/core/camel-yaml-io/src/test/resources/yaml-rest.yaml
@@ -17,10 +17,11 @@
- rest:
path: /api
- verbs:
+ get:
- path: /hello
to:
uri: direct:hello
+ post:
- path: /bye
consumes: application/json
to:
diff --git a/core/camel-yaml-io/src/test/resources/yaml-route-template.yaml
b/core/camel-yaml-io/src/test/resources/yaml-route-template.yaml
index a1ad10bd8c38..544d3a05f61a 100644
--- a/core/camel-yaml-io/src/test/resources/yaml-route-template.yaml
+++ b/core/camel-yaml-io/src/test/resources/yaml-route-template.yaml
@@ -18,7 +18,7 @@
- routeTemplate:
customId: true
id: myTemplate
- templateParameter:
+ parameters:
- description: the foo parameter
name: foo
- description: the bar parameter
diff --git
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
index 1800adfbabfd..c15293a15c68 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
@@ -4193,3 +4193,23 @@ WebSocket transport already were. Before, it kept the
JVM alive when the applica
Spring Boot application whose port was in use: Camel had not started, so it
never stopped the connector. An application
that only kept running because of this thread now exits; keep it running on
purpose, for example with
`camel.main.run-controller=true` for a Spring Boot application without a web
server.
+
+=== Route dumps as YAML and Java
+
+Routes dumped as YAML (`camel.main.dumpRoutes=yaml`, `camel transform route
--format=yaml`) are now written as the
+YAML DSL loads them:
+
+* the verbs of a rest are listed by their kind (`get`, `post`, ...) instead of
under `verbs`, and their parameters and
+ response messages as `param` and `responseMessage`;
+* a route template has `parameters` and `beans` instead of `templateParameter`
and `templateBean`;
+* the properties of a bean are a map, also when they are nested, instead of a
`property` list.
+
+Before, such a dump did not load in the YAML DSL.
+
+A route built in Java with its own error handler
(`from("...").errorHandler(deadLetterChannel("..."))`) now has the
+error handler in its model, so dumping the route (as XML, YAML or Java) writes
it; it was left out before. When written
+as Java, the error handler of an XML or YAML route is now `.errorHandler(...)`
on the route, instead of an
+`errorHandler(...)` that applies to every route of the route builder.
+
+The `timeout(long)` of the Resequencer EIP in Java keeps the millis as given
(`2000`), instead of a duration
+(`2s0ms`).
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/test/resources/blueprint-out.yaml
b/dsl/camel-jbang/camel-jbang-core/src/test/resources/blueprint-out.yaml
index 754bcf2ae5e9..e304ac8d51cc 100644
--- a/dsl/camel-jbang/camel-jbang-core/src/test/resources/blueprint-out.yaml
+++ b/dsl/camel-jbang/camel-jbang-core/src/test/resources/blueprint-out.yaml
@@ -17,7 +17,7 @@
- beans:
- name: userService
- type: "org.apache.camel.example.rest.UserService"
+ type: org.apache.camel.example.rest.UserService
- route:
from:
uri: direct
diff --git
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlRestTemplateRoundTripTest.java
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlRestTemplateRoundTripTest.java
new file mode 100644
index 000000000000..e6e5792cf56e
--- /dev/null
+++
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlRestTemplateRoundTripTest.java
@@ -0,0 +1,221 @@
+/*
+ * 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.dsl.yaml.validator;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+
+import com.networknt.schema.Error;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.model.ModelCamelContext;
+import org.apache.camel.model.RouteTemplatesDefinition;
+import org.apache.camel.model.rest.RestsDefinition;
+import org.apache.camel.support.PluginHelper;
+import org.apache.camel.support.ResourceHelper;
+import org.apache.camel.xml.LwModelToXMLDumper;
+import org.apache.camel.xml.in.ModelParser;
+import org.apache.camel.yaml.LwModelToYAMLDumper;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assumptions.assumeTrue;
+
+/**
+ * Rests and route templates dumped as YAML are loaded back by the YAML DSL as
they were (CAMEL-25255): the YAML writer
+ * writes the verbs of a rest by their kind (get, post, ...), and the
parameters and beans of a route template as the
+ * YAML DSL names them.
+ */
+class YamlRestTemplateRoundTripTest {
+
+ private static final String NS = "http://camel.apache.org/schema/xml-io";
+ private static final Path CORPUS =
Path.of("../../../core/camel-xml-io/src/test/resources");
+
+ private static final String REST = """
+ <rests xmlns="http://camel.apache.org/schema/xml-io">
+ <rest id="api" path="/api" tag="orders"
consumes="application/json" bindingMode="json">
+ <securityDefinitions>
+ <apiKey key="key" name="X-Key" inHeader="true"/>
+ <oauth2 key="oauth" flow="implicit"
authorizationUrl="https://auth/authorize">
+ <scopes key="read" value="Read orders"/>
+ </oauth2>
+ </securityDefinitions>
+ <securityRequirements key="key"/>
+ <get path="/orders/{id}" id="getOrder"
outType="com.foo.Order" routeId="get-order"
+ description="Gets an order">
+ <param name="id" type="path" description="The order
id" dataType="integer">
+ <examples key="one" value="1"/>
+ </param>
+ <param name="verbose" type="query" required="false"
defaultValue="false">
+ <allowableValues>
+ <value>true</value>
+ <value>false</value>
+ </allowableValues>
+ </param>
+ <responseMessage code="404" message="Not found">
+ <header name="X-Reason" dataType="string"/>
+ <examples key="json" value="{}"/>
+ </responseMessage>
+ <security key="oauth" scopes="read"/>
+ <to uri="direct:getOrder"/>
+ </get>
+ <get path="/orders">
+ <to uri="direct:listOrders"/>
+ </get>
+ <post path="/orders" type="com.foo.Order">
+ <to uri="direct:createOrder"/>
+ </post>
+ <put path="/orders/{id}">
+ <to uri="direct:updateOrder"/>
+ </put>
+ <delete path="/orders/{id}">
+ <to uri="direct:deleteOrder"/>
+ </delete>
+ <head path="/orders">
+ <to uri="direct:headOrders"/>
+ </head>
+ <patch path="/orders/{id}">
+ <to uri="direct:patchOrder"/>
+ </patch>
+ </rest>
+ <rest path="/petstore">
+ <openApi specification="petstore.json"
missingOperation="mock"/>
+ </rest>
+ </rests>
+ """;
+
+ private static final String TEMPLATE = """
+ <routeTemplates xmlns="http://camel.apache.org/schema/xml-io">
+ <routeTemplate id="myTemplate">
+ <templateParameter name="foo" description="The input"/>
+ <templateParameter name="bar" defaultValue="out"
required="false"/>
+ <templateBean name="greeter" type="com.foo.Greeter">
+ <properties>
+ <property key="greeting" value="Hello"/>
+ </properties>
+ </templateBean>
+ <route>
+ <from uri="direct:{{foo}}"/>
+ <to uri="mock:{{bar}}"/>
+ </route>
+ </routeTemplate>
+ </routeTemplates>
+ """;
+
+ @Test
+ void restWithAllItsParts() throws Exception {
+ assertThat(restsRoundTrip(REST)).isNull();
+ }
+
+ @Test
+ void routeTemplateWithParametersAndBeans() throws Exception {
+ assertThat(templatesRoundTrip(TEMPLATE)).isNull();
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = { "barRest.xml", "simpleRest.xml",
"restAllowedValues.xml" })
+ void restsOfTheCorpus(String file) throws Exception {
+ assumeTrue(Files.isDirectory(CORPUS), "the core test routes are in the
source tree");
+
assertThat(restsRoundTrip(Files.readString(CORPUS.resolve(file)))).isNull();
+ }
+
+ @Test
+ void routeTemplatesOfTheCorpus() throws Exception {
+ assumeTrue(Files.isDirectory(CORPUS), "the core test routes are in the
source tree");
+
assertThat(templatesRoundTrip(Files.readString(CORPUS.resolve("barTemplate.xml")))).isNull();
+ }
+
+ private static InputStream in(String xml) {
+ return new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8));
+ }
+
+ /**
+ * Null when the rests read back as they were, else what went wrong. Both
sides are added to a context the same way,
+ * so what loading adds (such as the params of a path) is on both.
+ */
+ private static String restsRoundTrip(String xml) throws Exception {
+ RestsDefinition rests = new ModelParser(in(xml),
NS).parseRestsDefinition().orElseThrow();
+ String yaml;
+ String before;
+ try (DefaultCamelContext context = new DefaultCamelContext()) {
+ yaml = new LwModelToYAMLDumper().dumpModelAsYaml(context, rests);
+ context.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() {
+ getRestCollection().setRests(rests.getRests());
+ }
+ });
+ before = dumpRests(context);
+ }
+ return readBack(yaml, before,
YamlRestTemplateRoundTripTest::dumpRests);
+ }
+
+ /** Null when the route templates read back as they were, else what went
wrong. */
+ private static String templatesRoundTrip(String xml) throws Exception {
+ RouteTemplatesDefinition templates = new ModelParser(in(xml),
NS).parseRouteTemplatesDefinition().orElseThrow();
+ String yaml;
+ String before;
+ try (DefaultCamelContext context = new DefaultCamelContext()) {
+ yaml = new LwModelToYAMLDumper().dumpModelAsYaml(context,
templates);
+ context.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() {
+
getRouteTemplateCollection().setRouteTemplates(templates.getRouteTemplates());
+ }
+ });
+ before = dumpTemplates(context);
+ }
+ return readBack(yaml, before,
YamlRestTemplateRoundTripTest::dumpTemplates);
+ }
+
+ private static String dumpRests(DefaultCamelContext context) throws
Exception {
+ RestsDefinition rests = new RestsDefinition();
+
rests.setRests(context.getCamelContextExtension().getContextPlugin(ModelCamelContext.class).getRestDefinitions());
+ return new LwModelToXMLDumper().dumpModelAsXml(context, rests);
+ }
+
+ private static String dumpTemplates(DefaultCamelContext context) throws
Exception {
+ RouteTemplatesDefinition templates = new RouteTemplatesDefinition();
+
templates.setRouteTemplates(context.getCamelContextExtension().getContextPlugin(ModelCamelContext.class)
+ .getRouteTemplateDefinitions());
+ return new LwModelToXMLDumper().dumpModelAsXml(context, templates);
+ }
+
+ private interface Dump {
+ String dump(DefaultCamelContext context) throws Exception;
+ }
+
+ private static String readBack(String yaml, String before, Dump dump)
throws Exception {
+ List<Error> errors = new YamlValidator().validate(yaml);
+ if (!errors.isEmpty()) {
+ return yaml + "\n--- does not validate: " + errors;
+ }
+ try (DefaultCamelContext context = new DefaultCamelContext()) {
+
PluginHelper.getRoutesLoader(context).loadRoutes(ResourceHelper.fromString("rest.yaml",
yaml));
+ String after = dump.dump(context);
+ return after.equals(before) ? null : yaml + "\n--- was ---\n" +
before + "\n--- read back as ---\n" + after;
+ } catch (RuntimeException e) {
+ return yaml + "\n--- does not load: " + e;
+ }
+ }
+}
diff --git
a/tooling/maven/camel-package-maven-plugin/src/main/resources/velocity/model-yaml-writer.vm
b/tooling/maven/camel-package-maven-plugin/src/main/resources/velocity/model-yaml-writer.vm
index 711796bc892a..c9153ee6f1a6 100644
---
a/tooling/maven/camel-package-maven-plugin/src/main/resources/velocity/model-yaml-writer.vm
+++
b/tooling/maven/camel-package-maven-plugin/src/main/resources/velocity/model-yaml-writer.vm
@@ -15,6 +15,8 @@
## limitations under the License.
## ---------------------------------------------------------------------------
#set( $default = "#[[##default]]#" )
+## the YAML DSL names some lists other than the XML DSL does (keyed by
class.member)
+#set( $yamlKeys = { "VerbDefinition.params": "param",
"VerbDefinition.responseMsgs": "responseMessage",
"RouteTemplateDefinition.templateParameters": "parameters",
"RouteTemplateDefinition.templateBeans": "beans" } )
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
@@ -141,8 +143,13 @@ public class YamlModelWriter extends
YamlModelWriterSupport {
#elseif( $member.xmlElementRef )
#set( $foo = $elementRefs.add($root) )
#if( $list )
+ #set( $yamlKey =
$yamlKeys.get("${clazz.simpleName}.${member.name}") )
#if( $member.name == "outputs" )
doWriteOutputs(jo, def.${member.getter}(),
this::doWrite${root.simpleName}Ref);
+ #elseif( $clazz.simpleName == "RestDefinition" &&
$member.name == "verbs" )
+ doWriteVerbs(jo, def.${member.getter}(),
this::doWrite${root.simpleName});
+ #elseif( $yamlKey )
+ doWriteChildList(jo, null, "${yamlKey}", def.${member.getter}(),
this::doWrite${root.simpleName});
#else
#set( $refName = $member.xmlElementRef.name() )
#if( $refName && $refName != $default &&
$refName != "" )
@@ -186,6 +193,10 @@ public class YamlModelWriter extends
YamlModelWriterSupport {
#if( $n == $default )
#set( $n = ${member.name} )
#end
+ #set( $yamlKey =
$yamlKeys.get("${clazz.simpleName}.${member.name}") )
+ #if( $yamlKey )
+ #set( $n = $yamlKey )
+ #end
#if( $list )
#set( $w = false )
#if( $member.xmlElementWrapper )
@@ -198,7 +209,9 @@ public class YamlModelWriter extends YamlModelWriterSupport
{
#end
#else
#set( $adapter = $member.xmlJavaTypeAdapter )
- #if( $adapter )
+ #if( $adapter && $clazz.simpleName ==
"BeanFactoryDefinition" )
+ doWriteBeanMap(jo, "${n}", def.${member.getter}());
+ #elseif( $adapter )
#set( $cl = $adapter.value() )
#set( $actualType = false )
#foreach( $m in $cl.declaredMethods )
@@ -268,7 +281,9 @@ public class YamlModelWriter extends YamlModelWriterSupport
{
#writeYamlAttrs
#end
#set( $value =
$mojo.getValues($mojo.getClassAndSuper($clazz)).findFirst() )
- #if( $value.isPresent() )
+ #if( $value.isPresent() && $name == "ValueDefinition" )
+ doWriteAttribute(jo, "value", def.${value.get().getter}(), null);
+ #elseif( $value.isPresent() )
doWriteValue(jo, def.${value.get().getter}());
#end
#if( $hasDerived && !$thisElements.isEmpty() )