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 3afaf2ffb261 CAMEL-25204: camel-java-io - the Java DSL parser resolves 
the header names of the endpoint DSL (#27155)
3afaf2ffb261 is described below

commit 3afaf2ffb26174a83b9b598ab9d4b9e94220e8a6
Author: Claus Ibsen <[email protected]>
AuthorDate: Thu Oct 1 07:06:37 2026 +0200

    CAMEL-25204: camel-java-io - the Java DSL parser resolves the header names 
of the endpoint DSL (#27155)
    
    …names of the endpoint DSL
    
    headers().kafka().kafkaKey() (EndpointRouteBuilder) is the header name
    CamelKafkaKey. The EndpointDslResolver plug gets a headerName(component,
    method) lookup, null by default; the catalog resolver of camel-jbang finds 
the
    header whose generated method has that name (a leading Camel dropped, then
    camel case, as EndpointDslMojo generates it). Checked against all 3456 
header
    methods of the generated endpoint DSL.
    
    Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../org/apache/camel/java/in/ChainReplayer.java    | 13 ++++++
 .../apache/camel/java/in/EndpointDslResolver.java  | 11 +++++
 .../org/apache/camel/java/in/LwJavaParserTest.java | 34 +++++++++++++++
 .../commands/ai/CatalogEndpointDslResolver.java    | 45 +++++++++++++++++++
 .../ai/CatalogEndpointDslResolverTest.java         | 50 ++++++++++++++++++++++
 5 files changed, 153 insertions(+)

diff --git 
a/core/camel-java-io/src/main/java/org/apache/camel/java/in/ChainReplayer.java 
b/core/camel-java-io/src/main/java/org/apache/camel/java/in/ChainReplayer.java
index 3ed1fc167b82..21080e22f456 100644
--- 
a/core/camel-java-io/src/main/java/org/apache/camel/java/in/ChainReplayer.java
+++ 
b/core/camel-java-io/src/main/java/org/apache/camel/java/in/ChainReplayer.java
@@ -635,6 +635,12 @@ final class ChainReplayer {
             String text = formatText(c.calls().get(0).args());
             return text != null ? text : new Unknown(c, "a format the parser 
cannot work out");
         }
+        if (isEndpointHeader(c)) {
+            // headers().kafka().kafkaKey(): the header name, as the endpoint 
DSL has it
+            String name = endpointDsl != null
+                    ? endpointDsl.headerName(c.calls().get(1).name(), 
c.calls().get(2).name()) : null;
+            return name != null ? name : new Unknown(c, "a header of the 
endpoint DSL the parser does not know");
+        }
         if (c.qualifier() != null && c.calls().size() == 1
                 && COLLECTIONS.contains(c.qualifier() + "." + 
c.calls().get(0).name())) {
             // setHeaders(Map.of("foo", constant("ABC"))): a collection of 
values the parser works out
@@ -701,6 +707,13 @@ final class ChainReplayer {
         return target;
     }
 
+    /** {@code headers().kafka().kafkaKey()} of EndpointRouteBuilder: three 
calls without arguments. */
+    private static boolean isEndpointHeader(Chain c) {
+        List<Call> calls = c.calls();
+        return (c.qualifier() == null || 
"EndpointRouteBuilder".equals(c.qualifier())) && calls.size() == 3
+                && calls.get(0).name().equals("headers") && 
calls.stream().allMatch(call -> call.args().isEmpty());
+    }
+
     /** Factories of JDK collections a route passes values in: only data, 
nothing of the project runs. */
     private static final Set<String> COLLECTIONS = Set.of(
             "Map.of", "java.util.Map.of", "List.of", "java.util.List.of", 
"Set.of", "java.util.Set.of",
diff --git 
a/core/camel-java-io/src/main/java/org/apache/camel/java/in/EndpointDslResolver.java
 
b/core/camel-java-io/src/main/java/org/apache/camel/java/in/EndpointDslResolver.java
index a16ccae99e08..1b7f012d7b28 100644
--- 
a/core/camel-java-io/src/main/java/org/apache/camel/java/in/EndpointDslResolver.java
+++ 
b/core/camel-java-io/src/main/java/org/apache/camel/java/in/EndpointDslResolver.java
@@ -50,6 +50,17 @@ public interface EndpointDslResolver {
      */
     Endpoint endpoint(String factory, List<String> args, List<Option> options);
 
+    /**
+     * The name of a header of the endpoint DSL, {@code 
headers().kafka().kafkaKey()}, or null when it is not known: the
+     * method names are generated from the header names of the component, 
which only a resolver with the catalog has.
+     *
+     * @param component the component method, such as {@code kafka} or {@code 
file}
+     * @param method    the header method, such as {@code kafkaKey}
+     */
+    default String headerName(String component, String method) {
+        return null;
+    }
+
     /** The rules the endpoint DSL is generated with; the default. */
     EndpointDslResolver NAMING = new NamingEndpointDslResolver();
 }
diff --git 
a/core/camel-java-io/src/test/java/org/apache/camel/java/in/LwJavaParserTest.java
 
b/core/camel-java-io/src/test/java/org/apache/camel/java/in/LwJavaParserTest.java
index 3aacf6067369..4939cb8ccea0 100644
--- 
a/core/camel-java-io/src/test/java/org/apache/camel/java/in/LwJavaParserTest.java
+++ 
b/core/camel-java-io/src/test/java/org/apache/camel/java/in/LwJavaParserTest.java
@@ -333,6 +333,40 @@ class LwJavaParserTest {
         assertThat(result.isComplete()).as("%s", result.unresolved()).isTrue();
     }
 
+    @Test
+    void theHeaderNamesOfTheEndpointDsl() {
+        // CAMEL-25204: headers().kafka().kafkaKey() is a header name, which a 
resolver with the catalog knows
+        String source = """
+                public class R extends EndpointRouteBuilder {
+                    public void configure() {
+                        from(direct("in"))
+                            .setHeader(headers().kafka().kafkaKey(), 
constant("myKey"))
+                            .to(kafka("orders"));
+                    }
+                }
+                """;
+        EndpointDslResolver withHeaders = new EndpointDslResolver() {
+            @Override
+            public Endpoint endpoint(String factory, List<String> args, 
List<Option> options) {
+                return EndpointDslResolver.NAMING.endpoint(factory, args, 
options);
+            }
+
+            @Override
+            public String headerName(String component, String method) {
+                return "kafka".equals(component) && "kafkaKey".equals(method) 
? "CamelKafkaKey" : null;
+            }
+        };
+        JavaParseResult result = new 
LwJavaParser().setEndpointDslResolver(withHeaders).parse(source);
+        assertThat(result.isComplete()).as("%s", result.unresolved()).isTrue();
+        SetHeaderDefinition header = (SetHeaderDefinition) 
result.routes().getRoutes().get(0).getOutputs().get(0);
+        assertThat(header.getName()).isEqualTo("CamelKafkaKey");
+
+        // without one it is unknown, and said so
+        result = new LwJavaParser().parse(source);
+        
assertThat(result.unresolved()).extracting(JavaParseResult.Unresolved::reason)
+                .contains("a header of the endpoint DSL the parser does not 
know");
+    }
+
     @Test
     void globalErrorHandlingAndRest() {
         JavaParseResult result = new LwJavaParser().parse("""
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogEndpointDslResolver.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogEndpointDslResolver.java
index 502883bce5ab..c97c05a5f832 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogEndpointDslResolver.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogEndpointDslResolver.java
@@ -18,6 +18,7 @@ package org.apache.camel.dsl.jbang.core.commands.ai;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Locale;
 
 import org.apache.camel.catalog.CamelCatalog;
 import org.apache.camel.java.in.EndpointDslResolver;
@@ -38,6 +39,50 @@ final class CatalogEndpointDslResolver implements 
EndpointDslResolver {
         this.catalog = catalog;
     }
 
+    @Override
+    public String headerName(String component, String method) {
+        // the component method is the factory's name: kafka, aws2S3
+        Endpoint named = NAMING.endpoint(component, List.of("x"), List.of());
+        if (named == null) {
+            return null;
+        }
+        ComponentModel model = catalog.componentModel(named.uri().substring(0, 
named.uri().indexOf("://")));
+        if (model == null) {
+            return null;
+        }
+        for (ComponentModel.EndpointHeaderModel header : 
model.getEndpointHeaders()) {
+            if (method.equals(headerMethod(header.getName()))) {
+                return header.getName();
+            }
+        }
+        return null;
+    }
+
+    /**
+     * The method of a header in the endpoint DSL, as the endpoint DSL is 
generated (EndpointDslMojo): a leading Camel
+     * dropped, then camel case at : - . and _, else the first letter lower 
case.
+     */
+    static String headerMethod(String header) {
+        String name = header.toLowerCase(Locale.ROOT).startsWith("camel") ? 
header.substring(5) : header;
+        if (name.isEmpty()) {
+            return name;
+        }
+        if (name.chars().anyMatch(c -> c == ':' || c == '-' || c == '.' || c 
== '_')) {
+            StringBuilder sb = new StringBuilder();
+            boolean upper = false;
+            for (char ch : name.toLowerCase(Locale.ROOT).toCharArray()) {
+                if (ch == ':' || ch == '-' || ch == '.' || ch == '_') {
+                    upper = sb.length() > 0;
+                } else {
+                    sb.append(upper ? Character.toUpperCase(ch) : ch);
+                    upper = false;
+                }
+            }
+            return sb.toString();
+        }
+        return Character.toLowerCase(name.charAt(0)) + name.substring(1);
+    }
+
     @Override
     public Endpoint endpoint(String factory, List<String> args, List<Option> 
options) {
         Endpoint named = NAMING.endpoint(factory, args, List.of());
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogEndpointDslResolverTest.java
 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogEndpointDslResolverTest.java
new file mode 100644
index 000000000000..15d8be178685
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogEndpointDslResolverTest.java
@@ -0,0 +1,50 @@
+/*
+ * 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.ai;
+
+import org.junit.jupiter.api.Test;
+
+import static 
org.apache.camel.dsl.jbang.core.commands.ai.ProjectOverviewTest.CATALOG;
+import static org.assertj.core.api.Assertions.assertThat;
+
+class CatalogEndpointDslResolverTest {
+
+    private final CatalogEndpointDslResolver resolver = new 
CatalogEndpointDslResolver(CATALOG);
+
+    @Test
+    void theHeaderNamesOfTheEndpointDsl() {
+        // CAMEL-25204: checked against every header method the endpoint DSL 
generates, 3456 of them
+        assertThat(resolver.headerName("file", 
"fileName")).isEqualTo("CamelFileName");
+        assertThat(resolver.headerName("kafka", 
"kafkaKey")).isEqualTo("CamelKafkaKey");
+        // a header name with dots or dashes, and a component whose factory 
differs from its scheme
+        assertThat(resolver.headerName("cxfrs", 
"orgApacheCxfMessageMessageProtocolHeaders"))
+                .isEqualTo("org.apache.cxf.message.Message.PROTOCOL_HEADERS");
+        assertThat(resolver.headerName("imap", 
"replyTo")).isEqualTo("Reply-To");
+        assertThat(resolver.headerName("aws2S3", 
"awsS3Key")).isEqualTo("CamelAwsS3Key");
+        // not a header, not a component
+        assertThat(resolver.headerName("kafka", "noSuchHeader")).isNull();
+        assertThat(resolver.headerName("noSuchComponent", 
"fileName")).isNull();
+    }
+
+    @Test
+    void theMethodOfAHeaderAsTheEndpointDslIsGenerated() {
+        
assertThat(CatalogEndpointDslResolver.headerMethod("CamelFileName")).isEqualTo("fileName");
+        
assertThat(CatalogEndpointDslResolver.headerMethod("Content-Type")).isEqualTo("contentType");
+        
assertThat(CatalogEndpointDslResolver.headerMethod("CamelBox.")).isEqualTo("box");
+        
assertThat(CatalogEndpointDslResolver.headerMethod("kafka.KEY")).isEqualTo("kafkaKey");
+    }
+}

Reply via email to