This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch fix/CAMEL-23756 in repository https://gitbox.apache.org/repos/asf/camel.git
commit 7a3b660f632e0540103c34ccde281b985782aa65 Author: Claus Ibsen <[email protected]> AuthorDate: Mon Jun 15 13:32:36 2026 +0200 CAMEL-23756: camel-jbang-mcp - Support Java DSL as source format in route transform tool Co-Authored-By: Claude <[email protected]> Signed-off-by: Claus Ibsen <[email protected]> --- dsl/camel-jbang/camel-jbang-mcp/pom.xml | 4 + .../jbang/core/commands/mcp/TransformTools.java | 74 ++++++++- .../core/commands/mcp/TransformToolsTest.java | 168 +++++++++++++++++++++ 3 files changed, 238 insertions(+), 8 deletions(-) diff --git a/dsl/camel-jbang/camel-jbang-mcp/pom.xml b/dsl/camel-jbang/camel-jbang-mcp/pom.xml index ccf1351464ca..9bab3f333f9c 100644 --- a/dsl/camel-jbang/camel-jbang-mcp/pom.xml +++ b/dsl/camel-jbang/camel-jbang-mcp/pom.xml @@ -111,6 +111,10 @@ <groupId>org.apache.camel</groupId> <artifactId>camel-yaml-dsl</artifactId> </dependency> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-java-joor-dsl</artifactId> + </dependency> <!-- Swagger/OpenAPI parser for contract-first OpenAPI tools --> <dependency> diff --git a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/TransformTools.java b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/TransformTools.java index 8b19c5e21ceb..604f7339b7da 100644 --- a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/TransformTools.java +++ b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/TransformTools.java @@ -25,6 +25,8 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; @@ -52,6 +54,8 @@ import org.apache.camel.yaml.out.YamlModelWriter; @ApplicationScoped public class TransformTools { + private static final Pattern CLASS_NAME_PATTERN = Pattern.compile("(?:public\\s+)?class\\s+(\\w+)"); + @Inject CatalogService catalogService; @@ -158,7 +162,8 @@ public class TransformTools { */ @Tool(annotations = @Tool.Annotations(readOnlyHint = true, destructiveHint = false, openWorldHint = false), description = "Transform a Camel route between different DSL formats (YAML, XML). " + - "Note: Java to YAML/XML transformation has limitations.") + "Note: Java to YAML/XML transformation has limitations." + + " Java DSL can only be used as source format, not as target format.") public TransformResult camel_transform_route( @ToolArg(description = "Route definition to transform") String route, @ToolArg(description = "Source format (yaml, xml, java)") String fromFormat, @@ -181,13 +186,6 @@ public class TransformTools { return result; } - if ("java".equals(from)) { - result.supported = false; - result.note = "Java DSL to " + toFormat + " transformation is not supported. " - + "There is no lightweight Java DSL parser available."; - return result; - } - try { if ("xml".equals(from) && "yaml".equals(to)) { result.result = transformXmlToYaml(route); @@ -195,6 +193,12 @@ public class TransformTools { } else if ("yaml".equals(from) && "xml".equals(to)) { result.result = transformYamlToXml(route); result.supported = true; + } else if ("java".equals(from) && "yaml".equals(to)) { + result.result = transformJavaToFormat(route, "yaml"); + result.supported = true; + } else if ("java".equals(from) && "xml".equals(to)) { + result.result = transformJavaToFormat(route, "xml"); + result.supported = true; } else { result.supported = false; result.note = "Unsupported transformation: " + fromFormat + " to " + toFormat; @@ -264,6 +268,60 @@ public class TransformTools { } } + private String transformJavaToFormat(String java, String targetFormat) throws Exception { + DefaultCamelContext ctx = new DefaultCamelContext(); + try { + ctx.build(); + + String source = wrapSnippetIfNeeded(java); + String className = extractClassName(source); + Resource resource = ResourceHelper.fromString(className + ".java", source); + PluginHelper.getRoutesLoader(ctx).loadRoutes(resource); + + List<RouteDefinition> routeDefs = ctx.getRouteDefinitions(); + if (routeDefs == null || routeDefs.isEmpty()) { + throw new IllegalArgumentException( + "Could not parse Java route. Ensure it contains a valid route definition."); + } + + if ("yaml".equals(targetFormat)) { + YamlModelWriter writer = new YamlModelWriter(); + List<JsonObject> roots = new ArrayList<>(); + for (RouteDefinition route : routeDefs) { + roots.add(writer.writeRouteDefinition(route)); + } + return writer.printAsYaml(roots); + } else { + RoutesDefinition rd = new RoutesDefinition(); + rd.setRoutes(routeDefs); + + StringWriter sw = new StringWriter(); + new org.apache.camel.xml.out.ModelWriter(sw).writeRoutesDefinition(rd); + return sw.toString(); + } + } finally { + ctx.stop(); + } + } + + private static String wrapSnippetIfNeeded(String source) { + if (CLASS_NAME_PATTERN.matcher(source).find()) { + return source; + } + return "import org.apache.camel.builder.RouteBuilder;\n\n" + + "public class SnippetRoute extends RouteBuilder {\n" + + " @Override\n" + + " public void configure() {\n" + + " " + source + "\n" + + " }\n" + + "}\n"; + } + + private static String extractClassName(String source) { + Matcher m = CLASS_NAME_PATTERN.matcher(source); + return m.find() ? m.group(1) : "Route"; + } + /** * Tool to validate a YAML DSL route definition against the Camel YAML DSL JSON schema. */ diff --git a/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/TransformToolsTest.java b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/TransformToolsTest.java new file mode 100644 index 000000000000..3d7644f97fd3 --- /dev/null +++ b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/TransformToolsTest.java @@ -0,0 +1,168 @@ +/* + * 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.jbang.core.commands.mcp; + +import java.util.Optional; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class TransformToolsTest { + + private TransformTools createTools() { + CatalogService catalogService = new CatalogService(); + catalogService.catalogRepos = Optional.empty(); + + TransformTools tools = new TransformTools(); + tools.catalogService = catalogService; + return tools; + } + + @Test + void transformXmlToYaml() { + TransformTools tools = createTools(); + String xml = """ + <routes xmlns="http://camel.apache.org/schema/spring"> + <route> + <from uri="timer:hello"/> + <log message="Hello World"/> + </route> + </routes> + """; + + TransformTools.TransformResult result = tools.camel_transform_route(xml, "xml", "yaml"); + + assertThat(result.supported).isTrue(); + assertThat(result.result).contains("timer"); + assertThat(result.result).contains("log"); + } + + @Test + void transformYamlToXml() { + TransformTools tools = createTools(); + String yaml = """ + - route: + from: + uri: timer:hello + steps: + - log: + message: Hello World + """; + + TransformTools.TransformResult result = tools.camel_transform_route(yaml, "yaml", "xml"); + + assertThat(result.supported).isTrue(); + assertThat(result.result).contains("timer:hello"); + assertThat(result.result).contains("<log"); + } + + @Test + void transformJavaToYaml() { + TransformTools tools = createTools(); + String java = """ + import org.apache.camel.builder.RouteBuilder; + + public class MyRoute extends RouteBuilder { + @Override + public void configure() { + from("timer:hello") + .log("Hello World"); + } + } + """; + + TransformTools.TransformResult result = tools.camel_transform_route(java, "java", "yaml"); + + assertThat(result.supported).isTrue(); + assertThat(result.result).contains("timer"); + assertThat(result.result).contains("log"); + } + + @Test + void transformJavaToXml() { + TransformTools tools = createTools(); + String java = """ + import org.apache.camel.builder.RouteBuilder; + + public class MyRoute extends RouteBuilder { + @Override + public void configure() { + from("timer:hello") + .log("Hello World"); + } + } + """; + + TransformTools.TransformResult result = tools.camel_transform_route(java, "java", "xml"); + + assertThat(result.supported).isTrue(); + assertThat(result.result).contains("timer:hello"); + assertThat(result.result).contains("<log"); + } + + @Test + void transformJavaSnippetToYaml() { + TransformTools tools = createTools(); + String snippet = """ + from("timer:hello") + .log("Hello World"); + """; + + TransformTools.TransformResult result = tools.camel_transform_route(snippet, "java", "yaml"); + + assertThat(result.supported).isTrue(); + assertThat(result.result).contains("timer"); + assertThat(result.result).contains("log"); + } + + @Test + void transformJavaSnippetToXml() { + TransformTools tools = createTools(); + String snippet = """ + from("timer:hello") + .to("log:foo"); + """; + + TransformTools.TransformResult result = tools.camel_transform_route(snippet, "java", "xml"); + + assertThat(result.supported).isTrue(); + assertThat(result.result).contains("timer:hello"); + assertThat(result.result).contains("log:foo"); + } + + @Test + void sameFormatReturnsInput() { + TransformTools tools = createTools(); + String yaml = "- route:\n from:\n uri: timer:hello\n"; + + TransformTools.TransformResult result = tools.camel_transform_route(yaml, "yaml", "yaml"); + + assertThat(result.supported).isTrue(); + assertThat(result.result).isEqualTo(yaml); + } + + @Test + void unsupportedFormatReturnsNotSupported() { + TransformTools tools = createTools(); + + TransformTools.TransformResult result = tools.camel_transform_route("some route", "groovy", "yaml"); + + assertThat(result.supported).isFalse(); + assertThat(result.note).contains("Unsupported"); + } +}
