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";