This is an automated email from the ASF dual-hosted git repository. lburgazzoli pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/camel-quarkus.git
commit 82ced6bef097ef093b6e74df0639b33d038ef462 Author: lburgazzoli <lburgazz...@gmail.com> AuthorDate: Fri Jun 19 00:45:52 2020 +0200 Support Quarkus command mode #1037 --- examples/pom.xml | 1 + examples/timer-log-main/README.adoc | 56 +++++++++ examples/timer-log-main/pom.xml | 90 +++++++++++++++ .../src/main/java/org/acme/timer/Main.java | 18 +-- .../src/main/java/org/acme/timer/TimerRoute.java | 23 ++-- .../src/main/resources/application.properties | 42 +++++++ .../core/deployment/CamelBootstrapProcessor.java | 21 +++- .../core/deployment/spi/CamelRuntimeBuildItem.java | 12 +- .../camel/quarkus/core/CamelBootstrapRecorder.java | 9 +- .../camel/quarkus/core/CamelContextRuntime.java | 37 +++++- .../apache/camel/quarkus/core/CamelRuntime.java | 4 +- extensions-core/main/deployment/pom.xml | 5 + .../main/deployment/CamelMainProcessor.java | 23 +++- .../main/deployment/CamelMainRoutesFilterTest.java | 15 ++- .../org/apache/camel/quarkus/main/CamelMain.java | 128 ++++++++++++--------- .../camel/quarkus/main/CamelMainApplication.java} | 21 ++-- .../quarkus/main/CamelMainEventDispatcher.java | 7 +- .../camel/quarkus/main/CamelMainRecorder.java | 3 +- .../camel/quarkus/main/CamelMainRuntime.java | 28 ++++- .../component/log/deployment/LogProcessor.java | 9 ++ .../test/devmode/timer/TimerDevModeTest.java | 2 + .../camel/quarkus/main/CamelDevModeTest.java | 2 + 22 files changed, 446 insertions(+), 110 deletions(-) diff --git a/examples/pom.xml b/examples/pom.xml index 1ed0651..95a9a93 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -39,6 +39,7 @@ <module>timer-log</module> <module>timer-log-cdi</module> <module>timer-log-kotlin</module> + <module>timer-log-main</module> <module>timer-log-xml</module> <module>timer-log-spring</module> <module>file-split-log-xml</module> diff --git a/examples/timer-log-main/README.adoc b/examples/timer-log-main/README.adoc new file mode 100644 index 0000000..1a25c86 --- /dev/null +++ b/examples/timer-log-main/README.adoc @@ -0,0 +1,56 @@ += timer-log + +This is a basic hello world example that uses a cusomt main to set-up +a Camel timer that triggers every second and prints to the log. + +TIP: Check the https://camel.apache.org/camel-quarkus/latest/first-steps.html[Camel Quarkus User guide] for prerequisites +and other general information. + +== Start in the Development mode + +[source,shell] +---- +$ mvn clean compile quarkus:dev +---- + +The above command compiles the project, starts the application and lets the Quarkus tooling watch for changes in your +workspace. Any modifications in your project will automatically take effect in the running application. + +TIP: Please refer to the Development mode section of +https://camel.apache.org/camel-quarkus/latest/first-steps.html#_development_mode[Camel Quarkus User guide] for more details. + +Then look at the log output in the console. As we run the example in Quarkus Dev Mode, you can edit the source code and have live updates. +For example try to change `Incremented the counter` to `Counter state` in the message body or change the property `timer.period` in application.properties + +=== Package and run the application + +Once you are done with developing you may want to package and run the application. + +TIP: Find more details about the JVM mode and Native mode in the Package and run section of +https://camel.apache.org/camel-quarkus/latest/first-steps.html#_package_and_run_the_application[Camel Quarkus User guide] + +==== JVM mode + +[source,shell] +---- +$ mvn clean package +$ java -jar target/*-runner.jar +... +[io.quarkus] (main) Quarkus 1.3.2 started in 1.163s. Listening on: http://[::]:8080 +---- + +==== Native mode + +IMPORTANT: Native mode requires having GraalVM and other tools installed. Please check the Prerequisites section +of https://camel.apache.org/camel-quarkus/latest/first-steps.html#_prerequisites[Camel Quarkus User guide]. + +To prepare a native executable using GraalVM, run the following command: + +[source,shell] +---- +$ mvn clean package -Pnative +$ ./target/*-runner +... +[io.quarkus] (main) Quarkus 1.3.2 started in 0.013s. Listening on: http://[::]:8080 +... +---- diff --git a/examples/timer-log-main/pom.xml b/examples/timer-log-main/pom.xml new file mode 100644 index 0000000..c674791 --- /dev/null +++ b/examples/timer-log-main/pom.xml @@ -0,0 +1,90 @@ +<?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/xsd/maven-4.0.0.xsd"> + <parent> + <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-build-parent</artifactId> + <version>1.1.0-SNAPSHOT</version> + <relativePath>../../poms/build-parent/pom.xml</relativePath> + </parent> + + <modelVersion>4.0.0</modelVersion> + + <artifactId>camel-quarkus-examples-timer-log-main</artifactId> + <name>Camel Quarkus :: Examples :: Timer Log Main</name> + <description>Camel Quarkus Example :: Timer to Log Main</description> + + <properties> + <!-- mvnd, a.k.a. Maven Daemon: https://github.com/gnodet/mvnd --> + <!-- The following rule tells mvnd to build the listed deployment modules before this module. --> + <!-- This is important because mvnd builds modules in parallel by default. The deployment modules are not --> + <!-- explicit dependencies of this module in the Maven sense, although they are required by the Quarkus Maven plugin. --> + <!-- Please update rule whenever you change the dependencies of this module by running --> + <!-- mvn process-resources -Pformat from the root directory --> + <mvnd.builder.rule>camel-quarkus-log-deployment,camel-quarkus-support-policy-deployment,camel-quarkus-timer-deployment</mvnd.builder.rule> + </properties> + + <dependencies> + <dependency> + <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-main</artifactId> + </dependency> + <dependency> + <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-timer</artifactId> + </dependency> + <dependency> + <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-log</artifactId> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-maven-plugin</artifactId> + <executions> + <execution> + <id>build</id> + <goals> + <goal>build</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> + + <profiles> + <profile> + <id>native</id> + <activation> + <property> + <name>native</name> + </property> + </activation> + <properties> + <quarkus.package.type>native</quarkus.package.type> + </properties> + </profile> + </profiles> + +</project> diff --git a/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/CamelRuntime.java b/examples/timer-log-main/src/main/java/org/acme/timer/Main.java similarity index 72% copy from extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/CamelRuntime.java copy to examples/timer-log-main/src/main/java/org/acme/timer/Main.java index 75c8ca7..9299d43 100644 --- a/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/CamelRuntime.java +++ b/examples/timer-log-main/src/main/java/org/acme/timer/Main.java @@ -14,15 +14,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.camel.quarkus.core; +package org.acme.timer; -import org.apache.camel.spi.HasCamelContext; +import io.quarkus.runtime.Quarkus; +import io.quarkus.runtime.annotations.QuarkusMain; +import org.apache.camel.quarkus.main.CamelMainApplication; -/** - * Represent a runnable Camel instance. - */ -public interface CamelRuntime extends HasCamelContext { - void start(); - - void stop(); +@QuarkusMain +public class Main { + public static void main(String... args) { + Quarkus.run(CamelMainApplication.class, args); + } } diff --git a/extensions/log/deployment/src/main/java/org/apache/camel/quarkus/component/log/deployment/LogProcessor.java b/examples/timer-log-main/src/main/java/org/acme/timer/TimerRoute.java similarity index 69% copy from extensions/log/deployment/src/main/java/org/apache/camel/quarkus/component/log/deployment/LogProcessor.java copy to examples/timer-log-main/src/main/java/org/acme/timer/TimerRoute.java index 7f35e8f..c33c153 100644 --- a/extensions/log/deployment/src/main/java/org/apache/camel/quarkus/component/log/deployment/LogProcessor.java +++ b/examples/timer-log-main/src/main/java/org/acme/timer/TimerRoute.java @@ -14,18 +14,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.camel.quarkus.component.log.deployment; +package org.acme.timer; -import io.quarkus.deployment.annotations.BuildStep; -import io.quarkus.deployment.builditem.FeatureBuildItem; +import javax.enterprise.context.ApplicationScoped; -class LogProcessor { +import org.apache.camel.builder.RouteBuilder; - private static final String FEATURE = "camel-log"; - - @BuildStep - FeatureBuildItem feature() { - return new FeatureBuildItem(FEATURE); +/** + * A simple {@link RouteBuilder}. + */ +@ApplicationScoped +public class TimerRoute extends RouteBuilder { + @Override + public void configure() throws Exception { + from("timer:foo?period={{timer.period}}") + .setBody().constant("Hello from Main!") + .to("log:example"); } - } diff --git a/examples/timer-log-main/src/main/resources/application.properties b/examples/timer-log-main/src/main/resources/application.properties new file mode 100644 index 0000000..dd370b0 --- /dev/null +++ b/examples/timer-log-main/src/main/resources/application.properties @@ -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. +## --------------------------------------------------------------------------- + +# +# Quarkus +# +quarkus.banner.enabled = false + +# camel look-up beans using BeanManager so we don't want +# ArC to remove beans without injection points. +quarkus.arc.remove-unused-beans = false + +# +# Camel - Main +# +camel.main.duration-hit-exit-code = 15 + +# +# Camel - Components +# +camel.component.log.exchange-formatter = #class:org.apache.camel.support.processor.DefaultExchangeFormatter +camel.component.log.exchange-formatter.show-exchange-pattern = false +camel.component.log.exchange-formatter.show-body-type = false + +# +# Integration +# +timer.period = 5000 diff --git a/extensions-core/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/CamelBootstrapProcessor.java b/extensions-core/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/CamelBootstrapProcessor.java index 46bdf28..ae03d6c 100644 --- a/extensions-core/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/CamelBootstrapProcessor.java +++ b/extensions-core/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/CamelBootstrapProcessor.java @@ -20,6 +20,7 @@ import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.annotations.ExecutionTime; import io.quarkus.deployment.annotations.Produce; import io.quarkus.deployment.annotations.Record; +import io.quarkus.deployment.builditem.RawCommandLineArgumentsBuildItem; import io.quarkus.deployment.builditem.ShutdownContextBuildItem; import io.quarkus.runtime.ShutdownContext; import org.apache.camel.quarkus.core.CamelBootstrapRecorder; @@ -31,14 +32,24 @@ class CamelBootstrapProcessor { /** * Starts the given {@link CamelRuntimeBuildItem}. * - * @param recorder the recorder. - * @param runtime a reference to the {@link CamelRuntimeBuildItem}. - * @param shutdown a reference to a {@link ShutdownContext} used tor register the Camel's related shutdown tasks. + * @param recorder the recorder. + * @param runtime a reference to the {@link CamelRuntimeBuildItem}. + * @param commandLineArguments a reference to the raw command line arguments as they were passed to the application. + * @param shutdown a reference to a {@link ShutdownContext} used tor register the Camel's related shutdown + * tasks. */ @BuildStep(onlyIf = { CamelConfigFlags.BootstrapEnabled.class }) @Record(value = ExecutionTime.RUNTIME_INIT) @Produce(CamelBootstrapCompletedBuildItem.class) - void boot(CamelBootstrapRecorder recorder, CamelRuntimeBuildItem runtime, ShutdownContextBuildItem shutdown) { - recorder.start(shutdown, runtime.get()); + void boot( + CamelBootstrapRecorder recorder, + CamelRuntimeBuildItem runtime, + RawCommandLineArgumentsBuildItem commandLineArguments, + ShutdownContextBuildItem shutdown) { + + recorder.addShutdownTask(shutdown, runtime.runtime()); + if (runtime.isAutoStartup()) { + recorder.start(runtime.runtime(), commandLineArguments); + } } } diff --git a/extensions-core/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/spi/CamelRuntimeBuildItem.java b/extensions-core/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/spi/CamelRuntimeBuildItem.java index 7a4b212..90e2065 100644 --- a/extensions-core/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/spi/CamelRuntimeBuildItem.java +++ b/extensions-core/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/spi/CamelRuntimeBuildItem.java @@ -22,12 +22,22 @@ import org.apache.camel.quarkus.core.CamelRuntime; public final class CamelRuntimeBuildItem extends SimpleBuildItem { private final RuntimeValue<CamelRuntime> runtime; + private final boolean autoStartup; public CamelRuntimeBuildItem(RuntimeValue<CamelRuntime> runtime) { + this(runtime, true); + } + + public CamelRuntimeBuildItem(RuntimeValue<CamelRuntime> runtime, boolean autoStartup) { this.runtime = runtime; + this.autoStartup = autoStartup; } - public RuntimeValue<CamelRuntime> get() { + public RuntimeValue<CamelRuntime> runtime() { return runtime; } + + public boolean isAutoStartup() { + return autoStartup; + } } diff --git a/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/CamelBootstrapRecorder.java b/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/CamelBootstrapRecorder.java index d88c962..f04799f 100644 --- a/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/CamelBootstrapRecorder.java +++ b/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/CamelBootstrapRecorder.java @@ -16,6 +16,8 @@ */ package org.apache.camel.quarkus.core; +import java.util.function.Supplier; + import io.quarkus.runtime.RuntimeValue; import io.quarkus.runtime.ShutdownContext; import io.quarkus.runtime.annotations.Recorder; @@ -23,7 +25,7 @@ import org.jboss.logging.Logger; @Recorder public class CamelBootstrapRecorder { - public void start(ShutdownContext shutdown, RuntimeValue<CamelRuntime> runtime) { + public void addShutdownTask(ShutdownContext shutdown, RuntimeValue<CamelRuntime> runtime) { shutdown.addShutdownTask(new Runnable() { @Override public void run() { @@ -34,12 +36,13 @@ public class CamelBootstrapRecorder { } } }); + } + public void start(RuntimeValue<CamelRuntime> runtime, Supplier<String[]> arguments) { try { Logger logger = Logger.getLogger(CamelBootstrapRecorder.class); logger.infof("bootstrap runtime: %s", runtime.getValue().getClass().getName()); - - runtime.getValue().start(); + runtime.getValue().start(arguments.get()); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/CamelContextRuntime.java b/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/CamelContextRuntime.java index 0d6c13e..4bcbc59 100644 --- a/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/CamelContextRuntime.java +++ b/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/CamelContextRuntime.java @@ -16,20 +16,44 @@ */ package org.apache.camel.quarkus.core; +import java.util.Arrays; +import java.util.concurrent.CountDownLatch; + import org.apache.camel.CamelContext; +import org.apache.camel.spi.CamelEvent; +import org.apache.camel.support.EventNotifierSupport; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * A simple implementation of the {@link CamelRuntime} that directly starts/stop the {@link CamelContext}. */ public class CamelContextRuntime implements CamelRuntime { + private static final Logger LOGGER = LoggerFactory.getLogger(CamelContextRuntime.class); private final CamelContext camelContext; + private final CountDownLatch latch; public CamelContextRuntime(CamelContext camelContext) { this.camelContext = camelContext; + this.latch = new CountDownLatch(1); } @Override - public void start() { + public void start(String[] args) { + if (args.length > 0) { + LOGGER.info("Ignoring args: {}", Arrays.toString(args)); + } + camelContext.getManagementStrategy().addEventNotifier(new EventNotifierSupport() { + @Override + public void notify(CamelEvent event) throws Exception { + latch.countDown(); + } + + @Override + public boolean isEnabled(CamelEvent event) { + return event instanceof CamelEvent.CamelContextStoppedEvent; + } + }); camelContext.start(); } @@ -39,6 +63,17 @@ public class CamelContextRuntime implements CamelRuntime { } @Override + public int waitForExit() { + try { + this.latch.await(); + } catch (InterruptedException e) { + LOGGER.warn("", e); + } + + return 0; + } + + @Override public CamelContext getCamelContext() { return camelContext; } diff --git a/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/CamelRuntime.java b/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/CamelRuntime.java index 75c8ca7..933ec14 100644 --- a/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/CamelRuntime.java +++ b/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/CamelRuntime.java @@ -22,7 +22,9 @@ import org.apache.camel.spi.HasCamelContext; * Represent a runnable Camel instance. */ public interface CamelRuntime extends HasCamelContext { - void start(); + void start(String[] args); void stop(); + + int waitForExit(); } diff --git a/extensions-core/main/deployment/pom.xml b/extensions-core/main/deployment/pom.xml index b88052d..5feacbb 100644 --- a/extensions-core/main/deployment/pom.xml +++ b/extensions-core/main/deployment/pom.xml @@ -81,6 +81,11 @@ <artifactId>assertj-core</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>org.awaitility</groupId> + <artifactId>awaitility</artifactId> + <scope>test</scope> + </dependency> </dependencies> <build> diff --git a/extensions-core/main/deployment/src/main/java/org/apache/camel/quarkus/main/deployment/CamelMainProcessor.java b/extensions-core/main/deployment/src/main/java/org/apache/camel/quarkus/main/deployment/CamelMainProcessor.java index 482458f..d98124b 100644 --- a/extensions-core/main/deployment/src/main/java/org/apache/camel/quarkus/main/deployment/CamelMainProcessor.java +++ b/extensions-core/main/deployment/src/main/java/org/apache/camel/quarkus/main/deployment/CamelMainProcessor.java @@ -26,8 +26,10 @@ import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.annotations.ExecutionTime; import io.quarkus.deployment.annotations.Overridable; import io.quarkus.deployment.annotations.Record; +import io.quarkus.deployment.builditem.CombinedIndexBuildItem; import io.quarkus.deployment.builditem.ServiceStartBuildItem; import io.quarkus.runtime.RuntimeValue; +import io.quarkus.runtime.annotations.QuarkusMain; import org.apache.camel.CamelContext; import org.apache.camel.quarkus.core.CamelRecorder; import org.apache.camel.quarkus.core.CamelRuntime; @@ -44,8 +46,14 @@ import org.apache.camel.quarkus.main.CamelMainRecorder; import org.apache.camel.quarkus.main.deployment.spi.CamelMainBuildItem; import org.apache.camel.quarkus.main.deployment.spi.CamelMainListenerBuildItem; import org.apache.camel.quarkus.main.deployment.spi.CamelRoutesCollectorBuildItem; +import org.jboss.jandex.DotName; +import org.jboss.jandex.IndexView; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class CamelMainProcessor { + private static Logger LOGGER = LoggerFactory.getLogger(CamelMainProcessor.class); + @BuildStep void unremovableBeans(BuildProducer<AdditionalBeanBuildItem> beanProducer) { beanProducer.produce(AdditionalBeanBuildItem.unremovableOf(CamelMainProducers.class)); @@ -128,16 +136,17 @@ public class CamelMainProcessor { * <li>Health * </ul> * <li>take control of the application life-cycle and initiates Quarkus shutdown according to some conditions as example - * after having processed a certain number of messagees. + * after having processed a certain number of messages.. * </ul> * + * @param index a reference to a {@link IndexView} * @param beanContainer a reference to a fully initialized CDI bean container * @param recorder the recorder. * @param main a reference to a {@link CamelMain}. - * @param customizers a list of {@link org.apache.camel.quarkus.core.CamelContextCustomizer} that will be executed - * before starting the {@link CamelContext} at {@link ExecutionTime#RUNTIME_INIT}. - * @param startList a placeholder to ensure camel-main start after the ArC container is fully initialized. This - * is required as under the hoods the camel registry may look-up beans form the + * @param customizers a list of {@link org.apache.camel.quarkus.core.CamelContextCustomizer} that will be + * executed before starting the {@link CamelContext} at {@link ExecutionTime#RUNTIME_INIT}. + * @param startList a placeholder to ensure camel-main start after the ArC container is fully initialized. + * This is required as under the hoods the camel registry may look-up beans form the * container thus we need it to be fully initialized to avoid unexpected behaviors. * @param runtimeTasks a placeholder to ensure all the runtime task are properly are done. * @return a build item holding a {@link CamelRuntime} instance. @@ -145,6 +154,7 @@ public class CamelMainProcessor { @BuildStep @Record(value = ExecutionTime.RUNTIME_INIT, optional = true) CamelRuntimeBuildItem runtime( + CombinedIndexBuildItem index, BeanContainerBuildItem beanContainer, CamelMainRecorder recorder, CamelMainBuildItem main, @@ -163,6 +173,7 @@ public class CamelMainProcessor { customizers.stream().map(RuntimeCamelContextCustomizerBuildItem::get).collect(Collectors.toList())); return new CamelRuntimeBuildItem( - recorder.createRuntime(beanContainer.getValue(), main.getInstance())); + recorder.createRuntime(beanContainer.getValue(), main.getInstance()), + index.getIndex().getAnnotations(DotName.createSimple(QuarkusMain.class.getName())).isEmpty()); } } diff --git a/extensions-core/main/deployment/src/test/java/org/apache/camel/quarkus/main/deployment/CamelMainRoutesFilterTest.java b/extensions-core/main/deployment/src/test/java/org/apache/camel/quarkus/main/deployment/CamelMainRoutesFilterTest.java index 841e968..d0d00fc 100644 --- a/extensions-core/main/deployment/src/test/java/org/apache/camel/quarkus/main/deployment/CamelMainRoutesFilterTest.java +++ b/extensions-core/main/deployment/src/test/java/org/apache/camel/quarkus/main/deployment/CamelMainRoutesFilterTest.java @@ -20,6 +20,7 @@ import java.io.IOException; import java.io.StringWriter; import java.io.Writer; import java.util.Properties; +import java.util.concurrent.TimeUnit; import javax.inject.Inject; @@ -34,11 +35,13 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; public class CamelMainRoutesFilterTest { @RegisterExtension static final QuarkusUnitTest CONFIG = new QuarkusUnitTest() .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) + .addClasses(MyRoute.class, MyRouteFiltered.class) .addAsResource(applicationProperties(), "application.properties")); @Inject @@ -63,13 +66,17 @@ public class CamelMainRoutesFilterTest { @Test public void testRoutesFilter() { - assertThat(main.getCamelContext().getRoutes()) - .hasSize(1) - .first().hasFieldOrPropertyWithValue("id", "my-route"); - assertThat(main.configure().getRoutesBuilders()) .hasSize(1) .first().isInstanceOf(MyRoute.class); + + // since Camel is started in a dedicated thread, then routes + // may not be available until some time + await().atMost(10, TimeUnit.SECONDS).untilAsserted(() -> { + assertThat(main.getCamelContext().getRoutes()) + .hasSize(1) + .first().hasFieldOrPropertyWithValue("id", "my-route"); + }); } public static class MyRoute extends RouteBuilder { diff --git a/extensions-core/main/runtime/src/main/java/org/apache/camel/quarkus/main/CamelMain.java b/extensions-core/main/runtime/src/main/java/org/apache/camel/quarkus/main/CamelMain.java index f04d87a..5d2b383 100644 --- a/extensions-core/main/runtime/src/main/java/org/apache/camel/quarkus/main/CamelMain.java +++ b/extensions-core/main/runtime/src/main/java/org/apache/camel/quarkus/main/CamelMain.java @@ -18,54 +18,30 @@ package org.apache.camel.quarkus.main; import java.util.Collection; import java.util.Collections; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import io.quarkus.runtime.Quarkus; import org.apache.camel.CamelContext; -import org.apache.camel.CamelContextAware; import org.apache.camel.ExtendedCamelContext; import org.apache.camel.ProducerTemplate; import org.apache.camel.RoutesBuilder; -import org.apache.camel.main.BaseMainSupport; +import org.apache.camel.main.MainCommandLineSupport; import org.apache.camel.main.MainConfigurationProperties; import org.apache.camel.main.MainListener; +import org.apache.camel.main.MainShutdownStrategy; import org.apache.camel.spi.CamelBeanPostProcessor; -import org.apache.camel.support.service.ServiceHelper; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.apache.camel.spi.HasCamelContext; -public class CamelMain extends BaseMainSupport implements CamelContextAware { - private static final Logger LOGGER = LoggerFactory.getLogger(CamelMain.class); - - @Override - public void setCamelContext(CamelContext camelContext) { +public final class CamelMain extends MainCommandLineSupport implements HasCamelContext { + public CamelMain(CamelContext camelContext) { this.camelContext = camelContext; } @Override - protected void doInit() throws Exception { - super.doInit(); - postProcessCamelContext(getCamelContext()); - getCamelContext().init(); - } - - @Override - protected void doStart() throws Exception { - for (MainListener listener : listeners) { - listener.beforeStart(this); - } - - getCamelContext().start(); - - for (MainListener listener : listeners) { - listener.afterStart(this); - } - } - - @Override - protected void postProcessCamelContext(CamelContext camelContext) throws Exception { - super.postProcessCamelContext(camelContext); - - // post process classes with camel's post processor so classes have support - // for camel's simple di + protected void loadRouteBuilders(CamelContext camelContext) throws Exception { + // routes are discovered and pre-instantiated which allow to post process them to support Camel's DI CamelBeanPostProcessor postProcessor = camelContext.adapt(ExtendedCamelContext.class).getBeanPostProcessor(); for (RoutesBuilder builder : mainConfigurationProperties.getRoutesBuilders()) { postProcessor.postProcessBeforeInitialization(builder, builder.getClass().getName()); @@ -74,35 +50,40 @@ public class CamelMain extends BaseMainSupport implements CamelContextAware { } @Override - protected void loadRouteBuilders(CamelContext camelContext) throws Exception { - // classes are automatically discovered by build processors + protected void doInit() throws Exception { + setShutdownStrategy(new ShutdownStrategy()); + + super.doInit(); + initCamelContext(); } @Override - protected void doStop() throws Exception { + protected void doStart() throws Exception { + super.doStart(); try { - if (camelTemplate != null) { - ServiceHelper.stopService(camelTemplate); - camelTemplate = null; + // if we were veto started then mark as completed + this.camelContext.start(); + } finally { + if (getCamelContext().isVetoStarted()) { + completed(); } - } catch (Exception e) { - LOGGER.debug("Error stopping camelTemplate due " + e.getMessage() + ". This exception is ignored.", e); } + } - for (MainListener listener : listeners) { - listener.beforeStop(this); - } - - getCamelContext().stop(); - - for (MainListener listener : listeners) { - listener.afterStop(this); - } + @Override + protected void doStop() throws Exception { + super.doStop(); + this.camelContext.stop(); } @Override protected ProducerTemplate findOrCreateCamelTemplate() { - return getCamelContext().createProducerTemplate(); + return this.camelContext.createProducerTemplate(); + } + + @Override + protected void initCamelContext() throws Exception { + postProcessCamelContext(camelContext); } @Override @@ -117,4 +98,45 @@ public class CamelMain extends BaseMainSupport implements CamelContextAware { MainConfigurationProperties getMainConfigurationProperties() { return mainConfigurationProperties; } + + /** + * Implementation of a {@link MainShutdownStrategy} based on Quarkus Command Mode. + * + * @see <a href="https://quarkus.io/guides/command-mode-reference">Quarkus Command Mode Applications</a> + */ + private class ShutdownStrategy implements MainShutdownStrategy { + private final AtomicBoolean completed; + private final CountDownLatch latch; + + public ShutdownStrategy() { + this.completed = new AtomicBoolean(false); + this.latch = new CountDownLatch(1); + } + + @Override + public boolean isRunAllowed() { + return !completed.get(); + } + + @Override + public boolean shutdown() { + if (completed.compareAndSet(false, true)) { + latch.countDown(); + Quarkus.asyncExit(getExitCode()); + return true; + } + + return false; + } + + @Override + public void await() throws InterruptedException { + latch.await(); + } + + @Override + public void await(long timeout, TimeUnit unit) throws InterruptedException { + latch.await(timeout, unit); + } + } } diff --git a/extensions-core/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/spi/CamelRuntimeBuildItem.java b/extensions-core/main/runtime/src/main/java/org/apache/camel/quarkus/main/CamelMainApplication.java similarity index 66% copy from extensions-core/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/spi/CamelRuntimeBuildItem.java copy to extensions-core/main/runtime/src/main/java/org/apache/camel/quarkus/main/CamelMainApplication.java index 7a4b212..bb08522 100644 --- a/extensions-core/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/spi/CamelRuntimeBuildItem.java +++ b/extensions-core/main/runtime/src/main/java/org/apache/camel/quarkus/main/CamelMainApplication.java @@ -14,20 +14,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.camel.quarkus.core.deployment.spi; +package org.apache.camel.quarkus.main; -import io.quarkus.builder.item.SimpleBuildItem; -import io.quarkus.runtime.RuntimeValue; +import io.quarkus.arc.Arc; +import io.quarkus.runtime.QuarkusApplication; import org.apache.camel.quarkus.core.CamelRuntime; -public final class CamelRuntimeBuildItem extends SimpleBuildItem { - private final RuntimeValue<CamelRuntime> runtime; - - public CamelRuntimeBuildItem(RuntimeValue<CamelRuntime> runtime) { - this.runtime = runtime; - } - - public RuntimeValue<CamelRuntime> get() { - return runtime; +public class CamelMainApplication implements QuarkusApplication { + @Override + public int run(String... args) throws Exception { + CamelRuntime runtime = Arc.container().instance(CamelRuntime.class).get(); + runtime.start(args); + return runtime.waitForExit(); } } diff --git a/extensions-core/main/runtime/src/main/java/org/apache/camel/quarkus/main/CamelMainEventDispatcher.java b/extensions-core/main/runtime/src/main/java/org/apache/camel/quarkus/main/CamelMainEventDispatcher.java index 841e6e5..7ee3a46 100644 --- a/extensions-core/main/runtime/src/main/java/org/apache/camel/quarkus/main/CamelMainEventDispatcher.java +++ b/extensions-core/main/runtime/src/main/java/org/apache/camel/quarkus/main/CamelMainEventDispatcher.java @@ -16,6 +16,8 @@ */ package org.apache.camel.quarkus.main; +import javax.enterprise.inject.spi.BeanManager; + import io.quarkus.arc.Arc; import org.apache.camel.CamelContext; import org.apache.camel.main.BaseMainSupport; @@ -66,6 +68,9 @@ public class CamelMainEventDispatcher implements org.apache.camel.main.MainListe } private static <T> void fireEvent(Class<T> clazz, T event) { - Arc.container().beanManager().getEvent().select(clazz).fire(event); + BeanManager beanManager = Arc.container().beanManager(); + if (beanManager != null) { + beanManager.getEvent().select(clazz).fire(event); + } } } diff --git a/extensions-core/main/runtime/src/main/java/org/apache/camel/quarkus/main/CamelMainRecorder.java b/extensions-core/main/runtime/src/main/java/org/apache/camel/quarkus/main/CamelMainRecorder.java index b21ca91..4f95759 100644 --- a/extensions-core/main/runtime/src/main/java/org/apache/camel/quarkus/main/CamelMainRecorder.java +++ b/extensions-core/main/runtime/src/main/java/org/apache/camel/quarkus/main/CamelMainRecorder.java @@ -39,9 +39,8 @@ public class CamelMainRecorder { RuntimeValue<CamelContext> runtime, RuntimeValue<RoutesCollector> routesCollector, BeanContainer container) { - CamelMain main = new CamelMain(); + CamelMain main = new CamelMain(runtime.getValue()); main.setRoutesCollector(routesCollector.getValue()); - main.setCamelContext(runtime.getValue()); main.addMainListener(new CamelMainEventDispatcher()); // autowire only non null values as components may have configured diff --git a/extensions-core/main/runtime/src/main/java/org/apache/camel/quarkus/main/CamelMainRuntime.java b/extensions-core/main/runtime/src/main/java/org/apache/camel/quarkus/main/CamelMainRuntime.java index b5e314b..7b1a964 100644 --- a/extensions-core/main/runtime/src/main/java/org/apache/camel/quarkus/main/CamelMainRuntime.java +++ b/extensions-core/main/runtime/src/main/java/org/apache/camel/quarkus/main/CamelMainRuntime.java @@ -16,13 +16,19 @@ */ package org.apache.camel.quarkus.main; +import java.util.Arrays; + +import io.quarkus.runtime.Quarkus; import org.apache.camel.CamelContext; import org.apache.camel.quarkus.core.CamelRuntime; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * An implementation of the {@link CamelRuntime} based on camel-main. */ public class CamelMainRuntime implements CamelRuntime { + private static final Logger LOGGER = LoggerFactory.getLogger(CamelMainRuntime.class); private final CamelMain main; public CamelMainRuntime(CamelMain main) { @@ -30,8 +36,20 @@ public class CamelMainRuntime implements CamelRuntime { } @Override - public void start() { - main.start(); + public void start(String[] args) { + if (args.length > 0) { + LOGGER.info("Starting camel-quarkus with args: {}", Arrays.toString(args)); + } + + new Thread(() -> { + try { + main.run(args); + } catch (Exception e) { + LOGGER.error("Failed to start application", e); + stop(); + throw new RuntimeException(e); + } + }).start(); } @Override @@ -40,6 +58,12 @@ public class CamelMainRuntime implements CamelRuntime { } @Override + public int waitForExit() { + Quarkus.waitForExit(); + return this.main.getExitCode(); + } + + @Override public CamelContext getCamelContext() { return main.getCamelContext(); } diff --git a/extensions/log/deployment/src/main/java/org/apache/camel/quarkus/component/log/deployment/LogProcessor.java b/extensions/log/deployment/src/main/java/org/apache/camel/quarkus/component/log/deployment/LogProcessor.java index 7f35e8f..1e8da12 100644 --- a/extensions/log/deployment/src/main/java/org/apache/camel/quarkus/component/log/deployment/LogProcessor.java +++ b/extensions/log/deployment/src/main/java/org/apache/camel/quarkus/component/log/deployment/LogProcessor.java @@ -18,6 +18,7 @@ package org.apache.camel.quarkus.component.log.deployment; import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.builditem.FeatureBuildItem; +import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; class LogProcessor { @@ -28,4 +29,12 @@ class LogProcessor { return new FeatureBuildItem(FEATURE); } + @BuildStep + ReflectiveClassBuildItem reflectiveClasses() { + return new ReflectiveClassBuildItem( + true, + false, + org.apache.camel.support.processor.DefaultExchangeFormatter.class); + } + } diff --git a/extensions/timer/deployment/src/test/java/org/apache/camel/quarkus/component/test/devmode/timer/TimerDevModeTest.java b/extensions/timer/deployment/src/test/java/org/apache/camel/quarkus/component/test/devmode/timer/TimerDevModeTest.java index f83c82e..f9db780 100644 --- a/extensions/timer/deployment/src/test/java/org/apache/camel/quarkus/component/test/devmode/timer/TimerDevModeTest.java +++ b/extensions/timer/deployment/src/test/java/org/apache/camel/quarkus/component/test/devmode/timer/TimerDevModeTest.java @@ -34,6 +34,7 @@ import org.jboss.shrinkwrap.api.asset.Asset; import org.jboss.shrinkwrap.api.asset.StringAsset; import org.jboss.shrinkwrap.api.spec.JavaArchive; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -48,6 +49,7 @@ public class TimerDevModeTest { .addAsResource(applicationProperties(), "application.properties")) .setLogFileName(LOG_FILE.getFileName().toString()); + @Disabled("Requires https://github.com/quarkusio/quarkus/pull/10109") @Test void logMessageEdit() throws IOException { Awaitility.await() diff --git a/integration-tests/main-devmode/src/test/java/org/apache/camel/quarkus/main/CamelDevModeTest.java b/integration-tests/main-devmode/src/test/java/org/apache/camel/quarkus/main/CamelDevModeTest.java index 5257d4c..d51308d 100644 --- a/integration-tests/main-devmode/src/test/java/org/apache/camel/quarkus/main/CamelDevModeTest.java +++ b/integration-tests/main-devmode/src/test/java/org/apache/camel/quarkus/main/CamelDevModeTest.java @@ -36,6 +36,7 @@ import org.jboss.shrinkwrap.api.asset.StringAsset; import org.jboss.shrinkwrap.api.spec.JavaArchive; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -92,6 +93,7 @@ public class CamelDevModeTest { StandardCopyOption.REPLACE_EXISTING); } + @Disabled("Requires https://github.com/quarkusio/quarkus/pull/10109") @Test public void testRoutesDiscovery() throws IOException { await().atMost(10, TimeUnit.SECONDS).untilAsserted(() -> {