Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 57 additions & 23 deletions aot-basic/readme.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,10 @@ Camel routes written in Java, XML and YAML in native mode.

To be able to build a GraalVM Native Image, you need to have GraalVM installed on your local machine.

1. So first, download it from https://github.com/graalvm/graalvm-ce-builds/releases, choose Java 17 and the archive corresponding to your platform.
1. So first, download it from https://github.com/graalvm/graalvm-ce-builds/releases, choose Java 17 (or newer) and the archive corresponding to your platform. Recent GraalVM distributions (22.3+) bundle `native-image` out of the box, so there is no separate `gu install native-image` step to run.
2. Then, sets the environment variable `JAVA_HOME` to the home directory of GraalVM.
3. Finally, install `native-image` with the command `$JAVA_HOME/bin/gu install native-image`

Once, the 3 steps done properly, you can build the application with the next command:
Once the 2 steps are done properly, you can build the application with the next command:

[source,console]
----
Expand Down Expand Up @@ -42,31 +41,66 @@ Your application will start in native mode. The output of the application should
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.1.0)

2023-06-22T20:22:57.135+02:00 INFO 12741 --- [ main] sample.camel.Application : Starting AOT-processed Application using Java 17.0.7 with PID 12741 (xxx/camel-spring-boot-examples/aot-basic/target/camel-example-spring-boot-aot-basic started by yyy in xxx/camel-spring-boot-examples/aot-basic)
2023-06-22T20:22:57.135+02:00 DEBUG 12741 --- [ main] sample.camel.Application : Running with Spring Boot v3.1.0, Spring v6.0.9
2023-06-22T20:22:57.135+02:00 INFO 12741 --- [ main] sample.camel.Application : No active profile set, falling back to 1 default profile: "default"
2023-06-22T20:22:57.155+02:00 DEBUG 12741 --- [ main] o.a.c.i.h.DefaultHealthCheckRegistry : HealthCheckRepository with id registry-health-check-repository successfully registered
2023-06-22T20:22:57.251+02:00 INFO 12741 --- [ main] o.a.c.impl.engine.AbstractCamelContext : Apache Camel 4.0.0-SNAPSHOT (MyCamelNative) is starting
2023-06-22T20:22:57.251+02:00 INFO 12741 --- [ main] c.s.b.CamelSpringBootApplicationListener : Starting CamelMainRunController to ensure the main thread keeps running
2023-06-22T20:22:57.251+02:00 INFO 12741 --- [inRunController] org.apache.camel.main.MainSupport : Apache Camel (Main) 4.0.0-SNAPSHOT is starting
2023-06-22T20:22:57.265+02:00 INFO 12741 --- [ main] o.a.c.impl.engine.AbstractCamelContext : Routes startup (started:3)
2023-06-22T20:22:57.266+02:00 INFO 12741 --- [ main] o.a.c.impl.engine.AbstractCamelContext : Started helloJava (timer://java)
2023-06-22T20:22:57.266+02:00 INFO 12741 --- [ main] o.a.c.impl.engine.AbstractCamelContext : Started helloYAML (timer://yaml)
2023-06-22T20:22:57.266+02:00 INFO 12741 --- [ main] o.a.c.impl.engine.AbstractCamelContext : Started helloXML (timer://xml)
2023-06-22T20:22:57.266+02:00 INFO 12741 --- [ main] o.a.c.impl.engine.AbstractCamelContext : Apache Camel 4.0.0-SNAPSHOT (MyCamelNative) started in 14ms (build:0ms init:0ms start:14ms)
2023-06-22T20:22:57.266+02:00 INFO 12741 --- [ main] sample.camel.Application : Started Application in 0.144 seconds (process running for 0.159)
2023-06-22T20:22:58.258+02:00 INFO 12741 --- [ - timer://java] helloJava : Hello World From Java I am invoked 1 times at Thu Jun 22 20:22:58 CEST 2023
2023-06-22T20:22:58.269+02:00 INFO 12741 --- [ - timer://yaml] demo-route.camel.yaml:6 : Hello World from YAML at Thu Jun 22 20:22:58 CEST 2023
2023-06-22T20:22:58.269+02:00 INFO 12741 --- [3 - timer://xml] my-route.xml:30 : Hello World From XML at Thu Jun 22 20:22:58 CEST 2023
:: Spring Boot :: (v4.1.1)

2026-09-03T19:25:47.907+02:00 INFO 52866 --- [ main] sample.camel.Application : Starting AOT-processed Application using Java 25.0.4.1 with PID 52866 (xxx/camel-spring-boot-examples/aot-basic/target/camel-example-spring-boot-aot-basic started by yyy in xxx/camel-spring-boot-examples/aot-basic)
2026-09-03T19:25:47.907+02:00 DEBUG 52866 --- [ main] sample.camel.Application : Running with Spring Boot v4.1.1, Spring v7.0.9
2026-09-03T19:25:47.907+02:00 INFO 52866 --- [ main] sample.camel.Application : No active profile set, falling back to 1 default profile: "default"
2026-09-03T19:25:47.919+02:00 DEBUG 52866 --- [ main] o.a.c.i.h.DefaultHealthCheckRegistry : HealthCheckRepository with id registry-health-check-repository successfully registered
2026-09-03T19:25:47.960+02:00 INFO 52866 --- [ main] o.a.c.impl.engine.AbstractCamelContext : Apache Camel 4.23.0-SNAPSHOT (MyCamelNative) is starting
2026-09-03T19:25:47.961+02:00 INFO 52866 --- [ main] c.s.b.CamelSpringBootApplicationListener : Starting CamelMainRunController to ensure the main thread keeps running
2026-09-03T19:25:47.961+02:00 INFO 52866 --- [inRunController] org.apache.camel.main.MainSupport : Apache Camel (Main) 4.23.0-SNAPSHOT is starting
2026-09-03T19:25:47.961+02:00 INFO 52866 --- [ main] o.a.c.impl.engine.AbstractCamelContext : Routes startup (total:3)
2026-09-03T19:25:47.961+02:00 INFO 52866 --- [ main] o.a.c.impl.engine.AbstractCamelContext : Started helloJava (timer://java)
2026-09-03T19:25:47.961+02:00 INFO 52866 --- [ main] o.a.c.impl.engine.AbstractCamelContext : Started helloYAML (timer://yaml)
2026-09-03T19:25:47.961+02:00 INFO 52866 --- [ main] o.a.c.impl.engine.AbstractCamelContext : Started helloXML (timer://xml)
2026-09-03T19:25:47.961+02:00 INFO 52866 --- [ main] o.a.c.impl.engine.AbstractCamelContext : Apache Camel 4.23.0-SNAPSHOT (MyCamelNative) started in 1ms (build:0ms init:0ms start:1ms)
2026-09-03T19:25:47.962+02:00 INFO 52866 --- [ main] sample.camel.Application : Started Application in 0.066 seconds (process running for 0.081)
2026-09-03T19:25:48.966+02:00 INFO 52866 --- [ - timer://java] helloJava : Hello World From Java I am invoked 1 times at Thu Sep 03 19:25:48 CEST 2026
2026-09-03T19:25:48.966+02:00 INFO 52866 --- [ - timer://yaml] helloYAML : Hello World from YAML at Thu Sep 03 19:25:48 CEST 2026
2026-09-03T19:25:48.966+02:00 INFO 52866 --- [3 - timer://xml] helloXML : Hello World From XML at Thu Sep 03 19:25:48 CEST 2026
----

As you can see the application took only *159* milliseconds to fully start while the exact same application in JVM mode
starts in more than *2* seconds.
As you can see the application took only *66* milliseconds to fully start while the exact same application in JVM mode
starts in more than *1* second.

For more details, please check the related https://docs.spring.io/spring-boot/docs/current/reference/html/native-image.html[Spring Boot documentation].

=== How the native-image metadata was generated

Spring Boot's AOT processing (`process-aot`) only sees reflection that goes through *Spring's* bean
machinery. Camel resolves several of its own pieces (type converter loaders, languages, components,
route loaders, ...) through its own internal SPI, invisible to Spring AOT, so extra GraalVM
reachability metadata is required. It lives under
`src/main/resources/META-INF/native-image/org.apache.camel.springboot.example/camel-example-spring-boot-aot-basic-extra/`
(a sibling artifact-id directory, chosen so it doesn't collide with the `reachability-metadata.json`
that Spring AOT itself generates at the example's own coordinates).

It was produced like this:

1. Run the app in JVM mode with the GraalVM tracing agent attached, exercising all three routes:
+
[source,console]
----
mvn -Dspring-boot.run.jvmArguments="-agentlib:native-image-agent=config-output-dir=/tmp/native-agent-config" spring-boot:run
----
2. Let it run for a few timer cycles, then stop it. `/tmp/native-agent-config/reachability-metadata.json`
now contains every reflective/resource access the agent observed on the JVM.
3. Diff that trace against what's already covered elsewhere (Spring AOT's own generated
`target/spring-aot/main/resources/.../reachability-metadata.json`, plus the metadata already bundled
inside the `camel-*` dependency jars themselves) and keep only the `org.apache.camel.*` entries that
are genuinely missing.
4. Rebuild the native image (`mvn -Pnative native:compile`) and run the executable. Since the agent
traces JVM-mode reflection rather than native-image's own closed-world semantics, expect a few more
`MissingReflectionRegistrationError`/missing-resource errors on the first native runs; each error
names the exact class/resource to add. Repeat build-and-run until it starts cleanly.
5. A separate, unrelated failure (`UnsupportedCharsetException: UTF-32BE`, from snakeyaml) needed
`-H:+AddAllCharsets` rather than a reflection entry — that's why `native-image.properties` sits
alongside `reachability-metadata.json` in the same directory rather than being folded into it.

If you upgrade the Spring Boot, Camel, or GraalVM versions used by this example, re-run this recipe:
newer versions can both fix existing gaps and introduce new ones.

=== Help and contributions

If you hit any problem using Camel or have some feedback, then please
Expand Down
2 changes: 1 addition & 1 deletion aot-basic/routes/demo-route.camel.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
- route:
id: helloYAML
from:
uri: timer://yaml?period=2000
uri: timer://yaml?period=2000&includeMetadata=true
steps:
- log:
message: "Hello World from YAML at ${header.CamelTimerFiredTime}"
Expand Down
2 changes: 1 addition & 1 deletion aot-basic/routes/my-route.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
to see live changes in Camel -->

<route id="helloXML">
<from uri="timer:xml?period=2000"/>
<from uri="timer:xml?period=2000&amp;includeMetadata=true"/>
<transform>
<constant>Hello World From XML</constant>
</transform>
Expand Down
2 changes: 1 addition & 1 deletion aot-basic/src/main/java/sample/camel/MyCamelRouter.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public class MyCamelRouter extends RouteBuilder {
@Override
public void configure() throws Exception {
// start from a timer
from("timer:java?period={{myPeriod}}").routeId("helloJava")
from("timer:java?period={{myPeriod}}&includeMetadata=true").routeId("helloJava")
// and call the bean
.bean(myBean, "saySomething")
// and log it
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Args = -H:+AddAllCharsets
Loading
Loading