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

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


The following commit(s) were added to refs/heads/main by this push:
     new eda59709c0 Fixes #9153. Native build fails on the kamelet 
DelegatingSchemaResolver without camel-jackson
eda59709c0 is described below

commit eda59709c07a64404b012d283c4ca94ab2e8f16f
Author: Jiří Ondrušek <[email protected]>
AuthorDate: Tue Sep 15 08:33:44 2026 +0200

    Fixes #9153. Native build fails on the kamelet DelegatingSchemaResolver 
without camel-jackson
    
    camel-kamelet ships DelegatingSchemaResolver, which hard-references the
    schema resolvers of the optional camel-jackson, camel-jackson-avro and
    camel-jackson-protobuf dependencies. The class is the #class: template
    bean of the catalog's resolve-pojo-schema-action kamelet; whenever that
    kamelet is embedded into the native image (any application with the
    default quarkus.camel.kamelet.identifiers=*), the deployment registers
    the class reflectively and the GraalVM analysis fails parsing it if any
    of the optional dependencies is absent:
    
      UnresolvedElementException: NoClassDefFoundError:
      org/apache/camel/component/jackson/transform/JsonSchemaResolver
    
    KameletProcessor now skips the reflective registration of the class
    unless all three resolver classes are on the classpath, so the
    native-image analysis never reaches it. JVM mode is unaffected - the
    class was never loadable without the optional dependencies anyway.
    
    The kamelet integration tests now embed resolve-pojo-schema-action as
    the regression guard - without the fix their native build fails - and
    gain a file-backed source kamelet test along the way.
    
    Co-authored-by: Claude Fable 5 <[email protected]>
---
 .../kamelet/deployment/KameletProcessor.java       | 13 ++++++
 integration-tests/kamelet/pom.xml                  | 17 ++++++++
 .../component/kamelet/it/KameletResource.java      | 19 +++++++++
 .../component/kamelet/it/KameletRoutes.java        |  3 ++
 .../src/main/resources/application.properties      |  7 +++-
 .../kamelets/file-source-test.kamelet.yaml         | 46 ++++++++++++++++++++++
 .../quarkus/component/kamelet/it/KameletTest.java  | 23 +++++++++++
 7 files changed, 127 insertions(+), 1 deletion(-)

diff --git 
a/extensions/kamelet/deployment/src/main/java/org/apache/camel/quarkus/component/kamelet/deployment/KameletProcessor.java
 
b/extensions/kamelet/deployment/src/main/java/org/apache/camel/quarkus/component/kamelet/deployment/KameletProcessor.java
index 452a10f491..b548ec7fcc 100644
--- 
a/extensions/kamelet/deployment/src/main/java/org/apache/camel/quarkus/component/kamelet/deployment/KameletProcessor.java
+++ 
b/extensions/kamelet/deployment/src/main/java/org/apache/camel/quarkus/component/kamelet/deployment/KameletProcessor.java
@@ -33,6 +33,7 @@ import java.util.List;
 import java.util.Set;
 import java.util.stream.Collectors;
 
+import io.quarkus.bootstrap.classloading.QuarkusClassLoader;
 import io.quarkus.bootstrap.model.ApplicationModel;
 import io.quarkus.deployment.annotations.BuildProducer;
 import io.quarkus.deployment.annotations.BuildStep;
@@ -58,6 +59,10 @@ class KameletProcessor {
     private static final String FILE_PREFIX = "file";
     private static final String KAMELET_FILE_EXTENSION = ".kamelet.yaml";
     private static final String FEATURE = "camel-kamelet";
+    private static final String DELEGATING_SCHEMA_RESOLVER = 
"org.apache.camel.component.kamelet.utils.format.schema.DelegatingSchemaResolver";
+    private static final String JSON_SCHEMA_RESOLVER = 
"org.apache.camel.component.jackson.transform.JsonSchemaResolver";
+    private static final String AVRO_SCHEMA_RESOLVER = 
"org.apache.camel.component.jackson.avro.transform.AvroSchemaResolver";
+    private static final String PROTOBUF_SCHEMA_RESOLVER = 
"org.apache.camel.component.jackson.protobuf.transform.ProtobufSchemaResolver";
 
     @BuildStep
     FeatureBuildItem feature() {
@@ -144,6 +149,14 @@ class KameletProcessor {
         // Automatically register kamelet beans for reflection
         if (!kameletResources.isEmpty()) {
             Set<String> kameletBeanClasses = 
resolveKameletBeanClasses(kameletResources);
+            // DelegatingSchemaResolver hard-references the schema resolvers 
of the optional camel-jackson,
+            // camel-jackson-avro and camel-jackson-protobuf dependencies; if 
any of them is missing,
+            // registering it for reflection would make the native-image 
analysis fail parsing it
+            if 
(!QuarkusClassLoader.isClassPresentAtRuntime(JSON_SCHEMA_RESOLVER)
+                    || 
!QuarkusClassLoader.isClassPresentAtRuntime(AVRO_SCHEMA_RESOLVER)
+                    || 
!QuarkusClassLoader.isClassPresentAtRuntime(PROTOBUF_SCHEMA_RESOLVER)) {
+                kameletBeanClasses.remove(DELEGATING_SCHEMA_RESOLVER);
+            }
             if (!kameletBeanClasses.isEmpty()) {
                 
reflectiveClass.produce(ReflectiveClassBuildItem.builder(kameletBeanClasses.toArray(new
 String[0]))
                         .fields()
diff --git a/integration-tests/kamelet/pom.xml 
b/integration-tests/kamelet/pom.xml
index 06eeaf254a..24e33b5b16 100644
--- a/integration-tests/kamelet/pom.xml
+++ b/integration-tests/kamelet/pom.xml
@@ -35,6 +35,10 @@
             <groupId>org.apache.camel.quarkus</groupId>
             <artifactId>camel-quarkus-direct</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-file</artifactId>
+        </dependency>
         <dependency>
             <groupId>org.apache.camel.quarkus</groupId>
             <artifactId>camel-quarkus-kamelet</artifactId>
@@ -153,6 +157,19 @@
                         </exclusion>
                     </exclusions>
                 </dependency>
+                <dependency>
+                    <groupId>org.apache.camel.quarkus</groupId>
+                    <artifactId>camel-quarkus-file-deployment</artifactId>
+                    <version>${project.version}</version>
+                    <type>pom</type>
+                    <scope>test</scope>
+                    <exclusions>
+                        <exclusion>
+                            <groupId>*</groupId>
+                            <artifactId>*</artifactId>
+                        </exclusion>
+                    </exclusions>
+                </dependency>
                 <dependency>
                     <groupId>org.apache.camel.quarkus</groupId>
                     <artifactId>camel-quarkus-kamelet-deployment</artifactId>
diff --git 
a/integration-tests/kamelet/src/main/java/org/apache/camel/quarkus/component/kamelet/it/KameletResource.java
 
b/integration-tests/kamelet/src/main/java/org/apache/camel/quarkus/component/kamelet/it/KameletResource.java
index b3ad8ae43d..ab8f2c5060 100644
--- 
a/integration-tests/kamelet/src/main/java/org/apache/camel/quarkus/component/kamelet/it/KameletResource.java
+++ 
b/integration-tests/kamelet/src/main/java/org/apache/camel/quarkus/component/kamelet/it/KameletResource.java
@@ -16,6 +16,8 @@
  */
 package org.apache.camel.quarkus.component.kamelet.it;
 
+import java.nio.file.Files;
+
 import jakarta.inject.Inject;
 import jakarta.ws.rs.Consumes;
 import jakarta.ws.rs.GET;
@@ -55,6 +57,23 @@ public class KameletResource {
         return consumerTemplate.receiveBody("kamelet:tick", 10000, 
Integer.class);
     }
 
+    /** Writes a document into the file-source-test kamelet's directory — 
app-side, so native mode shares the path. */
+    @Path("/file/{name}")
+    @POST
+    @Consumes(MediaType.TEXT_PLAIN)
+    public void writeFile(@PathParam("name") String name, String content) 
throws Exception {
+        java.nio.file.Path dir = 
java.nio.file.Path.of("target/kamelet-file-source-test");
+        Files.createDirectories(dir);
+        Files.writeString(dir.resolve(name), content);
+    }
+
+    @Path("/file-content")
+    @GET
+    @Produces(MediaType.TEXT_PLAIN)
+    public String fileContent() {
+        return consumerTemplate.receiveBody("seda:file-source-test", 10000, 
String.class);
+    }
+
     @Path("/property")
     @GET
     @Produces(MediaType.TEXT_PLAIN)
diff --git 
a/integration-tests/kamelet/src/main/java/org/apache/camel/quarkus/component/kamelet/it/KameletRoutes.java
 
b/integration-tests/kamelet/src/main/java/org/apache/camel/quarkus/component/kamelet/it/KameletRoutes.java
index 5e18f24c2b..5d67254c2f 100644
--- 
a/integration-tests/kamelet/src/main/java/org/apache/camel/quarkus/component/kamelet/it/KameletRoutes.java
+++ 
b/integration-tests/kamelet/src/main/java/org/apache/camel/quarkus/component/kamelet/it/KameletRoutes.java
@@ -76,6 +76,9 @@ public class KameletRoutes extends RouteBuilder {
 
         from("kamelet:timer-source?repeatCount=1&message=Hello From Timer 
Source Kamelet")
                 .to("seda:timer-source");
+
+        
from("kamelet:file-source-test?directory=target/kamelet-file-source-test")
+                .to("seda:file-source-test");
     }
 
     @RegisterForReflection
diff --git 
a/integration-tests/kamelet/src/main/resources/application.properties 
b/integration-tests/kamelet/src/main/resources/application.properties
index ac2e5ac72e..f79f069ec6 100644
--- a/integration-tests/kamelet/src/main/resources/application.properties
+++ b/integration-tests/kamelet/src/main/resources/application.properties
@@ -17,5 +17,10 @@
 
camel.component.kamelet.location=classpath:kamelets,classpath:org/apache/camel/quarkus/custom/kamelet
 camel.kamelet.setBodyFromProperties.bodyValueFromProperty=Camel Quarkus 
Kamelet Property
 camel.main.routes-include-pattern=pipes/greeting-pipe.yaml
-quarkus.camel.kamelet.identifiers = 
injector,logger,greeting,greeting-from-property,custom,timer-source
+# resolve-pojo-schema-action is embedded from the camel-kamelets catalog on 
purpose: its
+# #class: template bean is 
org.apache.camel.component.kamelet.utils.format.schema
+# .DelegatingSchemaResolver, which the deployment would register reflectively 
- the #9153
+# regression guard: without the DelegatingSchemaResolver handling in 
KameletProcessor the
+# native-image analysis fails parsing it, camel-jackson being absent here
+quarkus.camel.kamelet.identifiers = 
injector,logger,greeting,greeting-from-property,custom,timer-source,file-source-test,resolve-pojo-schema-action
 quarkus.native.resources.includes=kamelets-runtime/*.xml,pipes/*.yaml
\ No newline at end of file
diff --git 
a/integration-tests/kamelet/src/main/resources/kamelets/file-source-test.kamelet.yaml
 
b/integration-tests/kamelet/src/main/resources/kamelets/file-source-test.kamelet.yaml
new file mode 100644
index 0000000000..bbee229c32
--- /dev/null
+++ 
b/integration-tests/kamelet/src/main/resources/kamelets/file-source-test.kamelet.yaml
@@ -0,0 +1,46 @@
+#
+# 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.
+#
+
+apiVersion: camel.apache.org/v1
+kind: Kamelet
+metadata:
+  name: file-source-test
+  labels:
+    camel.apache.org/kamelet.type: "source"
+    camel.apache.org/kamelet.name: "file-source-test"
+    camel.apache.org/kamelet.version: "v1"
+    camel.apache.org/kamelet.revision: "1"
+spec:
+  definition:
+    title: "File source test"
+    description: >-
+      Watches a directory with the file consumer.
+    properties:
+      directory:
+        title: Directory
+        description: The directory to watch
+        type: string
+  dependencies:
+    - "camel:file"
+  template:
+    from:
+      uri: "file:{{directory}}"
+      parameters:
+        noop: "true"
+        idempotent: "true"
+      steps:
+        - to: "kamelet:sink"
diff --git 
a/integration-tests/kamelet/src/test/java/org/apache/camel/quarkus/component/kamelet/it/KameletTest.java
 
b/integration-tests/kamelet/src/test/java/org/apache/camel/quarkus/component/kamelet/it/KameletTest.java
index 6ca4af34c3..04332a12a7 100644
--- 
a/integration-tests/kamelet/src/test/java/org/apache/camel/quarkus/component/kamelet/it/KameletTest.java
+++ 
b/integration-tests/kamelet/src/test/java/org/apache/camel/quarkus/component/kamelet/it/KameletTest.java
@@ -26,6 +26,29 @@ import static org.hamcrest.Matchers.is;
 @QuarkusTest
 class KameletTest {
 
+    /**
+     * A file-backed source kamelet. Note the module also embeds the catalog's
+     * {@code resolve-pojo-schema-action} kamelet (see {@code 
quarkus.camel.kamelet.identifiers}):
+     * its {@code #class:} template bean is the kamelet component's
+     * {@code DelegatingSchemaResolver}, whose reflective registration made 
native builds fail
+     * without camel-jackson on the classpath — the native image building at 
all is the
+     * regression assertion of #9153.
+     */
+    @Test
+    public void testFileSourceKamelet() {
+        RestAssured.given()
+                .contentType(ContentType.TEXT)
+                .body("Hello From File Source Kamelet")
+                .post("/kamelet/file/greeting.txt")
+                .then()
+                .statusCode(204);
+
+        RestAssured.get("/kamelet/file-content")
+                .then()
+                .statusCode(200)
+                .body(is("Hello From File Source Kamelet"));
+    }
+
     @Test
     public void testKameletProducing() {
         String message = "Camel Quarkus Kamelet";

Reply via email to