You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2021/10/31 08:21:54 UTC
[camel-spring-boot] branch main updated: Camel 17061 (#400)
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-spring-boot.git
The following commit(s) were added to refs/heads/main by this push:
new eaf68b5 Camel 17061 (#400)
eaf68b5 is described below
commit eaf68b5767c827d3b463b061a6e8aebc062cce46
Author: Steve Storck <st...@gmail.com>
AuthorDate: Sun Oct 31 04:21:46 2021 -0400
Camel 17061 (#400)
* CAMEL-17061: camel-openapi-java-starter - Integrate with springdoc. WIP. Should be moved to a new -starter JAR instead.
* CAMEL-17061: camel-openapi-java-starter - Integrate with springdoc. WIP. Should be moved to a new -starter JAR instead.
* CAMEL-17061: camel-springdoc-starter -- Integration with Springdoc.
Co-authored-by: Claus Ibsen <cl...@gmail.com>
---
.../camel-openapi-java-starter/pom.xml | 5 +
.../src/main/docs/openapi-java-starter.adoc | 29 +++
.../openapi/OpenApiAutoConfiguration.java | 216 +++++++++++++++++++++
.../springboot/openapi/OpenApiConfiguration.java | 40 ++++
.../src/main/resources/META-INF/spring.factories | 18 ++
components-starter/camel-springdoc-starter/pom.xml | 77 ++++++++
.../src/main/docs/springdoc-starter.adoc | 29 +++
.../src/main/docs/springdoc.json | 19 ++
.../springdoc/SpringdocAutoConfiguration.java | 187 ++++++++++++++++++
.../springdoc/SpringdocConfiguration.java | 40 ++++
.../src/main/resources/META-INF/LICENSE.txt | 203 +++++++++++++++++++
.../src/main/resources/META-INF/NOTICE.txt | 11 ++
.../src/main/resources/META-INF/spring.factories | 18 ++
.../src/main/resources/META-INF/spring.provides | 17 ++
components-starter/pom.xml | 1 +
.../spring-boot/partials/openapi-java-starter.adoc | 29 +++
16 files changed, 939 insertions(+)
diff --git a/components-starter/camel-openapi-java-starter/pom.xml b/components-starter/camel-openapi-java-starter/pom.xml
index f600709..e38734d 100644
--- a/components-starter/camel-openapi-java-starter/pom.xml
+++ b/components-starter/camel-openapi-java-starter/pom.xml
@@ -39,6 +39,11 @@
<artifactId>camel-openapi-java</artifactId>
<version>${camel-version}</version>
</dependency>
+ <dependency>
+ <groupId>io.swagger.parser.v3</groupId>
+ <artifactId>swagger-parser-v3</artifactId>
+ <version>2.0.28</version>
+ </dependency>
<!--START OF GENERATED CODE-->
<dependency>
<groupId>org.apache.camel.springboot</groupId>
diff --git a/components-starter/camel-openapi-java-starter/src/main/docs/openapi-java-starter.adoc b/components-starter/camel-openapi-java-starter/src/main/docs/openapi-java-starter.adoc
new file mode 100644
index 0000000..4306df8
--- /dev/null
+++ b/components-starter/camel-openapi-java-starter/src/main/docs/openapi-java-starter.adoc
@@ -0,0 +1,29 @@
+// spring-boot-auto-configure options: START
+:page-partial:
+:doctitle: Camel Spring Boot Starter for openapi-java
+
+== Spring Boot Auto-Configuration
+
+When using openapi-java with Spring Boot make sure to use the following Maven dependency to have support for auto configuration:
+
+[source,xml]
+----
+<dependency>
+ <groupId>org.apache.camel.springboot</groupId>
+ <artifactId>camel-openapi-java-starter</artifactId>
+ <version>x.x.x</version>
+ <!-- use the same version as your Camel core version -->
+</dependency>
+----
+
+
+The component supports 1 options, which are listed below.
+
+
+
+[width="100%",cols="2,5,^1,2",options="header"]
+|===
+| Name | Description | Default | Type
+| *camel.openapi.enabled* | Enables Camel Rest DSL to automatic register its OpenAPI (eg swagger doc) in Spring Boot which allows tooling such as SpringDoc to integrate with Camel. | true | Boolean
+|===
+// spring-boot-auto-configure options: END
diff --git a/components-starter/camel-openapi-java-starter/src/main/java/org/apache/camel/springboot/openapi/OpenApiAutoConfiguration.java b/components-starter/camel-openapi-java-starter/src/main/java/org/apache/camel/springboot/openapi/OpenApiAutoConfiguration.java
new file mode 100644
index 0000000..e9e5998
--- /dev/null
+++ b/components-starter/camel-openapi-java-starter/src/main/java/org/apache/camel/springboot/openapi/OpenApiAutoConfiguration.java
@@ -0,0 +1,216 @@
+/*
+ * 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.springboot.openapi;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
+import io.apicurio.datamodels.openapi.models.OasDocument;
+import io.swagger.v3.oas.models.OpenAPI;
+import io.swagger.v3.oas.models.info.Contact;
+import io.swagger.v3.oas.models.info.Info;
+import io.swagger.v3.parser.OpenAPIV3Parser;
+import io.swagger.v3.parser.core.models.SwaggerParseResult;
+import org.apache.camel.CamelContext;
+import org.apache.camel.model.rest.RestDefinition;
+import org.apache.camel.openapi.BeanConfig;
+import org.apache.camel.openapi.DefaultRestDefinitionsResolver;
+import org.apache.camel.openapi.RestDefinitionsResolver;
+import org.apache.camel.openapi.RestOpenApiReader;
+import org.apache.camel.spi.RestConfiguration;
+import org.apache.camel.spring.boot.CamelContextConfiguration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.autoconfigure.AutoConfigureAfter;
+import org.springframework.boot.autoconfigure.AutoConfigureBefore;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.support.GenericApplicationContext;
+
+import java.util.List;
+import java.util.Map;
+
+import static org.apache.camel.openapi.OpenApiHelper.clearVendorExtensions;
+
+/**
+ * Open API auto-configuration.
+ */
+@Configuration(proxyBeanMethods = false)
+@EnableConfigurationProperties({OpenApiConfiguration.class})
+@ConditionalOnBean(type = "org.apache.camel.spring.boot.CamelAutoConfiguration")
+@ConditionalOnProperty(name = "camel.openapi.enabled", matchIfMissing = true)
+@AutoConfigureAfter(name = "org.apache.camel.spring.boot.CamelAutoConfiguration")
+@AutoConfigureBefore(name = "org.springdoc.core.SpringDocConfiguration")
+public class OpenApiAutoConfiguration {
+
+ private static final Logger LOG = LoggerFactory.getLogger(OpenApiAutoConfiguration.class);
+
+ private final RestOpenApiReader reader = new RestOpenApiReader();
+ private final RestDefinitionsResolver resolver = new DefaultRestDefinitionsResolver();
+ private final OpenAPI openAPI = new OpenAPI();
+
+ @Bean
+ CamelContextConfiguration onBeforeStart(GenericApplicationContext ac, CamelContext camelContext, OpenApiConfiguration config) {
+ return new CamelContextConfiguration() {
+ @Override
+ public void beforeApplicationStart(CamelContext camelContext) {
+ // routes have now been loaded, so we need to detect rest-dsl APIs in Camel
+ // this will trigger spring boot to create the bean which springdoc can detect
+ try {
+ OpenAPI created = createOpenAPI(camelContext);
+ if (created != null) {
+ LOG.info("OpenAPI ({}) created from Camel Rest-DSL v{} - {}", created.getOpenapi(), created.getInfo().getVersion(), created.getInfo().getTitle());
+ // transfer data to the existing
+ openAPI.setInfo(created.getInfo());
+ openAPI.setOpenapi(created.getOpenapi());
+ if (created.getComponents() != null) {
+ openAPI.setComponents(created.getComponents());
+ }
+ if (created.getExtensions() != null) {
+ openAPI.setExtensions(created.getExtensions());
+ }
+ if (created.getSecurity() != null) {
+ openAPI.setSecurity(created.getSecurity());
+ }
+ if (created.getExternalDocs() != null) {
+ openAPI.setExternalDocs(created.getExternalDocs());
+ }
+ if (created.getPaths() != null) {
+ openAPI.setPaths(created.getPaths());
+ }
+ if (created.getTags() != null) {
+ openAPI.setTags(created.getTags());
+ }
+ // do not copy servers as we use the spring-boot configured setting
+ }
+ } catch (Exception e) {
+ LOG.warn("Error generating OpenAPI from Camel Rest DSL due to: " + e.getMessage() + ". This exception is ignored.", e);
+ }
+ }
+
+ @Override
+ public void afterApplicationStart(CamelContext camelContext) {
+ // noop
+ }
+ };
+ }
+
+ @Bean
+ OpenAPI camelRestDSLOpenApi() {
+ // due to ordering how beans are resolved and setup in spring boot, then we need to create
+ // this provisional bean which is later updated with the actual Rest DSL APIs after the routes has been loaded
+ // into Camel
+ return openAPI;
+ }
+
+ private OpenAPI createOpenAPI(CamelContext camelContext) throws Exception {
+ List<RestDefinition> rests = resolver.getRestDefinitions(camelContext, null);
+ if (rests == null || rests.isEmpty()) {
+ return null;
+ }
+
+ BeanConfig bc = new BeanConfig();
+ Info info = new Info();
+
+ RestConfiguration rc = camelContext.getRestConfiguration();
+ initOpenApi(bc, info, rc.getApiProperties());
+
+ OasDocument openApi = reader.read(camelContext, rests, "", bc, null, camelContext.getClassResolver());
+ if (!rc.isApiVendorExtension()) {
+ clearVendorExtensions(openApi);
+ }
+
+ // dump to json
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.enable(SerializationFeature.INDENT_OUTPUT);
+ mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
+ Object dump = io.apicurio.datamodels.Library.writeNode(openApi);
+ byte[] jsonData = mapper.writeValueAsBytes(dump);
+ // json to yaml
+ JsonNode node = mapper.readTree(jsonData);
+ String yaml = new YAMLMapper().writeValueAsString(node);
+
+ // parse bytes into swagger
+ OpenAPIV3Parser parser = new OpenAPIV3Parser();
+ SwaggerParseResult spr = parser.readContents(yaml);
+ OpenAPI answer = spr.getOpenAPI();
+ if (answer != null) {
+ answer.setInfo(info);
+ }
+ return answer;
+ }
+
+ private static void initOpenApi(BeanConfig bc, Info info, Map<String, Object> config) {
+ // configure openApi options
+ String s = (String) config.get("openapi.version");
+ if (s != null) {
+ bc.setVersion(s);
+ }
+ s = (String) config.get("base.path");
+ if (s != null) {
+ bc.setBasePath(s);
+ }
+ s = (String) config.get("host");
+ if (s != null) {
+ bc.setHost(s);
+ }
+ s = (String) config.get("schemes");
+ if (s == null) {
+ // deprecated due typo
+ s = (String) config.get("schemas");
+ }
+ if (s != null) {
+ String[] schemes = s.split(",");
+ bc.setSchemes(schemes);
+ } else {
+ // assume http by default
+ bc.setSchemes(new String[] { "http" });
+ }
+
+ String version = (String) config.get("api.version");
+ String title = (String) config.get("api.title");
+ String description = (String) config.get("api.description");
+ String termsOfService = (String) config.get("api.termsOfService");
+ String licenseName = (String) config.get("api.license.name");
+ String licenseUrl = (String) config.get("api.license.url");
+ String contactName = (String) config.get("api.contact.name");
+ String contactUrl = (String) config.get("api.contact.url");
+ String contactEmail = (String) config.get("api.contact.email");
+
+ bc.setTitle(title);
+ bc.setLicense(licenseName);
+ bc.setLicenseUrl(licenseUrl);
+
+ info.setTitle(title);
+ info.setVersion(version);
+ info.setDescription(description);
+ info.setTermsOfService(termsOfService);
+ if (contactName != null) {
+ Contact contact = new Contact();
+ contact.setName(contactName);
+ contact.setEmail(contactEmail);
+ contact.setUrl(contactUrl);
+ info.setContact(contact);
+ }
+ }
+
+}
diff --git a/components-starter/camel-openapi-java-starter/src/main/java/org/apache/camel/springboot/openapi/OpenApiConfiguration.java b/components-starter/camel-openapi-java-starter/src/main/java/org/apache/camel/springboot/openapi/OpenApiConfiguration.java
new file mode 100644
index 0000000..6556ade
--- /dev/null
+++ b/components-starter/camel-openapi-java-starter/src/main/java/org/apache/camel/springboot/openapi/OpenApiConfiguration.java
@@ -0,0 +1,40 @@
+/*
+ * 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.springboot.openapi;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+/**
+ * Mapping settings for the Camel Open API
+ */
+@ConfigurationProperties(prefix = "camel.openapi")
+public class OpenApiConfiguration {
+
+ /**
+ * Enables Camel Rest DSL to automatic register its OpenAPI (eg swagger doc) in Spring Boot
+ * which allows tooling such as SpringDoc to integrate with Camel.
+ */
+ private Boolean enabled = true;
+
+ public Boolean getEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(Boolean enabled) {
+ this.enabled = enabled;
+ }
+}
diff --git a/components-starter/camel-openapi-java-starter/src/main/resources/META-INF/spring.factories b/components-starter/camel-openapi-java-starter/src/main/resources/META-INF/spring.factories
new file mode 100644
index 0000000..2b188dc
--- /dev/null
+++ b/components-starter/camel-openapi-java-starter/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,18 @@
+## ---------------------------------------------------------------------------
+## Licensed to the Apache Software Foundation (ASF) under one or more
+## contributor license agreements. See the NOTICE file distributed with
+## this work for additional information regarding copyright ownership.
+## The ASF licenses this file to You under the Apache License, Version 2.0
+## (the "License"); you may not use this file except in compliance with
+## the License. You may obtain a copy of the License at
+##
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+## ---------------------------------------------------------------------------
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+org.apache.camel.springboot.openapi.OpenApiAutoConfiguration
\ No newline at end of file
diff --git a/components-starter/camel-springdoc-starter/pom.xml b/components-starter/camel-springdoc-starter/pom.xml
new file mode 100644
index 0000000..c7ce86d
--- /dev/null
+++ b/components-starter/camel-springdoc-starter/pom.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.apache.camel.springboot</groupId>
+ <artifactId>components-starter</artifactId>
+ <version>3.13.0-SNAPSHOT</version>
+ </parent>
+ <artifactId>camel-springdoc-starter</artifactId>
+ <packaging>jar</packaging>
+ <name>Camel SB Starters :: Springdoc</name>
+ <description>Spring-Boot Starter for Rest-dsl support when using openapi doc and the Springdoc Swagger UI</description>
+ <properties>
+ <springdoc-version>1.5.11</springdoc-version>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter</artifactId>
+ <version>${spring-boot-version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-openapi-java</artifactId>
+ <version>${camel-version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springdoc</groupId>
+ <artifactId>springdoc-openapi-ui</artifactId>
+ <version>${springdoc-version}</version>
+ </dependency>
+ <dependency>
+ <groupId>io.swagger.parser.v3</groupId>
+ <artifactId>swagger-parser-v3</artifactId>
+ <version>2.0.28</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel.springboot</groupId>
+ <artifactId>camel-core-starter</artifactId>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.camel.springboot</groupId>
+ <artifactId>camel-spring-boot-generator-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <!-- This module does not have a camel-springdoc-java module,
+ so we do not want to generate in the usual fashion -->
+ <id>generate</id>
+ <phase>none</phase>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/components-starter/camel-springdoc-starter/src/main/docs/springdoc-starter.adoc b/components-starter/camel-springdoc-starter/src/main/docs/springdoc-starter.adoc
new file mode 100644
index 0000000..fb8c136
--- /dev/null
+++ b/components-starter/camel-springdoc-starter/src/main/docs/springdoc-starter.adoc
@@ -0,0 +1,29 @@
+// spring-boot-auto-configure options: START
+:page-partial:
+:doctitle: Camel Spring Boot Starter for extending openapi-java with the Springdoc UI
+
+== Spring Boot Auto-Configuration
+
+When using openapi-java with Spring Boot, if you want to use the Springdoc Swagger UI, make sure to use the following Maven dependency to have support for auto-configuration:
+
+[source,xml]
+----
+<dependency>
+ <groupId>org.apache.camel.springboot</groupId>
+ <artifactId>camel-springdoc-starter</artifactId>
+ <version>x.x.x</version>
+ <!-- use the same version as your Camel core version -->
+</dependency>
+----
+
+
+The component supports 1 option, which is listed below.
+
+
+
+[width="100%",cols="2,5,^1,2",options="header"]
+|===
+| Name | Description | Default | Type
+| *camel.springdoc.enabled* | Enables Camel Rest DSL to automatic register its OpenAPI (eg swagger doc) in Spring Boot which allows tooling such as SpringDoc to integrate with Camel. | true | Boolean
+|===
+// spring-boot-auto-configure options: END
diff --git a/components-starter/camel-springdoc-starter/src/main/docs/springdoc.json b/components-starter/camel-springdoc-starter/src/main/docs/springdoc.json
new file mode 100644
index 0000000..264d94d
--- /dev/null
+++ b/components-starter/camel-springdoc-starter/src/main/docs/springdoc.json
@@ -0,0 +1,19 @@
+{
+ "groups": [
+ {
+ "name": "camel.springdoc",
+ "type": "org.apache.camel.springboot.springdoc.SpringdocConfiguration",
+ "sourceType": "org.apache.camel.springboot.springdoc.SpringdocConfiguration"
+ }
+ ],
+ "properties": [
+ {
+ "name": "camel.springdoc.enabled",
+ "type": "java.lang.Boolean",
+ "description": "Enables Camel Rest DSL to automatic register its OpenAPI (eg swagger doc) in Spring Boot which allows tooling such as SpringDoc to integrate with Camel.",
+ "sourceType": "org.apache.camel.springboot.springdoc.SpringdocConfiguration",
+ "defaultValue": true
+ }
+ ],
+ "hints": []
+}
\ No newline at end of file
diff --git a/components-starter/camel-springdoc-starter/src/main/java/org/apache/camel/springboot/springdoc/SpringdocAutoConfiguration.java b/components-starter/camel-springdoc-starter/src/main/java/org/apache/camel/springboot/springdoc/SpringdocAutoConfiguration.java
new file mode 100644
index 0000000..1ee15c5
--- /dev/null
+++ b/components-starter/camel-springdoc-starter/src/main/java/org/apache/camel/springboot/springdoc/SpringdocAutoConfiguration.java
@@ -0,0 +1,187 @@
+/*
+ * 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.springboot.springdoc;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
+import io.apicurio.datamodels.openapi.models.OasDocument;
+import io.swagger.v3.oas.models.OpenAPI;
+import io.swagger.v3.oas.models.info.Contact;
+import io.swagger.v3.oas.models.info.Info;
+import io.swagger.v3.parser.OpenAPIV3Parser;
+import io.swagger.v3.parser.core.models.SwaggerParseResult;
+import org.apache.camel.CamelContext;
+import org.apache.camel.model.rest.RestDefinition;
+import org.apache.camel.openapi.BeanConfig;
+import org.apache.camel.openapi.DefaultRestDefinitionsResolver;
+import org.apache.camel.openapi.RestDefinitionsResolver;
+import org.apache.camel.openapi.RestOpenApiReader;
+import org.apache.camel.spi.RestConfiguration;
+import org.apache.camel.spring.boot.CamelContextConfiguration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.autoconfigure.AutoConfigureAfter;
+import org.springframework.boot.autoconfigure.AutoConfigureBefore;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.support.GenericApplicationContext;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+
+import static org.apache.camel.openapi.OpenApiHelper.clearVendorExtensions;
+
+/**
+ * Springdoc auto-configuration.
+ */
+@Configuration(proxyBeanMethods = false)
+@EnableConfigurationProperties({SpringdocConfiguration.class})
+@ConditionalOnBean(type = "org.apache.camel.spring.boot.CamelAutoConfiguration")
+@ConditionalOnProperty(name = "camel.springdoc.enabled", matchIfMissing = true)
+@AutoConfigureAfter(name = "org.apache.camel.spring.boot.CamelAutoConfiguration")
+@AutoConfigureBefore(name = "org.springdoc.core.SpringdocConfiguration")
+public class SpringdocAutoConfiguration {
+
+ private static final Logger LOG = LoggerFactory.getLogger(SpringdocAutoConfiguration.class);
+
+ private final RestOpenApiReader reader = new RestOpenApiReader();
+ private final RestDefinitionsResolver resolver = new DefaultRestDefinitionsResolver();
+ private final OpenAPI openapi = new OpenAPI();
+
+ @Bean
+ CamelContextConfiguration springdocOnBeforeStart(final GenericApplicationContext ac, final CamelContext camelContext, final SpringdocConfiguration config) {
+ return new CamelContextConfiguration() {
+ @Override
+ public void beforeApplicationStart(final CamelContext camelContext) {
+ // routes have now been loaded, so we need to detect rest-dsl APIs in Camel
+ // this will trigger spring boot to create the bean which springdoc can detect
+ try {
+ Optional.ofNullable(createOpenAPI(camelContext))
+ .ifPresent(created -> {
+ LOG.info("OpenAPI ({}) created from Camel Rest-DSL v{} - {}",
+ created.getOpenapi(), created.getInfo().getVersion(), created.getInfo().getTitle());
+ // transfer data to the existing OpenAPI instance
+ Optional.ofNullable(created.getInfo()).ifPresent(openapi::setInfo);
+ Optional.ofNullable(created.getOpenapi()).ifPresent(openapi::setOpenapi);
+ Optional.ofNullable(created.getComponents()).ifPresent(openapi::setComponents);
+ Optional.ofNullable(created.getExtensions()).ifPresent(openapi::setExtensions);
+ Optional.ofNullable(created.getSecurity()).ifPresent(openapi::setSecurity);
+ Optional.ofNullable(created.getExternalDocs()).ifPresent(openapi::setExternalDocs);
+ Optional.ofNullable(created.getPaths()).ifPresent(openapi::setPaths);
+ Optional.ofNullable(created.getTags()).ifPresent(openapi::setTags);
+ // Note: do not copy servers, since we use the spring-boot configured setting
+ });
+ } catch (Exception e) {
+ LOG.warn("Error generating OpenAPI from Camel Rest DSL due to: {}. This exception is ignored.", e.getMessage(), e);
+ }
+ }
+
+ @Override
+ public void afterApplicationStart(final CamelContext camelContext) {
+ // noop
+ }
+ };
+ }
+
+ @Bean
+ OpenAPI camelRestDSLOpenApi() {
+ // due to ordering how beans are resolved and setup in spring boot, then we need to create
+ // this provisional bean which is later updated with the actual Rest DSL APIs after the routes has been loaded
+ // into Camel
+ return openapi;
+ }
+
+ private OpenAPI createOpenAPI(final CamelContext camelContext) throws Exception {
+ final List<RestDefinition> rests = resolver.getRestDefinitions(camelContext, null);
+ if (rests == null || rests.isEmpty()) {
+ return null;
+ }
+
+ final BeanConfig bc = new BeanConfig();
+ final Info info = new Info();
+
+ final RestConfiguration rc = camelContext.getRestConfiguration();
+ initOpenApi(bc, info, rc.getApiProperties());
+
+ final OasDocument openApi = reader.read(camelContext, rests, "", bc, null, camelContext.getClassResolver());
+ if (!rc.isApiVendorExtension()) {
+ clearVendorExtensions(openApi);
+ }
+
+ // dump to json
+ final ObjectMapper mapper = new ObjectMapper(new JsonFactory());
+ mapper.enable(SerializationFeature.INDENT_OUTPUT);
+ mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
+ final Object dump = io.apicurio.datamodels.Library.writeNode(openApi);
+ final byte[] jsonData = mapper.writeValueAsBytes(dump);
+ // json to yaml
+ final JsonNode node = mapper.readTree(jsonData);
+ final String yaml = new YAMLMapper().writeValueAsString(node);
+
+ // parse bytes into swagger
+ return Optional.ofNullable(new OpenAPIV3Parser().readContents(yaml))
+ .map(SwaggerParseResult::getOpenAPI)
+ .map(a -> a.info(info))
+ .orElse(null);
+ }
+
+ /**
+ * This consumes a property object, if non-null, by converting it to a string and running the given
+ * string consumer on the value.
+ */
+ private static final BiConsumer<Object, Consumer<String>> consumeProperty = (a, b) ->
+ Optional.ofNullable(a)
+ .map(String.class::cast)
+ .ifPresent(b);
+
+ private static void initOpenApi(BeanConfig bc, Info info, Map<String, Object> config) {
+ // configure openApi options
+ consumeProperty.accept(config.get("openapi.version"), bc::setVersion);
+ consumeProperty.accept(config.get("base.path"), bc::setBasePath);
+ consumeProperty.accept(config.get("host"), bc::setHost);
+ consumeProperty.accept(config.get("api.version"), info::setVersion);
+ consumeProperty.accept(config.get("api.description"), info::setDescription);
+ consumeProperty.accept(config.get("api.termsOfService"), info::setTermsOfService);
+ consumeProperty.accept(config.get("api.license.name"), bc::setLicense);
+ consumeProperty.accept(config.get("api.license.url"), bc::setLicenseUrl);
+ consumeProperty.accept(config.get("api.title"), s -> {
+ bc.setTitle(s);
+ info.setTitle(s);
+ });
+ Optional.of(config.getOrDefault("schemes", config.getOrDefault("schemas", "http")))
+ .map(String.class::cast)
+ .map(v -> v.split(","))
+ .ifPresent(bc::setSchemes);
+ Optional.ofNullable(config.get("api.contact.name"))
+ .map(String.class::cast)
+ .map(name -> new Contact()
+ .name(name)
+ .email((String) config.get("api.contact.email"))
+ .url((String) config.get("api.contact.url")))
+ .ifPresent(info::setContact);
+ }
+}
diff --git a/components-starter/camel-springdoc-starter/src/main/java/org/apache/camel/springboot/springdoc/SpringdocConfiguration.java b/components-starter/camel-springdoc-starter/src/main/java/org/apache/camel/springboot/springdoc/SpringdocConfiguration.java
new file mode 100644
index 0000000..9ae0642
--- /dev/null
+++ b/components-starter/camel-springdoc-starter/src/main/java/org/apache/camel/springboot/springdoc/SpringdocConfiguration.java
@@ -0,0 +1,40 @@
+/*
+ * 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.springboot.springdoc;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+/**
+ * Mapping settings for the Camel Open API
+ */
+@ConfigurationProperties(prefix = "camel.springdoc")
+public class SpringdocConfiguration {
+
+ /**
+ * Enables Camel Rest DSL to automatic register its OpenAPI (eg swagger doc) in Spring Boot
+ * which allows tooling such as SpringDoc to integrate with Camel.
+ */
+ private Boolean enabled = true;
+
+ public Boolean getEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(Boolean enabled) {
+ this.enabled = enabled;
+ }
+}
diff --git a/components-starter/camel-springdoc-starter/src/main/resources/META-INF/LICENSE.txt b/components-starter/camel-springdoc-starter/src/main/resources/META-INF/LICENSE.txt
new file mode 100644
index 0000000..6b0b127
--- /dev/null
+++ b/components-starter/camel-springdoc-starter/src/main/resources/META-INF/LICENSE.txt
@@ -0,0 +1,203 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed 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.
+
diff --git a/components-starter/camel-springdoc-starter/src/main/resources/META-INF/NOTICE.txt b/components-starter/camel-springdoc-starter/src/main/resources/META-INF/NOTICE.txt
new file mode 100644
index 0000000..2e215bf
--- /dev/null
+++ b/components-starter/camel-springdoc-starter/src/main/resources/META-INF/NOTICE.txt
@@ -0,0 +1,11 @@
+ =========================================================================
+ == NOTICE file corresponding to the section 4 d of ==
+ == the Apache License, Version 2.0, ==
+ == in this case for the Apache Camel distribution. ==
+ =========================================================================
+
+ This product includes software developed by
+ The Apache Software Foundation (http://www.apache.org/).
+
+ Please read the different LICENSE files present in the licenses directory of
+ this distribution.
diff --git a/components-starter/camel-springdoc-starter/src/main/resources/META-INF/spring.factories b/components-starter/camel-springdoc-starter/src/main/resources/META-INF/spring.factories
new file mode 100644
index 0000000..cb2a2a7
--- /dev/null
+++ b/components-starter/camel-springdoc-starter/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,18 @@
+## ---------------------------------------------------------------------------
+## Licensed to the Apache Software Foundation (ASF) under one or more
+## contributor license agreements. See the NOTICE file distributed with
+## this work for additional information regarding copyright ownership.
+## The ASF licenses this file to You under the Apache License, Version 2.0
+## (the "License"); you may not use this file except in compliance with
+## the License. You may obtain a copy of the License at
+##
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+## ---------------------------------------------------------------------------
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+org.apache.camel.springboot.springdoc.SpringdocAutoConfiguration
\ No newline at end of file
diff --git a/components-starter/camel-springdoc-starter/src/main/resources/META-INF/spring.provides b/components-starter/camel-springdoc-starter/src/main/resources/META-INF/spring.provides
new file mode 100644
index 0000000..9cf4da5
--- /dev/null
+++ b/components-starter/camel-springdoc-starter/src/main/resources/META-INF/spring.provides
@@ -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.
+## ---------------------------------------------------------------------------
+provides: camel-openapi-java
diff --git a/components-starter/pom.xml b/components-starter/pom.xml
index d9253eb..adf0f93 100644
--- a/components-starter/pom.xml
+++ b/components-starter/pom.xml
@@ -396,6 +396,7 @@
<module>camel-spring-security-starter</module>
<module>camel-spring-starter</module>
<module>camel-spring-ws-starter</module>
+ <module>camel-springdoc-starter</module>
<module>camel-sql-starter</module>
<module>camel-ssh-starter</module>
<module>camel-stax-starter</module>
diff --git a/docs/components/modules/spring-boot/partials/openapi-java-starter.adoc b/docs/components/modules/spring-boot/partials/openapi-java-starter.adoc
new file mode 100644
index 0000000..4306df8
--- /dev/null
+++ b/docs/components/modules/spring-boot/partials/openapi-java-starter.adoc
@@ -0,0 +1,29 @@
+// spring-boot-auto-configure options: START
+:page-partial:
+:doctitle: Camel Spring Boot Starter for openapi-java
+
+== Spring Boot Auto-Configuration
+
+When using openapi-java with Spring Boot make sure to use the following Maven dependency to have support for auto configuration:
+
+[source,xml]
+----
+<dependency>
+ <groupId>org.apache.camel.springboot</groupId>
+ <artifactId>camel-openapi-java-starter</artifactId>
+ <version>x.x.x</version>
+ <!-- use the same version as your Camel core version -->
+</dependency>
+----
+
+
+The component supports 1 options, which are listed below.
+
+
+
+[width="100%",cols="2,5,^1,2",options="header"]
+|===
+| Name | Description | Default | Type
+| *camel.openapi.enabled* | Enables Camel Rest DSL to automatic register its OpenAPI (eg swagger doc) in Spring Boot which allows tooling such as SpringDoc to integrate with Camel. | true | Boolean
+|===
+// spring-boot-auto-configure options: END