This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new 7d1e0b8f1422 CAMEL-18552: Add camel-jbang --openapi-ui with Swagger UI
at /q/openapi
7d1e0b8f1422 is described below
commit 7d1e0b8f142241cb8023cb0a1058ee6b29ca8cba
Author: Omar Atie <[email protected]>
AuthorDate: Mon Aug 3 03:04:48 2026 -0700
CAMEL-18552: Add camel-jbang --openapi-ui with Swagger UI at /q/openapi
Expose bundled Swagger UI on the embedded management server and wire
the JBang --openapi-ui flag to enable REST OpenAPI at /q/openapi.json.
Closes #25202
Co-authored-by: Cursor <[email protected]>
---
.../org/apache/camel/catalog/docs/main.adoc | 4 +-
.../jbang/camel-jbang-configuration-metadata.json | 1 +
.../main/camel-main-configuration-metadata.json | 2 +
components/camel-platform-http-main/pom.xml | 12 ++
.../http/main/DefaultMainHttpServerFactory.java | 2 +
.../platform/http/main/ManagementHttpServer.java | 29 +++++
.../platform/http/main/OpenApiUiSupport.java | 144 +++++++++++++++++++++
.../http/main/OpenApiUiHttpServerTest.java | 95 ++++++++++++++
.../platform/http/main/OpenApiUiRestMainTest.java | 96 ++++++++++++++
.../platform/http/main/OpenApiUiSupportTest.java | 53 ++++++++
...entServerConfigurationPropertiesConfigurer.java | 14 ++
.../camel-main-configuration-metadata.json | 2 +
core/camel-main/src/main/docs/main.adoc | 4 +-
...ttpManagementServerConfigurationProperties.java | 45 +++++++
.../ROOT/pages/camel-4x-upgrade-guide-4_22.adoc | 8 ++
.../ROOT/pages/camel-jbang-configuration.adoc | 3 +-
.../modules/ROOT/pages/camel-jbang-managing.adoc | 17 +++
.../pages/jbang-commands/camel-jbang-debug.adoc | 1 +
.../ROOT/pages/jbang-commands/camel-jbang-dev.adoc | 1 +
.../ROOT/pages/jbang-commands/camel-jbang-run.adoc | 1 +
.../META-INF/camel-jbang-commands-metadata.json | 6 +-
.../camel-jbang-configuration-metadata.json | 1 +
.../apache/camel/dsl/jbang/core/commands/Run.java | 39 ++++++
.../dsl/jbang/core/common/CamelJBangConstants.java | 4 +
.../core/commands/RunOpenApiUiOptionsTest.java | 74 +++++++++++
.../java/org/apache/camel/main/KameletMain.java | 28 ++++
parent/pom.xml | 1 +
.../maven/packaging/PrepareCamelJBangMojo.java | 5 +
28 files changed, 686 insertions(+), 6 deletions(-)
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/main.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/main.adoc
index 71ff06d3aee5..352d5a8e8fd5 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/main.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/main.adoc
@@ -225,7 +225,7 @@ The camel.server supports 21 options, which are listed
below.
=== Camel Embedded HTTP management Server (only for standalone; not Spring
Boot or Quarkus) configurations
-The camel.management supports 27 options, which are listed below.
+The camel.management supports 29 options, which are listed below.
[width="100%",cols="2,5,^1,2",options="header"]
|===
@@ -251,6 +251,8 @@ The camel.management supports 27 options, which are listed
below.
| *camel.management.jwtKeystorePath* | Path to the keystore file used for JWT
tokens validation. | | String
| *camel.management.jwtKeystoreType* | Type of the keystore used for JWT
tokens validation (jks, pkcs12, etc.). | | String
| *camel.management.metricsEnabled* | Whether to enable metrics. If enabled
then you can access metrics on context-path: /observe/metrics | false | boolean
+| *camel.management.openapiUiEnabled* | Whether to enable Swagger UI for REST
DSL OpenAPI documents. If enabled then you can browse the API at context-path:
/q/openapi. The OpenAPI document is expected at openapiUiSpecPath (by default
/q/openapi.json from REST DSL apiContextPath ). | false | boolean
+| *camel.management.openapiUiSpecPath* | Path or URL of the OpenAPI document
loaded by Swagger UI at /q/openapi. | /q/openapi.json | String
| *camel.management.path* | Context-path to use for embedded HTTP server | / |
String
| *camel.management.port* | Port to use for binding embedded HTTP management
server. Use 0 to dynamic assign a free random port number. | 8080 | int
| *camel.management.sendEnabled* | Whether to enable sending messages to Camel
via HTTP. This makes it possible to use Camel to send messages to Camel
endpoint URIs via HTTP. | false | boolean
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/jbang/camel-jbang-configuration-metadata.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/jbang/camel-jbang-configuration-metadata.json
index 4f02be84a402..2009c64f7a0c 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/jbang/camel-jbang-configuration-metadata.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/jbang/camel-jbang-configuration-metadata.json
@@ -36,6 +36,7 @@
{ "name": "camel.jbang.mavenWrapper", "required": false, "description":
"Include Maven Wrapper files in the exported project", "type": "boolean",
"javaType": "boolean", "defaultValue": true, "secret": false },
{ "name": "camel.jbang.metrics", "required": false, "description":
"Metrics (Micrometer and Prometheus) at \/observe\/metrics on local HTTP server
(port 8080 by default) when running standalone Camel", "type": "boolean",
"javaType": "boolean", "defaultValue": false, "secret": false, "deprecated":
true },
{ "name": "camel.jbang.openApi", "required": false, "description": "File
name of open-api spec file (JSON or YAML) to generate routes from the
swagger\/openapi API spec file.", "type": "string", "javaType": "String",
"secret": false },
+ { "name": "camel.jbang.openapiUi", "required": false, "description":
"Swagger UI for REST OpenAPI at \/q\/openapi on local HTTP server (port 8080 by
default)", "type": "boolean", "javaType": "boolean", "defaultValue": false,
"secret": false, "security": "insecure:dev" },
{ "name": "camel.jbang.packageScanJars", "required": false, "description":
"Whether to automatic package scan JARs for custom Spring or Quarkus beans
making them available for Camel CLI", "label": "advanced", "type": "boolean",
"javaType": "boolean", "defaultValue": false, "secret": false },
{ "name": "camel.jbang.parentPom", "required": false, "description":
"Parent POM coordinate (groupId:artifactId:version) to use in the exported
project", "type": "string", "javaType": "String", "secret": false },
{ "name": "camel.jbang.prompt", "required": false, "description": "Allow
user to type in required parameters in prompt if not present in application",
"label": "advanced", "type": "boolean", "javaType": "boolean", "defaultValue":
false, "secret": false },
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/main/camel-main-configuration-metadata.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/main/camel-main-configuration-metadata.json
index feb0abb216fb..d16fb079e975 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/main/camel-main-configuration-metadata.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/main/camel-main-configuration-metadata.json
@@ -310,6 +310,8 @@
{ "name": "camel.management.jwtKeystorePath", "required": false,
"description": "Path to the keystore file used for JWT tokens validation.",
"sourceType":
"org.apache.camel.main.HttpManagementServerConfigurationProperties", "type":
"string", "javaType": "java.lang.String", "secret": false },
{ "name": "camel.management.jwtKeystoreType", "required": false,
"description": "Type of the keystore used for JWT tokens validation (jks,
pkcs12, etc.).", "sourceType":
"org.apache.camel.main.HttpManagementServerConfigurationProperties", "type":
"string", "javaType": "java.lang.String", "secret": false },
{ "name": "camel.management.metricsEnabled", "required": false,
"description": "Whether to enable metrics. If enabled then you can access
metrics on context-path: \/observe\/metrics", "sourceType":
"org.apache.camel.main.HttpManagementServerConfigurationProperties", "type":
"boolean", "javaType": "boolean", "defaultValue": false, "secret": false },
+ { "name": "camel.management.openapiUiEnabled", "required": false,
"description": "Whether to enable Swagger UI for REST DSL OpenAPI documents. If
enabled then you can browse the API at context-path: \/q\/openapi. The OpenAPI
document is expected at openapiUiSpecPath (by default \/q\/openapi.json from
REST DSL apiContextPath ).", "sourceType":
"org.apache.camel.main.HttpManagementServerConfigurationProperties", "type":
"boolean", "javaType": "boolean", "defaultValue": false, "secret": [...]
+ { "name": "camel.management.openapiUiSpecPath", "required": false,
"description": "Path or URL of the OpenAPI document loaded by Swagger UI at
\/q\/openapi.", "sourceType":
"org.apache.camel.main.HttpManagementServerConfigurationProperties", "type":
"string", "javaType": "java.lang.String", "defaultValue": "\/q\/openapi.json",
"secret": false },
{ "name": "camel.management.path", "required": false, "description":
"Context-path to use for embedded HTTP server", "sourceType":
"org.apache.camel.main.HttpManagementServerConfigurationProperties", "type":
"string", "javaType": "java.lang.String", "defaultValue": "\/", "secret": false
},
{ "name": "camel.management.port", "required": false, "description": "Port
to use for binding embedded HTTP management server. Use 0 to dynamic assign a
free random port number.", "sourceType":
"org.apache.camel.main.HttpManagementServerConfigurationProperties", "type":
"integer", "javaType": "int", "defaultValue": 8080, "secret": false },
{ "name": "camel.management.sendEnabled", "required": false,
"description": "Whether to enable sending messages to Camel via HTTP. This
makes it possible to use Camel to send messages to Camel endpoint URIs via
HTTP.", "sourceType":
"org.apache.camel.main.HttpManagementServerConfigurationProperties", "type":
"boolean", "javaType": "boolean", "defaultValue": false, "secret": false,
"security": "insecure:dev" },
diff --git a/components/camel-platform-http-main/pom.xml
b/components/camel-platform-http-main/pom.xml
index 9b4cd83e1dbe..80f9088f1288 100644
--- a/components/camel-platform-http-main/pom.xml
+++ b/components/camel-platform-http-main/pom.xml
@@ -68,6 +68,13 @@
<version>${vertx-version}</version>
</dependency>
+ <dependency>
+ <groupId>org.webjars</groupId>
+ <artifactId>swagger-ui</artifactId>
+ <version>${swagger-ui-version}</version>
+ <optional>true</optional>
+ </dependency>
+
<!-- test dependencies -->
<dependency>
<groupId>org.apache.camel</groupId>
@@ -84,6 +91,11 @@
<artifactId>camel-console</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-openapi-java</artifactId>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
diff --git
a/components/camel-platform-http-main/src/main/java/org/apache/camel/component/platform/http/main/DefaultMainHttpServerFactory.java
b/components/camel-platform-http-main/src/main/java/org/apache/camel/component/platform/http/main/DefaultMainHttpServerFactory.java
index 40446c3c39e8..c5fb8e2f4835 100644
---
a/components/camel-platform-http-main/src/main/java/org/apache/camel/component/platform/http/main/DefaultMainHttpServerFactory.java
+++
b/components/camel-platform-http-main/src/main/java/org/apache/camel/component/platform/http/main/DefaultMainHttpServerFactory.java
@@ -105,6 +105,8 @@ public class DefaultMainHttpServerFactory implements
CamelContextAware, MainHttp
server.setUploadSourceDir(configuration.getUploadSourceDir());
server.setDownloadEnabled(configuration.isDownloadEnabled());
server.setSendEnabled(configuration.isSendEnabled());
+ server.setOpenapiUiEnabled(configuration.isOpenapiUiEnabled());
+ server.setOpenapiUiSpecPath(configuration.getOpenapiUiSpecPath());
if (configuration.isAuthenticationEnabled()) {
configureAuthentication(server, configuration);
diff --git
a/components/camel-platform-http-main/src/main/java/org/apache/camel/component/platform/http/main/ManagementHttpServer.java
b/components/camel-platform-http-main/src/main/java/org/apache/camel/component/platform/http/main/ManagementHttpServer.java
index 6d9dfe93629c..b04e9b4fa80b 100644
---
a/components/camel-platform-http-main/src/main/java/org/apache/camel/component/platform/http/main/ManagementHttpServer.java
+++
b/components/camel-platform-http-main/src/main/java/org/apache/camel/component/platform/http/main/ManagementHttpServer.java
@@ -129,6 +129,8 @@ public class ManagementHttpServer extends ServiceSupport
implements CamelContext
private String uploadSourceDir;
private boolean downloadEnabled;
private boolean sendEnabled;
+ private boolean openapiUiEnabled;
+ private String openapiUiSpecPath = OpenApiUiSupport.DEFAULT_SPEC_PATH;
@Override
public CamelContext getCamelContext() {
@@ -358,6 +360,30 @@ public class ManagementHttpServer extends ServiceSupport
implements CamelContext
this.sendEnabled = sendEnabled;
}
+ @ManagedAttribute(description = "Whether OpenAPI Swagger UI is enabled
(q/openapi)")
+ public boolean isOpenapiUiEnabled() {
+ return openapiUiEnabled;
+ }
+
+ /**
+ * Whether Swagger UI is enabled at {@link
OpenApiUiSupport#OPENAPI_UI_PATH}.
+ */
+ public void setOpenapiUiEnabled(boolean openapiUiEnabled) {
+ this.openapiUiEnabled = openapiUiEnabled;
+ }
+
+ @ManagedAttribute(description = "OpenAPI document URL used by Swagger UI")
+ public String getOpenapiUiSpecPath() {
+ return openapiUiSpecPath;
+ }
+
+ /**
+ * Path or URL of the OpenAPI document loaded by Swagger UI (default
{@value OpenApiUiSupport#DEFAULT_SPEC_PATH}).
+ */
+ public void setOpenapiUiSpecPath(String openapiUiSpecPath) {
+ this.openapiUiSpecPath = openapiUiSpecPath;
+ }
+
public VertxPlatformHttpServerConfiguration.Cors getCors() {
return configuration.getCors();
}
@@ -457,6 +483,9 @@ public class ManagementHttpServer extends ServiceSupport
implements CamelContext
if (sendEnabled) {
setupSendConsole();
}
+ if (openapiUiEnabled) {
+ OpenApiUiSupport.setup(router, platformHttpComponent,
openapiUiSpecPath);
+ }
// metrics will be setup in camel-micrometer-prometheus
}
diff --git
a/components/camel-platform-http-main/src/main/java/org/apache/camel/component/platform/http/main/OpenApiUiSupport.java
b/components/camel-platform-http-main/src/main/java/org/apache/camel/component/platform/http/main/OpenApiUiSupport.java
new file mode 100644
index 000000000000..0a7c9d8a7d44
--- /dev/null
+++
b/components/camel-platform-http-main/src/main/java/org/apache/camel/component/platform/http/main/OpenApiUiSupport.java
@@ -0,0 +1,144 @@
+/*
+ * 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.component.platform.http.main;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+
+import io.vertx.core.http.HttpMethod;
+import io.vertx.ext.web.Route;
+import io.vertx.ext.web.handler.StaticHandler;
+import org.apache.camel.component.platform.http.PlatformHttpComponent;
+import org.apache.camel.component.platform.http.vertx.VertxPlatformHttpRouter;
+import org.apache.camel.util.ObjectHelper;
+
+/**
+ * Serves Swagger UI at {@value #OPENAPI_UI_PATH} for exploring REST DSL
OpenAPI documents.
+ */
+final class OpenApiUiSupport {
+
+ static final String OPENAPI_UI_PATH = "/q/openapi";
+ static final String DEFAULT_SPEC_PATH = "/q/openapi.json";
+ private static final String SWAGGER_UI_WEBJAR_VERSION =
resolveSwaggerUiWebjarVersion();
+
+ private OpenApiUiSupport() {
+ }
+
+ static void setup(VertxPlatformHttpRouter router, PlatformHttpComponent
platformHttpComponent, String specPath) {
+ ObjectHelper.notNull(router, "router");
+ ObjectHelper.notNull(platformHttpComponent, "platformHttpComponent");
+ String spec = normalizeSpecPath(specPath);
+
+ String webjarsRoot = "META-INF/resources/webjars/swagger-ui/" +
SWAGGER_UI_WEBJAR_VERSION;
+ StaticHandler assets =
StaticHandler.create(webjarsRoot).setCachingEnabled(true);
+
+ Route assetsRoute = router.route(OPENAPI_UI_PATH + "/webjars/*");
+ assetsRoute.method(HttpMethod.GET);
+ assetsRoute.handler(assets);
+
+ String html = buildIndexHtml(spec);
+
+ Route ui = router.route(OPENAPI_UI_PATH);
+ ui.method(HttpMethod.GET);
+ ui.handler(ctx -> {
+ ctx.response().putHeader("content-type",
"text/html;charset=utf-8");
+ ctx.end(html);
+ });
+
+ Route uiSlash = router.route(OPENAPI_UI_PATH + "/");
+ uiSlash.method(HttpMethod.GET);
+ uiSlash.handler(ctx -> {
+ ctx.response().putHeader("content-type",
"text/html;charset=utf-8");
+ ctx.end(html);
+ });
+
+ platformHttpComponent.addHttpManagementEndpoint(OPENAPI_UI_PATH,
"GET", null, "text/html", null);
+ }
+
+ private static String buildIndexHtml(String specPath) {
+ String prefix = OPENAPI_UI_PATH + "/webjars";
+ String escapedSpecPath = escapeForJavaScriptString(specPath);
+ return """
+ <!DOCTYPE html>
+ <html lang="en">
+ <head>
+ <meta charset="UTF-8"/>
+ <title>Camel OpenAPI</title>
+ <link rel="stylesheet" href="%s/swagger-ui.css"/>
+ </head>
+ <body>
+ <div id="swagger-ui"></div>
+ <script src="%s/swagger-ui-bundle.js"></script>
+ <script src="%s/swagger-ui-standalone-preset.js"></script>
+ <script>
+ window.onload = function () {
+ window.ui = SwaggerUIBundle({
+ url: "%s",
+ dom_id: '#swagger-ui',
+ deepLinking: true,
+ presets: [SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset],
+ layout: "StandaloneLayout"
+ });
+ };
+ </script>
+ </body>
+ </html>
+ """.formatted(prefix, prefix, prefix, escapedSpecPath);
+ }
+
+ static String normalizeSpecPath(String specPath) {
+ String spec = ObjectHelper.isNotEmpty(specPath) ? specPath :
DEFAULT_SPEC_PATH;
+ if (spec.startsWith("http://") || spec.startsWith("https://")) {
+ return spec;
+ }
+ if (!spec.startsWith("/")) {
+ spec = "/" + spec;
+ }
+ return spec;
+ }
+
+ static String escapeForJavaScriptString(String value) {
+ if (value == null) {
+ return "";
+ }
+ return value.replace("\\", "\\\\")
+ .replace("\"", "\\\"")
+ .replace("'", "\\'")
+ .replace("<", "\\u003c")
+ .replace(">", "\\u003e")
+ .replace("\n", "\\n")
+ .replace("\r", "\\r");
+ }
+
+ static String resolveSwaggerUiWebjarVersion() {
+ try (InputStream is = OpenApiUiSupport.class.getResourceAsStream(
+ "/META-INF/maven/org.webjars/swagger-ui/pom.properties")) {
+ if (is != null) {
+ Properties properties = new Properties();
+ properties.load(is);
+ String version = properties.getProperty("version");
+ if (version != null && !version.isBlank()) {
+ return version;
+ }
+ }
+ } catch (IOException e) {
+ // fall through
+ }
+ throw new IllegalStateException("Unable to resolve swagger-ui webjar
version from classpath");
+ }
+}
diff --git
a/components/camel-platform-http-main/src/test/java/org/apache/camel/component/platform/http/main/OpenApiUiHttpServerTest.java
b/components/camel-platform-http-main/src/test/java/org/apache/camel/component/platform/http/main/OpenApiUiHttpServerTest.java
new file mode 100644
index 000000000000..8f177bd63a01
--- /dev/null
+++
b/components/camel-platform-http-main/src/test/java/org/apache/camel/component/platform/http/main/OpenApiUiHttpServerTest.java
@@ -0,0 +1,95 @@
+/*
+ * 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.component.platform.http.main;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.test.AvailablePortFinder;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class OpenApiUiHttpServerTest {
+
+ @RegisterExtension
+ AvailablePortFinder.Port port = AvailablePortFinder.find();
+
+ private CamelContext camelContext;
+ private ManagementHttpServer server;
+
+ @BeforeEach
+ void setUp() throws Exception {
+ camelContext = new DefaultCamelContext();
+ camelContext.start();
+
+ server = new ManagementHttpServer();
+ server.setCamelContext(camelContext);
+ server.setHost("0.0.0.0");
+ server.setPort(port.getPort());
+ server.setPath("/");
+ server.setOpenapiUiEnabled(true);
+ server.start();
+ }
+
+ @AfterEach
+ void tearDown() throws Exception {
+ if (server != null) {
+ server.stop();
+ }
+ if (camelContext != null) {
+ camelContext.stop();
+ }
+ }
+
+ @Test
+ void openApiUiPageContainsSwaggerBundle() throws IOException,
InterruptedException {
+ HttpRequest request = HttpRequest.newBuilder()
+ .uri(URI.create("http://localhost:" + port.getPort() +
OpenApiUiSupport.OPENAPI_UI_PATH))
+ .GET()
+ .build();
+
+ HttpResponse<String> response =
HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
+
+ assertThat(response.statusCode()).isEqualTo(200);
+
assertThat(response.headers().firstValue("content-type").orElse("")).contains("text/html");
+ assertThat(response.body()).contains("SwaggerUIBundle");
+
assertThat(response.body()).contains(OpenApiUiSupport.DEFAULT_SPEC_PATH);
+ }
+
+ @Test
+ void openApiUiWebjarServesSwaggerCss() throws IOException,
InterruptedException {
+ HttpRequest request = HttpRequest.newBuilder()
+ .uri(URI.create("http://localhost:" + port.getPort() +
OpenApiUiSupport.OPENAPI_UI_PATH
+ + "/webjars/swagger-ui.css"))
+ .GET()
+ .build();
+
+ HttpResponse<String> response =
HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
+
+ assertThat(response.statusCode()).isEqualTo(200);
+
assertThat(response.headers().firstValue("content-type").orElse("")).contains("text/css");
+ }
+}
diff --git
a/components/camel-platform-http-main/src/test/java/org/apache/camel/component/platform/http/main/OpenApiUiRestMainTest.java
b/components/camel-platform-http-main/src/test/java/org/apache/camel/component/platform/http/main/OpenApiUiRestMainTest.java
new file mode 100644
index 000000000000..5265f9836de4
--- /dev/null
+++
b/components/camel-platform-http-main/src/test/java/org/apache/camel/component/platform/http/main/OpenApiUiRestMainTest.java
@@ -0,0 +1,96 @@
+/*
+ * 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.component.platform.http.main;
+
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.util.Properties;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.main.Main;
+import org.apache.camel.test.AvailablePortFinder;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class OpenApiUiRestMainTest {
+
+ @RegisterExtension
+ AvailablePortFinder.Port port = AvailablePortFinder.find();
+
+ private Main main;
+
+ @BeforeEach
+ void setUp() {
+ Properties properties = new Properties();
+ properties.setProperty("camel.server.enabled", "true");
+ properties.setProperty("camel.server.port",
Integer.toString(port.getPort()));
+ properties.setProperty("camel.management.enabled", "true");
+ properties.setProperty("camel.management.port",
Integer.toString(port.getPort()));
+ properties.setProperty("camel.management.openapiUiEnabled", "true");
+ properties.setProperty("camel.rest.component", "platform-http");
+ properties.setProperty("camel.rest.apiContextPath",
OpenApiUiSupport.DEFAULT_SPEC_PATH);
+
+ main = new Main();
+ main.setOverrideProperties(properties);
+ main.configure().addRoutesBuilder(new RouteBuilder() {
+ @Override
+ public void configure() {
+ restConfiguration().apiProperty("api.title", "Sample
API").apiProperty("api.version", "1.0.0");
+ rest("/demo").get("/ping").to("direct:ping");
+ from("direct:ping").setBody(constant("pong"));
+ }
+ });
+ }
+
+ @AfterEach
+ void tearDown() throws Exception {
+ if (main != null) {
+ main.stop();
+ }
+ }
+
+ @Test
+ void restOpenApiDocumentAndUiAreAvailable() throws Exception {
+ main.start();
+
+ HttpClient client = HttpClient.newHttpClient();
+
+ HttpResponse<String> spec = client.send(HttpRequest.newBuilder()
+ .uri(URI.create("http://localhost:" + port.getPort() +
OpenApiUiSupport.DEFAULT_SPEC_PATH))
+ .header("Accept", "application/json")
+ .GET()
+ .build(), HttpResponse.BodyHandlers.ofString());
+
+ assertThat(spec.statusCode()).isEqualTo(200);
+ assertThat(spec.body()).contains("\"openapi\"");
+ assertThat(spec.body()).contains("/demo/ping");
+
+ HttpResponse<String> ui = client.send(HttpRequest.newBuilder()
+ .uri(URI.create("http://localhost:" + port.getPort() +
OpenApiUiSupport.OPENAPI_UI_PATH))
+ .GET()
+ .build(), HttpResponse.BodyHandlers.ofString());
+
+ assertThat(ui.statusCode()).isEqualTo(200);
+ assertThat(ui.body()).contains("SwaggerUIBundle");
+ }
+}
diff --git
a/components/camel-platform-http-main/src/test/java/org/apache/camel/component/platform/http/main/OpenApiUiSupportTest.java
b/components/camel-platform-http-main/src/test/java/org/apache/camel/component/platform/http/main/OpenApiUiSupportTest.java
new file mode 100644
index 000000000000..8079b464fc97
--- /dev/null
+++
b/components/camel-platform-http-main/src/test/java/org/apache/camel/component/platform/http/main/OpenApiUiSupportTest.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.component.platform.http.main;
+
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class OpenApiUiSupportTest {
+
+ @Test
+ void normalizeSpecPathDefaultsToOpenApiJson() {
+
assertThat(OpenApiUiSupport.normalizeSpecPath(null)).isEqualTo(OpenApiUiSupport.DEFAULT_SPEC_PATH);
+
assertThat(OpenApiUiSupport.normalizeSpecPath("")).isEqualTo(OpenApiUiSupport.DEFAULT_SPEC_PATH);
+ }
+
+ @Test
+ void normalizeSpecPathAddsLeadingSlashForRelativePaths() {
+
assertThat(OpenApiUiSupport.normalizeSpecPath("api-docs.json")).isEqualTo("/api-docs.json");
+ }
+
+ @Test
+ void normalizeSpecPathPreservesAbsoluteHttpUrls() {
+ String url = "http://localhost:9090/q/openapi.json";
+ assertThat(OpenApiUiSupport.normalizeSpecPath(url)).isEqualTo(url);
+ }
+
+ @Test
+ void escapeForJavaScriptStringEscapesQuotesAndScriptDelimiters() {
+
assertThat(OpenApiUiSupport.escapeForJavaScriptString("/q/openapi.json")).isEqualTo("/q/openapi.json");
+
assertThat(OpenApiUiSupport.escapeForJavaScriptString("http://host/path\"</script>"))
+ .isEqualTo("http://host/path\\\"\\u003c/script\\u003e");
+ }
+
+ @Test
+ void resolveSwaggerUiWebjarVersionMatchesClasspathArtifact() {
+
assertThat(OpenApiUiSupport.resolveSwaggerUiWebjarVersion()).matches("\\d+\\.\\d+\\.\\d+");
+ }
+}
diff --git
a/core/camel-main/src/generated/java/org/apache/camel/main/HttpManagementServerConfigurationPropertiesConfigurer.java
b/core/camel-main/src/generated/java/org/apache/camel/main/HttpManagementServerConfigurationPropertiesConfigurer.java
index 701f3a5f97f3..22c4403b0c29 100644
---
a/core/camel-main/src/generated/java/org/apache/camel/main/HttpManagementServerConfigurationPropertiesConfigurer.java
+++
b/core/camel-main/src/generated/java/org/apache/camel/main/HttpManagementServerConfigurationPropertiesConfigurer.java
@@ -43,6 +43,8 @@ public class
HttpManagementServerConfigurationPropertiesConfigurer extends org.a
map.put("JwtKeystorePath", java.lang.String.class);
map.put("JwtKeystoreType", java.lang.String.class);
map.put("MetricsEnabled", boolean.class);
+ map.put("OpenapiUiEnabled", boolean.class);
+ map.put("OpenapiUiSpecPath", java.lang.String.class);
map.put("Path", java.lang.String.class);
map.put("Port", int.class);
map.put("SendEnabled", boolean.class);
@@ -96,6 +98,10 @@ public class
HttpManagementServerConfigurationPropertiesConfigurer extends org.a
case "jwtKeystoreType":
target.setJwtKeystoreType(property(camelContext, java.lang.String.class,
value)); return true;
case "metricsenabled":
case "metricsEnabled": target.setMetricsEnabled(property(camelContext,
boolean.class, value)); return true;
+ case "openapiuienabled":
+ case "openapiUiEnabled":
target.setOpenapiUiEnabled(property(camelContext, boolean.class, value));
return true;
+ case "openapiuispecpath":
+ case "openapiUiSpecPath":
target.setOpenapiUiSpecPath(property(camelContext, java.lang.String.class,
value)); return true;
case "path": target.setPath(property(camelContext,
java.lang.String.class, value)); return true;
case "port": target.setPort(property(camelContext, int.class, value));
return true;
case "sendenabled":
@@ -158,6 +164,10 @@ public class
HttpManagementServerConfigurationPropertiesConfigurer extends org.a
case "jwtKeystoreType": return java.lang.String.class;
case "metricsenabled":
case "metricsEnabled": return boolean.class;
+ case "openapiuienabled":
+ case "openapiUiEnabled": return boolean.class;
+ case "openapiuispecpath":
+ case "openapiUiSpecPath": return java.lang.String.class;
case "path": return java.lang.String.class;
case "port": return int.class;
case "sendenabled":
@@ -216,6 +226,10 @@ public class
HttpManagementServerConfigurationPropertiesConfigurer extends org.a
case "jwtKeystoreType": return target.getJwtKeystoreType();
case "metricsenabled":
case "metricsEnabled": return target.isMetricsEnabled();
+ case "openapiuienabled":
+ case "openapiUiEnabled": return target.isOpenapiUiEnabled();
+ case "openapiuispecpath":
+ case "openapiUiSpecPath": return target.getOpenapiUiSpecPath();
case "path": return target.getPath();
case "port": return target.getPort();
case "sendenabled":
diff --git
a/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json
b/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json
index feb0abb216fb..d16fb079e975 100644
---
a/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json
+++
b/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json
@@ -310,6 +310,8 @@
{ "name": "camel.management.jwtKeystorePath", "required": false,
"description": "Path to the keystore file used for JWT tokens validation.",
"sourceType":
"org.apache.camel.main.HttpManagementServerConfigurationProperties", "type":
"string", "javaType": "java.lang.String", "secret": false },
{ "name": "camel.management.jwtKeystoreType", "required": false,
"description": "Type of the keystore used for JWT tokens validation (jks,
pkcs12, etc.).", "sourceType":
"org.apache.camel.main.HttpManagementServerConfigurationProperties", "type":
"string", "javaType": "java.lang.String", "secret": false },
{ "name": "camel.management.metricsEnabled", "required": false,
"description": "Whether to enable metrics. If enabled then you can access
metrics on context-path: \/observe\/metrics", "sourceType":
"org.apache.camel.main.HttpManagementServerConfigurationProperties", "type":
"boolean", "javaType": "boolean", "defaultValue": false, "secret": false },
+ { "name": "camel.management.openapiUiEnabled", "required": false,
"description": "Whether to enable Swagger UI for REST DSL OpenAPI documents. If
enabled then you can browse the API at context-path: \/q\/openapi. The OpenAPI
document is expected at openapiUiSpecPath (by default \/q\/openapi.json from
REST DSL apiContextPath ).", "sourceType":
"org.apache.camel.main.HttpManagementServerConfigurationProperties", "type":
"boolean", "javaType": "boolean", "defaultValue": false, "secret": [...]
+ { "name": "camel.management.openapiUiSpecPath", "required": false,
"description": "Path or URL of the OpenAPI document loaded by Swagger UI at
\/q\/openapi.", "sourceType":
"org.apache.camel.main.HttpManagementServerConfigurationProperties", "type":
"string", "javaType": "java.lang.String", "defaultValue": "\/q\/openapi.json",
"secret": false },
{ "name": "camel.management.path", "required": false, "description":
"Context-path to use for embedded HTTP server", "sourceType":
"org.apache.camel.main.HttpManagementServerConfigurationProperties", "type":
"string", "javaType": "java.lang.String", "defaultValue": "\/", "secret": false
},
{ "name": "camel.management.port", "required": false, "description": "Port
to use for binding embedded HTTP management server. Use 0 to dynamic assign a
free random port number.", "sourceType":
"org.apache.camel.main.HttpManagementServerConfigurationProperties", "type":
"integer", "javaType": "int", "defaultValue": 8080, "secret": false },
{ "name": "camel.management.sendEnabled", "required": false,
"description": "Whether to enable sending messages to Camel via HTTP. This
makes it possible to use Camel to send messages to Camel endpoint URIs via
HTTP.", "sourceType":
"org.apache.camel.main.HttpManagementServerConfigurationProperties", "type":
"boolean", "javaType": "boolean", "defaultValue": false, "secret": false,
"security": "insecure:dev" },
diff --git a/core/camel-main/src/main/docs/main.adoc
b/core/camel-main/src/main/docs/main.adoc
index 71ff06d3aee5..352d5a8e8fd5 100644
--- a/core/camel-main/src/main/docs/main.adoc
+++ b/core/camel-main/src/main/docs/main.adoc
@@ -225,7 +225,7 @@ The camel.server supports 21 options, which are listed
below.
=== Camel Embedded HTTP management Server (only for standalone; not Spring
Boot or Quarkus) configurations
-The camel.management supports 27 options, which are listed below.
+The camel.management supports 29 options, which are listed below.
[width="100%",cols="2,5,^1,2",options="header"]
|===
@@ -251,6 +251,8 @@ The camel.management supports 27 options, which are listed
below.
| *camel.management.jwtKeystorePath* | Path to the keystore file used for JWT
tokens validation. | | String
| *camel.management.jwtKeystoreType* | Type of the keystore used for JWT
tokens validation (jks, pkcs12, etc.). | | String
| *camel.management.metricsEnabled* | Whether to enable metrics. If enabled
then you can access metrics on context-path: /observe/metrics | false | boolean
+| *camel.management.openapiUiEnabled* | Whether to enable Swagger UI for REST
DSL OpenAPI documents. If enabled then you can browse the API at context-path:
/q/openapi. The OpenAPI document is expected at openapiUiSpecPath (by default
/q/openapi.json from REST DSL apiContextPath ). | false | boolean
+| *camel.management.openapiUiSpecPath* | Path or URL of the OpenAPI document
loaded by Swagger UI at /q/openapi. | /q/openapi.json | String
| *camel.management.path* | Context-path to use for embedded HTTP server | / |
String
| *camel.management.port* | Port to use for binding embedded HTTP management
server. Use 0 to dynamic assign a free random port number. | 8080 | int
| *camel.management.sendEnabled* | Whether to enable sending messages to Camel
via HTTP. This makes it possible to use Camel to send messages to Camel
endpoint URIs via HTTP. | false | boolean
diff --git
a/core/camel-main/src/main/java/org/apache/camel/main/HttpManagementServerConfigurationProperties.java
b/core/camel-main/src/main/java/org/apache/camel/main/HttpManagementServerConfigurationProperties.java
index a705f287a334..405635887d8e 100644
---
a/core/camel-main/src/main/java/org/apache/camel/main/HttpManagementServerConfigurationProperties.java
+++
b/core/camel-main/src/main/java/org/apache/camel/main/HttpManagementServerConfigurationProperties.java
@@ -57,6 +57,11 @@ public class HttpManagementServerConfigurationProperties
implements BootstrapClo
@Metadata(security = "insecure:dev")
private boolean sendEnabled;
+ @Metadata(security = "insecure:dev")
+ private boolean openapiUiEnabled;
+ @Metadata(defaultValue = "/q/openapi.json")
+ private String openapiUiSpecPath = "/q/openapi.json";
+
@Metadata(label = "security")
private boolean useGlobalSslContextParameters;
@Metadata(label = "security")
@@ -290,6 +295,30 @@ public class HttpManagementServerConfigurationProperties
implements BootstrapClo
this.sendEnabled = sendEnabled;
}
+ public boolean isOpenapiUiEnabled() {
+ return openapiUiEnabled;
+ }
+
+ /**
+ * Whether to enable Swagger UI for REST DSL OpenAPI documents. If enabled
then you can browse the API at
+ * context-path: /q/openapi. The OpenAPI document is expected at {@link
#openapiUiSpecPath} (by default
+ * /q/openapi.json from REST DSL {@code apiContextPath}).
+ */
+ public void setOpenapiUiEnabled(boolean openapiUiEnabled) {
+ this.openapiUiEnabled = openapiUiEnabled;
+ }
+
+ public String getOpenapiUiSpecPath() {
+ return openapiUiSpecPath;
+ }
+
+ /**
+ * Path or URL of the OpenAPI document loaded by Swagger UI at /q/openapi.
+ */
+ public void setOpenapiUiSpecPath(String openapiUiSpecPath) {
+ this.openapiUiSpecPath = openapiUiSpecPath;
+ }
+
public boolean isAuthenticationEnabled() {
return authenticationEnabled;
}
@@ -528,6 +557,22 @@ public class HttpManagementServerConfigurationProperties
implements BootstrapClo
return this;
}
+ /**
+ * Whether to enable Swagger UI at /q/openapi (not intended for production
use).
+ */
+ public HttpManagementServerConfigurationProperties
withOpenapiUiEnabled(boolean openapiUiEnabled) {
+ this.openapiUiEnabled = openapiUiEnabled;
+ return this;
+ }
+
+ /**
+ * Path or URL of the OpenAPI document loaded by Swagger UI.
+ */
+ public HttpManagementServerConfigurationProperties
withOpenapiUiSpecPath(String openapiUiSpecPath) {
+ this.openapiUiSpecPath = openapiUiSpecPath;
+ return this;
+ }
+
/**
* Whether to enable HTTP authentication for embedded server (for
standalone applications; not Spring Boot or
* Quarkus).
diff --git
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc
index 79a5b665050d..0ab406ff49dd 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc
@@ -62,6 +62,14 @@ The Camel CLI and TUI AI prompt now support native **Google
Gemini** when `GEMIN
the `generativelanguage.googleapis.com` API with function-calling support.
See xref:camel-jbang-ai.adoc[Camel CLI - AI Tools] for the full detection
order.
+`camel run`, `camel dev`, and `camel debug` accept `--openapi-ui` to expose
Swagger UI for REST DSL OpenAPI
+at `/q/openapi` on the embedded HTTP server (OpenAPI JSON at `/q/openapi.json`
by default). The feature is
+gated as developer-only (`insecure:dev`) via `camel.jbang.openapiUi` and
`camel.management.openapiUiEnabled`.
+When `--port` is set without `--management-port`, the management server is
co-bound to the same port so the UI
+and spec stay together. `--openapi-ui` also sets override properties for
`camel.rest.component=platform-http`
+and `camel.rest.apiContextPath=/q/openapi.json`, which take precedence over
values in `application.properties`
+or other property sources (same as other CLI `--` server flags that use
override properties).
+
The `camel cmd route-diagram` and `camel cmd route-topology` now accept one or
more Camel route source files
(instead of only the name/pid of a running integration), so diagrams and
inter-route topology can be
generated at design/source time without starting the application first, for
example:
diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang-configuration.adoc
b/docs/user-manual/modules/ROOT/pages/camel-jbang-configuration.adoc
index 56301db3ccf3..16d59ff73148 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang-configuration.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang-configuration.adoc
@@ -4,7 +4,7 @@ This page covers configuring the Camel CLI — available options,
configuration
// jbang options: START
== Camel CLI configurations
-The camel.jbang supports 50 options, which are listed below.
+The camel.jbang supports 51 options, which are listed below.
[width="100%",cols="2,5,^1,2",options="header"]
|===
@@ -42,6 +42,7 @@ The camel.jbang supports 50 options, which are listed below.
| *camel.jbang.mavenWrapper* | Include Maven Wrapper files in the exported
project | true | boolean
| *camel.jbang.metrics* | Metrics (Micrometer and Prometheus) at
/observe/metrics on local HTTP server (port 8080 by default) when running
standalone Camel | false | boolean
| *camel.jbang.openApi* | File name of open-api spec file (JSON or YAML) to
generate routes from the swagger/openapi API spec file. | | String
+| *camel.jbang.openapiUi* | Swagger UI for REST OpenAPI at /q/openapi on local
HTTP server (port 8080 by default) | false | boolean
| *camel.jbang.packageScanJars* | Whether to automatic package scan JARs for
custom Spring or Quarkus beans making them available for Camel CLI | false |
boolean
| *camel.jbang.parentPom* | Parent POM coordinate (groupId:artifactId:version)
to use in the exported project | | String
| *camel.jbang.prompt* | Allow user to type in required parameters in prompt
if not present in application | false | boolean
diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang-managing.adoc
b/docs/user-manual/modules/ROOT/pages/camel-jbang-managing.adoc
index 9bb2551d7c22..107421fb034d 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang-managing.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang-managing.adoc
@@ -303,6 +303,23 @@ curl -s -X POST -H "Accept: application/json" -H
"Content-Type: application/json
http://0.0.0.0:8080/q/dev/eval-language | jq
----
+== OpenAPI Swagger UI
+
+For integrations using the xref:manual::rest-dsl.adoc[REST DSL], you can
browse the generated OpenAPI
+document in Swagger UI (similar to Hawtio for JMX):
+
+[source,bash]
+----
+camel run hello.java --openapi-ui
+----
+
+Swagger UI is served at http://localhost:8080/q/openapi. The OpenAPI JSON
document is at
+http://localhost:8080/q/openapi.json (configured automatically via
`camel.rest.apiContextPath`).
+
+You can combine this with REST routes defined in Java, YAML, or XML DSL. The
flag also works on
+`camel run --dev` and adds `camel-openapi-java` and `camel-platform-http-main`
to the runtime
+classpath when needed.
+
== Message history
*Available since Camel 4.17*
diff --git
a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-debug.adoc
b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-debug.adoc
index 402bfb81a441..de73fa1b3b02 100644
--- a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-debug.adoc
+++ b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-debug.adoc
@@ -68,6 +68,7 @@ camel debug [options]
| `--open-api` | Adds an OpenAPI spec from the given file (json or yaml file)
| | String
| `--open-telemetry-agent` | Enable OpenTelemetry Java Agent for
auto-instrumentation of third-party libraries (HTTP clients, JDBC, Kafka
clients, gRPC, etc.). Traces are shown in the TUI Spans tab with Camel spans in
cyan and 3rd-party agent spans in magenta. | false | boolean
| `--open-telemetry-agent-export` | Where to export OpenTelemetry Agent
telemetry: tui (embedded receiver in TUI), otlp (external OTLP collector for
traces), or observability (full stack with traces and logs). With otlp, start a
trace backend first (e.g. 'camel infra run jaeger'). With observability, start
the stack via 'camel infra run observability'. The value 'jaeger' is deprecated
and works as an alias for 'otlp'. | tui | String
+| `--openapi-ui` | Swagger UI for REST OpenAPI at /q/openapi (OpenAPI document
at /q/openapi.json; port 8080 by default) | false | boolean
| `--output` | File to store the current message body (will override). This
allows for manual inspecting the message later. | | String
| `--package-scan-jars` | Whether to automatic package scan JARs for custom
Spring or Quarkus beans making them available for Camel CLI | false | boolean
| `--port` | Embeds a local HTTP server on this port (port 8080 by default;
use 0 to dynamic assign a free random port number) | | int
diff --git
a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-dev.adoc
b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-dev.adoc
index 3511b72ca9de..fbe37951b652 100644
--- a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-dev.adoc
+++ b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-dev.adoc
@@ -64,6 +64,7 @@ camel dev [options]
| `--open-api` | Adds an OpenAPI spec from the given file (json or yaml file)
| | String
| `--open-telemetry-agent` | Enable OpenTelemetry Java Agent for
auto-instrumentation of third-party libraries (HTTP clients, JDBC, Kafka
clients, gRPC, etc.). Traces are shown in the TUI Spans tab with Camel spans in
cyan and 3rd-party agent spans in magenta. | false | boolean
| `--open-telemetry-agent-export` | Where to export OpenTelemetry Agent
telemetry: tui (embedded receiver in TUI), otlp (external OTLP collector for
traces), or observability (full stack with traces and logs). With otlp, start a
trace backend first (e.g. 'camel infra run jaeger'). With observability, start
the stack via 'camel infra run observability'. The value 'jaeger' is deprecated
and works as an alias for 'otlp'. | tui | String
+| `--openapi-ui` | Swagger UI for REST OpenAPI at /q/openapi (OpenAPI document
at /q/openapi.json; port 8080 by default) | false | boolean
| `--package-scan-jars` | Whether to automatic package scan JARs for custom
Spring or Quarkus beans making them available for Camel CLI | false | boolean
| `--port` | Embeds a local HTTP server on this port (port 8080 by default;
use 0 to dynamic assign a free random port number) | | int
| `--profile` | Profile to run (dev, test, prod). | dev | String
diff --git
a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-run.adoc
b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-run.adoc
index 25bc7a10095e..205ec1b76c99 100644
--- a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-run.adoc
+++ b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-run.adoc
@@ -64,6 +64,7 @@ camel run [options]
| `--open-api` | Adds an OpenAPI spec from the given file (json or yaml file)
| | String
| `--open-telemetry-agent` | Enable OpenTelemetry Java Agent for
auto-instrumentation of third-party libraries (HTTP clients, JDBC, Kafka
clients, gRPC, etc.). Traces are shown in the TUI Spans tab with Camel spans in
cyan and 3rd-party agent spans in magenta. | false | boolean
| `--open-telemetry-agent-export` | Where to export OpenTelemetry Agent
telemetry: tui (embedded receiver in TUI), otlp (external OTLP collector for
traces), or observability (full stack with traces and logs). With otlp, start a
trace backend first (e.g. 'camel infra run jaeger'). With observability, start
the stack via 'camel infra run observability'. The value 'jaeger' is deprecated
and works as an alias for 'otlp'. | tui | String
+| `--openapi-ui` | Swagger UI for REST OpenAPI at /q/openapi (OpenAPI document
at /q/openapi.json; port 8080 by default) | false | boolean
| `--package-scan-jars` | Whether to automatic package scan JARs for custom
Spring or Quarkus beans making them available for Camel CLI | false | boolean
| `--port` | Embeds a local HTTP server on this port (port 8080 by default;
use 0 to dynamic assign a free random port number) | | int
| `--profile` | Profile to run (dev, test, prod). | dev | String
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-commands-metadata.json
b/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-commands-metadata.json
index 0aa243db48fb..cda747041dbc 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-commands-metadata.json
+++
b/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-commands-metadata.json
@@ -6,9 +6,9 @@
{ "name": "cmd", "fullName": "cmd", "description": "Performs commands in
the running Camel integrations, such as start\/stop route, or change logging
levels.", "sourceClass":
"org.apache.camel.dsl.jbang.core.commands.action.CamelAction", "options": [ {
"names": "-h,--help", "description": "Display the help and sub-commands",
"javaType": "boolean", "type": "boolean" } ], "subcommands": [ { "name":
"browse", "fullName": "cmd browse", "description": "Browse pending messages on
endpoints [...]
{ "name": "completion", "fullName": "completion", "description": "Generate
completion script for bash\/zsh", "sourceClass":
"org.apache.camel.dsl.jbang.core.commands.Complete", "options": [ { "names":
"-h,--help", "description": "Display the help and sub-commands", "javaType":
"boolean", "type": "boolean" } ] },
{ "name": "config", "fullName": "config", "description": "Get and set user
configuration values", "sourceClass":
"org.apache.camel.dsl.jbang.core.commands.config.ConfigCommand", "options": [ {
"names": "-h,--help", "description": "Display the help and sub-commands",
"javaType": "boolean", "type": "boolean" } ], "subcommands": [ { "name": "get",
"fullName": "config get", "description": "Display user configuration value",
"sourceClass": "org.apache.camel.dsl.jbang.core.commands.config. [...]
- { "name": "debug", "fullName": "debug", "description": "Debug local Camel
integration", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.Debug",
"options": [ { "names": "--ago", "description": "Use ago instead of yyyy-MM-dd
HH:mm:ss in timestamp.", "javaType": "boolean", "type": "boolean" }, { "names":
"--background", "description": "Run in the background", "defaultValue":
"false", "javaType": "boolean", "type": "boolean" }, { "names":
"--background-wait", "description": "To [...]
+ { "name": "debug", "fullName": "debug", "description": "Debug local Camel
integration", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.Debug",
"options": [ { "names": "--ago", "description": "Use ago instead of yyyy-MM-dd
HH:mm:ss in timestamp.", "javaType": "boolean", "type": "boolean" }, { "names":
"--background", "description": "Run in the background", "defaultValue":
"false", "javaType": "boolean", "type": "boolean" }, { "names":
"--background-wait", "description": "To [...]
{ "name": "dependency", "fullName": "dependency", "description": "Displays
all Camel dependencies required to run", "sourceClass":
"org.apache.camel.dsl.jbang.core.commands.DependencyCommand", "options": [ {
"names": "-h,--help", "description": "Display the help and sub-commands",
"javaType": "boolean", "type": "boolean" } ], "subcommands": [ { "name":
"copy", "fullName": "dependency copy", "description": "Copies all Camel
dependencies required to run to a specific directory", "sourc [...]
- { "name": "dev", "fullName": "dev", "description": "Run in dev mode with
live reload", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.Dev",
"options": [ { "names": "--background", "description": "Run in the background",
"defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names":
"--background-wait", "description": "To wait for run in background to startup
successfully, before returning", "defaultValue": "true", "javaType": "boolean",
"type": "boolean" }, [...]
+ { "name": "dev", "fullName": "dev", "description": "Run in dev mode with
live reload", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.Dev",
"options": [ { "names": "--background", "description": "Run in the background",
"defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names":
"--background-wait", "description": "To wait for run in background to startup
successfully, before returning", "defaultValue": "true", "javaType": "boolean",
"type": "boolean" }, [...]
{ "name": "dirty", "fullName": "dirty", "description": "Check if there are
dirty files from previous Camel runs that did not terminate gracefully",
"sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.Dirty",
"options": [ { "names": "--clean", "description": "Clean dirty files which are
no longer in use", "defaultValue": "false", "javaType": "boolean", "type":
"boolean" }, { "names": "-h,--help", "description": "Display the help and
sub-commands", "javaType": "boolean", " [...]
{ "name": "doc", "fullName": "doc", "description": "Shows documentation
for kamelet, component, and other Camel resources", "sourceClass":
"org.apache.camel.dsl.jbang.core.commands.catalog.CatalogDoc", "options": [ {
"names": "--camel-version", "description": "To use a different Camel version
than the default version", "javaType": "java.lang.String", "type": "string" },
{ "names": "--download", "description": "Whether to allow automatic downloading
JAR dependencies (over the internet [...]
{ "name": "doctor", "fullName": "doctor", "description": "Checks the
environment and reports potential issues", "sourceClass":
"org.apache.camel.dsl.jbang.core.commands.Doctor", "options": [ { "names":
"-h,--help", "description": "Display the help and sub-commands", "javaType":
"boolean", "type": "boolean" } ] },
@@ -26,7 +26,7 @@
{ "name": "plugin", "fullName": "plugin", "description": "Manage plugins
that add sub-commands to this CLI", "sourceClass":
"org.apache.camel.dsl.jbang.core.commands.plugin.PluginCommand", "options": [ {
"names": "-h,--help", "description": "Display the help and sub-commands",
"javaType": "boolean", "type": "boolean" } ], "subcommands": [ { "name": "add",
"fullName": "plugin add", "description": "Add new plugin", "sourceClass":
"org.apache.camel.dsl.jbang.core.commands.plugin.PluginA [...]
{ "name": "ps", "fullName": "ps", "description": "List running Camel
integrations", "sourceClass":
"org.apache.camel.dsl.jbang.core.commands.process.ListProcess", "options": [ {
"names": "--json", "description": "Output in JSON Format", "javaType":
"boolean", "type": "boolean" }, { "names": "--pid", "description": "List only
pid in the output", "javaType": "boolean", "type": "boolean" }, { "names":
"--remote", "description": "Break down counters into remote\/total pairs",
"javaType": [...]
{ "name": "restart", "fullName": "restart", "description": "Restarts
running Camel integrations (stop + re-launch)", "sourceClass":
"org.apache.camel.dsl.jbang.core.commands.process.RestartProcess", "options": [
{ "names": "-h,--help", "description": "Display the help and sub-commands",
"javaType": "boolean", "type": "boolean" } ] },
- { "name": "run", "fullName": "run", "description": "Run as local Camel
integration", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.Run",
"options": [ { "names": "--background", "description": "Run in the background",
"defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names":
"--background-wait", "description": "To wait for run in background to startup
successfully, before returning", "defaultValue": "true", "javaType": "boolean",
"type": "boolean" }, { [...]
+ { "name": "run", "fullName": "run", "description": "Run as local Camel
integration", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.Run",
"options": [ { "names": "--background", "description": "Run in the background",
"defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names":
"--background-wait", "description": "To wait for run in background to startup
successfully, before returning", "defaultValue": "true", "javaType": "boolean",
"type": "boolean" }, { [...]
{ "name": "sbom", "fullName": "sbom", "description": "Generate a CycloneDX
or SPDX SBOM for a specific project", "sourceClass":
"org.apache.camel.dsl.jbang.core.commands.SBOMGenerator", "options": [ {
"names": "--build-property", "description": "Maven build properties, ex.
--build-property=prop1=foo", "javaType": "java.util.List", "type": "array" }, {
"names": "--camel-spring-boot-version", "description": "Camel version to use
with Spring Boot", "javaType": "java.lang.String", "type" [...]
{ "name": "script", "fullName": "script", "description": "Run Camel
integration as shell script for terminal scripting", "sourceClass":
"org.apache.camel.dsl.jbang.core.commands.Script", "options": [ { "names":
"--logging", "description": "Can be used to turn on logging (logs to file in
<user home>\/.camel directory)", "defaultValue": "false", "javaType":
"boolean", "type": "boolean" }, { "names": "--logging-level", "description":
"Logging level (ERROR, WARN, INFO, DEBUG, TRACE)", "d [...]
{ "name": "shell", "fullName": "shell", "description": "Interactive Camel
CLI shell.", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.Shell",
"options": [ { "names": "-h,--help", "description": "Display the help and
sub-commands", "javaType": "boolean", "type": "boolean" } ] },
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-configuration-metadata.json
b/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-configuration-metadata.json
index 4f02be84a402..2009c64f7a0c 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-configuration-metadata.json
+++
b/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-configuration-metadata.json
@@ -36,6 +36,7 @@
{ "name": "camel.jbang.mavenWrapper", "required": false, "description":
"Include Maven Wrapper files in the exported project", "type": "boolean",
"javaType": "boolean", "defaultValue": true, "secret": false },
{ "name": "camel.jbang.metrics", "required": false, "description":
"Metrics (Micrometer and Prometheus) at \/observe\/metrics on local HTTP server
(port 8080 by default) when running standalone Camel", "type": "boolean",
"javaType": "boolean", "defaultValue": false, "secret": false, "deprecated":
true },
{ "name": "camel.jbang.openApi", "required": false, "description": "File
name of open-api spec file (JSON or YAML) to generate routes from the
swagger\/openapi API spec file.", "type": "string", "javaType": "String",
"secret": false },
+ { "name": "camel.jbang.openapiUi", "required": false, "description":
"Swagger UI for REST OpenAPI at \/q\/openapi on local HTTP server (port 8080 by
default)", "type": "boolean", "javaType": "boolean", "defaultValue": false,
"secret": false, "security": "insecure:dev" },
{ "name": "camel.jbang.packageScanJars", "required": false, "description":
"Whether to automatic package scan JARs for custom Spring or Quarkus beans
making them available for Camel CLI", "label": "advanced", "type": "boolean",
"javaType": "boolean", "defaultValue": false, "secret": false },
{ "name": "camel.jbang.parentPom", "required": false, "description":
"Parent POM coordinate (groupId:artifactId:version) to use in the exported
project", "type": "string", "javaType": "String", "secret": false },
{ "name": "camel.jbang.prompt", "required": false, "description": "Allow
user to type in required parameters in prompt if not present in application",
"label": "advanced", "type": "boolean", "javaType": "boolean", "defaultValue":
false, "secret": false },
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java
index c6b1b7a7449f..29bf3e3d3b04 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java
@@ -69,6 +69,7 @@ import org.apache.camel.dsl.jbang.core.common.SourceHelper;
import org.apache.camel.dsl.jbang.core.common.SourceScheme;
import org.apache.camel.dsl.jbang.core.common.TemplateHelper;
import org.apache.camel.dsl.jbang.core.common.VersionHelper;
+import org.apache.camel.main.BaseMainSupport;
import org.apache.camel.main.KameletMain;
import org.apache.camel.main.download.DownloadListener;
import org.apache.camel.main.util.SuggestSimilarHelper;
@@ -845,6 +846,7 @@ public class Run extends CamelCommand {
writeSetting(main, profileProperties, HEALTH, serverOptions.health ?
"true" : "false");
writeSetting(main, profileProperties, METRICS, serverOptions.metrics ?
"true" : "false");
writeSetting(main, profileProperties, CONSOLE, serverOptions.console ?
"true" : "false");
+ writeSetting(main, profileProperties, OPENAPI_UI,
serverOptions.openapiUi ? "true" : "false");
writeSetting(main, profileProperties, VERBOSE, verbose ? "true" :
"false");
// the runtime version of Camel is what is loaded via the catalog
writeSetting(main, profileProperties, CAMEL_VERSION, new
DefaultCamelCatalog().getCatalogVersion());
@@ -914,6 +916,7 @@ public class Run extends CamelCommand {
writeSetting(main, profileProperties,
"camel.main.durationMaxIdleSeconds",
() -> executionLimitOptions.maxIdleSeconds > 0
? String.valueOf(executionLimitOptions.maxIdleSeconds)
: null);
+ prepareOpenApiUiServerOptions();
if (serverOptions.port != -1) {
// enable the main HTTP server when --port is explicitly specified
writeSetting(main, profileProperties, "camel.server.enabled",
"true");
@@ -1250,6 +1253,11 @@ public class Run extends CamelCommand {
dependencies.add("camel:observability-services");
main.addOverrideProperty("camel.metrics.logMetricsOnShutdown",
"false");
}
+ if (serverOptions.openapiUi) {
+ dependencies.add("camel:platform-http-main");
+ dependencies.add("camel:openapi-java");
+ applyOpenApiUiRuntimeOptions(main);
+ }
if (debugOptions.openTelemetryAgent) {
dependencies.add("camel:opentelemetry2");
boolean externalExport =
"otlp".equals(debugOptions.openTelemetryAgentExport)
@@ -3081,6 +3089,10 @@ public class Run extends CamelCommand {
description = "Developer console at /q/dev on local HTTP
server (port 8080 by default)")
boolean console;
+ @Option(names = { "--openapi-ui" }, defaultValue = "false",
+ description = "Swagger UI for REST OpenAPI at /q/openapi
(OpenAPI document at /q/openapi.json; port 8080 by default)")
+ boolean openapiUi;
+
@Deprecated
@Option(names = { "--health" }, defaultValue = "false",
description = "Deprecated: use --observe instead. Health check
at /q/health on local HTTP server (port 8080 by default)")
@@ -3096,6 +3108,33 @@ public class Run extends CamelCommand {
boolean observe;
}
+ /**
+ * Align HTTP and management ports when OpenAPI UI is enabled so Swagger
UI and the REST OpenAPI document are
+ * co-hosted (same Vert.x server when ports match).
+ */
+ void prepareOpenApiUiServerOptions() {
+ if (!serverOptions.openapiUi) {
+ return;
+ }
+ if (serverOptions.port == -1) {
+ serverOptions.port = 8080;
+ }
+ if (serverOptions.managementPort == -1) {
+ serverOptions.managementPort = serverOptions.port;
+ }
+ }
+
+ void applyOpenApiUiRuntimeOptions(BaseMainSupport main) {
+ if (!serverOptions.openapiUi) {
+ return;
+ }
+ main.addOverrideProperty("camel.rest.apiContextPath",
"/q/openapi.json");
+ main.addOverrideProperty("camel.rest.component", "platform-http");
+ main.addOverrideProperty("camel.management.openapiUiEnabled", "true");
+ main.addOverrideProperty("camel.server.enabled", "true");
+ main.addOverrideProperty("camel.management.enabled", "true");
+ }
+
static class FilesConsumer extends ParameterConsumer<Run> {
@Override
protected void doConsumeParameters(Stack<String> args, Run cmd) {
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/CamelJBangConstants.java
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/CamelJBangConstants.java
index e97203d1d431..334cd054afb5 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/CamelJBangConstants.java
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/CamelJBangConstants.java
@@ -201,6 +201,10 @@ public final class CamelJBangConstants {
javaType = "boolean")
public static final String CONSOLE = "camel.jbang.console";
+ @Metadata(description = "Swagger UI for REST OpenAPI at /q/openapi on
local HTTP server (port 8080 by default)",
+ javaType = "boolean", security = "insecure:dev")
+ public static final String OPENAPI_UI = "camel.jbang.openapiUi";
+
@Metadata(description = "Verbose output of startup activity (dependency
resolution and downloading",
javaType = "boolean")
public static final String VERBOSE = "camel.jbang.verbose";
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/RunOpenApiUiOptionsTest.java
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/RunOpenApiUiOptionsTest.java
new file mode 100644
index 000000000000..d7cb8965533a
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/RunOpenApiUiOptionsTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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.dsl.jbang.core.commands;
+
+import org.apache.camel.main.Main;
+import org.junit.jupiter.api.Test;
+import picocli.CommandLine;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class RunOpenApiUiOptionsTest {
+
+ @Test
+ void shouldParseOpenApiUiFlag() {
+ Run run = new Run(new CamelJBangMain());
+ CommandLine cmd = new CommandLine(run);
+
+ cmd.parseArgs("--openapi-ui", "hello.java");
+
+ assertThat(run.serverOptions.openapiUi).isTrue();
+ assertThat(run.files).containsExactly("hello.java");
+ }
+
+ @Test
+ void shouldAlignManagementPortWithAppPortForOpenApiUi() {
+ Run run = new Run(new CamelJBangMain());
+ new CommandLine(run).parseArgs("--openapi-ui", "--port=9090",
"hello.java");
+
+ run.prepareOpenApiUiServerOptions();
+
+ assertThat(run.serverOptions.port).isEqualTo(9090);
+ assertThat(run.serverOptions.managementPort).isEqualTo(9090);
+ }
+
+ @Test
+ void shouldPreserveExplicitManagementPortForOpenApiUi() {
+ Run run = new Run(new CamelJBangMain());
+ new CommandLine(run).parseArgs("--openapi-ui", "--port=9090",
"--management-port=9999", "hello.java");
+
+ run.prepareOpenApiUiServerOptions();
+
+ assertThat(run.serverOptions.port).isEqualTo(9090);
+ assertThat(run.serverOptions.managementPort).isEqualTo(9999);
+ }
+
+ @Test
+ void shouldApplyOpenApiUiOverrideProperties() {
+ Run run = new Run(new CamelJBangMain());
+ new CommandLine(run).parseArgs("--openapi-ui", "hello.java");
+ Main main = new Main();
+
+ run.applyOpenApiUiRuntimeOptions(main);
+
+
assertThat(main.getOverrideProperties().getProperty("camel.rest.component")).isEqualTo("platform-http");
+
assertThat(main.getOverrideProperties().getProperty("camel.rest.apiContextPath")).isEqualTo("/q/openapi.json");
+
assertThat(main.getOverrideProperties().getProperty("camel.management.openapiUiEnabled")).isEqualTo("true");
+
assertThat(main.getOverrideProperties().getProperty("camel.server.enabled")).isEqualTo("true");
+
assertThat(main.getOverrideProperties().getProperty("camel.management.enabled")).isEqualTo("true");
+ }
+}
diff --git
a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/KameletMain.java
b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/KameletMain.java
index 4c666e8756b1..7d7cade212fe 100644
---
a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/KameletMain.java
+++
b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/KameletMain.java
@@ -573,6 +573,18 @@ public class KameletMain extends MainCommandLineSupport {
configure().httpManagementServer().withInfoEnabled(true);
configure().httpManagementServer().withJolokiaEnabled(true);
}
+ boolean openapiUi =
"true".equals(getInitialProperties().get(getInstanceType() + ".openapiUi"));
+ if (openapiUi) {
+ configure().httpManagementServer().withEnabled(true);
+ configure().httpManagementServer().withOpenapiUiEnabled(true);
+ configure().httpServer().withEnabled(true);
+ if (!isConfigured("camel.rest.component")) {
+ configure().rest().withComponent("platform-http");
+ }
+ if (!isConfigured("camel.rest.apiContextPath")) {
+ configure().rest().withApiContextPath("/q/openapi.json");
+ }
+ }
boolean tracing =
"true".equals(getInitialProperties().get(getInstanceType() +
".backlogTracing"));
if (tracing) {
configure().tracerConfig().withEnabled(true);
@@ -987,6 +999,22 @@ public class KameletMain extends MainCommandLineSupport {
blueprintXmlBeansHandler.createAndRegisterBeans(camelContext);
}
+ private boolean isConfigured(String key) {
+ if (getOverrideProperties() != null) {
+ String value = getOverrideProperties().getProperty(key);
+ if (value != null && !value.isBlank()) {
+ return true;
+ }
+ }
+ if (getInitialProperties() != null) {
+ String value = getInitialProperties().getProperty(key);
+ if (value != null && !value.isBlank()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private static String getPid() {
return String.valueOf(ProcessHandle.current().pid());
}
diff --git a/parent/pom.xml b/parent/pom.xml
index fb09ab8b89ad..5958e4dcf9e6 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -504,6 +504,7 @@
<swagger-java-parser-version>1.0.65</swagger-java-parser-version>
<swagger-openapi3-java-parser-version>2.1.45</swagger-openapi3-java-parser-version>
<swagger-request-validator-version>2.46.1</swagger-request-validator-version>
+ <swagger-ui-version>5.21.0</swagger-ui-version>
<stringtemplate-version>4.3.4</stringtemplate-version>
<tahu-version>1.0.21</tahu-version>
<tamboui-version>0.4.0</tamboui-version>
diff --git
a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareCamelJBangMojo.java
b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareCamelJBangMojo.java
index 491acba04431..9dc44ce47db4 100644
---
a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareCamelJBangMojo.java
+++
b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareCamelJBangMojo.java
@@ -83,6 +83,10 @@ public class PrepareCamelJBangMojo extends
AbstractGeneratorMojo {
String desc = as.getStringValue("description");
String label = as.getStringValue("label");
boolean secret = "true".equals(as.getStringValue("secret"));
+ String security = as.getStringValue("security");
+ if ("secret".equals(security) && !secret) {
+ secret = true;
+ }
boolean required =
"true".equals(as.getStringValue("required"));
boolean deprecated = clazz.getAnnotation(Deprecated.class) !=
null || f.getAnnotation(Deprecated.class) != null;
JBangModel.JBangOptionModel model = new
JBangModel.JBangOptionModel();
@@ -93,6 +97,7 @@ public class PrepareCamelJBangMojo extends
AbstractGeneratorMojo {
model.setSourceType(null);
model.setDeprecated(deprecated);
model.setSecret(secret);
+ model.setSecurity(security);
model.setRequired(required);
List<String> enums = null;
String text = as.getStringValue("enums");