This is an automated email from the ASF dual-hosted git repository.

jamesnetherton pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-quarkus.git


The following commit(s) were added to refs/heads/main by this push:
     new 604a33e978 Add camel-console management endpoint
604a33e978 is described below

commit 604a33e978f9f4aaea75f4e5da0d6520d115ea88
Author: James Netherton <[email protected]>
AuthorDate: Thu Feb 13 11:23:34 2025 +0000

    Add camel-console management endpoint
    
    Fixes #6997
---
 .../ROOT/pages/reference/extensions/console.adoc   |  80 +++++++++++++++
 extensions-jvm/console/deployment/pom.xml          |   9 ++
 .../console/deployment/ConsoleProcessor.java       |  69 ++++++++++++-
 .../component/console/ConsoleDisabledTest.java     |  18 ++--
 .../ConsoleEnabledWithCamelMainPropertyTest.java   |  20 ++--
 .../ConsoleManagementEndpointCustomPathTest.java   |  28 +++--
 .../component/console/ConsoleNotExposedTest.java   |  20 ++--
 extensions-jvm/console/runtime/pom.xml             |   4 +
 .../console/runtime/src/main/doc/usage.adoc        |  40 ++++++++
 .../component/console/CamelConsoleConfig.java      |  53 ++++++++++
 .../component/console/CamelConsoleRecorder.java    | 113 +++++++++++++++++++++
 .../src/main/resources/application.properties      |  17 ++++
 .../quarkus/component/console/it/ConsoleTest.java  |  41 ++++++++
 13 files changed, 475 insertions(+), 37 deletions(-)

diff --git a/docs/modules/ROOT/pages/reference/extensions/console.adoc 
b/docs/modules/ROOT/pages/reference/extensions/console.adoc
index fcbb1132bd..497814c6f1 100644
--- a/docs/modules/ROOT/pages/reference/extensions/console.adoc
+++ b/docs/modules/ROOT/pages/reference/extensions/console.adoc
@@ -39,3 +39,83 @@ Please refer to the above link for usage and configuration 
details.
 ifeval::[{doc-show-user-guide-link} == true]
 Check the xref:user-guide/index.adoc[User guide] for more information about 
writing Camel Quarkus applications.
 endif::[]
+
+[id="extensions-console-usage"]
+== Usage
+[id="extensions-console-usage-developer-console-endpoint"]
+=== Developer console endpoint
+
+To access the developer console, you must first enable it by adding 
configuration to `application.properties`.
+
+[source,properties]
+----
+quarkus.camel.console.enabled=true
+----
+
+Alternatively you can use a `camel-main` configuration option.
+
+[source,properties]
+----
+camel.main.dev-console-enabled=true
+----
+
+The console is then available at the following URL.
+
+[source,text]
+----
+http://localhost:8080/q/camel/dev-console
+----
+
+You can then call a console by its id, such as `routes`:
+
+[source,text]
+----
+http://localhost:8080/q/camel/dev-console/routes
+----
+
+[id="extensions-console-usage-exposing-the-developer-console-in-prod-mode"]
+=== Exposing the developer console in prod mode
+
+By default, the console is only exposed in dev and test modes. To expose the 
console in prod mode, add the following configuration to 
`application.properties`.
+
+[source,properties]
+----
+quarkus.camel.console.exposure-mode=ALL
+----
+
+See the configuration overview below for further details about `exposure-mode`.
+
+
+[id="extensions-console-additional-camel-quarkus-configuration"]
+== Additional Camel Quarkus configuration
+
+[width="100%",cols="80,5,15",options="header"]
+|===
+| Configuration property | Type | Default
+
+
+|icon:lock[title=Fixed at build time] 
[[quarkus.camel.console.enabled]]`link:#quarkus.camel.console.enabled[quarkus.camel.console.enabled]`
+
+Whether the Camel developer console is enabled.
+| `boolean`
+| `false`
+
+|icon:lock[title=Fixed at build time] 
[[quarkus.camel.console.path]]`link:#quarkus.camel.console.path[quarkus.camel.console.path]`
+
+The context path under which the Camel developer console is deployed (default 
/q/camel/dev-console).
+| `string`
+| `camel/dev-console`
+
+|icon:lock[title=Fixed at build time] 
[[quarkus.camel.console.exposure-mode]]`link:#quarkus.camel.console.exposure-mode[quarkus.camel.console.exposure-mode]`
+
+The modes in which the Camel developer console is available. The default 
{@code dev-test} enables the developer
+console only in dev mode and test modes.
+A value of {@code all} enables agent discovery in dev, test and prod modes. 
Setting the value to {@code none} will
+not expose the developer console HTTP endpoint.
+| `all`, `dev-test`, `none`
+| `dev-test`
+|===
+
+[.configuration-legend]
+{doc-link-icon-lock}[title=Fixed at build time] Configuration property fixed 
at build time. All other configuration properties are overridable at runtime.
+
diff --git a/extensions-jvm/console/deployment/pom.xml 
b/extensions-jvm/console/deployment/pom.xml
index 4563fd6da4..5dd3e3613b 100644
--- a/extensions-jvm/console/deployment/pom.xml
+++ b/extensions-jvm/console/deployment/pom.xml
@@ -30,6 +30,10 @@
     <name>Camel Quarkus :: Console :: Deployment</name>
 
     <dependencies>
+        <dependency>
+            <groupId>io.quarkus</groupId>
+            <artifactId>quarkus-vertx-http-deployment</artifactId>
+        </dependency>
         <dependency>
             <groupId>org.apache.camel.quarkus</groupId>
             <artifactId>camel-quarkus-core-deployment</artifactId>
@@ -44,6 +48,11 @@
             <artifactId>quarkus-junit5-internal</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>io.rest-assured</groupId>
+            <artifactId>rest-assured</artifactId>
+            <scope>test</scope>
+        </dependency>
         <dependency>
             <groupId>org.apache.camel</groupId>
             <artifactId>camel-stub</artifactId>
diff --git 
a/extensions-jvm/console/deployment/src/main/java/org/apache/camel/quarkus/component/console/deployment/ConsoleProcessor.java
 
b/extensions-jvm/console/deployment/src/main/java/org/apache/camel/quarkus/component/console/deployment/ConsoleProcessor.java
index 789f2187d5..60a66ccbd7 100644
--- 
a/extensions-jvm/console/deployment/src/main/java/org/apache/camel/quarkus/component/console/deployment/ConsoleProcessor.java
+++ 
b/extensions-jvm/console/deployment/src/main/java/org/apache/camel/quarkus/component/console/deployment/ConsoleProcessor.java
@@ -16,18 +16,33 @@
  */
 package org.apache.camel.quarkus.component.console.deployment;
 
+import java.util.function.BooleanSupplier;
+import java.util.function.Consumer;
+
+import io.quarkus.deployment.annotations.BuildProducer;
 import io.quarkus.deployment.annotations.BuildStep;
+import io.quarkus.deployment.annotations.BuildSteps;
 import io.quarkus.deployment.annotations.ExecutionTime;
 import io.quarkus.deployment.annotations.Record;
 import io.quarkus.deployment.builditem.FeatureBuildItem;
 import io.quarkus.deployment.pkg.steps.NativeOrNativeSourcesBuild;
+import io.quarkus.runtime.LaunchMode;
+import io.quarkus.vertx.http.deployment.NonApplicationRootPathBuildItem;
+import io.quarkus.vertx.http.deployment.RouteBuildItem;
+import io.vertx.core.Handler;
+import io.vertx.ext.web.Route;
+import io.vertx.ext.web.RoutingContext;
+import org.apache.camel.quarkus.component.console.CamelConsoleConfig;
+import org.apache.camel.quarkus.component.console.CamelConsoleRecorder;
 import org.apache.camel.quarkus.core.JvmOnlyRecorder;
+import org.apache.camel.quarkus.core.deployment.spi.CamelContextBuildItem;
 import org.apache.camel.quarkus.core.deployment.spi.CamelServiceDestination;
 import 
org.apache.camel.quarkus.core.deployment.spi.CamelServicePatternBuildItem;
+import org.eclipse.microprofile.config.ConfigProvider;
 import org.jboss.logging.Logger;
 
+@BuildSteps(onlyIf = ConsoleProcessor.CamelConsoleEnabled.class)
 class ConsoleProcessor {
-
     private static final Logger LOG = Logger.getLogger(ConsoleProcessor.class);
     private static final String FEATURE = "camel-console";
 
@@ -42,6 +57,41 @@ class ConsoleProcessor {
                 "META-INF/services/org/apache/camel/dev-console/*");
     }
 
+    @BuildStep
+    @Record(ExecutionTime.STATIC_INIT)
+    void initDevConsoleRegistry(
+            CamelContextBuildItem camelContext,
+            CamelConsoleRecorder recorder) {
+        recorder.initDevConsoleRegistry(camelContext.getCamelContext());
+    }
+
+    @BuildStep
+    @Record(ExecutionTime.RUNTIME_INIT)
+    void createManagementRoute(
+            CamelContextBuildItem camelContext,
+            NonApplicationRootPathBuildItem nonApplicationRootPathBuildItem,
+            BuildProducer<RouteBuildItem> routes,
+            CamelConsoleConfig config,
+            CamelConsoleRecorder recorder) {
+
+        if (canExposeManagementEndpoint(config)) {
+            Consumer<Route> route = recorder.route();
+            Handler<RoutingContext> handler = 
recorder.getHandler(camelContext.getCamelContext());
+
+            routes.produce(nonApplicationRootPathBuildItem.routeBuilder()
+                    .management()
+                    .routeFunction(config.path(), route)
+                    .handler(handler)
+                    .build());
+
+            routes.produce(nonApplicationRootPathBuildItem.routeBuilder()
+                    .management()
+                    .routeFunction(config.path() + "/:id", route)
+                    .handler(handler)
+                    .build());
+        }
+    }
+
     /**
      * Remove this once this extension starts supporting the native mode.
      */
@@ -51,4 +101,21 @@ class ConsoleProcessor {
         JvmOnlyRecorder.warnJvmInNative(LOG, FEATURE); // warn at build time
         recorder.warnJvmInNative(FEATURE); // warn at runtime
     }
+
+    private static boolean canExposeManagementEndpoint(CamelConsoleConfig 
config) {
+        return (LaunchMode.current().isDevOrTest()
+                && config.exposureMode() == 
CamelConsoleConfig.ExposureMode.DEV_TEST)
+                || 
config.exposureMode().equals(CamelConsoleConfig.ExposureMode.ALL);
+    }
+
+    static final class CamelConsoleEnabled implements BooleanSupplier {
+        CamelConsoleConfig config;
+
+        @Override
+        public boolean getAsBoolean() {
+            return config.enabled() || ConfigProvider.getConfig()
+                    .getOptionalValue("camel.main.dev-console-enabled", 
Boolean.class)
+                    .orElse(false);
+        }
+    }
 }
diff --git 
a/integration-tests-jvm/console/src/test/java/org/apache/camel/quarkus/component/console/it/ConsoleTest.java
 
b/extensions-jvm/console/deployment/src/test/java/org/apache/camel/quarkus/component/console/ConsoleDisabledTest.java
similarity index 69%
copy from 
integration-tests-jvm/console/src/test/java/org/apache/camel/quarkus/component/console/it/ConsoleTest.java
copy to 
extensions-jvm/console/deployment/src/test/java/org/apache/camel/quarkus/component/console/ConsoleDisabledTest.java
index f06127b8a6..ebc62d6f1d 100644
--- 
a/integration-tests-jvm/console/src/test/java/org/apache/camel/quarkus/component/console/it/ConsoleTest.java
+++ 
b/extensions-jvm/console/deployment/src/test/java/org/apache/camel/quarkus/component/console/ConsoleDisabledTest.java
@@ -14,21 +14,21 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.quarkus.component.console.it;
+package org.apache.camel.quarkus.component.console;
 
-import io.quarkus.test.junit.QuarkusTest;
+import io.quarkus.test.QuarkusUnitTest;
 import io.restassured.RestAssured;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
 
-import static org.hamcrest.Matchers.is;
+class ConsoleDisabledTest {
+    @RegisterExtension
+    static final QuarkusUnitTest CONFIG = new 
QuarkusUnitTest().withEmptyApplication();
 
-@QuarkusTest
-class ConsoleTest {
     @Test
-    public void resolveContextDevConsole() {
-        RestAssured.get("/console/context")
+    void managementEndpointDisabled() {
+        RestAssured.get("/q/camel/dev-console")
                 .then()
-                .statusCode(200)
-                .body(is("context"));
+                .statusCode(404);
     }
 }
diff --git 
a/integration-tests-jvm/console/src/test/java/org/apache/camel/quarkus/component/console/it/ConsoleTest.java
 
b/extensions-jvm/console/deployment/src/test/java/org/apache/camel/quarkus/component/console/ConsoleEnabledWithCamelMainPropertyTest.java
similarity index 64%
copy from 
integration-tests-jvm/console/src/test/java/org/apache/camel/quarkus/component/console/it/ConsoleTest.java
copy to 
extensions-jvm/console/deployment/src/test/java/org/apache/camel/quarkus/component/console/ConsoleEnabledWithCamelMainPropertyTest.java
index f06127b8a6..c4c441bd77 100644
--- 
a/integration-tests-jvm/console/src/test/java/org/apache/camel/quarkus/component/console/it/ConsoleTest.java
+++ 
b/extensions-jvm/console/deployment/src/test/java/org/apache/camel/quarkus/component/console/ConsoleEnabledWithCamelMainPropertyTest.java
@@ -14,21 +14,23 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.quarkus.component.console.it;
+package org.apache.camel.quarkus.component.console;
 
-import io.quarkus.test.junit.QuarkusTest;
+import io.quarkus.test.QuarkusUnitTest;
 import io.restassured.RestAssured;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
 
-import static org.hamcrest.Matchers.is;
+class ConsoleEnabledWithCamelMainPropertyTest {
+    @RegisterExtension
+    static final QuarkusUnitTest CONFIG = new QuarkusUnitTest()
+            .withEmptyApplication()
+            .overrideConfigKey("camel.main.dev-console-enabled", "true");
 
-@QuarkusTest
-class ConsoleTest {
     @Test
-    public void resolveContextDevConsole() {
-        RestAssured.get("/console/context")
+    void managementEndpointEnabled() {
+        RestAssured.get("/q/camel/dev-console")
                 .then()
-                .statusCode(200)
-                .body(is("context"));
+                .statusCode(200);
     }
 }
diff --git 
a/integration-tests-jvm/console/src/test/java/org/apache/camel/quarkus/component/console/it/ConsoleTest.java
 
b/extensions-jvm/console/deployment/src/test/java/org/apache/camel/quarkus/component/console/ConsoleManagementEndpointCustomPathTest.java
similarity index 54%
copy from 
integration-tests-jvm/console/src/test/java/org/apache/camel/quarkus/component/console/it/ConsoleTest.java
copy to 
extensions-jvm/console/deployment/src/test/java/org/apache/camel/quarkus/component/console/ConsoleManagementEndpointCustomPathTest.java
index f06127b8a6..df98b99a08 100644
--- 
a/integration-tests-jvm/console/src/test/java/org/apache/camel/quarkus/component/console/it/ConsoleTest.java
+++ 
b/extensions-jvm/console/deployment/src/test/java/org/apache/camel/quarkus/component/console/ConsoleManagementEndpointCustomPathTest.java
@@ -14,21 +14,31 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.quarkus.component.console.it;
+package org.apache.camel.quarkus.component.console;
 
-import io.quarkus.test.junit.QuarkusTest;
+import io.quarkus.test.QuarkusUnitTest;
 import io.restassured.RestAssured;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
 
-import static org.hamcrest.Matchers.is;
+class ConsoleManagementEndpointCustomPathTest {
+    @RegisterExtension
+    static final QuarkusUnitTest CONFIG = new QuarkusUnitTest()
+            .withEmptyApplication()
+            .overrideConfigKey("quarkus.camel.console.enabled", "true")
+            .overrideConfigKey("quarkus.camel.console.path", "camel/other");
 
-@QuarkusTest
-class ConsoleTest {
     @Test
-    public void resolveContextDevConsole() {
-        RestAssured.get("/console/context")
+    void alternateManagementEndpointPath() {
+        RestAssured.get("/q/camel/other")
                 .then()
-                .statusCode(200)
-                .body(is("context"));
+                .statusCode(200);
+    }
+
+    @Test
+    void alternateManagementEndpointPathWithId() {
+        RestAssured.get("/q/camel/other/context")
+                .then()
+                .statusCode(200);
     }
 }
diff --git 
a/integration-tests-jvm/console/src/test/java/org/apache/camel/quarkus/component/console/it/ConsoleTest.java
 
b/extensions-jvm/console/deployment/src/test/java/org/apache/camel/quarkus/component/console/ConsoleNotExposedTest.java
similarity index 64%
copy from 
integration-tests-jvm/console/src/test/java/org/apache/camel/quarkus/component/console/it/ConsoleTest.java
copy to 
extensions-jvm/console/deployment/src/test/java/org/apache/camel/quarkus/component/console/ConsoleNotExposedTest.java
index f06127b8a6..93c0c11366 100644
--- 
a/integration-tests-jvm/console/src/test/java/org/apache/camel/quarkus/component/console/it/ConsoleTest.java
+++ 
b/extensions-jvm/console/deployment/src/test/java/org/apache/camel/quarkus/component/console/ConsoleNotExposedTest.java
@@ -14,21 +14,23 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.quarkus.component.console.it;
+package org.apache.camel.quarkus.component.console;
 
-import io.quarkus.test.junit.QuarkusTest;
+import io.quarkus.test.QuarkusUnitTest;
 import io.restassured.RestAssured;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
 
-import static org.hamcrest.Matchers.is;
+class ConsoleNotExposedTest {
+    @RegisterExtension
+    static final QuarkusUnitTest CONFIG = new QuarkusUnitTest()
+            .withEmptyApplication()
+            .overrideConfigKey("quarkus.camel.console.exposure-mode", "none");
 
-@QuarkusTest
-class ConsoleTest {
     @Test
-    public void resolveContextDevConsole() {
-        RestAssured.get("/console/context")
+    void managementEndpointNotExposed() {
+        RestAssured.get("/q/camel/dev-console")
                 .then()
-                .statusCode(200)
-                .body(is("context"));
+                .statusCode(404);
     }
 }
diff --git a/extensions-jvm/console/runtime/pom.xml 
b/extensions-jvm/console/runtime/pom.xml
index 960901bf3f..59c6b0e700 100644
--- a/extensions-jvm/console/runtime/pom.xml
+++ b/extensions-jvm/console/runtime/pom.xml
@@ -35,6 +35,10 @@
     </properties>
 
     <dependencies>
+        <dependency>
+            <groupId>io.quarkus</groupId>
+            <artifactId>quarkus-vertx-http</artifactId>
+        </dependency>
         <dependency>
             <groupId>org.apache.camel.quarkus</groupId>
             <artifactId>camel-quarkus-core</artifactId>
diff --git a/extensions-jvm/console/runtime/src/main/doc/usage.adoc 
b/extensions-jvm/console/runtime/src/main/doc/usage.adoc
new file mode 100644
index 0000000000..4f4c6f6dd8
--- /dev/null
+++ b/extensions-jvm/console/runtime/src/main/doc/usage.adoc
@@ -0,0 +1,40 @@
+=== Developer console endpoint
+
+To access the developer console, you must first enable it by adding 
configuration to `application.properties`.
+
+[source,properties]
+----
+quarkus.camel.console.enabled=true
+----
+
+Alternatively you can use a `camel-main` configuration option.
+
+[source,properties]
+----
+camel.main.dev-console-enabled=true
+----
+
+The console is then available at the following URL.
+
+[source,text]
+----
+http://localhost:8080/q/camel/dev-console
+----
+
+You can then call a console by its id, such as `routes`:
+
+[source,text]
+----
+http://localhost:8080/q/camel/dev-console/routes
+----
+
+=== Exposing the developer console in prod mode
+
+By default, the console is only exposed in dev and test modes. To expose the 
console in prod mode, add the following configuration to 
`application.properties`.
+
+[source,properties]
+----
+quarkus.camel.console.exposure-mode=ALL
+----
+
+See the configuration overview below for further details about `exposure-mode`.
diff --git 
a/extensions-jvm/console/runtime/src/main/java/org/apache/camel/quarkus/component/console/CamelConsoleConfig.java
 
b/extensions-jvm/console/runtime/src/main/java/org/apache/camel/quarkus/component/console/CamelConsoleConfig.java
new file mode 100644
index 0000000000..89e3234805
--- /dev/null
+++ 
b/extensions-jvm/console/runtime/src/main/java/org/apache/camel/quarkus/component/console/CamelConsoleConfig.java
@@ -0,0 +1,53 @@
+/*
+ * 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.quarkus.component.console;
+
+import io.quarkus.runtime.annotations.ConfigPhase;
+import io.quarkus.runtime.annotations.ConfigRoot;
+import io.smallrye.config.ConfigMapping;
+import io.smallrye.config.WithDefault;
+
+@ConfigRoot(phase = ConfigPhase.BUILD_AND_RUN_TIME_FIXED)
+@ConfigMapping(prefix = "quarkus.camel.console")
+public interface CamelConsoleConfig {
+    /**
+     * Whether the Camel developer console is enabled.
+     */
+    @WithDefault("false")
+    boolean enabled();
+
+    /**
+     * The context path under which the Camel developer console is deployed 
(default /q/camel/dev-console).
+     */
+    @WithDefault("camel/dev-console")
+    String path();
+
+    /**
+     * The modes in which the Camel developer console is available. The 
default {@code dev-test} enables the developer
+     * console only in dev mode and test modes.
+     * A value of {@code all} enables agent discovery in dev, test and prod 
modes. Setting the value to {@code none} will
+     * not expose the developer console HTTP endpoint.
+     */
+    @WithDefault("DEV_TEST")
+    ExposureMode exposureMode();
+
+    enum ExposureMode {
+        ALL,
+        DEV_TEST,
+        NONE,
+    }
+}
diff --git 
a/extensions-jvm/console/runtime/src/main/java/org/apache/camel/quarkus/component/console/CamelConsoleRecorder.java
 
b/extensions-jvm/console/runtime/src/main/java/org/apache/camel/quarkus/component/console/CamelConsoleRecorder.java
new file mode 100644
index 0000000000..f88ff78be7
--- /dev/null
+++ 
b/extensions-jvm/console/runtime/src/main/java/org/apache/camel/quarkus/component/console/CamelConsoleRecorder.java
@@ -0,0 +1,113 @@
+/*
+ * 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.quarkus.component.console;
+
+import java.util.Map;
+import java.util.function.Consumer;
+
+import io.quarkus.runtime.RuntimeValue;
+import io.quarkus.runtime.annotations.Recorder;
+import io.vertx.core.Handler;
+import io.vertx.core.http.HttpServerResponse;
+import io.vertx.core.json.JsonObject;
+import io.vertx.ext.web.Route;
+import io.vertx.ext.web.RoutingContext;
+import org.apache.camel.CamelContext;
+import org.apache.camel.Exchange;
+import org.apache.camel.console.DevConsole;
+import org.apache.camel.console.DevConsoleRegistry;
+import org.apache.camel.util.ObjectHelper;
+
+@Recorder
+public class CamelConsoleRecorder {
+    public Consumer<Route> route() {
+        return new Consumer<Route>() {
+            @Override
+            public void accept(Route route) {
+                route.produces("application/json");
+            }
+        };
+    }
+
+    public Handler<RoutingContext> getHandler(RuntimeValue<CamelContext> 
contextRuntimeValue) {
+        CamelContext context = contextRuntimeValue.getValue();
+        DevConsoleRegistry devConsoleRegistry = 
context.getCamelContextExtension().getContextPlugin(DevConsoleRegistry.class);
+        return new CamelConsoleHandler(devConsoleRegistry);
+    }
+
+    public void initDevConsoleRegistry(RuntimeValue<CamelContext> 
camelContextRuntimeValue) {
+        camelContextRuntimeValue.getValue()
+                .getCamelContextExtension()
+                .getContextPlugin(DevConsoleRegistry.class)
+                .loadDevConsoles();
+    }
+
+    static final class CamelConsoleHandler implements Handler<RoutingContext> {
+        private final DevConsoleRegistry devConsoleRegistry;
+
+        CamelConsoleHandler(DevConsoleRegistry devConsoleRegistry) {
+            this.devConsoleRegistry = devConsoleRegistry;
+        }
+
+        @Override
+        public void handle(RoutingContext context) {
+            if (devConsoleRegistry != null && devConsoleRegistry.isEnabled()) {
+                String id = context.pathParam("id");
+                if (ObjectHelper.isNotEmpty(id)) {
+                    getConsoleById(context, id);
+                } else {
+                    getConsoles(context);
+                }
+            }
+        }
+
+        void getConsoles(RoutingContext context) {
+            JsonObject root = new JsonObject();
+            devConsoleRegistry.stream().forEach(devConsole -> {
+                JsonObject console = new JsonObject();
+                console.put("id", devConsole.getId());
+                console.put("displayName", devConsole.getDisplayName());
+                console.put("description", devConsole.getDescription());
+                root.put(devConsole.getId(), console);
+            });
+            writeResponse(context.response(), root);
+        }
+
+        void getConsoleById(RoutingContext context, String id) {
+            JsonObject root = new JsonObject();
+            devConsoleRegistry.stream().sorted((o1, o2) -> {
+                int p1 = id.indexOf(o1.getId());
+                int p2 = id.indexOf(o2.getId());
+                return Integer.compare(p1, p2);
+            }).forEach(devConsole -> {
+                boolean include = "all".equals(id) || 
id.contains(devConsole.getId());
+                if (include && 
devConsole.supportMediaType(DevConsole.MediaType.JSON)) {
+                    Object result = devConsole.call(DevConsole.MediaType.JSON, 
Map.of(Exchange.HTTP_PATH, id));
+                    if (result != null) {
+                        root.put(devConsole.getId(), result);
+                    }
+                }
+            });
+            writeResponse(context.response(), root);
+        }
+
+        void writeResponse(HttpServerResponse response, JsonObject jsonObject) 
{
+            response.putHeader("Content-Type", "application/json");
+            response.end(jsonObject.encode());
+        }
+    }
+}
diff --git 
a/integration-tests-jvm/console/src/main/resources/application.properties 
b/integration-tests-jvm/console/src/main/resources/application.properties
new file mode 100644
index 0000000000..c467323045
--- /dev/null
+++ b/integration-tests-jvm/console/src/main/resources/application.properties
@@ -0,0 +1,17 @@
+## ---------------------------------------------------------------------------
+## 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.camel.console.enabled=true
\ No newline at end of file
diff --git 
a/integration-tests-jvm/console/src/test/java/org/apache/camel/quarkus/component/console/it/ConsoleTest.java
 
b/integration-tests-jvm/console/src/test/java/org/apache/camel/quarkus/component/console/it/ConsoleTest.java
index f06127b8a6..e277a3d40d 100644
--- 
a/integration-tests-jvm/console/src/test/java/org/apache/camel/quarkus/component/console/it/ConsoleTest.java
+++ 
b/integration-tests-jvm/console/src/test/java/org/apache/camel/quarkus/component/console/it/ConsoleTest.java
@@ -16,11 +16,18 @@
  */
 package org.apache.camel.quarkus.component.console.it;
 
+import java.util.Map;
+
 import io.quarkus.test.junit.QuarkusTest;
 import io.restassured.RestAssured;
+import io.restassured.path.json.JsonPath;
+import org.apache.camel.ServiceStatus;
 import org.junit.jupiter.api.Test;
 
+import static org.hamcrest.Matchers.anEmptyMap;
 import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 @QuarkusTest
 class ConsoleTest {
@@ -31,4 +38,38 @@ class ConsoleTest {
                 .statusCode(200)
                 .body(is("context"));
     }
+
+    @Test
+    void listConsoles() {
+        JsonPath response = RestAssured.get("/q/camel/dev-console")
+                .then()
+                .statusCode(200)
+                .extract()
+                .response()
+                .jsonPath();
+
+        Map<Object, Object> consoles = response.getMap("$");
+        assertFalse(consoles.isEmpty());
+        assertTrue(consoles.containsKey("bean"));
+        assertTrue(consoles.containsKey("blocked"));
+        assertTrue(consoles.containsKey("browse"));
+        assertTrue(consoles.containsKey("context"));
+    }
+
+    @Test
+    void invokeConsole() {
+        RestAssured.get("/q/camel/dev-console/context")
+                .then()
+                .statusCode(200)
+                .body("context.name", is("camel-1"),
+                        "context.state", is(ServiceStatus.Started.name()));
+    }
+
+    @Test
+    void invokeInvalidConsole() {
+        RestAssured.get("/q/camel/dev-console/foo")
+                .then()
+                .statusCode(200)
+                .body("", anEmptyMap());
+    }
 }

Reply via email to