You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ff...@apache.org on 2019/12/19 22:42:40 UTC

[camel] branch master updated: [CAMEL-14315]add openapi-rest-dsl-generator

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

ffang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/master by this push:
     new 946a24f  [CAMEL-14315]add openapi-rest-dsl-generator
946a24f is described below

commit 946a24fc574576131f80bfa3a279b5f4fef55180
Author: Freeman Fang <fr...@gmail.com>
AuthorDate: Thu Dec 19 17:36:35 2019 -0500

    [CAMEL-14315]add openapi-rest-dsl-generator
---
 docs/components/modules/ROOT/nav.adoc              |    1 -
 .../ROOT/pages/aws-translate-component.adoc        |    4 +-
 .../openapi-rest-dsl-generator/openapi-spec.json   | 1093 ++++++++++++++++++++
 tooling/openapi-rest-dsl-generator/openapi-v2.json | 1042 +++++++++++++++++++
 tooling/openapi-rest-dsl-generator/pom.xml         |   94 ++
 .../generator/openapi/AppendableGenerator.java     |   38 +
 .../camel/generator/openapi/CodeEmitter.java       |   25 +
 .../generator/openapi/DestinationGenerator.java    |   26 +
 .../generator/openapi/DirectToOperationId.java     |   38 +
 .../camel/generator/openapi/FilerGenerator.java    |   40 +
 .../openapi/MethodBodySourceCodeEmitter.java       |  166 +++
 .../camel/generator/openapi/OperationFilter.java   |   45 +
 .../camel/generator/openapi/OperationVisitor.java  |  239 +++++
 .../camel/generator/openapi/PathGenerator.java     |   39 +
 .../camel/generator/openapi/PathVisitor.java       |   98 ++
 .../generator/openapi/RestDefinitionEmitter.java   |   99 ++
 .../openapi/RestDslDefinitionGenerator.java        |   40 +
 .../camel/generator/openapi/RestDslGenerator.java  |  203 ++++
 .../openapi/RestDslSourceCodeGenerator.java        |  207 ++++
 .../generator/openapi/RestDslXmlGenerator.java     |  114 ++
 .../SpringBootProjectSourceCodeGenerator.java      |  107 ++
 .../openapi/MethodBodySourceCodeEmitterTest.java   |   56 +
 .../PathSpringBootProjectSourceGeneratorTest.java  |   46 +
 .../openapi/RestDefinitionEmitterTest.java         |   71 ++
 .../generator/openapi/RestDslGeneratorTest.java    |  122 +++
 .../generator/openapi/RestDslGeneratorV3Test.java  |  118 +++
 .../openapi/RestDslSourceCodeGeneratorTest.java    |  109 ++
 .../openapi/RestDslSourceCodeGeneratorV3Test.java  |  112 ++
 .../generator/openapi/RestDslXmlGeneratorTest.java |  117 +++
 .../openapi/RestDslXmlGeneratorV3Test.java         |  116 +++
 .../src/test/resources/MyRestRoute.txt             |  294 ++++++
 .../src/test/resources/MyRestRouteFilter.txt       |   78 ++
 .../src/test/resources/MyRestRouteFilterV3.txt     |   76 ++
 .../src/test/resources/MyRestRouteV3.txt           |  288 ++++++
 .../src/test/resources/OpenApiPetstore.txt         |  291 ++++++
 .../resources/OpenApiPetstoreWithRestComponent.txt |  294 ++++++
 .../OpenApiPetstoreWithRestComponentXml.txt        |  101 ++
 .../src/test/resources/OpenApiPetstoreXml.txt      |  100 ++
 .../src/test/resources/OpenApiV3Petstore.txt       |  285 +++++
 .../OpenApiV3PetstoreWithRestComponent.txt         |  288 ++++++
 .../OpenApiV3PetstoreWithRestComponentXml.txt      |   99 ++
 .../src/test/resources/OpenApiV3PetstoreXml.txt    |   99 ++
 .../test/resources/SpringBootRestController.txt    |   24 +
 .../src/test/resources/log4j2.properties           |   28 +
 tooling/pom.xml                                    |    1 +
 45 files changed, 6969 insertions(+), 2 deletions(-)

diff --git a/docs/components/modules/ROOT/nav.adoc b/docs/components/modules/ROOT/nav.adoc
index fab718f..ee5e565 100644
--- a/docs/components/modules/ROOT/nav.adoc
+++ b/docs/components/modules/ROOT/nav.adoc
@@ -42,7 +42,6 @@
 * xref:aws-sqs-component.adoc[AWS Simple Queue Service Component]
 * xref:aws-swf-component.adoc[AWS Simple Workflow Component]
 * xref:aws-translate-component.adoc[AWS Translate Component]
-* xref:aws-translate-component.adoc[AWS Translate Component]
 * xref:aws-xray.adoc[AWS XRay Component]
 * xref:azure-blob-component.adoc[Azure Storage Blob Service Component]
 * xref:azure-queue-component.adoc[Azure Storage Queue Service Component]
diff --git a/docs/components/modules/ROOT/pages/aws-translate-component.adoc b/docs/components/modules/ROOT/pages/aws-translate-component.adoc
index 9c667da..82505c6 100644
--- a/docs/components/modules/ROOT/pages/aws-translate-component.adoc
+++ b/docs/components/modules/ROOT/pages/aws-translate-component.adoc
@@ -1,6 +1,6 @@
 [[aws-translate-component]]
 = AWS Translate Component
-:page-source: components/camel-aws-translate/bin/src/main/docs/aws-translate-component.adoc
+:page-source: components/camel-aws-translate/src/main/docs/aws-translate-component.adoc
 
 *Since Camel 3.0*
 
@@ -153,6 +153,8 @@ the https://aws.amazon.com/translate/[Amazon Translate] service.
 
 |`CamelAwsTranslateTargetLanguage` |`String` |The text target language
 
+|`CamelAwsTranslateTerminologyNames` |`String` |The terminologies to use
+
 |`CamelAwsTranslateOperation` |`String` |The operation to perform
 |=======================================================================
 
diff --git a/tooling/openapi-rest-dsl-generator/openapi-spec.json b/tooling/openapi-rest-dsl-generator/openapi-spec.json
new file mode 100644
index 0000000..099a353
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/openapi-spec.json
@@ -0,0 +1,1093 @@
+{
+    "openapi": "3.0.2",
+    "info": {
+        "title": "OpenApi Petstore",
+        "version": "1.0.0",
+        "description": "This is a sample server Petstore server.  You can find out more about Swagger 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.",
+        "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"
+        }
+    },
+    "servers": [
+        {
+            "url": "http://petstore.openapi.io/api/v3"
+        }
+    ],
+    "paths": {
+        "/pet": {
+            "put": {
+                "requestBody": {
+                    "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"
+                            }
+                        }
+                    },
+                    "required": true
+                },
+                "tags": [
+                    "pet"
+                ],
+                "responses": {
+                    "400": {
+                        "description": "Invalid ID supplied"
+                    },
+                    "404": {
+                        "description": "Pet not found"
+                    },
+                    "405": {
+                        "description": "Validation exception"
+                    }
+                },
+                "security": [
+                    {
+                        "petstore_auth": [
+                            "write:pets",
+                            "read:pets"
+                        ]
+                    }
+                ],
+                "operationId": "updatePet",
+                "summary": "Update an existing pet",
+                "description": ""
+            },
+            "post": {
+                "requestBody": {
+                    "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"
+                            }
+                        }
+                    },
+                    "required": true
+                },
+                "tags": [
+                    "pet"
+                ],
+                "parameters": [
+                    {
+                        "name": "verbose",
+                        "description": "Verbose data",
+                        "schema": {
+                            "default": false,
+                            "type": "boolean"
+                        },
+                        "in": "query"
+                    }
+                ],
+                "responses": {
+                    "405": {
+                        "description": "Invalid input"
+                    }
+                },
+                "security": [
+                    {
+                        "petstore_auth": [
+                            "write:pets",
+                            "read:pets"
+                        ]
+                    }
+                ],
+                "operationId": "addPet",
+                "summary": "Add a new pet to the store",
+                "description": ""
+            }
+        },
+        "/pet/findByStatus": {
+            "get": {
+                "tags": [
+                    "pet"
+                ],
+                "parameters": [
+                    {
+                        "style": "form",
+                        "explode": true,
+                        "name": "status",
+                        "description": "Status values that need to be considered for filter",
+                        "schema": {
+                            "type": "array",
+                            "items": {
+                                "default": "available",
+                                "enum": [
+                                    "available",
+                                    "pending",
+                                    "sold"
+                                ],
+                                "type": "string"
+                            }
+                        },
+                        "in": "query",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "content": {
+                            "application/xml": {
+                                "schema": {
+                                    "type": "array",
+                                    "items": {
+                                        "$ref": "#/components/schemas/Pet"
+                                    }
+                                }
+                            },
+                            "application/json": {
+                                "schema": {
+                                    "type": "array",
+                                    "items": {
+                                        "$ref": "#/components/schemas/Pet"
+                                    }
+                                }
+                            }
+                        },
+                        "description": "successful operation"
+                    },
+                    "400": {
+                        "description": "Invalid status value"
+                    }
+                },
+                "security": [
+                    {
+                        "petstore_auth": [
+                            "write:pets",
+                            "read:pets"
+                        ]
+                    }
+                ],
+                "operationId": "findPetsByStatus",
+                "summary": "Finds Pets by status",
+                "description": "Multiple status values can be provided with comma separated strings"
+            }
+        },
+        "/pet/findByTags": {
+            "get": {
+                "tags": [
+                    "pet"
+                ],
+                "parameters": [
+                    {
+                        "style": "form",
+                        "explode": true,
+                        "name": "tags",
+                        "description": "Tags to filter by",
+                        "schema": {
+                            "type": "array",
+                            "items": {
+                                "type": "string"
+                            }
+                        },
+                        "in": "query",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "content": {
+                            "application/xml": {
+                                "schema": {
+                                    "type": "array",
+                                    "items": {
+                                        "$ref": "#/components/schemas/Pet"
+                                    }
+                                }
+                            },
+                            "application/json": {
+                                "schema": {
+                                    "type": "array",
+                                    "items": {
+                                        "$ref": "#/components/schemas/Pet"
+                                    }
+                                }
+                            }
+                        },
+                        "description": "successful operation"
+                    },
+                    "400": {
+                        "description": "Invalid tag value"
+                    }
+                },
+                "deprecated": true,
+                "security": [
+                    {
+                        "petstore_auth": [
+                            "write:pets",
+                            "read:pets"
+                        ]
+                    }
+                ],
+                "operationId": "findPetsByTags",
+                "summary": "Finds Pets by tags",
+                "description": "Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing."
+            }
+        },
+        "/pet/{petId}": {
+            "get": {
+                "tags": [
+                    "pet"
+                ],
+                "parameters": [
+                    {
+                        "name": "petId",
+                        "description": "ID of pet to return",
+                        "schema": {
+                            "format": "int64",
+                            "type": "integer"
+                        },
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "content": {
+                            "application/xml": {
+                                "schema": {
+                                    "$ref": "#/components/schemas/Pet"
+                                }
+                            },
+                            "application/json": {
+                                "schema": {
+                                    "$ref": "#/components/schemas/Pet"
+                                }
+                            }
+                        },
+                        "description": "successful operation"
+                    },
+                    "400": {
+                        "description": "Invalid ID supplied"
+                    },
+                    "404": {
+                        "description": "Pet not found"
+                    }
+                },
+                "security": [
+                    {
+                        "api_key": []
+                    }
+                ],
+                "operationId": "getPetById",
+                "summary": "Find pet by ID",
+                "description": "Returns a single pet"
+            },
+            "post": {
+                "requestBody": {
+                    "content": {
+                        "application/x-www-form-urlencoded": {
+                            "schema": {
+                                "type": "object",
+                                "properties": {
+                                    "name": {
+                                        "description": "Updated name of the pet",
+                                        "type": "string"
+                                    },
+                                    "status": {
+                                        "description": "Updated status of the pet",
+                                        "type": "string"
+                                    }
+                                }
+                            }
+                        }
+                    },
+                    "required": true
+                },
+                "tags": [
+                    "pet"
+                ],
+                "parameters": [
+                    {
+                        "name": "petId",
+                        "description": "ID of pet that needs to be updated",
+                        "schema": {
+                            "format": "int64",
+                            "type": "integer"
+                        },
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "405": {
+                        "description": "Invalid input"
+                    }
+                },
+                "security": [
+                    {
+                        "petstore_auth": [
+                            "write:pets",
+                            "read:pets"
+                        ]
+                    }
+                ],
+                "operationId": "updatePetWithForm",
+                "summary": "Updates a pet in the store with form data",
+                "description": ""
+            },
+            "delete": {
+                "tags": [
+                    "pet"
+                ],
+                "parameters": [
+                    {
+                        "name": "api_key",
+                        "schema": {
+                            "type": "string"
+                        },
+                        "in": "header",
+                        "required": false
+                    },
+                    {
+                        "name": "petId",
+                        "description": "Pet id to delete",
+                        "schema": {
+                            "format": "int64",
+                            "type": "integer"
+                        },
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "400": {
+                        "description": "Invalid ID supplied"
+                    },
+                    "404": {
+                        "description": "Pet not found"
+                    }
+                },
+                "security": [
+                    {
+                        "petstore_auth": [
+                            "write:pets",
+                            "read:pets"
+                        ]
+                    }
+                ],
+                "operationId": "deletePet",
+                "summary": "Deletes a pet",
+                "description": ""
+            }
+        },
+        "/pet/{petId}/uploadImage": {
+            "post": {
+                "requestBody": {
+                    "content": {
+                        "multipart/form-data": {
+                            "schema": {
+                                "type": "object",
+                                "properties": {
+                                    "additionalMetadata": {
+                                        "description": "Additional data to pass to server",
+                                        "type": "string"
+                                    },
+                                    "file": {
+                                        "format": "binary",
+                                        "description": "file to upload",
+                                        "type": "string"
+                                    }
+                                }
+                            }
+                        }
+                    },
+                    "required": true
+                },
+                "tags": [
+                    "pet"
+                ],
+                "parameters": [
+                    {
+                        "name": "petId",
+                        "description": "ID of pet to update",
+                        "schema": {
+                            "format": "int64",
+                            "type": "integer"
+                        },
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "content": {
+                            "application/json": {
+                                "schema": {
+                                    "$ref": "#/components/schemas/ApiResponse"
+                                }
+                            }
+                        },
+                        "description": "successful operation"
+                    }
+                },
+                "security": [
+                    {
+                        "petstore_auth": [
+                            "write:pets",
+                            "read:pets"
+                        ]
+                    }
+                ],
+                "operationId": "uploadFile",
+                "summary": "uploads an image",
+                "description": ""
+            }
+        },
+        "/store/inventory": {
+            "get": {
+                "tags": [
+                    "store"
+                ],
+                "responses": {
+                    "200": {
+                        "content": {
+                            "application/json": {
+                                "schema": {
+                                    "type": "object",
+                                    "additionalProperties": {
+                                        "format": "int32",
+                                        "type": "integer"
+                                    }
+                                }
+                            }
+                        },
+                        "description": "successful operation"
+                    }
+                },
+                "security": [
+                    {
+                        "api_key": []
+                    }
+                ],
+                "operationId": "getInventory",
+                "summary": "Returns pet inventories by status",
+                "description": "Returns a map of status codes to quantities"
+            }
+        },
+        "/store/order": {
+            "post": {
+                "requestBody": {
+                    "description": "order placed for purchasing the pet",
+                    "content": {
+                        "*/*": {
+                            "schema": {
+                                "$ref": "#/components/schemas/Order"
+                            }
+                        }
+                    },
+                    "required": true
+                },
+                "tags": [
+                    "store"
+                ],
+                "responses": {
+                    "200": {
+                        "content": {
+                            "application/xml": {
+                                "schema": {
+                                    "$ref": "#/components/schemas/Order"
+                                }
+                            },
+                            "application/json": {
+                                "schema": {
+                                    "$ref": "#/components/schemas/Order"
+                                }
+                            }
+                        },
+                        "description": "successful operation"
+                    },
+                    "400": {
+                        "description": "Invalid Order"
+                    }
+                },
+                "operationId": "placeOrder",
+                "summary": "Place an order for a pet",
+                "description": ""
+            }
+        },
+        "/store/order/{orderId}": {
+            "get": {
+                "tags": [
+                    "store"
+                ],
+                "parameters": [
+                    {
+                        "name": "orderId",
+                        "description": "ID of pet that needs to be fetched",
+                        "schema": {
+                            "format": "int64",
+                            "maximum": 10,
+                            "minimum": 1,
+                            "type": "integer"
+                        },
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "content": {
+                            "application/xml": {
+                                "schema": {
+                                    "$ref": "#/components/schemas/Order"
+                                }
+                            },
+                            "application/json": {
+                                "schema": {
+                                    "$ref": "#/components/schemas/Order"
+                                }
+                            }
+                        },
+                        "description": "successful operation"
+                    },
+                    "400": {
+                        "description": "Invalid ID supplied"
+                    },
+                    "404": {
+                        "description": "Order not found"
+                    }
+                },
+                "operationId": "getOrderById",
+                "summary": "Find purchase order by ID",
+                "description": "For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions"
+            },
+            "delete": {
+                "tags": [
+                    "store"
+                ],
+                "parameters": [
+                    {
+                        "name": "orderId",
+                        "description": "ID of the order that needs to be deleted",
+                        "schema": {
+                            "format": "int64",
+                            "minimum": 1,
+                            "type": "integer"
+                        },
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "400": {
+                        "description": "Invalid ID supplied"
+                    },
+                    "404": {
+                        "description": "Order not found"
+                    }
+                },
+                "operationId": "deleteOrder",
+                "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"
+            }
+        },
+        "/user": {
+            "post": {
+                "requestBody": {
+                    "description": "Created user object",
+                    "content": {
+                        "*/*": {
+                            "schema": {
+                                "$ref": "#/components/schemas/User"
+                            }
+                        }
+                    },
+                    "required": true
+                },
+                "tags": [
+                    "user"
+                ],
+                "responses": {
+                    "default": {
+                        "description": "successful operation"
+                    }
+                },
+                "operationId": "createUser",
+                "summary": "Create user",
+                "description": "This can only be done by the logged in user."
+            }
+        },
+        "/user/createWithArray": {
+            "post": {
+                "requestBody": {
+                    "description": "List of user object",
+                    "content": {
+                        "*/*": {
+                            "schema": {
+                                "type": "array",
+                                "items": {
+                                    "$ref": "#/components/schemas/User"
+                                }
+                            }
+                        }
+                    },
+                    "required": true
+                },
+                "tags": [
+                    "user"
+                ],
+                "responses": {
+                    "default": {
+                        "description": "successful operation"
+                    }
+                },
+                "operationId": "createUsersWithArrayInput",
+                "summary": "Creates list of users with given input array",
+                "description": ""
+            }
+        },
+        "/user/createWithList": {
+            "post": {
+                "requestBody": {
+                    "description": "List of user object",
+                    "content": {
+                        "*/*": {
+                            "schema": {
+                                "type": "array",
+                                "items": {
+                                    "$ref": "#/components/schemas/User"
+                                }
+                            }
+                        }
+                    },
+                    "required": true
+                },
+                "tags": [
+                    "user"
+                ],
+                "responses": {
+                    "default": {
+                        "description": "successful operation"
+                    }
+                },
+                "operationId": "createUsersWithListInput",
+                "summary": "Creates list of users with given input array",
+                "description": ""
+            }
+        },
+        "/user/login": {
+            "get": {
+                "tags": [
+                    "user"
+                ],
+                "parameters": [
+                    {
+                        "name": "username",
+                        "description": "The user name for login",
+                        "schema": {
+                            "type": "string"
+                        },
+                        "in": "query",
+                        "required": true
+                    },
+                    {
+                        "name": "password",
+                        "description": "The password for login in clear text",
+                        "schema": {
+                            "type": "string"
+                        },
+                        "in": "query",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "headers": {
+                            "X-Rate-Limit": {
+                                "schema": {
+                                    "format": "int32",
+                                    "type": "integer"
+                                },
+                                "description": "calls per hour allowed by the user"
+                            },
+                            "X-Expires-After": {
+                                "schema": {
+                                    "format": "date-time",
+                                    "type": "string"
+                                },
+                                "description": "date in UTC when token expires"
+                            }
+                        },
+                        "content": {
+                            "application/xml": {
+                                "schema": {
+                                    "type": "string"
+                                }
+                            },
+                            "application/json": {
+                                "schema": {
+                                    "type": "string"
+                                }
+                            }
+                        },
+                        "description": "successful operation"
+                    },
+                    "400": {
+                        "description": "Invalid username/password supplied"
+                    }
+                },
+                "operationId": "loginUser",
+                "summary": "Logs user into the system",
+                "description": ""
+            }
+        },
+        "/user/logout": {
+            "get": {
+                "tags": [
+                    "user"
+                ],
+                "responses": {
+                    "default": {
+                        "description": "successful operation"
+                    }
+                },
+                "operationId": "logoutUser",
+                "summary": "Logs out current logged in user session",
+                "description": ""
+            }
+        },
+        "/user/{username}": {
+            "get": {
+                "tags": [
+                    "user"
+                ],
+                "parameters": [
+                    {
+                        "name": "username",
+                        "description": "The name that needs to be fetched. Use user1 for testing. ",
+                        "schema": {
+                            "type": "string"
+                        },
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "content": {
+                            "application/xml": {
+                                "schema": {
+                                    "$ref": "#/components/schemas/User"
+                                }
+                            },
+                            "application/json": {
+                                "schema": {
+                                    "$ref": "#/components/schemas/User"
+                                }
+                            }
+                        },
+                        "description": "successful operation"
+                    },
+                    "400": {
+                        "description": "Invalid username supplied"
+                    },
+                    "404": {
+                        "description": "User not found"
+                    }
+                },
+                "operationId": "getUserByName",
+                "summary": "Get user by user name",
+                "description": ""
+            },
+            "put": {
+                "requestBody": {
+                    "description": "Updated user object",
+                    "content": {
+                        "*/*": {
+                            "schema": {
+                                "$ref": "#/components/schemas/User"
+                            }
+                        }
+                    },
+                    "required": true
+                },
+                "tags": [
+                    "user"
+                ],
+                "parameters": [
+                    {
+                        "name": "username",
+                        "description": "name that need to be updated",
+                        "schema": {
+                            "type": "string"
+                        },
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "400": {
+                        "description": "Invalid user supplied"
+                    },
+                    "404": {
+                        "description": "User not found"
+                    }
+                },
+                "operationId": "updateUser",
+                "summary": "Updated user",
+                "description": "This can only be done by the logged in user."
+            },
+            "delete": {
+                "tags": [
+                    "user"
+                ],
+                "parameters": [
+                    {
+                        "name": "username",
+                        "description": "The name that needs to be deleted",
+                        "schema": {
+                            "type": "string"
+                        },
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "400": {
+                        "description": "Invalid username supplied"
+                    },
+                    "404": {
+                        "description": "User not found"
+                    }
+                },
+                "operationId": "deleteUser",
+                "summary": "Delete user",
+                "description": "This can only be done by the logged in user."
+            }
+        }
+    },
+    "components": {
+        "schemas": {
+            "Order": {
+                "type": "object",
+                "properties": {
+                    "id": {
+                        "format": "int64",
+                        "type": "integer"
+                    },
+                    "petId": {
+                        "format": "int64",
+                        "type": "integer"
+                    },
+                    "quantity": {
+                        "format": "int32",
+                        "type": "integer"
+                    },
+                    "shipDate": {
+                        "format": "date-time",
+                        "type": "string"
+                    },
+                    "status": {
+                        "description": "Order Status",
+                        "enum": [
+                            "placed",
+                            "approved",
+                            "delivered"
+                        ],
+                        "type": "string"
+                    },
+                    "complete": {
+                        "default": false,
+                        "type": "boolean"
+                    }
+                },
+                "xml": {
+                    "name": "Order"
+                }
+            },
+            "Category": {
+                "type": "object",
+                "properties": {
+                    "id": {
+                        "format": "int64",
+                        "type": "integer"
+                    },
+                    "name": {
+                        "type": "string"
+                    }
+                },
+                "xml": {
+                    "name": "Category"
+                }
+            },
+            "User": {
+                "type": "object",
+                "properties": {
+                    "id": {
+                        "format": "int64",
+                        "type": "integer"
+                    },
+                    "username": {
+                        "type": "string"
+                    },
+                    "firstName": {
+                        "type": "string"
+                    },
+                    "lastName": {
+                        "type": "string"
+                    },
+                    "email": {
+                        "type": "string"
+                    },
+                    "password": {
+                        "type": "string"
+                    },
+                    "phone": {
+                        "type": "string"
+                    },
+                    "userStatus": {
+                        "format": "int32",
+                        "description": "User Status",
+                        "type": "integer"
+                    }
+                },
+                "xml": {
+                    "name": "User"
+                }
+            },
+            "Tag": {
+                "type": "object",
+                "properties": {
+                    "id": {
+                        "format": "int64",
+                        "type": "integer"
+                    },
+                    "name": {
+                        "type": "string"
+                    }
+                },
+                "xml": {
+                    "name": "Tag"
+                }
+            },
+            "Pet": {
+                "required": [
+                    "name",
+                    "photoUrls"
+                ],
+                "type": "object",
+                "properties": {
+                    "id": {
+                        "format": "int64",
+                        "type": "integer"
+                    },
+                    "category": {
+                        "$ref": "#/components/schemas/Category"
+                    },
+                    "name": {
+                        "type": "string",
+                        "example": "doggie"
+                    },
+                    "photoUrls": {
+                        "type": "array",
+                        "items": {
+                            "type": "string"
+                        },
+                        "xml": {
+                            "name": "photoUrl",
+                            "wrapped": true
+                        }
+                    },
+                    "tags": {
+                        "type": "array",
+                        "items": {
+                            "$ref": "#/components/schemas/Tag"
+                        },
+                        "xml": {
+                            "name": "tag",
+                            "wrapped": true
+                        }
+                    },
+                    "status": {
+                        "description": "pet status in the store",
+                        "enum": [
+                            "available",
+                            "pending",
+                            "sold"
+                        ],
+                        "type": "string"
+                    }
+                },
+                "xml": {
+                    "name": "Pet"
+                }
+            },
+            "ApiResponse": {
+                "type": "object",
+                "properties": {
+                    "code": {
+                        "format": "int32",
+                        "type": "integer"
+                    },
+                    "type": {
+                        "type": "string"
+                    },
+                    "message": {
+                        "type": "string"
+                    }
+                }
+            }
+        },
+        "securitySchemes": {
+            "petstore_auth": {
+                "flows": {
+                    "implicit": {
+                        "authorizationUrl": "http://petstore.swagger.io/oauth/dialog",
+                        "scopes": {
+                            "write:pets": "modify pets in your account",
+                            "read:pets": "read your pets"
+                        }
+                    }
+                },
+                "type": "oauth2"
+            },
+            "api_key": {
+                "type": "apiKey",
+                "name": "api_key",
+                "in": "header"
+            }
+        }
+    },
+    "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"
+            }
+        }
+    ],
+    "externalDocs": {
+        "description": "Find out more about Swagger",
+        "url": "http://swagger.io"
+    }
+}
diff --git a/tooling/openapi-rest-dsl-generator/openapi-v2.json b/tooling/openapi-rest-dsl-generator/openapi-v2.json
new file mode 100644
index 0000000..8d762a8
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/openapi-v2.json
@@ -0,0 +1,1042 @@
+{
+  "swagger": "2.0",
+  "info": {
+    "description": "This is a sample server Petstore server.  You can find out more about Swagger 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.openapi.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"
+            }
+          },
+          {
+            "name": "verbose",
+            "in": "query",
+            "description": "Verbose data",
+            "type": "boolean",
+            "default": false
+          }
+        ],
+        "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/tooling/openapi-rest-dsl-generator/pom.xml b/tooling/openapi-rest-dsl-generator/pom.xml
new file mode 100644
index 0000000..f2d3b28
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/pom.xml
@@ -0,0 +1,94 @@
+<?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/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.camel</groupId>
+        <artifactId>tooling</artifactId>
+        <version>3.1.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>camel-openapi-rest-dsl-generator</artifactId>
+
+    <name>Camel :: Tooling :: OpenApi REST DSL Generator</name>
+    <description>Generator of REST DSL route definitions from OpenAPI specification</description>
+
+    <properties>
+        <camel.osgi.import.pkg>
+            org.apache.camel.*;${camel.osgi.import.camel.version},
+            *
+        </camel.osgi.import.pkg>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.squareup</groupId>
+            <artifactId>javapoet</artifactId>
+            <version>${javapoet-version}</version>
+        </dependency>
+        <dependency>
+            <groupId>io.apicurio</groupId>
+            <artifactId>apicurio-data-models</artifactId>
+            <version>1.0.16.Final</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+            <version>${commons-lang3-version}</version>
+        </dependency>
+
+        <!-- test -->
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.assertj</groupId>
+            <artifactId>assertj-core</artifactId>
+            <version>${assertj-version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.logging.log4j</groupId>
+            <artifactId>log4j-api</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.logging.log4j</groupId>
+            <artifactId>log4j-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.logging.log4j</groupId>
+            <artifactId>log4j-slf4j-impl</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+    </dependencies>
+</project>
diff --git a/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/AppendableGenerator.java b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/AppendableGenerator.java
new file mode 100644
index 0000000..4aabcd8
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/AppendableGenerator.java
@@ -0,0 +1,38 @@
+/*
+ * 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.generator.openapi;
+
+import java.io.IOException;
+
+import com.squareup.javapoet.JavaFile;
+import io.apicurio.datamodels.openapi.models.OasDocument;
+
+
+final class AppendableGenerator extends RestDslSourceCodeGenerator<Appendable> {
+
+    AppendableGenerator(final OasDocument openapi) {
+        super(openapi);
+    }
+
+    @Override
+    public void generate(final Appendable destination) throws IOException {
+        final JavaFile javaFile = generateSourceCode();
+
+        javaFile.writeTo(destination);
+    }
+
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/CodeEmitter.java b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/CodeEmitter.java
new file mode 100644
index 0000000..47ac904
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/CodeEmitter.java
@@ -0,0 +1,25 @@
+/*
+ * 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.generator.openapi;
+
+interface CodeEmitter<T> {
+
+    CodeEmitter<T> emit(String method, Object... args);
+
+    T result();
+
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/DestinationGenerator.java b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/DestinationGenerator.java
new file mode 100644
index 0000000..acdd138
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/DestinationGenerator.java
@@ -0,0 +1,26 @@
+/*
+ * 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.generator.openapi;
+
+import io.apicurio.datamodels.openapi.models.OasOperation;
+
+@FunctionalInterface
+public interface DestinationGenerator {
+
+    String generateDestinationFor(OasOperation operation);
+
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/DirectToOperationId.java b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/DirectToOperationId.java
new file mode 100644
index 0000000..bab373b
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/DirectToOperationId.java
@@ -0,0 +1,38 @@
+/*
+ * 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.generator.openapi;
+
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import io.apicurio.datamodels.openapi.models.OasOperation;
+
+
+public final class DirectToOperationId implements DestinationGenerator {
+
+    private final AtomicInteger directRouteCount = new AtomicInteger(0);
+
+    @Override
+    public String generateDestinationFor(final OasOperation operation) {
+        return "direct:" + Optional.ofNullable(operation.operationId).orElseGet(this::generateDirectName);
+    }
+
+    String generateDirectName() {
+        return "rest" + directRouteCount.incrementAndGet();
+    }
+
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/FilerGenerator.java b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/FilerGenerator.java
new file mode 100644
index 0000000..ead7341
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/FilerGenerator.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.generator.openapi;
+
+import java.io.IOException;
+
+import javax.annotation.processing.Filer;
+
+import com.squareup.javapoet.JavaFile;
+import io.apicurio.datamodels.openapi.models.OasDocument;
+
+
+final class FilerGenerator extends RestDslSourceCodeGenerator<Filer> {
+
+    FilerGenerator(final OasDocument openapi) {
+        super(openapi);
+    }
+
+    @Override
+    public void generate(final Filer destination) throws IOException {
+        final JavaFile javaFile = generateSourceCode();
+
+        javaFile.writeTo(destination);
+    }
+
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/MethodBodySourceCodeEmitter.java b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/MethodBodySourceCodeEmitter.java
new file mode 100644
index 0000000..8b2524b
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/MethodBodySourceCodeEmitter.java
@@ -0,0 +1,166 @@
+/*
+ * 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.generator.openapi;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.List;
+import java.util.StringJoiner;
+import java.util.stream.Collectors;
+
+import com.squareup.javapoet.MethodSpec;
+
+import static org.apache.commons.lang3.ClassUtils.isPrimitiveOrWrapper;
+
+class MethodBodySourceCodeEmitter implements CodeEmitter<MethodSpec> {
+
+    private final MethodSpec.Builder builder;
+
+    private boolean first = true;
+
+    private final Deque<Integer> indentIntentStack = new ArrayDeque<>();
+
+    private final Deque<Integer> indentStack = new ArrayDeque<>();
+
+    MethodBodySourceCodeEmitter(final MethodSpec.Builder builder) {
+        this.builder = builder;
+        indentStack.push(0);
+    }
+
+    @Override
+    public CodeEmitter<MethodSpec> emit(final String method, final Object... args) {
+        final boolean hasArgs = args != null && args.length > 0;
+
+        final int indent = indentLevelOf(method);
+
+        if (!first) {
+            builder.addCode("\n");
+        }
+
+        builder.addCode(String.join("", Collections.nCopies(indentStack.peek(), "$<")));
+        builder.addCode(String.join("", Collections.nCopies(indent, "$>")));
+
+        if (!first) {
+            builder.addCode(".");
+        }
+
+        indentStack.push(indent);
+
+        if (hasArgs) {
+            builder.addCode("$L(" + invocationLiteralsFor(args) + ")", extend(method, argumentsFor(args)));
+        } else {
+            builder.addCode("$L()", method);
+        }
+
+        first = false;
+
+        return this;
+    }
+
+    @Override
+    public MethodSpec result() {
+        builder.addCode(String.join("", Collections.nCopies(indentStack.peek(), "$<")));
+        builder.addCode(";\n");
+        return builder.build();
+    }
+
+    Object[] argumentsFor(final Object[] args) {
+        final List<Object> arguments = new ArrayList<>(args.length);
+
+        for (final Object arg : args) {
+            if (isPrimitiveOrWrapper(arg.getClass())) {
+                arguments.add(arg);
+            } else if (arg instanceof String) {
+                arguments.add(arg);
+            } else if (arg instanceof Enum) {
+                arguments.add(arg.getClass());
+                arguments.add(arg);
+            } else if (arg instanceof String[]) {
+                arguments.add(Arrays.stream((String[]) arg).collect(Collectors.joining(",")));
+            }
+        }
+
+        return arguments.toArray(new Object[arguments.size()]);
+    }
+
+    Object[] extend(final Object first, final Object... others) {
+        if (others == null || others.length == 0) {
+            return new Object[] {first};
+        }
+
+        final Object[] ret = new Object[1 + others.length];
+
+        ret[0] = first;
+        System.arraycopy(others, 0, ret, 1, others.length);
+
+        return ret;
+    }
+
+    int indentLevelOf(final String method) {
+        switch (method) {
+        case "rest":
+            return 0;
+        case "post":
+        case "get":
+        case "put":
+        case "patch":
+        case "delete":
+        case "head":
+        case "options":
+            return 1;
+        case "param":
+            indentIntentStack.push(3);
+            return 2;
+        case "endParam":
+            indentIntentStack.pop();
+            return 2;
+        case "route":
+            indentIntentStack.push(3);
+            return 2;
+        case "endRest":
+            indentIntentStack.pop();
+            return 2;
+        default:
+            if (indentIntentStack.isEmpty()) {
+                return 2;
+            }
+            return indentIntentStack.peek();
+        }
+    }
+
+    String invocationLiteralsFor(final Object[] args) {
+        final StringJoiner literals = new StringJoiner(",");
+
+        for (final Object arg : args) {
+            if (isPrimitiveOrWrapper(arg.getClass())) {
+                literals.add("$L");
+            } else if (arg instanceof String) {
+                literals.add("$S");
+            } else if (arg instanceof Enum) {
+                literals.add("$T.$L");
+            } else if (arg instanceof String[]) {
+                literals.add("$S");
+            }
+        }
+
+        return literals.toString();
+    }
+
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/OperationFilter.java b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/OperationFilter.java
new file mode 100644
index 0000000..82371f6
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/OperationFilter.java
@@ -0,0 +1,45 @@
+/*
+ * 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.generator.openapi;
+
+import java.util.Arrays;
+
+import org.apache.camel.support.PatternHelper;
+
+class OperationFilter {
+
+    // operation names to include separated by comma (wildcards can be used, eg find*)
+    private String includes;
+
+    public String getIncludes() {
+        return includes;
+    }
+
+    public void setIncludes(String includes) {
+        this.includes = includes;
+    }
+
+    boolean accept(String name) {
+        boolean match = true;
+
+        if (includes != null) {
+            String[] patterns = includes.split(",");
+            match = Arrays.stream(patterns).anyMatch(pattern -> PatternHelper.matchPattern(name, pattern));
+        }
+        return match;
+    }
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/OperationVisitor.java b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/OperationVisitor.java
new file mode 100644
index 0000000..368f6f7
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/OperationVisitor.java
@@ -0,0 +1,239 @@
+/*
+ * 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.generator.openapi;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map.Entry;
+
+import io.apicurio.datamodels.openapi.models.OasOperation;
+import io.apicurio.datamodels.openapi.models.OasParameter;
+import io.apicurio.datamodels.openapi.models.OasResponse;
+import io.apicurio.datamodels.openapi.models.OasSchema;
+import io.apicurio.datamodels.openapi.v2.models.Oas20Items;
+import io.apicurio.datamodels.openapi.v2.models.Oas20Operation;
+import io.apicurio.datamodels.openapi.v2.models.Oas20Parameter;
+import io.apicurio.datamodels.openapi.v3.models.Oas30MediaType;
+import io.apicurio.datamodels.openapi.v3.models.Oas30Operation;
+import io.apicurio.datamodels.openapi.v3.models.Oas30Parameter;
+import io.apicurio.datamodels.openapi.v3.models.Oas30RequestBody;
+import io.apicurio.datamodels.openapi.v3.models.Oas30Response;
+import io.apicurio.datamodels.openapi.v3.models.Oas30Schema;
+import io.apicurio.datamodels.openapi.v3.models.Oas30Schema.Oas30ItemsSchema;
+import org.apache.camel.model.rest.CollectionFormat;
+import org.apache.camel.model.rest.RestParamType;
+import org.apache.camel.util.ObjectHelper;
+
+class OperationVisitor<T> {
+
+    private final DestinationGenerator destinationGenerator;
+
+    private final CodeEmitter<T> emitter;
+
+    private final OperationFilter filter;
+
+    private final String path;
+
+    OperationVisitor(final CodeEmitter<T> emitter, final OperationFilter filter, final String path, final DestinationGenerator destinationGenerator) {
+        this.emitter = emitter;
+        this.filter = filter;
+        this.path = path;
+        this.destinationGenerator = destinationGenerator;
+    }
+
+    List<String> asStringList(final List<?> values) {
+        if (values == null || values.isEmpty()) {
+            return Collections.emptyList();
+        }
+
+        final List<String> stringList = new ArrayList<>();
+        values.forEach(v -> stringList.add(String.valueOf(v)));
+
+        return stringList;
+    }
+
+    CodeEmitter<T> emit(final OasParameter parameter) {
+        emitter.emit("param");
+        emit("name", parameter.getName());
+        final String parameterType = parameter.in;
+        if (ObjectHelper.isNotEmpty(parameterType)) {
+            emit("type", RestParamType.valueOf(parameterType));
+        }
+        if (!parameterType.equals("body")) {
+            if (parameter instanceof Oas20Parameter) {
+                final Oas20Parameter serializableParameter = (Oas20Parameter)parameter;
+
+                final String dataType = serializableParameter.type;
+                emit("dataType", dataType);
+                emit("allowableValues", asStringList(serializableParameter.enum_));
+                final String collectionFormat = serializableParameter.collectionFormat;
+                if (ObjectHelper.isNotEmpty(collectionFormat)) {
+                    emit("collectionFormat", CollectionFormat.valueOf(collectionFormat));
+                }
+                if (ObjectHelper.isNotEmpty(serializableParameter.default_)) {
+                    String value = serializableParameter.default_.toString();
+                    emit("defaultValue", value);
+                }
+
+                final Oas20Items items = serializableParameter.items;
+                if ("array".equals(dataType) && items != null) {
+                    emit("arrayType", items.type);
+                }
+            } else if (parameter instanceof Oas30Parameter) {
+                final Oas30Parameter serializableParameter = (Oas30Parameter)parameter;
+                Oas30Schema schema = (Oas30Schema)serializableParameter.schema;
+                if (schema != null) {
+                    final String dataType = schema.type;
+                    if (ObjectHelper.isNotEmpty(dataType)) {
+                        emit("dataType", dataType);
+                    }
+                    emit("allowableValues", asStringList(schema.enum_));
+                    final String collectionFormat = serializableParameter.style;
+                    if (ObjectHelper.isNotEmpty(collectionFormat)) {
+                        if (collectionFormat.equals("form")) {
+                            if (serializableParameter.explode) {
+                                emit("collectionFormat", CollectionFormat.multi);
+                            } else {
+                                emit("collectionFormat", CollectionFormat.csv);
+                            }
+                        }
+                    }
+                    if (ObjectHelper.isNotEmpty(schema.default_)) {
+                        String value = schema.default_.toString();
+                        emit("defaultValue", value);
+                    }
+
+                    if ("array".equals(dataType) && schema.items != null
+                        && schema.items instanceof Oas30ItemsSchema) {
+                        emit("arrayType", ((Oas30ItemsSchema)schema.items).type);
+                    }
+                }
+            }
+        }
+        if (parameter.required != null) {
+            emit("required", parameter.required);
+        } else {
+            emit("required", Boolean.FALSE);
+        }
+        emit("description", parameter.description);
+        emitter.emit("endParam");
+
+        return emitter;
+    }
+
+    CodeEmitter<T> emit(final String method, final List<String> values) {
+        if (values == null || values.isEmpty()) {
+            return emitter;
+        }
+
+        return emitter.emit(method, new Object[] {values.toArray(new String[values.size()])});
+    }
+
+    CodeEmitter<T> emit(final String method, final Object value) {
+        if (ObjectHelper.isEmpty(value)) {
+            return emitter;
+        }
+
+        return emitter.emit(method, value);
+    }
+
+    void visit(final PathVisitor.HttpMethod method, final OasOperation operation) {
+        if (filter.accept(operation.operationId)) {
+            final String methodName = method.name().toLowerCase();
+            emitter.emit(methodName, path);
+
+            emit("id", operation.operationId);
+            emit("description", operation.description);
+            List<String> operationLevelConsumes = new ArrayList<String>();
+            if (operation instanceof Oas20Operation) {
+                operationLevelConsumes = ((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()) {
+                        operationLevelConsumes.add(ct);
+                    }
+                }
+                    
+            }
+            emit("consumes", operationLevelConsumes);
+            List<String> operationLevelProduces = new ArrayList<String>();
+            if (operation instanceof Oas20Operation) {
+                operationLevelProduces = ((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()) {
+                            operationLevelProduces.add(ct);
+                        }
+                    }
+                }
+            }
+            emit("produces", operationLevelProduces);
+            
+            if (operation.getParameters() != null) {
+                operation.getParameters().forEach(parameter -> {
+                    emit(parameter);
+                });
+            }
+            if (operation instanceof Oas30Operation) {
+                emitOas30Operation((Oas30Operation)operation);
+            }
+
+            emitter.emit("to", destinationGenerator.generateDestinationFor(operation));
+        }
+    }
+
+    private CodeEmitter<T> emitOas30Operation(Oas30Operation operation) {
+        
+        if (operation.requestBody != null) {
+            boolean foundForm = false;
+            Oas30RequestBody requestBody = operation.requestBody;
+            for (Entry<String, Oas30MediaType> entry : requestBody.content.entrySet()) {
+                String ct = entry.getKey();
+                Oas30MediaType mediaType = entry.getValue();
+                if (ct.contains("form") && mediaType.schema.properties != null) {
+                    for (Entry<String, OasSchema> entrySchema :  mediaType.schema.properties.entrySet()) {
+                        foundForm = true;
+                        emitter.emit("param");
+                        emit("name", entrySchema.getKey());
+                        emit("type", RestParamType.formData);
+                        emit("dataType", entrySchema.getValue().type);
+                        emit("required", requestBody.required);
+                        emit("description", entrySchema.getValue().description);
+                        emitter.emit("endParam");
+                    }
+                }
+            }
+            if (!foundForm) {
+                emitter.emit("param");
+                emit("name", "body");
+                emit("type", RestParamType.valueOf("body"));
+                emit("required", Boolean.TRUE);
+                emit("description", requestBody.description);
+                emitter.emit("endParam");
+            }
+        }
+        
+        return emitter;
+        
+    }
+}
\ No newline at end of file
diff --git a/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/PathGenerator.java b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/PathGenerator.java
new file mode 100644
index 0000000..1844295
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/PathGenerator.java
@@ -0,0 +1,39 @@
+/*
+ * 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.generator.openapi;
+
+import java.io.IOException;
+import java.nio.file.Path;
+
+import com.squareup.javapoet.JavaFile;
+import io.apicurio.datamodels.openapi.models.OasDocument;
+
+
+final class PathGenerator extends RestDslSourceCodeGenerator<Path> {
+
+    PathGenerator(final OasDocument openapi) {
+        super(openapi);
+    }
+
+    @Override
+    public void generate(final Path destination) throws IOException {
+        final JavaFile javaFile = generateSourceCode();
+
+        javaFile.writeTo(destination);
+    }
+
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/PathVisitor.java b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/PathVisitor.java
new file mode 100644
index 0000000..53d01c8
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/PathVisitor.java
@@ -0,0 +1,98 @@
+/*
+ * 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.generator.openapi;
+
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import io.apicurio.datamodels.openapi.models.OasOperation;
+import io.apicurio.datamodels.openapi.models.OasPathItem;
+import org.apache.camel.util.ObjectHelper;
+
+
+
+class PathVisitor<T> {
+
+    private final DestinationGenerator destinationGenerator;
+
+    private final CodeEmitter<T> emitter;
+    private final OperationFilter filter;
+
+    PathVisitor(final String basePath, final CodeEmitter<T> emitter, OperationFilter filter, final DestinationGenerator destinationGenerator) {
+        this.emitter = emitter;
+        this.filter = filter;
+        this.destinationGenerator = destinationGenerator;
+
+        if (ObjectHelper.isEmpty(basePath)) {
+            emitter.emit("rest");
+        } else {
+            emitter.emit("rest", basePath);
+        }
+    }
+
+    void visit(final String path, final OasPathItem definition) {
+        final OperationVisitor<T> restDslOperation = new OperationVisitor<>(emitter, filter, path, destinationGenerator);
+
+        getOperationMap(definition).forEach(restDslOperation::visit);
+    }
+    
+    void visit(final OasPathItem definition) {
+        final OperationVisitor<T> restDslOperation = new OperationVisitor<>(emitter, filter, definition.getPath(), destinationGenerator);
+
+        getOperationMap(definition).forEach(restDslOperation::visit);
+    }
+    
+    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 enum HttpMethod {
+        POST,
+        GET,
+        PUT,
+        PATCH,
+        DELETE,
+        HEAD,
+        OPTIONS
+    }
+
+}
\ No newline at end of file
diff --git a/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/RestDefinitionEmitter.java b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/RestDefinitionEmitter.java
new file mode 100644
index 0000000..955647b
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/RestDefinitionEmitter.java
@@ -0,0 +1,99 @@
+/*
+ * 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.generator.openapi;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.model.rest.RestsDefinition;
+
+class RestDefinitionEmitter implements CodeEmitter<RestsDefinition> {
+
+    private final RestsDefinition definition;
+
+    private Object variable;
+
+    RestDefinitionEmitter(final CamelContext context) {
+        definition = new RestsDefinition();
+        variable = definition;
+    }
+
+    @Override
+    public CodeEmitter<RestsDefinition> emit(final String method, final Object... args) {
+        try {
+            final Class<? extends Object> type = variable.getClass();
+
+            final Object[] arguments = argumentsFor(args);
+
+            final Method declaredMethod = type.getMethod(method, parameterTypesOf(arguments));
+
+            variable = declaredMethod.invoke(variable, arguments);
+        } catch (final Throwable e) {
+            if (e instanceof RuntimeException) {
+                throw (RuntimeException) e;
+            } else {
+                throw new IllegalStateException(e);
+            }
+        }
+
+        return this;
+    }
+
+    @Override
+    public RestsDefinition result() {
+        return definition;
+    }
+
+    Object[] argumentsFor(final Object[] args) {
+        final List<Object> arguments = new ArrayList<>(args.length);
+
+        for (final Object arg : args) {
+            if (arg instanceof String[]) {
+                arguments.add(Arrays.stream((String[]) arg).collect(Collectors.joining(",")));
+            } else {
+                arguments.add(arg);
+            }
+        }
+
+        return arguments.toArray(new Object[arguments.size()]);
+    }
+
+    Class<?>[] parameterTypesOf(final Object[] args) {
+        final Class<?>[] parameterTypes = new Class<?>[args.length];
+
+        for (int i = 0; i < args.length; i++) {
+            parameterTypes[i] = args[i].getClass();
+        }
+
+        return parameterTypes;
+    }
+
+    Class<?>[] typesOf(final Object[] args) {
+        final Class<?>[] types = new Class<?>[args.length];
+
+        for (int i = 0; i < types.length; i++) {
+            types[i] = args[i].getClass();
+        }
+
+        return types;
+    }
+
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/RestDslDefinitionGenerator.java b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/RestDslDefinitionGenerator.java
new file mode 100644
index 0000000..01210c8
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/RestDslDefinitionGenerator.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.generator.openapi;
+
+import io.apicurio.datamodels.openapi.models.OasDocument;
+import org.apache.camel.CamelContext;
+import org.apache.camel.model.rest.RestsDefinition;
+
+
+public final class RestDslDefinitionGenerator extends RestDslGenerator<RestDslDefinitionGenerator> {
+
+    RestDslDefinitionGenerator(final OasDocument openapi) {
+        super(openapi);
+    }
+
+    public RestsDefinition generate(final CamelContext context) {
+        final RestDefinitionEmitter emitter = new RestDefinitionEmitter(context);
+        String basePath = RestDslGenerator.getBasePathFromOasDocument(openapi);
+        final PathVisitor<RestsDefinition> restDslStatement = new PathVisitor<>(basePath, emitter, filter, destinationGenerator());
+
+        openapi.paths.getPathItems().forEach(restDslStatement::visit);
+
+        return emitter.result();
+    }
+
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/RestDslGenerator.java b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/RestDslGenerator.java
new file mode 100644
index 0000000..65b6034
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/RestDslGenerator.java
@@ -0,0 +1,203 @@
+/*
+ * 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.generator.openapi;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.nio.file.Path;
+
+import javax.annotation.processing.Filer;
+
+import io.apicurio.datamodels.openapi.models.OasDocument;
+import io.apicurio.datamodels.openapi.v2.models.Oas20Document;
+import io.apicurio.datamodels.openapi.v3.models.Oas30Document;
+import org.apache.camel.model.rest.RestsDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+
+import static org.apache.camel.util.ObjectHelper.notNull;
+
+/**
+ * Source code and {@link RestsDefinition} generator that generates Camel REST
+ * DSL implementations from OpenAPI specifications.
+ */
+public abstract class RestDslGenerator<G> {
+
+    private static final Logger LOG = LoggerFactory.getLogger(RestDslGenerator.class);
+    final OasDocument openapi;
+
+    
+    DestinationGenerator destinationGenerator = new DirectToOperationId();
+    OperationFilter filter = new OperationFilter();
+    String restComponent;
+    String restContextPath;
+    String apiContextPath;
+    boolean springComponent;
+    boolean springBootProject;
+
+    RestDslGenerator(final OasDocument openapi) {
+        this.openapi = notNull(openapi, "openapi");
+    }
+
+    public G withDestinationGenerator(final DestinationGenerator directRouteGenerator) {
+        notNull(directRouteGenerator, "directRouteGenerator");
+        this.destinationGenerator = directRouteGenerator;
+
+        @SuppressWarnings("unchecked")
+        final G that = (G) this;
+
+        return that;
+    }
+
+    DestinationGenerator destinationGenerator() {
+        return destinationGenerator;
+    }
+
+    public G withOperationFilter(OperationFilter filter) {
+        this.filter = filter;
+
+        @SuppressWarnings("unchecked")
+        final G that = (G) this;
+
+        return that;
+    }
+
+    public G withOperationFilter(String include) {
+        this.filter.setIncludes(include);
+
+        @SuppressWarnings("unchecked")
+        final G that = (G) this;
+
+        return that;
+    }
+
+    public G withRestComponent(String restComponent) {
+        this.restComponent = restComponent;
+
+        @SuppressWarnings("unchecked")
+        final G that = (G) this;
+
+        return that;
+    }
+
+    public G withRestContextPath(String contextPath) {
+        this.restContextPath = contextPath;
+
+        @SuppressWarnings("unchecked")
+        final G that = (G) this;
+
+        return that;
+    }
+    
+    public G withApiContextPath(String contextPath) {
+        this.apiContextPath = contextPath;
+
+        @SuppressWarnings("unchecked")
+        final G that = (G) this;
+
+        return that;
+    }
+
+    public G asSpringComponent() {
+        this.springComponent = true;
+
+        @SuppressWarnings("unchecked")
+        final G that = (G) this;
+
+        return that;
+    }
+
+    public G asSpringBootProject() {
+        this.springBootProject = true;
+
+        @SuppressWarnings("unchecked")
+        final G that = (G) this;
+
+        return that;
+    }
+
+    public static RestDslSourceCodeGenerator<Appendable> toAppendable(final OasDocument openapi) {
+        return new AppendableGenerator(openapi);
+    }
+
+    public static RestDslDefinitionGenerator toDefinition(final OasDocument openapi) {
+        return new RestDslDefinitionGenerator(openapi);
+    }
+
+    public static RestDslXmlGenerator toXml(final OasDocument openapi) {
+        return new RestDslXmlGenerator(openapi);
+    }
+
+    public static RestDslSourceCodeGenerator<Filer> toFiler(final OasDocument openapi) {
+        return new FilerGenerator(openapi);
+    }
+
+    public static RestDslSourceCodeGenerator<Path> toPath(final OasDocument openapi) {
+        return new PathGenerator(openapi);
+    }
+    
+    public static String getHostFromOasDocument(final OasDocument openapi) {
+        String host = null;
+        if (openapi instanceof Oas20Document) {
+            host = ((Oas20Document)openapi).host;
+        } else if (openapi instanceof Oas30Document) {
+            if (((Oas30Document)openapi).getServers() != null 
+                && ((Oas30Document)openapi).getServers().get(0) != null) {
+                try {
+                    URL serverUrl = new URL(((Oas30Document)openapi).getServers().get(0).url);
+                    host = serverUrl.getHost();
+                
+                } catch (MalformedURLException e) {
+                    LOG.info("error when parsing OpenApi 3.0 doc server url", e);
+                }
+            }
+        }
+        return host;
+        
+    }
+    
+    public static String getBasePathFromOasDocument(final OasDocument openapi) {
+        String basePath = null;
+        if (openapi instanceof Oas20Document) {
+            basePath = ((Oas20Document)openapi).basePath;
+        } else if (openapi instanceof Oas30Document) {
+            if (((Oas30Document)openapi).getServers() != null 
+                && ((Oas30Document)openapi).getServers().get(0) != null) {
+                try {
+                    URL serverUrl = new URL(((Oas30Document)openapi).getServers().get(0).url);
+                    basePath = serverUrl.getPath();
+                    if (basePath.indexOf("//") == 0) {
+                        //strip off the first "/" if double "/" exists
+                        basePath = basePath.substring(1);
+                    }
+                    if ("/".equals(basePath)) {
+                        basePath = "";
+                    }
+                                    
+                } catch (MalformedURLException e) {
+                    //not a valid whole url, just the basePath
+                    basePath = ((Oas30Document)openapi).getServers().get(0).url;
+                }
+            }
+            
+        }
+        return basePath;
+        
+    }
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/RestDslSourceCodeGenerator.java b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/RestDslSourceCodeGenerator.java
new file mode 100644
index 0000000..70d9aa7
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/RestDslSourceCodeGenerator.java
@@ -0,0 +1,207 @@
+/*
+ * 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.generator.openapi;
+
+import java.io.IOException;
+import java.time.Instant;
+import java.util.function.Function;
+import java.util.stream.Collector;
+
+import javax.annotation.Generated;
+import javax.lang.model.element.Modifier;
+
+import com.squareup.javapoet.AnnotationSpec;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.JavaFile;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.TypeSpec;
+import io.apicurio.datamodels.openapi.models.OasDocument;
+import io.apicurio.datamodels.openapi.models.OasInfo;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.util.ObjectHelper;
+
+import static org.apache.camel.util.StringHelper.notEmpty;
+
+/**
+ * Generates Java source code
+ */
+public abstract class RestDslSourceCodeGenerator<T> extends RestDslGenerator<RestDslSourceCodeGenerator<T>> {
+    static final String DEFAULT_CLASS_NAME = "RestDslRoute";
+
+    static final String DEFAULT_PACKAGE_NAME = "rest.dsl.generated";
+    
+    private static final String DEFAULT_INDENT = "    ";
+
+    private Function<OasDocument, String> classNameGenerator = RestDslSourceCodeGenerator::generateClassName;
+
+    private Instant generated = Instant.now();
+
+    private String indent = DEFAULT_INDENT;
+
+    private Function<OasDocument, String> packageNameGenerator = RestDslSourceCodeGenerator::generatePackageName;
+
+    private boolean sourceCodeTimestamps;
+
+    RestDslSourceCodeGenerator(final OasDocument openapi) {
+        super(openapi);
+    }
+
+    public abstract void generate(T destination) throws IOException;
+
+    public RestDslSourceCodeGenerator<T> withClassName(final String className) {
+        notEmpty(className, "className");
+        this.classNameGenerator = s -> className;
+
+        return this;
+    }
+
+    public RestDslSourceCodeGenerator<T> withIndent(final String indent) {
+        this.indent = ObjectHelper.notNull(indent, "indent");
+
+        return this;
+    }
+
+    public RestDslSourceCodeGenerator<T> withoutSourceCodeTimestamps() {
+        sourceCodeTimestamps = false;
+
+        return this;
+    }
+
+    public RestDslSourceCodeGenerator<T> withPackageName(final String packageName) {
+        notEmpty(packageName, "packageName");
+        this.packageNameGenerator = s -> packageName;
+
+        return this;
+    }
+
+    public RestDslSourceCodeGenerator<T> withSourceCodeTimestamps() {
+        sourceCodeTimestamps = true;
+
+        return this;
+    }
+
+    MethodSpec generateConfigureMethod(final OasDocument openapi) {
+        final MethodSpec.Builder configure = MethodSpec.methodBuilder("configure").addModifiers(Modifier.PUBLIC)
+            .returns(void.class).addJavadoc("Defines Apache Camel routes using REST DSL fluent API.\n");
+
+        final MethodBodySourceCodeEmitter emitter = new MethodBodySourceCodeEmitter(configure);
+
+        if (restComponent != null) {
+            configure.addCode("\n");
+            configure.addCode("restConfiguration().component(\"" + restComponent + "\")");
+            if (restContextPath != null) {
+                configure.addCode(".contextPath(\"" + restContextPath + "\")");
+            }
+            if (ObjectHelper.isNotEmpty(apiContextPath)) {
+                configure.addCode(".apiContextPath(\"" + apiContextPath + "\")");
+            }
+            configure.addCode(";\n\n");
+        }
+
+        String basePath = RestDslGenerator.getBasePathFromOasDocument(openapi); 
+            
+        final PathVisitor<MethodSpec> restDslStatement = new PathVisitor<>(basePath, emitter, filter, destinationGenerator());
+        openapi.paths.getItems().forEach(restDslStatement::visit);
+        return emitter.result();
+    }
+
+    Instant generated() {
+        return generated;
+    }
+
+    JavaFile generateSourceCode() {
+        final MethodSpec methodSpec = generateConfigureMethod(openapi);
+
+        final String classNameToUse = classNameGenerator.apply(openapi);
+
+        final AnnotationSpec.Builder generatedAnnotation = AnnotationSpec.builder(Generated.class).addMember("value",
+            "$S", getClass().getName());
+        if (sourceCodeTimestamps) {
+            generatedAnnotation.addMember("date", "$S", generated());
+        }
+
+        TypeSpec.Builder builder = TypeSpec.classBuilder(classNameToUse).superclass(RouteBuilder.class)
+            .addModifiers(Modifier.PUBLIC, Modifier.FINAL).addMethod(methodSpec)
+            .addAnnotation(generatedAnnotation.build())
+            .addJavadoc("Generated from OpenApi specification by Camel REST DSL generator.\n");
+        if (springComponent) {
+            final AnnotationSpec.Builder springAnnotation = AnnotationSpec.builder(ClassName.bestGuess("org.springframework.stereotype.Component"));
+            builder.addAnnotation(springAnnotation.build());
+        }
+        TypeSpec generatedRouteBuilder = builder.build();
+
+        final String packageNameToUse = packageNameGenerator.apply(openapi);
+
+        return JavaFile.builder(packageNameToUse, generatedRouteBuilder).indent(indent).build();
+    }
+
+    RestDslSourceCodeGenerator<T> withGeneratedTime(final Instant generated) {
+        this.generated = generated;
+
+        return this;
+    }
+
+    static String generateClassName(final OasDocument openapi) {
+        final OasInfo info = (OasInfo)openapi.info;
+        if (info == null) {
+            return DEFAULT_CLASS_NAME;
+        }
+
+        final String title = info.title;
+        if (title == null) {
+            return DEFAULT_CLASS_NAME;
+        }
+
+        final String className = title.chars().filter(Character::isJavaIdentifierPart).filter(c -> c < 'z').boxed()
+            .collect(Collector.of(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append,
+                StringBuilder::toString));
+
+        if (className.isEmpty() || !Character.isJavaIdentifierStart(className.charAt(0))) {
+            return DEFAULT_CLASS_NAME;
+        }
+
+        return className;
+    }
+
+    static String generatePackageName(final OasDocument openapi) {
+        String host = RestDslGenerator.getHostFromOasDocument(openapi);
+        
+
+        if (ObjectHelper.isNotEmpty(host)) {
+            final StringBuilder packageName = new StringBuilder();
+
+            final String hostWithoutPort = host.replaceFirst(":.*", "");
+
+            if ("localhost".equalsIgnoreCase(hostWithoutPort)) {
+                return DEFAULT_PACKAGE_NAME;
+            }
+
+            final String[] parts = hostWithoutPort.split("\\.");
+
+            for (int i = parts.length - 1; i >= 0; i--) {
+                packageName.append(parts[i]);
+                if (i != 0) {
+                    packageName.append('.');
+                }
+            }
+
+            return packageName.toString();
+        }
+
+        return DEFAULT_PACKAGE_NAME;
+    }
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/RestDslXmlGenerator.java b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/RestDslXmlGenerator.java
new file mode 100644
index 0000000..f139b61
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/RestDslXmlGenerator.java
@@ -0,0 +1,114 @@
+/*
+ * 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.generator.openapi;
+
+import java.io.StringReader;
+import java.io.StringWriter;
+
+import javax.xml.XMLConstants;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+import org.xml.sax.InputSource;
+
+import io.apicurio.datamodels.openapi.models.OasDocument;
+import org.apache.camel.CamelContext;
+import org.apache.camel.model.ModelHelper;
+import org.apache.camel.model.rest.RestsDefinition;
+import org.apache.camel.util.ObjectHelper;
+
+
+
+public class RestDslXmlGenerator extends RestDslGenerator<RestDslXmlGenerator> {
+
+    private boolean blueprint;
+
+    RestDslXmlGenerator(final OasDocument openapi) {
+        super(openapi);
+    }
+
+    public String generate(final CamelContext context) throws Exception {
+        final RestDefinitionEmitter emitter = new RestDefinitionEmitter(context);
+        String basePath = RestDslGenerator.getBasePathFromOasDocument(openapi);
+        final PathVisitor<RestsDefinition> restDslStatement = new PathVisitor<>(basePath, emitter, filter,
+            destinationGenerator());
+
+        openapi.paths.getPathItems().forEach(restDslStatement::visit);
+
+        final RestsDefinition rests = emitter.result();
+        final String xml = ModelHelper.dumpModelAsXml(context, rests);
+
+        final DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
+        builderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
+        builderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
+        builderFactory.setNamespaceAware(true);
+
+        final DocumentBuilder builder = builderFactory.newDocumentBuilder();
+
+        final Document document = builder.parse(new InputSource(new StringReader(xml)));
+
+        final Element root = document.getDocumentElement();
+
+        if (blueprint) {
+            document.renameNode(root, "http://camel.apache.org/schema/blueprint", root.getTagName());
+        }
+
+        // remove all customId attributes as we do not want them in the output
+        final NodeList elements = document.getElementsByTagName("*");
+        for (int i = 0; i < elements.getLength(); i++) {
+            final Element element = (Element) elements.item(i);
+            element.removeAttribute("customId");
+        }
+
+        if (restComponent != null) {
+            final Element configuration = document.createElement("restConfiguration");
+            configuration.setAttribute("component", restComponent);
+
+            if (restContextPath != null) {
+                configuration.setAttribute("contextPath", restContextPath);
+            }
+
+            if (ObjectHelper.isNotEmpty(apiContextPath)) {
+                configuration.setAttribute("apiContextPath", apiContextPath);
+            }
+
+            root.insertBefore(configuration, root.getFirstChild());
+        }
+
+        final TransformerFactory transformerFactory = TransformerFactory.newInstance();
+        transformerFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, Boolean.TRUE);
+        final Transformer transformer = transformerFactory.newTransformer();
+
+        final StringWriter writer = new StringWriter();
+        transformer.transform(new DOMSource(document), new StreamResult(writer));
+
+        return writer.toString();
+    }
+
+    public RestDslXmlGenerator withBlueprint() {
+        blueprint = true;
+        return this;
+    }
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/SpringBootProjectSourceCodeGenerator.java b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/SpringBootProjectSourceCodeGenerator.java
new file mode 100644
index 0000000..dfd2604
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/SpringBootProjectSourceCodeGenerator.java
@@ -0,0 +1,107 @@
+/*
+ * 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.generator.openapi;
+
+import java.io.IOException;
+import java.nio.file.Path;
+
+import javax.annotation.Generated;
+import javax.lang.model.element.Modifier;
+
+import com.squareup.javapoet.AnnotationSpec;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.JavaFile;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.TypeSpec;
+import org.apache.camel.util.ObjectHelper;
+
+import static org.apache.camel.util.StringHelper.notEmpty;
+
+public class SpringBootProjectSourceCodeGenerator {
+
+    private static final String DEFAULT_INDENT = "    ";
+
+    private String indent = DEFAULT_INDENT;
+
+    private String packageName;
+
+    public void generate(Path destination) throws IOException {
+        final JavaFile javaFile = generateSourceCode();
+
+        javaFile.writeTo(destination);
+    }
+
+    public SpringBootProjectSourceCodeGenerator withIndent(final String indent) {
+        this.indent = ObjectHelper.notNull(indent, "indent");
+        return this;
+    }
+
+    public SpringBootProjectSourceCodeGenerator withPackageName(final String packageName) {
+        notEmpty(packageName, "packageName");
+        this.packageName = packageName;
+        return this;
+    }
+
+    JavaFile generateSourceCode() {
+        notEmpty(packageName, "packageName");
+
+        final MethodSpec methodSpec = generateRestMethod();
+
+        final String classNameToUse = "CamelRestController";
+
+        final AnnotationSpec.Builder generatedAnnotation = AnnotationSpec.builder(Generated.class).addMember("value",
+            "$S", getClass().getName());
+        final AnnotationSpec.Builder restAnnotation = AnnotationSpec.builder(ClassName.bestGuess("org.springframework.web.bind.annotation.RestController"));
+
+        TypeSpec.Builder builder = TypeSpec.classBuilder(classNameToUse)
+            .addModifiers(Modifier.PUBLIC, Modifier.FINAL).addMethod(methodSpec)
+            .addAnnotation(generatedAnnotation.build())
+            .addAnnotation(restAnnotation.build())
+            .addJavadoc("Forward requests to the Camel servlet so it can service REST requests.\n");
+        TypeSpec generatedRestController = builder.build();
+
+        return JavaFile.builder(packageName, generatedRestController).indent(indent).build();
+    }
+
+    MethodSpec generateRestMethod() {
+        ClassName req = ClassName.bestGuess("javax.servlet.http.HttpServletRequest");
+        ClassName res = ClassName.bestGuess("javax.servlet.http.HttpServletResponse");
+
+        final AnnotationSpec.Builder reqAnnotation = AnnotationSpec.builder(ClassName.bestGuess("org.springframework.web.bind.annotation.RequestMapping"))
+            .addMember("value", "\"/**\"");
+
+        final MethodSpec.Builder forward = MethodSpec.methodBuilder("camelServlet").addModifiers(Modifier.PUBLIC)
+            .addParameter(req, "request")
+            .addParameter(res, "response")
+            .addAnnotation(reqAnnotation.build())
+            .returns(void.class);
+
+        forward.addCode("try {\n");
+        forward.addCode("    String path = request.getRequestURI();\n");
+        forward.addCode("    request.getServletContext().getRequestDispatcher(\"/camel/\" + path).forward(request, response);\n");
+        forward.addCode("} catch (Exception e) {\n");
+        forward.addCode("    response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);\n");
+        forward.addCode("}\n");
+
+        return forward.build();
+    }
+
+    public static SpringBootProjectSourceCodeGenerator generator() {
+        return new SpringBootProjectSourceCodeGenerator();
+    }
+
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/MethodBodySourceCodeEmitterTest.java b/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/MethodBodySourceCodeEmitterTest.java
new file mode 100644
index 0000000..6d3cdaf
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/MethodBodySourceCodeEmitterTest.java
@@ -0,0 +1,56 @@
+/*
+ * 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.generator.openapi;
+
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.MethodSpec.Builder;
+import org.apache.camel.model.rest.RestParamType;
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class MethodBodySourceCodeEmitterTest {
+
+    @Test
+    public void shouldGenerateSourceCode() {
+        final Builder method = MethodSpec.methodBuilder("configure");
+
+        final MethodBodySourceCodeEmitter emitter = new MethodBodySourceCodeEmitter(method);
+
+        emitter.emit("rest");
+        emitter.emit("put", "/pet");
+        emitter.emit("consumes", new Object[] {new String[] {"application/json", "application/xml"}});
+        emitter.emit("produces", new Object[] {new String[] {"application/xml", "application/json"}});
+        emitter.emit("param");
+        emitter.emit("name", "body");
+        emitter.emit("type", RestParamType.body);
+        emitter.emit("required", true);
+        emitter.emit("endParam");
+
+        assertThat(emitter.result().toString()).isEqualTo("void configure() {\n"//
+            + "  rest()\n"//
+            + "    .put(\"/pet\")\n"//
+            + "      .consumes(\"application/json,application/xml\")\n"//
+            + "      .produces(\"application/xml,application/json\")\n"//
+            + "      .param()\n"//
+            + "        .name(\"body\")\n"//
+            + "        .type(org.apache.camel.model.rest.RestParamType.body)\n"//
+            + "        .required(true)\n"//
+            + "      .endParam();\n"//
+            + "}\n");
+    }
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/PathSpringBootProjectSourceGeneratorTest.java b/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/PathSpringBootProjectSourceGeneratorTest.java
new file mode 100644
index 0000000..e7d6a46
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/PathSpringBootProjectSourceGeneratorTest.java
@@ -0,0 +1,46 @@
+/*
+ * 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.generator.openapi;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class PathSpringBootProjectSourceGeneratorTest {
+
+    @Test
+    public void shouldGenerateSourceCodeWithDefaults() throws IOException, URISyntaxException {
+        Path path = new File("target/generated-sources").toPath();
+        SpringBootProjectSourceCodeGenerator.generator().withPackageName("com.foo").generate(path);
+        final String generatedContent = new String(Files.readAllBytes(Paths.get("target/generated-sources/com/foo/CamelRestController.java")), StandardCharsets.UTF_8);
+
+        final URI file = PathSpringBootProjectSourceGeneratorTest.class.getResource("/SpringBootRestController.txt").toURI();
+        final String expectedContent = new String(Files.readAllBytes(Paths.get(file)), StandardCharsets.UTF_8);
+
+        assertThat(generatedContent).isEqualTo(expectedContent);
+    }
+
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/RestDefinitionEmitterTest.java b/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/RestDefinitionEmitterTest.java
new file mode 100644
index 0000000..53d9f12
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/RestDefinitionEmitterTest.java
@@ -0,0 +1,71 @@
+/*
+ * 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.generator.openapi;
+
+import java.util.List;
+
+
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.model.rest.RestDefinition;
+import org.apache.camel.model.rest.RestOperationParamDefinition;
+import org.apache.camel.model.rest.RestParamType;
+import org.apache.camel.model.rest.RestsDefinition;
+import org.apache.camel.model.rest.VerbDefinition;
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class RestDefinitionEmitterTest {
+    @Test
+    public void shouldGenerateObjects() {
+        final DefaultCamelContext context = new DefaultCamelContext();
+
+        final RestDefinitionEmitter emitter = new RestDefinitionEmitter(context);
+
+        emitter.emit("rest");
+        emitter.emit("put", "/pet");
+        emitter.emit("consumes", new Object[] {new String[] {"application/json", "application/xml"}});
+        emitter.emit("produces", new Object[] {new String[] {"application/xml", "application/json"}});
+        emitter.emit("param");
+        emitter.emit("name", "body");
+        emitter.emit("type", RestParamType.body);
+        emitter.emit("required", true);
+        emitter.emit("endParam");
+
+        final RestsDefinition result = emitter.result();
+        final List<RestDefinition> rests = result.getRests();
+        assertThat(rests).hasSize(1);
+
+        final RestDefinition rest = rests.get(0);
+        final List<VerbDefinition> verbs = rest.getVerbs();
+        assertThat(verbs).hasSize(1);
+
+        final VerbDefinition definition = verbs.get(0);
+        assertThat(definition.asVerb()).isEqualTo("put");
+        assertThat(definition.getUri()).isEqualTo("/pet");
+        assertThat(definition.getConsumes()).isEqualTo("application/json,application/xml");
+        assertThat(definition.getProduces()).isEqualTo("application/xml,application/json");
+
+        final List<RestOperationParamDefinition> params = definition.getParams();
+        assertThat(params).hasSize(1);
+
+        final RestOperationParamDefinition param = params.get(0);
+        assertThat(param.getName()).isEqualTo("body");
+        assertThat(param.getType()).isEqualTo(RestParamType.body);
+        assertThat(param.getRequired()).isEqualTo(true);
+    }
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/RestDslGeneratorTest.java b/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/RestDslGeneratorTest.java
new file mode 100644
index 0000000..6e8913d
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/RestDslGeneratorTest.java
@@ -0,0 +1,122 @@
+/*
+ * 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.generator.openapi;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.time.Instant;
+
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.apicurio.datamodels.Library;
+import io.apicurio.datamodels.openapi.models.OasDocument;
+import org.apache.camel.CamelContext;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.model.rest.RestsDefinition;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class RestDslGeneratorTest {
+
+    static OasDocument openapi;
+    
+    final Instant generated = Instant.parse("2017-10-17T00:00:00.000Z");
+
+       
+    @BeforeClass
+    public static void readOpenApiDoc() throws Exception {
+        ObjectMapper mapper = new ObjectMapper();
+        FileInputStream fis = new FileInputStream(new File("openapi-v2.json"));
+        JsonNode node = mapper.readTree(fis);
+        openapi = (OasDocument)Library.readDocument(node);
+    }
+
+    @Test
+    public void shouldCreateDefinitions() {
+        final CamelContext context = new DefaultCamelContext();
+
+        final RestsDefinition definition = RestDslGenerator.toDefinition(openapi).generate(context);
+        assertThat(definition).isNotNull();
+        assertThat(definition.getRests()).hasSize(1);
+        assertThat(definition.getRests().get(0).getPath()).isEqualTo("/v2");
+               
+       
+    }
+
+    @Test
+    public void shouldGenerateSourceCodeWithDefaults() throws IOException, URISyntaxException {
+        final StringBuilder code = new StringBuilder();
+
+        RestDslGenerator.toAppendable(openapi).withGeneratedTime(generated).generate(code);
+
+        final URI file = RestDslGeneratorTest.class.getResource("/OpenApiPetstore.txt").toURI();
+        final String expectedContent = new String(Files.readAllBytes(Paths.get(file)), StandardCharsets.UTF_8);
+
+        assertThat(code.toString()).isEqualTo(expectedContent);
+    }
+
+    @Test
+    public void shouldGenerateSourceCodeWithRestComponent() throws IOException, URISyntaxException {
+        final StringBuilder code = new StringBuilder();
+
+        RestDslGenerator.toAppendable(openapi).withGeneratedTime(generated).withRestComponent("servlet").withRestContextPath("/").generate(code);
+
+        final URI file = RestDslGeneratorTest.class.getResource("/OpenApiPetstoreWithRestComponent.txt").toURI();
+        final String expectedContent = new String(Files.readAllBytes(Paths.get(file)), StandardCharsets.UTF_8);
+
+        assertThat(code.toString()).isEqualTo(expectedContent);
+    }
+
+    @Test
+    public void shouldGenerateSourceCodeWithOptions() throws IOException, URISyntaxException {
+        final StringBuilder code = new StringBuilder();
+
+        RestDslGenerator.toAppendable(openapi).withGeneratedTime(generated).withClassName("MyRestRoute")
+            .withPackageName("com.example").withIndent("\t").withSourceCodeTimestamps()
+            .withDestinationGenerator(o -> "direct:rest-" + o.operationId).generate(code);
+
+        final URI file = RestDslGeneratorTest.class.getResource("/MyRestRoute.txt").toURI();
+        final String expectedContent = new String(Files.readAllBytes(Paths.get(file)), StandardCharsets.UTF_8);
+
+        assertThat(code.toString()).isEqualTo(expectedContent);
+    }
+
+    @Test
+    public void shouldGenerateSourceCodeWithFilter() throws IOException, URISyntaxException {
+        final StringBuilder code = new StringBuilder();
+
+        RestDslGenerator.toAppendable(openapi).withGeneratedTime(generated).withClassName("MyRestRoute")
+            .withPackageName("com.example").withIndent("\t").withSourceCodeTimestamps()
+            .withOperationFilter("find*,deletePet,updatePet")
+            .withDestinationGenerator(o -> "direct:rest-" + o.operationId).generate(code);
+
+        final URI file = RestDslGeneratorTest.class.getResource("/MyRestRouteFilter.txt").toURI();
+        final String expectedContent = new String(Files.readAllBytes(Paths.get(file)), StandardCharsets.UTF_8);
+
+        assertThat(code.toString()).isEqualTo(expectedContent);
+    }
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/RestDslGeneratorV3Test.java b/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/RestDslGeneratorV3Test.java
new file mode 100644
index 0000000..f31460e
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/RestDslGeneratorV3Test.java
@@ -0,0 +1,118 @@
+/*
+ * 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.generator.openapi;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.time.Instant;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.apicurio.datamodels.Library;
+import io.apicurio.datamodels.openapi.models.OasDocument;
+import org.apache.camel.CamelContext;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.model.rest.RestsDefinition;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class RestDslGeneratorV3Test {
+
+    static OasDocument openapi;
+    
+    final Instant generated = Instant.parse("2017-10-17T00:00:00.000Z");
+
+       
+    @BeforeClass
+    public static void readOpenApiDoc() throws Exception {
+        ObjectMapper mapper = new ObjectMapper();
+        FileInputStream fis = new FileInputStream(new File("openapi-spec.json"));
+        JsonNode node = mapper.readTree(fis);
+        openapi = (OasDocument)Library.readDocument(node);
+    }
+
+    @Test
+    public void shouldCreateDefinitions() {
+        final CamelContext context = new DefaultCamelContext();
+        final RestsDefinition definition = RestDslGenerator.toDefinition(openapi).generate(context);
+        assertThat(definition).isNotNull();
+        assertThat(definition.getRests()).hasSize(1);
+        assertThat(definition.getRests().get(0).getPath()).isEqualTo("/api/v3");
+        
+    }
+
+    @Test
+    public void shouldGenerateSourceCodeWithDefaults() throws IOException, URISyntaxException {
+        final StringBuilder code = new StringBuilder();
+
+        RestDslGenerator.toAppendable(openapi).withGeneratedTime(generated).generate(code);
+
+        final URI file = RestDslGeneratorV3Test.class.getResource("/OpenApiV3Petstore.txt").toURI();
+        final String expectedContent = new String(Files.readAllBytes(Paths.get(file)), StandardCharsets.UTF_8);
+        assertThat(code.toString()).isEqualTo(expectedContent);
+        
+    }
+
+    @Test
+    public void shouldGenerateSourceCodeWithRestComponent() throws IOException, URISyntaxException {
+        final StringBuilder code = new StringBuilder();
+
+        RestDslGenerator.toAppendable(openapi).withGeneratedTime(generated).withRestComponent("servlet").withRestContextPath("/").generate(code);
+
+        final URI file = RestDslGeneratorV3Test.class.getResource("/OpenApiV3PetstoreWithRestComponent.txt").toURI();
+        final String expectedContent = new String(Files.readAllBytes(Paths.get(file)), StandardCharsets.UTF_8);
+        
+        assertThat(code.toString()).isEqualTo(expectedContent);
+    }
+
+    @Test
+    public void shouldGenerateSourceCodeWithOptions() throws IOException, URISyntaxException {
+        final StringBuilder code = new StringBuilder();
+
+        RestDslGenerator.toAppendable(openapi).withGeneratedTime(generated).withClassName("MyRestRoute")
+            .withPackageName("com.example").withIndent("\t").withSourceCodeTimestamps()
+            .withDestinationGenerator(o -> "direct:rest-" + o.operationId).generate(code);
+
+        final URI file = RestDslGeneratorV3Test.class.getResource("/MyRestRouteV3.txt").toURI();
+        final String expectedContent = new String(Files.readAllBytes(Paths.get(file)), StandardCharsets.UTF_8);
+        assertThat(code.toString()).isEqualTo(expectedContent);
+    }
+
+    @Test
+    public void shouldGenerateSourceCodeWithFilter() throws IOException, URISyntaxException {
+        final StringBuilder code = new StringBuilder();
+
+        RestDslGenerator.toAppendable(openapi).withGeneratedTime(generated).withClassName("MyRestRoute")
+            .withPackageName("com.example").withIndent("\t").withSourceCodeTimestamps()
+            .withOperationFilter("find*,deletePet,updatePet")
+            .withDestinationGenerator(o -> "direct:rest-" + o.operationId).generate(code);
+
+        final URI file = RestDslGeneratorV3Test.class.getResource("/MyRestRouteFilterV3.txt").toURI();
+        final String expectedContent = new String(Files.readAllBytes(Paths.get(file)), StandardCharsets.UTF_8);
+        assertThat(code.toString()).isEqualTo(expectedContent);
+    }
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/RestDslSourceCodeGeneratorTest.java b/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/RestDslSourceCodeGeneratorTest.java
new file mode 100644
index 0000000..77530cc
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/RestDslSourceCodeGeneratorTest.java
@@ -0,0 +1,109 @@
+/*
+ * 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.generator.openapi;
+
+import io.apicurio.datamodels.openapi.models.OasInfo;
+import io.apicurio.datamodels.openapi.v2.models.Oas20Document;
+import io.apicurio.datamodels.openapi.v2.models.Oas20Info;
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class RestDslSourceCodeGeneratorTest {
+
+    @Test
+    public void shouldCreatePackageNamesFromHostnames() {
+        final Oas20Document openapi = new Oas20Document();
+        openapi.host = "api.example.org";
+
+        assertThat(RestDslSourceCodeGenerator.generatePackageName(openapi)).isEqualTo("org.example.api");
+    }
+
+    @Test
+    public void shouldCreatePackageNamesFromHostnamesWithPorts() {
+        final Oas20Document openapi = new Oas20Document();
+        openapi.host = "api.example.org:8080";
+
+        assertThat(RestDslSourceCodeGenerator.generatePackageName(openapi)).isEqualTo("org.example.api");
+    }
+
+    @Test
+    public void shouldGenerateClassNameFromTitle() {
+        final Oas20Document openapi = new Oas20Document();
+        OasInfo info = new Oas20Info();
+        info.title = "Example API";
+        openapi.info = info;
+        assertThat(RestDslSourceCodeGenerator.generateClassName(openapi)).isEqualTo("ExampleAPI");
+    }
+
+    @Test
+    public void shouldGenerateClassNameFromTitleWithNonValidJavaIdentifiers() {
+        final Oas20Document openapi = new Oas20Document();
+        OasInfo info = new Oas20Info();
+        info.title = "Example-API 2.0";
+        openapi.info = info;
+        assertThat(RestDslSourceCodeGenerator.generateClassName(openapi)).isEqualTo("ExampleAPI20");
+    }
+
+    @Test
+    public void shouldUseDefaultClassNameIfInfoOrTitleIsNotPresent() {
+        final Oas20Document openapi = new Oas20Document();
+
+        assertThat(RestDslSourceCodeGenerator.generateClassName(openapi))
+            .isEqualTo(RestDslSourceCodeGenerator.DEFAULT_CLASS_NAME);
+        openapi.info = new Oas20Info();
+        assertThat(RestDslSourceCodeGenerator.generateClassName(openapi))
+            .isEqualTo(RestDslSourceCodeGenerator.DEFAULT_CLASS_NAME);
+    }
+
+    @Test
+    public void shouldUseDefaultClassNameIfTitleContainsOnlyNonValidJavaIdentifiers() {
+        final Oas20Document openapi = new Oas20Document();
+        OasInfo info = new Oas20Info();
+        info.title = "\\%/4";
+        openapi.info = info;
+       
+        assertThat(RestDslSourceCodeGenerator.generateClassName(openapi))
+            .isEqualTo(RestDslSourceCodeGenerator.DEFAULT_CLASS_NAME);
+    }
+
+    @Test
+    public void shouldUseDefaultPackageNameForLocalhost() {
+        final Oas20Document openapi = new Oas20Document();
+        openapi.host = "localhost";
+        
+        assertThat(RestDslSourceCodeGenerator.generatePackageName(openapi))
+            .isEqualTo(RestDslSourceCodeGenerator.DEFAULT_PACKAGE_NAME);
+    }
+
+    @Test
+    public void shouldUseDefaultPackageNameForLocalhostWithPort() {
+        final Oas20Document openapi = new Oas20Document();
+        openapi.host = "localhost:8080";
+        
+        assertThat(RestDslSourceCodeGenerator.generatePackageName(openapi))
+            .isEqualTo(RestDslSourceCodeGenerator.DEFAULT_PACKAGE_NAME);
+    }
+
+    @Test
+    public void shouldUseDefaultPackageNameIfNoHostIsSpecified() {
+        final Oas20Document openapi = new Oas20Document();
+
+        assertThat(RestDslSourceCodeGenerator.generatePackageName(openapi))
+            .isEqualTo(RestDslSourceCodeGenerator.DEFAULT_PACKAGE_NAME);
+    }
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/RestDslSourceCodeGeneratorV3Test.java b/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/RestDslSourceCodeGeneratorV3Test.java
new file mode 100644
index 0000000..faf8b81
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/RestDslSourceCodeGeneratorV3Test.java
@@ -0,0 +1,112 @@
+/*
+ * 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.generator.openapi;
+
+
+import io.apicurio.datamodels.openapi.models.OasInfo;
+import io.apicurio.datamodels.openapi.v3.models.Oas30Document;
+import io.apicurio.datamodels.openapi.v3.models.Oas30Info;
+import org.junit.Test;
+
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+
+public class RestDslSourceCodeGeneratorV3Test {
+
+    @Test
+    public void shouldCreatePackageNamesFromHostnames() {
+        final Oas30Document openapi = new Oas30Document();
+        openapi.addServer("http://api.example.org/", "test server url");
+            
+        assertThat(RestDslSourceCodeGenerator.generatePackageName(openapi)).isEqualTo("org.example.api");
+    }
+
+    @Test
+    public void shouldCreatePackageNamesFromHostnamesWithPorts() {
+        final Oas30Document openapi = new Oas30Document();
+        openapi.addServer("http://api.example.org:8080/", "test server url");
+        
+        assertThat(RestDslSourceCodeGenerator.generatePackageName(openapi)).isEqualTo("org.example.api");
+    }
+
+    @Test
+    public void shouldGenerateClassNameFromTitle() {
+        final Oas30Document openapi = new Oas30Document();
+        OasInfo info = new Oas30Info();
+        info.title = "Example API";
+        openapi.info = info;
+        assertThat(RestDslSourceCodeGenerator.generateClassName(openapi)).isEqualTo("ExampleAPI");
+    }
+
+    @Test
+    public void shouldGenerateClassNameFromTitleWithNonValidJavaIdentifiers() {
+        final Oas30Document openapi = new Oas30Document();
+        OasInfo info = new Oas30Info();
+        info.title = "Example-API 2.0";
+        openapi.info = info;
+        assertThat(RestDslSourceCodeGenerator.generateClassName(openapi)).isEqualTo("ExampleAPI20");
+    }
+
+    @Test
+    public void shouldUseDefaultClassNameIfInfoOrTitleIsNotPresent() {
+        final Oas30Document openapi = new Oas30Document();
+
+        assertThat(RestDslSourceCodeGenerator.generateClassName(openapi))
+            .isEqualTo(RestDslSourceCodeGenerator.DEFAULT_CLASS_NAME);
+        openapi.info = new Oas30Info();
+        assertThat(RestDslSourceCodeGenerator.generateClassName(openapi))
+            .isEqualTo(RestDslSourceCodeGenerator.DEFAULT_CLASS_NAME);
+    }
+
+    @Test
+    public void shouldUseDefaultClassNameIfTitleContainsOnlyNonValidJavaIdentifiers() {
+        final Oas30Document openapi = new Oas30Document();
+        OasInfo info = new Oas30Info();
+        info.title = "\\%/4";
+        openapi.info = info;
+       
+        assertThat(RestDslSourceCodeGenerator.generateClassName(openapi))
+            .isEqualTo(RestDslSourceCodeGenerator.DEFAULT_CLASS_NAME);
+    }
+
+    @Test
+    public void shouldUseDefaultPackageNameForLocalhost() {
+        final Oas30Document openapi = new Oas30Document();
+        openapi.addServer("http://localhost", "test server url");
+                
+        assertThat(RestDslSourceCodeGenerator.generatePackageName(openapi))
+            .isEqualTo(RestDslSourceCodeGenerator.DEFAULT_PACKAGE_NAME);
+    }
+
+    @Test
+    public void shouldUseDefaultPackageNameForLocalhostWithPort() {
+        final Oas30Document openapi = new Oas30Document();
+        openapi.addServer("http://localhost:8080", "test server url");
+        
+        assertThat(RestDslSourceCodeGenerator.generatePackageName(openapi))
+            .isEqualTo(RestDslSourceCodeGenerator.DEFAULT_PACKAGE_NAME);
+    }
+
+    @Test
+    public void shouldUseDefaultPackageNameIfNoHostIsSpecified() {
+        final Oas30Document openapi = new Oas30Document();
+
+        assertThat(RestDslSourceCodeGenerator.generatePackageName(openapi))
+            .isEqualTo(RestDslSourceCodeGenerator.DEFAULT_PACKAGE_NAME);
+    }
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/RestDslXmlGeneratorTest.java b/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/RestDslXmlGeneratorTest.java
new file mode 100644
index 0000000..ad4ab36
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/RestDslXmlGeneratorTest.java
@@ -0,0 +1,117 @@
+/*
+ * 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.generator.openapi;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.StringReader;
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.w3c.dom.Document;
+
+import org.xml.sax.InputSource;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.apicurio.datamodels.Library;
+import io.apicurio.datamodels.openapi.models.OasDocument;
+import org.apache.camel.CamelContext;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class RestDslXmlGeneratorTest {
+
+    static OasDocument openapi;
+    
+    @BeforeClass
+    public static void readOpenApiDoc() throws Exception {
+        ObjectMapper mapper = new ObjectMapper();
+        FileInputStream fis = new FileInputStream(new File("openapi-v2.json"));
+        JsonNode node = mapper.readTree(fis);
+        openapi = (OasDocument)Library.readDocument(node);
+    }
+
+    @Test
+    public void shouldGenerateBlueprintXml() throws Exception {
+        final CamelContext context = new DefaultCamelContext();
+
+        final String xml = RestDslGenerator.toXml(openapi).withBlueprint().generate(context);
+        assertThat(xml).isNotEmpty();
+        assertThat(xml.contains("http://camel.apache.org/schema/blueprint"));
+    }
+
+    @Test
+    public void shouldGenerateXml() throws Exception {
+        final CamelContext context = new DefaultCamelContext();
+
+        final String xml = RestDslGenerator.toXml(openapi).generate(context);
+        assertThat(xml).isNotEmpty();
+        assertThat(xml.contains("http://camel.apache.org/schema/spring"));
+    }
+
+    @Test
+    public void shouldGenerateXmlWithDefaultnamespace() throws Exception {
+        final CamelContext context = new DefaultCamelContext();
+
+        final String xml = RestDslGenerator.toXml(openapi).generate(context);
+
+        final DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
+        builderFactory.setNamespaceAware(true);
+
+        final DocumentBuilder builder = builderFactory.newDocumentBuilder();
+
+        final Document document = builder.parse(new InputSource(new StringReader(xml)));
+
+        assertThat(document.isDefaultNamespace("http://camel.apache.org/schema/spring")).isTrue();
+    }
+
+    @Test
+    public void shouldGenerateXmlWithDefaults() throws Exception {
+        final CamelContext context = new DefaultCamelContext();
+
+        final String xml = RestDslGenerator.toXml(openapi).generate(context);
+
+        final URI file = RestDslGeneratorTest.class.getResource("/OpenApiPetstoreXml.txt").toURI();
+        final String expectedContent = new String(Files.readAllBytes(Paths.get(file)), StandardCharsets.UTF_8);
+
+        assertThat(xml).isXmlEqualTo(expectedContent);
+    }
+
+    @Test
+    public void shouldGenerateXmlWithRestComponent() throws Exception {
+        final CamelContext context = new DefaultCamelContext();
+
+        final String xml = RestDslGenerator.toXml(openapi).withRestComponent("servlet").withRestContextPath("/foo")
+            .generate(context);
+
+        final URI file = RestDslGeneratorTest.class.getResource("/OpenApiPetstoreWithRestComponentXml.txt").toURI();
+        final String expectedContent = new String(Files.readAllBytes(Paths.get(file)), StandardCharsets.UTF_8);
+
+        assertThat(xml).isXmlEqualTo(expectedContent);
+    }
+
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/RestDslXmlGeneratorV3Test.java b/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/RestDslXmlGeneratorV3Test.java
new file mode 100644
index 0000000..616d370
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/RestDslXmlGeneratorV3Test.java
@@ -0,0 +1,116 @@
+/*
+ * 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.generator.openapi;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.StringReader;
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.w3c.dom.Document;
+
+import org.xml.sax.InputSource;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.apicurio.datamodels.Library;
+import io.apicurio.datamodels.openapi.models.OasDocument;
+import org.apache.camel.CamelContext;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class RestDslXmlGeneratorV3Test {
+
+    static OasDocument openapi;
+    
+    @BeforeClass
+    public static void readOpenApiDoc() throws Exception {
+        ObjectMapper mapper = new ObjectMapper();
+        FileInputStream fis = new FileInputStream(new File("openapi-spec.json"));
+        JsonNode node = mapper.readTree(fis);
+        openapi = (OasDocument)Library.readDocument(node);
+    }
+
+    @Test
+    public void shouldGenerateBlueprintXml() throws Exception {
+        final CamelContext context = new DefaultCamelContext();
+
+        final String xml = RestDslGenerator.toXml(openapi).withBlueprint().generate(context);
+        assertThat(xml).isNotEmpty();
+        assertThat(xml.contains("http://camel.apache.org/schema/blueprint"));
+    }
+
+    @Test
+    public void shouldGenerateXml() throws Exception {
+        final CamelContext context = new DefaultCamelContext();
+
+        final String xml = RestDslGenerator.toXml(openapi).generate(context);
+        assertThat(xml).isNotEmpty();
+        assertThat(xml.contains("http://camel.apache.org/schema/spring"));
+    }
+
+    @Test
+    public void shouldGenerateXmlWithDefaultnamespace() throws Exception {
+        final CamelContext context = new DefaultCamelContext();
+
+        final String xml = RestDslGenerator.toXml(openapi).generate(context);
+
+        final DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
+        builderFactory.setNamespaceAware(true);
+
+        final DocumentBuilder builder = builderFactory.newDocumentBuilder();
+
+        final Document document = builder.parse(new InputSource(new StringReader(xml)));
+
+        assertThat(document.isDefaultNamespace("http://camel.apache.org/schema/spring")).isTrue();
+    }
+
+    @Test
+    public void shouldGenerateXmlWithDefaults() throws Exception {
+        final CamelContext context = new DefaultCamelContext();
+
+        final String xml = RestDslGenerator.toXml(openapi).generate(context);
+
+        final URI file = RestDslGeneratorTest.class.getResource("/OpenApiV3PetstoreXml.txt").toURI();
+        final String expectedContent = new String(Files.readAllBytes(Paths.get(file)), StandardCharsets.UTF_8);
+        
+        assertThat(xml).isXmlEqualTo(expectedContent);
+    }
+
+    @Test
+    public void shouldGenerateXmlWithRestComponent() throws Exception {
+        final CamelContext context = new DefaultCamelContext();
+
+        final String xml = RestDslGenerator.toXml(openapi).withRestComponent("servlet").withRestContextPath("/foo")
+            .generate(context);
+
+        final URI file = RestDslGeneratorTest.class.getResource("/OpenApiV3PetstoreWithRestComponentXml.txt").toURI();
+        final String expectedContent = new String(Files.readAllBytes(Paths.get(file)), StandardCharsets.UTF_8);
+        assertThat(xml).isXmlEqualTo(expectedContent);
+    }
+
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/test/resources/MyRestRoute.txt b/tooling/openapi-rest-dsl-generator/src/test/resources/MyRestRoute.txt
new file mode 100644
index 0000000..d08f3b5
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/test/resources/MyRestRoute.txt
@@ -0,0 +1,294 @@
+package com.example;
+
+import javax.annotation.Generated;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.model.rest.CollectionFormat;
+import org.apache.camel.model.rest.RestParamType;
+
+/**
+ * Generated from OpenApi specification by Camel REST DSL generator.
+ */
+@Generated(
+		value = "org.apache.camel.generator.openapi.AppendableGenerator",
+		date = "2017-10-17T00:00:00Z"
+)
+public final class MyRestRoute extends RouteBuilder {
+	/**
+	 * Defines Apache Camel routes using REST DSL fluent API.
+	 */
+	public void configure() {
+		rest("/v2")
+			.put("/pet")
+				.id("updatePet")
+				.consumes("application/json,application/xml")
+				.produces("application/xml,application/json")
+				.param()
+					.name("body")
+					.type(RestParamType.body)
+					.required(true)
+					.description("Pet object that needs to be added to the store")
+				.endParam()
+				.to("direct:rest-updatePet")
+			.post("/pet")
+				.id("addPet")
+				.consumes("application/json,application/xml")
+				.produces("application/xml,application/json")
+				.param()
+					.name("body")
+					.type(RestParamType.body)
+					.required(true)
+					.description("Pet object that needs to be added to the store")
+				.endParam()
+				.param()
+					.name("verbose")
+					.type(RestParamType.query)
+					.dataType("boolean")
+					.defaultValue("false")
+					.required(false)
+					.description("Verbose data")
+				.endParam()
+				.to("direct:rest-addPet")
+			.get("/pet/findByStatus")
+				.id("findPetsByStatus")
+				.description("Multiple status values can be provided with comma separated strings")
+				.produces("application/xml,application/json")
+				.param()
+					.name("status")
+					.type(RestParamType.query)
+					.dataType("array")
+					.collectionFormat(CollectionFormat.multi)
+					.arrayType("string")
+					.required(true)
+					.description("Status values that need to be considered for filter")
+				.endParam()
+				.to("direct:rest-findPetsByStatus")
+			.get("/pet/findByTags")
+				.id("findPetsByTags")
+				.description("Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.")
+				.produces("application/xml,application/json")
+				.param()
+					.name("tags")
+					.type(RestParamType.query)
+					.dataType("array")
+					.collectionFormat(CollectionFormat.multi)
+					.arrayType("string")
+					.required(true)
+					.description("Tags to filter by")
+				.endParam()
+				.to("direct:rest-findPetsByTags")
+			.get("/pet/{petId}")
+				.id("getPetById")
+				.description("Returns a single pet")
+				.produces("application/xml,application/json")
+				.param()
+					.name("petId")
+					.type(RestParamType.path)
+					.dataType("integer")
+					.required(true)
+					.description("ID of pet to return")
+				.endParam()
+				.to("direct:rest-getPetById")
+			.post("/pet/{petId}")
+				.id("updatePetWithForm")
+				.consumes("application/x-www-form-urlencoded")
+				.produces("application/xml,application/json")
+				.param()
+					.name("petId")
+					.type(RestParamType.path)
+					.dataType("integer")
+					.required(true)
+					.description("ID of pet that needs to be updated")
+				.endParam()
+				.param()
+					.name("name")
+					.type(RestParamType.formData)
+					.dataType("string")
+					.required(false)
+					.description("Updated name of the pet")
+				.endParam()
+				.param()
+					.name("status")
+					.type(RestParamType.formData)
+					.dataType("string")
+					.required(false)
+					.description("Updated status of the pet")
+				.endParam()
+				.to("direct:rest-updatePetWithForm")
+			.delete("/pet/{petId}")
+				.id("deletePet")
+				.produces("application/xml,application/json")
+				.param()
+					.name("api_key")
+					.type(RestParamType.header)
+					.dataType("string")
+					.required(false)
+				.endParam()
+				.param()
+					.name("petId")
+					.type(RestParamType.path)
+					.dataType("integer")
+					.required(true)
+					.description("Pet id to delete")
+				.endParam()
+				.to("direct:rest-deletePet")
+			.post("/pet/{petId}/uploadImage")
+				.id("uploadFile")
+				.consumes("multipart/form-data")
+				.produces("application/json")
+				.param()
+					.name("petId")
+					.type(RestParamType.path)
+					.dataType("integer")
+					.required(true)
+					.description("ID of pet to update")
+				.endParam()
+				.param()
+					.name("additionalMetadata")
+					.type(RestParamType.formData)
+					.dataType("string")
+					.required(false)
+					.description("Additional data to pass to server")
+				.endParam()
+				.param()
+					.name("file")
+					.type(RestParamType.formData)
+					.dataType("file")
+					.required(false)
+					.description("file to upload")
+				.endParam()
+				.to("direct:rest-uploadFile")
+			.get("/store/inventory")
+				.id("getInventory")
+				.description("Returns a map of status codes to quantities")
+				.produces("application/json")
+				.to("direct:rest-getInventory")
+			.post("/store/order")
+				.id("placeOrder")
+				.produces("application/xml,application/json")
+				.param()
+					.name("body")
+					.type(RestParamType.body)
+					.required(true)
+					.description("order placed for purchasing the pet")
+				.endParam()
+				.to("direct:rest-placeOrder")
+			.get("/store/order/{orderId}")
+				.id("getOrderById")
+				.description("For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions")
+				.produces("application/xml,application/json")
+				.param()
+					.name("orderId")
+					.type(RestParamType.path)
+					.dataType("integer")
+					.required(true)
+					.description("ID of pet that needs to be fetched")
+				.endParam()
+				.to("direct:rest-getOrderById")
+			.delete("/store/order/{orderId}")
+				.id("deleteOrder")
+				.description("For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors")
+				.produces("application/xml,application/json")
+				.param()
+					.name("orderId")
+					.type(RestParamType.path)
+					.dataType("integer")
+					.required(true)
+					.description("ID of the order that needs to be deleted")
+				.endParam()
+				.to("direct:rest-deleteOrder")
+			.post("/user")
+				.id("createUser")
+				.description("This can only be done by the logged in user.")
+				.produces("application/xml,application/json")
+				.param()
+					.name("body")
+					.type(RestParamType.body)
+					.required(true)
+					.description("Created user object")
+				.endParam()
+				.to("direct:rest-createUser")
+			.post("/user/createWithArray")
+				.id("createUsersWithArrayInput")
+				.produces("application/xml,application/json")
+				.param()
+					.name("body")
+					.type(RestParamType.body)
+					.required(true)
+					.description("List of user object")
+				.endParam()
+				.to("direct:rest-createUsersWithArrayInput")
+			.post("/user/createWithList")
+				.id("createUsersWithListInput")
+				.produces("application/xml,application/json")
+				.param()
+					.name("body")
+					.type(RestParamType.body)
+					.required(true)
+					.description("List of user object")
+				.endParam()
+				.to("direct:rest-createUsersWithListInput")
+			.get("/user/login")
+				.id("loginUser")
+				.produces("application/xml,application/json")
+				.param()
+					.name("username")
+					.type(RestParamType.query)
+					.dataType("string")
+					.required(true)
+					.description("The user name for login")
+				.endParam()
+				.param()
+					.name("password")
+					.type(RestParamType.query)
+					.dataType("string")
+					.required(true)
+					.description("The password for login in clear text")
+				.endParam()
+				.to("direct:rest-loginUser")
+			.get("/user/logout")
+				.id("logoutUser")
+				.produces("application/xml,application/json")
+				.to("direct:rest-logoutUser")
+			.get("/user/{username}")
+				.id("getUserByName")
+				.produces("application/xml,application/json")
+				.param()
+					.name("username")
+					.type(RestParamType.path)
+					.dataType("string")
+					.required(true)
+					.description("The name that needs to be fetched. Use user1 for testing. ")
+				.endParam()
+				.to("direct:rest-getUserByName")
+			.put("/user/{username}")
+				.id("updateUser")
+				.description("This can only be done by the logged in user.")
+				.produces("application/xml,application/json")
+				.param()
+					.name("username")
+					.type(RestParamType.path)
+					.dataType("string")
+					.required(true)
+					.description("name that need to be updated")
+				.endParam()
+				.param()
+					.name("body")
+					.type(RestParamType.body)
+					.required(true)
+					.description("Updated user object")
+				.endParam()
+				.to("direct:rest-updateUser")
+			.delete("/user/{username}")
+				.id("deleteUser")
+				.description("This can only be done by the logged in user.")
+				.produces("application/xml,application/json")
+				.param()
+					.name("username")
+					.type(RestParamType.path)
+					.dataType("string")
+					.required(true)
+					.description("The name that needs to be deleted")
+				.endParam()
+				.to("direct:rest-deleteUser");
+	}
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/test/resources/MyRestRouteFilter.txt b/tooling/openapi-rest-dsl-generator/src/test/resources/MyRestRouteFilter.txt
new file mode 100644
index 0000000..27547a6
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/test/resources/MyRestRouteFilter.txt
@@ -0,0 +1,78 @@
+package com.example;
+
+import javax.annotation.Generated;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.model.rest.CollectionFormat;
+import org.apache.camel.model.rest.RestParamType;
+
+/**
+ * Generated from OpenApi specification by Camel REST DSL generator.
+ */
+@Generated(
+		value = "org.apache.camel.generator.openapi.AppendableGenerator",
+		date = "2017-10-17T00:00:00Z"
+)
+public final class MyRestRoute extends RouteBuilder {
+	/**
+	 * Defines Apache Camel routes using REST DSL fluent API.
+	 */
+	public void configure() {
+		rest("/v2")
+			.put("/pet")
+				.id("updatePet")
+				.consumes("application/json,application/xml")
+				.produces("application/xml,application/json")
+				.param()
+					.name("body")
+					.type(RestParamType.body)
+					.required(true)
+					.description("Pet object that needs to be added to the store")
+				.endParam()
+				.to("direct:rest-updatePet")
+			.get("/pet/findByStatus")
+				.id("findPetsByStatus")
+				.description("Multiple status values can be provided with comma separated strings")
+				.produces("application/xml,application/json")
+				.param()
+					.name("status")
+					.type(RestParamType.query)
+					.dataType("array")
+					.collectionFormat(CollectionFormat.multi)
+					.arrayType("string")
+					.required(true)
+					.description("Status values that need to be considered for filter")
+				.endParam()
+				.to("direct:rest-findPetsByStatus")
+			.get("/pet/findByTags")
+				.id("findPetsByTags")
+				.description("Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.")
+				.produces("application/xml,application/json")
+				.param()
+					.name("tags")
+					.type(RestParamType.query)
+					.dataType("array")
+					.collectionFormat(CollectionFormat.multi)
+					.arrayType("string")
+					.required(true)
+					.description("Tags to filter by")
+				.endParam()
+				.to("direct:rest-findPetsByTags")
+			.delete("/pet/{petId}")
+				.id("deletePet")
+				.produces("application/xml,application/json")
+				.param()
+					.name("api_key")
+					.type(RestParamType.header)
+					.dataType("string")
+					.required(false)
+				.endParam()
+				.param()
+					.name("petId")
+					.type(RestParamType.path)
+					.dataType("integer")
+					.required(true)
+					.description("Pet id to delete")
+				.endParam()
+				.to("direct:rest-deletePet");
+	}
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/test/resources/MyRestRouteFilterV3.txt b/tooling/openapi-rest-dsl-generator/src/test/resources/MyRestRouteFilterV3.txt
new file mode 100644
index 0000000..473210d
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/test/resources/MyRestRouteFilterV3.txt
@@ -0,0 +1,76 @@
+package com.example;
+
+import javax.annotation.Generated;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.model.rest.CollectionFormat;
+import org.apache.camel.model.rest.RestParamType;
+
+/**
+ * Generated from OpenApi specification by Camel REST DSL generator.
+ */
+@Generated(
+		value = "org.apache.camel.generator.openapi.AppendableGenerator",
+		date = "2017-10-17T00:00:00Z"
+)
+public final class MyRestRoute extends RouteBuilder {
+	/**
+	 * Defines Apache Camel routes using REST DSL fluent API.
+	 */
+	public void configure() {
+		rest("/api/v3")
+			.put("/pet")
+				.id("updatePet")
+				.consumes("application/json,application/xml")
+				.param()
+					.name("body")
+					.type(RestParamType.body)
+					.required(true)
+					.description("Pet object that needs to be added to the store")
+				.endParam()
+				.to("direct:rest-updatePet")
+			.get("/pet/findByStatus")
+				.id("findPetsByStatus")
+				.description("Multiple status values can be provided with comma separated strings")
+				.produces("application/xml,application/json")
+				.param()
+					.name("status")
+					.type(RestParamType.query)
+					.dataType("array")
+					.collectionFormat(CollectionFormat.multi)
+					.arrayType("string")
+					.required(true)
+					.description("Status values that need to be considered for filter")
+				.endParam()
+				.to("direct:rest-findPetsByStatus")
+			.get("/pet/findByTags")
+				.id("findPetsByTags")
+				.description("Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.")
+				.produces("application/xml,application/json")
+				.param()
+					.name("tags")
+					.type(RestParamType.query)
+					.dataType("array")
+					.collectionFormat(CollectionFormat.multi)
+					.arrayType("string")
+					.required(true)
+					.description("Tags to filter by")
+				.endParam()
+				.to("direct:rest-findPetsByTags")
+			.delete("/pet/{petId}")
+				.id("deletePet")
+				.param()
+					.name("api_key")
+					.type(RestParamType.header)
+					.dataType("string")
+					.required(false)
+				.endParam()
+				.param()
+					.name("petId")
+					.type(RestParamType.path)
+					.dataType("integer")
+					.required(true)
+					.description("Pet id to delete")
+				.endParam()
+				.to("direct:rest-deletePet");
+	}
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/test/resources/MyRestRouteV3.txt b/tooling/openapi-rest-dsl-generator/src/test/resources/MyRestRouteV3.txt
new file mode 100644
index 0000000..1ec2154
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/test/resources/MyRestRouteV3.txt
@@ -0,0 +1,288 @@
+package com.example;
+
+import javax.annotation.Generated;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.model.rest.CollectionFormat;
+import org.apache.camel.model.rest.RestParamType;
+
+/**
+ * Generated from OpenApi specification by Camel REST DSL generator.
+ */
+@Generated(
+		value = "org.apache.camel.generator.openapi.AppendableGenerator",
+		date = "2017-10-17T00:00:00Z"
+)
+public final class MyRestRoute extends RouteBuilder {
+	/**
+	 * Defines Apache Camel routes using REST DSL fluent API.
+	 */
+	public void configure() {
+		rest("/api/v3")
+			.put("/pet")
+				.id("updatePet")
+				.consumes("application/json,application/xml")
+				.param()
+					.name("body")
+					.type(RestParamType.body)
+					.required(true)
+					.description("Pet object that needs to be added to the store")
+				.endParam()
+				.to("direct:rest-updatePet")
+			.post("/pet")
+				.id("addPet")
+				.consumes("application/json,application/xml")
+				.param()
+					.name("verbose")
+					.type(RestParamType.query)
+					.dataType("boolean")
+					.defaultValue("false")
+					.required(false)
+					.description("Verbose data")
+				.endParam()
+				.param()
+					.name("body")
+					.type(RestParamType.body)
+					.required(true)
+					.description("Pet object that needs to be added to the store")
+				.endParam()
+				.to("direct:rest-addPet")
+			.get("/pet/findByStatus")
+				.id("findPetsByStatus")
+				.description("Multiple status values can be provided with comma separated strings")
+				.produces("application/xml,application/json")
+				.param()
+					.name("status")
+					.type(RestParamType.query)
+					.dataType("array")
+					.collectionFormat(CollectionFormat.multi)
+					.arrayType("string")
+					.required(true)
+					.description("Status values that need to be considered for filter")
+				.endParam()
+				.to("direct:rest-findPetsByStatus")
+			.get("/pet/findByTags")
+				.id("findPetsByTags")
+				.description("Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.")
+				.produces("application/xml,application/json")
+				.param()
+					.name("tags")
+					.type(RestParamType.query)
+					.dataType("array")
+					.collectionFormat(CollectionFormat.multi)
+					.arrayType("string")
+					.required(true)
+					.description("Tags to filter by")
+				.endParam()
+				.to("direct:rest-findPetsByTags")
+			.get("/pet/{petId}")
+				.id("getPetById")
+				.description("Returns a single pet")
+				.produces("application/xml,application/json")
+				.param()
+					.name("petId")
+					.type(RestParamType.path)
+					.dataType("integer")
+					.required(true)
+					.description("ID of pet to return")
+				.endParam()
+				.to("direct:rest-getPetById")
+			.post("/pet/{petId}")
+				.id("updatePetWithForm")
+				.consumes("application/x-www-form-urlencoded")
+				.param()
+					.name("petId")
+					.type(RestParamType.path)
+					.dataType("integer")
+					.required(true)
+					.description("ID of pet that needs to be updated")
+				.endParam()
+				.param()
+					.name("name")
+					.type(RestParamType.formData)
+					.dataType("string")
+					.required(true)
+					.description("Updated name of the pet")
+				.endParam()
+				.param()
+					.name("status")
+					.type(RestParamType.formData)
+					.dataType("string")
+					.required(true)
+					.description("Updated status of the pet")
+				.endParam()
+				.to("direct:rest-updatePetWithForm")
+			.delete("/pet/{petId}")
+				.id("deletePet")
+				.param()
+					.name("api_key")
+					.type(RestParamType.header)
+					.dataType("string")
+					.required(false)
+				.endParam()
+				.param()
+					.name("petId")
+					.type(RestParamType.path)
+					.dataType("integer")
+					.required(true)
+					.description("Pet id to delete")
+				.endParam()
+				.to("direct:rest-deletePet")
+			.post("/pet/{petId}/uploadImage")
+				.id("uploadFile")
+				.consumes("multipart/form-data")
+				.produces("application/json")
+				.param()
+					.name("petId")
+					.type(RestParamType.path)
+					.dataType("integer")
+					.required(true)
+					.description("ID of pet to update")
+				.endParam()
+				.param()
+					.name("additionalMetadata")
+					.type(RestParamType.formData)
+					.dataType("string")
+					.required(true)
+					.description("Additional data to pass to server")
+				.endParam()
+				.param()
+					.name("file")
+					.type(RestParamType.formData)
+					.dataType("string")
+					.required(true)
+					.description("file to upload")
+				.endParam()
+				.to("direct:rest-uploadFile")
+			.get("/store/inventory")
+				.id("getInventory")
+				.description("Returns a map of status codes to quantities")
+				.produces("application/json")
+				.to("direct:rest-getInventory")
+			.post("/store/order")
+				.id("placeOrder")
+				.consumes("*/*")
+				.produces("application/xml,application/json")
+				.param()
+					.name("body")
+					.type(RestParamType.body)
+					.required(true)
+					.description("order placed for purchasing the pet")
+				.endParam()
+				.to("direct:rest-placeOrder")
+			.get("/store/order/{orderId}")
+				.id("getOrderById")
+				.description("For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions")
+				.produces("application/xml,application/json")
+				.param()
+					.name("orderId")
+					.type(RestParamType.path)
+					.dataType("integer")
+					.required(true)
+					.description("ID of pet that needs to be fetched")
+				.endParam()
+				.to("direct:rest-getOrderById")
+			.delete("/store/order/{orderId}")
+				.id("deleteOrder")
+				.description("For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors")
+				.param()
+					.name("orderId")
+					.type(RestParamType.path)
+					.dataType("integer")
+					.required(true)
+					.description("ID of the order that needs to be deleted")
+				.endParam()
+				.to("direct:rest-deleteOrder")
+			.post("/user")
+				.id("createUser")
+				.description("This can only be done by the logged in user.")
+				.consumes("*/*")
+				.param()
+					.name("body")
+					.type(RestParamType.body)
+					.required(true)
+					.description("Created user object")
+				.endParam()
+				.to("direct:rest-createUser")
+			.post("/user/createWithArray")
+				.id("createUsersWithArrayInput")
+				.consumes("*/*")
+				.param()
+					.name("body")
+					.type(RestParamType.body)
+					.required(true)
+					.description("List of user object")
+				.endParam()
+				.to("direct:rest-createUsersWithArrayInput")
+			.post("/user/createWithList")
+				.id("createUsersWithListInput")
+				.consumes("*/*")
+				.param()
+					.name("body")
+					.type(RestParamType.body)
+					.required(true)
+					.description("List of user object")
+				.endParam()
+				.to("direct:rest-createUsersWithListInput")
+			.get("/user/login")
+				.id("loginUser")
+				.produces("application/xml,application/json")
+				.param()
+					.name("username")
+					.type(RestParamType.query)
+					.dataType("string")
+					.required(true)
+					.description("The user name for login")
+				.endParam()
+				.param()
+					.name("password")
+					.type(RestParamType.query)
+					.dataType("string")
+					.required(true)
+					.description("The password for login in clear text")
+				.endParam()
+				.to("direct:rest-loginUser")
+			.get("/user/logout")
+				.id("logoutUser")
+				.to("direct:rest-logoutUser")
+			.get("/user/{username}")
+				.id("getUserByName")
+				.produces("application/xml,application/json")
+				.param()
+					.name("username")
+					.type(RestParamType.path)
+					.dataType("string")
+					.required(true)
+					.description("The name that needs to be fetched. Use user1 for testing. ")
+				.endParam()
+				.to("direct:rest-getUserByName")
+			.put("/user/{username}")
+				.id("updateUser")
+				.description("This can only be done by the logged in user.")
+				.consumes("*/*")
+				.param()
+					.name("username")
+					.type(RestParamType.path)
+					.dataType("string")
+					.required(true)
+					.description("name that need to be updated")
+				.endParam()
+				.param()
+					.name("body")
+					.type(RestParamType.body)
+					.required(true)
+					.description("Updated user object")
+				.endParam()
+				.to("direct:rest-updateUser")
+			.delete("/user/{username}")
+				.id("deleteUser")
+				.description("This can only be done by the logged in user.")
+				.param()
+					.name("username")
+					.type(RestParamType.path)
+					.dataType("string")
+					.required(true)
+					.description("The name that needs to be deleted")
+				.endParam()
+				.to("direct:rest-deleteUser");
+	}
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/test/resources/OpenApiPetstore.txt b/tooling/openapi-rest-dsl-generator/src/test/resources/OpenApiPetstore.txt
new file mode 100644
index 0000000..c3334f7
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/test/resources/OpenApiPetstore.txt
@@ -0,0 +1,291 @@
+package io.openapi.petstore;
+
+import javax.annotation.Generated;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.model.rest.CollectionFormat;
+import org.apache.camel.model.rest.RestParamType;
+
+/**
+ * Generated from OpenApi specification by Camel REST DSL generator.
+ */
+@Generated("org.apache.camel.generator.openapi.AppendableGenerator")
+public final class OpenApiPetstore extends RouteBuilder {
+    /**
+     * Defines Apache Camel routes using REST DSL fluent API.
+     */
+    public void configure() {
+        rest("/v2")
+            .put("/pet")
+                .id("updatePet")
+                .consumes("application/json,application/xml")
+                .produces("application/xml,application/json")
+                .param()
+                    .name("body")
+                    .type(RestParamType.body)
+                    .required(true)
+                    .description("Pet object that needs to be added to the store")
+                .endParam()
+                .to("direct:updatePet")
+            .post("/pet")
+                .id("addPet")
+                .consumes("application/json,application/xml")
+                .produces("application/xml,application/json")
+                .param()
+                    .name("body")
+                    .type(RestParamType.body)
+                    .required(true)
+                    .description("Pet object that needs to be added to the store")
+                .endParam()
+                .param()
+                    .name("verbose")
+                    .type(RestParamType.query)
+                    .dataType("boolean")
+                    .defaultValue("false")
+                    .required(false)
+                    .description("Verbose data")
+                .endParam()
+                .to("direct:addPet")
+            .get("/pet/findByStatus")
+                .id("findPetsByStatus")
+                .description("Multiple status values can be provided with comma separated strings")
+                .produces("application/xml,application/json")
+                .param()
+                    .name("status")
+                    .type(RestParamType.query)
+                    .dataType("array")
+                    .collectionFormat(CollectionFormat.multi)
+                    .arrayType("string")
+                    .required(true)
+                    .description("Status values that need to be considered for filter")
+                .endParam()
+                .to("direct:findPetsByStatus")
+            .get("/pet/findByTags")
+                .id("findPetsByTags")
+                .description("Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.")
+                .produces("application/xml,application/json")
+                .param()
+                    .name("tags")
+                    .type(RestParamType.query)
+                    .dataType("array")
+                    .collectionFormat(CollectionFormat.multi)
+                    .arrayType("string")
+                    .required(true)
+                    .description("Tags to filter by")
+                .endParam()
+                .to("direct:findPetsByTags")
+            .get("/pet/{petId}")
+                .id("getPetById")
+                .description("Returns a single pet")
+                .produces("application/xml,application/json")
+                .param()
+                    .name("petId")
+                    .type(RestParamType.path)
+                    .dataType("integer")
+                    .required(true)
+                    .description("ID of pet to return")
+                .endParam()
+                .to("direct:getPetById")
+            .post("/pet/{petId}")
+                .id("updatePetWithForm")
+                .consumes("application/x-www-form-urlencoded")
+                .produces("application/xml,application/json")
+                .param()
+                    .name("petId")
+                    .type(RestParamType.path)
+                    .dataType("integer")
+                    .required(true)
+                    .description("ID of pet that needs to be updated")
+                .endParam()
+                .param()
+                    .name("name")
+                    .type(RestParamType.formData)
+                    .dataType("string")
+                    .required(false)
+                    .description("Updated name of the pet")
+                .endParam()
+                .param()
+                    .name("status")
+                    .type(RestParamType.formData)
+                    .dataType("string")
+                    .required(false)
+                    .description("Updated status of the pet")
+                .endParam()
+                .to("direct:updatePetWithForm")
+            .delete("/pet/{petId}")
+                .id("deletePet")
+                .produces("application/xml,application/json")
+                .param()
+                    .name("api_key")
+                    .type(RestParamType.header)
+                    .dataType("string")
+                    .required(false)
+                .endParam()
+                .param()
+                    .name("petId")
+                    .type(RestParamType.path)
+                    .dataType("integer")
+                    .required(true)
+                    .description("Pet id to delete")
+                .endParam()
+                .to("direct:deletePet")
+            .post("/pet/{petId}/uploadImage")
+                .id("uploadFile")
+                .consumes("multipart/form-data")
+                .produces("application/json")
+                .param()
+                    .name("petId")
+                    .type(RestParamType.path)
+                    .dataType("integer")
+                    .required(true)
+                    .description("ID of pet to update")
+                .endParam()
+                .param()
+                    .name("additionalMetadata")
+                    .type(RestParamType.formData)
+                    .dataType("string")
+                    .required(false)
+                    .description("Additional data to pass to server")
+                .endParam()
+                .param()
+                    .name("file")
+                    .type(RestParamType.formData)
+                    .dataType("file")
+                    .required(false)
+                    .description("file to upload")
+                .endParam()
+                .to("direct:uploadFile")
+            .get("/store/inventory")
+                .id("getInventory")
+                .description("Returns a map of status codes to quantities")
+                .produces("application/json")
+                .to("direct:getInventory")
+            .post("/store/order")
+                .id("placeOrder")
+                .produces("application/xml,application/json")
+                .param()
+                    .name("body")
+                    .type(RestParamType.body)
+                    .required(true)
+                    .description("order placed for purchasing the pet")
+                .endParam()
+                .to("direct:placeOrder")
+            .get("/store/order/{orderId}")
+                .id("getOrderById")
+                .description("For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions")
+                .produces("application/xml,application/json")
+                .param()
+                    .name("orderId")
+                    .type(RestParamType.path)
+                    .dataType("integer")
+                    .required(true)
+                    .description("ID of pet that needs to be fetched")
+                .endParam()
+                .to("direct:getOrderById")
+            .delete("/store/order/{orderId}")
+                .id("deleteOrder")
+                .description("For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors")
+                .produces("application/xml,application/json")
+                .param()
+                    .name("orderId")
+                    .type(RestParamType.path)
+                    .dataType("integer")
+                    .required(true)
+                    .description("ID of the order that needs to be deleted")
+                .endParam()
+                .to("direct:deleteOrder")
+            .post("/user")
+                .id("createUser")
+                .description("This can only be done by the logged in user.")
+                .produces("application/xml,application/json")
+                .param()
+                    .name("body")
+                    .type(RestParamType.body)
+                    .required(true)
+                    .description("Created user object")
+                .endParam()
+                .to("direct:createUser")
+            .post("/user/createWithArray")
+                .id("createUsersWithArrayInput")
+                .produces("application/xml,application/json")
+                .param()
+                    .name("body")
+                    .type(RestParamType.body)
+                    .required(true)
+                    .description("List of user object")
+                .endParam()
+                .to("direct:createUsersWithArrayInput")
+            .post("/user/createWithList")
+                .id("createUsersWithListInput")
+                .produces("application/xml,application/json")
+                .param()
+                    .name("body")
+                    .type(RestParamType.body)
+                    .required(true)
+                    .description("List of user object")
+                .endParam()
+                .to("direct:createUsersWithListInput")
+            .get("/user/login")
+                .id("loginUser")
+                .produces("application/xml,application/json")
+                .param()
+                    .name("username")
+                    .type(RestParamType.query)
+                    .dataType("string")
+                    .required(true)
+                    .description("The user name for login")
+                .endParam()
+                .param()
+                    .name("password")
+                    .type(RestParamType.query)
+                    .dataType("string")
+                    .required(true)
+                    .description("The password for login in clear text")
+                .endParam()
+                .to("direct:loginUser")
+            .get("/user/logout")
+                .id("logoutUser")
+                .produces("application/xml,application/json")
+                .to("direct:logoutUser")
+            .get("/user/{username}")
+                .id("getUserByName")
+                .produces("application/xml,application/json")
+                .param()
+                    .name("username")
+                    .type(RestParamType.path)
+                    .dataType("string")
+                    .required(true)
+                    .description("The name that needs to be fetched. Use user1 for testing. ")
+                .endParam()
+                .to("direct:getUserByName")
+            .put("/user/{username}")
+                .id("updateUser")
+                .description("This can only be done by the logged in user.")
+                .produces("application/xml,application/json")
+                .param()
+                    .name("username")
+                    .type(RestParamType.path)
+                    .dataType("string")
+                    .required(true)
+                    .description("name that need to be updated")
+                .endParam()
+                .param()
+                    .name("body")
+                    .type(RestParamType.body)
+                    .required(true)
+                    .description("Updated user object")
+                .endParam()
+                .to("direct:updateUser")
+            .delete("/user/{username}")
+                .id("deleteUser")
+                .description("This can only be done by the logged in user.")
+                .produces("application/xml,application/json")
+                .param()
+                    .name("username")
+                    .type(RestParamType.path)
+                    .dataType("string")
+                    .required(true)
+                    .description("The name that needs to be deleted")
+                .endParam()
+                .to("direct:deleteUser");
+    }
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/test/resources/OpenApiPetstoreWithRestComponent.txt b/tooling/openapi-rest-dsl-generator/src/test/resources/OpenApiPetstoreWithRestComponent.txt
new file mode 100644
index 0000000..657025f
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/test/resources/OpenApiPetstoreWithRestComponent.txt
@@ -0,0 +1,294 @@
+package io.openapi.petstore;
+
+import javax.annotation.Generated;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.model.rest.CollectionFormat;
+import org.apache.camel.model.rest.RestParamType;
+
+/**
+ * Generated from OpenApi specification by Camel REST DSL generator.
+ */
+@Generated("org.apache.camel.generator.openapi.AppendableGenerator")
+public final class OpenApiPetstore extends RouteBuilder {
+    /**
+     * Defines Apache Camel routes using REST DSL fluent API.
+     */
+    public void configure() {
+
+        restConfiguration().component("servlet").contextPath("/");
+
+        rest("/v2")
+            .put("/pet")
+                .id("updatePet")
+                .consumes("application/json,application/xml")
+                .produces("application/xml,application/json")
+                .param()
+                    .name("body")
+                    .type(RestParamType.body)
+                    .required(true)
+                    .description("Pet object that needs to be added to the store")
+                .endParam()
+                .to("direct:updatePet")
+            .post("/pet")
+                .id("addPet")
+                .consumes("application/json,application/xml")
+                .produces("application/xml,application/json")
+                .param()
+                    .name("body")
+                    .type(RestParamType.body)
+                    .required(true)
+                    .description("Pet object that needs to be added to the store")
+                .endParam()
+                .param()
+                    .name("verbose")
+                    .type(RestParamType.query)
+                    .dataType("boolean")
+                    .defaultValue("false")
+                    .required(false)
+                    .description("Verbose data")
+                .endParam()
+                .to("direct:addPet")
+            .get("/pet/findByStatus")
+                .id("findPetsByStatus")
+                .description("Multiple status values can be provided with comma separated strings")
+                .produces("application/xml,application/json")
+                .param()
+                    .name("status")
+                    .type(RestParamType.query)
+                    .dataType("array")
+                    .collectionFormat(CollectionFormat.multi)
+                    .arrayType("string")
+                    .required(true)
+                    .description("Status values that need to be considered for filter")
+                .endParam()
+                .to("direct:findPetsByStatus")
+            .get("/pet/findByTags")
+                .id("findPetsByTags")
+                .description("Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.")
+                .produces("application/xml,application/json")
+                .param()
+                    .name("tags")
+                    .type(RestParamType.query)
+                    .dataType("array")
+                    .collectionFormat(CollectionFormat.multi)
+                    .arrayType("string")
+                    .required(true)
+                    .description("Tags to filter by")
+                .endParam()
+                .to("direct:findPetsByTags")
+            .get("/pet/{petId}")
+                .id("getPetById")
+                .description("Returns a single pet")
+                .produces("application/xml,application/json")
+                .param()
+                    .name("petId")
+                    .type(RestParamType.path)
+                    .dataType("integer")
+                    .required(true)
+                    .description("ID of pet to return")
+                .endParam()
+                .to("direct:getPetById")
+            .post("/pet/{petId}")
+                .id("updatePetWithForm")
+                .consumes("application/x-www-form-urlencoded")
+                .produces("application/xml,application/json")
+                .param()
+                    .name("petId")
+                    .type(RestParamType.path)
+                    .dataType("integer")
+                    .required(true)
+                    .description("ID of pet that needs to be updated")
+                .endParam()
+                .param()
+                    .name("name")
+                    .type(RestParamType.formData)
+                    .dataType("string")
+                    .required(false)
+                    .description("Updated name of the pet")
+                .endParam()
+                .param()
+                    .name("status")
+                    .type(RestParamType.formData)
+                    .dataType("string")
+                    .required(false)
+                    .description("Updated status of the pet")
+                .endParam()
+                .to("direct:updatePetWithForm")
+            .delete("/pet/{petId}")
+                .id("deletePet")
+                .produces("application/xml,application/json")
+                .param()
+                    .name("api_key")
+                    .type(RestParamType.header)
+                    .dataType("string")
+                    .required(false)
+                .endParam()
+                .param()
+                    .name("petId")
+                    .type(RestParamType.path)
+                    .dataType("integer")
+                    .required(true)
+                    .description("Pet id to delete")
+                .endParam()
+                .to("direct:deletePet")
+            .post("/pet/{petId}/uploadImage")
+                .id("uploadFile")
+                .consumes("multipart/form-data")
+                .produces("application/json")
+                .param()
+                    .name("petId")
+                    .type(RestParamType.path)
+                    .dataType("integer")
+                    .required(true)
+                    .description("ID of pet to update")
+                .endParam()
+                .param()
+                    .name("additionalMetadata")
+                    .type(RestParamType.formData)
+                    .dataType("string")
+                    .required(false)
+                    .description("Additional data to pass to server")
+                .endParam()
+                .param()
+                    .name("file")
+                    .type(RestParamType.formData)
+                    .dataType("file")
+                    .required(false)
+                    .description("file to upload")
+                .endParam()
+                .to("direct:uploadFile")
+            .get("/store/inventory")
+                .id("getInventory")
+                .description("Returns a map of status codes to quantities")
+                .produces("application/json")
+                .to("direct:getInventory")
+            .post("/store/order")
+                .id("placeOrder")
+                .produces("application/xml,application/json")
+                .param()
+                    .name("body")
+                    .type(RestParamType.body)
+                    .required(true)
+                    .description("order placed for purchasing the pet")
+                .endParam()
+                .to("direct:placeOrder")
+            .get("/store/order/{orderId}")
+                .id("getOrderById")
+                .description("For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions")
+                .produces("application/xml,application/json")
+                .param()
+                    .name("orderId")
+                    .type(RestParamType.path)
+                    .dataType("integer")
+                    .required(true)
+                    .description("ID of pet that needs to be fetched")
+                .endParam()
+                .to("direct:getOrderById")
+            .delete("/store/order/{orderId}")
+                .id("deleteOrder")
+                .description("For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors")
+                .produces("application/xml,application/json")
+                .param()
+                    .name("orderId")
+                    .type(RestParamType.path)
+                    .dataType("integer")
+                    .required(true)
+                    .description("ID of the order that needs to be deleted")
+                .endParam()
+                .to("direct:deleteOrder")
+            .post("/user")
+                .id("createUser")
+                .description("This can only be done by the logged in user.")
+                .produces("application/xml,application/json")
+                .param()
+                    .name("body")
+                    .type(RestParamType.body)
+                    .required(true)
+                    .description("Created user object")
+                .endParam()
+                .to("direct:createUser")
+            .post("/user/createWithArray")
+                .id("createUsersWithArrayInput")
+                .produces("application/xml,application/json")
+                .param()
+                    .name("body")
+                    .type(RestParamType.body)
+                    .required(true)
+                    .description("List of user object")
+                .endParam()
+                .to("direct:createUsersWithArrayInput")
+            .post("/user/createWithList")
+                .id("createUsersWithListInput")
+                .produces("application/xml,application/json")
+                .param()
+                    .name("body")
+                    .type(RestParamType.body)
+                    .required(true)
+                    .description("List of user object")
+                .endParam()
+                .to("direct:createUsersWithListInput")
+            .get("/user/login")
+                .id("loginUser")
+                .produces("application/xml,application/json")
+                .param()
+                    .name("username")
+                    .type(RestParamType.query)
+                    .dataType("string")
+                    .required(true)
+                    .description("The user name for login")
+                .endParam()
+                .param()
+                    .name("password")
+                    .type(RestParamType.query)
+                    .dataType("string")
+                    .required(true)
+                    .description("The password for login in clear text")
+                .endParam()
+                .to("direct:loginUser")
+            .get("/user/logout")
+                .id("logoutUser")
+                .produces("application/xml,application/json")
+                .to("direct:logoutUser")
+            .get("/user/{username}")
+                .id("getUserByName")
+                .produces("application/xml,application/json")
+                .param()
+                    .name("username")
+                    .type(RestParamType.path)
+                    .dataType("string")
+                    .required(true)
+                    .description("The name that needs to be fetched. Use user1 for testing. ")
+                .endParam()
+                .to("direct:getUserByName")
+            .put("/user/{username}")
+                .id("updateUser")
+                .description("This can only be done by the logged in user.")
+                .produces("application/xml,application/json")
+                .param()
+                    .name("username")
+                    .type(RestParamType.path)
+                    .dataType("string")
+                    .required(true)
+                    .description("name that need to be updated")
+                .endParam()
+                .param()
+                    .name("body")
+                    .type(RestParamType.body)
+                    .required(true)
+                    .description("Updated user object")
+                .endParam()
+                .to("direct:updateUser")
+            .delete("/user/{username}")
+                .id("deleteUser")
+                .description("This can only be done by the logged in user.")
+                .produces("application/xml,application/json")
+                .param()
+                    .name("username")
+                    .type(RestParamType.path)
+                    .dataType("string")
+                    .required(true)
+                    .description("The name that needs to be deleted")
+                .endParam()
+                .to("direct:deleteUser");
+    }
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/test/resources/OpenApiPetstoreWithRestComponentXml.txt b/tooling/openapi-rest-dsl-generator/src/test/resources/OpenApiPetstoreWithRestComponentXml.txt
new file mode 100644
index 0000000..d2ffa0f
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/test/resources/OpenApiPetstoreWithRestComponentXml.txt
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<rests xmlns="http://camel.apache.org/schema/spring">
+    <restConfiguration component="servlet" contextPath="/foo"/>
+    <rest path="/v2">
+        <put consumes="application/json,application/xml" id="updatePet" produces="application/xml,application/json" uri="/pet">
+            <param description="Pet object that needs to be added to the store" name="body" required="true" type="body"/>
+            <to uri="direct:updatePet"/>
+        </put>
+        <post consumes="application/json,application/xml" id="addPet" produces="application/xml,application/json" uri="/pet">
+            <param description="Pet object that needs to be added to the store" name="body" required="true" type="body"/>
+            <param dataType="boolean" defaultValue="false" description="Verbose data" name="verbose" required="false" type="query"/>
+            <to uri="direct:addPet"/>
+        </post>
+        <get id="findPetsByStatus" produces="application/xml,application/json" uri="/pet/findByStatus">
+            <description>Multiple status values can be provided with comma separated strings</description>
+            <param arrayType="string" collectionFormat="multi" dataType="array" description="Status values that need to be considered for filter" name="status" required="true" type="query"/>
+            <to uri="direct:findPetsByStatus"/>
+        </get>
+        <get id="findPetsByTags" produces="application/xml,application/json" uri="/pet/findByTags">
+            <description>Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.</description>
+            <param arrayType="string" collectionFormat="multi" dataType="array" description="Tags to filter by" name="tags" required="true" type="query"/>
+            <to uri="direct:findPetsByTags"/>
+        </get>
+        <get id="getPetById" produces="application/xml,application/json" uri="/pet/{petId}">
+            <description>Returns a single pet</description>
+            <param dataType="integer" description="ID of pet to return" name="petId" required="true" type="path"/>
+            <to uri="direct:getPetById"/>
+        </get>
+        <post consumes="application/x-www-form-urlencoded" id="updatePetWithForm" produces="application/xml,application/json" uri="/pet/{petId}">
+            <param dataType="integer" description="ID of pet that needs to be updated" name="petId" required="true" type="path"/>
+            <param dataType="string" description="Updated name of the pet" name="name" required="false" type="formData"/>
+            <param dataType="string" description="Updated status of the pet" name="status" required="false" type="formData"/>
+            <to uri="direct:updatePetWithForm"/>
+        </post>
+        <delete id="deletePet" produces="application/xml,application/json" uri="/pet/{petId}">
+            <param dataType="string" name="api_key" required="false" type="header"/>
+            <param dataType="integer" description="Pet id to delete" name="petId" required="true" type="path"/>
+            <to uri="direct:deletePet"/>
+        </delete>
+        <post consumes="multipart/form-data" id="uploadFile" produces="application/json" uri="/pet/{petId}/uploadImage">
+            <param dataType="integer" description="ID of pet to update" name="petId" required="true" type="path"/>
+            <param dataType="string" description="Additional data to pass to server" name="additionalMetadata" required="false" type="formData"/>
+            <param dataType="file" description="file to upload" name="file" required="false" type="formData"/>
+            <to uri="direct:uploadFile"/>
+        </post>
+        <get id="getInventory" produces="application/json" uri="/store/inventory">
+            <description>Returns a map of status codes to quantities</description>
+            <to uri="direct:getInventory"/>
+        </get>
+        <post id="placeOrder" produces="application/xml,application/json" uri="/store/order">
+            <param description="order placed for purchasing the pet" name="body" required="true" type="body"/>
+            <to uri="direct:placeOrder"/>
+        </post>
+        <get id="getOrderById" produces="application/xml,application/json" uri="/store/order/{orderId}">
+            <description>For valid response try integer IDs with value &gt;= 1 and &lt;= 10. Other values will generated exceptions</description>
+            <param dataType="integer" description="ID of pet that needs to be fetched" name="orderId" required="true" type="path"/>
+            <to uri="direct:getOrderById"/>
+        </get>
+        <delete id="deleteOrder" produces="application/xml,application/json" uri="/store/order/{orderId}">
+            <description>For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors</description>
+            <param dataType="integer" description="ID of the order that needs to be deleted" name="orderId" required="true" type="path"/>
+            <to uri="direct:deleteOrder"/>
+        </delete>
+        <post id="createUser" produces="application/xml,application/json" uri="/user">
+            <description>This can only be done by the logged in user.</description>
+            <param description="Created user object" name="body" required="true" type="body"/>
+            <to uri="direct:createUser"/>
+        </post>
+        <post id="createUsersWithArrayInput" produces="application/xml,application/json" uri="/user/createWithArray">
+            <param description="List of user object" name="body" required="true" type="body"/>
+            <to uri="direct:createUsersWithArrayInput"/>
+        </post>
+        <post id="createUsersWithListInput" produces="application/xml,application/json" uri="/user/createWithList">
+            <param description="List of user object" name="body" required="true" type="body"/>
+            <to uri="direct:createUsersWithListInput"/>
+        </post>
+        <get id="loginUser" produces="application/xml,application/json" uri="/user/login">
+            <param dataType="string" description="The user name for login" name="username" required="true" type="query"/>
+            <param dataType="string" description="The password for login in clear text" name="password" required="true" type="query"/>
+            <to uri="direct:loginUser"/>
+        </get>
+        <get id="logoutUser" produces="application/xml,application/json" uri="/user/logout">
+            <to uri="direct:logoutUser"/>
+        </get>
+        <get id="getUserByName" produces="application/xml,application/json" uri="/user/{username}">
+            <param dataType="string" description="The name that needs to be fetched. Use user1 for testing. " name="username" required="true" type="path"/>
+            <to uri="direct:getUserByName"/>
+        </get>
+        <put id="updateUser" produces="application/xml,application/json" uri="/user/{username}">
+            <description>This can only be done by the logged in user.</description>
+            <param dataType="string" description="name that need to be updated" name="username" required="true" type="path"/>
+            <param description="Updated user object" name="body" required="true" type="body"/>
+            <to uri="direct:updateUser"/>
+        </put>
+        <delete id="deleteUser" produces="application/xml,application/json" uri="/user/{username}">
+            <description>This can only be done by the logged in user.</description>
+            <param dataType="string" description="The name that needs to be deleted" name="username" required="true" type="path"/>
+            <to uri="direct:deleteUser"/>
+        </delete>
+    </rest>
+</rests>
diff --git a/tooling/openapi-rest-dsl-generator/src/test/resources/OpenApiPetstoreXml.txt b/tooling/openapi-rest-dsl-generator/src/test/resources/OpenApiPetstoreXml.txt
new file mode 100644
index 0000000..8d68e03
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/test/resources/OpenApiPetstoreXml.txt
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<rests xmlns="http://camel.apache.org/schema/spring">
+    <rest path="/v2">
+        <put consumes="application/json,application/xml" id="updatePet" produces="application/xml,application/json" uri="/pet">
+            <param description="Pet object that needs to be added to the store" name="body" required="true" type="body"/>
+            <to uri="direct:updatePet"/>
+        </put>
+        <post consumes="application/json,application/xml" id="addPet" produces="application/xml,application/json" uri="/pet">
+            <param description="Pet object that needs to be added to the store" name="body" required="true" type="body"/>
+            <param dataType="boolean" defaultValue="false" description="Verbose data" name="verbose" required="false" type="query"/>
+            <to uri="direct:addPet"/>
+        </post>
+        <get id="findPetsByStatus" produces="application/xml,application/json" uri="/pet/findByStatus">
+            <description>Multiple status values can be provided with comma separated strings</description>
+            <param arrayType="string" collectionFormat="multi" dataType="array" description="Status values that need to be considered for filter" name="status" required="true" type="query"/>
+            <to uri="direct:findPetsByStatus"/>
+        </get>
+        <get id="findPetsByTags" produces="application/xml,application/json" uri="/pet/findByTags">
+            <description>Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.</description>
+            <param arrayType="string" collectionFormat="multi" dataType="array" description="Tags to filter by" name="tags" required="true" type="query"/>
+            <to uri="direct:findPetsByTags"/>
+        </get>
+        <get id="getPetById" produces="application/xml,application/json" uri="/pet/{petId}">
+            <description>Returns a single pet</description>
+            <param dataType="integer" description="ID of pet to return" name="petId" required="true" type="path"/>
+            <to uri="direct:getPetById"/>
+        </get>
+        <post consumes="application/x-www-form-urlencoded" id="updatePetWithForm" produces="application/xml,application/json" uri="/pet/{petId}">
+            <param dataType="integer" description="ID of pet that needs to be updated" name="petId" required="true" type="path"/>
+            <param dataType="string" description="Updated name of the pet" name="name" required="false" type="formData"/>
+            <param dataType="string" description="Updated status of the pet" name="status" required="false" type="formData"/>
+            <to uri="direct:updatePetWithForm"/>
+        </post>
+        <delete id="deletePet" produces="application/xml,application/json" uri="/pet/{petId}">
+            <param dataType="string" name="api_key" required="false" type="header"/>
+            <param dataType="integer" description="Pet id to delete" name="petId" required="true" type="path"/>
+            <to uri="direct:deletePet"/>
+        </delete>
+        <post consumes="multipart/form-data" id="uploadFile" produces="application/json" uri="/pet/{petId}/uploadImage">
+            <param dataType="integer" description="ID of pet to update" name="petId" required="true" type="path"/>
+            <param dataType="string" description="Additional data to pass to server" name="additionalMetadata" required="false" type="formData"/>
+            <param dataType="file" description="file to upload" name="file" required="false" type="formData"/>
+            <to uri="direct:uploadFile"/>
+        </post>
+        <get id="getInventory" produces="application/json" uri="/store/inventory">
+            <description>Returns a map of status codes to quantities</description>
+            <to uri="direct:getInventory"/>
+        </get>
+        <post id="placeOrder" produces="application/xml,application/json" uri="/store/order">
+            <param description="order placed for purchasing the pet" name="body" required="true" type="body"/>
+            <to uri="direct:placeOrder"/>
+        </post>
+        <get id="getOrderById" produces="application/xml,application/json" uri="/store/order/{orderId}">
+            <description>For valid response try integer IDs with value &gt;= 1 and &lt;= 10. Other values will generated exceptions</description>
+            <param dataType="integer" description="ID of pet that needs to be fetched" name="orderId" required="true" type="path"/>
+            <to uri="direct:getOrderById"/>
+        </get>
+        <delete id="deleteOrder" produces="application/xml,application/json" uri="/store/order/{orderId}">
+            <description>For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors</description>
+            <param dataType="integer" description="ID of the order that needs to be deleted" name="orderId" required="true" type="path"/>
+            <to uri="direct:deleteOrder"/>
+        </delete>
+        <post id="createUser" produces="application/xml,application/json" uri="/user">
+            <description>This can only be done by the logged in user.</description>
+            <param description="Created user object" name="body" required="true" type="body"/>
+            <to uri="direct:createUser"/>
+        </post>
+        <post id="createUsersWithArrayInput" produces="application/xml,application/json" uri="/user/createWithArray">
+            <param description="List of user object" name="body" required="true" type="body"/>
+            <to uri="direct:createUsersWithArrayInput"/>
+        </post>
+        <post id="createUsersWithListInput" produces="application/xml,application/json" uri="/user/createWithList">
+            <param description="List of user object" name="body" required="true" type="body"/>
+            <to uri="direct:createUsersWithListInput"/>
+        </post>
+        <get id="loginUser" produces="application/xml,application/json" uri="/user/login">
+            <param dataType="string" description="The user name for login" name="username" required="true" type="query"/>
+            <param dataType="string" description="The password for login in clear text" name="password" required="true" type="query"/>
+            <to uri="direct:loginUser"/>
+        </get>
+        <get id="logoutUser" produces="application/xml,application/json" uri="/user/logout">
+            <to uri="direct:logoutUser"/>
+        </get>
+        <get id="getUserByName" produces="application/xml,application/json" uri="/user/{username}">
+            <param dataType="string" description="The name that needs to be fetched. Use user1 for testing. " name="username" required="true" type="path"/>
+            <to uri="direct:getUserByName"/>
+        </get>
+        <put id="updateUser" produces="application/xml,application/json" uri="/user/{username}">
+            <description>This can only be done by the logged in user.</description>
+            <param dataType="string" description="name that need to be updated" name="username" required="true" type="path"/>
+            <param description="Updated user object" name="body" required="true" type="body"/>
+            <to uri="direct:updateUser"/>
+        </put>
+        <delete id="deleteUser" produces="application/xml,application/json" uri="/user/{username}">
+            <description>This can only be done by the logged in user.</description>
+            <param dataType="string" description="The name that needs to be deleted" name="username" required="true" type="path"/>
+            <to uri="direct:deleteUser"/>
+        </delete>
+    </rest>
+</rests>
diff --git a/tooling/openapi-rest-dsl-generator/src/test/resources/OpenApiV3Petstore.txt b/tooling/openapi-rest-dsl-generator/src/test/resources/OpenApiV3Petstore.txt
new file mode 100644
index 0000000..239990e
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/test/resources/OpenApiV3Petstore.txt
@@ -0,0 +1,285 @@
+package io.openapi.petstore;
+
+import javax.annotation.Generated;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.model.rest.CollectionFormat;
+import org.apache.camel.model.rest.RestParamType;
+
+/**
+ * Generated from OpenApi specification by Camel REST DSL generator.
+ */
+@Generated("org.apache.camel.generator.openapi.AppendableGenerator")
+public final class OpenApiPetstore extends RouteBuilder {
+    /**
+     * Defines Apache Camel routes using REST DSL fluent API.
+     */
+    public void configure() {
+        rest("/api/v3")
+            .put("/pet")
+                .id("updatePet")
+                .consumes("application/json,application/xml")
+                .param()
+                    .name("body")
+                    .type(RestParamType.body)
+                    .required(true)
+                    .description("Pet object that needs to be added to the store")
+                .endParam()
+                .to("direct:updatePet")
+            .post("/pet")
+                .id("addPet")
+                .consumes("application/json,application/xml")
+                .param()
+                    .name("verbose")
+                    .type(RestParamType.query)
+                    .dataType("boolean")
+                    .defaultValue("false")
+                    .required(false)
+                    .description("Verbose data")
+                .endParam()
+                .param()
+                    .name("body")
+                    .type(RestParamType.body)
+                    .required(true)
+                    .description("Pet object that needs to be added to the store")
+                .endParam()
+                .to("direct:addPet")
+            .get("/pet/findByStatus")
+                .id("findPetsByStatus")
+                .description("Multiple status values can be provided with comma separated strings")
+                .produces("application/xml,application/json")
+                .param()
+                    .name("status")
+                    .type(RestParamType.query)
+                    .dataType("array")
+                    .collectionFormat(CollectionFormat.multi)
+                    .arrayType("string")
+                    .required(true)
+                    .description("Status values that need to be considered for filter")
+                .endParam()
+                .to("direct:findPetsByStatus")
+            .get("/pet/findByTags")
+                .id("findPetsByTags")
+                .description("Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.")
+                .produces("application/xml,application/json")
+                .param()
+                    .name("tags")
+                    .type(RestParamType.query)
+                    .dataType("array")
+                    .collectionFormat(CollectionFormat.multi)
+                    .arrayType("string")
+                    .required(true)
+                    .description("Tags to filter by")
+                .endParam()
+                .to("direct:findPetsByTags")
+            .get("/pet/{petId}")
+                .id("getPetById")
+                .description("Returns a single pet")
+                .produces("application/xml,application/json")
+                .param()
+                    .name("petId")
+                    .type(RestParamType.path)
+                    .dataType("integer")
+                    .required(true)
+                    .description("ID of pet to return")
+                .endParam()
+                .to("direct:getPetById")
+            .post("/pet/{petId}")
+                .id("updatePetWithForm")
+                .consumes("application/x-www-form-urlencoded")
+                .param()
+                    .name("petId")
+                    .type(RestParamType.path)
+                    .dataType("integer")
+                    .required(true)
+                    .description("ID of pet that needs to be updated")
+                .endParam()
+                .param()
+                    .name("name")
+                    .type(RestParamType.formData)
+                    .dataType("string")
+                    .required(true)
+                    .description("Updated name of the pet")
+                .endParam()
+                .param()
+                    .name("status")
+                    .type(RestParamType.formData)
+                    .dataType("string")
+                    .required(true)
+                    .description("Updated status of the pet")
+                .endParam()
+                .to("direct:updatePetWithForm")
+            .delete("/pet/{petId}")
+                .id("deletePet")
+                .param()
+                    .name("api_key")
+                    .type(RestParamType.header)
+                    .dataType("string")
+                    .required(false)
+                .endParam()
+                .param()
+                    .name("petId")
+                    .type(RestParamType.path)
+                    .dataType("integer")
+                    .required(true)
+                    .description("Pet id to delete")
+                .endParam()
+                .to("direct:deletePet")
+            .post("/pet/{petId}/uploadImage")
+                .id("uploadFile")
+                .consumes("multipart/form-data")
+                .produces("application/json")
+                .param()
+                    .name("petId")
+                    .type(RestParamType.path)
+                    .dataType("integer")
+                    .required(true)
+                    .description("ID of pet to update")
+                .endParam()
+                .param()
+                    .name("additionalMetadata")
+                    .type(RestParamType.formData)
+                    .dataType("string")
+                    .required(true)
+                    .description("Additional data to pass to server")
+                .endParam()
+                .param()
+                    .name("file")
+                    .type(RestParamType.formData)
+                    .dataType("string")
+                    .required(true)
+                    .description("file to upload")
+                .endParam()
+                .to("direct:uploadFile")
+            .get("/store/inventory")
+                .id("getInventory")
+                .description("Returns a map of status codes to quantities")
+                .produces("application/json")
+                .to("direct:getInventory")
+            .post("/store/order")
+                .id("placeOrder")
+                .consumes("*/*")
+                .produces("application/xml,application/json")
+                .param()
+                    .name("body")
+                    .type(RestParamType.body)
+                    .required(true)
+                    .description("order placed for purchasing the pet")
+                .endParam()
+                .to("direct:placeOrder")
+            .get("/store/order/{orderId}")
+                .id("getOrderById")
+                .description("For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions")
+                .produces("application/xml,application/json")
+                .param()
+                    .name("orderId")
+                    .type(RestParamType.path)
+                    .dataType("integer")
+                    .required(true)
+                    .description("ID of pet that needs to be fetched")
+                .endParam()
+                .to("direct:getOrderById")
+            .delete("/store/order/{orderId}")
+                .id("deleteOrder")
+                .description("For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors")
+                .param()
+                    .name("orderId")
+                    .type(RestParamType.path)
+                    .dataType("integer")
+                    .required(true)
+                    .description("ID of the order that needs to be deleted")
+                .endParam()
+                .to("direct:deleteOrder")
+            .post("/user")
+                .id("createUser")
+                .description("This can only be done by the logged in user.")
+                .consumes("*/*")
+                .param()
+                    .name("body")
+                    .type(RestParamType.body)
+                    .required(true)
+                    .description("Created user object")
+                .endParam()
+                .to("direct:createUser")
+            .post("/user/createWithArray")
+                .id("createUsersWithArrayInput")
+                .consumes("*/*")
+                .param()
+                    .name("body")
+                    .type(RestParamType.body)
+                    .required(true)
+                    .description("List of user object")
+                .endParam()
+                .to("direct:createUsersWithArrayInput")
+            .post("/user/createWithList")
+                .id("createUsersWithListInput")
+                .consumes("*/*")
+                .param()
+                    .name("body")
+                    .type(RestParamType.body)
+                    .required(true)
+                    .description("List of user object")
+                .endParam()
+                .to("direct:createUsersWithListInput")
+            .get("/user/login")
+                .id("loginUser")
+                .produces("application/xml,application/json")
+                .param()
+                    .name("username")
+                    .type(RestParamType.query)
+                    .dataType("string")
+                    .required(true)
+                    .description("The user name for login")
+                .endParam()
+                .param()
+                    .name("password")
+                    .type(RestParamType.query)
+                    .dataType("string")
+                    .required(true)
+                    .description("The password for login in clear text")
+                .endParam()
+                .to("direct:loginUser")
+            .get("/user/logout")
+                .id("logoutUser")
+                .to("direct:logoutUser")
+            .get("/user/{username}")
+                .id("getUserByName")
+                .produces("application/xml,application/json")
+                .param()
+                    .name("username")
+                    .type(RestParamType.path)
+                    .dataType("string")
+                    .required(true)
+                    .description("The name that needs to be fetched. Use user1 for testing. ")
+                .endParam()
+                .to("direct:getUserByName")
+            .put("/user/{username}")
+                .id("updateUser")
+                .description("This can only be done by the logged in user.")
+                .consumes("*/*")
+                .param()
+                    .name("username")
+                    .type(RestParamType.path)
+                    .dataType("string")
+                    .required(true)
+                    .description("name that need to be updated")
+                .endParam()
+                .param()
+                    .name("body")
+                    .type(RestParamType.body)
+                    .required(true)
+                    .description("Updated user object")
+                .endParam()
+                .to("direct:updateUser")
+            .delete("/user/{username}")
+                .id("deleteUser")
+                .description("This can only be done by the logged in user.")
+                .param()
+                    .name("username")
+                    .type(RestParamType.path)
+                    .dataType("string")
+                    .required(true)
+                    .description("The name that needs to be deleted")
+                .endParam()
+                .to("direct:deleteUser");
+    }
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/test/resources/OpenApiV3PetstoreWithRestComponent.txt b/tooling/openapi-rest-dsl-generator/src/test/resources/OpenApiV3PetstoreWithRestComponent.txt
new file mode 100644
index 0000000..614fd01
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/test/resources/OpenApiV3PetstoreWithRestComponent.txt
@@ -0,0 +1,288 @@
+package io.openapi.petstore;
+
+import javax.annotation.Generated;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.model.rest.CollectionFormat;
+import org.apache.camel.model.rest.RestParamType;
+
+/**
+ * Generated from OpenApi specification by Camel REST DSL generator.
+ */
+@Generated("org.apache.camel.generator.openapi.AppendableGenerator")
+public final class OpenApiPetstore extends RouteBuilder {
+    /**
+     * Defines Apache Camel routes using REST DSL fluent API.
+     */
+    public void configure() {
+
+        restConfiguration().component("servlet").contextPath("/");
+
+        rest("/api/v3")
+            .put("/pet")
+                .id("updatePet")
+                .consumes("application/json,application/xml")
+                .param()
+                    .name("body")
+                    .type(RestParamType.body)
+                    .required(true)
+                    .description("Pet object that needs to be added to the store")
+                .endParam()
+                .to("direct:updatePet")
+            .post("/pet")
+                .id("addPet")
+                .consumes("application/json,application/xml")
+                .param()
+                    .name("verbose")
+                    .type(RestParamType.query)
+                    .dataType("boolean")
+                    .defaultValue("false")
+                    .required(false)
+                    .description("Verbose data")
+                .endParam()
+                .param()
+                    .name("body")
+                    .type(RestParamType.body)
+                    .required(true)
+                    .description("Pet object that needs to be added to the store")
+                .endParam()
+                .to("direct:addPet")
+            .get("/pet/findByStatus")
+                .id("findPetsByStatus")
+                .description("Multiple status values can be provided with comma separated strings")
+                .produces("application/xml,application/json")
+                .param()
+                    .name("status")
+                    .type(RestParamType.query)
+                    .dataType("array")
+                    .collectionFormat(CollectionFormat.multi)
+                    .arrayType("string")
+                    .required(true)
+                    .description("Status values that need to be considered for filter")
+                .endParam()
+                .to("direct:findPetsByStatus")
+            .get("/pet/findByTags")
+                .id("findPetsByTags")
+                .description("Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.")
+                .produces("application/xml,application/json")
+                .param()
+                    .name("tags")
+                    .type(RestParamType.query)
+                    .dataType("array")
+                    .collectionFormat(CollectionFormat.multi)
+                    .arrayType("string")
+                    .required(true)
+                    .description("Tags to filter by")
+                .endParam()
+                .to("direct:findPetsByTags")
+            .get("/pet/{petId}")
+                .id("getPetById")
+                .description("Returns a single pet")
+                .produces("application/xml,application/json")
+                .param()
+                    .name("petId")
+                    .type(RestParamType.path)
+                    .dataType("integer")
+                    .required(true)
+                    .description("ID of pet to return")
+                .endParam()
+                .to("direct:getPetById")
+            .post("/pet/{petId}")
+                .id("updatePetWithForm")
+                .consumes("application/x-www-form-urlencoded")
+                .param()
+                    .name("petId")
+                    .type(RestParamType.path)
+                    .dataType("integer")
+                    .required(true)
+                    .description("ID of pet that needs to be updated")
+                .endParam()
+                .param()
+                    .name("name")
+                    .type(RestParamType.formData)
+                    .dataType("string")
+                    .required(true)
+                    .description("Updated name of the pet")
+                .endParam()
+                .param()
+                    .name("status")
+                    .type(RestParamType.formData)
+                    .dataType("string")
+                    .required(true)
+                    .description("Updated status of the pet")
+                .endParam()
+                .to("direct:updatePetWithForm")
+            .delete("/pet/{petId}")
+                .id("deletePet")
+                .param()
+                    .name("api_key")
+                    .type(RestParamType.header)
+                    .dataType("string")
+                    .required(false)
+                .endParam()
+                .param()
+                    .name("petId")
+                    .type(RestParamType.path)
+                    .dataType("integer")
+                    .required(true)
+                    .description("Pet id to delete")
+                .endParam()
+                .to("direct:deletePet")
+            .post("/pet/{petId}/uploadImage")
+                .id("uploadFile")
+                .consumes("multipart/form-data")
+                .produces("application/json")
+                .param()
+                    .name("petId")
+                    .type(RestParamType.path)
+                    .dataType("integer")
+                    .required(true)
+                    .description("ID of pet to update")
+                .endParam()
+                .param()
+                    .name("additionalMetadata")
+                    .type(RestParamType.formData)
+                    .dataType("string")
+                    .required(true)
+                    .description("Additional data to pass to server")
+                .endParam()
+                .param()
+                    .name("file")
+                    .type(RestParamType.formData)
+                    .dataType("string")
+                    .required(true)
+                    .description("file to upload")
+                .endParam()
+                .to("direct:uploadFile")
+            .get("/store/inventory")
+                .id("getInventory")
+                .description("Returns a map of status codes to quantities")
+                .produces("application/json")
+                .to("direct:getInventory")
+            .post("/store/order")
+                .id("placeOrder")
+                .consumes("*/*")
+                .produces("application/xml,application/json")
+                .param()
+                    .name("body")
+                    .type(RestParamType.body)
+                    .required(true)
+                    .description("order placed for purchasing the pet")
+                .endParam()
+                .to("direct:placeOrder")
+            .get("/store/order/{orderId}")
+                .id("getOrderById")
+                .description("For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions")
+                .produces("application/xml,application/json")
+                .param()
+                    .name("orderId")
+                    .type(RestParamType.path)
+                    .dataType("integer")
+                    .required(true)
+                    .description("ID of pet that needs to be fetched")
+                .endParam()
+                .to("direct:getOrderById")
+            .delete("/store/order/{orderId}")
+                .id("deleteOrder")
+                .description("For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors")
+                .param()
+                    .name("orderId")
+                    .type(RestParamType.path)
+                    .dataType("integer")
+                    .required(true)
+                    .description("ID of the order that needs to be deleted")
+                .endParam()
+                .to("direct:deleteOrder")
+            .post("/user")
+                .id("createUser")
+                .description("This can only be done by the logged in user.")
+                .consumes("*/*")
+                .param()
+                    .name("body")
+                    .type(RestParamType.body)
+                    .required(true)
+                    .description("Created user object")
+                .endParam()
+                .to("direct:createUser")
+            .post("/user/createWithArray")
+                .id("createUsersWithArrayInput")
+                .consumes("*/*")
+                .param()
+                    .name("body")
+                    .type(RestParamType.body)
+                    .required(true)
+                    .description("List of user object")
+                .endParam()
+                .to("direct:createUsersWithArrayInput")
+            .post("/user/createWithList")
+                .id("createUsersWithListInput")
+                .consumes("*/*")
+                .param()
+                    .name("body")
+                    .type(RestParamType.body)
+                    .required(true)
+                    .description("List of user object")
+                .endParam()
+                .to("direct:createUsersWithListInput")
+            .get("/user/login")
+                .id("loginUser")
+                .produces("application/xml,application/json")
+                .param()
+                    .name("username")
+                    .type(RestParamType.query)
+                    .dataType("string")
+                    .required(true)
+                    .description("The user name for login")
+                .endParam()
+                .param()
+                    .name("password")
+                    .type(RestParamType.query)
+                    .dataType("string")
+                    .required(true)
+                    .description("The password for login in clear text")
+                .endParam()
+                .to("direct:loginUser")
+            .get("/user/logout")
+                .id("logoutUser")
+                .to("direct:logoutUser")
+            .get("/user/{username}")
+                .id("getUserByName")
+                .produces("application/xml,application/json")
+                .param()
+                    .name("username")
+                    .type(RestParamType.path)
+                    .dataType("string")
+                    .required(true)
+                    .description("The name that needs to be fetched. Use user1 for testing. ")
+                .endParam()
+                .to("direct:getUserByName")
+            .put("/user/{username}")
+                .id("updateUser")
+                .description("This can only be done by the logged in user.")
+                .consumes("*/*")
+                .param()
+                    .name("username")
+                    .type(RestParamType.path)
+                    .dataType("string")
+                    .required(true)
+                    .description("name that need to be updated")
+                .endParam()
+                .param()
+                    .name("body")
+                    .type(RestParamType.body)
+                    .required(true)
+                    .description("Updated user object")
+                .endParam()
+                .to("direct:updateUser")
+            .delete("/user/{username}")
+                .id("deleteUser")
+                .description("This can only be done by the logged in user.")
+                .param()
+                    .name("username")
+                    .type(RestParamType.path)
+                    .dataType("string")
+                    .required(true)
+                    .description("The name that needs to be deleted")
+                .endParam()
+                .to("direct:deleteUser");
+    }
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/test/resources/OpenApiV3PetstoreWithRestComponentXml.txt b/tooling/openapi-rest-dsl-generator/src/test/resources/OpenApiV3PetstoreWithRestComponentXml.txt
new file mode 100644
index 0000000..abd42a6
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/test/resources/OpenApiV3PetstoreWithRestComponentXml.txt
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="UTF-8"?><rests xmlns="http://camel.apache.org/schema/spring"><restConfiguration component="servlet" contextPath="/foo"/>
+    <rest path="/api/v3">
+        <put consumes="application/json,application/xml" id="updatePet" uri="/pet">
+            <param description="Pet object that needs to be added to the store" name="body" required="true" type="body"/>
+            <to uri="direct:updatePet"/>
+        </put>
+        <post consumes="application/json,application/xml" id="addPet" uri="/pet">
+            <param dataType="boolean" defaultValue="false" description="Verbose data" name="verbose" required="false" type="query"/>
+            <param description="Pet object that needs to be added to the store" name="body" required="true" type="body"/>
+            <to uri="direct:addPet"/>
+        </post>
+        <get id="findPetsByStatus" produces="application/xml,application/json" uri="/pet/findByStatus">
+            <description>Multiple status values can be provided with comma separated strings</description>
+            <param arrayType="string" collectionFormat="multi" dataType="array" description="Status values that need to be considered for filter" name="status" required="true" type="query"/>
+            <to uri="direct:findPetsByStatus"/>
+        </get>
+        <get id="findPetsByTags" produces="application/xml,application/json" uri="/pet/findByTags">
+            <description>Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.</description>
+            <param arrayType="string" collectionFormat="multi" dataType="array" description="Tags to filter by" name="tags" required="true" type="query"/>
+            <to uri="direct:findPetsByTags"/>
+        </get>
+        <get id="getPetById" produces="application/xml,application/json" uri="/pet/{petId}">
+            <description>Returns a single pet</description>
+            <param dataType="integer" description="ID of pet to return" name="petId" required="true" type="path"/>
+            <to uri="direct:getPetById"/>
+        </get>
+        <post consumes="application/x-www-form-urlencoded" id="updatePetWithForm" uri="/pet/{petId}">
+            <param dataType="integer" description="ID of pet that needs to be updated" name="petId" required="true" type="path"/>
+            <param dataType="string" description="Updated name of the pet" name="name" required="true" type="formData"/>
+            <param dataType="string" description="Updated status of the pet" name="status" required="true" type="formData"/>
+            <to uri="direct:updatePetWithForm"/>
+        </post>
+        <delete id="deletePet" uri="/pet/{petId}">
+            <param dataType="string" name="api_key" required="false" type="header"/>
+            <param dataType="integer" description="Pet id to delete" name="petId" required="true" type="path"/>
+            <to uri="direct:deletePet"/>
+        </delete>
+        <post consumes="multipart/form-data" id="uploadFile" produces="application/json" uri="/pet/{petId}/uploadImage">
+            <param dataType="integer" description="ID of pet to update" name="petId" required="true" type="path"/>
+            <param dataType="string" description="Additional data to pass to server" name="additionalMetadata" required="true" type="formData"/>
+            <param dataType="string" description="file to upload" name="file" required="true" type="formData"/>
+            <to uri="direct:uploadFile"/>
+        </post>
+        <get id="getInventory" produces="application/json" uri="/store/inventory">
+            <description>Returns a map of status codes to quantities</description>
+            <to uri="direct:getInventory"/>
+        </get>
+        <post consumes="*/*" id="placeOrder" produces="application/xml,application/json" uri="/store/order">
+            <param description="order placed for purchasing the pet" name="body" required="true" type="body"/>
+            <to uri="direct:placeOrder"/>
+        </post>
+        <get id="getOrderById" produces="application/xml,application/json" uri="/store/order/{orderId}">
+            <description>For valid response try integer IDs with value &gt;= 1 and &lt;= 10. Other values will generated exceptions</description>
+            <param dataType="integer" description="ID of pet that needs to be fetched" name="orderId" required="true" type="path"/>
+            <to uri="direct:getOrderById"/>
+        </get>
+        <delete id="deleteOrder" uri="/store/order/{orderId}">
+            <description>For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors</description>
+            <param dataType="integer" description="ID of the order that needs to be deleted" name="orderId" required="true" type="path"/>
+            <to uri="direct:deleteOrder"/>
+        </delete>
+        <post consumes="*/*" id="createUser" uri="/user">
+            <description>This can only be done by the logged in user.</description>
+            <param description="Created user object" name="body" required="true" type="body"/>
+            <to uri="direct:createUser"/>
+        </post>
+        <post consumes="*/*" id="createUsersWithArrayInput" uri="/user/createWithArray">
+            <param description="List of user object" name="body" required="true" type="body"/>
+            <to uri="direct:createUsersWithArrayInput"/>
+        </post>
+        <post consumes="*/*" id="createUsersWithListInput" uri="/user/createWithList">
+            <param description="List of user object" name="body" required="true" type="body"/>
+            <to uri="direct:createUsersWithListInput"/>
+        </post>
+        <get id="loginUser" produces="application/xml,application/json" uri="/user/login">
+            <param dataType="string" description="The user name for login" name="username" required="true" type="query"/>
+            <param dataType="string" description="The password for login in clear text" name="password" required="true" type="query"/>
+            <to uri="direct:loginUser"/>
+        </get>
+        <get id="logoutUser" uri="/user/logout">
+            <to uri="direct:logoutUser"/>
+        </get>
+        <get id="getUserByName" produces="application/xml,application/json" uri="/user/{username}">
+            <param dataType="string" description="The name that needs to be fetched. Use user1 for testing. " name="username" required="true" type="path"/>
+            <to uri="direct:getUserByName"/>
+        </get>
+        <put consumes="*/*" id="updateUser" uri="/user/{username}">
+            <description>This can only be done by the logged in user.</description>
+            <param dataType="string" description="name that need to be updated" name="username" required="true" type="path"/>
+            <param description="Updated user object" name="body" required="true" type="body"/>
+            <to uri="direct:updateUser"/>
+        </put>
+        <delete id="deleteUser" uri="/user/{username}">
+            <description>This can only be done by the logged in user.</description>
+            <param dataType="string" description="The name that needs to be deleted" name="username" required="true" type="path"/>
+            <to uri="direct:deleteUser"/>
+        </delete>
+    </rest>
+</rests>
diff --git a/tooling/openapi-rest-dsl-generator/src/test/resources/OpenApiV3PetstoreXml.txt b/tooling/openapi-rest-dsl-generator/src/test/resources/OpenApiV3PetstoreXml.txt
new file mode 100644
index 0000000..ebb8da1
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/test/resources/OpenApiV3PetstoreXml.txt
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="UTF-8"?><rests xmlns="http://camel.apache.org/schema/spring">
+    <rest path="/api/v3">
+        <put consumes="application/json,application/xml" id="updatePet" uri="/pet">
+            <param description="Pet object that needs to be added to the store" name="body" required="true" type="body"/>
+            <to uri="direct:updatePet"/>
+        </put>
+        <post consumes="application/json,application/xml" id="addPet" uri="/pet">
+            <param dataType="boolean" defaultValue="false" description="Verbose data" name="verbose" required="false" type="query"/>
+            <param description="Pet object that needs to be added to the store" name="body" required="true" type="body"/>
+            <to uri="direct:addPet"/>
+        </post>
+        <get id="findPetsByStatus" produces="application/xml,application/json" uri="/pet/findByStatus">
+            <description>Multiple status values can be provided with comma separated strings</description>
+            <param arrayType="string" collectionFormat="multi" dataType="array" description="Status values that need to be considered for filter" name="status" required="true" type="query"/>
+            <to uri="direct:findPetsByStatus"/>
+        </get>
+        <get id="findPetsByTags" produces="application/xml,application/json" uri="/pet/findByTags">
+            <description>Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.</description>
+            <param arrayType="string" collectionFormat="multi" dataType="array" description="Tags to filter by" name="tags" required="true" type="query"/>
+            <to uri="direct:findPetsByTags"/>
+        </get>
+        <get id="getPetById" produces="application/xml,application/json" uri="/pet/{petId}">
+            <description>Returns a single pet</description>
+            <param dataType="integer" description="ID of pet to return" name="petId" required="true" type="path"/>
+            <to uri="direct:getPetById"/>
+        </get>
+        <post consumes="application/x-www-form-urlencoded" id="updatePetWithForm" uri="/pet/{petId}">
+            <param dataType="integer" description="ID of pet that needs to be updated" name="petId" required="true" type="path"/>
+            <param dataType="string" description="Updated name of the pet" name="name" required="true" type="formData"/>
+            <param dataType="string" description="Updated status of the pet" name="status" required="true" type="formData"/>
+            <to uri="direct:updatePetWithForm"/>
+        </post>
+        <delete id="deletePet" uri="/pet/{petId}">
+            <param dataType="string" name="api_key" required="false" type="header"/>
+            <param dataType="integer" description="Pet id to delete" name="petId" required="true" type="path"/>
+            <to uri="direct:deletePet"/>
+        </delete>
+        <post consumes="multipart/form-data" id="uploadFile" produces="application/json" uri="/pet/{petId}/uploadImage">
+            <param dataType="integer" description="ID of pet to update" name="petId" required="true" type="path"/>
+            <param dataType="string" description="Additional data to pass to server" name="additionalMetadata" required="true" type="formData"/>
+            <param dataType="string" description="file to upload" name="file" required="true" type="formData"/>
+            <to uri="direct:uploadFile"/>
+        </post>
+        <get id="getInventory" produces="application/json" uri="/store/inventory">
+            <description>Returns a map of status codes to quantities</description>
+            <to uri="direct:getInventory"/>
+        </get>
+        <post consumes="*/*" id="placeOrder" produces="application/xml,application/json" uri="/store/order">
+            <param description="order placed for purchasing the pet" name="body" required="true" type="body"/>
+            <to uri="direct:placeOrder"/>
+        </post>
+        <get id="getOrderById" produces="application/xml,application/json" uri="/store/order/{orderId}">
+            <description>For valid response try integer IDs with value &gt;= 1 and &lt;= 10. Other values will generated exceptions</description>
+            <param dataType="integer" description="ID of pet that needs to be fetched" name="orderId" required="true" type="path"/>
+            <to uri="direct:getOrderById"/>
+        </get>
+        <delete id="deleteOrder" uri="/store/order/{orderId}">
+            <description>For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors</description>
+            <param dataType="integer" description="ID of the order that needs to be deleted" name="orderId" required="true" type="path"/>
+            <to uri="direct:deleteOrder"/>
+        </delete>
+        <post consumes="*/*" id="createUser" uri="/user">
+            <description>This can only be done by the logged in user.</description>
+            <param description="Created user object" name="body" required="true" type="body"/>
+            <to uri="direct:createUser"/>
+        </post>
+        <post consumes="*/*" id="createUsersWithArrayInput" uri="/user/createWithArray">
+            <param description="List of user object" name="body" required="true" type="body"/>
+            <to uri="direct:createUsersWithArrayInput"/>
+        </post>
+        <post consumes="*/*" id="createUsersWithListInput" uri="/user/createWithList">
+            <param description="List of user object" name="body" required="true" type="body"/>
+            <to uri="direct:createUsersWithListInput"/>
+        </post>
+        <get id="loginUser" produces="application/xml,application/json" uri="/user/login">
+            <param dataType="string" description="The user name for login" name="username" required="true" type="query"/>
+            <param dataType="string" description="The password for login in clear text" name="password" required="true" type="query"/>
+            <to uri="direct:loginUser"/>
+        </get>
+        <get id="logoutUser" uri="/user/logout">
+            <to uri="direct:logoutUser"/>
+        </get>
+        <get id="getUserByName" produces="application/xml,application/json" uri="/user/{username}">
+            <param dataType="string" description="The name that needs to be fetched. Use user1 for testing. " name="username" required="true" type="path"/>
+            <to uri="direct:getUserByName"/>
+        </get>
+        <put consumes="*/*" id="updateUser" uri="/user/{username}">
+            <description>This can only be done by the logged in user.</description>
+            <param dataType="string" description="name that need to be updated" name="username" required="true" type="path"/>
+            <param description="Updated user object" name="body" required="true" type="body"/>
+            <to uri="direct:updateUser"/>
+        </put>
+        <delete id="deleteUser" uri="/user/{username}">
+            <description>This can only be done by the logged in user.</description>
+            <param dataType="string" description="The name that needs to be deleted" name="username" required="true" type="path"/>
+            <to uri="direct:deleteUser"/>
+        </delete>
+    </rest>
+</rests>
diff --git a/tooling/openapi-rest-dsl-generator/src/test/resources/SpringBootRestController.txt b/tooling/openapi-rest-dsl-generator/src/test/resources/SpringBootRestController.txt
new file mode 100644
index 0000000..0eb0f2f
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/test/resources/SpringBootRestController.txt
@@ -0,0 +1,24 @@
+package com.foo;
+
+import javax.annotation.Generated;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * Forward requests to the Camel servlet so it can service REST requests.
+ */
+@Generated("org.apache.camel.generator.openapi.SpringBootProjectSourceCodeGenerator")
+@RestController
+public final class CamelRestController {
+    @RequestMapping("/**")
+    public void camelServlet(HttpServletRequest request, HttpServletResponse response) {
+        try {
+            String path = request.getRequestURI();
+            request.getServletContext().getRequestDispatcher("/camel/" + path).forward(request, response);
+        } catch (Exception e) {
+            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+        }
+    }
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/test/resources/log4j2.properties b/tooling/openapi-rest-dsl-generator/src/test/resources/log4j2.properties
new file mode 100644
index 0000000..be6f29f
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/test/resources/log4j2.properties
@@ -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.
+## ---------------------------------------------------------------------------
+
+appender.file.type = File
+appender.file.name = file
+appender.file.fileName = target/camel-openapi-rest-dsl-generator.log
+appender.file.layout.type = PatternLayout
+appender.file.layout.pattern = %d [%-15.15t] %-5p %-30.30c{1} - %m%n
+appender.out.type = Console
+appender.out.name = out
+appender.out.layout.type = PatternLayout
+appender.out.layout.pattern = %d [%-15.15t] %-5p %-30.30c{1} - %m%n
+rootLogger.level = INFO
+rootLogger.appenderRef.file.ref = file
diff --git a/tooling/pom.xml b/tooling/pom.xml
index 76b1a9d..52eb485 100644
--- a/tooling/pom.xml
+++ b/tooling/pom.xml
@@ -41,6 +41,7 @@
         <module>camel-util-json</module>
         <module>camel-bundle-plugin</module>
         <module>swagger-rest-dsl-generator</module>
+        <module>openapi-rest-dsl-generator</module>
         <module>maven</module>
     </modules>