This is an automated email from the ASF dual-hosted git repository. nfilotto pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel-spring-boot-examples.git
The following commit(s) were added to refs/heads/main by this push: new eedf6a6 CAMEL-19308: Add a Spring Boot AOT basic example (#111) eedf6a6 is described below commit eedf6a6390dd8b6648b8302aec46286133a02d59 Author: Nicolas Filotto <essob...@users.noreply.github.com> AuthorDate: Fri Jun 23 07:40:26 2023 +0200 CAMEL-19308: Add a Spring Boot AOT basic example (#111) ## Motivation Illustrate thanks to an example how Spring Boot AOT can be leveraged to create a native Camel Spring Boot application. ## Modifications: * Add a new module called `aot-basic` --- README.adoc | 4 +- aot-basic/pom.xml | 150 +++++++++++++++++++++ aot-basic/readme.adoc | 78 +++++++++++ aot-basic/routes/demo-route.yaml | 8 ++ aot-basic/routes/my-route.xml | 33 +++++ .../src/main/java/sample/camel/Application.java | 37 +++++ aot-basic/src/main/java/sample/camel/MyBean.java | 42 ++++++ .../src/main/java/sample/camel/MyCamelRouter.java | 45 +++++++ .../src/main/resources/application.properties | 34 +++++ pom.xml | 3 +- 10 files changed, 432 insertions(+), 2 deletions(-) diff --git a/README.adoc b/README.adoc index 71b2ccb..d8521f8 100644 --- a/README.adoc +++ b/README.adoc @@ -27,12 +27,14 @@ readme's instructions. === Examples // examples: START -Number of Examples: 48 (0 deprecated) +Number of Examples: 49 (0 deprecated) [width="100%",cols="4,2,4",options="header"] |=== | Example | Category | Description +| link:aot-basic/readme.adoc[AOT Basic] (aot-basic) | | An example showing how to leverage Spring Boot AOT to build a native Camel Spring Boot application + | link:azure/camel-example-spring-boot-azure-eventhubs/README.adoc[Spring Boot Azure Eventhubs] (camel-example-spring-boot-azure-eventhubs) | | An example showing how to work with Camel, Azure Event Hubs and Spring Boot | link:azure/camel-example-spring-boot-azure-servicebus/README.adoc[Spring Boot Azure Servicebus] (camel-example-spring-boot-azure-servicebus) | | An example showing how to work with Camel, Azure Service Bus and Spring Boot diff --git a/aot-basic/pom.xml b/aot-basic/pom.xml new file mode 100644 index 0000000..f3150ae --- /dev/null +++ b/aot-basic/pom.xml @@ -0,0 +1,150 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + 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. + +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.apache.camel.springboot.example</groupId> + <artifactId>examples</artifactId> + <version>4.0.0-SNAPSHOT</version> + </parent> + + <artifactId>camel-example-spring-boot-aot-basic</artifactId> + <name>Camel SB Examples :: AOT Basic</name> + <description>Example on how to leverage Spring Boot AOT in Camel Spring Boot</description> + + <properties> + <category>AOT</category> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> + </properties> + + <dependencyManagement> + <dependencies> + <!-- Camel BOM --> + <dependency> + <groupId>org.apache.camel.springboot</groupId> + <artifactId>camel-spring-boot-bom</artifactId> + <version>${project.version}</version> + <type>pom</type> + <scope>import</scope> + </dependency> + </dependencies> + </dependencyManagement> + + <dependencies> + <!-- Camel --> + <dependency> + <groupId>org.apache.camel.springboot</groupId> + <artifactId>camel-spring-boot-starter</artifactId> + </dependency> + <dependency> + <groupId>org.apache.camel.springboot</groupId> + <artifactId>camel-yaml-dsl-starter</artifactId> + </dependency> + <dependency> + <groupId>org.apache.camel.springboot</groupId> + <artifactId>camel-xml-io-dsl-starter</artifactId> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + </plugin> + <plugin> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-maven-plugin</artifactId> + <version>${spring-boot-version}</version> + <executions> + <execution> + <goals> + <goal>repackage</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> + <profiles> + <profile> + <id>native</id> + <build> + <pluginManagement> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + <configuration> + <archive> + <manifestEntries> + <Spring-Boot-Native-Processed>true</Spring-Boot-Native-Processed> + </manifestEntries> + </archive> + </configuration> + </plugin> + <plugin> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-maven-plugin</artifactId> + <configuration> + <image> + <builder>paketobuildpacks/builder:tiny</builder> + <env> + <BP_NATIVE_IMAGE>true</BP_NATIVE_IMAGE> + </env> + </image> + </configuration> + <executions> + <execution> + <id>process-aot</id> + <goals> + <goal>process-aot</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + <configuration> + <classesDirectory>${project.build.outputDirectory}</classesDirectory> + <metadataRepository> + <enabled>true</enabled> + </metadataRepository> + <requiredVersion>22.3</requiredVersion> + </configuration> + <executions> + <execution> + <id>add-reachability-metadata</id> + <goals> + <goal>add-reachability-metadata</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </pluginManagement> + </build> + </profile> + </profiles> +</project> diff --git a/aot-basic/readme.adoc b/aot-basic/readme.adoc new file mode 100644 index 0000000..4401a8e --- /dev/null +++ b/aot-basic/readme.adoc @@ -0,0 +1,78 @@ +== Camel with Spring Boot AOT + +This example shows how you can build and execute a native image by leveraging Spring Boot AOT to launch +Camel routes written in Java, XML and YAML in native mode. + +=== How to build + +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. +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: + +[source,console] +---- +mvn -Pnative native:compile +---- + +Building a GraalVM Native Image is a relatively slow process, so be patient, it should take a couple of minutes. + +=== How to run + +A GraalVM Native Image is a complete, platform-specific executable. At this point, you don't need any Java Virtual +Machine to launch your application. + +So just, launch the executable file created in the target directory as next: + +[source,console] +---- +./target/camel-example-spring-boot-aot-basic +---- + +Your application will start in native mode. The output of the application should be of the next type: + +[source] +---- + . ____ _ __ _ _ + /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ +( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ + \\/ ___)| |_)| | | | | || (_| | ) ) ) ) + ' |____| .__|_| |_|_| |_\__, | / / / / + =========|_|==============|___/=/_/_/_/ + :: 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.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 +---- + +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. + +For more details, please check the related https://docs.spring.io/spring-boot/docs/current/reference/html/native-image.html[Spring Boot documentation]. + +=== Help and contributions + +If you hit any problem using Camel or have some feedback, then please +https://camel.apache.org/support.html[let us know]. + +We also love contributors, so +https://camel.apache.org/contributing.html[get involved] :-) + +The Camel riders! diff --git a/aot-basic/routes/demo-route.yaml b/aot-basic/routes/demo-route.yaml new file mode 100644 index 0000000..b802980 --- /dev/null +++ b/aot-basic/routes/demo-route.yaml @@ -0,0 +1,8 @@ +- route: + id: helloYAML + from: + uri: timer://yaml?period=2000 + steps: + - log: + message: "Hello World from YAML at ${header.CamelTimerFiredTime}" + diff --git a/aot-basic/routes/my-route.xml b/aot-basic/routes/my-route.xml new file mode 100644 index 0000000..5515a2c --- /dev/null +++ b/aot-basic/routes/my-route.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + 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. + +--> +<routes xmlns="http://camel.apache.org/schema/spring"> + + <!-- run this application with mvn spring-boot:run and then update and save this file + to see live changes in Camel --> + + <route id="helloXML"> + <from uri="timer:xml?period=2000"/> + <transform> + <constant>Hello World From XML</constant> + </transform> + <log message="${body} at ${header.CamelTimerFiredTime}"/> + </route> + +</routes> \ No newline at end of file diff --git a/aot-basic/src/main/java/sample/camel/Application.java b/aot-basic/src/main/java/sample/camel/Application.java new file mode 100644 index 0000000..32322d9 --- /dev/null +++ b/aot-basic/src/main/java/sample/camel/Application.java @@ -0,0 +1,37 @@ +/* + * 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 sample.camel; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +//CHECKSTYLE:OFF + +/** + * A sample Spring Boot application that starts the Camel routes. + */ +@SpringBootApplication +public class Application { + + /** + * A main method to start this application. + */ + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} +//CHECKSTYLE:ON diff --git a/aot-basic/src/main/java/sample/camel/MyBean.java b/aot-basic/src/main/java/sample/camel/MyBean.java new file mode 100644 index 0000000..0630630 --- /dev/null +++ b/aot-basic/src/main/java/sample/camel/MyBean.java @@ -0,0 +1,42 @@ +/* + * 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 sample.camel; + +import org.springframework.aot.hint.annotation.Reflective; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +/** + * A bean that returns a message when you call the {@link #saySomething()} method. + * <p/> + * Uses <tt>@Component("myBean")</tt> to register this bean with the name <tt>myBean</tt> + * that we use in the Camel route to lookup this bean. + */ +@Component("myBean") +public class MyBean { + + private int counter; + + @Value("${greeting} From Java") + private String say; + + @Reflective + public String saySomething() { + return String.format("%s I am invoked %d times", say, ++counter); + } + +} diff --git a/aot-basic/src/main/java/sample/camel/MyCamelRouter.java b/aot-basic/src/main/java/sample/camel/MyCamelRouter.java new file mode 100644 index 0000000..aecdfc0 --- /dev/null +++ b/aot-basic/src/main/java/sample/camel/MyCamelRouter.java @@ -0,0 +1,45 @@ +/* + * 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 sample.camel; + +import org.apache.camel.builder.RouteBuilder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * A simple Camel route that triggers from a timer and calls a bean and prints to system out. + * <p/> + * Use <tt>@Component</tt> to make Camel auto-detect this route when starting. + */ +@Component +public class MyCamelRouter extends RouteBuilder { + + // we can use spring dependency injection + @Autowired + MyBean myBean; + + @Override + public void configure() throws Exception { + // start from a timer + from("timer:java?period={{myPeriod}}").routeId("helloJava") + // and call the bean + .bean(myBean, "saySomething") + // and log it + .log("${body} at ${header.CamelTimerFiredTime}"); + } + +} diff --git a/aot-basic/src/main/resources/application.properties b/aot-basic/src/main/resources/application.properties new file mode 100644 index 0000000..b31e507 --- /dev/null +++ b/aot-basic/src/main/resources/application.properties @@ -0,0 +1,34 @@ +## --------------------------------------------------------------------------- +## 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. +## --------------------------------------------------------------------------- + +logging.level.org.springframework = INFO +logging.level.org.apache.camel.spring.boot = INFO +logging.level.org.apache.camel.health = DEBUG +logging.level.org.apache.camel.impl.health = DEBUG +logging.level.sample.camel = DEBUG + +camel.springboot.name = MyCamelNative + +camel.springboot.main-run-controller: true + +camel.springboot.routes-include-pattern: file:${user.dir}/routes/*.yaml,file:${user.dir}/routes/*.xml + +# what to say +greeting: Hello World + +# how often to trigger the timer (millis) +myPeriod: 2000 \ No newline at end of file diff --git a/pom.xml b/pom.xml index 5fadca7..6f038c8 100644 --- a/pom.xml +++ b/pom.xml @@ -38,8 +38,9 @@ <module>spring-boot</module> <module>actuator-http-metrics</module> <module>amqp</module> + <module>aot-basic</module> <module>arangodb</module> - <module>artemis</module> + <module>artemis</module> <module>aws-secrets-manager</module> <module>aws2-s3</module> <module>dynamic-router-eip</module>