CAMEL-10932 REST Swagger component

This includes the initial implementation of the `rest-swagger` component
that allows for a higher level abstraction over the REST API usage from
other Camel components that implement the `RestProducerFactory` SPI
combined with Swagger (Open API) specifications.

The most simple usage would be:

    to("rest-swagger:getPetById")

Which would pick up the Swagger specification from `swagger.json` and
try to find a single component that implements the `RestProducerFactory`
SPI and invoke the `getPetById` operation.

Other way of using this component could be:

    to("rest-swagger:http://petstore.swagger.io/v2/swagger.json#getPetById";)

That loads the Swagger specification from the
`http://petstore.swagger.io/v2/swagger.json` URL and invokes the
`getPetById` operation.

More concise way of configuring would be to configure most properties
on the component add it to CamelContext, and use only `operationId` path
parameter when triggering the exchange:

    // add `petstore` component to the CamelContext
    RestSwaggerComponent petstore =
        new RestSwaggerComponent(camelContext);
    petstore.setSpecificationUri("http://petstore.swagger.io/v2/swagger.json";);
    petstore.setComponentName("undertow");
    camelContext.addComponent("petstore", petstore);

And then use `operationId` in endpoint definition:

    ProducerTemplate template = camelContext.getProducerTemplate();
    template.requestBodyAndHeaders("petstore:getPetById", null, "petId",
        petId);


Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/5b209152
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/5b209152
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/5b209152

Branch: refs/heads/master
Commit: 5b209152b2ffb1ddd507c8b265bdab50aea620ef
Parents: ae2ae6f
Author: Zoran Regvart <zregv...@apache.org>
Authored: Thu Mar 16 12:54:47 2017 +0100
Committer: Zoran Regvart <zregv...@apache.org>
Committed: Thu Mar 16 22:28:20 2017 +0100

----------------------------------------------------------------------
 components/camel-rest-swagger/pom.xml           | 174 +++++++
 .../src/main/docs/rest-swagger-component.adoc   | 190 +++++++
 .../rest/swagger/RestSwaggerComponent.java      | 191 ++++++++
 .../rest/swagger/RestSwaggerEndpoint.java       | 490 +++++++++++++++++++
 .../rest/swagger/RestSwaggerHelper.java         |  50 ++
 .../src/main/resources/META-INF/LICENSE.txt     | 203 ++++++++
 .../src/main/resources/META-INF/NOTICE.txt      |  11 +
 .../org/apache/camel/component/rest-swagger     |  17 +
 .../camel/component/rest/swagger/Pet.java       |  28 ++
 .../rest/swagger/RestSwaggerComponentTest.java  | 152 ++++++
 .../rest/swagger/RestSwaggerEndpointTest.java   | 336 +++++++++++++
 .../RestSwaggerEndpointUriParsingTest.java      |  66 +++
 .../rest/swagger/RestSwaggerHelperTest.java     |  50 ++
 .../src/test/resources/log4j2.properties        |  24 +
 .../src/test/resources/swagger.json             |   1 +
 components/pom.xml                              |   1 +
 docs/user-manual/en/SUMMARY.md                  |   1 +
 examples/camel-example-rest-swagger/README.md   |  30 ++
 examples/camel-example-rest-swagger/pom.xml     | 102 ++++
 .../camel/example/RestSwaggerApplication.java   |  68 +++
 parent/pom.xml                                  |   6 +
 .../camel-rest-swagger-starter/pom.xml          |  51 ++
 .../RestSwaggerComponentAutoConfiguration.java  | 111 +++++
 .../RestSwaggerComponentConfiguration.java      | 142 ++++++
 .../src/main/resources/META-INF/LICENSE.txt     | 203 ++++++++
 .../src/main/resources/META-INF/NOTICE.txt      |  11 +
 ...dditional-spring-configuration-metadata.json |  10 +
 .../main/resources/META-INF/spring.factories    |  19 +
 .../src/main/resources/META-INF/spring.provides |  18 +
 .../spring-boot/components-starter/pom.xml      |   1 +
 30 files changed, 2757 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/5b209152/components/camel-rest-swagger/pom.xml
----------------------------------------------------------------------
diff --git a/components/camel-rest-swagger/pom.xml 
b/components/camel-rest-swagger/pom.xml
new file mode 100644
index 0000000..39d6da5
--- /dev/null
+++ b/components/camel-rest-swagger/pom.xml
@@ -0,0 +1,174 @@
+<?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</artifactId>
+    <version>2.19.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>camel-rest-swagger</artifactId>
+
+  <name>Camel :: REST Swagger</name>
+  <description>Camel REST support using Swagger</description>
+
+  <properties>
+    <firstVersion>2.19.0</firstVersion>
+    <label>rest,api,http</label>
+    
<camel.osgi.export.pkg>org.apache.camel.component.rest.swagger.*</camel.osgi.export.pkg>
+    
<camel.osgi.export.service>org.apache.camel.spi.ComponentResolver;component=rest-swagger</camel.osgi.export.service>
+  </properties>
+
+  <dependencies>
+
+    <dependency>
+      <groupId>org.apache.camel</groupId>
+      <artifactId>camel-core</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>io.swagger</groupId>
+      <artifactId>swagger-parser</artifactId>
+      <version>${swagger-java-parser-version}</version>
+      <exclusions>
+        <!--
+        Trying to keep transitive dependencies only to required ones.
+        -->
+        <exclusion>
+          <groupId>com.google.guava</groupId>
+          <artifactId>guava</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>org.slf4j</groupId>
+          <artifactId>slf4j-ext</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>io.swagger</groupId>
+          <artifactId>swagger-annotations</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>javax.validation</groupId>
+          <artifactId>validation-api</artifactId>
+        </exclusion>
+      </exclusions>
+    </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>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>com.github.tomakehurst</groupId>
+      <artifactId>wiremock</artifactId>
+      <version>${wiremock-version}</version>
+      <scope>test</scope>
+      <exclusions>
+        <exclusion>
+          <!--
+           Adding `camel-jetty9` forces WireMock to use mixed Jetty
+           versions, here we exclude WireMocks Jetty dependencies so
+           that we only use the ones declared by the `jetty9` 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-http4</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.camel</groupId>
+      <artifactId>camel-netty4-http</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.camel</groupId>
+      <artifactId>camel-restlet</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.camel</groupId>
+      <artifactId>camel-jetty9</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>

http://git-wip-us.apache.org/repos/asf/camel/blob/5b209152/components/camel-rest-swagger/src/main/docs/rest-swagger-component.adoc
----------------------------------------------------------------------
diff --git 
a/components/camel-rest-swagger/src/main/docs/rest-swagger-component.adoc 
b/components/camel-rest-swagger/src/main/docs/rest-swagger-component.adoc
new file mode 100644
index 0000000..acdde17
--- /dev/null
+++ b/components/camel-rest-swagger/src/main/docs/rest-swagger-component.adoc
@@ -0,0 +1,190 @@
+## REST Swagger Component
+
+*Available as of Camel version 2.19*
+
+The *rest-swagger* configures rest producers from 
+http://swagger.io/[Swagger] (Open API) specification document and
+delegates to a component implementing the _RestProducerFactory_
+interface. Currently known working components are:
+
+* link:http-component.html[http]
+* link:http4-component.html[http4]
+* link:netty4-http-component.html[netty4-http]
+* link:restlet-component.html[restlet]
+* link:jetty-component.html[jetty]
+* link:undertow-component.html[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-swagger</artifactId>
+    <version>x.x.x</version>
+    <!-- use the same version as your Camel core version -->
+</dependency>
+------------------------------------------------------------
+
+### URI format
+
+[source,java]
+-------------------------------------------------------
+rest-swagger:[specificationPath#]operationId
+-------------------------------------------------------
+
+Where `operationId` is the ID of the operation in the Swagger
+specification, and `specificationPath` is the path to the
+specification.
+If the `specificationPath` is not specified it defaults to
+`swagger.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/swagger.json`) or reference a bean 
+(`ref:nameOfBean`) or use a method of a bean 
+(`bean:nameOfBean.methodName`) to get the specification resource,
+failing that Swagger'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 Swagger 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.
+
+### Options
+
+// component options: START
+The REST Swagger component supports 7 options which are listed below.
+
+
+
+[width="100%",cols="2,1,1m,1m,5",options="header"]
+|=======================================================================
+| Name | Group | Default | Java Type | Description
+| basePath | producer |  | String | API basePath for example /v2. Default is 
unset if set overrides the value present in Swagger specification.
+| componentName | producer |  | String | Name of the Camel component that will 
perform the requests. The compnent 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 overriden in endpoint configuration.
+| consumes | producer |  | String | 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 
Swagger specification. Can be overriden in endpoint configuration
+| host | producer |  | String | 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 correspoding REST configuration in the Camel 
Context. If you give this component a name (e.g. petstore) that REST 
configuration is consulted first rest-swagger next and global configuration 
last. If set overrides any value found in the Swagger specification 
RestConfiguration. Can be overriden in endpoint configuration.
+| produces | producer |  | String | 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 Swagger specification. Can be overriden in endpoint configuration.
+| specificationUri | producer | swagger.json | URI | Path to the Swagger 
specification file. The scheme host base path are taken from this specification 
but these can be overriden with properties on the component or endpoint level. 
If not given the component tries to load swagger.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 overriden in endpoint configuration.
+| resolvePropertyPlaceholders | advanced | true | boolean | Whether the 
component should resolve property placeholders on itself when starting. Only 
properties which are of String type can use property placeholders.
+|=======================================================================
+// component options: END
+
+// endpoint options: START
+The REST Swagger endpoint is configured using URI syntax:
+
+    rest-swagger:specificationUri#operationId
+
+with the following path and query parameters:
+
+#### Path Parameters (2 parameters):
+
+[width="100%",cols="2,1,1m,6",options="header"]
+|=======================================================================
+| Name | Default | Java Type | Description
+| specificationUri | swagger.json | URI | Path to the Swagger specification 
file. The scheme host base path are taken from this specification but these can 
be overriden with properties on the component or endpoint level. If not given 
the component tries to load swagger.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). Overrides component configuration.
+| operationId |  | String | *Required* ID of the operation from the Swagger 
specification.
+|=======================================================================
+
+#### Query Parameters (6 parameters):
+
+[width="100%",cols="2,1,1m,1m,5",options="header"]
+|=======================================================================
+| Name | Group | Default | Java Type | Description
+| basePath | producer |  | String | API basePath for example /v2. Default is 
unset if set overrides the value present in Swagger specification and in the 
component configuration.
+| componentName | producer |  | String | Name of the Camel component that will 
perform the requests. The compnent 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.
+| consumes | producer |  | String | 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 
Swagger specification and. in the component configuration
+| host | producer |  | String | 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 correspoding REST configuration in the Camel 
Context. If you give this component a name (e.g. petstore) that REST 
configuration is consulted first rest-swagger next and global configuration 
last. If set overrides any value found in the Swagger specification 
RestConfiguration. Overrides all other configuration.
+| produces | producer |  | String | 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 Swagger specification. Overrides all other configuration.
+| synchronous | advanced | false | boolean | Sets whether synchronous 
processing should be strictly used or Camel is allowed to use asynchronous 
processing (if supported).
+|=======================================================================
+// endpoint options: END
+
+### Example: PetStore
+
+Checkout the example in the `camel-example-rest-swagger` project in
+the `examples` directory.
+
+For example if you wanted to use the 
+http://petstore.swagger.io/[_PetStore_] provided REST API simply
+reference the specification URI and desired operation id from the
+Swagger specification or download the specification and store it as
+`swagger.json` (in the root) of CLASSPATH that way it will be 
+automaticaly used. Let's use the link:undertow-component.html[Undertow]
+component to perform all the requests and Camels excelent support for 
+link:spring-boot.html[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-swagger-starter</artifactId>
+</dependency>
+----
+
+Start by defining the _Undertow_ component and the
+_RestSwaggerComponent_:
+
+[source,java]
+----
+@Bean
+public Component petstore(CamelContext camelContext, UndertowComponent 
undertow) {
+    RestSwaggerComponent petstore = new RestSwaggerComponent(camelContext);
+    petstore.setSpecificationUri("http://petstore.swagger.io/v2/swagger.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-swagger` 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);
+}
+----

http://git-wip-us.apache.org/repos/asf/camel/blob/5b209152/components/camel-rest-swagger/src/main/java/org/apache/camel/component/rest/swagger/RestSwaggerComponent.java
----------------------------------------------------------------------
diff --git 
a/components/camel-rest-swagger/src/main/java/org/apache/camel/component/rest/swagger/RestSwaggerComponent.java
 
b/components/camel-rest-swagger/src/main/java/org/apache/camel/component/rest/swagger/RestSwaggerComponent.java
new file mode 100644
index 0000000..943f834
--- /dev/null
+++ 
b/components/camel-rest-swagger/src/main/java/org/apache/camel/component/rest/swagger/RestSwaggerComponent.java
@@ -0,0 +1,191 @@
+/**
+ * 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.swagger;
+
+import java.net.URI;
+import java.util.Map;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Endpoint;
+import org.apache.camel.impl.DefaultComponent;
+import org.apache.camel.spi.Metadata;
+import org.apache.camel.spi.RestProducerFactory;
+
+import static 
org.apache.camel.component.rest.swagger.RestSwaggerHelper.isHostParam;
+import static 
org.apache.camel.component.rest.swagger.RestSwaggerHelper.isMediaRange;
+import static org.apache.camel.util.ObjectHelper.notNull;
+import static org.apache.camel.util.StringHelper.notEmpty;
+
+/**
+ * An awesome REST component backed by Swagger specifications. Creates 
endpoints
+ * that connect to REST APIs defined by Swagger specification. This component
+ * delegates to other {@link RestProducerFactory} components to act as REST
+ * clients, but it configures them from Swagger 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-swagger:http://petstore.swagger.io/v2/swagger.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-swagger:http://petstore.swagger.io/v2/swagger.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 RestSwaggerComponent();
+ * petstore.setSpecificationUri("http://petstore.swagger.io/v2/swagger.json";);
+ * petstore.setComponentName("undertow");
+ * //...
+ * camelContext.addComponent("petstore", petstore);
+ *
+ * from(...).to("petstore:getPetById")
+ * </pre>
+ */
+public final class RestSwaggerComponent extends DefaultComponent {
+    public static final String DEFAULT_BASE_PATH = "/";
+
+    static final URI DEFAULT_SPECIFICATION_URI = 
URI.create(RestSwaggerComponent.DEFAULT_SPECIFICATION_URI_STR);
+
+    static final String DEFAULT_SPECIFICATION_URI_STR = "swagger.json";
+
+    @Metadata(
+        description = "API basePath, for example \"`/v2`\". Default is unset, 
if set overrides the value present in Swagger specification.",
+        defaultValue = "", label = "producer", required = "false")
+    private String basePath = "";
+
+    @Metadata(description = "Name of the Camel component that will perform the 
requests. The compnent 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 overriden 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 Swagger specification."
+            + " Can be overriden in endpoint configuration",
+        label = "producer", required = "false")
+    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 correspoding"
+        + " REST configuration in the Camel Context. If you give this 
component a name (e.g. `petstore`) that"
+        + " REST configuration is consulted first, `rest-swagger` next, and 
global configuration last. If set"
+        + " overrides any value found in the Swagger specification, 
RestConfiguration. Can be overriden in endpoint"
+        + " configuration.", label = "producer", required = "false")
+    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 Swagger specification. Can be 
overriden in endpoint configuration.",
+        label = "producer", required = "false")
+    private String produces;
+
+    @Metadata(description = "Path to the Swagger specification file. The 
scheme, host base path are taken from this"
+        + " specification, but these can be overriden with properties on the 
component or endpoint level. If not"
+        + " given the component tries to load `swagger.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 overriden in endpoint"
+        + " configuration.", defaultValue = DEFAULT_SPECIFICATION_URI_STR, 
label = "producer", required = "false")
+    private URI specificationUri;
+
+    public RestSwaggerComponent() {
+    }
+
+    public RestSwaggerComponent(final CamelContext context) {
+        super(context);
+    }
+
+    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 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");
+    }
+
+    @Override
+    protected Endpoint createEndpoint(final String uri, final String 
remaining, final Map<String, Object> parameters)
+        throws Exception {
+        final Endpoint endpoint = new RestSwaggerEndpoint(uri, remaining, 
this);
+
+        setProperties(endpoint, parameters);
+
+        return endpoint;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/5b209152/components/camel-rest-swagger/src/main/java/org/apache/camel/component/rest/swagger/RestSwaggerEndpoint.java
----------------------------------------------------------------------
diff --git 
a/components/camel-rest-swagger/src/main/java/org/apache/camel/component/rest/swagger/RestSwaggerEndpoint.java
 
b/components/camel-rest-swagger/src/main/java/org/apache/camel/component/rest/swagger/RestSwaggerEndpoint.java
new file mode 100644
index 0000000..463d534
--- /dev/null
+++ 
b/components/camel-rest-swagger/src/main/java/org/apache/camel/component/rest/swagger/RestSwaggerEndpoint.java
@@ -0,0 +1,490 @@
+/**
+ * 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.swagger;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import static java.util.Optional.ofNullable;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import io.swagger.models.HttpMethod;
+import io.swagger.models.Operation;
+import io.swagger.models.Path;
+import io.swagger.models.Scheme;
+import io.swagger.models.Swagger;
+import io.swagger.parser.SwaggerParser;
+import io.swagger.util.Json;
+
+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.impl.DefaultEndpoint;
+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.util.ResourceHelper;
+
+import static 
org.apache.camel.component.rest.swagger.RestSwaggerHelper.isHostParam;
+import static 
org.apache.camel.component.rest.swagger.RestSwaggerHelper.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 Swagger specifications.
+ */
+@UriEndpoint(firstVersion = "2.19.0", scheme = "rest-swagger", title = "REST 
Swagger",
+    syntax = "rest-swagger:specificationUri#operationId", label = 
"rest,swagger,http", producerOnly = true)
+public final class RestSwaggerEndpoint extends DefaultEndpoint {
+
+    /** The name of the Camel component, be it `rest-swagger` or `petstore` */
+    private String assignedComponentName;
+
+    @UriParam(
+        description = "API basePath, for example \"`/v2`\". Default is unset, 
if set overrides the value present in"
+            + " Swagger specification and in the component configuration.",
+        defaultValue = "", label = "producer")
+    @Metadata(required = "false")
+    private String basePath;
+
+    @UriParam(description = "Name of the Camel component that will perform the 
requests. The compnent 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")
+    @Metadata(required = "false")
+    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 Swagger 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 correspoding"
+        + " REST configuration in the Camel Context. If you give this 
component a name (e.g. `petstore`) that"
+        + " REST configuration is consulted first, `rest-swagger` next, and 
global configuration last. If set"
+        + " overrides any value found in the Swagger specification, 
RestConfiguration. Overrides all other "
+        + " configuration.", label = "producer")
+    private String host;
+
+    @UriPath(description = "ID of the operation from the Swagger 
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 Swagger specification. Overrides all 
other configuration.", label = "producer")
+    private String produces;
+
+    @UriPath(
+        description = "Path to the Swagger specification file. The scheme, 
host base path are taken from this"
+            + " specification, but these can be overriden with properties on 
the component or endpoint level. If not"
+            + " given the component tries to load `swagger.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`). 
Overrides component configuration.",
+        defaultValue = RestSwaggerComponent.DEFAULT_SPECIFICATION_URI_STR,
+        defaultValueNote = "By default loads `swagger.json` file", label = 
"producer")
+    private URI specificationUri = 
RestSwaggerComponent.DEFAULT_SPECIFICATION_URI;
+
+    public RestSwaggerEndpoint() {
+        // help tooling instantiate endpoint
+    }
+
+    public RestSwaggerEndpoint(final String uri, final String remaining, final 
RestSwaggerComponent component) {
+        super(notEmpty(uri, "uri"), notNull(component, "component"));
+
+        assignedComponentName = before(uri, ":");
+
+        final URI componentSpecificationUri = component.getSpecificationUri();
+
+        specificationUri = ofNullable(before(remaining, "#")).map(URI::create)
+            
.orElse(ofNullable(componentSpecificationUri).orElse(RestSwaggerComponent.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 Swagger swagger = loadSpecificationFrom(camelContext, 
specificationUri);
+
+        final Map<String, Path> paths = swagger.getPaths();
+
+        for (final Entry<String, Path> pathEntry : paths.entrySet()) {
+            final Path path = pathEntry.getValue();
+
+            final Optional<Entry<HttpMethod, Operation>> maybeOperationEntry = 
path.getOperationMap().entrySet()
+                .stream().filter(operationEntry -> 
operationId.equals(operationEntry.getValue().getOperationId()))
+                .findAny();
+
+            if (maybeOperationEntry.isPresent()) {
+                final Entry<HttpMethod, Operation> operationEntry = 
maybeOperationEntry.get();
+
+                final String uriTemplate = pathEntry.getKey();
+
+                final HttpMethod httpMethod = operationEntry.getKey();
+                final String method = httpMethod.name();
+
+                final Operation operation = operationEntry.getValue();
+
+                return createProducerFor(swagger, operation, method, 
uriTemplate);
+            }
+        }
+
+        final String supportedOperations = paths.values().stream().flatMap(p 
-> p.getOperations().stream())
+            .map(Operation::getOperationId).collect(Collectors.joining(", "));
+
+        throw new IllegalArgumentException("The specified operation with ID: 
`" + operationId
+            + "` cannot be found in the Swagger specification loaded from `" + 
specificationUri
+            + "`. Operations defined in the specification are: " + 
supportedOperations);
+    }
+
+    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 isSingleton() {
+        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");
+    }
+
+    RestSwaggerComponent component() {
+        return (RestSwaggerComponent) getComponent();
+    }
+
+    Producer createProducerFor(final Swagger swagger, final Operation 
operation, final String method,
+        final String uriTemplate) throws Exception {
+        final String basePath = determineBasePath(swagger);
+
+        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());
+
+        setProperties(endpoint, determineEndpointParameters(swagger, 
operation));
+
+        return endpoint.createProducer();
+    }
+
+    String determineBasePath(final Swagger swagger) {
+        if (isNotEmpty(basePath)) {
+            return basePath;
+        }
+
+        final String componentBasePath = component().getBasePath();
+        if (isNotEmpty(componentBasePath)) {
+            return componentBasePath;
+        }
+
+        final String specificationBasePath = swagger.getBasePath();
+        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-swagger", true);
+        final String restConfigurationBasePath = 
restConfiguration.getContextPath();
+        if (isNotEmpty(restConfigurationBasePath)) {
+            return restConfigurationBasePath;
+        }
+
+        return RestSwaggerComponent.DEFAULT_BASE_PATH;
+    }
+
+    String determineComponentName() {
+        return 
Optional.ofNullable(componentName).orElse(component().getComponentName());
+    }
+
+    Map<String, Object> determineEndpointParameters(final Swagger swagger, 
final Operation operation) {
+        final Map<String, Object> parameters = new HashMap<>();
+
+        final String componentName = determineComponentName();
+        if (componentName != null) {
+            parameters.put("componentName", componentName);
+        }
+
+        final String host = determineHost(swagger);
+        if (host != null) {
+            parameters.put("host", host);
+        }
+
+        // what we consume is what the API defined by Swagger specification
+        // produces
+        final String determinedConsumes = 
determineOption(swagger.getProduces(), operation.getProduces(),
+            component().getConsumes(), consumes);
+
+        if (isNotEmpty(determinedConsumes)) {
+            parameters.put("consumes", determinedConsumes);
+        }
+
+        // what we produce is what the API defined by Swagger specification
+        // consumes
+        final String determinedProducers = 
determineOption(swagger.getConsumes(), operation.getConsumes(),
+            component().getProduces(), produces);
+
+        if (isNotEmpty(determinedProducers)) {
+            parameters.put("produces", determinedProducers);
+        }
+
+        return parameters;
+    }
+
+    String determineHost(final Swagger swagger) {
+        if (isNotEmpty(host)) {
+            return host;
+        }
+
+        final String componentHost = component().getHost();
+        if (isNotEmpty(componentHost)) {
+            return componentHost;
+        }
+
+        final String swaggerScheme = 
pickBestScheme(specificationUri.getScheme(), swagger.getSchemes());
+        final String swaggerHost = swagger.getHost();
+
+        if (isNotEmpty(swaggerScheme) && isNotEmpty(swaggerHost)) {
+            return swaggerScheme + "://" + swaggerHost;
+        }
+
+        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-swagger", 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-swagger".equals(assignedComponentName);
+
+        throw new IllegalStateException("Unable to determine destionation host 
for requests. The Swagger 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-swagger` component" : assignedComponentName 
+ "` or `rest-swagger` components")
+            + " and there is no global RestConfiguration with those 
properties");
+    }
+
+    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 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 Swagger definition model from the given path. Tries to resolve
+     * the resource using Camel's resource loading support, if it fails uses
+     * Swagger's resource loading support instead.
+     *
+     * @param uri URI of the specification
+     * @param camelContext context to use
+     * @return the specification
+     * @throws IOException
+     */
+    static Swagger loadSpecificationFrom(final CamelContext camelContext, 
final URI uri) throws IOException {
+        final ObjectMapper mapper = Json.mapper();
+
+        final SwaggerParser swaggerParser = new SwaggerParser();
+
+        final String uriAsString = uri.toString();
+
+        try (InputStream stream = 
ResourceHelper.resolveMandatoryResourceAsInputStream(camelContext, 
uriAsString)) {
+            final JsonNode node = mapper.readTree(stream);
+
+            return swaggerParser.read(node);
+        } catch (final IOException e) {
+            // try Swaggers loader
+            final Swagger swagger = swaggerParser.read(uriAsString);
+
+            if (swagger != null) {
+                return swagger;
+            }
+
+            throw new IllegalArgumentException("The given Swagger 
specification could not be loaded from `" + uri
+                + "`. Tried loading using Camel's resource resolution and 
using Swagger's own resource resolution."
+                + " Swagger tends to swallow exceptions while parsing, try 
specifying Java system property `debugParser`"
+                + " (e.g. `-DdebugParser=true`), the exception that occured 
when loading using Camel's resource"
+                + " loader follows", e);
+        }
+    }
+
+    static String pickBestScheme(final String specificationScheme, final 
List<Scheme> schemes) {
+        if (schemes != null && !schemes.isEmpty()) {
+            if (schemes.contains(Scheme.HTTPS)) {
+                return "https";
+            }
+
+            if (schemes.contains(Scheme.HTTP)) {
+                return "http";
+            }
+        }
+
+        if (specificationScheme != null) {
+            return specificationScheme;
+        }
+
+        // there is no support for WebSocket (Scheme.WS, Scheme.WSS)
+
+        return null;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/5b209152/components/camel-rest-swagger/src/main/java/org/apache/camel/component/rest/swagger/RestSwaggerHelper.java
----------------------------------------------------------------------
diff --git 
a/components/camel-rest-swagger/src/main/java/org/apache/camel/component/rest/swagger/RestSwaggerHelper.java
 
b/components/camel-rest-swagger/src/main/java/org/apache/camel/component/rest/swagger/RestSwaggerHelper.java
new file mode 100644
index 0000000..ca7e972
--- /dev/null
+++ 
b/components/camel-rest-swagger/src/main/java/org/apache/camel/component/rest/swagger/RestSwaggerHelper.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.swagger;
+
+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 RestSwaggerHelper {
+
+    private static final Pattern HOST_PATTERN = 
Pattern.compile("https?://[^:]+(:\\d+)?", Pattern.CASE_INSENSITIVE);
+
+    private RestSwaggerHelper() {
+        // 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 apsolute URI (e.g. http://api.example.com), 
given: `" + hostUri + "`");
+        }
+
+        return hostUri;
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/5b209152/components/camel-rest-swagger/src/main/resources/META-INF/LICENSE.txt
----------------------------------------------------------------------
diff --git 
a/components/camel-rest-swagger/src/main/resources/META-INF/LICENSE.txt 
b/components/camel-rest-swagger/src/main/resources/META-INF/LICENSE.txt
new file mode 100755
index 0000000..6b0b127
--- /dev/null
+++ b/components/camel-rest-swagger/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.
+

http://git-wip-us.apache.org/repos/asf/camel/blob/5b209152/components/camel-rest-swagger/src/main/resources/META-INF/NOTICE.txt
----------------------------------------------------------------------
diff --git 
a/components/camel-rest-swagger/src/main/resources/META-INF/NOTICE.txt 
b/components/camel-rest-swagger/src/main/resources/META-INF/NOTICE.txt
new file mode 100644
index 0000000..2e215bf
--- /dev/null
+++ b/components/camel-rest-swagger/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.

http://git-wip-us.apache.org/repos/asf/camel/blob/5b209152/components/camel-rest-swagger/src/main/resources/META-INF/services/org/apache/camel/component/rest-swagger
----------------------------------------------------------------------
diff --git 
a/components/camel-rest-swagger/src/main/resources/META-INF/services/org/apache/camel/component/rest-swagger
 
b/components/camel-rest-swagger/src/main/resources/META-INF/services/org/apache/camel/component/rest-swagger
new file mode 100644
index 0000000..f4213a9
--- /dev/null
+++ 
b/components/camel-rest-swagger/src/main/resources/META-INF/services/org/apache/camel/component/rest-swagger
@@ -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.
+#
+class=org.apache.camel.component.rest.swagger.RestSwaggerComponent

http://git-wip-us.apache.org/repos/asf/camel/blob/5b209152/components/camel-rest-swagger/src/test/java/org/apache/camel/component/rest/swagger/Pet.java
----------------------------------------------------------------------
diff --git 
a/components/camel-rest-swagger/src/test/java/org/apache/camel/component/rest/swagger/Pet.java
 
b/components/camel-rest-swagger/src/test/java/org/apache/camel/component/rest/swagger/Pet.java
new file mode 100644
index 0000000..50308ba
--- /dev/null
+++ 
b/components/camel-rest-swagger/src/test/java/org/apache/camel/component/rest/swagger/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.swagger;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement(name = "Pet")
+public class Pet {
+
+    public Integer id;
+
+    public String name;
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/5b209152/components/camel-rest-swagger/src/test/java/org/apache/camel/component/rest/swagger/RestSwaggerComponentTest.java
----------------------------------------------------------------------
diff --git 
a/components/camel-rest-swagger/src/test/java/org/apache/camel/component/rest/swagger/RestSwaggerComponentTest.java
 
b/components/camel-rest-swagger/src/test/java/org/apache/camel/component/rest/swagger/RestSwaggerComponentTest.java
new file mode 100644
index 0000000..c69649c
--- /dev/null
+++ 
b/components/camel-rest-swagger/src/test/java/org/apache/camel/component/rest/swagger/RestSwaggerComponentTest.java
@@ -0,0 +1,152 @@
+/**
+ * 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.swagger;
+
+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 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.core.WireMockConfiguration.wireMockConfig;
+
+@RunWith(Parameterized.class)
+public class RestSwaggerComponentTest 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")));
+    }
+
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        final CamelContext camelContext = super.createCamelContext();
+
+        final RestSwaggerComponent component = new RestSwaggerComponent();
+        component.setComponentName(componentName);
+        component.setHost("http://localhost:"; + petstore.port());
+
+        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);
+
+                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:addPet").marshal(jaxb).to("petStore:addPet").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("/swagger.json")).willReturn(aResponse().withBody(
+            
Files.readAllBytes(Paths.get(RestSwaggerComponentTest.class.getResource("/swagger.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>")));
+    }
+
+}

Reply via email to