diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index b3126be08..f7e591d81 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -110,6 +110,7 @@ jobs: matrix: native-image-project: - :camel-k-quarkus-itests-core + - :camel-k-quarkus-itests-runtime - :camel-k-quarkus-itests-cron - :camel-k-quarkus-itests-master - :camel-k-quarkus-itests-kamelet diff --git a/camel-k-quarkus/camel-k-quarkus-itests/camel-k-quarkus-itests-cron/pom.xml b/camel-k-quarkus/camel-k-quarkus-itests/camel-k-quarkus-itests-cron/pom.xml index 291f5a37a..032eb98c7 100644 --- a/camel-k-quarkus/camel-k-quarkus-itests/camel-k-quarkus-itests-cron/pom.xml +++ b/camel-k-quarkus/camel-k-quarkus-itests/camel-k-quarkus-itests-cron/pom.xml @@ -28,10 +28,22 @@ camel-k-quarkus-itests-cron + + org.apache.camel.k + camel-k-runtime-quarkus + org.apache.camel.k camel-k-quarkus-cron + + org.apache.camel.k + camel-k-quarkus-loader-yaml + + + org.apache.camel.quarkus + camel-quarkus-timer + @@ -68,6 +80,13 @@ hamcrest-core test + + + org.awaitility + awaitility + ${awaitility-version} + + diff --git a/camel-k-quarkus/camel-k-quarkus-itests/camel-k-quarkus-itests-cron/src/main/java/org/apache/camel/k/quarkus/cron/deployment/Application.java b/camel-k-quarkus/camel-k-quarkus-itests/camel-k-quarkus-itests-cron/src/main/java/org/apache/camel/k/quarkus/cron/deployment/Application.java index 3d8cb21ef..a70fd026f 100644 --- a/camel-k-quarkus/camel-k-quarkus-itests/camel-k-quarkus-itests-cron/src/main/java/org/apache/camel/k/quarkus/cron/deployment/Application.java +++ b/camel-k-quarkus/camel-k-quarkus-itests/camel-k-quarkus-itests-cron/src/main/java/org/apache/camel/k/quarkus/cron/deployment/Application.java @@ -16,22 +16,38 @@ */ package org.apache.camel.k.quarkus.cron.deployment; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + import javax.enterprise.context.ApplicationScoped; import javax.inject.Inject; +import javax.inject.Singleton; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; +import io.quarkus.arc.Unremovable; import org.apache.camel.CamelContext; import org.apache.camel.ExtendedCamelContext; import org.apache.camel.k.Constants; +import org.apache.camel.k.Runtime; +import org.apache.camel.k.Source; +import org.apache.camel.k.SourceLoader; +import org.apache.camel.k.Sources; +import org.apache.camel.k.cron.CronSourceLoaderInterceptor; +import org.apache.camel.k.loader.yaml.YamlSourceLoader; @Path("/test") @ApplicationScoped public class Application { @Inject CamelContext context; + @Inject + Runtime runtime; + + private final AtomicBoolean stopped = new AtomicBoolean(); @GET @Path("/find-cron-interceptor") @@ -43,4 +59,54 @@ public String findCronInterceptor() { .map(Class::getName) .orElse(""); } + + @GET + @Path("/load") + @Produces(MediaType.TEXT_PLAIN) + public String load() throws Exception { + final String code = "" + + "\n- from:" + + "\n uri: \"timer:tick?period=1&delay=60000\"" + + "\n steps:" + + "\n - log: \"${body}\""; + + final SourceLoader loader = new YamlSourceLoader(); + final Source source = Sources.fromBytes("my-cron", "yaml", null, List.of("cron"), code.getBytes(StandardCharsets.UTF_8)); + + final CronSourceLoaderInterceptor interceptor = new CronSourceLoaderInterceptor(); + interceptor.setRuntime(runtime); + interceptor.setOverridableComponents("timer"); + + SourceLoader.Result result = interceptor.afterLoad( + loader, + source, + loader.load(runtime, source)); + + result.builder().ifPresent(b -> { + try { + context.addRoutes(b); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + + return "" + context.getRoutesSize(); + } + + @GET + @Path("/stopped") + @Produces(MediaType.TEXT_PLAIN) + public String stopped() { + return "" + stopped.get(); + } + + /* + * Override the default ShutdownTask for testing purpose. + */ + @Unremovable + @Singleton + @javax.enterprise.inject.Produces + org.apache.camel.k.quarkus.Application.ShutdownTask shutdownTask() { + return () -> stopped.set(true); + } } diff --git a/camel-k-quarkus/camel-k-quarkus-itests/camel-k-quarkus-itests-cron/src/main/resources/application.properties b/camel-k-quarkus/camel-k-quarkus-itests/camel-k-quarkus-itests-cron/src/main/resources/application.properties index 3f6864acc..1882d46d2 100644 --- a/camel-k-quarkus/camel-k-quarkus-itests/camel-k-quarkus-itests-cron/src/main/resources/application.properties +++ b/camel-k-quarkus/camel-k-quarkus-itests/camel-k-quarkus-itests-cron/src/main/resources/application.properties @@ -26,3 +26,5 @@ quarkus.banner.enabled = false # quarkus.camel.routes-discovery.enabled = false + + diff --git a/camel-k-quarkus/camel-k-quarkus-itests/camel-k-quarkus-itests-cron/src/test/java/org/apache/camel/k/quarkus/cron/deployment/CronTest.java b/camel-k-quarkus/camel-k-quarkus-itests/camel-k-quarkus-itests-cron/src/test/java/org/apache/camel/k/quarkus/cron/deployment/CronTest.java index ff38d6161..e0f945639 100644 --- a/camel-k-quarkus/camel-k-quarkus-itests/camel-k-quarkus-itests-cron/src/test/java/org/apache/camel/k/quarkus/cron/deployment/CronTest.java +++ b/camel-k-quarkus/camel-k-quarkus-itests/camel-k-quarkus-itests-cron/src/test/java/org/apache/camel/k/quarkus/cron/deployment/CronTest.java @@ -16,23 +16,43 @@ */ package org.apache.camel.k.quarkus.cron.deployment; -import java.io.IOException; +import java.util.concurrent.TimeUnit; import io.quarkus.test.junit.QuarkusTest; import org.apache.camel.k.cron.CronSourceLoaderInterceptor; import org.junit.jupiter.api.Test; import static io.restassured.RestAssured.when; +import static org.awaitility.Awaitility.await; import static org.hamcrest.Matchers.is; @QuarkusTest public class CronTest { @Test - public void cronInterceptorIsRegistered() throws IOException { + public void cronInterceptorIsRegistered() { when() .get("/test/find-cron-interceptor") .then() .statusCode(200) .body(is(CronSourceLoaderInterceptor.class.getName())); } + + @Test + public void cronInvokesShutdown() { + when() + .get("/test/load") + .then() + .statusCode(200) + .body(is("1")); + + await().atMost(10, TimeUnit.SECONDS).until(() -> { + String result = when() + .get("/test/stopped") + .then() + .statusCode(200) + .extract().body().asString(); + + return "true".equals(result); + }); + } } diff --git a/camel-k-quarkus/camel-k-quarkus-itests/camel-k-quarkus-itests-master/src/main/java/org/apache/camel/k/quarkus/master/Application.java b/camel-k-quarkus/camel-k-quarkus-itests/camel-k-quarkus-itests-master/src/main/java/org/apache/camel/k/quarkus/master/Application.java index 3135739d6..d81ffd4eb 100644 --- a/camel-k-quarkus/camel-k-quarkus-itests/camel-k-quarkus-itests-master/src/main/java/org/apache/camel/k/quarkus/master/Application.java +++ b/camel-k-quarkus/camel-k-quarkus-itests/camel-k-quarkus-itests-master/src/main/java/org/apache/camel/k/quarkus/master/Application.java @@ -37,13 +37,12 @@ public class Application { @GET @Path("/inspect") @Produces(MediaType.APPLICATION_JSON) - public JsonObject findCronInterceptor() { - KubernetesClusterService service = context.hasService(KubernetesClusterService.class); + public JsonObject inspect() { + var service = context.hasService(KubernetesClusterService.class); return Json.createObjectBuilder() .add("cluster-service", service != null ? service.getClass().getName() : "") .add("cluster-service-cm", service != null ? service.getConfigMapName() : "") - .build(); } } diff --git a/camel-k-quarkus/camel-k-quarkus-itests/camel-k-quarkus-itests-master/src/main/resources/application.properties b/camel-k-quarkus/camel-k-quarkus-itests/camel-k-quarkus-itests-master/src/main/resources/application.properties index 95370157d..e84134f0a 100644 --- a/camel-k-quarkus/camel-k-quarkus-itests/camel-k-quarkus-itests-master/src/main/resources/application.properties +++ b/camel-k-quarkus/camel-k-quarkus-itests/camel-k-quarkus-itests-master/src/main/resources/application.properties @@ -18,7 +18,7 @@ # # Quarkus # -quarkus.log.console.enable = true +quarkus.log.console.enable = false quarkus.banner.enabled = false # diff --git a/camel-k-quarkus/camel-k-quarkus-itests/camel-k-quarkus-itests-runtime/pom.xml b/camel-k-quarkus/camel-k-quarkus-itests/camel-k-quarkus-itests-runtime/pom.xml new file mode 100644 index 000000000..90f33260f --- /dev/null +++ b/camel-k-quarkus/camel-k-quarkus-itests/camel-k-quarkus-itests-runtime/pom.xml @@ -0,0 +1,158 @@ + + + + + org.apache.camel.k + camel-k-quarkus-itests + 1.5.1-SNAPSHOT + + 4.0.0 + + camel-k-quarkus-itests-runtime + + + + org.apache.camel.k + camel-k-runtime-quarkus + + + + io.quarkus + quarkus-jsonb + + + io.quarkus + quarkus-resteasy + + + io.quarkus + quarkus-resteasy-jsonb + + + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + org.assertj + assertj-core + test + + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + ${build-helper-maven-plugin-version} + + + reserve-network-port + + reserve-network-port + + process-resources + + + test.http.port.jvm + test.http.port.native + + + + + + + io.quarkus + quarkus-maven-plugin + ${quarkus-version} + + + + build + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + ${project.basedir}/src/test/resources/test.properties + + + ${test.http.port.jvm} + org.jboss.logmanager.LogManager + ${project.basedir}/src/test/resources/test.properties + + + + + + + + + native + + + native + + + + native + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + + integration-test + verify + + + + ${project.basedir}/src/test/resources/test.properties + + + ${test.http.port.native} + ${project.build.directory}/${project.build.finalName}-runner + + + + + + + + + + + diff --git a/camel-k-quarkus/camel-k-quarkus-itests/camel-k-quarkus-itests-runtime/src/main/java/org/apache/camel/k/quarkus/it/Application.java b/camel-k-quarkus/camel-k-quarkus-itests/camel-k-quarkus-itests-runtime/src/main/java/org/apache/camel/k/quarkus/it/Application.java new file mode 100644 index 000000000..f3112a83f --- /dev/null +++ b/camel-k-quarkus/camel-k-quarkus-itests/camel-k-quarkus-itests-runtime/src/main/java/org/apache/camel/k/quarkus/it/Application.java @@ -0,0 +1,73 @@ +/* + * 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 org.apache.camel.k.quarkus.it; + +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Inject; +import javax.json.Json; +import javax.json.JsonObject; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +import org.apache.camel.CamelContext; +import org.apache.camel.k.Runtime; +import org.eclipse.microprofile.config.Config; + +import static org.apache.camel.k.quarkus.Application.instance; + +@Path("/test") +@ApplicationScoped +public class Application { + @Inject + Config config; + + @GET + @Path("/inspect") + @Produces(MediaType.APPLICATION_JSON) + public JsonObject inspect() { + return Json.createObjectBuilder() + .add( + "camel-context", + instance(CamelContext.class) + .map(Object::getClass) + .map(Class::getName) + .orElse("")) + .add( + "camel-k-runtime", + instance(Runtime.class) + .map(Object::getClass) + .map(Class::getName) + .orElse("")) + .add( + "shutdown-task", + instance(org.apache.camel.k.quarkus.Application.ShutdownTask.class) + .map(Object::getClass) + .map(Class::getName) + .orElse("")) + .build(); + } + + @GET + @Path("/property/{name}") + @Produces(MediaType.TEXT_PLAIN) + public String property(@PathParam("name") String name) { + return config.getValue(name, String.class); + } +} diff --git a/camel-k-quarkus/camel-k-quarkus-itests/camel-k-quarkus-itests-runtime/src/main/resources/application.properties b/camel-k-quarkus/camel-k-quarkus-itests/camel-k-quarkus-itests-runtime/src/main/resources/application.properties new file mode 100644 index 000000000..0d67e0636 --- /dev/null +++ b/camel-k-quarkus/camel-k-quarkus-itests/camel-k-quarkus-itests-runtime/src/main/resources/application.properties @@ -0,0 +1,22 @@ +## --------------------------------------------------------------------------- +## 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.log.console.enable = false +quarkus.banner.enabled = false diff --git a/camel-k-quarkus/camel-k-quarkus-itests/camel-k-quarkus-itests-runtime/src/test/java/org/apache/camel/k/quarkus/it/RuntimeIT.java b/camel-k-quarkus/camel-k-quarkus-itests/camel-k-quarkus-itests-runtime/src/test/java/org/apache/camel/k/quarkus/it/RuntimeIT.java new file mode 100644 index 000000000..29f1c1745 --- /dev/null +++ b/camel-k-quarkus/camel-k-quarkus-itests/camel-k-quarkus-itests-runtime/src/test/java/org/apache/camel/k/quarkus/it/RuntimeIT.java @@ -0,0 +1,23 @@ +/* + * 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 org.apache.camel.k.quarkus.it; + +import io.quarkus.test.junit.NativeImageTest; + +@NativeImageTest +public class RuntimeIT extends RuntimeTest { +} \ No newline at end of file diff --git a/camel-k-quarkus/camel-k-quarkus-itests/camel-k-quarkus-itests-runtime/src/test/java/org/apache/camel/k/quarkus/it/RuntimeTest.java b/camel-k-quarkus/camel-k-quarkus-itests/camel-k-quarkus-itests-runtime/src/test/java/org/apache/camel/k/quarkus/it/RuntimeTest.java new file mode 100644 index 000000000..5689b734c --- /dev/null +++ b/camel-k-quarkus/camel-k-quarkus-itests/camel-k-quarkus-itests-runtime/src/test/java/org/apache/camel/k/quarkus/it/RuntimeTest.java @@ -0,0 +1,62 @@ +/* + * 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 org.apache.camel.k.quarkus.it; + +import javax.ws.rs.core.MediaType; + +import io.quarkus.test.junit.QuarkusTest; +import io.restassured.RestAssured; +import io.restassured.path.json.JsonPath; +import org.apache.camel.k.quarkus.Application; +import org.apache.camel.k.quarkus.ApplicationProducers; +import org.apache.camel.quarkus.core.FastCamelContext; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +@QuarkusTest +public class RuntimeTest { + @Test + public void inspect() { + JsonPath p = RestAssured.given() + .accept(MediaType.APPLICATION_JSON) + .get("/test/inspect") + .then() + .statusCode(200) + .extract() + .body() + .jsonPath(); + + assertThat(p.getString("camel-context")).isEqualTo(FastCamelContext.class.getName()); + assertThat(p.getString("camel-k-runtime")).isEqualTo(Application.Runtime.class.getName()); + assertThat(p.getString("shutdown-task")).isEqualTo(ApplicationProducers.DefaultShutdownTask.class.getName()); + } + + @Test + public void configSourceProvider() { + String result = RestAssured.given() + .accept(MediaType.TEXT_PLAIN) + .get("/test/property/quarkus.my-property") + .then() + .statusCode(200) + .extract() + .body() + .asString(); + + assertThat(result).isEqualTo("my-test-value"); + } +} \ No newline at end of file diff --git a/camel-k-quarkus/camel-k-quarkus-itests/camel-k-quarkus-itests-runtime/src/test/resources/test.properties b/camel-k-quarkus/camel-k-quarkus-itests/camel-k-quarkus-itests-runtime/src/test/resources/test.properties new file mode 100644 index 000000000..eb73887b5 --- /dev/null +++ b/camel-k-quarkus/camel-k-quarkus-itests/camel-k-quarkus-itests-runtime/src/test/resources/test.properties @@ -0,0 +1,18 @@ +## --------------------------------------------------------------------------- +## 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.my-property = my-test-value \ No newline at end of file diff --git a/camel-k-quarkus/camel-k-quarkus-itests/pom.xml b/camel-k-quarkus/camel-k-quarkus-itests/pom.xml index bc5052555..dd4ddf147 100644 --- a/camel-k-quarkus/camel-k-quarkus-itests/pom.xml +++ b/camel-k-quarkus/camel-k-quarkus-itests/pom.xml @@ -30,6 +30,7 @@ camel-k-quarkus-itests-core + camel-k-quarkus-itests-runtime camel-k-quarkus-itests-cron camel-k-quarkus-itests-master camel-k-quarkus-itests-kamelet diff --git a/camel-k-quarkus/camel-k-runtime-quarkus/deployment/src/main/java/org/apache/camel/k/quarkus/deployment/DeploymentProcessor.java b/camel-k-quarkus/camel-k-runtime-quarkus/deployment/src/main/java/org/apache/camel/k/quarkus/deployment/DeploymentProcessor.java index 076ca3cb1..f95949203 100644 --- a/camel-k-quarkus/camel-k-runtime-quarkus/deployment/src/main/java/org/apache/camel/k/quarkus/deployment/DeploymentProcessor.java +++ b/camel-k-quarkus/camel-k-runtime-quarkus/deployment/src/main/java/org/apache/camel/k/quarkus/deployment/DeploymentProcessor.java @@ -20,13 +20,20 @@ import java.util.List; import java.util.ServiceLoader; +import io.quarkus.arc.deployment.AdditionalBeanBuildItem; +import io.quarkus.arc.deployment.BeanContainerBuildItem; +import io.quarkus.arc.deployment.SyntheticBeansRuntimeInitBuildItem; import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.annotations.Consume; import io.quarkus.deployment.annotations.ExecutionTime; import io.quarkus.deployment.annotations.Record; import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; import org.apache.camel.k.Runtime; +import org.apache.camel.k.quarkus.ApplicationProducers; import org.apache.camel.k.quarkus.ApplicationRecorder; +import org.apache.camel.quarkus.core.deployment.spi.CamelRuntimeTaskBuildItem; import org.apache.camel.quarkus.main.CamelMainApplication; +import org.apache.camel.quarkus.main.deployment.spi.CamelMainBuildItem; import org.apache.camel.quarkus.main.deployment.spi.CamelMainListenerBuildItem; public class DeploymentProcessor { @@ -43,4 +50,24 @@ CamelMainListenerBuildItem registerListener(ApplicationRecorder recorder) { return new CamelMainListenerBuildItem(recorder.createMainListener(listeners)); } + + @Record(ExecutionTime.RUNTIME_INIT) + @BuildStep + @Consume(SyntheticBeansRuntimeInitBuildItem.class) + CamelRuntimeTaskBuildItem registerRuntime( + ApplicationRecorder recorder, + CamelMainBuildItem camelMain, + BeanContainerBuildItem beanContainer) { + + recorder.publishRuntime(camelMain.getInstance(), beanContainer.getValue()); + + return new CamelRuntimeTaskBuildItem("camel-k-runtime"); + } + + @BuildStep + List unremovableBeans() { + return List.of( + AdditionalBeanBuildItem.unremovableOf(ApplicationProducers.class) + ); + } } diff --git a/camel-k-quarkus/camel-k-runtime-quarkus/runtime/src/main/java/org/apache/camel/k/quarkus/Application.java b/camel-k-quarkus/camel-k-runtime-quarkus/runtime/src/main/java/org/apache/camel/k/quarkus/Application.java new file mode 100644 index 000000000..fb0392a1d --- /dev/null +++ b/camel-k-quarkus/camel-k-runtime-quarkus/runtime/src/main/java/org/apache/camel/k/quarkus/Application.java @@ -0,0 +1,170 @@ +/* + * 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 org.apache.camel.k.quarkus; + +import java.util.Comparator; +import java.util.List; +import java.util.Optional; +import java.util.Properties; +import java.util.concurrent.atomic.AtomicBoolean; + +import io.quarkus.arc.Arc; +import io.quarkus.arc.ArcContainer; +import io.quarkus.arc.InstanceHandle; +import io.quarkus.runtime.Quarkus; +import org.apache.camel.CamelContext; +import org.apache.camel.RoutesBuilder; +import org.apache.camel.main.BaseMainSupport; +import org.apache.camel.main.MainListener; + +public final class Application { + private Application() { + } + + /** + * The camel-k runtime impl based on camel-quarkus + */ + public static class Runtime implements org.apache.camel.k.Runtime { + private final BaseMainSupport main; + private final AtomicBoolean stopped; + + public Runtime(BaseMainSupport main) { + this.main = main; + this.stopped = new AtomicBoolean(); + } + + @Override + public CamelContext getCamelContext() { + return main.getCamelContext(); + } + + @Override + public void addRoutes(RoutesBuilder builder) { + main.configure().addRoutesBuilder(builder); + } + + @Override + public void addConfiguration(Object configuration) { + main.configure().addConfiguration(configuration); + } + + @Override + public void setInitialProperties(Properties properties) { + main.setInitialProperties(properties); + } + + @Override + public void setProperties(Properties properties) { + main.setOverrideProperties(properties); + } + + @Override + public void stop() throws Exception { + if (!this.stopped.compareAndExchange(false, true)) { + instance(ShutdownTask.class).ifPresentOrElse( + ShutdownTask::run, + Quarkus::asyncExit); + } + } + } + + /** + * Adapts main events to camel-k runtime lifecycle + */ + public static class ListenerAdapter implements MainListener { + private final org.apache.camel.k.Runtime.Listener[] listeners; + + public ListenerAdapter(List listeners) { + this.listeners = listeners.stream() + .sorted(Comparator.comparingInt(org.apache.camel.k.Runtime.Listener::getOrder)) + .toArray(org.apache.camel.k.Runtime.Listener[]::new); + } + + @Override + public void beforeInitialize(BaseMainSupport main) { + invokeListeners(org.apache.camel.k.Runtime.Phase.ConfigureProperties); + } + + @Override + public void beforeConfigure(BaseMainSupport main) { + invokeListeners(org.apache.camel.k.Runtime.Phase.ConfigureRoutes); + } + + @Override + public void afterConfigure(BaseMainSupport main) { + invokeListeners(org.apache.camel.k.Runtime.Phase.ConfigureContext); + } + + @Override + public void configure(CamelContext context) { + // no-op + } + + @Override + public void beforeStart(BaseMainSupport main) { + invokeListeners(org.apache.camel.k.Runtime.Phase.Starting); + } + + @Override + public void afterStart(BaseMainSupport main) { + invokeListeners(org.apache.camel.k.Runtime.Phase.Started); + } + + @Override + public void beforeStop(BaseMainSupport main) { + invokeListeners(org.apache.camel.k.Runtime.Phase.Stopping); + } + + @Override + public void afterStop(BaseMainSupport main) { + invokeListeners(org.apache.camel.k.Runtime.Phase.Stopped); + } + + private void invokeListeners(org.apache.camel.k.Runtime.Phase phase) { + org.apache.camel.k.Runtime runtime = instance(org.apache.camel.k.Runtime.class) + .orElseThrow(() -> new IllegalStateException("Unable to fine a Runtime instance")); + + for (int i = 0; i < listeners.length; i ++) { + listeners[i].accept(phase, runtime); + } + } + } + + /** + * Provide the task to be executed to shutdown the runtime + */ + @FunctionalInterface + public interface ShutdownTask { + void run(); + } + + // ********************************* + // + // Helpers + // + // ********************************* + + public static Optional container() { + return Optional.of(Arc.container()); + } + + public static Optional instance(Class type) { + return container() + .map(container -> container.instance(type)) + .map(InstanceHandle::get); + } +} diff --git a/camel-k-quarkus/camel-k-runtime-quarkus/runtime/src/main/java/org/apache/camel/k/quarkus/ApplicationRuntimeConfigSourceProvider.java b/camel-k-quarkus/camel-k-runtime-quarkus/runtime/src/main/java/org/apache/camel/k/quarkus/ApplicationConfigSourceProvider.java similarity index 95% rename from camel-k-quarkus/camel-k-runtime-quarkus/runtime/src/main/java/org/apache/camel/k/quarkus/ApplicationRuntimeConfigSourceProvider.java rename to camel-k-quarkus/camel-k-runtime-quarkus/runtime/src/main/java/org/apache/camel/k/quarkus/ApplicationConfigSourceProvider.java index 302e0c805..cef2910a0 100644 --- a/camel-k-quarkus/camel-k-runtime-quarkus/runtime/src/main/java/org/apache/camel/k/quarkus/ApplicationRuntimeConfigSourceProvider.java +++ b/camel-k-quarkus/camel-k-runtime-quarkus/runtime/src/main/java/org/apache/camel/k/quarkus/ApplicationConfigSourceProvider.java @@ -24,7 +24,7 @@ import org.eclipse.microprofile.config.spi.ConfigSource; import org.eclipse.microprofile.config.spi.ConfigSourceProvider; -public class ApplicationRuntimeConfigSourceProvider implements ConfigSourceProvider { +public class ApplicationConfigSourceProvider implements ConfigSourceProvider { @Override public Iterable getConfigSources(ClassLoader forClassLoader) { final Properties applicationProperties = PropertiesSupport.loadProperties(); diff --git a/camel-k-quarkus/camel-k-runtime-quarkus/runtime/src/main/java/org/apache/camel/k/quarkus/ApplicationListenerAdapter.java b/camel-k-quarkus/camel-k-runtime-quarkus/runtime/src/main/java/org/apache/camel/k/quarkus/ApplicationListenerAdapter.java deleted file mode 100644 index ccb01cca5..000000000 --- a/camel-k-quarkus/camel-k-runtime-quarkus/runtime/src/main/java/org/apache/camel/k/quarkus/ApplicationListenerAdapter.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * 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 org.apache.camel.k.quarkus; - -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.Properties; - -import io.quarkus.runtime.Quarkus; -import org.apache.camel.CamelContext; -import org.apache.camel.RoutesBuilder; -import org.apache.camel.k.Runtime; -import org.apache.camel.main.BaseMainSupport; -import org.apache.camel.main.MainListener; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class ApplicationListenerAdapter implements MainListener { - private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationListenerAdapter.class); - - private final List listeners; - - public ApplicationListenerAdapter() { - this.listeners = new ArrayList<>(); - } - - public ApplicationListenerAdapter(List listeners) { - this.listeners = new ArrayList<>(listeners); - } - - public void setListeners(List listeners) { - this.listeners.clear(); - this.listeners.addAll(listeners); - } - - @Override - public void beforeInitialize(BaseMainSupport main) { - invokeListeners(listeners, on(main), Runtime.Phase.ConfigureProperties); - } - - @Override - public void beforeConfigure(BaseMainSupport main) { - invokeListeners(listeners, on(main), Runtime.Phase.ConfigureRoutes); - } - - @Override - public void afterConfigure(BaseMainSupport main) { - invokeListeners(listeners, on(main), Runtime.Phase.ConfigureContext); - } - - @Override - public void configure(CamelContext context) { - // no-op - } - - @Override - public void beforeStart(BaseMainSupport main) { - invokeListeners(listeners, on(main), Runtime.Phase.Starting); - } - - @Override - public void afterStart(BaseMainSupport main) { - invokeListeners(listeners, on(main), Runtime.Phase.Started); - } - - @Override - public void beforeStop(BaseMainSupport main) { - invokeListeners(listeners, on(main), Runtime.Phase.Stopping); - } - - @Override - public void afterStop(BaseMainSupport main) { - invokeListeners(listeners, on(main), Runtime.Phase.Stopped); - } - - // ************************ - // - // Helpers - // - // ************************ - - private static void invokeListeners(List listeners, Runtime runtime, Runtime.Phase phase) { - listeners.stream() - .sorted(Comparator.comparingInt(Runtime.Listener::getOrder)) - .forEach(l -> { - if (l.accept(phase, runtime)) { - LOGGER.debug("Listener {} executed in phase {}", l, phase); - } - }); - } - - private static Runtime on(BaseMainSupport main) { - return new Runtime() { - @Override - public CamelContext getCamelContext() { - return main.getCamelContext(); - } - - @Override - public void addRoutes(RoutesBuilder builder) { - main.configure().addRoutesBuilder(builder); - } - - @Override - public void addConfiguration(Object configuration) { - main.configure().addConfiguration(configuration); - } - - @Override - public void setInitialProperties(Properties properties) { - main.setInitialProperties(properties); - } - - @Override - public void setProperties(Properties properties) { - main.setOverrideProperties(properties); - } - - @Override - public void stop() throws Exception { - Quarkus.asyncExit(); - } - }; - } -} diff --git a/camel-k-quarkus/camel-k-runtime-quarkus/runtime/src/main/java/org/apache/camel/k/quarkus/ApplicationProducers.java b/camel-k-quarkus/camel-k-runtime-quarkus/runtime/src/main/java/org/apache/camel/k/quarkus/ApplicationProducers.java new file mode 100644 index 000000000..f44f3c9c8 --- /dev/null +++ b/camel-k-quarkus/camel-k-runtime-quarkus/runtime/src/main/java/org/apache/camel/k/quarkus/ApplicationProducers.java @@ -0,0 +1,57 @@ +/* + * 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 org.apache.camel.k.quarkus; + +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.inject.Produces; +import javax.inject.Singleton; + +import io.quarkus.arc.DefaultBean; +import io.quarkus.arc.Unremovable; +import io.quarkus.runtime.Quarkus; +import org.apache.camel.k.Runtime; + +@ApplicationScoped +public class ApplicationProducers { + private volatile Runtime runtime; + + public void setRuntime(Runtime runtime) { + this.runtime = runtime; + } + + @Unremovable + @Singleton + @Produces + Runtime runtime() { + return this.runtime; + } + + @Unremovable + @DefaultBean + @Singleton + @Produces + Application.ShutdownTask shutdownTask() { + return new DefaultShutdownTask(); + } + + public static class DefaultShutdownTask implements Application.ShutdownTask { + @Override + public void run() { + Quarkus.asyncExit(); + } + } +} diff --git a/camel-k-quarkus/camel-k-runtime-quarkus/runtime/src/main/java/org/apache/camel/k/quarkus/ApplicationRecorder.java b/camel-k-quarkus/camel-k-runtime-quarkus/runtime/src/main/java/org/apache/camel/k/quarkus/ApplicationRecorder.java index 587dd9b1d..e4ed9a86e 100644 --- a/camel-k-quarkus/camel-k-runtime-quarkus/runtime/src/main/java/org/apache/camel/k/quarkus/ApplicationRecorder.java +++ b/camel-k-quarkus/camel-k-runtime-quarkus/runtime/src/main/java/org/apache/camel/k/quarkus/ApplicationRecorder.java @@ -18,14 +18,20 @@ import java.util.List; +import io.quarkus.arc.runtime.BeanContainer; import io.quarkus.runtime.RuntimeValue; import io.quarkus.runtime.annotations.Recorder; import org.apache.camel.k.Runtime; import org.apache.camel.main.MainListener; +import org.apache.camel.quarkus.main.CamelMain; @Recorder public class ApplicationRecorder { public RuntimeValue createMainListener(List listeners) { - return new RuntimeValue<>(new ApplicationListenerAdapter(listeners)); + return new RuntimeValue<>(new Application.ListenerAdapter(listeners)); + } + + public void publishRuntime(RuntimeValue main, BeanContainer container) { + container.instance(ApplicationProducers.class).setRuntime(new Application.Runtime(main.getValue())); } } diff --git a/camel-k-quarkus/camel-k-runtime-quarkus/runtime/src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.ConfigSourceProvider b/camel-k-quarkus/camel-k-runtime-quarkus/runtime/src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.ConfigSourceProvider index 30fe91e1b..1079ba1eb 100644 --- a/camel-k-quarkus/camel-k-runtime-quarkus/runtime/src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.ConfigSourceProvider +++ b/camel-k-quarkus/camel-k-runtime-quarkus/runtime/src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.ConfigSourceProvider @@ -14,4 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -org.apache.camel.k.quarkus.ApplicationRuntimeConfigSourceProvider \ No newline at end of file +org.apache.camel.k.quarkus.ApplicationConfigSourceProvider \ No newline at end of file diff --git a/camel-k-runtime-cron/src/main/java/org/apache/camel/k/cron/CronSourceLoaderInterceptor.java b/camel-k-runtime-cron/src/main/java/org/apache/camel/k/cron/CronSourceLoaderInterceptor.java index d3026d1a1..3c965ac0b 100644 --- a/camel-k-runtime-cron/src/main/java/org/apache/camel/k/cron/CronSourceLoaderInterceptor.java +++ b/camel-k-runtime-cron/src/main/java/org/apache/camel/k/cron/CronSourceLoaderInterceptor.java @@ -30,6 +30,7 @@ import org.apache.camel.spi.CamelEvent; import org.apache.camel.spi.Configurer; import org.apache.camel.support.EventNotifierSupport; +import org.apache.camel.support.service.ServiceHelper; import org.apache.camel.util.ObjectHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -100,7 +101,10 @@ public Optional builder() { // Don't install the shutdown strategy more than once. // if (context.getManagementStrategy().getEventNotifiers().stream().noneMatch(CronShutdownStrategy.class::isInstance)) { - context.getManagementStrategy().addEventNotifier(new CronShutdownStrategy(runtime)); + CronShutdownStrategy strategy = new CronShutdownStrategy(runtime); + ServiceHelper.startService(strategy); + + context.getManagementStrategy().addEventNotifier(strategy); } } } diff --git a/camel-k-runtime-knative/src/generated/resources/META-INF/services/org/apache/camel/configurer/org.apache.camel.k.loader.knative.KnativeSourceLoaderInterceptor b/camel-k-runtime-knative/src/generated/resources/META-INF/services/org/apache/camel/configurer/org.apache.camel.k.loader.knative.KnativeSourceLoaderInterceptor deleted file mode 100644 index 50833dcf2..000000000 --- a/camel-k-runtime-knative/src/generated/resources/META-INF/services/org/apache/camel/configurer/org.apache.camel.k.loader.knative.KnativeSourceLoaderInterceptor +++ /dev/null @@ -1,2 +0,0 @@ -# Generated by camel build tools - do NOT edit this file! -class=org.apache.camel.k.loader.knative.KnativeSourceLoaderInterceptorConfigurer diff --git a/examples/camel-k-runtime-example-quarkus-cron/data/application.properties b/examples/camel-k-runtime-example-quarkus-cron/data/application.properties index 339b6c409..c32de635a 100644 --- a/examples/camel-k-runtime-example-quarkus-cron/data/application.properties +++ b/examples/camel-k-runtime-example-quarkus-cron/data/application.properties @@ -30,6 +30,6 @@ camel.main.stream-caching-spool-directory = ${java.io.tmpdir}/camel-q # # Camel K # -camel.k.loader.interceptor.cron.overridable-components = timer_ +camel.k.loader.interceptor.cron.overridable-components = timer diff --git a/pom.xml b/pom.xml index fb3c0fe62..a7febd7b6 100644 --- a/pom.xml +++ b/pom.xml @@ -618,6 +618,12 @@ ${rest-assured-version} + + org.awaitility + awaitility + ${awaitility-version} + + org.codehaus.groovy groovy