Merge pull request #3384 from ffang/CAMEL-14227

[CAMEL-14227]add a new camel-rest-openapi component and use apicurio-…
diff --git a/components/camel-rest-openapi/pom.xml b/components/camel-rest-openapi/pom.xml
new file mode 100644
index 0000000..f14910a
--- /dev/null
+++ b/components/camel-rest-openapi/pom.xml
@@ -0,0 +1,141 @@
+<?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:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
+         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</groupId>
+        <artifactId>components</artifactId>
+        <version>3.1.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>camel-rest-openapi</artifactId>
+
+    <name>Camel :: REST OpenApi</name>
+    <description>Camel REST support using OpenApi</description>
+
+    <properties>
+        <firstVersion>3.1.0</firstVersion>
+        <label>rest,api,http</label>
+    </properties>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-support</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>io.apicurio</groupId>
+            <artifactId>apicurio-data-models</artifactId>
+        </dependency>
+
+        <!-- test -->
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.assertj</groupId>
+            <artifactId>assertj-core</artifactId>
+            <version>${assertj-version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>com.github.tomakehurst</groupId>
+            <artifactId>wiremock</artifactId>
+            <version>${wiremock-version}</version>
+            <scope>test</scope>
+            <exclusions>
+                <exclusion>
+                    <!--
+                     Adding `camel-jetty` forces WireMock to use mixed Jetty
+                     versions, here we exclude WireMocks Jetty dependencies so
+                     that we only use the ones declared by the `jetty` component
+                     -->
+                    <groupId>org.eclipse.jetty</groupId>
+                    <artifactId>*</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-jaxb</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-http</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-netty-http</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-jetty</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-undertow</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.logging.log4j</groupId>
+            <artifactId>log4j-slf4j-impl</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.logging.log4j</groupId>
+            <artifactId>log4j-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+    </dependencies>
+
+</project>
diff --git a/components/camel-rest-openapi/src/main/docs/rest-openapi-component.adoc b/components/camel-rest-openapi/src/main/docs/rest-openapi-component.adoc
new file mode 100644
index 0000000..a013925
--- /dev/null
+++ b/components/camel-rest-openapi/src/main/docs/rest-openapi-component.adoc
@@ -0,0 +1,249 @@
+[[rest-openapi-component]]
+= REST OpenApi Component
+= REST OpenApi Component
+*Since Camel 3.1*
+
+
+*Since Camel 3.1*
+
+// HEADER START
+*Only producer is supported*
+// HEADER END
+
+The REST OpenApi* configures rest producers from
+https://www.openapis.org/[OpenApi] (Open API) specification document and
+delegates to a component implementing the _RestProducerFactory_
+interface. Currently known working components are:
+
+* xref:http-component.adoc[http]
+* xref:netty-http-component.adoc[netty-http]
+* xref:undertow-component.adoc[undertow]
+
+Maven users will need to add the following dependency to their
+`pom.xml` for this component:
+
+[source,xml]
+------------------------------------------------------------
+<dependency>
+    <groupId>org.apache.camel</groupId>
+    <artifactId>camel-rest-openapi</artifactId>
+    <version>x.x.x</version>
+    <!-- use the same version as your Camel core version -->
+</dependency>
+------------------------------------------------------------
+
+== URI format
+
+[source,java]
+-------------------------------------------------------
+rest-openapi:[specificationPath#]operationId
+-------------------------------------------------------
+
+Where `operationId` is the ID of the operation in the OpenApi
+specification, and `specificationPath` is the path to the
+specification.
+If the `specificationPath` is not specified it defaults to
+`openapi.json`. The lookup mechanism uses Camels `ResourceHelper` to
+load the resource, which means that you can use CLASSPATH resources 
+(`classpath:my-specification.json`), files 
+(`file:/some/path.json`), the web 
+(`\http://api.example.com/openapi.json`) or reference a bean 
+(`ref:nameOfBean`) or use a method of a bean 
+(`bean:nameOfBean.methodName`) to get the specification resource,
+failing that OpenApi's own resource loading support.
+
+This component does not act as a HTTP client, it delegates that to
+another component mentioned above. The lookup mechanism searches for a
+single component that implements the _RestProducerFactory_ interface and
+uses that. If the CLASSPATH contains more than one, then the property
+`componentName` should be set to indicate which component to delegate
+to.
+
+Most of the configuration is taken from the OpenApi specification but
+the option exists to override those by specifying them on the component
+or on the endpoint. Typically you would just need to override the 
+`host` or `basePath` if those differ from the specification.
+
+NOTE: The `host` parameter should contain the absolute URI containing
+scheme, hostname and port number, for instance:
+`\https://api.example.com`
+
+With `componentName` you specify what component is used to perform the
+requests, this named component needs to be present in the Camel context
+and implement the required _RestProducerFactory_ interface -- as do the
+components listed at the top.
+
+If you do not specify the _componentName_ at either component or 
+endpoint level, CLASSPATH is searched for a suitable delegate. There 
+should be only one component present on the CLASSPATH that implements 
+the _RestProducerFactory_ interface for this to work.
+
+This component's endpoint URI is lenient which means that in addition
+to message headers you can specify REST operation's parameters as
+endpoint parameters, these will be constant for all subsequent
+invocations so it makes sense to use this feature only for parameters
+that are indeed constant for all invocations -- for example API version
+in path such as `/api/\{version\}/users/\{id\}`. 
+
+== Options
+
+// component options: START
+The REST OpenApi component supports 11 options, which are listed below.
+
+
+
+[width="100%",cols="2,5,^1,2",options="header"]
+|===
+| Name | Description | Default | Type
+| *basePath* (producer) | API basePath, for example /v2. Default is unset, if set overrides the value present in OpenApi specification. |  | String
+| *componentName* (producer) | Name of the Camel component that will perform the requests. The component must be present in Camel registry and it must implement RestProducerFactory service provider interface. If not set CLASSPATH is searched for single component that implements RestProducerFactory SPI. Can be overridden in endpoint configuration. |  | String
+| *consumes* (producer) | What payload type this component capable of consuming. Could be one type, like application/json or multiple types as application/json, application/xml; q=0.5 according to the RFC7231. This equates to the value of Accept HTTP header. If set overrides any value found in the OpenApi specification. Can be overridden in endpoint configuration |  | String
+| *host* (producer) | Scheme hostname and port to direct the HTTP requests to in the form of \https://hostname:port. Can be configured at the endpoint, component or in the corresponding REST configuration in the Camel Context. If you give this component a name (e.g. petstore) that REST configuration is consulted first, rest-openapi next, and global configuration last. If set overrides any value found in the OpenApi specification, RestConfiguration. Can be overridden in endpoint configuration. |  | String
+| *produces* (producer) | What payload type this component is producing. For example application/json according to the RFC7231. This equates to the value of Content-Type HTTP header. If set overrides any value present in the OpenApi specification. Can be overridden in endpoint configuration. |  | String
+| *specificationUri* (producer) | Path to the OpenApi specification file. The scheme, host base path are taken from this specification, but these can be overridden with properties on the component or endpoint level. If not given the component tries to load openapi.json resource. Note that the host defined on the component and endpoint of this Component should contain the scheme, hostname and optionally the port in the URI syntax (i.e. \https://api.example.com:8080). Can be overridden in endpoint configuration. | openapi.json | URI
+| *sslContextParameters* (security) | Customize TLS parameters used by the component. If not set defaults to the TLS parameters set in the Camel context |  | SSLContextParameters
+| *useGlobalSslContext Parameters* (security) | Enable usage of global SSL context parameters. | false | boolean
+| *basicPropertyBinding* (advanced) | Whether the component should use basic property binding (Camel 2.x) or the newer property binding with additional capabilities | false | boolean
+| *lazyStartProducer* (producer) | Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during starting and cause the route to fail being started. By deferring this startup to be lazy then the startup failure can be handled during routing messages via Camel's routing error handlers. Beware that when the first message is processed then creating and starting the producer may take a little time and prolong the total processing time of the processing. | false | boolean
+| *bridgeErrorHandler* (consumer) | Allows for bridging the consumer to the Camel routing Error Handler, which mean any exceptions occurred while the consumer is trying to pickup incoming messages, or the likes, will now be processed as a message and handled by the routing Error Handler. By default the consumer will use the org.apache.camel.spi.ExceptionHandler to deal with exceptions, that will be logged at WARN or ERROR level and ignored. | false | boolean
+|===
+// component options: END
+
+// endpoint options: START
+The REST OpenApi endpoint is configured using URI syntax:
+
+----
+rest-openapi:specificationUri#operationId
+----
+
+with the following path and query parameters:
+
+=== Path Parameters (2 parameters):
+
+
+[width="100%",cols="2,5,^1,2",options="header"]
+|===
+| Name | Description | Default | Type
+| *specificationUri* | Path to the OpenApi specification file. The scheme, host base path are taken from this specification, but these can be overridden with properties on the component or endpoint level. If not given the component tries to load openapi.json resource from the classpath. Note that the host defined on the component and endpoint of this Component should contain the scheme, hostname and optionally the port in the URI syntax (i.e. \http://api.example.com:8080). Overrides component configuration. The OpenApi specification can be loaded from different sources by prefixing with file: classpath: http: https:. Support for https is limited to using the JDK installed UrlHandler, and as such it can be cumbersome to setup TLS/SSL certificates for https (such as setting a number of javax.net.ssl JVM system properties). How to do that consult the JDK documentation for UrlHandler. | openapi.json | URI
+| *operationId* | *Required* ID of the operation from the OpenApi specification. |  | String
+|===
+
+
+=== Query Parameters (8 parameters):
+
+
+[width="100%",cols="2,5,^1,2",options="header"]
+|===
+| Name | Description | Default | Type
+| *basePath* (producer) | API basePath, for example /v2. Default is unset, if set overrides the value present in OpenApi specification and in the component configuration. |  | String
+| *componentName* (producer) | Name of the Camel component that will perform the requests. The component must be present in Camel registry and it must implement RestProducerFactory service provider interface. If not set CLASSPATH is searched for single component that implements RestProducerFactory SPI. Overrides component configuration. |  | String
+| *consumes* (producer) | What payload type this component capable of consuming. Could be one type, like application/json or multiple types as application/json, application/xml; q=0.5 according to the RFC7231. This equates to the value of Accept HTTP header. If set overrides any value found in the OpenApi specification and. in the component configuration |  | String
+| *host* (producer) | Scheme hostname and port to direct the HTTP requests to in the form of \https://hostname:port. Can be configured at the endpoint, component or in the corresponding REST configuration in the Camel Context. If you give this component a name (e.g. petstore) that REST configuration is consulted first, rest-openapi next, and global configuration last. If set overrides any value found in the OpenApi specification, RestConfiguration. Overrides all other configuration. |  | String
+| *lazyStartProducer* (producer) | Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during starting and cause the route to fail being started. By deferring this startup to be lazy then the startup failure can be handled during routing messages via Camel's routing error handlers. Beware that when the first message is processed then creating and starting the producer may take a little time and prolong the total processing time of the processing. | false | boolean
+| *produces* (producer) | What payload type this component is producing. For example application/json according to the RFC7231. This equates to the value of Content-Type HTTP header. If set overrides any value present in the OpenApi specification. Overrides all other configuration. |  | String
+| *basicPropertyBinding* (advanced) | Whether the endpoint should use basic property binding (Camel 2.x) or the newer property binding with additional capabilities | false | boolean
+| *synchronous* (advanced) | Sets whether synchronous processing should be strictly used, or Camel is allowed to use asynchronous processing (if supported). | false | boolean
+|===
+// endpoint options: END
+// spring-boot-auto-configure options: START
+== Spring Boot Auto-Configuration
+
+When using Spring Boot make sure to use the following Maven dependency to have support for auto configuration:
+
+[source,xml]
+----
+<dependency>
+  <groupId>org.apache.camel</groupId>
+  <artifactId>camel-rest-openapi-starter</artifactId>
+  <version>x.x.x</version>
+  <!-- use the same version as your Camel core version -->
+</dependency>
+----
+
+
+The component supports 10 options, which are listed below.
+
+
+
+[width="100%",cols="2,5,^1,2",options="header"]
+|===
+| Name | Description | Default | Type
+| *camel.component.rest-openapi.base-path* | API basePath, for example /v2. Default is unset, if set overrides the value present in OpenApi specification. |  | String
+| *camel.component.rest-openapi.basic-property-binding* | Whether the component should use basic property binding (Camel 2.x) or the newer property binding with additional capabilities | false | Boolean
+| *camel.component.rest-openapi.component-name* | Name of the Camel component that will perform the requests. The component must be present in Camel registry and it must implement RestProducerFactory service provider interface. If not set CLASSPATH is searched for single component that implements RestProducerFactory SPI. Can be overridden in endpoint configuration. |  | String
+| *camel.component.rest-openapi.consumes* | What payload type this component capable of consuming. Could be one type, like application/json or multiple types as application/json, application/xml; q=0.5 according to the RFC7231. This equates to the value of Accept HTTP header. If set overrides any value found in the OpenApi specification. Can be overridden in endpoint configuration |  | String
+| *camel.component.rest-openapi.enabled* | Enable rest-openapi component | true | Boolean
+| *camel.component.rest-openapi.host* | Scheme hostname and port to direct the HTTP requests to in the form of \https://hostname:port. Can be configured at the endpoint, component or in the corresponding REST configuration in the Camel Context. If you give this component a name (e.g. petstore) that REST configuration is consulted first, rest-openapi next, and global configuration last. If set overrides any value found in the OpenApi specification, RestConfiguration. Can be overridden in endpoint configuration. |  | String
+| *camel.component.rest-openapi.produces* | What payload type this component is producing. For example application/json according to the RFC7231. This equates to the value of Content-Type HTTP header. If set overrides any value present in the OpenApi specification. Can be overridden in endpoint configuration. |  | String
+| *camel.component.rest-openapi.specification-uri* | Path to the OpenApi specification file. The scheme, host base path are taken from this specification, but these can be overridden with properties on the component or endpoint level. If not given the component tries to load openapi.json resource. Note that the host defined on the component and endpoint of this Component should contain the scheme, hostname and optionally the port in the URI syntax (i.e. \https://api.example.com:8080). Can be overridden in endpoint configuration. |  | URI
+| *camel.component.rest-openapi.ssl-context-parameters* | Customize TLS parameters used by the component. If not set defaults to the TLS parameters set in the Camel context. The option is a org.apache.camel.support.jsse.SSLContextParameters type. |  | String
+| *camel.component.rest-openapi.use-global-ssl-context-parameters* | Enable usage of global SSL context parameters. | false | Boolean
+|===
+// spring-boot-auto-configure options: END
+
+
+== Example: PetStore
+
+Checkout the example in the `camel-example-rest-openapi` project in
+the `examples` directory.
+
+For example if you wanted to use the 
+https://petstore3.swagger.io/api/v3/[_PetStore_] provided REST API simply
+reference the specification URI and desired operation id from the
+OpenApi specification or download the specification and store it as
+`openapi.json` (in the root) of CLASSPATH that way it will be 
+automaticaly used. Let's use the xref:undertow-component.adoc[Undertow]
+component to perform all the requests and Camels excelent support for 
+Spring Boot.
+
+Here are our dependencies defined in Maven POM file:
+
+[source,xml]
+----
+<dependency>
+  <groupId>org.apache.camel</groupId>
+  <artifactId>camel-undertow-starter</artifactId>
+</dependency>
+
+<dependency>
+  <groupId>org.apache.camel</groupId>
+  <artifactId>camel-rest-openapi-starter</artifactId>
+</dependency>
+----
+
+Start by defining the _Undertow_ component and the
+_RestOpenApiComponent_:
+
+[source,java]
+----
+@Bean
+public Component petstore(CamelContext camelContext, UndertowComponent undertow) {
+    RestOpenApiComponent petstore = new RestOpenApiComponent(camelContext);
+    petstore.setSpecificationUri("https://petstore3.swagger.io/api/v3/openapi.json");
+    petstore.setDelegate(undertow);
+
+    return petstore;
+}
+----
+
+NOTE: Support in Camel for Spring Boot will auto create the 
+`UndertowComponent` Spring bean, and you can configure it using
+`application.properties` (or `application.yml`) using prefix
+`camel.component.undertow.`. We are defining the `petstore`
+component here in order to have a named component in the Camel context
+that we can use to interact with the PetStore REST API, if this is the
+only `rest-openapi` component used we might configure it in the same
+manner (using `application.properties`).
+
+Now in our application we can simply use the `ProducerTemplate` to
+invoke PetStore REST methods:
+
+[source,java]
+----
+@Autowired
+ProducerTemplate template;
+
+String getPetJsonById(int petId) {
+    return template.requestBodyAndHeaders("petstore:getPetById", null, "petId", petId);
+}
+----
diff --git a/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenApiComponent.java b/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenApiComponent.java
new file mode 100644
index 0000000..5efb97d
--- /dev/null
+++ b/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenApiComponent.java
@@ -0,0 +1,218 @@
+/*
+ * 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.rest.openapi;
+
+import java.net.URI;
+import java.util.Map;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Endpoint;
+import org.apache.camel.SSLContextParametersAware;
+import org.apache.camel.spi.Metadata;
+import org.apache.camel.spi.RestProducerFactory;
+import org.apache.camel.spi.annotations.Component;
+import org.apache.camel.support.DefaultComponent;
+import org.apache.camel.support.jsse.SSLContextParameters;
+
+import static org.apache.camel.component.rest.openapi.RestOpenApiHelper.isHostParam;
+import static org.apache.camel.component.rest.openapi.RestOpenApiHelper.isMediaRange;
+import static org.apache.camel.util.ObjectHelper.notNull;
+import static org.apache.camel.util.StringHelper.notEmpty;
+
+/**
+ * An awesome REST component backed by OpenApi specifications. Creates endpoints
+ * that connect to REST APIs defined by OpenApi specification. This component
+ * delegates to other {@link RestProducerFactory} components to act as REST
+ * clients, but it configures them from OpenApi specification. Client needs to
+ * point to operation that it wants to invoke via REST, provide any additional
+ * HTTP headers as headers in the Camel message, and any payload as the body of
+ * the incoming message.
+ * <p>
+ * Example usage using Java DSL:
+ * <p>
+ *
+ * <pre>
+ * from(...).to("rest-openapi:https://petstore3.swagger.io/api/v3/openapi.json#getPetById")
+ * </pre>
+ *
+ * This relies on only one {@link RestProducerFactory} component being available
+ * to Camel, you can use specific, for instance preconfigured component by using
+ * the {@code componentName} endpoint property. For example using Undertow
+ * component in Java DSL:
+ * <p>
+ *
+ * <pre>
+ * Component undertow = new UndertowComponent();
+ * undertow.setSslContextParameters(...);
+ * //...
+ * camelContext.addComponent("myUndertow", undertow);
+ *
+ * from(...).to("rest-openapi:https://petstore3.swagger.io/api/v3/openapi.json#getPetById?componentName=myUndertow")
+ * </pre>
+ *
+ * The most concise way of using this component would be to define it in the
+ * Camel context under a meaningful name, for example:
+ *
+ * <pre>
+ * Component petstore = new RestOpenApiComponent();
+ * petstore.setSpecificationUri("https://petstore3.swagger.io/api/v3/openapi.json");
+ * petstore.setComponentName("undertow");
+ * //...
+ * camelContext.addComponent("petstore", petstore);
+ *
+ * from(...).to("petstore:getPetById")
+ * </pre>
+ */
+@Component("rest-openapi")
+public final class RestOpenApiComponent extends DefaultComponent implements SSLContextParametersAware {
+    public static final String DEFAULT_BASE_PATH = "/";
+
+    static final URI DEFAULT_SPECIFICATION_URI = URI.create(RestOpenApiComponent.DEFAULT_SPECIFICATION_URI_STR);
+
+    static final String DEFAULT_SPECIFICATION_URI_STR = "openapi.json";
+
+    @Metadata(
+        description = "API basePath, for example \"`/v2`\". Default is unset, if set overrides the value present in OpenApi specification.",
+        defaultValue = "", label = "producer")
+    private String basePath = "";
+
+    @Metadata(description = "Name of the Camel component that will perform the requests. The component must be present"
+        + " in Camel registry and it must implement RestProducerFactory service provider interface. If not set"
+        + " CLASSPATH is searched for single component that implements RestProducerFactory SPI. Can be overridden in"
+        + " endpoint configuration.", label = "producer", required = false)
+    private String componentName;
+
+    @Metadata(
+        description = "What payload type this component capable of consuming. Could be one type, like `application/json`"
+            + " or multiple types as `application/json, application/xml; q=0.5` according to the RFC7231. This equates"
+            + " to the value of `Accept` HTTP header. If set overrides any value found in the OpenApi specification."
+            + " Can be overridden in endpoint configuration",
+        label = "producer")
+    private String consumes;
+
+    @Metadata(description = "Scheme hostname and port to direct the HTTP requests to in the form of"
+        + " `http[s]://hostname[:port]`. Can be configured at the endpoint, component or in the corresponding"
+        + " REST configuration in the Camel Context. If you give this component a name (e.g. `petstore`) that"
+        + " REST configuration is consulted first, `rest-openapi` next, and global configuration last. If set"
+        + " overrides any value found in the OpenApi specification, RestConfiguration. Can be overridden in endpoint"
+        + " configuration.", label = "producer")
+    private String host;
+
+    @Metadata(
+        description = "What payload type this component is producing. For example `application/json`"
+            + " according to the RFC7231. This equates to the value of `Content-Type` HTTP header. If set overrides"
+            + " any value present in the OpenApi specification. Can be overridden in endpoint configuration.",
+        label = "producer")
+    private String produces;
+
+    @Metadata(description = "Path to the OpenApi specification file. The scheme, host base path are taken from this"
+        + " specification, but these can be overridden with properties on the component or endpoint level. If not"
+        + " given the component tries to load `openapi.json` resource. Note that the `host` defined on the"
+        + " component and endpoint of this Component should contain the scheme, hostname and optionally the"
+        + " port in the URI syntax (i.e. `https://api.example.com:8080`). Can be overridden in endpoint"
+        + " configuration.", defaultValue = DEFAULT_SPECIFICATION_URI_STR, label = "producer")
+    private URI specificationUri;
+
+    @Metadata(description = "Customize TLS parameters used by the component. If not set defaults to the TLS parameters"
+        + " set in the Camel context ", label = "security")
+    private SSLContextParameters sslContextParameters;
+
+    @Metadata(description = "Enable usage of global SSL context parameters.", label = "security",
+        defaultValue = "false")
+    private boolean useGlobalSslContextParameters;
+
+    public RestOpenApiComponent() {
+    }
+
+    public RestOpenApiComponent(final CamelContext context) {
+        super(context);
+    }
+
+    @Override
+    protected Endpoint createEndpoint(final String uri, final String remaining, final Map<String, Object> parameters) throws Exception {
+        Endpoint endpoint = new RestOpenApiEndpoint(uri, remaining, this, parameters);
+        setProperties(endpoint, parameters);
+        return endpoint;
+    }
+
+    public String getBasePath() {
+        return basePath;
+    }
+
+    public String getComponentName() {
+        return componentName;
+    }
+
+    public String getConsumes() {
+        return consumes;
+    }
+
+    public String getHost() {
+        return host;
+    }
+
+    public String getProduces() {
+        return produces;
+    }
+
+    public URI getSpecificationUri() {
+        return specificationUri;
+    }
+
+    public SSLContextParameters getSslContextParameters() {
+        return sslContextParameters;
+    }
+
+    @Override
+    public boolean isUseGlobalSslContextParameters() {
+        return useGlobalSslContextParameters;
+    }
+
+    public void setBasePath(final String basePath) {
+        this.basePath = notEmpty(basePath, "basePath");
+    }
+
+    public void setComponentName(final String componentName) {
+        this.componentName = notEmpty(componentName, "componentName");
+    }
+
+    public void setConsumes(final String consumes) {
+        this.consumes = isMediaRange(consumes, "consumes");
+    }
+
+    public void setHost(final String host) {
+        this.host = isHostParam(host);
+    }
+
+    public void setProduces(final String produces) {
+        this.produces = isMediaRange(produces, "produces");
+    }
+
+    public void setSpecificationUri(final URI specificationUri) {
+        this.specificationUri = notNull(specificationUri, "specificationUri");
+    }
+
+    public void setSslContextParameters(final SSLContextParameters sslContextParameters) {
+        this.sslContextParameters = sslContextParameters;
+    }
+
+    @Override
+    public void setUseGlobalSslContextParameters(final boolean useGlobalSslContextParameters) {
+        this.useGlobalSslContextParameters = useGlobalSslContextParameters;
+    }
+
+}
diff --git a/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenApiEndpoint.java b/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenApiEndpoint.java
new file mode 100644
index 0000000..91b0f83
--- /dev/null
+++ b/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenApiEndpoint.java
@@ -0,0 +1,790 @@
+/*
+ * 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.rest.openapi;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.apicurio.datamodels.Library;
+import io.apicurio.datamodels.core.models.Document;
+import io.apicurio.datamodels.core.models.common.SecurityRequirement;
+import io.apicurio.datamodels.openapi.models.OasDocument;
+import io.apicurio.datamodels.openapi.models.OasOperation;
+import io.apicurio.datamodels.openapi.models.OasParameter;
+import io.apicurio.datamodels.openapi.models.OasPathItem;
+import io.apicurio.datamodels.openapi.models.OasPaths;
+import io.apicurio.datamodels.openapi.models.OasResponse;
+import io.apicurio.datamodels.openapi.v2.models.Oas20Document;
+import io.apicurio.datamodels.openapi.v2.models.Oas20Operation;
+import io.apicurio.datamodels.openapi.v2.models.Oas20Parameter;
+import io.apicurio.datamodels.openapi.v2.models.Oas20SecurityDefinitions;
+import io.apicurio.datamodels.openapi.v2.models.Oas20SecurityScheme;
+import io.apicurio.datamodels.openapi.v3.models.Oas30Document;
+import io.apicurio.datamodels.openapi.v3.models.Oas30Operation;
+import io.apicurio.datamodels.openapi.v3.models.Oas30Parameter;
+import io.apicurio.datamodels.openapi.v3.models.Oas30Response;
+import io.apicurio.datamodels.openapi.v3.models.Oas30SecurityScheme;
+import org.apache.camel.CamelContext;
+import org.apache.camel.Consumer;
+import org.apache.camel.Endpoint;
+import org.apache.camel.ExchangePattern;
+import org.apache.camel.Processor;
+import org.apache.camel.Producer;
+import org.apache.camel.spi.Metadata;
+import org.apache.camel.spi.RestConfiguration;
+import org.apache.camel.spi.UriEndpoint;
+import org.apache.camel.spi.UriParam;
+import org.apache.camel.spi.UriPath;
+import org.apache.camel.support.DefaultEndpoint;
+import org.apache.camel.support.ResourceHelper;
+import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.StringHelper;
+import org.apache.camel.util.UnsafeUriCharactersEncoder;
+
+import static java.util.Optional.ofNullable;
+import static org.apache.camel.component.rest.openapi.RestOpenApiHelper.isHostParam;
+import static org.apache.camel.component.rest.openapi.RestOpenApiHelper.isMediaRange;
+import static org.apache.camel.util.ObjectHelper.isNotEmpty;
+import static org.apache.camel.util.ObjectHelper.notNull;
+import static org.apache.camel.util.StringHelper.after;
+import static org.apache.camel.util.StringHelper.before;
+import static org.apache.camel.util.StringHelper.notEmpty;
+
+/**
+ * An awesome REST endpoint backed by OpenApi specifications.
+ */
+@UriEndpoint(firstVersion = "3.1.0", scheme = "rest-openapi", title = "REST OpenApi",
+    syntax = "rest-openapi:specificationUri#operationId", label = "rest,openapi,http", producerOnly = true)
+public final class RestOpenApiEndpoint extends DefaultEndpoint {
+
+    /**
+     * Remaining parameters specified in the Endpoint URI.
+     */
+    Map<String, Object> parameters = Collections.emptyMap();
+
+    /** The name of the Camel component, be it `rest-openapi` or `petstore` */
+    private String assignedComponentName;
+
+    @UriParam(
+        description = "API basePath, for example \"`/v2`\". Default is unset, if set overrides the value present in"
+            + " OpenApi specification and in the component configuration.",
+        defaultValue = "", label = "producer")
+    private String basePath;
+
+    @UriParam(description = "Name of the Camel component that will perform the requests. The component must be present"
+        + " in Camel registry and it must implement RestProducerFactory service provider interface. If not set"
+        + " CLASSPATH is searched for single component that implements RestProducerFactory SPI. Overrides"
+        + " component configuration.", label = "producer")
+    private String componentName;
+
+    @UriParam(
+        description = "What payload type this component capable of consuming. Could be one type, like `application/json`"
+            + " or multiple types as `application/json, application/xml; q=0.5` according to the RFC7231. This equates"
+            + " to the value of `Accept` HTTP header. If set overrides any value found in the OpenApi specification and."
+            + " in the component configuration",
+        label = "producer")
+    private String consumes;
+
+    @UriParam(description = "Scheme hostname and port to direct the HTTP requests to in the form of"
+        + " `http[s]://hostname[:port]`. Can be configured at the endpoint, component or in the corresponding"
+        + " REST configuration in the Camel Context. If you give this component a name (e.g. `petstore`) that"
+        + " REST configuration is consulted first, `rest-openapi` next, and global configuration last. If set"
+        + " overrides any value found in the OpenApi specification, RestConfiguration. Overrides all other "
+        + " configuration.", label = "producer")
+    private String host;
+
+    @UriPath(description = "ID of the operation from the OpenApi specification.", label = "producer")
+    @Metadata(required = true)
+    private String operationId;
+
+    @UriParam(description = "What payload type this component is producing. For example `application/json`"
+        + " according to the RFC7231. This equates to the value of `Content-Type` HTTP header. If set overrides"
+        + " any value present in the OpenApi specification. Overrides all other configuration.", label = "producer")
+    private String produces;
+
+    @UriPath(description = "Path to the OpenApi specification file. The scheme, host base path are taken from this"
+        + " specification, but these can be overridden with properties on the component or endpoint level. If not"
+        + " given the component tries to load `openapi.json` resource from the classpath. Note that the `host` defined on the"
+        + " component and endpoint of this Component should contain the scheme, hostname and optionally the"
+        + " port in the URI syntax (i.e. `http://api.example.com:8080`). Overrides component configuration."
+        + " The OpenApi specification can be loaded from different sources by prefixing with file: classpath: http: https:."
+        + " Support for https is limited to using the JDK installed UrlHandler, and as such it can be cumbersome to setup"
+        + " TLS/SSL certificates for https (such as setting a number of javax.net.ssl JVM system properties)."
+        + " How to do that consult the JDK documentation for UrlHandler.",
+        defaultValue = RestOpenApiComponent.DEFAULT_SPECIFICATION_URI_STR,
+        defaultValueNote = "By default loads `openapi.json` file", label = "producer")
+    private URI specificationUri = RestOpenApiComponent.DEFAULT_SPECIFICATION_URI;
+
+    public RestOpenApiEndpoint() {
+        // help tooling instantiate endpoint
+    }
+
+    public RestOpenApiEndpoint(final String uri, final String remaining, final RestOpenApiComponent component,
+        final Map<String, Object> parameters) {
+        super(notEmpty(uri, "uri"), notNull(component, "component"));
+        this.parameters = parameters;
+
+        assignedComponentName = before(uri, ":");
+
+        final URI componentSpecificationUri = component.getSpecificationUri();
+
+        specificationUri = before(remaining, "#", StringHelper::trimToNull).map(URI::create)
+            .orElse(ofNullable(componentSpecificationUri).orElse(RestOpenApiComponent.DEFAULT_SPECIFICATION_URI));
+
+        operationId = ofNullable(after(remaining, "#")).orElse(remaining);
+
+        setExchangePattern(ExchangePattern.InOut);
+    }
+
+    @Override
+    public Consumer createConsumer(final Processor processor) throws Exception {
+        throw new UnsupportedOperationException("Consumer not supported");
+    }
+
+    @Override
+    public Producer createProducer() throws Exception {
+        final CamelContext camelContext = getCamelContext();
+        final Document openapiDoc = loadSpecificationFrom(camelContext, specificationUri);
+
+        final OasPaths paths = ((OasDocument)openapiDoc).paths;
+
+        for (final OasPathItem path : paths.getItems()) {
+            final Optional<Entry<HttpMethod, OasOperation>> maybeOperationEntry = getOperationMap(path).entrySet()
+                .stream().filter(operationEntry -> operationId.equals(operationEntry.getValue().operationId))
+                .findAny();
+
+            if (maybeOperationEntry.isPresent()) {
+                final Entry<HttpMethod, OasOperation> operationEntry = maybeOperationEntry.get();
+
+                final OasOperation operation = operationEntry.getValue();
+                Map<String, OasParameter> pathParameters = null;
+                if (operation.getParameters() != null) {
+                    pathParameters = operation.getParameters().stream()
+                        .filter(p -> "path".equals(p.in))
+                        .collect(Collectors.toMap(OasParameter::getName, Function.identity()));
+                } else {
+                    pathParameters = new HashMap<String, OasParameter>();
+                }
+                final String uriTemplate = resolveUri(path.getPath(), pathParameters);
+
+                final HttpMethod httpMethod = operationEntry.getKey();
+                final String method = httpMethod.name();
+
+                return createProducerFor(openapiDoc, operation, method, uriTemplate);
+            }
+        }
+
+        
+     
+        String supportedOperations = paths.getItems().stream().flatMap(p -> getOperationMap(p).values().stream())
+            .map(p -> p.operationId).collect(Collectors.joining(", "));
+        throw new IllegalArgumentException("The specified operation with ID: `" + operationId
+            + "` cannot be found in the OpenApi specification loaded from `" + specificationUri
+            + "`. Operations defined in the specification are: " + supportedOperations);
+    }
+
+    
+    private Map<HttpMethod, OasOperation> getOperationMap(OasPathItem path) {
+        Map<HttpMethod, OasOperation> result = new LinkedHashMap<HttpMethod, OasOperation>();
+
+        if (path.get != null) {
+            result.put(HttpMethod.GET, path.get);
+        }
+        if (path.put != null) {
+            result.put(HttpMethod.PUT, path.put);
+        }
+        if (path.post != null) {
+            result.put(HttpMethod.POST, path.post);
+        }
+        if (path.delete != null) {
+            result.put(HttpMethod.DELETE, path.delete);
+        }
+        if (path.patch != null) {
+            result.put(HttpMethod.PATCH, path.patch);
+        }
+        if (path.head != null) {
+            result.put(HttpMethod.HEAD, path.head);
+        }
+        if (path.options != null) {
+            result.put(HttpMethod.OPTIONS, path.options);
+        }
+
+        return result;
+    }
+
+    public String getBasePath() {
+        return basePath;
+    }
+
+    public String getComponentName() {
+        return componentName;
+    }
+
+    public String getConsumes() {
+        return consumes;
+    }
+
+    public String getHost() {
+        return host;
+    }
+
+    public String getOperationId() {
+        return operationId;
+    }
+
+    public String getProduces() {
+        return produces;
+    }
+
+    public URI getSpecificationUri() {
+        return specificationUri;
+    }
+
+    @Override
+    public boolean isLenientProperties() {
+        return true;
+    }
+
+    public void setBasePath(final String basePath) {
+        this.basePath = notEmpty(basePath, "basePath");
+    }
+
+    public void setComponentName(final String componentName) {
+        this.componentName = notEmpty(componentName, "componentName");
+    }
+
+    public void setConsumes(final String consumes) {
+        this.consumes = isMediaRange(consumes, "consumes");
+    }
+
+    public void setHost(final String host) {
+        this.host = isHostParam(host);
+    }
+
+    public void setOperationId(final String operationId) {
+        this.operationId = notEmpty(operationId, "operationId");
+    }
+
+    public void setProduces(final String produces) {
+        this.produces = isMediaRange(produces, "produces");
+    }
+
+    public void setSpecificationUri(final URI specificationUri) {
+        this.specificationUri = notNull(specificationUri, "specificationUri");
+    }
+
+    RestOpenApiComponent component() {
+        return (RestOpenApiComponent) getComponent();
+    }
+
+    Producer createProducerFor(final Document openapi, final OasOperation operation, final String method,
+        final String uriTemplate) throws Exception {
+        final String basePath = determineBasePath(openapi);
+
+        final StringBuilder componentEndpointUri = new StringBuilder(200).append("rest:").append(method).append(":")
+            .append(basePath).append(":").append(uriTemplate);
+
+        final CamelContext camelContext = getCamelContext();
+
+        final Endpoint endpoint = camelContext.getEndpoint(componentEndpointUri.toString());
+
+        Map<String, Object> params = determineEndpointParameters(openapi, operation);
+        boolean hasHost = params.containsKey("host");
+        if (endpoint instanceof DefaultEndpoint) {
+            // let the rest endpoint configure itself
+            DefaultEndpoint de = (DefaultEndpoint) endpoint;
+            de.setProperties(endpoint, params);
+        }
+
+        // if there is a host then we should use this hardcoded host instead of any Header that may have an existing
+        // Host header from some other HTTP input, and if so then lets remove it
+        return new RestOpenApiProducer(endpoint.createAsyncProducer(), hasHost);
+    }
+
+    String determineBasePath(final Document openapi) {
+        if (isNotEmpty(basePath)) {
+            return basePath;
+        }
+
+        final String componentBasePath = component().getBasePath();
+        if (isNotEmpty(componentBasePath)) {
+            return componentBasePath;
+        }
+
+        final String specificationBasePath;
+        if (openapi instanceof Oas20Document) {
+            specificationBasePath = ((Oas20Document)openapi).basePath;
+        } else {
+            specificationBasePath = "";
+        } 
+        if (isNotEmpty(specificationBasePath)) {
+            return specificationBasePath;
+        }
+
+        final CamelContext camelContext = getCamelContext();
+        final RestConfiguration specificConfiguration = camelContext.getRestConfiguration(assignedComponentName, false);
+        if (specificConfiguration != null && isNotEmpty(specificConfiguration.getContextPath())) {
+            return specificConfiguration.getContextPath();
+        }
+
+        final RestConfiguration restConfiguration = camelContext.getRestConfiguration("rest-openapi", true);
+        final String restConfigurationBasePath = restConfiguration.getContextPath();
+        if (isNotEmpty(restConfigurationBasePath)) {
+            return restConfigurationBasePath;
+        }
+
+        return RestOpenApiComponent.DEFAULT_BASE_PATH;
+    }
+
+    String determineComponentName() {
+        return Optional.ofNullable(componentName).orElse(component().getComponentName());
+    }
+
+    Map<String, Object> determineEndpointParameters(final Document openapi, final OasOperation operation) {
+        final Map<String, Object> parameters = new HashMap<>();
+
+        final String componentName = determineComponentName();
+        if (componentName != null) {
+            parameters.put("producerComponentName", componentName);
+        }
+
+        final String host = determineHost(openapi);
+        if (host != null) {
+            parameters.put("host", host);
+        }
+
+        final RestOpenApiComponent component = component();
+
+        // what we consume is what the API defined by OpenApi specification
+        // produces
+        List<String> specificationLevelConsumers = new ArrayList<String>();
+        if (openapi instanceof Oas20Document) {
+            specificationLevelConsumers = ((Oas20Document)openapi).produces;
+        } 
+        List<String> operationLevelConsumers = new ArrayList<String>();
+        if (operation instanceof Oas20Operation) {
+            operationLevelConsumers = ((Oas20Operation)operation).produces;
+        } else if (operation instanceof Oas30Operation) {
+            Oas30Operation oas30Operation = (Oas30Operation)operation;
+            if (oas30Operation.responses != null) {
+                for (OasResponse response : oas30Operation.responses.getResponses()) {
+                    Oas30Response oas30Response = (Oas30Response)response;
+                    for (String ct : oas30Response.content.keySet()) {
+                        operationLevelConsumers.add(ct);
+                    }
+                }
+            }
+        }
+        final String determinedConsumes = determineOption(specificationLevelConsumers, operationLevelConsumers,
+            component.getConsumes(), consumes);
+
+        if (isNotEmpty(determinedConsumes)) {
+            parameters.put("consumes", determinedConsumes);
+        }
+
+        // what we produce is what the API defined by OpenApi specification
+        // consumes
+        
+        List<String> specificationLevelProducers = new ArrayList<String>();
+        if (openapi instanceof Oas20Document) {
+            specificationLevelProducers = ((Oas20Document)openapi).consumes;
+        } 
+        List<String> operationLevelProducers = new ArrayList<String>();
+        if (operation instanceof Oas20Operation) {
+            operationLevelProducers = ((Oas20Operation)operation).consumes;
+        } else if (operation instanceof Oas30Operation) {
+            Oas30Operation oas30Operation = (Oas30Operation)operation;
+            if (oas30Operation.requestBody != null 
+                && oas30Operation.requestBody.content != null) { 
+                for (String ct : oas30Operation.requestBody.content.keySet()) {
+                    operationLevelProducers.add(ct);
+                }
+            }
+                
+        }
+        
+        final String determinedProducers = determineOption(specificationLevelProducers, operationLevelProducers,
+            component.getProduces(), produces);
+
+        if (isNotEmpty(determinedProducers)) {
+            parameters.put("produces", determinedProducers);
+        }
+
+        final String queryParameters = determineQueryParameters(openapi, operation).map(this::queryParameter)
+            .collect(Collectors.joining("&"));
+        if (isNotEmpty(queryParameters)) {
+            parameters.put("queryParameters", queryParameters);
+        }
+
+        // pass properties that might be applied if the delegate component is
+        // created, i.e. if it's not
+        // present in the Camel Context already
+        final Map<String, Object> componentParameters = new HashMap<>();
+
+        if (component.isUseGlobalSslContextParameters()) {
+            // by default it's false
+            componentParameters.put("useGlobalSslContextParameters", component.isUseGlobalSslContextParameters());
+        }
+        if (component.getSslContextParameters() != null) {
+            componentParameters.put("sslContextParameters", component.getSslContextParameters());
+        }
+
+        if (!componentParameters.isEmpty()) {
+            final Map<Object, Object> nestedParameters = new HashMap<>();
+            nestedParameters.put("component", componentParameters);
+
+            // we're trying to set RestEndpoint.parameters['component']
+            parameters.put("parameters", nestedParameters);
+        }
+
+        return parameters;
+    }
+
+    String determineHost(final Document openapi) {
+        if (isNotEmpty(host)) {
+            return host;
+        }
+
+        final String componentHost = component().getHost();
+        if (isNotEmpty(componentHost)) {
+            return componentHost;
+        }
+
+        
+        
+        if (openapi instanceof Oas20Document) {
+            final String openapiScheme = pickBestScheme(specificationUri.getScheme(), ((Oas20Document)openapi).schemes);
+            final String openapiHost = ((Oas20Document)openapi).host;
+
+            if (isNotEmpty(openapiScheme) && isNotEmpty(openapiHost)) {
+                return openapiScheme + "://" + openapiHost;
+            }
+        } else if (openapi instanceof Oas30Document) {
+            //In OpenApi 3.0, scheme/host are in servers url section
+            //But there could be many servers url(like one for production and one for test)
+            //Use first one here
+            Oas30Document oas30Document = (Oas30Document)openapi;
+            if (oas30Document.getServers() != null 
+                && oas30Document.getServers().get(0) != null) {
+                try {
+                    URL serverUrl = new URL(oas30Document.getServers().get(0).url);
+                    final String openapiScheme = serverUrl.getProtocol();
+                    final String openapiHost = serverUrl.getHost();
+                    if (isNotEmpty(openapiScheme) && isNotEmpty(openapiHost)) {
+                        return openapiScheme + "://" + openapiHost;
+                    }
+                } catch (MalformedURLException e) {
+                    throw new IllegalStateException(e);
+                }
+            }
+        }
+
+        final CamelContext camelContext = getCamelContext();
+
+        final RestConfiguration specificRestConfiguration = camelContext.getRestConfiguration(assignedComponentName,
+            false);
+        final String specificConfigurationHost = hostFrom(specificRestConfiguration);
+        if (specificConfigurationHost != null) {
+            return specificConfigurationHost;
+        }
+
+        final RestConfiguration componentRestConfiguration = camelContext.getRestConfiguration("rest-openapi", false);
+        final String componentConfigurationHost = hostFrom(componentRestConfiguration);
+        if (componentConfigurationHost != null) {
+            return componentConfigurationHost;
+        }
+
+        final RestConfiguration globalRestConfiguration = camelContext.getRestConfiguration();
+        final String globalConfigurationHost = hostFrom(globalRestConfiguration);
+        if (globalConfigurationHost != null) {
+            return globalConfigurationHost;
+        }
+
+        final String specificationScheme = specificationUri.getScheme();
+        if (specificationUri.isAbsolute() && specificationScheme.toLowerCase().startsWith("http")) {
+            try {
+                return new URI(specificationUri.getScheme(), specificationUri.getUserInfo(), specificationUri.getHost(),
+                    specificationUri.getPort(), null, null, null).toString();
+            } catch (final URISyntaxException e) {
+                throw new IllegalStateException("Unable to create a new URI from: " + specificationUri, e);
+            }
+        }
+
+        final boolean areTheSame = "rest-openapi".equals(assignedComponentName);
+
+        throw new IllegalStateException("Unable to determine destination host for requests. The OpenApi specification"
+            + " does not specify `scheme` and `host` parameters, the specification URI is not absolute with `http` or"
+            + " `https` scheme, and no RestConfigurations configured with `scheme`, `host` and `port` were found for `"
+            + (areTheSame ? "rest-openapi` component" : assignedComponentName + "` or `rest-openapi` components")
+            + " and there is no global RestConfiguration with those properties");
+    }
+
+    String literalPathParameterValue(final OasParameter parameter) {
+        final String name = parameter.getName();
+
+        final String valueStr = String.valueOf(parameters.get(name));
+        final String encoded = UnsafeUriCharactersEncoder.encode(valueStr);
+
+        return encoded;
+    }
+
+    String literalQueryParameterValue(final OasParameter parameter) {
+        final String name = parameter.getName();
+
+        final String valueStr = String.valueOf(parameters.get(name));
+        final String encoded = UnsafeUriCharactersEncoder.encode(valueStr);
+
+        return name + "=" + encoded;
+    }
+
+    String queryParameter(final OasParameter parameter) {
+        final String name = parameter.getName();
+        if (ObjectHelper.isEmpty(name)) {
+            return "";
+        }
+
+        if (parameters.containsKey(name)) {
+            return literalQueryParameterValue(parameter);
+        }
+
+        return queryParameterExpression(parameter);
+    }
+
+    String resolveUri(final String uriTemplate, final Map<String, OasParameter> pathParameters) {
+        if (pathParameters.isEmpty()) {
+            return uriTemplate;
+        }
+
+        int start = uriTemplate.indexOf('{');
+
+        if (start == -1) {
+            return uriTemplate;
+        }
+
+        int pos = 0;
+        final StringBuilder resolved = new StringBuilder(uriTemplate.length() * 2);
+        while (start != -1) {
+            resolved.append(uriTemplate.substring(pos, start));
+
+            final int end = uriTemplate.indexOf('}', start);
+
+            final String name = uriTemplate.substring(start + 1, end);
+
+            if (parameters.containsKey(name)) {
+                final OasParameter parameter = pathParameters.get(name);
+                final Object value = literalPathParameterValue(parameter);
+                resolved.append(value);
+            } else {
+                resolved.append('{').append(name).append('}');
+            }
+
+            pos = end + 1;
+            start = uriTemplate.indexOf('{', pos);
+        }
+
+        if (pos < uriTemplate.length()) {
+            resolved.append(uriTemplate.substring(pos));
+        }
+
+        return resolved.toString();
+    }
+
+    static String determineOption(final List<String> specificationLevel, final List<String> operationLevel,
+        final String componentLevel, final String endpointLevel) {
+        if (isNotEmpty(endpointLevel)) {
+            return endpointLevel;
+        }
+
+        if (isNotEmpty(componentLevel)) {
+            return componentLevel;
+        }
+
+        if (operationLevel != null && !operationLevel.isEmpty()) {
+            return String.join(", ", operationLevel);
+        }
+
+        if (specificationLevel != null && !specificationLevel.isEmpty()) {
+            return String.join(", ", specificationLevel);
+        }
+
+        return null;
+    }
+
+    static Stream<OasParameter> determineQueryParameters(final Document openapi, final OasOperation operation) {
+        final List<SecurityRequirement> securityRequirements = operation.security;
+        final List<OasParameter> apiKeyQueryParameters = new ArrayList<>();
+        if (securityRequirements != null) {
+            if (openapi instanceof Oas20Document) {
+                Oas20Document oas20Document = (Oas20Document)openapi;
+                Oas20SecurityDefinitions securityDefinitions = oas20Document.securityDefinitions;
+                
+                for (final SecurityRequirement securityRequirement : securityRequirements) {
+                    for (final String securityRequirementName : securityRequirement.getSecurityRequirementNames()) {
+                        final Oas20SecurityScheme securitySchemeDefinition = securityDefinitions
+                            .getSecurityScheme(securityRequirementName);
+                        if (securitySchemeDefinition.in != null 
+                            && securitySchemeDefinition.in.equals("query")) {
+                            Oas20Parameter securityParameter = new Oas20Parameter(securitySchemeDefinition.name);
+                            securityParameter.required = true;
+                            securityParameter.type = "string";
+                            securityParameter.description = securitySchemeDefinition.description;
+                            apiKeyQueryParameters.add(securityParameter);
+                        }
+                        
+                    }
+                }
+            } else if (openapi instanceof Oas30Document) {
+                Oas30Document oas30Document = (Oas30Document)openapi;
+                for (final SecurityRequirement securityRequirement : securityRequirements) {
+                    for (final String securityRequirementName : securityRequirement.getSecurityRequirementNames()) {
+                        final Oas30SecurityScheme securitySchemeDefinition = oas30Document.components
+                            .getSecurityScheme(securityRequirementName);
+                        if (securitySchemeDefinition.in != null && securitySchemeDefinition.in.equals("query")) {
+                            Oas30Parameter securityParameter = new Oas30Parameter(securitySchemeDefinition.name);
+                            securityParameter.required = true;
+                            securityParameter.description = securitySchemeDefinition.description;
+                            apiKeyQueryParameters.add(securityParameter);
+                        }
+                        
+                    }
+                }
+            } else {
+                throw new IllegalStateException("We only support OpenApi 2.0 or 3.0 document here");
+            }
+            
+        }
+
+        if (operation.getParameters() != null) {
+            return Stream.concat(apiKeyQueryParameters.stream(),
+                                 operation.getParameters().stream().filter(p -> "query".equals(p.in)));
+        } else {
+            return apiKeyQueryParameters.stream();
+        }
+    }
+
+    static String hostFrom(final RestConfiguration restConfiguration) {
+        if (restConfiguration == null) {
+            return null;
+        }
+
+        final String scheme = restConfiguration.getScheme();
+        final String host = restConfiguration.getHost();
+        final int port = restConfiguration.getPort();
+
+        if (scheme == null || host == null) {
+            return null;
+        }
+
+        final StringBuilder answer = new StringBuilder(scheme).append("://").append(host);
+        if (port > 0 && !("http".equalsIgnoreCase(scheme) && port == 80)
+            && !("https".equalsIgnoreCase(scheme) && port == 443)) {
+            answer.append(':').append(port);
+        }
+
+        return answer.toString();
+    }
+
+    /**
+     * Loads the OpenApi definition model from the given path. Tries to resolve
+     * the resource using Camel's resource loading support, if it fails uses
+     * OpenApi's resource loading support instead.
+     *
+     * @param uri URI of the specification
+     * @param camelContext context to use
+     * @return the specification
+     * @throws IOException
+     */
+    static Document loadSpecificationFrom(final CamelContext camelContext, final URI uri) throws IOException {
+        final ObjectMapper mapper = new ObjectMapper();
+
+      
+        final String uriAsString = uri.toString();
+
+        try (InputStream stream = ResourceHelper.resolveMandatoryResourceAsInputStream(camelContext, uriAsString)) {
+            final JsonNode node = mapper.readTree(stream);
+
+            return Library.readDocument(node);
+        } catch (final Exception e) {
+            
+            throw new IllegalArgumentException("The given OpenApi specification could not be loaded from `" + uri
+                + "`. Tried loading using Camel's resource resolution and using OpenApi's own resource resolution."
+                + " OpenApi tends to swallow exceptions while parsing, try specifying Java system property `debugParser`"
+                + " (e.g. `-DdebugParser=true`), the exception that occurred when loading using Camel's resource"
+                + " loader follows", e);
+        }
+    }
+
+    static String pickBestScheme(final String specificationScheme, final List<String> schemes) {
+        if (schemes != null && !schemes.isEmpty()) {
+            if (schemes.contains("https")) {
+                return "https";
+            }
+
+            if (schemes.contains("http")) {
+                return "http";
+            }
+        }
+
+        if (specificationScheme != null) {
+            return specificationScheme;
+        }
+
+        // there is no support for WebSocket (Scheme.WS, Scheme.WSS)
+
+        return null;
+    }
+
+    static String queryParameterExpression(final OasParameter parameter) {
+        final String name = parameter.getName();
+
+        final StringBuilder expression = new StringBuilder(name).append("={").append(name);
+        if (parameter.required == null || !parameter.required) {
+            expression.append('?');
+        }
+        expression.append('}');
+
+        return expression.toString();
+    }
+    
+    enum HttpMethod {
+        POST,
+        GET,
+        PUT,
+        PATCH,
+        DELETE,
+        HEAD,
+        OPTIONS
+    }
+
+}
diff --git a/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenApiHelper.java b/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenApiHelper.java
new file mode 100644
index 0000000..68badaf
--- /dev/null
+++ b/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenApiHelper.java
@@ -0,0 +1,50 @@
+/*
+ * 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.rest.openapi;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.camel.util.StringHelper;
+
+import static org.apache.camel.util.StringHelper.notEmpty;
+
+final class RestOpenApiHelper {
+
+    private static final Pattern HOST_PATTERN = Pattern.compile("https?://[^:]+(:\\d+)?", Pattern.CASE_INSENSITIVE);
+
+    private RestOpenApiHelper() {
+        // utility class
+    }
+
+    public static String isMediaRange(final String given, final String name) {
+        return notEmpty(given, name);
+    }
+
+    static String isHostParam(final String given) {
+        final String hostUri = StringHelper.notEmpty(given, "host");
+
+        final Matcher matcher = HOST_PATTERN.matcher(given);
+
+        if (!matcher.matches()) {
+            throw new IllegalArgumentException(
+                "host must be an absolute URI (e.g. http://api.example.com), given: `" + hostUri + "`");
+        }
+
+        return hostUri;
+    }
+}
diff --git a/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenApiProducer.java b/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenApiProducer.java
new file mode 100644
index 0000000..2eaa539
--- /dev/null
+++ b/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenApiProducer.java
@@ -0,0 +1,65 @@
+/*
+ * 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.rest.openapi;
+
+import java.util.concurrent.CompletableFuture;
+
+import org.apache.camel.AsyncCallback;
+import org.apache.camel.AsyncProducer;
+import org.apache.camel.Endpoint;
+import org.apache.camel.Exchange;
+import org.apache.camel.support.processor.DelegateAsyncProcessor;
+
+public class RestOpenApiProducer extends DelegateAsyncProcessor implements AsyncProducer {
+
+    private final AsyncProducer delegate;
+    private final boolean removeHostHeader;
+
+    public RestOpenApiProducer(AsyncProducer delegate, boolean removeHostHeader) {
+        super(delegate);
+        this.delegate = delegate;
+        this.removeHostHeader = removeHostHeader;
+    }
+
+    @Override
+    public boolean process(Exchange exchange, AsyncCallback callback) {
+        if (removeHostHeader) {
+            exchange.getMessage().removeHeader("Host");
+        }
+        return super.process(exchange, callback);
+    }
+
+    @Override
+    public CompletableFuture<Exchange> processAsync(Exchange exchange) {
+        if (removeHostHeader) {
+            exchange.getMessage().removeHeader("Host");
+        }
+        return super.processAsync(exchange);
+    }
+
+    @Override
+    public Endpoint getEndpoint() {
+        return delegate.getEndpoint();
+    }
+
+    @Override
+    public boolean isSingleton() {
+        return delegate.isSingleton();
+    }
+
+
+}
diff --git a/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/HttpsTest.java b/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/HttpsTest.java
new file mode 100644
index 0000000..9e9f0af
--- /dev/null
+++ b/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/HttpsTest.java
@@ -0,0 +1,169 @@
+/*
+ * 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.rest.openapi;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URISyntaxException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.security.KeyStore;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.Marshaller;
+
+import com.github.tomakehurst.wiremock.common.HttpsSettings;
+import com.github.tomakehurst.wiremock.junit.WireMockRule;
+import com.google.common.io.Resources;
+import org.apache.camel.CamelContext;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.rest.RestEndpoint;
+import org.apache.camel.converter.jaxb.JaxbDataFormat;
+import org.apache.camel.support.jsse.CipherSuitesParameters;
+import org.apache.camel.support.jsse.SSLContextParameters;
+import org.apache.camel.support.jsse.TrustManagersParameters;
+import org.apache.camel.test.junit4.CamelTestSupport;
+import org.eclipse.jetty.util.resource.Resource;
+import org.eclipse.jetty.util.security.CertificateUtils;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
+import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
+import static com.github.tomakehurst.wiremock.client.WireMock.get;
+import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor;
+import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
+import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
+
+@RunWith(Parameterized.class)
+public abstract class HttpsTest extends CamelTestSupport {
+
+    @ClassRule
+    public static WireMockRule petstore = new WireMockRule(
+        wireMockConfig().httpServerFactory(new Jetty94ServerFactory()).containerThreads(13).dynamicPort()
+            .dynamicHttpsPort().keystorePath(Resources.getResource("localhost.p12").toString()).keystoreType("PKCS12")
+            .keystorePassword("changeit"));
+
+    static final Object NO_BODY = null;
+
+    @Parameter
+    public String componentName;
+
+    @Before
+    public void resetWireMock() {
+        petstore.resetRequests();
+    }
+
+    @Test
+    public void shouldBeConfiguredForHttps() throws Exception {
+        final Pet pet = template.requestBodyAndHeader("direct:getPetById", NO_BODY, "petId", 14, Pet.class);
+
+        assertNotNull(pet);
+
+        assertEquals(Integer.valueOf(14), pet.id);
+        assertEquals("Olafur Eliason Arnalds", pet.name);
+
+        petstore.verify(getRequestedFor(urlEqualTo("/v2/pet/14")).withHeader("Accept",
+            equalTo("application/xml, application/json")));
+    }
+
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        final CamelContext camelContext = super.createCamelContext();
+
+        final RestOpenApiComponent component = new RestOpenApiComponent();
+        component.setComponentName(componentName);
+        component.setHost("https://localhost:" + petstore.httpsPort());
+
+        camelContext.addComponent("petStore", component);
+
+        return camelContext;
+    }
+
+    @Override
+    protected RoutesBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                final JAXBContext jaxbContext = JAXBContext.newInstance(Pet.class, Pets.class);
+
+                final JaxbDataFormat jaxb = new JaxbDataFormat(jaxbContext);
+
+                jaxb.setJaxbProviderProperties(Collections.singletonMap(Marshaller.JAXB_FORMATTED_OUTPUT, false));
+
+                from("direct:getPetById").to("petStore:getPetById").unmarshal(jaxb);
+            }
+        };
+    }
+
+    @Parameters(name = "component = {0}")
+    public static Iterable<String> knownProducers() {
+        final List<String> producers = new ArrayList<>(Arrays.asList(RestEndpoint.DEFAULT_REST_PRODUCER_COMPONENTS));
+
+        // we cannot support SSL in the `http` component as it needs to be
+        // configured via static helper method and this influences all users of
+        // the commons-httpclient (all endpoints, component instances)
+        producers.remove("http");
+
+        return producers;
+    }
+
+    @BeforeClass
+    public static void setupStubs() throws IOException, URISyntaxException {
+        petstore.stubFor(get(urlEqualTo("/openapi.json")).willReturn(aResponse().withBody(
+            Files.readAllBytes(Paths.get(RestOpenApiGlobalHttpsTest.class.getResource("/openapi.json").toURI())))));
+
+        petstore.stubFor(
+            get(urlEqualTo("/v2/pet/14")).willReturn(aResponse().withStatus(HttpURLConnection.HTTP_OK).withBody(
+                "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><Pet><id>14</id><name>Olafur Eliason Arnalds</name></Pet>")));
+    }
+
+    static SSLContextParameters createHttpsParameters(final CamelContext camelContext) throws Exception {
+        final TrustManagersParameters trustManagerParameters = new TrustManagersParameters();
+        trustManagerParameters.setCamelContext(camelContext);
+        final TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+        final HttpsSettings httpsSettings = petstore.getOptions().httpsSettings();
+        final KeyStore trustStore = CertificateUtils.getKeyStore(Resource.newResource(httpsSettings.keyStorePath()),
+            httpsSettings.keyStoreType(), null, httpsSettings.keyStorePassword());
+        trustManagerFactory.init(trustStore);
+        final TrustManager trustManager = trustManagerFactory.getTrustManagers()[0];
+        trustManagerParameters.setTrustManager(trustManager);
+
+        final SSLContextParameters sslContextParameters = new SSLContextParameters();
+        sslContextParameters.setCamelContext(camelContext);
+        sslContextParameters.setTrustManagers(trustManagerParameters);
+        final CipherSuitesParameters cipherSuites = new CipherSuitesParameters();
+        cipherSuites.setCipherSuite(Collections.singletonList("TLS_DHE_RSA_WITH_AES_128_GCM_SHA256"));
+        sslContextParameters.setCipherSuites(cipherSuites);
+        sslContextParameters.setSecureSocketProtocol("TLSv1.2");
+        return sslContextParameters;
+    }
+
+}
diff --git a/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/HttpsV3Test.java b/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/HttpsV3Test.java
new file mode 100644
index 0000000..f958b5b
--- /dev/null
+++ b/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/HttpsV3Test.java
@@ -0,0 +1,169 @@
+/*
+ * 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.rest.openapi;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URISyntaxException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.security.KeyStore;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.Marshaller;
+
+import com.github.tomakehurst.wiremock.common.HttpsSettings;
+import com.github.tomakehurst.wiremock.junit.WireMockRule;
+import com.google.common.io.Resources;
+import org.apache.camel.CamelContext;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.rest.RestEndpoint;
+import org.apache.camel.converter.jaxb.JaxbDataFormat;
+import org.apache.camel.support.jsse.CipherSuitesParameters;
+import org.apache.camel.support.jsse.SSLContextParameters;
+import org.apache.camel.support.jsse.TrustManagersParameters;
+import org.apache.camel.test.junit4.CamelTestSupport;
+import org.eclipse.jetty.util.resource.Resource;
+import org.eclipse.jetty.util.security.CertificateUtils;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
+import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
+import static com.github.tomakehurst.wiremock.client.WireMock.get;
+import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor;
+import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
+import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
+
+@RunWith(Parameterized.class)
+public abstract class HttpsV3Test extends CamelTestSupport {
+
+    @ClassRule
+    public static WireMockRule petstore = new WireMockRule(
+        wireMockConfig().httpServerFactory(new Jetty94ServerFactory()).containerThreads(13).dynamicPort()
+            .dynamicHttpsPort().keystorePath(Resources.getResource("localhost.p12").toString()).keystoreType("PKCS12")
+            .keystorePassword("changeit"));
+
+    static final Object NO_BODY = null;
+
+    @Parameter
+    public String componentName;
+
+    @Before
+    public void resetWireMock() {
+        petstore.resetRequests();
+    }
+
+    @Test
+    public void shouldBeConfiguredForHttps() throws Exception {
+        final Pet pet = template.requestBodyAndHeader("direct:getPetById", NO_BODY, "petId", 14, Pet.class);
+
+        assertNotNull(pet);
+
+        assertEquals(Integer.valueOf(14), pet.id);
+        assertEquals("Olafur Eliason Arnalds", pet.name);
+
+        petstore.verify(getRequestedFor(urlEqualTo("/pet/14")).withHeader("Accept",
+            equalTo("application/xml, application/json")));
+    }
+
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        final CamelContext camelContext = super.createCamelContext();
+
+        final RestOpenApiComponent component = new RestOpenApiComponent();
+        component.setComponentName(componentName);
+        component.setHost("https://localhost:" + petstore.httpsPort());
+        component.setSpecificationUri(RestOpenApiComponentV3Test.class.getResource("/openapi-v3.json").toURI());
+        camelContext.addComponent("petStore", component);
+
+        return camelContext;
+    }
+
+    @Override
+    protected RoutesBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                final JAXBContext jaxbContext = JAXBContext.newInstance(Pet.class, Pets.class);
+
+                final JaxbDataFormat jaxb = new JaxbDataFormat(jaxbContext);
+
+                jaxb.setJaxbProviderProperties(Collections.singletonMap(Marshaller.JAXB_FORMATTED_OUTPUT, false));
+
+                from("direct:getPetById").to("petStore:getPetById").unmarshal(jaxb);
+            }
+        };
+    }
+
+    @Parameters(name = "component = {0}")
+    public static Iterable<String> knownProducers() {
+        final List<String> producers = new ArrayList<>(Arrays.asList(RestEndpoint.DEFAULT_REST_PRODUCER_COMPONENTS));
+
+        // we cannot support SSL in the `http` component as it needs to be
+        // configured via static helper method and this influences all users of
+        // the commons-httpclient (all endpoints, component instances)
+        producers.remove("http");
+
+        return producers;
+    }
+
+    @BeforeClass
+    public static void setupStubs() throws IOException, URISyntaxException {
+        petstore.stubFor(get(urlEqualTo("/openapi.json")).willReturn(aResponse().withBody(
+            Files.readAllBytes(Paths.get(RestOpenApiGlobalHttpsTest.class.getResource("/openapi.json").toURI())))));
+
+        petstore.stubFor(
+            get(urlEqualTo("/pet/14")).willReturn(aResponse().withStatus(HttpURLConnection.HTTP_OK).withBody(
+                "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><Pet><id>14</id><name>Olafur Eliason Arnalds</name></Pet>")));
+    }
+
+    static SSLContextParameters createHttpsParameters(final CamelContext camelContext) throws Exception {
+        final TrustManagersParameters trustManagerParameters = new TrustManagersParameters();
+        trustManagerParameters.setCamelContext(camelContext);
+        final TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+        final HttpsSettings httpsSettings = petstore.getOptions().httpsSettings();
+        final KeyStore trustStore = CertificateUtils.getKeyStore(Resource.newResource(httpsSettings.keyStorePath()),
+            httpsSettings.keyStoreType(), null, httpsSettings.keyStorePassword());
+        trustManagerFactory.init(trustStore);
+        final TrustManager trustManager = trustManagerFactory.getTrustManagers()[0];
+        trustManagerParameters.setTrustManager(trustManager);
+
+        final SSLContextParameters sslContextParameters = new SSLContextParameters();
+        sslContextParameters.setCamelContext(camelContext);
+        sslContextParameters.setTrustManagers(trustManagerParameters);
+        final CipherSuitesParameters cipherSuites = new CipherSuitesParameters();
+        cipherSuites.setCipherSuite(Collections.singletonList("TLS_DHE_RSA_WITH_AES_128_GCM_SHA256"));
+        sslContextParameters.setCipherSuites(cipherSuites);
+        sslContextParameters.setSecureSocketProtocol("TLSv1.2");
+        return sslContextParameters;
+    }
+
+}
diff --git a/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/Jetty94ServerFactory.java b/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/Jetty94ServerFactory.java
new file mode 100644
index 0000000..30c7412
--- /dev/null
+++ b/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/Jetty94ServerFactory.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.component.rest.openapi;
+
+import com.github.tomakehurst.wiremock.common.HttpsSettings;
+import com.github.tomakehurst.wiremock.common.JettySettings;
+import com.github.tomakehurst.wiremock.core.Options;
+import com.github.tomakehurst.wiremock.http.AdminRequestHandler;
+import com.github.tomakehurst.wiremock.http.HttpServer;
+import com.github.tomakehurst.wiremock.http.StubRequestHandler;
+import com.github.tomakehurst.wiremock.jetty9.JettyHttpServer;
+import com.github.tomakehurst.wiremock.jetty9.JettyHttpServerFactory;
+import org.eclipse.jetty.io.NetworkTrafficListener;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.server.SecureRequestCustomizer;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.SslConnectionFactory;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+
+/**
+ * Wiremock 2.18.0 ships with Jetty 9.2, Camel (currently) uses 9.4 and the
+ * {@link org.eclipse.jetty.util.ssl.SslContextFactory} removed
+ * {@code selectCipherSuites} method.
+ */
+public final class Jetty94ServerFactory extends JettyHttpServerFactory {
+    @Override
+    public HttpServer buildHttpServer(final Options options, final AdminRequestHandler adminRequestHandler,
+        final StubRequestHandler stubRequestHandler) {
+
+        return new JettyHttpServer(options, adminRequestHandler, stubRequestHandler) {
+            @Override
+            protected ServerConnector createHttpsConnector(final String bindAddress, final HttpsSettings httpsSettings,
+                final JettySettings jettySettings, final NetworkTrafficListener listener) {
+                final SslContextFactory sslContextFactory = new SslContextFactory.Server();
+
+                sslContextFactory.setKeyStorePath(httpsSettings.keyStorePath());
+                sslContextFactory.setKeyManagerPassword(httpsSettings.keyStorePassword());
+                sslContextFactory.setKeyStorePassword(httpsSettings.keyStorePassword());
+                sslContextFactory.setKeyStoreType(httpsSettings.keyStoreType());
+                if (httpsSettings.hasTrustStore()) {
+                    sslContextFactory.setTrustStorePath(httpsSettings.trustStorePath());
+                    sslContextFactory.setTrustStorePassword(httpsSettings.trustStorePassword());
+                    sslContextFactory.setTrustStoreType(httpsSettings.trustStoreType());
+                }
+                sslContextFactory.setNeedClientAuth(httpsSettings.needClientAuth());
+                sslContextFactory.setIncludeCipherSuites("TLS_DHE_RSA_WITH_AES_128_GCM_SHA256");
+                sslContextFactory.setProtocol("TLSv1.2");
+
+                final HttpConfiguration httpConfig = createHttpConfig(jettySettings);
+                httpConfig.addCustomizer(new SecureRequestCustomizer());
+
+                final int port = httpsSettings.port();
+
+                return createServerConnector(bindAddress, jettySettings, port, listener,
+                    new SslConnectionFactory(sslContextFactory, "http/1.1"), new HttpConnectionFactory(httpConfig));
+            }
+        };
+    }
+}
diff --git a/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/Pet.java b/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/Pet.java
new file mode 100644
index 0000000..e84e7aa
--- /dev/null
+++ b/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/Pet.java
@@ -0,0 +1,28 @@
+/*
+ * 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.rest.openapi;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement(name = "Pet")
+public class Pet {
+
+    public Integer id;
+
+    public String name;
+
+}
diff --git a/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/Pets.java b/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/Pets.java
new file mode 100644
index 0000000..3c60f89
--- /dev/null
+++ b/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/Pets.java
@@ -0,0 +1,33 @@
+/*
+ * 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.rest.openapi;
+
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement(name = "pets")
+@XmlAccessorType(XmlAccessType.FIELD)
+public class Pets {
+
+    @XmlElement(name = "Pet")
+    public List<Pet> pets;
+
+}
diff --git a/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/RestOpenApiComponentTest.java b/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/RestOpenApiComponentTest.java
new file mode 100644
index 0000000..a1e6046
--- /dev/null
+++ b/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/RestOpenApiComponentTest.java
@@ -0,0 +1,234 @@
+/*
+ * 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.rest.openapi;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URISyntaxException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.Marshaller;
+
+import com.github.tomakehurst.wiremock.junit.WireMockRule;
+import org.apache.camel.CamelContext;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.rest.RestEndpoint;
+import org.apache.camel.converter.jaxb.JaxbDataFormat;
+import org.apache.camel.test.junit4.CamelTestSupport;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
+import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
+import static com.github.tomakehurst.wiremock.client.WireMock.get;
+import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor;
+import static com.github.tomakehurst.wiremock.client.WireMock.post;
+import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor;
+import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
+import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo;
+import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
+
+@RunWith(Parameterized.class)
+public class RestOpenApiComponentTest extends CamelTestSupport {
+
+    @ClassRule
+    public static WireMockRule petstore = new WireMockRule(wireMockConfig().dynamicPort());
+
+    static final Object NO_BODY = null;
+
+    @Parameter
+    public String componentName;
+
+    @Before
+    public void resetWireMock() {
+        petstore.resetRequests();
+    }
+
+    @Test
+    public void shouldBeAddingPets() {
+        final Pet pet = new Pet();
+        pet.name = "Jean-Luc Picard";
+
+        final Pet created = template.requestBody("direct:addPet", pet, Pet.class);
+
+        assertNotNull(created);
+
+        assertEquals(Integer.valueOf(14), created.id);
+
+        petstore.verify(
+            postRequestedFor(urlEqualTo("/v2/pet")).withHeader("Accept", equalTo("application/xml, application/json"))
+                .withHeader("Content-Type", equalTo("application/xml")));
+    }
+
+    @Test
+    public void shouldBeGettingPetsById() {
+        final Pet pet = template.requestBodyAndHeader("direct:getPetById", NO_BODY, "petId", 14, Pet.class);
+
+        assertNotNull(pet);
+
+        assertEquals(Integer.valueOf(14), pet.id);
+        assertEquals("Olafur Eliason Arnalds", pet.name);
+
+        petstore.verify(getRequestedFor(urlEqualTo("/v2/pet/14")).withHeader("Accept",
+            equalTo("application/xml, application/json")));
+    }
+
+    @Test
+    public void shouldBeGettingPetsByIdSpecifiedInEndpointParameters() {
+        final Pet pet = template.requestBody("direct:getPetByIdWithEndpointParams", NO_BODY, Pet.class);
+
+        assertNotNull(pet);
+
+        assertEquals(Integer.valueOf(14), pet.id);
+        assertEquals("Olafur Eliason Arnalds", pet.name);
+
+        petstore.verify(getRequestedFor(urlEqualTo("/v2/pet/14")).withHeader("Accept",
+            equalTo("application/xml, application/json")));
+    }
+
+    @Test
+    public void shouldBeGettingPetsByIdWithApiKeysInHeader() {
+        final Map<String, Object> headers = new HashMap<>();
+        headers.put("petId", 14);
+        headers.put("api_key", "dolphins");
+        final Pet pet = template.requestBodyAndHeaders("direct:getPetById", NO_BODY, headers, Pet.class);
+
+        assertNotNull(pet);
+
+        assertEquals(Integer.valueOf(14), pet.id);
+        assertEquals("Olafur Eliason Arnalds", pet.name);
+
+        petstore.verify(
+            getRequestedFor(urlEqualTo("/v2/pet/14")).withHeader("Accept", equalTo("application/xml, application/json"))
+                .withHeader("api_key", equalTo("dolphins")));
+    }
+
+    @Test
+    public void shouldBeGettingPetsByIdWithApiKeysInQueryParameter() {
+        final Map<String, Object> headers = new HashMap<>();
+        headers.put("petId", 14);
+        headers.put("api_key", "dolphins");
+        final Pet pet = template.requestBodyAndHeaders("altPetStore:getPetById", NO_BODY, headers, Pet.class);
+
+        assertNotNull(pet);
+
+        assertEquals(Integer.valueOf(14), pet.id);
+        assertEquals("Olafur Eliason Arnalds", pet.name);
+
+        petstore.verify(getRequestedFor(urlEqualTo("/v2/pet/14?api_key=dolphins")).withHeader("Accept",
+            equalTo("application/xml, application/json")));
+    }
+
+    @Test
+    public void shouldBeGettingPetsByStatus() {
+        final Pets pets = template.requestBodyAndHeader("direct:findPetsByStatus", NO_BODY, "status", "available",
+            Pets.class);
+
+        assertNotNull(pets);
+        assertNotNull(pets.pets);
+        assertEquals(2, pets.pets.size());
+
+        petstore.verify(
+            getRequestedFor(urlPathEqualTo("/v2/pet/findByStatus")).withQueryParam("status", equalTo("available"))
+                .withHeader("Accept", equalTo("application/xml, application/json")));
+    }
+
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        final CamelContext camelContext = super.createCamelContext();
+
+        final RestOpenApiComponent component = new RestOpenApiComponent();
+        component.setComponentName(componentName);
+        component.setHost("http://localhost:" + petstore.port());
+
+        camelContext.addComponent("petStore", component);
+
+        final RestOpenApiComponent altPetStore = new RestOpenApiComponent();
+        altPetStore.setComponentName(componentName);
+        altPetStore.setHost("http://localhost:" + petstore.port());
+        altPetStore.setSpecificationUri(RestOpenApiComponentTest.class.getResource("/alt-petstore.json").toURI());
+
+        camelContext.addComponent("altPetStore", altPetStore);
+
+        return camelContext;
+    }
+
+    @Override
+    protected RoutesBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                final JAXBContext jaxbContext = JAXBContext.newInstance(Pet.class, Pets.class);
+
+                final JaxbDataFormat jaxb = new JaxbDataFormat(jaxbContext);
+
+                jaxb.setJaxbProviderProperties(Collections.singletonMap(Marshaller.JAXB_FORMATTED_OUTPUT, false));
+
+                from("direct:getPetById").to("petStore:getPetById").unmarshal(jaxb);
+
+                from("direct:getPetByIdWithEndpointParams").to("petStore:getPetById?petId=14").unmarshal(jaxb);
+
+                from("direct:addPet").marshal(jaxb).to("petStore:addPet").unmarshal(jaxb);
+
+                from("direct:findPetsByStatus").to("petStore:findPetsByStatus").unmarshal(jaxb);
+            }
+        };
+    }
+
+    @Parameters(name = "component = {0}")
+    public static Iterable<String> knownProducers() {
+        return Arrays.asList(RestEndpoint.DEFAULT_REST_PRODUCER_COMPONENTS);
+    }
+
+    @BeforeClass
+    public static void setupStubs() throws IOException, URISyntaxException {
+        petstore.stubFor(get(urlEqualTo("/openapi.json")).willReturn(aResponse().withBody(
+            Files.readAllBytes(Paths.get(RestOpenApiComponentTest.class.getResource("/openapi.json").toURI())))));
+
+        petstore.stubFor(post(urlEqualTo("/v2/pet"))
+            .withRequestBody(equalTo(
+                "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><Pet><name>Jean-Luc Picard</name></Pet>"))
+            .willReturn(aResponse().withStatus(HttpURLConnection.HTTP_CREATED)
+                .withBody("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><Pet><id>14</id></Pet>")));
+
+        petstore.stubFor(
+            get(urlEqualTo("/v2/pet/14")).willReturn(aResponse().withStatus(HttpURLConnection.HTTP_OK).withBody(
+                "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><Pet><id>14</id><name>Olafur Eliason Arnalds</name></Pet>")));
+
+        petstore.stubFor(get(urlPathEqualTo("/v2/pet/findByStatus")).withQueryParam("status", equalTo("available"))
+            .willReturn(aResponse().withStatus(HttpURLConnection.HTTP_OK).withBody(
+                "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><pets><Pet><id>1</id><name>Olafur Eliason Arnalds</name></Pet><Pet><name>Jean-Luc Picard</name></Pet></pets>")));
+
+        petstore.stubFor(get(urlEqualTo("/v2/pet/14?api_key=dolphins"))
+            .willReturn(aResponse().withStatus(HttpURLConnection.HTTP_OK).withBody(
+                "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><Pet><id>14</id><name>Olafur Eliason Arnalds</name></Pet>")));
+    }
+
+}
diff --git a/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/RestOpenApiComponentV3Test.java b/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/RestOpenApiComponentV3Test.java
new file mode 100644
index 0000000..a39a87e
--- /dev/null
+++ b/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/RestOpenApiComponentV3Test.java
@@ -0,0 +1,235 @@
+/*
+ * 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.rest.openapi;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URISyntaxException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.Marshaller;
+
+import com.github.tomakehurst.wiremock.junit.WireMockRule;
+import org.apache.camel.CamelContext;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.rest.RestEndpoint;
+import org.apache.camel.converter.jaxb.JaxbDataFormat;
+import org.apache.camel.test.junit4.CamelTestSupport;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
+import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
+import static com.github.tomakehurst.wiremock.client.WireMock.get;
+import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor;
+import static com.github.tomakehurst.wiremock.client.WireMock.post;
+import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor;
+import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
+import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo;
+import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
+
+@RunWith(Parameterized.class)
+public class RestOpenApiComponentV3Test extends CamelTestSupport {
+
+    @ClassRule
+    public static WireMockRule petstore = new WireMockRule(wireMockConfig().dynamicPort());
+
+    static final Object NO_BODY = null;
+
+    @Parameter
+    public String componentName;
+
+    @Before
+    public void resetWireMock() {
+        petstore.resetRequests();
+    }
+
+    @Test
+    public void shouldBeAddingPets() {
+        final Pet pet = new Pet();
+        pet.name = "Jean-Luc Picard";
+
+        final Pet created = template.requestBody("direct:addPet", pet, Pet.class);
+
+        assertNotNull(created);
+
+        assertEquals(Integer.valueOf(14), created.id);
+
+        petstore.verify(
+            postRequestedFor(urlEqualTo("/pet")).withHeader("Accept", equalTo("application/xml, application/json"))
+                .withHeader("Content-Type", equalTo("application/xml")));
+    }
+
+    @Test
+    public void shouldBeGettingPetsById() {
+        final Pet pet = template.requestBodyAndHeader("direct:getPetById", NO_BODY, "petId", 14, Pet.class);
+
+        assertNotNull(pet);
+
+        assertEquals(Integer.valueOf(14), pet.id);
+        assertEquals("Olafur Eliason Arnalds", pet.name);
+
+        petstore.verify(getRequestedFor(urlEqualTo("/pet/14")).withHeader("Accept",
+            equalTo("application/xml, application/json")));
+    }
+
+    @Test
+    public void shouldBeGettingPetsByIdSpecifiedInEndpointParameters() {
+        final Pet pet = template.requestBody("direct:getPetByIdWithEndpointParams", NO_BODY, Pet.class);
+
+        assertNotNull(pet);
+
+        assertEquals(Integer.valueOf(14), pet.id);
+        assertEquals("Olafur Eliason Arnalds", pet.name);
+
+        petstore.verify(getRequestedFor(urlEqualTo("/pet/14")).withHeader("Accept",
+            equalTo("application/xml, application/json")));
+    }
+
+    @Test
+    public void shouldBeGettingPetsByIdWithApiKeysInHeader() {
+        final Map<String, Object> headers = new HashMap<>();
+        headers.put("petId", 14);
+        headers.put("api_key", "dolphins");
+        final Pet pet = template.requestBodyAndHeaders("direct:getPetById", NO_BODY, headers, Pet.class);
+
+        assertNotNull(pet);
+
+        assertEquals(Integer.valueOf(14), pet.id);
+        assertEquals("Olafur Eliason Arnalds", pet.name);
+
+        petstore.verify(
+            getRequestedFor(urlEqualTo("/pet/14")).withHeader("Accept", equalTo("application/xml, application/json"))
+                .withHeader("api_key", equalTo("dolphins")));
+    }
+
+    @Test
+    public void shouldBeGettingPetsByIdWithApiKeysInQueryParameter() {
+        final Map<String, Object> headers = new HashMap<>();
+        headers.put("petId", 14);
+        headers.put("api_key", "dolphins");
+        final Pet pet = template.requestBodyAndHeaders("altPetStore:getPetById", NO_BODY, headers, Pet.class);
+
+        assertNotNull(pet);
+
+        assertEquals(Integer.valueOf(14), pet.id);
+        assertEquals("Olafur Eliason Arnalds", pet.name);
+
+        petstore.verify(getRequestedFor(urlEqualTo("/pet/14?api_key=dolphins")).withHeader("Accept",
+            equalTo("application/xml, application/json")));
+    }
+
+    @Test
+    public void shouldBeGettingPetsByStatus() {
+        final Pets pets = template.requestBodyAndHeader("direct:findPetsByStatus", NO_BODY, "status", "available",
+            Pets.class);
+
+        assertNotNull(pets);
+        assertNotNull(pets.pets);
+        assertEquals(2, pets.pets.size());
+
+        petstore.verify(
+            getRequestedFor(urlPathEqualTo("/pet/findByStatus")).withQueryParam("status", equalTo("available"))
+                .withHeader("Accept", equalTo("application/xml, application/json")));
+    }
+
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        final CamelContext camelContext = super.createCamelContext();
+
+        final RestOpenApiComponent component = new RestOpenApiComponent();
+        component.setComponentName(componentName);
+        component.setHost("http://localhost:" + petstore.port());
+        component.setSpecificationUri(RestOpenApiComponentV3Test.class.getResource("/openapi-v3.json").toURI());
+
+        camelContext.addComponent("petStore", component);
+
+        final RestOpenApiComponent altPetStore = new RestOpenApiComponent();
+        altPetStore.setComponentName(componentName);
+        altPetStore.setHost("http://localhost:" + petstore.port());
+        altPetStore.setSpecificationUri(RestOpenApiComponentV3Test.class.getResource("/alt-openapi.json").toURI());
+
+        camelContext.addComponent("altPetStore", altPetStore);
+
+        return camelContext;
+    }
+
+    @Override
+    protected RoutesBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                final JAXBContext jaxbContext = JAXBContext.newInstance(Pet.class, Pets.class);
+
+                final JaxbDataFormat jaxb = new JaxbDataFormat(jaxbContext);
+
+                jaxb.setJaxbProviderProperties(Collections.singletonMap(Marshaller.JAXB_FORMATTED_OUTPUT, false));
+
+                from("direct:getPetById").to("petStore:getPetById").unmarshal(jaxb);
+
+                from("direct:getPetByIdWithEndpointParams").to("petStore:getPetById?petId=14").unmarshal(jaxb);
+
+                from("direct:addPet").marshal(jaxb).to("petStore:addPet").unmarshal(jaxb);
+
+                from("direct:findPetsByStatus").to("petStore:findPetsByStatus").unmarshal(jaxb);
+            }
+        };
+    }
+
+    @Parameters(name = "component = {0}")
+    public static Iterable<String> knownProducers() {
+        return Arrays.asList(RestEndpoint.DEFAULT_REST_PRODUCER_COMPONENTS);
+    }
+
+    @BeforeClass
+    public static void setupStubs() throws IOException, URISyntaxException {
+        petstore.stubFor(get(urlEqualTo("/openapi-v3.json")).willReturn(aResponse().withBody(
+            Files.readAllBytes(Paths.get(RestOpenApiComponentV3Test.class.getResource("/openapi-v3.json").toURI())))));
+
+        petstore.stubFor(post(urlEqualTo("/pet"))
+            .withRequestBody(equalTo(
+                "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><Pet><name>Jean-Luc Picard</name></Pet>"))
+            .willReturn(aResponse().withStatus(HttpURLConnection.HTTP_CREATED)
+                .withBody("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><Pet><id>14</id></Pet>")));
+
+        petstore.stubFor(
+            get(urlEqualTo("/pet/14")).willReturn(aResponse().withStatus(HttpURLConnection.HTTP_OK).withBody(
+                "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><Pet><id>14</id><name>Olafur Eliason Arnalds</name></Pet>")));
+
+        petstore.stubFor(get(urlPathEqualTo("/pet/findByStatus")).withQueryParam("status", equalTo("available"))
+            .willReturn(aResponse().withStatus(HttpURLConnection.HTTP_OK).withBody(
+                "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><pets><Pet><id>1</id><name>Olafur Eliason Arnalds</name></Pet><Pet><name>Jean-Luc Picard</name></Pet></pets>")));
+
+        petstore.stubFor(get(urlEqualTo("/pet/14?api_key=dolphins"))
+            .willReturn(aResponse().withStatus(HttpURLConnection.HTTP_OK).withBody(
+                "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><Pet><id>14</id><name>Olafur Eliason Arnalds</name></Pet>")));
+    }
+
+}
diff --git a/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/RestOpenApiDelegateHttpsTest.java b/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/RestOpenApiDelegateHttpsTest.java
new file mode 100644
index 0000000..fcd5cc0d
--- /dev/null
+++ b/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/RestOpenApiDelegateHttpsTest.java
@@ -0,0 +1,44 @@
+/*
+ * 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.rest.openapi;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Component;
+import org.apache.camel.ExtendedCamelContext;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.spi.BeanIntrospection;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RestOpenApiDelegateHttpsTest extends HttpsTest {
+
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        final CamelContext camelContext = super.createCamelContext();
+
+        final Component delegate = ((DefaultCamelContext) camelContext).getComponentResolver()
+            .resolveComponent(componentName, camelContext);
+
+        BeanIntrospection beanIntrospection = camelContext.adapt(ExtendedCamelContext.class).getBeanIntrospection();
+        beanIntrospection.setProperty(camelContext, delegate, "sslContextParameters", createHttpsParameters(camelContext));
+        camelContext.addComponent(componentName, delegate);
+
+        return camelContext;
+    }
+
+}
diff --git a/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/RestOpenApiDelegateHttpsV3Test.java b/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/RestOpenApiDelegateHttpsV3Test.java
new file mode 100644
index 0000000..736c09c
--- /dev/null
+++ b/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/RestOpenApiDelegateHttpsV3Test.java
@@ -0,0 +1,44 @@
+/*
+ * 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.rest.openapi;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Component;
+import org.apache.camel.ExtendedCamelContext;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.spi.BeanIntrospection;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RestOpenApiDelegateHttpsV3Test extends HttpsV3Test {
+
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        final CamelContext camelContext = super.createCamelContext();
+
+        final Component delegate = ((DefaultCamelContext) camelContext).getComponentResolver()
+            .resolveComponent(componentName, camelContext);
+
+        BeanIntrospection beanIntrospection = camelContext.adapt(ExtendedCamelContext.class).getBeanIntrospection();
+        beanIntrospection.setProperty(camelContext, delegate, "sslContextParameters", createHttpsParameters(camelContext));
+        camelContext.addComponent(componentName, delegate);
+
+        return camelContext;
+    }
+
+}
diff --git a/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/RestOpenApiEndpointTest.java b/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/RestOpenApiEndpointTest.java
new file mode 100644
index 0000000..d57bf28
--- /dev/null
+++ b/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/RestOpenApiEndpointTest.java
@@ -0,0 +1,447 @@
+/*
+ * 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.rest.openapi;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import io.apicurio.datamodels.core.models.common.SecurityRequirement;
+import io.apicurio.datamodels.openapi.models.OasParameter;
+import io.apicurio.datamodels.openapi.v2.models.Oas20Document;
+import io.apicurio.datamodels.openapi.v2.models.Oas20Operation;
+import io.apicurio.datamodels.openapi.v2.models.Oas20Parameter;
+import io.apicurio.datamodels.openapi.v2.models.Oas20SecurityScheme;
+import org.apache.camel.CamelContext;
+import org.apache.camel.impl.engine.DefaultClassResolver;
+import org.apache.camel.spi.RestConfiguration;
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.entry;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class RestOpenApiEndpointTest {
+
+    URI componentJsonUri = URI.create("component.json");
+
+    URI endpointUri = URI.create("endpoint.json");
+
+    @Test(expected = IllegalArgumentException.class)
+    public void shouldComplainForUnknownOperations() throws Exception {
+        final CamelContext camelContext = mock(CamelContext.class);
+        when(camelContext.getClassResolver()).thenReturn(new DefaultClassResolver());
+
+        final RestOpenApiComponent component = new RestOpenApiComponent(camelContext);
+
+        final RestOpenApiEndpoint endpoint = new RestOpenApiEndpoint("rest-openapi:unknown", "unknown", component,
+            Collections.emptyMap());
+
+        endpoint.createProducer();
+    }
+
+    @Test
+    public void shouldComputeQueryParameters() {
+        final RestOpenApiEndpoint endpoint = new RestOpenApiEndpoint();
+        endpoint.parameters = new HashMap<>();
+        endpoint.parameters.put("literal", "value");
+        assertThat(endpoint.queryParameter(new Oas20Parameter())).isEqualTo("");
+        assertThat(endpoint.queryParameter(new Oas20Parameter("param"))).isEqualTo("param={param?}");
+        assertThat(endpoint.queryParameter(new Oas20Parameter("literal"))).isEqualTo("literal=value");
+    }
+
+    @Test
+    public void shouldCreateQueryParameterExpressions() {
+        Oas20Parameter oas20Parameter = new Oas20Parameter("q");
+        oas20Parameter.required = true;
+        assertThat(RestOpenApiEndpoint.queryParameterExpression(oas20Parameter))
+            .isEqualTo("q={q}");
+        oas20Parameter.required = false;
+        assertThat(RestOpenApiEndpoint.queryParameterExpression(oas20Parameter))
+            .isEqualTo("q={q?}");
+    }
+
+    @Test
+    public void shouldDetermineBasePath() {
+        final RestConfiguration restConfiguration = new RestConfiguration();
+
+        final CamelContext camelContext = mock(CamelContext.class);
+        when(camelContext.getRestConfiguration("rest-openapi", true)).thenReturn(restConfiguration);
+
+        final Oas20Document openapi = new Oas20Document();
+
+        final RestOpenApiComponent component = new RestOpenApiComponent();
+        component.setCamelContext(camelContext);
+        final RestOpenApiEndpoint endpoint = new RestOpenApiEndpoint("rest-openapi:getPetById", "getPetById", component,
+            Collections.emptyMap());
+
+        assertThat(endpoint.determineBasePath(openapi))
+            .as("When no base path is specified on component, endpoint or rest configuration it should default to `/`")
+            .isEqualTo("/");
+
+        restConfiguration.setContextPath("/rest");
+        assertThat(endpoint.determineBasePath(openapi)).as(
+            "When base path is specified in REST configuration and not specified in component the base path should be from the REST configuration")
+            .isEqualTo("/rest");
+
+        openapi.basePath = "/specification";
+        assertThat(endpoint.determineBasePath(openapi)).as(
+            "When base path is specified in the specification it should take precedence the one specified in the REST configuration")
+            .isEqualTo("/specification");
+
+        component.setBasePath("/component");
+        assertThat(endpoint.determineBasePath(openapi)).as(
+            "When base path is specified on the component it should take precedence over OpenApi specification and REST configuration")
+            .isEqualTo("/component");
+
+        endpoint.setBasePath("/endpoint");
+        assertThat(endpoint.determineBasePath(openapi))
+            .as("When base path is specified on the endpoint it should take precedence over any other")
+            .isEqualTo("/endpoint");
+    }
+
+    @Test
+    public void shouldDetermineEndpointParameters() {
+        final CamelContext camelContext = mock(CamelContext.class);
+
+        final RestOpenApiComponent component = new RestOpenApiComponent();
+        component.setCamelContext(camelContext);
+
+        final RestOpenApiEndpoint endpoint = new RestOpenApiEndpoint("uri", "remaining", component,
+            Collections.emptyMap());
+        endpoint.setHost("http://petstore.openapi.io");
+
+        final Oas20Document openapi = new Oas20Document();
+        final Oas20Operation operation = new Oas20Operation("get");
+        operation.createParameter();
+        assertThat(endpoint.determineEndpointParameters(openapi, operation))
+            .containsOnly(entry("host", "http://petstore.openapi.io"));
+        
+
+        component.setComponentName("xyz");
+        assertThat(endpoint.determineEndpointParameters(openapi, operation))
+            .containsOnly(entry("host", "http://petstore.openapi.io"), entry("producerComponentName", "xyz"));
+
+        List<String> consumers = new ArrayList<String>();
+        consumers.add("application/json");
+        List<String> produces = new ArrayList<String>();
+        produces.add("application/xml");
+        openapi.consumes = consumers;
+        openapi.produces = produces;
+                
+        assertThat(endpoint.determineEndpointParameters(openapi, operation)).containsOnly(
+            entry("host", "http://petstore.openapi.io"), entry("producerComponentName", "xyz"),
+            entry("consumes", "application/xml"), entry("produces", "application/json"));
+
+        component.setProduces("application/json");
+        component.setConsumes("application/atom+xml");
+        assertThat(endpoint.determineEndpointParameters(openapi, operation)).containsOnly(
+            entry("host", "http://petstore.openapi.io"), entry("producerComponentName", "xyz"),
+            entry("consumes", "application/atom+xml"), entry("produces", "application/json"));
+
+        endpoint.setProduces("application/atom+xml");
+        endpoint.setConsumes("application/json");
+        assertThat(endpoint.determineEndpointParameters(openapi, operation)).containsOnly(
+            entry("host", "http://petstore.openapi.io"), entry("producerComponentName", "xyz"),
+            entry("consumes", "application/json"), entry("produces", "application/atom+xml"));
+
+        endpoint.setComponentName("zyx");
+        assertThat(endpoint.determineEndpointParameters(openapi, operation)).containsOnly(
+            entry("host", "http://petstore.openapi.io"), entry("producerComponentName", "zyx"),
+            entry("consumes", "application/json"), entry("produces", "application/atom+xml"));
+
+        Oas20Parameter oas20Parameter = new Oas20Parameter("q");
+        oas20Parameter.in = "query";
+        oas20Parameter.required = true;
+        operation.addParameter(oas20Parameter);
+        assertThat(endpoint.determineEndpointParameters(openapi, operation)).containsOnly(
+            entry("host", "http://petstore.openapi.io"), entry("producerComponentName", "zyx"),
+            entry("consumes", "application/json"), entry("produces", "application/atom+xml"),
+            entry("queryParameters", "q={q}"));
+
+        oas20Parameter = new Oas20Parameter("o");
+        oas20Parameter.in = "query";
+        operation.addParameter(oas20Parameter);
+        assertThat(endpoint.determineEndpointParameters(openapi, operation)).containsOnly(
+            entry("host", "http://petstore.openapi.io"), entry("producerComponentName", "zyx"),
+            entry("consumes", "application/json"), entry("produces", "application/atom+xml"),
+            entry("queryParameters", "q={q}&o={o?}"));
+    }
+
+    @Test
+    public void shouldDetermineHostFromRestConfiguration() {
+        assertThat(RestOpenApiEndpoint.hostFrom(null)).isNull();
+
+        final RestConfiguration configuration = new RestConfiguration();
+        assertThat(RestOpenApiEndpoint.hostFrom(configuration)).isNull();
+
+        configuration.setScheme("ftp");
+        assertThat(RestOpenApiEndpoint.hostFrom(configuration)).isNull();
+
+        configuration.setScheme("http");
+        assertThat(RestOpenApiEndpoint.hostFrom(configuration)).isNull();
+
+        configuration.setHost("petstore.openapi.io");
+        assertThat(RestOpenApiEndpoint.hostFrom(configuration)).isEqualTo("http://petstore.openapi.io");
+
+        configuration.setPort(80);
+        assertThat(RestOpenApiEndpoint.hostFrom(configuration)).isEqualTo("http://petstore.openapi.io");
+
+        configuration.setPort(8080);
+        assertThat(RestOpenApiEndpoint.hostFrom(configuration)).isEqualTo("http://petstore.openapi.io:8080");
+
+        configuration.setScheme("https");
+        configuration.setPort(80);
+        assertThat(RestOpenApiEndpoint.hostFrom(configuration)).isEqualTo("https://petstore.openapi.io:80");
+
+        configuration.setPort(443);
+        assertThat(RestOpenApiEndpoint.hostFrom(configuration)).isEqualTo("https://petstore.openapi.io");
+    }
+
+    @Test
+    public void shouldDetermineHostFromSpecification() {
+        final RestOpenApiComponent component = new RestOpenApiComponent();
+
+        final RestOpenApiEndpoint endpoint = new RestOpenApiEndpoint("rest-openapi:http://some-uri#getPetById",
+            "http://some-uri#getPetById", component, Collections.emptyMap());
+
+        final Oas20Document openapi = new Oas20Document();
+        openapi.host = "petstore.swagger.io";
+
+        assertThat(endpoint.determineHost(openapi)).isEqualTo("http://petstore.swagger.io");
+
+        openapi.schemes = Arrays.asList("https");
+        assertThat(endpoint.determineHost(openapi)).isEqualTo("https://petstore.swagger.io");
+    }
+
+    @Test
+    public void shouldDetermineOptions() {
+        assertThat(RestOpenApiEndpoint.determineOption(null, null, null, null)).isNull();
+
+        assertThat(RestOpenApiEndpoint.determineOption(Collections.emptyList(), Collections.emptyList(), "", ""))
+            .isNull();
+
+        assertThat(RestOpenApiEndpoint.determineOption(Arrays.asList("specification"), null, null, null))
+            .isEqualTo("specification");
+
+        assertThat(
+            RestOpenApiEndpoint.determineOption(Arrays.asList("specification"), Arrays.asList("operation"), null, null))
+                .isEqualTo("operation");
+
+        assertThat(RestOpenApiEndpoint.determineOption(Arrays.asList("specification"), Arrays.asList("operation"),
+            "component", null)).isEqualTo("component");
+
+        assertThat(RestOpenApiEndpoint.determineOption(Arrays.asList("specification"), Arrays.asList("operation"),
+            "component", "operation")).isEqualTo("operation");
+    }
+
+    @Test
+    public void shouldHonourComponentSpecificationPathProperty() throws Exception {
+        final RestOpenApiComponent component = new RestOpenApiComponent();
+        component.setSpecificationUri(componentJsonUri);
+
+        final RestOpenApiEndpoint endpoint = new RestOpenApiEndpoint("rest-openapi:getPetById", "getPetById", component,
+            Collections.emptyMap());
+
+        assertThat(endpoint.getSpecificationUri()).isEqualTo(componentJsonUri);
+    }
+
+    @Test
+    public void shouldHonourEndpointUriPathSpecificationPathProperty() throws Exception {
+        final RestOpenApiComponent component = new RestOpenApiComponent();
+        component.setSpecificationUri(componentJsonUri);
+
+        final RestOpenApiEndpoint endpoint = new RestOpenApiEndpoint("rest-openapi:endpoint.json#getPetById",
+            "endpoint.json#getPetById", component, Collections.emptyMap());
+
+        assertThat(endpoint.getSpecificationUri()).isEqualTo(endpointUri);
+    }
+
+    @Test
+    public void shouldHonourHostPrecedence() {
+        final RestConfiguration globalRestConfiguration = new RestConfiguration();
+
+        final RestConfiguration componentRestConfiguration = new RestConfiguration();
+        final RestConfiguration specificRestConfiguration = new RestConfiguration();
+
+        final CamelContext camelContext = mock(CamelContext.class);
+        when(camelContext.getRestConfiguration()).thenReturn(globalRestConfiguration);
+        when(camelContext.getRestConfiguration("rest-openapi", false)).thenReturn(componentRestConfiguration);
+        when(camelContext.getRestConfiguration("petstore", false)).thenReturn(specificRestConfiguration);
+
+        final RestOpenApiComponent component = new RestOpenApiComponent();
+        component.setCamelContext(camelContext);
+
+        final RestOpenApiEndpoint endpoint = new RestOpenApiEndpoint("petstore:http://specification-uri#getPetById",
+            "http://specification-uri#getPetById", component, Collections.emptyMap());
+
+        final Oas20Document openapi = new Oas20Document();
+        assertThat(endpoint.determineHost(openapi)).isEqualTo("http://specification-uri");
+
+        globalRestConfiguration.setHost("global-rest");
+        globalRestConfiguration.setScheme("http");
+        assertThat(endpoint.determineHost(openapi)).isEqualTo("http://global-rest");
+
+        globalRestConfiguration.setHost("component-rest");
+        globalRestConfiguration.setScheme("http");
+        assertThat(endpoint.determineHost(openapi)).isEqualTo("http://component-rest");
+
+        specificRestConfiguration.setHost("specific-rest");
+        specificRestConfiguration.setScheme("http");
+        assertThat(endpoint.determineHost(openapi)).isEqualTo("http://specific-rest");
+
+        openapi.host = "specification";
+        openapi.schemes = Arrays.asList("http");
+        assertThat(endpoint.determineHost(openapi)).isEqualTo("http://specification");
+
+        component.setHost("http://component");
+        assertThat(endpoint.determineHost(openapi)).isEqualTo("http://component");
+
+        endpoint.setHost("http://endpoint");
+        assertThat(endpoint.determineHost(openapi)).isEqualTo("http://endpoint");
+    }
+
+    @Test
+    public void shouldIncludeApiKeysQueryParameters() {
+        final CamelContext camelContext = mock(CamelContext.class);
+
+        final RestOpenApiComponent component = new RestOpenApiComponent();
+        component.setCamelContext(camelContext);
+
+        final RestOpenApiEndpoint endpoint = new RestOpenApiEndpoint("uri", "remaining", component,
+            Collections.emptyMap());
+        endpoint.setHost("http://petstore.openapi.io");
+
+        final Oas20Document openapi = new Oas20Document();
+        final Oas20SecurityScheme apiKeys = new Oas20SecurityScheme("key");
+        apiKeys.name = "key";
+        apiKeys.in = "header";
+        openapi.securityDefinitions = openapi.createSecurityDefinitions();
+        openapi.securityDefinitions.addItem("apiKeys", apiKeys);
+        
+        final Oas20Operation operation = new Oas20Operation("get");
+        Oas20Parameter oas20Parameter = new Oas20Parameter("q");
+        oas20Parameter.in = "query";
+        oas20Parameter.required = true;
+        operation.addParameter(oas20Parameter);
+        SecurityRequirement securityRequirement =  operation.createSecurityRequirement();
+        securityRequirement.addSecurityRequirementItem("apiKeys", Collections.emptyList());
+        operation.addSecurityRequirement(securityRequirement);
+        
+        
+        assertThat(endpoint.determineEndpointParameters(openapi, operation))
+            .containsOnly(entry("host", "http://petstore.openapi.io"), entry("queryParameters", "q={q}"));
+
+        apiKeys.in = "query";
+        assertThat(endpoint.determineEndpointParameters(openapi, operation))
+            .containsOnly(entry("host", "http://petstore.openapi.io"), entry("queryParameters", "key={key}&q={q}"));
+    }
+
+    @Test
+    public void shouldLoadOpenApiSpecifications() throws IOException {
+        final CamelContext camelContext = mock(CamelContext.class);
+        when(camelContext.getClassResolver()).thenReturn(new DefaultClassResolver());
+
+        assertThat(
+            RestOpenApiEndpoint.loadSpecificationFrom(camelContext, RestOpenApiComponent.DEFAULT_SPECIFICATION_URI))
+                .isNotNull();
+    }
+
+    @Test
+    public void shouldPickBestScheme() {
+        assertThat(RestOpenApiEndpoint.pickBestScheme("http", Arrays.asList("http", "https")))
+            .isEqualTo("https");
+
+        assertThat(RestOpenApiEndpoint.pickBestScheme("https", Arrays.asList("http"))).isEqualTo("http");
+
+        assertThat(RestOpenApiEndpoint.pickBestScheme("http", Collections.emptyList())).isEqualTo("http");
+
+        assertThat(RestOpenApiEndpoint.pickBestScheme("http", null)).isEqualTo("http");
+
+        assertThat(RestOpenApiEndpoint.pickBestScheme(null, Collections.emptyList())).isNull();
+
+        assertThat(RestOpenApiEndpoint.pickBestScheme(null, null)).isNull();
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void shouldRaiseExceptionsForMissingSpecifications() throws IOException {
+        final CamelContext camelContext = mock(CamelContext.class);
+        when(camelContext.getClassResolver()).thenReturn(new DefaultClassResolver());
+
+        RestOpenApiEndpoint.loadSpecificationFrom(camelContext, URI.create("non-existant.json"));
+    }
+
+    @Test
+    public void shouldResolveUris() {
+        final RestOpenApiEndpoint endpoint = new RestOpenApiEndpoint();
+        endpoint.parameters = new HashMap<>();
+        endpoint.parameters.put("param1", "value1");
+
+        final Map<String, OasParameter> pathParameters = new HashMap<>();
+        pathParameters.put("param1", new Oas20Parameter("param1"));
+        pathParameters.put("param2", new Oas20Parameter("param2"));
+
+        assertThat(endpoint.resolveUri("/path", pathParameters)).isEqualTo("/path");
+        assertThat(endpoint.resolveUri("/path/{param1}", pathParameters)).isEqualTo("/path/value1");
+        assertThat(endpoint.resolveUri("/{param1}/path", pathParameters)).isEqualTo("/value1/path");
+        assertThat(endpoint.resolveUri("/{param1}/path/{param2}", pathParameters)).isEqualTo("/value1/path/{param2}");
+        assertThat(endpoint.resolveUri("/{param1}/{param2}", pathParameters)).isEqualTo("/value1/{param2}");
+        assertThat(endpoint.resolveUri("/path/{param1}/to/{param2}/rest", pathParameters))
+            .isEqualTo("/path/value1/to/{param2}/rest");
+    }
+
+    @Test
+    public void shouldSerializeGivenLiteralValues() {
+        final RestOpenApiEndpoint endpoint = new RestOpenApiEndpoint();
+        endpoint.parameters = new HashMap<>();
+        endpoint.parameters.put("param", "va lue");
+
+        final OasParameter queryParameter = new Oas20Parameter("param");
+
+        assertThat(endpoint.literalQueryParameterValue(queryParameter)).isEqualTo("param=va%20lue");
+
+        final Oas20Parameter pathParameter = new Oas20Parameter("param");
+        assertThat(endpoint.literalPathParameterValue(pathParameter)).isEqualTo("va%20lue");
+    }
+
+    @Test
+    public void shouldUseDefaultSpecificationUri() throws Exception {
+        final RestOpenApiComponent component = new RestOpenApiComponent();
+
+        final RestOpenApiEndpoint endpoint = new RestOpenApiEndpoint("rest-openapi:getPetById", "getPetById", component,
+            Collections.emptyMap());
+
+        assertThat(endpoint.getSpecificationUri()).isEqualTo(RestOpenApiComponent.DEFAULT_SPECIFICATION_URI);
+    }
+
+    @Test
+    public void shouldUseDefaultSpecificationUriEvenIfHashIsPresent() throws Exception {
+        final RestOpenApiComponent component = new RestOpenApiComponent();
+
+        final RestOpenApiEndpoint endpoint = new RestOpenApiEndpoint("rest-openapi:#getPetById", "#getPetById",
+            component, Collections.emptyMap());
+
+        assertThat(endpoint.getSpecificationUri()).isEqualTo(RestOpenApiComponent.DEFAULT_SPECIFICATION_URI);
+    }
+
+}
diff --git a/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/RestOpenApiEndpointUriParsingTest.java b/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/RestOpenApiEndpointUriParsingTest.java
new file mode 100644
index 0000000..5c8c0ce
--- /dev/null
+++ b/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/RestOpenApiEndpointUriParsingTest.java
@@ -0,0 +1,68 @@
+/*
+ * 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.rest.openapi;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@RunWith(Parameterized.class)
+public class RestOpenApiEndpointUriParsingTest {
+
+    @Parameter(3)
+    public String operationId;
+
+    @Parameter(1)
+    public String remaining;
+
+    @Parameter(2)
+    public String specificationUri;
+
+    @Parameter(0)
+    public String uri;
+
+    @Test
+    public void shouldParseEndpointUri() {
+        final RestOpenApiComponent component = new RestOpenApiComponent();
+
+        final RestOpenApiEndpoint endpoint = new RestOpenApiEndpoint(specificationUri, remaining, component,
+            Collections.emptyMap());
+
+        assertThat(endpoint.getSpecificationUri().toString()).isEqualTo(specificationUri);
+        assertThat(endpoint.getOperationId()).isEqualTo(operationId);
+    }
+
+    @Parameters(name = "uri={0}, remaining={1}")
+    public static Iterable<Object[]> parameters() {
+        return Arrays.asList(params("rest-openapi:operation", "operation", "openapi.json", "operation"),
+            params("rest-openapi:my-api.json#operation", "my-api.json#operation", "my-api.json", "operation"),
+            params("rest-openapi:http://api.example.com/swagger.json#operation",
+                "http://api.example.com/swagger.json#operation", "http://api.example.com/swagger.json", "operation"));
+    }
+
+    static Object[] params(final String uri, final String remaining, final String specificationUri,
+        final String operationId) {
+        return new Object[] {uri, remaining, specificationUri, operationId};
+    }
+}
diff --git a/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/RestOpenApiEndpointV3Test.java b/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/RestOpenApiEndpointV3Test.java
new file mode 100644
index 0000000..fdc6633
--- /dev/null
+++ b/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/RestOpenApiEndpointV3Test.java
@@ -0,0 +1,462 @@
+/*
+ * 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.rest.openapi;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import io.apicurio.datamodels.core.models.common.SecurityRequirement;
+import io.apicurio.datamodels.openapi.models.OasParameter;
+import io.apicurio.datamodels.openapi.models.OasResponse;
+import io.apicurio.datamodels.openapi.v3.models.Oas30Document;
+import io.apicurio.datamodels.openapi.v3.models.Oas30Operation;
+import io.apicurio.datamodels.openapi.v3.models.Oas30Parameter;
+import io.apicurio.datamodels.openapi.v3.models.Oas30Response;
+import io.apicurio.datamodels.openapi.v3.models.Oas30SecurityScheme;
+import org.apache.camel.CamelContext;
+import org.apache.camel.impl.engine.DefaultClassResolver;
+import org.apache.camel.spi.RestConfiguration;
+import org.junit.Test;
+
+
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.entry;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class RestOpenApiEndpointV3Test {
+
+    URI componentJsonUri = URI.create("component.json");
+
+    URI endpointUri = URI.create("endpoint.json");
+
+    @Test(expected = IllegalArgumentException.class)
+    public void shouldComplainForUnknownOperations() throws Exception {
+        final CamelContext camelContext = mock(CamelContext.class);
+        when(camelContext.getClassResolver()).thenReturn(new DefaultClassResolver());
+
+        final RestOpenApiComponent component = new RestOpenApiComponent(camelContext);
+
+        final RestOpenApiEndpoint endpoint = new RestOpenApiEndpoint("rest-openapi:unknown", "unknown", component,
+            Collections.emptyMap());
+
+        endpoint.createProducer();
+    }
+
+    @Test
+    public void shouldComputeQueryParameters() {
+        final RestOpenApiEndpoint endpoint = new RestOpenApiEndpoint();
+        endpoint.parameters = new HashMap<>();
+        endpoint.parameters.put("literal", "value");
+        assertThat(endpoint.queryParameter(new Oas30Parameter())).isEqualTo("");
+        assertThat(endpoint.queryParameter(new Oas30Parameter("param"))).isEqualTo("param={param?}");
+        assertThat(endpoint.queryParameter(new Oas30Parameter("literal"))).isEqualTo("literal=value");
+    }
+
+    @Test
+    public void shouldCreateQueryParameterExpressions() {
+        Oas30Parameter oas30Parameter = new Oas30Parameter("q");
+        oas30Parameter.required = true;
+        assertThat(RestOpenApiEndpoint.queryParameterExpression(oas30Parameter))
+            .isEqualTo("q={q}");
+        oas30Parameter.required = false;
+        assertThat(RestOpenApiEndpoint.queryParameterExpression(oas30Parameter))
+            .isEqualTo("q={q?}");
+    }
+
+    @Test
+    public void shouldDetermineBasePath() {
+        final RestConfiguration restConfiguration = new RestConfiguration();
+
+        final CamelContext camelContext = mock(CamelContext.class);
+        when(camelContext.getRestConfiguration("rest-openapi", true)).thenReturn(restConfiguration);
+
+        final Oas30Document openapi = new Oas30Document();
+
+        final RestOpenApiComponent component = new RestOpenApiComponent();
+        component.setCamelContext(camelContext);
+        final RestOpenApiEndpoint endpoint = new RestOpenApiEndpoint("rest-openapi:getPetById", "getPetById", component,
+            Collections.emptyMap());
+
+        assertThat(endpoint.determineBasePath(openapi))
+            .as("When no base path is specified on component, endpoint or rest configuration it should default to `/`")
+            .isEqualTo("/");
+
+        restConfiguration.setContextPath("/rest");
+        assertThat(endpoint.determineBasePath(openapi)).as(
+            "When base path is specified in REST configuration and not specified in component the base path should be from the REST configuration")
+            .isEqualTo("/rest");
+
+        openapi.addServer("http://petstore.openapi.io", "v3 test");
+        
+
+        component.setBasePath("/component");
+        assertThat(endpoint.determineBasePath(openapi)).as(
+            "When base path is specified on the component it should take precedence over OpenApi specification and REST configuration")
+            .isEqualTo("/component");
+
+        endpoint.setBasePath("/endpoint");
+        assertThat(endpoint.determineBasePath(openapi))
+            .as("When base path is specified on the endpoint it should take precedence over any other")
+            .isEqualTo("/endpoint");
+    }
+
+    @Test
+    public void shouldDetermineEndpointParameters() {
+        final CamelContext camelContext = mock(CamelContext.class);
+
+        final RestOpenApiComponent component = new RestOpenApiComponent();
+        component.setCamelContext(camelContext);
+
+        final RestOpenApiEndpoint endpoint = new RestOpenApiEndpoint("uri", "remaining", component,
+            Collections.emptyMap());
+        endpoint.setHost("http://petstore.openapi.io");
+
+        final Oas30Document openapi = new Oas30Document();
+        final Oas30Operation operation = new Oas30Operation("get");
+        operation.createParameter();
+        assertThat(endpoint.determineEndpointParameters(openapi, operation))
+            .containsOnly(entry("host", "http://petstore.openapi.io"));
+       
+
+        component.setComponentName("xyz");
+        assertThat(endpoint.determineEndpointParameters(openapi, operation))
+            .containsOnly(entry("host", "http://petstore.openapi.io"), entry("producerComponentName", "xyz"));
+
+        List<String> consumers = new ArrayList<String>();
+        consumers.add("application/json");
+        List<String> produces = new ArrayList<String>();
+        produces.add("application/xml");
+        operation.requestBody = operation.createRequestBody();
+        operation.responses = operation.createResponses();
+        operation.responses.addResponse("200", operation.responses.createResponse("200"));
+        for (String consumer : consumers) {
+            operation.requestBody.content.put(consumer, operation.requestBody.createMediaType(consumer));
+            
+        }
+        for (String produce : produces) {
+            for (OasResponse response : operation.responses.getResponses()) {
+                Oas30Response oas30Response = (Oas30Response)response;
+                oas30Response.content.put(produce, oas30Response.createMediaType(produce));
+            }
+        }
+        
+        assertThat(endpoint.determineEndpointParameters(openapi, operation)).containsOnly(
+            entry("host", "http://petstore.openapi.io"), entry("producerComponentName", "xyz"),
+            entry("consumes", "application/xml"), entry("produces", "application/json"));
+
+        component.setProduces("application/json");
+        component.setConsumes("application/atom+xml");
+        assertThat(endpoint.determineEndpointParameters(openapi, operation)).containsOnly(
+            entry("host", "http://petstore.openapi.io"), entry("producerComponentName", "xyz"),
+            entry("consumes", "application/atom+xml"), entry("produces", "application/json"));
+
+        endpoint.setProduces("application/atom+xml");
+        endpoint.setConsumes("application/json");
+        assertThat(endpoint.determineEndpointParameters(openapi, operation)).containsOnly(
+            entry("host", "http://petstore.openapi.io"), entry("producerComponentName", "xyz"),
+            entry("consumes", "application/json"), entry("produces", "application/atom+xml"));
+
+        endpoint.setComponentName("zyx");
+        assertThat(endpoint.determineEndpointParameters(openapi, operation)).containsOnly(
+            entry("host", "http://petstore.openapi.io"), entry("producerComponentName", "zyx"),
+            entry("consumes", "application/json"), entry("produces", "application/atom+xml"));
+
+        Oas30Parameter oas30Parameter = new Oas30Parameter("q");
+        oas30Parameter.in = "query";
+        oas30Parameter.required = true;
+        operation.addParameter(oas30Parameter);
+        assertThat(endpoint.determineEndpointParameters(openapi, operation)).containsOnly(
+            entry("host", "http://petstore.openapi.io"), entry("producerComponentName", "zyx"),
+            entry("consumes", "application/json"), entry("produces", "application/atom+xml"),
+            entry("queryParameters", "q={q}"));
+
+        oas30Parameter = new Oas30Parameter("o");
+        oas30Parameter.in = "query";
+        operation.addParameter(oas30Parameter);
+        assertThat(endpoint.determineEndpointParameters(openapi, operation)).containsOnly(
+            entry("host", "http://petstore.openapi.io"), entry("producerComponentName", "zyx"),
+            entry("consumes", "application/json"), entry("produces", "application/atom+xml"),
+            entry("queryParameters", "q={q}&o={o?}"));
+    }
+
+    @Test
+    public void shouldDetermineHostFromRestConfiguration() {
+        assertThat(RestOpenApiEndpoint.hostFrom(null)).isNull();
+
+        final RestConfiguration configuration = new RestConfiguration();
+        assertThat(RestOpenApiEndpoint.hostFrom(configuration)).isNull();
+
+        configuration.setScheme("ftp");
+        assertThat(RestOpenApiEndpoint.hostFrom(configuration)).isNull();
+
+        configuration.setScheme("http");
+        assertThat(RestOpenApiEndpoint.hostFrom(configuration)).isNull();
+
+        configuration.setHost("petstore.openapi.io");
+        assertThat(RestOpenApiEndpoint.hostFrom(configuration)).isEqualTo("http://petstore.openapi.io");
+
+        configuration.setPort(80);
+        assertThat(RestOpenApiEndpoint.hostFrom(configuration)).isEqualTo("http://petstore.openapi.io");
+
+        configuration.setPort(8080);
+        assertThat(RestOpenApiEndpoint.hostFrom(configuration)).isEqualTo("http://petstore.openapi.io:8080");
+
+        configuration.setScheme("https");
+        configuration.setPort(80);
+        assertThat(RestOpenApiEndpoint.hostFrom(configuration)).isEqualTo("https://petstore.openapi.io:80");
+
+        configuration.setPort(443);
+        assertThat(RestOpenApiEndpoint.hostFrom(configuration)).isEqualTo("https://petstore.openapi.io");
+    }
+
+    @Test
+    public void shouldDetermineHostFromSpecification() {
+        final RestOpenApiComponent component = new RestOpenApiComponent();
+
+        final RestOpenApiEndpoint endpoint = new RestOpenApiEndpoint("rest-openapi:http://some-uri#getPetById",
+            "http://some-uri#getPetById", component, Collections.emptyMap());
+
+        final Oas30Document openapi = new Oas30Document();
+        openapi.addServer("http://petstore.openapi.io", "v3 test");
+        openapi.addServer("http://anotherpetstore.openapi.io", "v3 test");
+
+        assertThat(endpoint.determineHost(openapi)).isEqualTo("http://petstore.openapi.io");
+
+        openapi.getServers().clear();
+        openapi.addServer("https://petstore.openapi.io", "v3 test");
+        assertThat(endpoint.determineHost(openapi)).isEqualTo("https://petstore.openapi.io");
+    }
+
+    @Test
+    public void shouldDetermineOptions() {
+        assertThat(RestOpenApiEndpoint.determineOption(null, null, null, null)).isNull();
+
+        assertThat(RestOpenApiEndpoint.determineOption(Collections.emptyList(), Collections.emptyList(), "", ""))
+            .isNull();
+
+        assertThat(RestOpenApiEndpoint.determineOption(Arrays.asList("specification"), null, null, null))
+            .isEqualTo("specification");
+
+        assertThat(
+            RestOpenApiEndpoint.determineOption(Arrays.asList("specification"), Arrays.asList("operation"), null, null))
+                .isEqualTo("operation");
+
+        assertThat(RestOpenApiEndpoint.determineOption(Arrays.asList("specification"), Arrays.asList("operation"),
+            "component", null)).isEqualTo("component");
+
+        assertThat(RestOpenApiEndpoint.determineOption(Arrays.asList("specification"), Arrays.asList("operation"),
+            "component", "operation")).isEqualTo("operation");
+    }
+
+    @Test
+    public void shouldHonourComponentSpecificationPathProperty() throws Exception {
+        final RestOpenApiComponent component = new RestOpenApiComponent();
+        component.setSpecificationUri(componentJsonUri);
+
+        final RestOpenApiEndpoint endpoint = new RestOpenApiEndpoint("rest-openapi:getPetById", "getPetById", component,
+            Collections.emptyMap());
+
+        assertThat(endpoint.getSpecificationUri()).isEqualTo(componentJsonUri);
+    }
+
+    @Test
+    public void shouldHonourEndpointUriPathSpecificationPathProperty() throws Exception {
+        final RestOpenApiComponent component = new RestOpenApiComponent();
+        component.setSpecificationUri(componentJsonUri);
+
+        final RestOpenApiEndpoint endpoint = new RestOpenApiEndpoint("rest-openapi:endpoint.json#getPetById",
+            "endpoint.json#getPetById", component, Collections.emptyMap());
+
+        assertThat(endpoint.getSpecificationUri()).isEqualTo(endpointUri);
+    }
+
+    @Test
+    public void shouldHonourHostPrecedence() {
+        final RestConfiguration globalRestConfiguration = new RestConfiguration();
+
+        final RestConfiguration componentRestConfiguration = new RestConfiguration();
+        final RestConfiguration specificRestConfiguration = new RestConfiguration();
+
+        final CamelContext camelContext = mock(CamelContext.class);
+        when(camelContext.getRestConfiguration()).thenReturn(globalRestConfiguration);
+        when(camelContext.getRestConfiguration("rest-openapi", false)).thenReturn(componentRestConfiguration);
+        when(camelContext.getRestConfiguration("petstore", false)).thenReturn(specificRestConfiguration);
+
+        final RestOpenApiComponent component = new RestOpenApiComponent();
+        component.setCamelContext(camelContext);
+
+        final RestOpenApiEndpoint endpoint = new RestOpenApiEndpoint("petstore:http://specification-uri#getPetById",
+            "http://specification-uri#getPetById", component, Collections.emptyMap());
+
+        final Oas30Document openapi = new Oas30Document();
+        
+        assertThat(endpoint.determineHost(openapi)).isEqualTo("http://specification-uri");
+        
+        globalRestConfiguration.setHost("global-rest");
+        globalRestConfiguration.setScheme("http");
+        assertThat(endpoint.determineHost(openapi)).isEqualTo("http://global-rest");
+
+        globalRestConfiguration.setHost("component-rest");
+        globalRestConfiguration.setScheme("http");
+        assertThat(endpoint.determineHost(openapi)).isEqualTo("http://component-rest");
+
+        specificRestConfiguration.setHost("specific-rest");
+        specificRestConfiguration.setScheme("http");
+        assertThat(endpoint.determineHost(openapi)).isEqualTo("http://specific-rest");
+
+        
+
+        component.setHost("http://component");
+        assertThat(endpoint.determineHost(openapi)).isEqualTo("http://component");
+
+        endpoint.setHost("http://endpoint");
+        assertThat(endpoint.determineHost(openapi)).isEqualTo("http://endpoint");
+    }
+
+    @Test
+    public void shouldIncludeApiKeysQueryParameters() {
+        final CamelContext camelContext = mock(CamelContext.class);
+
+        final RestOpenApiComponent component = new RestOpenApiComponent();
+        component.setCamelContext(camelContext);
+
+        final RestOpenApiEndpoint endpoint = new RestOpenApiEndpoint("uri", "remaining", component,
+            Collections.emptyMap());
+        endpoint.setHost("http://petstore.openapi.io");
+
+        final Oas30Document openapi = new Oas30Document();
+        final Oas30SecurityScheme apiKeys = new Oas30SecurityScheme("key");
+        apiKeys.name = "key";
+        apiKeys.in = "header";
+        openapi.components = openapi.createComponents();
+        
+        openapi.components.addSecurityScheme("apiKeys", apiKeys);
+        
+        final Oas30Operation operation = new Oas30Operation("get");
+        Oas30Parameter oas30Parameter = new Oas30Parameter("q");
+        oas30Parameter.in = "query";
+        oas30Parameter.required = true;
+        operation.addParameter(oas30Parameter);
+        SecurityRequirement securityRequirement =  operation.createSecurityRequirement();
+        securityRequirement.addSecurityRequirementItem("apiKeys", Collections.emptyList());
+        operation.addSecurityRequirement(securityRequirement);
+        
+        
+        assertThat(endpoint.determineEndpointParameters(openapi, operation))
+            .containsOnly(entry("host", "http://petstore.openapi.io"), entry("queryParameters", "q={q}"));
+
+        apiKeys.in = "query";
+        assertThat(endpoint.determineEndpointParameters(openapi, operation))
+            .containsOnly(entry("host", "http://petstore.openapi.io"), entry("queryParameters", "key={key}&q={q}"));
+    }
+
+    @Test
+    public void shouldLoadOpenApiSpecifications() throws IOException {
+        final CamelContext camelContext = mock(CamelContext.class);
+        when(camelContext.getClassResolver()).thenReturn(new DefaultClassResolver());
+
+        assertThat(
+            RestOpenApiEndpoint.loadSpecificationFrom(camelContext, RestOpenApiComponent.DEFAULT_SPECIFICATION_URI))
+                .isNotNull();
+    }
+
+    @Test
+    public void shouldPickBestScheme() {
+        assertThat(RestOpenApiEndpoint.pickBestScheme("http", Arrays.asList("http", "https")))
+            .isEqualTo("https");
+
+        assertThat(RestOpenApiEndpoint.pickBestScheme("https", Arrays.asList("http"))).isEqualTo("http");
+
+        assertThat(RestOpenApiEndpoint.pickBestScheme("http", Collections.emptyList())).isEqualTo("http");
+
+        assertThat(RestOpenApiEndpoint.pickBestScheme("http", null)).isEqualTo("http");
+
+        assertThat(RestOpenApiEndpoint.pickBestScheme(null, Collections.emptyList())).isNull();
+
+        assertThat(RestOpenApiEndpoint.pickBestScheme(null, null)).isNull();
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void shouldRaiseExceptionsForMissingSpecifications() throws IOException {
+        final CamelContext camelContext = mock(CamelContext.class);
+        when(camelContext.getClassResolver()).thenReturn(new DefaultClassResolver());
+
+        RestOpenApiEndpoint.loadSpecificationFrom(camelContext, URI.create("non-existant.json"));
+    }
+
+    @Test
+    public void shouldResolveUris() {
+        final RestOpenApiEndpoint endpoint = new RestOpenApiEndpoint();
+        endpoint.parameters = new HashMap<>();
+        endpoint.parameters.put("param1", "value1");
+
+        final Map<String, OasParameter> pathParameters = new HashMap<>();
+        pathParameters.put("param1", new Oas30Parameter("param1"));
+        pathParameters.put("param2", new Oas30Parameter("param2"));
+
+        assertThat(endpoint.resolveUri("/path", pathParameters)).isEqualTo("/path");
+        assertThat(endpoint.resolveUri("/path/{param1}", pathParameters)).isEqualTo("/path/value1");
+        assertThat(endpoint.resolveUri("/{param1}/path", pathParameters)).isEqualTo("/value1/path");
+        assertThat(endpoint.resolveUri("/{param1}/path/{param2}", pathParameters)).isEqualTo("/value1/path/{param2}");
+        assertThat(endpoint.resolveUri("/{param1}/{param2}", pathParameters)).isEqualTo("/value1/{param2}");
+        assertThat(endpoint.resolveUri("/path/{param1}/to/{param2}/rest", pathParameters))
+            .isEqualTo("/path/value1/to/{param2}/rest");
+    }
+
+    @Test
+    public void shouldSerializeGivenLiteralValues() {
+        final RestOpenApiEndpoint endpoint = new RestOpenApiEndpoint();
+        endpoint.parameters = new HashMap<>();
+        endpoint.parameters.put("param", "va lue");
+
+        final OasParameter queryParameter = new Oas30Parameter("param");
+
+        assertThat(endpoint.literalQueryParameterValue(queryParameter)).isEqualTo("param=va%20lue");
+
+        final Oas30Parameter pathParameter = new Oas30Parameter("param");
+        assertThat(endpoint.literalPathParameterValue(pathParameter)).isEqualTo("va%20lue");
+    }
+
+    @Test
+    public void shouldUseDefaultSpecificationUri() throws Exception {
+        final RestOpenApiComponent component = new RestOpenApiComponent();
+
+        final RestOpenApiEndpoint endpoint = new RestOpenApiEndpoint("rest-openapi:getPetById", "getPetById", component,
+            Collections.emptyMap());
+
+        assertThat(endpoint.getSpecificationUri()).isEqualTo(RestOpenApiComponent.DEFAULT_SPECIFICATION_URI);
+    }
+
+    @Test
+    public void shouldUseDefaultSpecificationUriEvenIfHashIsPresent() throws Exception {
+        final RestOpenApiComponent component = new RestOpenApiComponent();
+
+        final RestOpenApiEndpoint endpoint = new RestOpenApiEndpoint("rest-openapi:#getPetById", "#getPetById",
+            component, Collections.emptyMap());
+
+        assertThat(endpoint.getSpecificationUri()).isEqualTo(RestOpenApiComponent.DEFAULT_SPECIFICATION_URI);
+    }
+
+}
diff --git a/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/RestOpenApiGlobalHttpsTest.java b/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/RestOpenApiGlobalHttpsTest.java
new file mode 100644
index 0000000..bbbca82
--- /dev/null
+++ b/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/RestOpenApiGlobalHttpsTest.java
@@ -0,0 +1,34 @@
+/*
+ * 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.rest.openapi;
+
+import org.apache.camel.CamelContext;
+
+
+public class RestOpenApiGlobalHttpsTest extends HttpsTest {
+
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        CamelContext camelContext = super.createCamelContext();
+        camelContext.setSSLContextParameters(createHttpsParameters(camelContext));
+
+        RestOpenApiComponent component = camelContext.getComponent("petStore", RestOpenApiComponent.class);
+        component.setUseGlobalSslContextParameters(true);
+
+        return camelContext;
+    }
+}
diff --git a/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/RestOpenApiGlobalHttpsV3Test.java b/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/RestOpenApiGlobalHttpsV3Test.java
new file mode 100644
index 0000000..a43a27b
--- /dev/null
+++ b/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/RestOpenApiGlobalHttpsV3Test.java
@@ -0,0 +1,33 @@
+/*
+ * 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.rest.openapi;
+
+import org.apache.camel.CamelContext;
+
+public class RestOpenApiGlobalHttpsV3Test extends HttpsV3Test {
+
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        CamelContext camelContext = super.createCamelContext();
+        camelContext.setSSLContextParameters(createHttpsParameters(camelContext));
+
+        RestOpenApiComponent component = camelContext.getComponent("petStore", RestOpenApiComponent.class);
+        component.setUseGlobalSslContextParameters(true);
+
+        return camelContext;
+    }
+}
diff --git a/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/RestOpenApiHelperTest.java b/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/RestOpenApiHelperTest.java
new file mode 100644
index 0000000..26cb47d
--- /dev/null
+++ b/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/RestOpenApiHelperTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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.rest.openapi;
+
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+
+
+public class RestOpenApiHelperTest {
+
+    @Test(expected = IllegalArgumentException.class)
+    public void emptyHostParamsAreNotAllowed() {
+        RestOpenApiHelper.isHostParam("");
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void nonUriHostParametersAreNotAllowed() {
+        RestOpenApiHelper.isHostParam("carrot");
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void nullHostParamsAreNotAllowed() {
+        RestOpenApiHelper.isHostParam(null);
+    }
+
+    @Test
+    public void shouldNiceify() {
+        assertThat(RestOpenApiHelper.isHostParam("http://api.example.com")).isEqualTo("http://api.example.com");
+    }
+
+    @Test
+    public void shouldReturnUriParameter() {
+        assertThat(RestOpenApiHelper.isHostParam("http://api.example.com")).isEqualTo("http://api.example.com");
+    }
+
+}
diff --git a/components/camel-rest-openapi/src/test/resources/alt-openapi.json b/components/camel-rest-openapi/src/test/resources/alt-openapi.json
new file mode 100644
index 0000000..19c19d6
--- /dev/null
+++ b/components/camel-rest-openapi/src/test/resources/alt-openapi.json
@@ -0,0 +1 @@
+{"openapi":"3.0.2","info":{"title":"Swagger Petstore - OpenAPI 3.0","description":"This is a sample Pet Store Server based on the OpenAPI 3.0 specification.  You can find out more about\nSwagger at [http://swagger.io](http://swagger.io). In the third iteration of the pet store, we've switched to the design first approach!\nYou can now help us improve the API whether it's by making changes to the definition itself or to the code.\nThat way, with time, we can improve the API in general, and expose some of the new features in OAS3.\n\nSome useful links:\n- [The Pet Store repository](https://github.com/swagger-api/swagger-petstore)\n- [The source API definition for the Pet Store](https://github.com/swagger-api/swagger-petstore/blob/master/src/main/resources/openapi.yaml) ","termsOfService":"http://swagger.io/terms/","contact":{"email":"apiteam@swagger.io"},"license":{"name":"Apache 2.0","url":"http://www.apache.org/licenses/LICENSE-2.0.html"},"version":"1.0.4"},"externalDocs":{"description":"Find out more about Swagger","url":"http://swagger.io"},"servers":[{"url":"/api/v3"}],"tags":[{"name":"pet","description":"Everything about your Pets","externalDocs":{"description":"Find out more","url":"http://swagger.io"}},{"name":"store","description":"Operations about user"},{"name":"user","description":"Access to Petstore orders","externalDocs":{"description":"Find out more about our store","url":"http://swagger.io"}}],"paths":{"/pet":{"put":{"tags":["pet"],"summary":"Update an existing pet","description":"Update an existing pet by Id","operationId":"updatePet","requestBody":{"description":"Update an existent pet in the store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Pet"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Pet"}},"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Pet"}}},"required":true},"responses":{"200":{"description":"Successful operation","content":{"application/xml":{"schema":{"$ref":"#/components/schemas/Pet"}},"application/json":{"schema":{"$ref":"#/components/schemas/Pet"}}}},"400":{"description":"Invalid ID supplied"},"404":{"description":"Pet not found"},"405":{"description":"Validation exception"}},"security":[{"petstore_auth":["write:pets","read:pets"]}]},"post":{"tags":["pet"],"summary":"Add a new pet to the store","description":"Add a new pet to the store","operationId":"addPet","requestBody":{"description":"Create a new pet in the store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Pet"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Pet"}},"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Pet"}}},"required":true},"responses":{"200":{"description":"Successful operation","content":{"application/xml":{"schema":{"$ref":"#/components/schemas/Pet"}},"application/json":{"schema":{"$ref":"#/components/schemas/Pet"}}}},"405":{"description":"Invalid input"}},"security":[{"petstore_auth":["write:pets","read:pets"]}]}},"/pet/findByStatus":{"get":{"tags":["pet"],"summary":"Finds Pets by status","description":"Multiple status values can be provided with comma separated strings","operationId":"findPetsByStatus","parameters":[{"name":"status","in":"query","description":"Status values that need to be considered for filter","required":false,"explode":true,"schema":{"type":"string","default":"available","enum":["available","pending","sold"]}}],"responses":{"200":{"description":"successful operation","content":{"application/xml":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Pet"}}},"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Pet"}}}}},"400":{"description":"Invalid status value"}},"security":[{"petstore_auth":["write:pets","read:pets"]}]}},"/pet/findByTags":{"get":{"tags":["pet"],"summary":"Finds Pets by tags","description":"Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.","operationId":"findPetsByTags","parameters":[{"name":"tags","in":"query","description":"Tags to filter by","required":false,"explode":true,"schema":{"type":"array","items":{"type":"string"}}}],"responses":{"200":{"description":"successful operation","content":{"application/xml":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Pet"}}},"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Pet"}}}}},"400":{"description":"Invalid tag value"}},"security":[{"petstore_auth":["write:pets","read:pets"]}]}},"/pet/{petId}":{"get":{"tags":["pet"],"summary":"Find pet by ID","description":"Returns a single pet","operationId":"getPetById","parameters":[{"name":"petId","in":"path","description":"ID of pet to return","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"successful operation","content":{"application/xml":{"schema":{"$ref":"#/components/schemas/Pet"}},"application/json":{"schema":{"$ref":"#/components/schemas/Pet"}}}},"400":{"description":"Invalid ID supplied"},"404":{"description":"Pet not found"}},"security":[{"api_key":[]},{"petstore_auth":["write:pets","read:pets"]}]},"post":{"tags":["pet"],"summary":"Updates a pet in the store with form data","description":"","operationId":"updatePetWithForm","parameters":[{"name":"petId","in":"path","description":"ID of pet that needs to be updated","required":true,"schema":{"type":"integer","format":"int64"}},{"name":"name","in":"query","description":"Name of pet that needs to be updated","schema":{"type":"string"}},{"name":"status","in":"query","description":"Status of pet that needs to be updated","schema":{"type":"string"}}],"responses":{"405":{"description":"Invalid input"}},"security":[{"petstore_auth":["write:pets","read:pets"]}]},"delete":{"tags":["pet"],"summary":"Deletes a pet","description":"","operationId":"deletePet","parameters":[{"name":"api_key","in":"header","description":"","required":false,"schema":{"type":"string"}},{"name":"petId","in":"path","description":"Pet id to delete","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"400":{"description":"Invalid pet value"}},"security":[{"petstore_auth":["write:pets","read:pets"]}]}},"/pet/{petId}/uploadImage":{"post":{"tags":["pet"],"summary":"uploads an image","description":"","operationId":"uploadFile","parameters":[{"name":"petId","in":"path","description":"ID of pet to update","required":true,"schema":{"type":"integer","format":"int64"}},{"name":"additionalMetadata","in":"query","description":"Additional Metadata","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"responses":{"200":{"description":"successful operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiResponse"}}}}},"security":[{"petstore_auth":["write:pets","read:pets"]}]}},"/store/inventory":{"get":{"tags":["store"],"summary":"Returns pet inventories by status","description":"Returns a map of status codes to quantities","operationId":"getInventory","responses":{"200":{"description":"successful operation","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"integer","format":"int32"}}}}}},"security":[{"api_key":[]}]}},"/store/order":{"post":{"tags":["store"],"summary":"Place an order for a pet","description":"Place a new order in the store","operationId":"placeOrder","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Order"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Order"}},"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Order"}}}},"responses":{"200":{"description":"successful operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Order"}}}},"405":{"description":"Invalid input"}}}},"/store/order/{orderId}":{"get":{"tags":["store"],"summary":"Find purchase order by ID","description":"For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions","operationId":"getOrderById","parameters":[{"name":"orderId","in":"path","description":"ID of order that needs to be fetched","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"successful operation","content":{"application/xml":{"schema":{"$ref":"#/components/schemas/Order"}},"application/json":{"schema":{"$ref":"#/components/schemas/Order"}}}},"400":{"description":"Invalid ID supplied"},"404":{"description":"Order not found"}}},"delete":{"tags":["store"],"summary":"Delete purchase order by ID","description":"For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors","operationId":"deleteOrder","parameters":[{"name":"orderId","in":"path","description":"ID of the order that needs to be deleted","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"400":{"description":"Invalid ID supplied"},"404":{"description":"Order not found"}}}},"/user":{"post":{"tags":["user"],"summary":"Create user","description":"This can only be done by the logged in user.","operationId":"createUser","requestBody":{"description":"Created user object","content":{"application/json":{"schema":{"$ref":"#/components/schemas/User"}},"application/xml":{"schema":{"$ref":"#/components/schemas/User"}},"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/User"}}}},"responses":{"default":{"description":"successful operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/User"}},"application/xml":{"schema":{"$ref":"#/components/schemas/User"}}}}}}},"/user/createWithList":{"post":{"tags":["user"],"summary":"Creates list of users with given input array","description":"Creates list of users with given input array","operationId":"createUsersWithListInput","requestBody":{"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/User"}}}}},"responses":{"200":{"description":"Successful operation","content":{"application/xml":{"schema":{"$ref":"#/components/schemas/User"}},"application/json":{"schema":{"$ref":"#/components/schemas/User"}}}},"default":{"description":"successful operation"}}}},"/user/login":{"get":{"tags":["user"],"summary":"Logs user into the system","description":"","operationId":"loginUser","parameters":[{"name":"username","in":"query","description":"The user name for login","required":false,"schema":{"type":"string"}},{"name":"password","in":"query","description":"The password for login in clear text","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"successful operation","headers":{"X-Rate-Limit":{"description":"calls per hour allowed by the user","schema":{"type":"integer","format":"int32"}},"X-Expires-After":{"description":"date in UTC when toekn expires","schema":{"type":"string","format":"date-time"}}},"content":{"application/xml":{"schema":{"type":"string"}},"application/json":{"schema":{"type":"string"}}}},"400":{"description":"Invalid username/password supplied"}}}},"/user/logout":{"get":{"tags":["user"],"summary":"Logs out current logged in user session","description":"","operationId":"logoutUser","parameters":[],"responses":{"default":{"description":"successful operation"}}}},"/user/{username}":{"get":{"tags":["user"],"summary":"Get user by user name","description":"","operationId":"getUserByName","parameters":[{"name":"username","in":"path","description":"The name that needs to be fetched. Use user1 for testing. ","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"successful operation","content":{"application/xml":{"schema":{"$ref":"#/components/schemas/User"}},"application/json":{"schema":{"$ref":"#/components/schemas/User"}}}},"400":{"description":"Invalid username supplied"},"404":{"description":"User not found"}}},"put":{"tags":["user"],"summary":"Update user","description":"This can only be done by the logged in user.","operationId":"updateUser","parameters":[{"name":"username","in":"path","description":"name that need to be deleted","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"Update an existent user in the store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/User"}},"application/xml":{"schema":{"$ref":"#/components/schemas/User"}},"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/User"}}}},"responses":{"default":{"description":"successful operation"}}},"delete":{"tags":["user"],"summary":"Delete user","description":"This can only be done by the logged in user.","operationId":"deleteUser","parameters":[{"name":"username","in":"path","description":"The name that needs to be deleted","required":true,"schema":{"type":"string"}}],"responses":{"400":{"description":"Invalid username supplied"},"404":{"description":"User not found"}}}}},"components":{"schemas":{"Order":{"type":"object","properties":{"id":{"type":"integer","format":"int64","example":10},"petId":{"type":"integer","format":"int64","example":198772},"quantity":{"type":"integer","format":"int32","example":7},"shipDate":{"type":"string","format":"date-time"},"status":{"type":"string","description":"Order Status","example":"approved","enum":["placed","approved","delivered"]},"complete":{"type":"boolean"}},"xml":{"name":"order"}},"Customer":{"type":"object","properties":{"id":{"type":"integer","format":"int64","example":100000},"username":{"type":"string","example":"fehguy"},"address":{"type":"array","xml":{"name":"addresses","wrapped":true},"items":{"$ref":"#/components/schemas/Address"}}},"xml":{"name":"customer"}},"Address":{"type":"object","properties":{"street":{"type":"string","example":"437 Lytton"},"city":{"type":"string","example":"Palo Alto"},"state":{"type":"string","example":"CA"},"zip":{"type":"string","example":"94301"}},"xml":{"name":"address"}},"Category":{"type":"object","properties":{"id":{"type":"integer","format":"int64","example":1},"name":{"type":"string","example":"Dogs"}},"xml":{"name":"category"}},"User":{"type":"object","properties":{"id":{"type":"integer","format":"int64","example":10},"username":{"type":"string","example":"theUser"},"firstName":{"type":"string","example":"John"},"lastName":{"type":"string","example":"James"},"email":{"type":"string","example":"john@email.com"},"password":{"type":"string","example":"12345"},"phone":{"type":"string","example":"12345"},"userStatus":{"type":"integer","description":"User Status","format":"int32","example":1}},"xml":{"name":"user"}},"Tag":{"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"}},"xml":{"name":"tag"}},"Pet":{"required":["name","photoUrls"],"type":"object","properties":{"id":{"type":"integer","format":"int64","example":10},"name":{"type":"string","example":"doggie"},"category":{"$ref":"#/components/schemas/Category"},"photoUrls":{"type":"array","xml":{"wrapped":true},"items":{"type":"string","xml":{"name":"photoUrl"}}},"tags":{"type":"array","xml":{"wrapped":true},"items":{"$ref":"#/components/schemas/Tag"}},"status":{"type":"string","description":"pet status in the store","enum":["available","pending","sold"]}},"xml":{"name":"pet"}},"ApiResponse":{"type":"object","properties":{"code":{"type":"integer","format":"int32"},"type":{"type":"string"},"message":{"type":"string"}},"xml":{"name":"##default"}}},"requestBodies":{"Pet":{"description":"Pet object that needs to be added to the store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Pet"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Pet"}}}},"UserArray":{"description":"List of user object","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/User"}}}}}},"securitySchemes":{"petstore_auth":{"type":"oauth2","flows":{"implicit":{"authorizationUrl":"https://petstore3.swagger.io/oauth/authorize","scopes":{"write:pets":"modify pets in your account","read:pets":"read your pets"}}}},"api_key":{"type":"apiKey","name":"api_key","in":"query"}}}}
diff --git a/components/camel-rest-openapi/src/test/resources/alt-petstore.json b/components/camel-rest-openapi/src/test/resources/alt-petstore.json
new file mode 100644
index 0000000..12cd770
--- /dev/null
+++ b/components/camel-rest-openapi/src/test/resources/alt-petstore.json
@@ -0,0 +1,1039 @@
+{
+    "swagger": "2.0",
+    "info": {
+        "description": "This is a sample server Petstore server.  You can find out more about OpenApi at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/).  For this sample, you can use the api key `special-key` to test the authorization filters.",
+        "version": "1.0.0",
+        "title": "OpenApi Petstore",
+        "termsOfService": "http://swagger.io/terms/",
+        "contact": {
+            "email": "apiteam@swagger.io"
+        },
+        "license": {
+            "name": "Apache 2.0",
+            "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
+        }
+    },
+    "host": "petstore.swagger.io",
+    "basePath": "/v2",
+    "tags": [
+        {
+            "name": "pet",
+            "description": "Everything about your Pets",
+            "externalDocs": {
+                "description": "Find out more",
+                "url": "http://swagger.io"
+            }
+        },
+        {
+            "name": "store",
+            "description": "Access to Petstore orders"
+        },
+        {
+            "name": "user",
+            "description": "Operations about user",
+            "externalDocs": {
+                "description": "Find out more about our store",
+                "url": "http://swagger.io"
+            }
+        }
+    ],
+    "schemes": [
+        "http"
+    ],
+    "paths": {
+        "/pet": {
+            "post": {
+                "tags": [
+                    "pet"
+                ],
+                "summary": "Add a new pet to the store",
+                "description": "",
+                "operationId": "addPet",
+                "consumes": [
+                    "application/json",
+                    "application/xml"
+                ],
+                "produces": [
+                    "application/xml",
+                    "application/json"
+                ],
+                "parameters": [
+                    {
+                        "in": "body",
+                        "name": "body",
+                        "description": "Pet object that needs to be added to the store",
+                        "required": true,
+                        "schema": {
+                            "$ref": "#/definitions/Pet"
+                        }
+                    }
+                ],
+                "responses": {
+                    "405": {
+                        "description": "Invalid input"
+                    }
+                },
+                "security": [
+                    {
+                        "petstore_auth": [
+                            "write:pets",
+                            "read:pets"
+                        ]
+                    }
+                ]
+            },
+            "put": {
+                "tags": [
+                    "pet"
+                ],
+                "summary": "Update an existing pet",
+                "description": "",
+                "operationId": "updatePet",
+                "consumes": [
+                    "application/json",
+                    "application/xml"
+                ],
+                "produces": [
+                    "application/xml",
+                    "application/json"
+                ],
+                "parameters": [
+                    {
+                        "in": "body",
+                        "name": "body",
+                        "description": "Pet object that needs to be added to the store",
+                        "required": true,
+                        "schema": {
+                            "$ref": "#/definitions/Pet"
+                        }
+                    }
+                ],
+                "responses": {
+                    "400": {
+                        "description": "Invalid ID supplied"
+                    },
+                    "404": {
+                        "description": "Pet not found"
+                    },
+                    "405": {
+                        "description": "Validation exception"
+                    }
+                },
+                "security": [
+                    {
+                        "petstore_auth": [
+                            "write:pets",
+                            "read:pets"
+                        ]
+                    }
+                ]
+            }
+        },
+        "/pet/findByStatus": {
+            "get": {
+                "tags": [
+                    "pet"
+                ],
+                "summary": "Finds Pets by status",
+                "description": "Multiple status values can be provided with comma separated strings",
+                "operationId": "findPetsByStatus",
+                "produces": [
+                    "application/xml",
+                    "application/json"
+                ],
+                "parameters": [
+                    {
+                        "name": "status",
+                        "in": "query",
+                        "description": "Status values that need to be considered for filter",
+                        "required": true,
+                        "type": "array",
+                        "items": {
+                            "type": "string",
+                            "enum": [
+                                "available",
+                                "pending",
+                                "sold"
+                            ],
+                            "default": "available"
+                        },
+                        "collectionFormat": "multi"
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "successful operation",
+                        "schema": {
+                            "type": "array",
+                            "items": {
+                                "$ref": "#/definitions/Pet"
+                            }
+                        }
+                    },
+                    "400": {
+                        "description": "Invalid status value"
+                    }
+                },
+                "security": [
+                    {
+                        "petstore_auth": [
+                            "write:pets",
+                            "read:pets"
+                        ]
+                    }
+                ]
+            }
+        },
+        "/pet/findByTags": {
+            "get": {
+                "tags": [
+                    "pet"
+                ],
+                "summary": "Finds Pets by tags",
+                "description": "Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.",
+                "operationId": "findPetsByTags",
+                "produces": [
+                    "application/xml",
+                    "application/json"
+                ],
+                "parameters": [
+                    {
+                        "name": "tags",
+                        "in": "query",
+                        "description": "Tags to filter by",
+                        "required": true,
+                        "type": "array",
+                        "items": {
+                            "type": "string"
+                        },
+                        "collectionFormat": "multi"
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "successful operation",
+                        "schema": {
+                            "type": "array",
+                            "items": {
+                                "$ref": "#/definitions/Pet"
+                            }
+                        }
+                    },
+                    "400": {
+                        "description": "Invalid tag value"
+                    }
+                },
+                "security": [
+                    {
+                        "petstore_auth": [
+                            "write:pets",
+                            "read:pets"
+                        ]
+                    }
+                ],
+                "deprecated": true
+            }
+        },
+        "/pet/{petId}": {
+            "get": {
+                "tags": [
+                    "pet"
+                ],
+                "summary": "Find pet by ID",
+                "description": "Returns a single pet",
+                "operationId": "getPetById",
+                "produces": [
+                    "application/xml",
+                    "application/json"
+                ],
+                "parameters": [
+                    {
+                        "name": "petId",
+                        "in": "path",
+                        "description": "ID of pet to return",
+                        "required": true,
+                        "type": "integer",
+                        "format": "int64"
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "successful operation",
+                        "schema": {
+                            "$ref": "#/definitions/Pet"
+                        }
+                    },
+                    "400": {
+                        "description": "Invalid ID supplied"
+                    },
+                    "404": {
+                        "description": "Pet not found"
+                    }
+                },
+                "security": [
+                    {
+                        "api_key": [
+                        ]
+                    }
+                ]
+            },
+            "post": {
+                "tags": [
+                    "pet"
+                ],
+                "summary": "Updates a pet in the store with form data",
+                "description": "",
+                "operationId": "updatePetWithForm",
+                "consumes": [
+                    "application/x-www-form-urlencoded"
+                ],
+                "produces": [
+                    "application/xml",
+                    "application/json"
+                ],
+                "parameters": [
+                    {
+                        "name": "petId",
+                        "in": "path",
+                        "description": "ID of pet that needs to be updated",
+                        "required": true,
+                        "type": "integer",
+                        "format": "int64"
+                    },
+                    {
+                        "name": "name",
+                        "in": "formData",
+                        "description": "Updated name of the pet",
+                        "required": false,
+                        "type": "string"
+                    },
+                    {
+                        "name": "status",
+                        "in": "formData",
+                        "description": "Updated status of the pet",
+                        "required": false,
+                        "type": "string"
+                    }
+                ],
+                "responses": {
+                    "405": {
+                        "description": "Invalid input"
+                    }
+                },
+                "security": [
+                    {
+                        "petstore_auth": [
+                            "write:pets",
+                            "read:pets"
+                        ]
+                    }
+                ]
+            },
+            "delete": {
+                "tags": [
+                    "pet"
+                ],
+                "summary": "Deletes a pet",
+                "description": "",
+                "operationId": "deletePet",
+                "produces": [
+                    "application/xml",
+                    "application/json"
+                ],
+                "parameters": [
+                    {
+                        "name": "api_key",
+                        "in": "header",
+                        "required": false,
+                        "type": "string"
+                    },
+                    {
+                        "name": "petId",
+                        "in": "path",
+                        "description": "Pet id to delete",
+                        "required": true,
+                        "type": "integer",
+                        "format": "int64"
+                    }
+                ],
+                "responses": {
+                    "400": {
+                        "description": "Invalid ID supplied"
+                    },
+                    "404": {
+                        "description": "Pet not found"
+                    }
+                },
+                "security": [
+                    {
+                        "petstore_auth": [
+                            "write:pets",
+                            "read:pets"
+                        ]
+                    }
+                ]
+            }
+        },
+        "/pet/{petId}/uploadImage": {
+            "post": {
+                "tags": [
+                    "pet"
+                ],
+                "summary": "uploads an image",
+                "description": "",
+                "operationId": "uploadFile",
+                "consumes": [
+                    "multipart/form-data"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "parameters": [
+                    {
+                        "name": "petId",
+                        "in": "path",
+                        "description": "ID of pet to update",
+                        "required": true,
+                        "type": "integer",
+                        "format": "int64"
+                    },
+                    {
+                        "name": "additionalMetadata",
+                        "in": "formData",
+                        "description": "Additional data to pass to server",
+                        "required": false,
+                        "type": "string"
+                    },
+                    {
+                        "name": "file",
+                        "in": "formData",
+                        "description": "file to upload",
+                        "required": false,
+                        "type": "file"
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "successful operation",
+                        "schema": {
+                            "$ref": "#/definitions/ApiResponse"
+                        }
+                    }
+                },
+                "security": [
+                    {
+                        "petstore_auth": [
+                            "write:pets",
+                            "read:pets"
+                        ]
+                    }
+                ]
+            }
+        },
+        "/store/inventory": {
+            "get": {
+                "tags": [
+                    "store"
+                ],
+                "summary": "Returns pet inventories by status",
+                "description": "Returns a map of status codes to quantities",
+                "operationId": "getInventory",
+                "produces": [
+                    "application/json"
+                ],
+                "parameters": [
+                ],
+                "responses": {
+                    "200": {
+                        "description": "successful operation",
+                        "schema": {
+                            "type": "object",
+                            "additionalProperties": {
+                                "type": "integer",
+                                "format": "int32"
+                            }
+                        }
+                    }
+                },
+                "security": [
+                    {
+                        "api_key": [
+                        ]
+                    }
+                ]
+            }
+        },
+        "/store/order": {
+            "post": {
+                "tags": [
+                    "store"
+                ],
+                "summary": "Place an order for a pet",
+                "description": "",
+                "operationId": "placeOrder",
+                "produces": [
+                    "application/xml",
+                    "application/json"
+                ],
+                "parameters": [
+                    {
+                        "in": "body",
+                        "name": "body",
+                        "description": "order placed for purchasing the pet",
+                        "required": true,
+                        "schema": {
+                            "$ref": "#/definitions/Order"
+                        }
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "successful operation",
+                        "schema": {
+                            "$ref": "#/definitions/Order"
+                        }
+                    },
+                    "400": {
+                        "description": "Invalid Order"
+                    }
+                }
+            }
+        },
+        "/store/order/{orderId}": {
+            "get": {
+                "tags": [
+                    "store"
+                ],
+                "summary": "Find purchase order by ID",
+                "description": "For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions",
+                "operationId": "getOrderById",
+                "produces": [
+                    "application/xml",
+                    "application/json"
+                ],
+                "parameters": [
+                    {
+                        "name": "orderId",
+                        "in": "path",
+                        "description": "ID of pet that needs to be fetched",
+                        "required": true,
+                        "type": "integer",
+                        "maximum": 10.0,
+                        "minimum": 1.0,
+                        "format": "int64"
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "successful operation",
+                        "schema": {
+                            "$ref": "#/definitions/Order"
+                        }
+                    },
+                    "400": {
+                        "description": "Invalid ID supplied"
+                    },
+                    "404": {
+                        "description": "Order not found"
+                    }
+                }
+            },
+            "delete": {
+                "tags": [
+                    "store"
+                ],
+                "summary": "Delete purchase order by ID",
+                "description": "For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors",
+                "operationId": "deleteOrder",
+                "produces": [
+                    "application/xml",
+                    "application/json"
+                ],
+                "parameters": [
+                    {
+                        "name": "orderId",
+                        "in": "path",
+                        "description": "ID of the order that needs to be deleted",
+                        "required": true,
+                        "type": "integer",
+                        "minimum": 1.0,
+                        "format": "int64"
+                    }
+                ],
+                "responses": {
+                    "400": {
+                        "description": "Invalid ID supplied"
+                    },
+                    "404": {
+                        "description": "Order not found"
+                    }
+                }
+            }
+        },
+        "/user": {
+            "post": {
+                "tags": [
+                    "user"
+                ],
+                "summary": "Create user",
+                "description": "This can only be done by the logged in user.",
+                "operationId": "createUser",
+                "produces": [
+                    "application/xml",
+                    "application/json"
+                ],
+                "parameters": [
+                    {
+                        "in": "body",
+                        "name": "body",
+                        "description": "Created user object",
+                        "required": true,
+                        "schema": {
+                            "$ref": "#/definitions/User"
+                        }
+                    }
+                ],
+                "responses": {
+                    "default": {
+                        "description": "successful operation"
+                    }
+                }
+            }
+        },
+        "/user/createWithArray": {
+            "post": {
+                "tags": [
+                    "user"
+                ],
+                "summary": "Creates list of users with given input array",
+                "description": "",
+                "operationId": "createUsersWithArrayInput",
+                "produces": [
+                    "application/xml",
+                    "application/json"
+                ],
+                "parameters": [
+                    {
+                        "in": "body",
+                        "name": "body",
+                        "description": "List of user object",
+                        "required": true,
+                        "schema": {
+                            "type": "array",
+                            "items": {
+                                "$ref": "#/definitions/User"
+                            }
+                        }
+                    }
+                ],
+                "responses": {
+                    "default": {
+                        "description": "successful operation"
+                    }
+                }
+            }
+        },
+        "/user/createWithList": {
+            "post": {
+                "tags": [
+                    "user"
+                ],
+                "summary": "Creates list of users with given input array",
+                "description": "",
+                "operationId": "createUsersWithListInput",
+                "produces": [
+                    "application/xml",
+                    "application/json"
+                ],
+                "parameters": [
+                    {
+                        "in": "body",
+                        "name": "body",
+                        "description": "List of user object",
+                        "required": true,
+                        "schema": {
+                            "type": "array",
+                            "items": {
+                                "$ref": "#/definitions/User"
+                            }
+                        }
+                    }
+                ],
+                "responses": {
+                    "default": {
+                        "description": "successful operation"
+                    }
+                }
+            }
+        },
+        "/user/login": {
+            "get": {
+                "tags": [
+                    "user"
+                ],
+                "summary": "Logs user into the system",
+                "description": "",
+                "operationId": "loginUser",
+                "produces": [
+                    "application/xml",
+                    "application/json"
+                ],
+                "parameters": [
+                    {
+                        "name": "username",
+                        "in": "query",
+                        "description": "The user name for login",
+                        "required": true,
+                        "type": "string"
+                    },
+                    {
+                        "name": "password",
+                        "in": "query",
+                        "description": "The password for login in clear text",
+                        "required": true,
+                        "type": "string"
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "successful operation",
+                        "schema": {
+                            "type": "string"
+                        },
+                        "headers": {
+                            "X-Rate-Limit": {
+                                "type": "integer",
+                                "format": "int32",
+                                "description": "calls per hour allowed by the user"
+                            },
+                            "X-Expires-After": {
+                                "type": "string",
+                                "format": "date-time",
+                                "description": "date in UTC when token expires"
+                            }
+                        }
+                    },
+                    "400": {
+                        "description": "Invalid username/password supplied"
+                    }
+                }
+            }
+        },
+        "/user/logout": {
+            "get": {
+                "tags": [
+                    "user"
+                ],
+                "summary": "Logs out current logged in user session",
+                "description": "",
+                "operationId": "logoutUser",
+                "produces": [
+                    "application/xml",
+                    "application/json"
+                ],
+                "parameters": [
+                ],
+                "responses": {
+                    "default": {
+                        "description": "successful operation"
+                    }
+                }
+            }
+        },
+        "/user/{username}": {
+            "get": {
+                "tags": [
+                    "user"
+                ],
+                "summary": "Get user by user name",
+                "description": "",
+                "operationId": "getUserByName",
+                "produces": [
+                    "application/xml",
+                    "application/json"
+                ],
+                "parameters": [
+                    {
+                        "name": "username",
+                        "in": "path",
+                        "description": "The name that needs to be fetched. Use user1 for testing. ",
+                        "required": true,
+                        "type": "string"
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "successful operation",
+                        "schema": {
+                            "$ref": "#/definitions/User"
+                        }
+                    },
+                    "400": {
+                        "description": "Invalid username supplied"
+                    },
+                    "404": {
+                        "description": "User not found"
+                    }
+                }
+            },
+            "put": {
+                "tags": [
+                    "user"
+                ],
+                "summary": "Updated user",
+                "description": "This can only be done by the logged in user.",
+                "operationId": "updateUser",
+                "produces": [
+                    "application/xml",
+                    "application/json"
+                ],
+                "parameters": [
+                    {
+                        "name": "username",
+                        "in": "path",
+                        "description": "name that need to be updated",
+                        "required": true,
+                        "type": "string"
+                    },
+                    {
+                        "in": "body",
+                        "name": "body",
+                        "description": "Updated user object",
+                        "required": true,
+                        "schema": {
+                            "$ref": "#/definitions/User"
+                        }
+                    }
+                ],
+                "responses": {
+                    "400": {
+                        "description": "Invalid user supplied"
+                    },
+                    "404": {
+                        "description": "User not found"
+                    }
+                }
+            },
+            "delete": {
+                "tags": [
+                    "user"
+                ],
+                "summary": "Delete user",
+                "description": "This can only be done by the logged in user.",
+                "operationId": "deleteUser",
+                "produces": [
+                    "application/xml",
+                    "application/json"
+                ],
+                "parameters": [
+                    {
+                        "name": "username",
+                        "in": "path",
+                        "description": "The name that needs to be deleted",
+                        "required": true,
+                        "type": "string"
+                    }
+                ],
+                "responses": {
+                    "400": {
+                        "description": "Invalid username supplied"
+                    },
+                    "404": {
+                        "description": "User not found"
+                    }
+                }
+            }
+        }
+    },
+    "securityDefinitions": {
+        "petstore_auth": {
+            "type": "oauth2",
+            "authorizationUrl": "http://petstore.swagger.io/oauth/dialog",
+            "flow": "implicit",
+            "scopes": {
+                "write:pets": "modify pets in your account",
+                "read:pets": "read your pets"
+            }
+        },
+        "api_key": {
+            "type": "apiKey",
+            "name": "api_key",
+            "in": "query"
+        }
+    },
+    "definitions": {
+        "Order": {
+            "type": "object",
+            "properties": {
+                "id": {
+                    "type": "integer",
+                    "format": "int64"
+                },
+                "petId": {
+                    "type": "integer",
+                    "format": "int64"
+                },
+                "quantity": {
+                    "type": "integer",
+                    "format": "int32"
+                },
+                "shipDate": {
+                    "type": "string",
+                    "format": "date-time"
+                },
+                "status": {
+                    "type": "string",
+                    "description": "Order Status",
+                    "enum": [
+                        "placed",
+                        "approved",
+                        "delivered"
+                    ]
+                },
+                "complete": {
+                    "type": "boolean",
+                    "default": false
+                }
+            },
+            "xml": {
+                "name": "Order"
+            }
+        },
+        "Category": {
+            "type": "object",
+            "properties": {
+                "id": {
+                    "type": "integer",
+                    "format": "int64"
+                },
+                "name": {
+                    "type": "string"
+                }
+            },
+            "xml": {
+                "name": "Category"
+            }
+        },
+        "User": {
+            "type": "object",
+            "properties": {
+                "id": {
+                    "type": "integer",
+                    "format": "int64"
+                },
+                "username": {
+                    "type": "string"
+                },
+                "firstName": {
+                    "type": "string"
+                },
+                "lastName": {
+                    "type": "string"
+                },
+                "email": {
+                    "type": "string"
+                },
+                "password": {
+                    "type": "string"
+                },
+                "phone": {
+                    "type": "string"
+                },
+                "userStatus": {
+                    "type": "integer",
+                    "format": "int32",
+                    "description": "User Status"
+                }
+            },
+            "xml": {
+                "name": "User"
+            }
+        },
+        "Tag": {
+            "type": "object",
+            "properties": {
+                "id": {
+                    "type": "integer",
+                    "format": "int64"
+                },
+                "name": {
+                    "type": "string"
+                }
+            },
+            "xml": {
+                "name": "Tag"
+            }
+        },
+        "Pet": {
+            "type": "object",
+            "required": [
+                "name",
+                "photoUrls"
+            ],
+            "properties": {
+                "id": {
+                    "type": "integer",
+                    "format": "int64"
+                },
+                "category": {
+                    "$ref": "#/definitions/Category"
+                },
+                "name": {
+                    "type": "string",
+                    "example": "doggie"
+                },
+                "photoUrls": {
+                    "type": "array",
+                    "xml": {
+                        "name": "photoUrl",
+                        "wrapped": true
+                    },
+                    "items": {
+                        "type": "string"
+                    }
+                },
+                "tags": {
+                    "type": "array",
+                    "xml": {
+                        "name": "tag",
+                        "wrapped": true
+                    },
+                    "items": {
+                        "$ref": "#/definitions/Tag"
+                    }
+                },
+                "status": {
+                    "type": "string",
+                    "description": "pet status in the store",
+                    "enum": [
+                        "available",
+                        "pending",
+                        "sold"
+                    ]
+                }
+            },
+            "xml": {
+                "name": "Pet"
+            }
+        },
+        "ApiResponse": {
+            "type": "object",
+            "properties": {
+                "code": {
+                    "type": "integer",
+                    "format": "int32"
+                },
+                "type": {
+                    "type": "string"
+                },
+                "message": {
+                    "type": "string"
+                }
+            }
+        }
+    },
+    "externalDocs": {
+        "description": "Find out more about OpenApi",
+        "url": "http://swagger.io"
+    }
+}
diff --git a/components/camel-rest-openapi/src/test/resources/localhost.p12 b/components/camel-rest-openapi/src/test/resources/localhost.p12
new file mode 100644
index 0000000..5533b4f
--- /dev/null
+++ b/components/camel-rest-openapi/src/test/resources/localhost.p12
Binary files differ
diff --git a/components/camel-rest-openapi/src/test/resources/log4j2.properties b/components/camel-rest-openapi/src/test/resources/log4j2.properties
new file mode 100644
index 0000000..5dd6ea5
--- /dev/null
+++ b/components/camel-rest-openapi/src/test/resources/log4j2.properties
@@ -0,0 +1,24 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+
+appender.file.type = File
+appender.file.name = file
+appender.file.fileName = target/camel-rest-openapi-test.log
+appender.file.layout.type = PatternLayout
+appender.file.layout.pattern = %d %-5p %c{1} - %m %n
+rootLogger.level = INFO
+rootLogger.appenderRef.file.ref = file
diff --git a/components/camel-rest-openapi/src/test/resources/openapi-v3.json b/components/camel-rest-openapi/src/test/resources/openapi-v3.json
new file mode 100644
index 0000000..f01d15e
--- /dev/null
+++ b/components/camel-rest-openapi/src/test/resources/openapi-v3.json
@@ -0,0 +1 @@
+{"openapi":"3.0.2","info":{"title":"Swagger Petstore - OpenAPI 3.0","description":"This is a sample Pet Store Server based on the OpenAPI 3.0 specification.  You can find out more about\nSwagger at [http://swagger.io](http://swagger.io). In the third iteration of the pet store, we've switched to the design first approach!\nYou can now help us improve the API whether it's by making changes to the definition itself or to the code.\nThat way, with time, we can improve the API in general, and expose some of the new features in OAS3.\n\nSome useful links:\n- [The Pet Store repository](https://github.com/swagger-api/swagger-petstore)\n- [The source API definition for the Pet Store](https://github.com/swagger-api/swagger-petstore/blob/master/src/main/resources/openapi.yaml) ","termsOfService":"http://swagger.io/terms/","contact":{"email":"apiteam@swagger.io"},"license":{"name":"Apache 2.0","url":"http://www.apache.org/licenses/LICENSE-2.0.html"},"version":"1.0.4"},"externalDocs":{"description":"Find out more about Swagger","url":"http://swagger.io"},"servers":[{"url":"/api/v3"}],"tags":[{"name":"pet","description":"Everything about your Pets","externalDocs":{"description":"Find out more","url":"http://swagger.io"}},{"name":"store","description":"Operations about user"},{"name":"user","description":"Access to Petstore orders","externalDocs":{"description":"Find out more about our store","url":"http://swagger.io"}}],"paths":{"/pet":{"put":{"tags":["pet"],"summary":"Update an existing pet","description":"Update an existing pet by Id","operationId":"updatePet","requestBody":{"description":"Update an existent pet in the store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Pet"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Pet"}},"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Pet"}}},"required":true},"responses":{"200":{"description":"Successful operation","content":{"application/xml":{"schema":{"$ref":"#/components/schemas/Pet"}},"application/json":{"schema":{"$ref":"#/components/schemas/Pet"}}}},"400":{"description":"Invalid ID supplied"},"404":{"description":"Pet not found"},"405":{"description":"Validation exception"}},"security":[{"petstore_auth":["write:pets","read:pets"]}]},"post":{"tags":["pet"],"summary":"Add a new pet to the store","description":"Add a new pet to the store","operationId":"addPet","requestBody":{"description":"Create a new pet in the store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Pet"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Pet"}},"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Pet"}}},"required":true},"responses":{"200":{"description":"Successful operation","content":{"application/xml":{"schema":{"$ref":"#/components/schemas/Pet"}},"application/json":{"schema":{"$ref":"#/components/schemas/Pet"}}}},"405":{"description":"Invalid input"}},"security":[{"petstore_auth":["write:pets","read:pets"]}]}},"/pet/findByStatus":{"get":{"tags":["pet"],"summary":"Finds Pets by status","description":"Multiple status values can be provided with comma separated strings","operationId":"findPetsByStatus","parameters":[{"name":"status","in":"query","description":"Status values that need to be considered for filter","required":false,"explode":true,"schema":{"type":"string","default":"available","enum":["available","pending","sold"]}}],"responses":{"200":{"description":"successful operation","content":{"application/xml":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Pet"}}},"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Pet"}}}}},"400":{"description":"Invalid status value"}},"security":[{"petstore_auth":["write:pets","read:pets"]}]}},"/pet/findByTags":{"get":{"tags":["pet"],"summary":"Finds Pets by tags","description":"Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.","operationId":"findPetsByTags","parameters":[{"name":"tags","in":"query","description":"Tags to filter by","required":false,"explode":true,"schema":{"type":"array","items":{"type":"string"}}}],"responses":{"200":{"description":"successful operation","content":{"application/xml":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Pet"}}},"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Pet"}}}}},"400":{"description":"Invalid tag value"}},"security":[{"petstore_auth":["write:pets","read:pets"]}]}},"/pet/{petId}":{"get":{"tags":["pet"],"summary":"Find pet by ID","description":"Returns a single pet","operationId":"getPetById","parameters":[{"name":"petId","in":"path","description":"ID of pet to return","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"successful operation","content":{"application/xml":{"schema":{"$ref":"#/components/schemas/Pet"}},"application/json":{"schema":{"$ref":"#/components/schemas/Pet"}}}},"400":{"description":"Invalid ID supplied"},"404":{"description":"Pet not found"}},"security":[{"api_key":[]},{"petstore_auth":["write:pets","read:pets"]}]},"post":{"tags":["pet"],"summary":"Updates a pet in the store with form data","description":"","operationId":"updatePetWithForm","parameters":[{"name":"petId","in":"path","description":"ID of pet that needs to be updated","required":true,"schema":{"type":"integer","format":"int64"}},{"name":"name","in":"query","description":"Name of pet that needs to be updated","schema":{"type":"string"}},{"name":"status","in":"query","description":"Status of pet that needs to be updated","schema":{"type":"string"}}],"responses":{"405":{"description":"Invalid input"}},"security":[{"petstore_auth":["write:pets","read:pets"]}]},"delete":{"tags":["pet"],"summary":"Deletes a pet","description":"","operationId":"deletePet","parameters":[{"name":"api_key","in":"header","description":"","required":false,"schema":{"type":"string"}},{"name":"petId","in":"path","description":"Pet id to delete","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"400":{"description":"Invalid pet value"}},"security":[{"petstore_auth":["write:pets","read:pets"]}]}},"/pet/{petId}/uploadImage":{"post":{"tags":["pet"],"summary":"uploads an image","description":"","operationId":"uploadFile","parameters":[{"name":"petId","in":"path","description":"ID of pet to update","required":true,"schema":{"type":"integer","format":"int64"}},{"name":"additionalMetadata","in":"query","description":"Additional Metadata","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"responses":{"200":{"description":"successful operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiResponse"}}}}},"security":[{"petstore_auth":["write:pets","read:pets"]}]}},"/store/inventory":{"get":{"tags":["store"],"summary":"Returns pet inventories by status","description":"Returns a map of status codes to quantities","operationId":"getInventory","responses":{"200":{"description":"successful operation","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"integer","format":"int32"}}}}}},"security":[{"api_key":[]}]}},"/store/order":{"post":{"tags":["store"],"summary":"Place an order for a pet","description":"Place a new order in the store","operationId":"placeOrder","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Order"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Order"}},"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Order"}}}},"responses":{"200":{"description":"successful operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Order"}}}},"405":{"description":"Invalid input"}}}},"/store/order/{orderId}":{"get":{"tags":["store"],"summary":"Find purchase order by ID","description":"For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions","operationId":"getOrderById","parameters":[{"name":"orderId","in":"path","description":"ID of order that needs to be fetched","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"successful operation","content":{"application/xml":{"schema":{"$ref":"#/components/schemas/Order"}},"application/json":{"schema":{"$ref":"#/components/schemas/Order"}}}},"400":{"description":"Invalid ID supplied"},"404":{"description":"Order not found"}}},"delete":{"tags":["store"],"summary":"Delete purchase order by ID","description":"For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors","operationId":"deleteOrder","parameters":[{"name":"orderId","in":"path","description":"ID of the order that needs to be deleted","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"400":{"description":"Invalid ID supplied"},"404":{"description":"Order not found"}}}},"/user":{"post":{"tags":["user"],"summary":"Create user","description":"This can only be done by the logged in user.","operationId":"createUser","requestBody":{"description":"Created user object","content":{"application/json":{"schema":{"$ref":"#/components/schemas/User"}},"application/xml":{"schema":{"$ref":"#/components/schemas/User"}},"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/User"}}}},"responses":{"default":{"description":"successful operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/User"}},"application/xml":{"schema":{"$ref":"#/components/schemas/User"}}}}}}},"/user/createWithList":{"post":{"tags":["user"],"summary":"Creates list of users with given input array","description":"Creates list of users with given input array","operationId":"createUsersWithListInput","requestBody":{"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/User"}}}}},"responses":{"200":{"description":"Successful operation","content":{"application/xml":{"schema":{"$ref":"#/components/schemas/User"}},"application/json":{"schema":{"$ref":"#/components/schemas/User"}}}},"default":{"description":"successful operation"}}}},"/user/login":{"get":{"tags":["user"],"summary":"Logs user into the system","description":"","operationId":"loginUser","parameters":[{"name":"username","in":"query","description":"The user name for login","required":false,"schema":{"type":"string"}},{"name":"password","in":"query","description":"The password for login in clear text","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"successful operation","headers":{"X-Rate-Limit":{"description":"calls per hour allowed by the user","schema":{"type":"integer","format":"int32"}},"X-Expires-After":{"description":"date in UTC when toekn expires","schema":{"type":"string","format":"date-time"}}},"content":{"application/xml":{"schema":{"type":"string"}},"application/json":{"schema":{"type":"string"}}}},"400":{"description":"Invalid username/password supplied"}}}},"/user/logout":{"get":{"tags":["user"],"summary":"Logs out current logged in user session","description":"","operationId":"logoutUser","parameters":[],"responses":{"default":{"description":"successful operation"}}}},"/user/{username}":{"get":{"tags":["user"],"summary":"Get user by user name","description":"","operationId":"getUserByName","parameters":[{"name":"username","in":"path","description":"The name that needs to be fetched. Use user1 for testing. ","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"successful operation","content":{"application/xml":{"schema":{"$ref":"#/components/schemas/User"}},"application/json":{"schema":{"$ref":"#/components/schemas/User"}}}},"400":{"description":"Invalid username supplied"},"404":{"description":"User not found"}}},"put":{"tags":["user"],"summary":"Update user","description":"This can only be done by the logged in user.","operationId":"updateUser","parameters":[{"name":"username","in":"path","description":"name that need to be deleted","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"Update an existent user in the store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/User"}},"application/xml":{"schema":{"$ref":"#/components/schemas/User"}},"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/User"}}}},"responses":{"default":{"description":"successful operation"}}},"delete":{"tags":["user"],"summary":"Delete user","description":"This can only be done by the logged in user.","operationId":"deleteUser","parameters":[{"name":"username","in":"path","description":"The name that needs to be deleted","required":true,"schema":{"type":"string"}}],"responses":{"400":{"description":"Invalid username supplied"},"404":{"description":"User not found"}}}}},"components":{"schemas":{"Order":{"type":"object","properties":{"id":{"type":"integer","format":"int64","example":10},"petId":{"type":"integer","format":"int64","example":198772},"quantity":{"type":"integer","format":"int32","example":7},"shipDate":{"type":"string","format":"date-time"},"status":{"type":"string","description":"Order Status","example":"approved","enum":["placed","approved","delivered"]},"complete":{"type":"boolean"}},"xml":{"name":"order"}},"Customer":{"type":"object","properties":{"id":{"type":"integer","format":"int64","example":100000},"username":{"type":"string","example":"fehguy"},"address":{"type":"array","xml":{"name":"addresses","wrapped":true},"items":{"$ref":"#/components/schemas/Address"}}},"xml":{"name":"customer"}},"Address":{"type":"object","properties":{"street":{"type":"string","example":"437 Lytton"},"city":{"type":"string","example":"Palo Alto"},"state":{"type":"string","example":"CA"},"zip":{"type":"string","example":"94301"}},"xml":{"name":"address"}},"Category":{"type":"object","properties":{"id":{"type":"integer","format":"int64","example":1},"name":{"type":"string","example":"Dogs"}},"xml":{"name":"category"}},"User":{"type":"object","properties":{"id":{"type":"integer","format":"int64","example":10},"username":{"type":"string","example":"theUser"},"firstName":{"type":"string","example":"John"},"lastName":{"type":"string","example":"James"},"email":{"type":"string","example":"john@email.com"},"password":{"type":"string","example":"12345"},"phone":{"type":"string","example":"12345"},"userStatus":{"type":"integer","description":"User Status","format":"int32","example":1}},"xml":{"name":"user"}},"Tag":{"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"}},"xml":{"name":"tag"}},"Pet":{"required":["name","photoUrls"],"type":"object","properties":{"id":{"type":"integer","format":"int64","example":10},"name":{"type":"string","example":"doggie"},"category":{"$ref":"#/components/schemas/Category"},"photoUrls":{"type":"array","xml":{"wrapped":true},"items":{"type":"string","xml":{"name":"photoUrl"}}},"tags":{"type":"array","xml":{"wrapped":true},"items":{"$ref":"#/components/schemas/Tag"}},"status":{"type":"string","description":"pet status in the store","enum":["available","pending","sold"]}},"xml":{"name":"pet"}},"ApiResponse":{"type":"object","properties":{"code":{"type":"integer","format":"int32"},"type":{"type":"string"},"message":{"type":"string"}},"xml":{"name":"##default"}}},"requestBodies":{"Pet":{"description":"Pet object that needs to be added to the store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Pet"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Pet"}}}},"UserArray":{"description":"List of user object","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/User"}}}}}},"securitySchemes":{"petstore_auth":{"type":"oauth2","flows":{"implicit":{"authorizationUrl":"https://petstore3.swagger.io/oauth/authorize","scopes":{"write:pets":"modify pets in your account","read:pets":"read your pets"}}}},"api_key":{"type":"apiKey","name":"api_key","in":"header"}}}}
\ No newline at end of file
diff --git a/components/camel-rest-openapi/src/test/resources/openapi.json b/components/camel-rest-openapi/src/test/resources/openapi.json
new file mode 100644
index 0000000..081b0f5b
--- /dev/null
+++ b/components/camel-rest-openapi/src/test/resources/openapi.json
@@ -0,0 +1 @@
+{"swagger":"2.0","info":{"description":"This is a sample server Petstore server.  You can find out more about OpenApi at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/).  For this sample, you can use the api key `special-key` to test the authorization filters.","version":"1.0.0","title":"Swagger Petstore","termsOfService":"http://swagger.io/terms/","contact":{"email":"apiteam@swagger.io"},"license":{"name":"Apache 2.0","url":"http://www.apache.org/licenses/LICENSE-2.0.html"}},"host":"petstore.swagger.io","basePath":"/v2","tags":[{"name":"pet","description":"Everything about your Pets","externalDocs":{"description":"Find out more","url":"http://swagger.io"}},{"name":"store","description":"Access to Petstore orders"},{"name":"user","description":"Operations about user","externalDocs":{"description":"Find out more about our store","url":"http://swagger.io"}}],"schemes":["http"],"paths":{"/pet":{"post":{"tags":["pet"],"summary":"Add a new pet to the store","description":"","operationId":"addPet","consumes":["application/json","application/xml"],"produces":["application/xml","application/json"],"parameters":[{"in":"body","name":"body","description":"Pet object that needs to be added to the store","required":true,"schema":{"$ref":"#/definitions/Pet"}}],"responses":{"405":{"description":"Invalid input"}},"security":[{"petstore_auth":["write:pets","read:pets"]}]},"put":{"tags":["pet"],"summary":"Update an existing pet","description":"","operationId":"updatePet","consumes":["application/json","application/xml"],"produces":["application/xml","application/json"],"parameters":[{"in":"body","name":"body","description":"Pet object that needs to be added to the store","required":true,"schema":{"$ref":"#/definitions/Pet"}}],"responses":{"400":{"description":"Invalid ID supplied"},"404":{"description":"Pet not found"},"405":{"description":"Validation exception"}},"security":[{"petstore_auth":["write:pets","read:pets"]}]}},"/pet/findByStatus":{"get":{"tags":["pet"],"summary":"Finds Pets by status","description":"Multiple status values can be provided with comma separated strings","operationId":"findPetsByStatus","produces":["application/xml","application/json"],"parameters":[{"name":"status","in":"query","description":"Status values that need to be considered for filter","required":true,"type":"array","items":{"type":"string","enum":["available","pending","sold"],"default":"available"},"collectionFormat":"multi"}],"responses":{"200":{"description":"successful operation","schema":{"type":"array","items":{"$ref":"#/definitions/Pet"}}},"400":{"description":"Invalid status value"}},"security":[{"petstore_auth":["write:pets","read:pets"]}]}},"/pet/findByTags":{"get":{"tags":["pet"],"summary":"Finds Pets by tags","description":"Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.","operationId":"findPetsByTags","produces":["application/xml","application/json"],"parameters":[{"name":"tags","in":"query","description":"Tags to filter by","required":true,"type":"array","items":{"type":"string"},"collectionFormat":"multi"}],"responses":{"200":{"description":"successful operation","schema":{"type":"array","items":{"$ref":"#/definitions/Pet"}}},"400":{"description":"Invalid tag value"}},"security":[{"petstore_auth":["write:pets","read:pets"]}],"deprecated":true}},"/pet/{petId}":{"get":{"tags":["pet"],"summary":"Find pet by ID","description":"Returns a single pet","operationId":"getPetById","produces":["application/xml","application/json"],"parameters":[{"name":"petId","in":"path","description":"ID of pet to return","required":true,"type":"integer","format":"int64"}],"responses":{"200":{"description":"successful operation","schema":{"$ref":"#/definitions/Pet"}},"400":{"description":"Invalid ID supplied"},"404":{"description":"Pet not found"}},"security":[{"api_key":[]}]},"post":{"tags":["pet"],"summary":"Updates a pet in the store with form data","description":"","operationId":"updatePetWithForm","consumes":["application/x-www-form-urlencoded"],"produces":["application/xml","application/json"],"parameters":[{"name":"petId","in":"path","description":"ID of pet that needs to be updated","required":true,"type":"integer","format":"int64"},{"name":"name","in":"formData","description":"Updated name of the pet","required":false,"type":"string"},{"name":"status","in":"formData","description":"Updated status of the pet","required":false,"type":"string"}],"responses":{"405":{"description":"Invalid input"}},"security":[{"petstore_auth":["write:pets","read:pets"]}]},"delete":{"tags":["pet"],"summary":"Deletes a pet","description":"","operationId":"deletePet","produces":["application/xml","application/json"],"parameters":[{"name":"api_key","in":"header","required":false,"type":"string"},{"name":"petId","in":"path","description":"Pet id to delete","required":true,"type":"integer","format":"int64"}],"responses":{"400":{"description":"Invalid ID supplied"},"404":{"description":"Pet not found"}},"security":[{"petstore_auth":["write:pets","read:pets"]}]}},"/pet/{petId}/uploadImage":{"post":{"tags":["pet"],"summary":"uploads an image","description":"","operationId":"uploadFile","consumes":["multipart/form-data"],"produces":["application/json"],"parameters":[{"name":"petId","in":"path","description":"ID of pet to update","required":true,"type":"integer","format":"int64"},{"name":"additionalMetadata","in":"formData","description":"Additional data to pass to server","required":false,"type":"string"},{"name":"file","in":"formData","description":"file to upload","required":false,"type":"file"}],"responses":{"200":{"description":"successful operation","schema":{"$ref":"#/definitions/ApiResponse"}}},"security":[{"petstore_auth":["write:pets","read:pets"]}]}},"/store/inventory":{"get":{"tags":["store"],"summary":"Returns pet inventories by status","description":"Returns a map of status codes to quantities","operationId":"getInventory","produces":["application/json"],"parameters":[],"responses":{"200":{"description":"successful operation","schema":{"type":"object","additionalProperties":{"type":"integer","format":"int32"}}}},"security":[{"api_key":[]}]}},"/store/order":{"post":{"tags":["store"],"summary":"Place an order for a pet","description":"","operationId":"placeOrder","produces":["application/xml","application/json"],"parameters":[{"in":"body","name":"body","description":"order placed for purchasing the pet","required":true,"schema":{"$ref":"#/definitions/Order"}}],"responses":{"200":{"description":"successful operation","schema":{"$ref":"#/definitions/Order"}},"400":{"description":"Invalid Order"}}}},"/store/order/{orderId}":{"get":{"tags":["store"],"summary":"Find purchase order by ID","description":"For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions","operationId":"getOrderById","produces":["application/xml","application/json"],"parameters":[{"name":"orderId","in":"path","description":"ID of pet that needs to be fetched","required":true,"type":"integer","maximum":10.0,"minimum":1.0,"format":"int64"}],"responses":{"200":{"description":"successful operation","schema":{"$ref":"#/definitions/Order"}},"400":{"description":"Invalid ID supplied"},"404":{"description":"Order not found"}}},"delete":{"tags":["store"],"summary":"Delete purchase order by ID","description":"For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors","operationId":"deleteOrder","produces":["application/xml","application/json"],"parameters":[{"name":"orderId","in":"path","description":"ID of the order that needs to be deleted","required":true,"type":"integer","minimum":1.0,"format":"int64"}],"responses":{"400":{"description":"Invalid ID supplied"},"404":{"description":"Order not found"}}}},"/user":{"post":{"tags":["user"],"summary":"Create user","description":"This can only be done by the logged in user.","operationId":"createUser","produces":["application/xml","application/json"],"parameters":[{"in":"body","name":"body","description":"Created user object","required":true,"schema":{"$ref":"#/definitions/User"}}],"responses":{"default":{"description":"successful operation"}}}},"/user/createWithArray":{"post":{"tags":["user"],"summary":"Creates list of users with given input array","description":"","operationId":"createUsersWithArrayInput","produces":["application/xml","application/json"],"parameters":[{"in":"body","name":"body","description":"List of user object","required":true,"schema":{"type":"array","items":{"$ref":"#/definitions/User"}}}],"responses":{"default":{"description":"successful operation"}}}},"/user/createWithList":{"post":{"tags":["user"],"summary":"Creates list of users with given input array","description":"","operationId":"createUsersWithListInput","produces":["application/xml","application/json"],"parameters":[{"in":"body","name":"body","description":"List of user object","required":true,"schema":{"type":"array","items":{"$ref":"#/definitions/User"}}}],"responses":{"default":{"description":"successful operation"}}}},"/user/login":{"get":{"tags":["user"],"summary":"Logs user into the system","description":"","operationId":"loginUser","produces":["application/xml","application/json"],"parameters":[{"name":"username","in":"query","description":"The user name for login","required":true,"type":"string"},{"name":"password","in":"query","description":"The password for login in clear text","required":true,"type":"string"}],"responses":{"200":{"description":"successful operation","schema":{"type":"string"},"headers":{"X-Rate-Limit":{"type":"integer","format":"int32","description":"calls per hour allowed by the user"},"X-Expires-After":{"type":"string","format":"date-time","description":"date in UTC when token expires"}}},"400":{"description":"Invalid username/password supplied"}}}},"/user/logout":{"get":{"tags":["user"],"summary":"Logs out current logged in user session","description":"","operationId":"logoutUser","produces":["application/xml","application/json"],"parameters":[],"responses":{"default":{"description":"successful operation"}}}},"/user/{username}":{"get":{"tags":["user"],"summary":"Get user by user name","description":"","operationId":"getUserByName","produces":["application/xml","application/json"],"parameters":[{"name":"username","in":"path","description":"The name that needs to be fetched. Use user1 for testing. ","required":true,"type":"string"}],"responses":{"200":{"description":"successful operation","schema":{"$ref":"#/definitions/User"}},"400":{"description":"Invalid username supplied"},"404":{"description":"User not found"}}},"put":{"tags":["user"],"summary":"Updated user","description":"This can only be done by the logged in user.","operationId":"updateUser","produces":["application/xml","application/json"],"parameters":[{"name":"username","in":"path","description":"name that need to be updated","required":true,"type":"string"},{"in":"body","name":"body","description":"Updated user object","required":true,"schema":{"$ref":"#/definitions/User"}}],"responses":{"400":{"description":"Invalid user supplied"},"404":{"description":"User not found"}}},"delete":{"tags":["user"],"summary":"Delete user","description":"This can only be done by the logged in user.","operationId":"deleteUser","produces":["application/xml","application/json"],"parameters":[{"name":"username","in":"path","description":"The name that needs to be deleted","required":true,"type":"string"}],"responses":{"400":{"description":"Invalid username supplied"},"404":{"description":"User not found"}}}}},"securityDefinitions":{"petstore_auth":{"type":"oauth2","authorizationUrl":"http://petstore.swagger.io/oauth/dialog","flow":"implicit","scopes":{"write:pets":"modify pets in your account","read:pets":"read your pets"}},"api_key":{"type":"apiKey","name":"api_key","in":"header"}},"definitions":{"Order":{"type":"object","properties":{"id":{"type":"integer","format":"int64"},"petId":{"type":"integer","format":"int64"},"quantity":{"type":"integer","format":"int32"},"shipDate":{"type":"string","format":"date-time"},"status":{"type":"string","description":"Order Status","enum":["placed","approved","delivered"]},"complete":{"type":"boolean","default":false}},"xml":{"name":"Order"}},"Category":{"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"}},"xml":{"name":"Category"}},"User":{"type":"object","properties":{"id":{"type":"integer","format":"int64"},"username":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"},"password":{"type":"string"},"phone":{"type":"string"},"userStatus":{"type":"integer","format":"int32","description":"User Status"}},"xml":{"name":"User"}},"Tag":{"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"}},"xml":{"name":"Tag"}},"Pet":{"type":"object","required":["name","photoUrls"],"properties":{"id":{"type":"integer","format":"int64"},"category":{"$ref":"#/definitions/Category"},"name":{"type":"string","example":"doggie"},"photoUrls":{"type":"array","xml":{"name":"photoUrl","wrapped":true},"items":{"type":"string"}},"tags":{"type":"array","xml":{"name":"tag","wrapped":true},"items":{"$ref":"#/definitions/Tag"}},"status":{"type":"string","description":"pet status in the store","enum":["available","pending","sold"]}},"xml":{"name":"Pet"}},"ApiResponse":{"type":"object","properties":{"code":{"type":"integer","format":"int32"},"type":{"type":"string"},"message":{"type":"string"}}}},"externalDocs":{"description":"Find out more about Swagger","url":"http://swagger.io"}}
diff --git a/components/pom.xml b/components/pom.xml
index 394295c..8d7607a 100644
--- a/components/pom.xml
+++ b/components/pom.xml
@@ -285,6 +285,7 @@
         <module>camel-reactor</module>
         <module>camel-resilience4j</module>
         <module>camel-rest-swagger</module>
+        <module>camel-rest-openapi</module>
         <module>camel-robotframework</module>
         <module>camel-rss</module>
         <module>camel-rxjava</module>
diff --git a/core/camel-endpointdsl/src/main/java/org/apache/camel/builder/endpoint/EndpointBuilderFactory.java b/core/camel-endpointdsl/src/main/java/org/apache/camel/builder/endpoint/EndpointBuilderFactory.java
index 206fd0a..b522fae 100644
--- a/core/camel-endpointdsl/src/main/java/org/apache/camel/builder/endpoint/EndpointBuilderFactory.java
+++ b/core/camel-endpointdsl/src/main/java/org/apache/camel/builder/endpoint/EndpointBuilderFactory.java
@@ -260,6 +260,7 @@
         RefEndpointBuilderFactory,
         RestApiEndpointBuilderFactory,
         RestEndpointBuilderFactory,
+        RestOpenApiEndpointBuilderFactory,
         RestSwaggerEndpointBuilderFactory,
         RobotFrameworkEndpointBuilderFactory,
         RssEndpointBuilderFactory,
diff --git a/core/camel-endpointdsl/src/main/java/org/apache/camel/builder/endpoint/dsl/RestOpenApiEndpointBuilderFactory.java b/core/camel-endpointdsl/src/main/java/org/apache/camel/builder/endpoint/dsl/RestOpenApiEndpointBuilderFactory.java
new file mode 100644
index 0000000..b38dd13
--- /dev/null
+++ b/core/camel-endpointdsl/src/main/java/org/apache/camel/builder/endpoint/dsl/RestOpenApiEndpointBuilderFactory.java
@@ -0,0 +1,257 @@
+/*
+ * 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.builder.endpoint.dsl;
+
+import javax.annotation.Generated;
+import org.apache.camel.builder.EndpointConsumerBuilder;
+import org.apache.camel.builder.EndpointProducerBuilder;
+import org.apache.camel.builder.endpoint.AbstractEndpointBuilder;
+
+/**
+ * An awesome REST endpoint backed by OpenApi specifications.
+ * 
+ * Generated by camel-package-maven-plugin - do not edit this file!
+ */
+@Generated("org.apache.camel.maven.packaging.EndpointDslMojo")
+public interface RestOpenApiEndpointBuilderFactory {
+
+
+    /**
+     * Builder for endpoint for the REST OpenApi component.
+     */
+    public interface RestOpenApiEndpointBuilder
+            extends
+                EndpointProducerBuilder {
+        default AdvancedRestOpenApiEndpointBuilder advanced() {
+            return (AdvancedRestOpenApiEndpointBuilder) this;
+        }
+        /**
+         * API basePath, for example /v2. Default is unset, if set overrides the
+         * value present in OpenApi specification and in the component
+         * configuration.
+         * 
+         * The option is a: <code>java.lang.String</code> type.
+         * 
+         * Group: producer
+         */
+        default RestOpenApiEndpointBuilder basePath(String basePath) {
+            doSetProperty("basePath", basePath);
+            return this;
+        }
+        /**
+         * Name of the Camel component that will perform the requests. The
+         * component must be present in Camel registry and it must implement
+         * RestProducerFactory service provider interface. If not set CLASSPATH
+         * is searched for single component that implements RestProducerFactory
+         * SPI. Overrides component configuration.
+         * 
+         * The option is a: <code>java.lang.String</code> type.
+         * 
+         * Group: producer
+         */
+        default RestOpenApiEndpointBuilder componentName(String componentName) {
+            doSetProperty("componentName", componentName);
+            return this;
+        }
+        /**
+         * What payload type this component capable of consuming. Could be one
+         * type, like application/json or multiple types as application/json,
+         * application/xml; q=0.5 according to the RFC7231. This equates to the
+         * value of Accept HTTP header. If set overrides any value found in the
+         * OpenApi specification and. in the component configuration.
+         * 
+         * The option is a: <code>java.lang.String</code> type.
+         * 
+         * Group: producer
+         */
+        default RestOpenApiEndpointBuilder consumes(String consumes) {
+            doSetProperty("consumes", consumes);
+            return this;
+        }
+        /**
+         * Scheme hostname and port to direct the HTTP requests to in the form
+         * of https://hostname:port. Can be configured at the endpoint,
+         * component or in the corresponding REST configuration in the Camel
+         * Context. If you give this component a name (e.g. petstore) that REST
+         * configuration is consulted first, rest-openapi next, and global
+         * configuration last. If set overrides any value found in the OpenApi
+         * specification, RestConfiguration. Overrides all other configuration.
+         * 
+         * The option is a: <code>java.lang.String</code> type.
+         * 
+         * Group: producer
+         */
+        default RestOpenApiEndpointBuilder host(String host) {
+            doSetProperty("host", host);
+            return this;
+        }
+        /**
+         * Whether the producer should be started lazy (on the first message).
+         * By starting lazy you can use this to allow CamelContext and routes to
+         * startup in situations where a producer may otherwise fail during
+         * starting and cause the route to fail being started. By deferring this
+         * startup to be lazy then the startup failure can be handled during
+         * routing messages via Camel's routing error handlers. Beware that when
+         * the first message is processed then creating and starting the
+         * producer may take a little time and prolong the total processing time
+         * of the processing.
+         * 
+         * The option is a: <code>boolean</code> type.
+         * 
+         * Group: producer
+         */
+        default RestOpenApiEndpointBuilder lazyStartProducer(
+                boolean lazyStartProducer) {
+            doSetProperty("lazyStartProducer", lazyStartProducer);
+            return this;
+        }
+        /**
+         * Whether the producer should be started lazy (on the first message).
+         * By starting lazy you can use this to allow CamelContext and routes to
+         * startup in situations where a producer may otherwise fail during
+         * starting and cause the route to fail being started. By deferring this
+         * startup to be lazy then the startup failure can be handled during
+         * routing messages via Camel's routing error handlers. Beware that when
+         * the first message is processed then creating and starting the
+         * producer may take a little time and prolong the total processing time
+         * of the processing.
+         * 
+         * The option will be converted to a <code>boolean</code> type.
+         * 
+         * Group: producer
+         */
+        default RestOpenApiEndpointBuilder lazyStartProducer(
+                String lazyStartProducer) {
+            doSetProperty("lazyStartProducer", lazyStartProducer);
+            return this;
+        }
+        /**
+         * What payload type this component is producing. For example
+         * application/json according to the RFC7231. This equates to the value
+         * of Content-Type HTTP header. If set overrides any value present in
+         * the OpenApi specification. Overrides all other configuration.
+         * 
+         * The option is a: <code>java.lang.String</code> type.
+         * 
+         * Group: producer
+         */
+        default RestOpenApiEndpointBuilder produces(String produces) {
+            doSetProperty("produces", produces);
+            return this;
+        }
+    }
+
+    /**
+     * Advanced builder for endpoint for the REST OpenApi component.
+     */
+    public interface AdvancedRestOpenApiEndpointBuilder
+            extends
+                EndpointProducerBuilder {
+        default RestOpenApiEndpointBuilder basic() {
+            return (RestOpenApiEndpointBuilder) this;
+        }
+        /**
+         * Whether the endpoint should use basic property binding (Camel 2.x) or
+         * the newer property binding with additional capabilities.
+         * 
+         * The option is a: <code>boolean</code> type.
+         * 
+         * Group: advanced
+         */
+        default AdvancedRestOpenApiEndpointBuilder basicPropertyBinding(
+                boolean basicPropertyBinding) {
+            doSetProperty("basicPropertyBinding", basicPropertyBinding);
+            return this;
+        }
+        /**
+         * Whether the endpoint should use basic property binding (Camel 2.x) or
+         * the newer property binding with additional capabilities.
+         * 
+         * The option will be converted to a <code>boolean</code> type.
+         * 
+         * Group: advanced
+         */
+        default AdvancedRestOpenApiEndpointBuilder basicPropertyBinding(
+                String basicPropertyBinding) {
+            doSetProperty("basicPropertyBinding", basicPropertyBinding);
+            return this;
+        }
+        /**
+         * Sets whether synchronous processing should be strictly used, or Camel
+         * is allowed to use asynchronous processing (if supported).
+         * 
+         * The option is a: <code>boolean</code> type.
+         * 
+         * Group: advanced
+         */
+        default AdvancedRestOpenApiEndpointBuilder synchronous(
+                boolean synchronous) {
+            doSetProperty("synchronous", synchronous);
+            return this;
+        }
+        /**
+         * Sets whether synchronous processing should be strictly used, or Camel
+         * is allowed to use asynchronous processing (if supported).
+         * 
+         * The option will be converted to a <code>boolean</code> type.
+         * 
+         * Group: advanced
+         */
+        default AdvancedRestOpenApiEndpointBuilder synchronous(
+                String synchronous) {
+            doSetProperty("synchronous", synchronous);
+            return this;
+        }
+    }
+    /**
+     * REST OpenApi (camel-rest-openapi)
+     * An awesome REST endpoint backed by OpenApi specifications.
+     * 
+     * Category: rest,openapi,http
+     * Since: 3.1
+     * Maven coordinates: org.apache.camel:camel-rest-openapi
+     * 
+     * Syntax: <code>rest-openapi:specificationUri#operationId</code>
+     * 
+     * Path parameter: specificationUri
+     * Path to the OpenApi specification file. The scheme, host base path are
+     * taken from this specification, but these can be overridden with
+     * properties on the component or endpoint level. If not given the component
+     * tries to load openapi.json resource from the classpath. Note that the
+     * host defined on the component and endpoint of this Component should
+     * contain the scheme, hostname and optionally the port in the URI syntax
+     * (i.e. http://api.example.com:8080). Overrides component configuration.
+     * The OpenApi specification can be loaded from different sources by
+     * prefixing with file: classpath: http: https:. Support for https is
+     * limited to using the JDK installed UrlHandler, and as such it can be
+     * cumbersome to setup TLS/SSL certificates for https (such as setting a
+     * number of javax.net.ssl JVM system properties). How to do that consult
+     * the JDK documentation for UrlHandler.
+     * Default value: openapi.json
+     * 
+     * Path parameter: operationId (required)
+     * ID of the operation from the OpenApi specification.
+     */
+    default RestOpenApiEndpointBuilder restOpenapi(String path) {
+        class RestOpenApiEndpointBuilderImpl extends AbstractEndpointBuilder implements RestOpenApiEndpointBuilder, AdvancedRestOpenApiEndpointBuilder {
+            public RestOpenApiEndpointBuilderImpl(String path) {
+                super("rest-openapi", path);
+            }
+        }
+        return new RestOpenApiEndpointBuilderImpl(path);
+    }
+}
\ No newline at end of file
diff --git a/parent/pom.xml b/parent/pom.xml
index 5d8fd3a..4608be7 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -616,6 +616,7 @@
         <swagger-java-version>1.5.24</swagger-java-version>
         <swagger-java-parser-version>1.0.47</swagger-java-parser-version>
         <swagger-java-guava-version>27.1-jre</swagger-java-guava-version>
+        <apicurio-version>1.0.16.Final</apicurio-version>
         <stax-api-version>1.0.1</stax-api-version>
         <stax2-api-bundle-version>3.1.4</stax2-api-bundle-version>
         <stax2v4-api-bundle-version>4.2</stax2v4-api-bundle-version>
@@ -1989,6 +1990,11 @@
         <version>${project.version}</version>
       </dependency>
       <dependency>
+        <groupId>io.apicurio</groupId>
+        <artifactId>apicurio-data-models</artifactId>
+        <version>${apicurio-version}</version>
+      </dependency>
+      <dependency>
         <groupId>org.apache.camel</groupId>
         <artifactId>camel-ribbon</artifactId>
         <version>${project.version}</version>
diff --git a/platforms/spring-boot/components-starter/camel-rest-openapi-starter/pom.xml b/platforms/spring-boot/components-starter/camel-rest-openapi-starter/pom.xml
new file mode 100644
index 0000000..8f39d2a
--- /dev/null
+++ b/platforms/spring-boot/components-starter/camel-rest-openapi-starter/pom.xml
@@ -0,0 +1,53 @@
+<?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</groupId>
+    <artifactId>components-starter</artifactId>
+    <version>3.1.0-SNAPSHOT</version>
+  </parent>
+  <artifactId>camel-rest-openapi-starter</artifactId>
+  <packaging>jar</packaging>
+  <name>Spring-Boot Starter :: Camel :: REST OpenApi</name>
+  <description>Spring-Boot Starter for Camel REST support using OpenApi</description>
+  <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-rest-openapi</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <!--START OF GENERATED CODE-->
+    <dependency>
+      <groupId>org.apache.camel.springboot</groupId>
+      <artifactId>camel-core-starter</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.camel.springboot</groupId>
+      <artifactId>camel-spring-boot-starter</artifactId>
+    </dependency>
+    <!--END OF GENERATED CODE-->
+  </dependencies>
+</project>
diff --git a/platforms/spring-boot/components-starter/camel-rest-openapi-starter/src/main/java/org/apache/camel/component/rest/openapi/springboot/RestOpenApiComponentAutoConfiguration.java b/platforms/spring-boot/components-starter/camel-rest-openapi-starter/src/main/java/org/apache/camel/component/rest/openapi/springboot/RestOpenApiComponentAutoConfiguration.java
new file mode 100644
index 0000000..34931e1
--- /dev/null
+++ b/platforms/spring-boot/components-starter/camel-rest-openapi-starter/src/main/java/org/apache/camel/component/rest/openapi/springboot/RestOpenApiComponentAutoConfiguration.java
@@ -0,0 +1,128 @@
+/*
+ * 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.rest.openapi.springboot;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import javax.annotation.Generated;
+import org.apache.camel.CamelContext;
+import org.apache.camel.component.rest.openapi.RestOpenApiComponent;
+import org.apache.camel.spi.ComponentCustomizer;
+import org.apache.camel.spi.HasId;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.spring.boot.ComponentConfigurationProperties;
+import org.apache.camel.spring.boot.util.CamelPropertiesHelper;
+import org.apache.camel.spring.boot.util.ConditionalOnCamelContextAndAutoConfigurationBeans;
+import org.apache.camel.spring.boot.util.GroupCondition;
+import org.apache.camel.spring.boot.util.HierarchicalPropertiesEvaluator;
+import org.apache.camel.support.IntrospectionSupport;
+import org.apache.camel.util.ObjectHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.AutoConfigureAfter;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Conditional;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Lazy;
+
+/**
+ * Generated by camel-package-maven-plugin - do not edit this file!
+ */
+@Generated("org.apache.camel.maven.packaging.SpringBootAutoConfigurationMojo")
+@Configuration
+@Conditional({ConditionalOnCamelContextAndAutoConfigurationBeans.class,
+        RestOpenApiComponentAutoConfiguration.GroupConditions.class})
+@AutoConfigureAfter(CamelAutoConfiguration.class)
+@EnableConfigurationProperties({ComponentConfigurationProperties.class,
+        RestOpenApiComponentConfiguration.class})
+public class RestOpenApiComponentAutoConfiguration {
+
+    private static final Logger LOGGER = LoggerFactory
+            .getLogger(RestOpenApiComponentAutoConfiguration.class);
+    @Autowired
+    private ApplicationContext applicationContext;
+    @Autowired
+    private CamelContext camelContext;
+    @Autowired
+    private RestOpenApiComponentConfiguration configuration;
+    @Autowired(required = false)
+    private List<ComponentCustomizer<RestOpenApiComponent>> customizers;
+
+    static class GroupConditions extends GroupCondition {
+        public GroupConditions() {
+            super("camel.component", "camel.component.rest-openapi");
+        }
+    }
+
+    @Lazy
+    @Bean(name = "rest-openapi-component")
+    @ConditionalOnMissingBean(RestOpenApiComponent.class)
+    public RestOpenApiComponent configureRestOpenApiComponent() throws Exception {
+        RestOpenApiComponent component = new RestOpenApiComponent();
+        component.setCamelContext(camelContext);
+        Map<String, Object> parameters = new HashMap<>();
+        IntrospectionSupport.getProperties(configuration, parameters, null,
+                false);
+        for (Map.Entry<String, Object> entry : parameters.entrySet()) {
+            Object value = entry.getValue();
+            Class<?> paramClass = value.getClass();
+            if (paramClass.getName().endsWith("NestedConfiguration")) {
+                Class nestedClass = null;
+                try {
+                    nestedClass = (Class) paramClass.getDeclaredField(
+                            "CAMEL_NESTED_CLASS").get(null);
+                    HashMap<String, Object> nestedParameters = new HashMap<>();
+                    IntrospectionSupport.getProperties(value, nestedParameters,
+                            null, false);
+                    Object nestedProperty = nestedClass.newInstance();
+                    CamelPropertiesHelper.setCamelProperties(camelContext,
+                            nestedProperty, nestedParameters, false);
+                    entry.setValue(nestedProperty);
+                } catch (NoSuchFieldException e) {
+                }
+            }
+        }
+        CamelPropertiesHelper.setCamelProperties(camelContext, component,
+                parameters, false);
+        if (ObjectHelper.isNotEmpty(customizers)) {
+            for (ComponentCustomizer<RestOpenApiComponent> customizer : customizers) {
+                boolean useCustomizer = (customizer instanceof HasId)
+                        ? HierarchicalPropertiesEvaluator.evaluate(
+                                applicationContext.getEnvironment(),
+                                "camel.component.customizer",
+                                "camel.component.rest-openapi.customizer",
+                                ((HasId) customizer).getId())
+                        : HierarchicalPropertiesEvaluator.evaluate(
+                                applicationContext.getEnvironment(),
+                                "camel.component.customizer",
+                                "camel.component.rest-openapi.customizer");
+                if (useCustomizer) {
+                    LOGGER.debug("Configure component {}, with customizer {}",
+                            component, customizer);
+                    customizer.customize(component);
+                }
+            }
+        }
+        return component;
+    }
+}
\ No newline at end of file
diff --git a/platforms/spring-boot/components-starter/camel-rest-openapi-starter/src/main/java/org/apache/camel/component/rest/openapi/springboot/RestOpenApiComponentConfiguration.java b/platforms/spring-boot/components-starter/camel-rest-openapi-starter/src/main/java/org/apache/camel/component/rest/openapi/springboot/RestOpenApiComponentConfiguration.java
new file mode 100644
index 0000000..0e51143
--- /dev/null
+++ b/platforms/spring-boot/components-starter/camel-rest-openapi-starter/src/main/java/org/apache/camel/component/rest/openapi/springboot/RestOpenApiComponentConfiguration.java
@@ -0,0 +1,213 @@
+/*
+ * 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.rest.openapi.springboot;
+
+import java.net.URI;
+import javax.annotation.Generated;
+import org.apache.camel.spring.boot.ComponentConfigurationPropertiesCommon;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+/**
+ * An awesome REST endpoint backed by OpenApi specifications.
+ * 
+ * Generated by camel-package-maven-plugin - do not edit this file!
+ */
+@Generated("org.apache.camel.maven.packaging.SpringBootAutoConfigurationMojo")
+@ConfigurationProperties(prefix = "camel.component.rest-openapi")
+public class RestOpenApiComponentConfiguration
+        extends
+            ComponentConfigurationPropertiesCommon {
+
+    /**
+     * Whether to enable auto configuration of the rest-openapi component. This
+     * is enabled by default.
+     */
+    private Boolean enabled;
+    /**
+     * API basePath, for example /v2. Default is unset, if set overrides the
+     * value present in OpenApi specification.
+     */
+    private String basePath;
+    /**
+     * Name of the Camel component that will perform the requests. The component
+     * must be present in Camel registry and it must implement
+     * RestProducerFactory service provider interface. If not set CLASSPATH is
+     * searched for single component that implements RestProducerFactory SPI.
+     * Can be overridden in endpoint configuration.
+     */
+    private String componentName;
+    /**
+     * What payload type this component capable of consuming. Could be one type,
+     * like application/json or multiple types as application/json,
+     * application/xml; q=0.5 according to the RFC7231. This equates to the
+     * value of Accept HTTP header. If set overrides any value found in the
+     * OpenApi specification. Can be overridden in endpoint configuration
+     */
+    private String consumes;
+    /**
+     * Scheme hostname and port to direct the HTTP requests to in the form of
+     * https://hostname:port. Can be configured at the endpoint, component or in
+     * the corresponding REST configuration in the Camel Context. If you give
+     * this component a name (e.g. petstore) that REST configuration is
+     * consulted first, rest-openapi next, and global configuration last. If set
+     * overrides any value found in the OpenApi specification,
+     * RestConfiguration. Can be overridden in endpoint configuration.
+     */
+    private String host;
+    /**
+     * What payload type this component is producing. For example
+     * application/json according to the RFC7231. This equates to the value of
+     * Content-Type HTTP header. If set overrides any value present in the
+     * OpenApi specification. Can be overridden in endpoint configuration.
+     */
+    private String produces;
+    /**
+     * Path to the OpenApi specification file. The scheme, host base path are
+     * taken from this specification, but these can be overridden with
+     * properties on the component or endpoint level. If not given the component
+     * tries to load openapi.json resource. Note that the host defined on the
+     * component and endpoint of this Component should contain the scheme,
+     * hostname and optionally the port in the URI syntax (i.e.
+     * https://api.example.com:8080). Can be overridden in endpoint
+     * configuration.
+     */
+    private URI specificationUri;
+    /**
+     * Customize TLS parameters used by the component. If not set defaults to
+     * the TLS parameters set in the Camel context. The option is a
+     * org.apache.camel.support.jsse.SSLContextParameters type.
+     */
+    private String sslContextParameters;
+    /**
+     * Enable usage of global SSL context parameters.
+     */
+    private Boolean useGlobalSslContextParameters = false;
+    /**
+     * Whether the component should use basic property binding (Camel 2.x) or
+     * the newer property binding with additional capabilities
+     */
+    private Boolean basicPropertyBinding = false;
+    /**
+     * Whether the producer should be started lazy (on the first message). By
+     * starting lazy you can use this to allow CamelContext and routes to
+     * startup in situations where a producer may otherwise fail during starting
+     * and cause the route to fail being started. By deferring this startup to
+     * be lazy then the startup failure can be handled during routing messages
+     * via Camel's routing error handlers. Beware that when the first message is
+     * processed then creating and starting the producer may take a little time
+     * and prolong the total processing time of the processing.
+     */
+    private Boolean lazyStartProducer = false;
+    /**
+     * Allows for bridging the consumer to the Camel routing Error Handler,
+     * which mean any exceptions occurred while the consumer is trying to pickup
+     * incoming messages, or the likes, will now be processed as a message and
+     * handled by the routing Error Handler. By default the consumer will use
+     * the org.apache.camel.spi.ExceptionHandler to deal with exceptions, that
+     * will be logged at WARN or ERROR level and ignored.
+     */
+    private Boolean bridgeErrorHandler = false;
+
+    public String getBasePath() {
+        return basePath;
+    }
+
+    public void setBasePath(String basePath) {
+        this.basePath = basePath;
+    }
+
+    public String getComponentName() {
+        return componentName;
+    }
+
+    public void setComponentName(String componentName) {
+        this.componentName = componentName;
+    }
+
+    public String getConsumes() {
+        return consumes;
+    }
+
+    public void setConsumes(String consumes) {
+        this.consumes = consumes;
+    }
+
+    public String getHost() {
+        return host;
+    }
+
+    public void setHost(String host) {
+        this.host = host;
+    }
+
+    public String getProduces() {
+        return produces;
+    }
+
+    public void setProduces(String produces) {
+        this.produces = produces;
+    }
+
+    public URI getSpecificationUri() {
+        return specificationUri;
+    }
+
+    public void setSpecificationUri(URI specificationUri) {
+        this.specificationUri = specificationUri;
+    }
+
+    public String getSslContextParameters() {
+        return sslContextParameters;
+    }
+
+    public void setSslContextParameters(String sslContextParameters) {
+        this.sslContextParameters = sslContextParameters;
+    }
+
+    public Boolean getUseGlobalSslContextParameters() {
+        return useGlobalSslContextParameters;
+    }
+
+    public void setUseGlobalSslContextParameters(
+            Boolean useGlobalSslContextParameters) {
+        this.useGlobalSslContextParameters = useGlobalSslContextParameters;
+    }
+
+    public Boolean getBasicPropertyBinding() {
+        return basicPropertyBinding;
+    }
+
+    public void setBasicPropertyBinding(Boolean basicPropertyBinding) {
+        this.basicPropertyBinding = basicPropertyBinding;
+    }
+
+    public Boolean getLazyStartProducer() {
+        return lazyStartProducer;
+    }
+
+    public void setLazyStartProducer(Boolean lazyStartProducer) {
+        this.lazyStartProducer = lazyStartProducer;
+    }
+
+    public Boolean getBridgeErrorHandler() {
+        return bridgeErrorHandler;
+    }
+
+    public void setBridgeErrorHandler(Boolean bridgeErrorHandler) {
+        this.bridgeErrorHandler = bridgeErrorHandler;
+    }
+}
\ No newline at end of file
diff --git a/platforms/spring-boot/components-starter/camel-rest-openapi-starter/src/main/resources/META-INF/LICENSE.txt b/platforms/spring-boot/components-starter/camel-rest-openapi-starter/src/main/resources/META-INF/LICENSE.txt
new file mode 100644
index 0000000..6b0b127
--- /dev/null
+++ b/platforms/spring-boot/components-starter/camel-rest-openapi-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/platforms/spring-boot/components-starter/camel-rest-openapi-starter/src/main/resources/META-INF/NOTICE.txt b/platforms/spring-boot/components-starter/camel-rest-openapi-starter/src/main/resources/META-INF/NOTICE.txt
new file mode 100644
index 0000000..2e215bf
--- /dev/null
+++ b/platforms/spring-boot/components-starter/camel-rest-openapi-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/platforms/spring-boot/components-starter/camel-rest-openapi-starter/src/main/resources/META-INF/spring.factories b/platforms/spring-boot/components-starter/camel-rest-openapi-starter/src/main/resources/META-INF/spring.factories
new file mode 100644
index 0000000..6d86026
--- /dev/null
+++ b/platforms/spring-boot/components-starter/camel-rest-openapi-starter/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,19 @@
+## ---------------------------------------------------------------------------
+## 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.component.rest.openapi.springboot.RestOpenApiComponentAutoConfiguration
diff --git a/platforms/spring-boot/components-starter/camel-rest-openapi-starter/src/main/resources/META-INF/spring.provides b/platforms/spring-boot/components-starter/camel-rest-openapi-starter/src/main/resources/META-INF/spring.provides
new file mode 100644
index 0000000..19648b0
--- /dev/null
+++ b/platforms/spring-boot/components-starter/camel-rest-openapi-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-rest-openapi
diff --git a/platforms/spring-boot/components-starter/pom.xml b/platforms/spring-boot/components-starter/pom.xml
index 05e5fd2..a4a1da2 100644
--- a/platforms/spring-boot/components-starter/pom.xml
+++ b/platforms/spring-boot/components-starter/pom.xml
@@ -315,6 +315,7 @@
     <module>camel-reactor-starter</module>
     <module>camel-ref-starter</module>
     <module>camel-resilience4j-starter</module>
+    <module>camel-rest-openapi-starter</module>
     <module>camel-rest-starter</module>
     <module>camel-rest-swagger-starter</module>
     <module>camel-ribbon-starter</module>