camel-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From davscl...@apache.org
Subject [2/3] camel git commit: CAMEL-10532 Convert RestConsumerBindingProcessor into processor advice so it works fine with contract advice together
Date Tue, 14 Feb 2017 12:21:23 GMT
CAMEL-10532 Convert RestConsumerBindingProcessor into processor advice so it works fine with contract advice together


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

Branch: refs/heads/master
Commit: 8c9184b6f2d75ebbab90a5cb42eb51e114fec42b
Parents: 25b2ba2
Author: Tomohisa Igarashi <tm.igarashi@gmail.com>
Authored: Thu Feb 9 17:48:16 2017 +0900
Committer: Tomohisa Igarashi <tm.igarashi@gmail.com>
Committed: Tue Feb 14 20:43:46 2017 +0900

----------------------------------------------------------------------
 .../rest/RestConsumerBindingProcessor.java      | 461 -------------------
 .../apache/camel/impl/DefaultRouteContext.java  |  23 +-
 .../org/apache/camel/model/RouteDefinition.java |  23 +-
 .../camel/model/rest/RestBindingDefinition.java |  18 +-
 .../apache/camel/model/rest/RestDefinition.java |   2 +-
 .../apache/camel/processor/ContractAdvice.java  |  34 +-
 .../camel/processor/RestBindingAdvice.java      | 418 +++++++++++++++++
 .../java/org/apache/camel/spi/DataType.java     |   2 +-
 .../java/org/apache/camel/spi/RouteContext.java |   5 -
 .../rest/FromRestGetEmbeddedRouteTest.java      |   4 +-
 .../component/rest/FromRestGetEndPathTest.java  |   2 +-
 .../rest/FromRestGetInterceptTest.java          |   2 +-
 .../ManagedTransformerRegistryTest.java         |   4 +-
 ...estJettyBindingModeJsonWithContractTest.java |  92 ++++
 ...RestJettyBindingModeOffWithContractTest.java |  99 ++++
 .../camel/component/jetty/rest/UserPojoEx.java  |  48 ++
 ...ettyHttpBindingModeJsonWithContractTest.java |  83 ++++
 ...NettyHttpBindingModeOffWithContractTest.java |  90 ++++
 .../component/netty4/http/rest/UserPojoEx.java  |  48 ++
 ...tRestletBindingModeJsonWithContractTest.java |  91 ++++
 ...stRestletBindingModeOffWithContractTest.java |  98 ++++
 .../RestRestletCustomDataFormatInvalidTest.java |   2 +-
 .../camel/component/restlet/UserPojoEx.java     |  48 ++
 ...tServletBindingModeJsonWithContractTest.java |  92 ++++
 ...stServletBindingModeOffWithContractTest.java |  99 ++++
 .../component/servlet/rest/UserPojoEx.java      |  48 ++
 .../rest/FromRestGetEmbeddedRouteTest.java      |   4 +-
 .../rest/FromRestGetInterceptTest.java          |   2 +-
 ...rtowHttpBindingModeJsonWithContractTest.java |  83 ++++
 ...ertowHttpBindingModeOffWithContractTest.java |  90 ++++
 .../component/undertow/rest/UserPojoEx.java     |  48 ++
 31 files changed, 1657 insertions(+), 506 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/8c9184b6/camel-core/src/main/java/org/apache/camel/component/rest/RestConsumerBindingProcessor.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/component/rest/RestConsumerBindingProcessor.java b/camel-core/src/main/java/org/apache/camel/component/rest/RestConsumerBindingProcessor.java
deleted file mode 100644
index 888fc2f..0000000
--- a/camel-core/src/main/java/org/apache/camel/component/rest/RestConsumerBindingProcessor.java
+++ /dev/null
@@ -1,461 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.camel.component.rest;
-
-import java.util.Locale;
-import java.util.Map;
-
-import org.apache.camel.AsyncCallback;
-import org.apache.camel.AsyncProcessor;
-import org.apache.camel.CamelContext;
-import org.apache.camel.CamelContextAware;
-import org.apache.camel.Exchange;
-import org.apache.camel.Message;
-import org.apache.camel.Route;
-import org.apache.camel.processor.MarshalProcessor;
-import org.apache.camel.processor.UnmarshalProcessor;
-import org.apache.camel.processor.binding.BindingException;
-import org.apache.camel.spi.DataFormat;
-import org.apache.camel.spi.RestConfiguration;
-import org.apache.camel.support.ServiceSupport;
-import org.apache.camel.support.SynchronizationAdapter;
-import org.apache.camel.util.AsyncProcessorHelper;
-import org.apache.camel.util.ExchangeHelper;
-import org.apache.camel.util.MessageHelper;
-import org.apache.camel.util.ObjectHelper;
-import org.apache.camel.util.ServiceHelper;
-
-/**
- * A {@link org.apache.camel.Processor} that binds the REST DSL incoming and outgoing messages
- * from sources of json or xml to Java Objects.
- * <p/>
- * The binding uses {@link org.apache.camel.spi.DataFormat} for the actual work to transform
- * from xml/json to Java Objects and reverse again.
- */
-public class RestConsumerBindingProcessor extends ServiceSupport implements AsyncProcessor {
-
-    private final CamelContext camelContext;
-    private final AsyncProcessor jsonUnmarshal;
-    private final AsyncProcessor xmlUnmarshal;
-    private final AsyncProcessor jsonMarshal;
-    private final AsyncProcessor xmlMarshal;
-    private final String consumes;
-    private final String produces;
-    private final String bindingMode;
-    private final boolean skipBindingOnErrorCode;
-    private final boolean enableCORS;
-    private final Map<String, String> corsHeaders;
-    private final Map<String, String> queryDefaultValues;
-
-    public RestConsumerBindingProcessor(CamelContext camelContext, DataFormat jsonDataFormat, DataFormat xmlDataFormat,
-                                        DataFormat outJsonDataFormat, DataFormat outXmlDataFormat,
-                                        String consumes, String produces, String bindingMode,
-                                        boolean skipBindingOnErrorCode, boolean enableCORS,
-                                        Map<String, String> corsHeaders,
-                                        Map<String, String> queryDefaultValues) {
-
-        this.camelContext = camelContext;
-
-        if (jsonDataFormat != null) {
-            this.jsonUnmarshal = new UnmarshalProcessor(jsonDataFormat);
-        } else {
-            this.jsonUnmarshal = null;
-        }
-        if (outJsonDataFormat != null) {
-            this.jsonMarshal = new MarshalProcessor(outJsonDataFormat);
-        } else if (jsonDataFormat != null) {
-            this.jsonMarshal = new MarshalProcessor(jsonDataFormat);
-        } else {
-            this.jsonMarshal = null;
-        }
-
-        if (xmlDataFormat != null) {
-            this.xmlUnmarshal = new UnmarshalProcessor(xmlDataFormat);
-        } else {
-            this.xmlUnmarshal = null;
-        }
-        if (outXmlDataFormat != null) {
-            this.xmlMarshal = new MarshalProcessor(outXmlDataFormat);
-        } else if (xmlDataFormat != null) {
-            this.xmlMarshal = new MarshalProcessor(xmlDataFormat);
-        } else {
-            this.xmlMarshal = null;
-        }
-
-        this.consumes = consumes;
-        this.produces = produces;
-        this.bindingMode = bindingMode;
-        this.skipBindingOnErrorCode = skipBindingOnErrorCode;
-        this.enableCORS = enableCORS;
-        this.corsHeaders = corsHeaders;
-        this.queryDefaultValues = queryDefaultValues;
-    }
-
-    @Override
-    public void process(Exchange exchange) throws Exception {
-        AsyncProcessorHelper.process(this, exchange);
-    }
-
-    @Override
-    public boolean process(Exchange exchange, final AsyncCallback callback) {
-        if (enableCORS) {
-            exchange.addOnCompletion(new RestConsumerBindingCORSOnCompletion(corsHeaders));
-        }
-
-        String method = exchange.getIn().getHeader(Exchange.HTTP_METHOD, String.class);
-        if ("OPTIONS".equalsIgnoreCase(method)) {
-            // for OPTIONS methods then we should not route at all as its part of CORS
-            exchange.setProperty(Exchange.ROUTE_STOP, true);
-            callback.done(true);
-            return true;
-        }
-
-        boolean isXml = false;
-        boolean isJson = false;
-
-        String contentType = ExchangeHelper.getContentType(exchange);
-        if (contentType != null) {
-            isXml = contentType.toLowerCase(Locale.ENGLISH).contains("xml");
-            isJson = contentType.toLowerCase(Locale.ENGLISH).contains("json");
-        }
-        // if content type could not tell us if it was json or xml, then fallback to if the binding was configured with
-        // that information in the consumes
-        if (!isXml && !isJson) {
-            isXml = consumes != null && consumes.toLowerCase(Locale.ENGLISH).contains("xml");
-            isJson = consumes != null && consumes.toLowerCase(Locale.ENGLISH).contains("json");
-        }
-
-        // only allow xml/json if the binding mode allows that
-        isXml &= bindingMode.equals("auto") || bindingMode.contains("xml");
-        isJson &= bindingMode.equals("auto") || bindingMode.contains("json");
-
-        // if we do not yet know if its xml or json, then use the binding mode to know the mode
-        if (!isJson && !isXml) {
-            isXml = bindingMode.equals("auto") || bindingMode.contains("xml");
-            isJson = bindingMode.equals("auto") || bindingMode.contains("json");
-        }
-
-        String accept = exchange.getIn().getHeader("Accept", String.class);
-
-        String body = null;
-        if (exchange.getIn().getBody() != null) {
-
-            // okay we have a binding mode, so need to check for empty body as that can cause the marshaller to fail
-            // as they assume a non-empty body
-            if (isXml || isJson) {
-                // we have binding enabled, so we need to know if there body is empty or not
-                // so force reading the body as a String which we can work with
-                body = MessageHelper.extractBodyAsString(exchange.getIn());
-                if (body != null) {
-                    exchange.getIn().setBody(body);
-
-                    if (isXml && isJson) {
-                        // we have still not determined between xml or json, so check the body if its xml based or not
-                        isXml = body.startsWith("<");
-                        isJson = !isXml;
-                    }
-                }
-            }
-        }
-
-        // add missing default values which are mapped as headers
-        if (queryDefaultValues != null) {
-            for (Map.Entry<String, String> entry : queryDefaultValues.entrySet()) {
-                if (exchange.getIn().getHeader(entry.getKey()) == null) {
-                    exchange.getIn().setHeader(entry.getKey(), entry.getValue());
-                }
-            }
-        }
-
-        // favor json over xml
-        if (isJson && jsonUnmarshal != null) {
-            // add reverse operation
-            exchange.addOnCompletion(new RestConsumerBindingMarshalOnCompletion(exchange.getFromRouteId(), jsonMarshal, xmlMarshal, false, accept));
-            if (ObjectHelper.isNotEmpty(body)) {
-                return jsonUnmarshal.process(exchange, callback);
-            } else {
-                callback.done(true);
-                return true;
-            }
-        } else if (isXml && xmlUnmarshal != null) {
-            // add reverse operation
-            exchange.addOnCompletion(new RestConsumerBindingMarshalOnCompletion(exchange.getFromRouteId(), jsonMarshal, xmlMarshal, true, accept));
-            if (ObjectHelper.isNotEmpty(body)) {
-                return xmlUnmarshal.process(exchange, callback);
-            } else {
-                callback.done(true);
-                return true;
-            }
-        }
-
-        // we could not bind
-        if ("off".equals(bindingMode) || bindingMode.equals("auto")) {
-            // okay for auto we do not mind if we could not bind
-            exchange.addOnCompletion(new RestConsumerBindingMarshalOnCompletion(exchange.getFromRouteId(), jsonMarshal, xmlMarshal, false, accept));
-            callback.done(true);
-            return true;
-        } else {
-            if (bindingMode.contains("xml")) {
-                exchange.setException(new BindingException("Cannot bind to xml as message body is not xml compatible", exchange));
-            } else {
-                exchange.setException(new BindingException("Cannot bind to json as message body is not json compatible", exchange));
-            }
-            callback.done(true);
-            return true;
-        }
-    }
-
-    @Override
-    public String toString() {
-        return "RestConsumerBindingProcessor";
-    }
-
-    @Override
-    protected void doStart() throws Exception {
-        // inject CamelContext before starting
-        if (jsonMarshal instanceof CamelContextAware) {
-            ((CamelContextAware) jsonMarshal).setCamelContext(camelContext);
-        }
-        if (jsonUnmarshal instanceof CamelContextAware) {
-            ((CamelContextAware) jsonUnmarshal).setCamelContext(camelContext);
-        }
-        if (xmlMarshal instanceof CamelContextAware) {
-            ((CamelContextAware) xmlMarshal).setCamelContext(camelContext);
-        }
-        if (xmlUnmarshal instanceof CamelContextAware) {
-            ((CamelContextAware) xmlUnmarshal).setCamelContext(camelContext);
-        }
-        ServiceHelper.startServices(jsonMarshal, jsonUnmarshal, xmlMarshal, xmlUnmarshal);
-    }
-
-    @Override
-    protected void doStop() throws Exception {
-        ServiceHelper.stopServices(jsonMarshal, jsonUnmarshal, xmlMarshal, xmlUnmarshal);
-    }
-
-    /**
-     * An {@link org.apache.camel.spi.Synchronization} that does the reverse operation
-     * of marshalling from POJO to json/xml
-     */
-    private final class RestConsumerBindingMarshalOnCompletion extends SynchronizationAdapter {
-
-        private final AsyncProcessor jsonMarshal;
-        private final AsyncProcessor xmlMarshal;
-        private final String routeId;
-        private boolean wasXml;
-        private String accept;
-
-        private RestConsumerBindingMarshalOnCompletion(String routeId, AsyncProcessor jsonMarshal, AsyncProcessor xmlMarshal, boolean wasXml, String accept) {
-            this.routeId = routeId;
-            this.jsonMarshal = jsonMarshal;
-            this.xmlMarshal = xmlMarshal;
-            this.wasXml = wasXml;
-            this.accept = accept;
-        }
-
-        @Override
-        public void onAfterRoute(Route route, Exchange exchange) {
-            // we use the onAfterRoute callback, to ensure the data has been marshalled before
-            // the consumer writes the response back
-
-            // only trigger when it was the 1st route that was done
-            if (!routeId.equals(route.getId())) {
-                return;
-            }
-
-            // only marshal if there was no exception
-            if (exchange.getException() != null) {
-                return;
-            }
-
-            if (skipBindingOnErrorCode) {
-                Integer code = exchange.hasOut() ? exchange.getOut().getHeader(Exchange.HTTP_RESPONSE_CODE, Integer.class) : exchange.getIn().getHeader(Exchange.HTTP_RESPONSE_CODE, Integer.class);
-                // if there is a custom http error code then skip binding
-                if (code != null && code >= 300) {
-                    return;
-                }
-            }
-
-            boolean isXml = false;
-            boolean isJson = false;
-
-            // accept takes precedence
-            if (accept != null) {
-                isXml = accept.toLowerCase(Locale.ENGLISH).contains("xml");
-                isJson = accept.toLowerCase(Locale.ENGLISH).contains("json");
-            }
-            // fallback to content type if still undecided
-            if (!isXml && !isJson) {
-                String contentType = ExchangeHelper.getContentType(exchange);
-                if (contentType != null) {
-                    isXml = contentType.toLowerCase(Locale.ENGLISH).contains("xml");
-                    isJson = contentType.toLowerCase(Locale.ENGLISH).contains("json");
-                }
-            }
-            // if content type could not tell us if it was json or xml, then fallback to if the binding was configured with
-            // that information in the consumes
-            if (!isXml && !isJson) {
-                isXml = produces != null && produces.toLowerCase(Locale.ENGLISH).contains("xml");
-                isJson = produces != null && produces.toLowerCase(Locale.ENGLISH).contains("json");
-            }
-
-            // only allow xml/json if the binding mode allows that (when off we still want to know if its xml or json)
-            if (bindingMode != null) {
-                isXml &= bindingMode.equals("off") || bindingMode.equals("auto") || bindingMode.contains("xml");
-                isJson &= bindingMode.equals("off") || bindingMode.equals("auto") || bindingMode.contains("json");
-
-                // if we do not yet know if its xml or json, then use the binding mode to know the mode
-                if (!isJson && !isXml) {
-                    isXml = bindingMode.equals("auto") || bindingMode.contains("xml");
-                    isJson = bindingMode.equals("auto") || bindingMode.contains("json");
-                }
-            }
-
-            // in case we have not yet been able to determine if xml or json, then use the same as in the unmarshaller
-            if (isXml && isJson) {
-                isXml = wasXml;
-                isJson = !wasXml;
-            }
-
-            // need to prepare exchange first
-            ExchangeHelper.prepareOutToIn(exchange);
-
-            // ensure there is a content type header (even if binding is off)
-            ensureHeaderContentType(produces, isXml, isJson, exchange);
-
-            if (bindingMode == null || "off".equals(bindingMode)) {
-                // binding is off, so no message body binding
-                return;
-            }
-
-            // is there any marshaller at all
-            if (jsonMarshal == null && xmlMarshal == null) {
-                return;
-            }
-
-            // is the body empty
-            if ((exchange.hasOut() && exchange.getOut().getBody() == null) || (!exchange.hasOut() && exchange.getIn().getBody() == null)) {
-                return;
-            }
-
-            String contentType = exchange.getIn().getHeader(Exchange.CONTENT_TYPE, String.class);
-            // need to lower-case so the contains check below can match if using upper case
-            contentType = contentType.toLowerCase(Locale.US);
-            try {
-                // favor json over xml
-                if (isJson && jsonMarshal != null) {
-                    // only marshal if its json content type
-                    if (contentType.contains("json")) {
-                        jsonMarshal.process(exchange);
-                    }
-                } else if (isXml && xmlMarshal != null) {
-                    // only marshal if its xml content type
-                    if (contentType.contains("xml")) {
-                        xmlMarshal.process(exchange);
-                    }
-                } else {
-                    // we could not bind
-                    if (bindingMode.equals("auto")) {
-                        // okay for auto we do not mind if we could not bind
-                    } else {
-                        if (bindingMode.contains("xml")) {
-                            exchange.setException(new BindingException("Cannot bind to xml as message body is not xml compatible", exchange));
-                        } else {
-                            exchange.setException(new BindingException("Cannot bind to json as message body is not json compatible", exchange));
-                        }
-                    }
-                }
-            } catch (Throwable e) {
-                exchange.setException(e);
-            }
-        }
-
-        private void ensureHeaderContentType(String contentType, boolean isXml, boolean isJson, Exchange exchange) {
-            // favor given content type
-            if (contentType != null) {
-                String type = ExchangeHelper.getContentType(exchange);
-                if (type == null) {
-                    exchange.getIn().setHeader(Exchange.CONTENT_TYPE, contentType);
-                }
-            }
-
-            // favor json over xml
-            if (isJson) {
-                // make sure there is a content-type with json
-                String type = ExchangeHelper.getContentType(exchange);
-                if (type == null) {
-                    exchange.getIn().setHeader(Exchange.CONTENT_TYPE, "application/json");
-                }
-            } else if (isXml) {
-                // make sure there is a content-type with xml
-                String type = ExchangeHelper.getContentType(exchange);
-                if (type == null) {
-                    exchange.getIn().setHeader(Exchange.CONTENT_TYPE, "application/xml");
-                }
-            }
-        }
-
-        @Override
-        public String toString() {
-            return "RestConsumerBindingMarshalOnCompletion";
-        }
-    }
-
-    private final class RestConsumerBindingCORSOnCompletion extends SynchronizationAdapter {
-
-        private final Map<String, String> corsHeaders;
-
-        private RestConsumerBindingCORSOnCompletion(Map<String, String> corsHeaders) {
-            this.corsHeaders = corsHeaders;
-        }
-
-        @Override
-        public void onAfterRoute(Route route, Exchange exchange) {
-            // add the CORS headers after routing, but before the consumer writes the response
-            Message msg = exchange.hasOut() ? exchange.getOut() : exchange.getIn();
-
-            // use default value if none has been configured
-            String allowOrigin = corsHeaders != null ? corsHeaders.get("Access-Control-Allow-Origin") : null;
-            if (allowOrigin == null) {
-                allowOrigin = RestConfiguration.CORS_ACCESS_CONTROL_ALLOW_ORIGIN;
-            }
-            String allowMethods = corsHeaders != null ? corsHeaders.get("Access-Control-Allow-Methods") : null;
-            if (allowMethods == null) {
-                allowMethods = RestConfiguration.CORS_ACCESS_CONTROL_ALLOW_METHODS;
-            }
-            String allowHeaders = corsHeaders != null ? corsHeaders.get("Access-Control-Allow-Headers") : null;
-            if (allowHeaders == null) {
-                allowHeaders = RestConfiguration.CORS_ACCESS_CONTROL_ALLOW_HEADERS;
-            }
-            String maxAge = corsHeaders != null ? corsHeaders.get("Access-Control-Max-Age") : null;
-            if (maxAge == null) {
-                maxAge = RestConfiguration.CORS_ACCESS_CONTROL_MAX_AGE;
-            }
-
-            msg.setHeader("Access-Control-Allow-Origin", allowOrigin);
-            msg.setHeader("Access-Control-Allow-Methods", allowMethods);
-            msg.setHeader("Access-Control-Allow-Headers", allowHeaders);
-            msg.setHeader("Access-Control-Max-Age", maxAge);
-        }
-
-        @Override
-        public String toString() {
-            return "RestConsumerBindingCORSOnCompletion";
-        }
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/camel/blob/8c9184b6/camel-core/src/main/java/org/apache/camel/impl/DefaultRouteContext.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/impl/DefaultRouteContext.java b/camel-core/src/main/java/org/apache/camel/impl/DefaultRouteContext.java
index 328dae9..fbb3673 100644
--- a/camel-core/src/main/java/org/apache/camel/impl/DefaultRouteContext.java
+++ b/camel-core/src/main/java/org/apache/camel/impl/DefaultRouteContext.java
@@ -37,6 +37,7 @@ import org.apache.camel.model.RouteDefinition;
 import org.apache.camel.processor.CamelInternalProcessor;
 import org.apache.camel.processor.ContractAdvice;
 import org.apache.camel.processor.Pipeline;
+import org.apache.camel.processor.RestBindingAdvice;
 import org.apache.camel.spi.Contract;
 import org.apache.camel.spi.InterceptStrategy;
 import org.apache.camel.spi.RouteContext;
@@ -70,7 +71,6 @@ public class DefaultRouteContext implements RouteContext {
     private List<RoutePolicy> routePolicyList = new ArrayList<RoutePolicy>();
     private ShutdownRoute shutdownRoute;
     private ShutdownRunningTask shutdownRunningTask;
-    private Contract contract;
 
     public DefaultRouteContext(CamelContext camelContext, RouteDefinition route, FromDefinition from, Collection<Route> routes) {
         this.camelContext = camelContext;
@@ -194,8 +194,24 @@ public class DefaultRouteContext implements RouteContext {
             // wrap in route lifecycle
             internal.addAdvice(new CamelInternalProcessor.RouteLifecycleAdvice());
 
+            // wrap in REST binding
+            if (route.getRestBindingDefinition() != null) {
+                try {
+                    internal.addAdvice(route.getRestBindingDefinition().createRestBindingAdvice(this));
+                } catch (Exception e) {
+                    throw ObjectHelper.wrapRuntimeCamelException(e);
+                }
+            }
+
             // wrap in contract
-            if (contract != null) {
+            if (route.getInputType() != null || route.getOutputType() != null) {
+                Contract contract = new Contract();
+                if (route.getInputType() != null) {
+                    contract.setInputType(route.getInputType().getUrn());
+                }
+                if (route.getOutputType() != null) {
+                    contract.setOutputType(route.getOutputType().getUrn());
+                }
                 internal.addAdvice(new ContractAdvice(contract));
             }
 
@@ -409,7 +425,4 @@ public class DefaultRouteContext implements RouteContext {
         return routePolicyList;
     }
 
-    public void setContract(Contract contract) {
-        this.contract = contract;
-    }
 }

http://git-wip-us.apache.org/repos/asf/camel/blob/8c9184b6/camel-core/src/main/java/org/apache/camel/model/RouteDefinition.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/model/RouteDefinition.java b/camel-core/src/main/java/org/apache/camel/model/RouteDefinition.java
index 76b4ec3..97078dd 100644
--- a/camel-core/src/main/java/org/apache/camel/model/RouteDefinition.java
+++ b/camel-core/src/main/java/org/apache/camel/model/RouteDefinition.java
@@ -44,6 +44,7 @@ import org.apache.camel.builder.AdviceWithTask;
 import org.apache.camel.builder.ErrorHandlerBuilderRef;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.impl.DefaultRouteContext;
+import org.apache.camel.model.rest.RestBindingDefinition;
 import org.apache.camel.model.rest.RestDefinition;
 import org.apache.camel.processor.interceptor.HandleFault;
 import org.apache.camel.spi.Contract;
@@ -88,6 +89,7 @@ public class RouteDefinition extends ProcessorDefinition<RouteDefinition> {
     private boolean contextScopedErrorHandler = true;
     private Boolean rest;
     private RestDefinition restDefinition;
+    private RestBindingDefinition restBindingDefinition;
     private InputTypeDefinition inputType;
     private OutputTypeDefinition outputType;
 
@@ -966,6 +968,15 @@ public class RouteDefinition extends ProcessorDefinition<RouteDefinition> {
         this.restDefinition = restDefinition;
     }
 
+    public RestBindingDefinition getRestBindingDefinition() {
+        return restBindingDefinition;
+    }
+
+    @XmlTransient
+    public void setRestBindingDefinition(RestBindingDefinition restBindingDefinition) {
+        this.restBindingDefinition = restBindingDefinition;
+    }
+
     @SuppressWarnings("deprecation")
     public boolean isContextScopedErrorHandler(CamelContext context) {
         if (!contextScopedErrorHandler) {
@@ -1127,18 +1138,6 @@ public class RouteDefinition extends ProcessorDefinition<RouteDefinition> {
             throw new FailedToCreateRouteException(route.getId(), route.toString(), at, cause);
         }
 
-        // add data type contract
-        if (inputType != null || outputType != null) {
-            Contract contract = new Contract();
-            if (inputType != null) {
-                contract.setInputType(inputType.getUrn());
-            }
-            if (outputType != null) {
-                contract.setOutputType(outputType.getUrn());
-            }
-            routeContext.setContract(contract);
-        }
-
         List<ProcessorDefinition<?>> list = new ArrayList<ProcessorDefinition<?>>(outputs);
         for (ProcessorDefinition<?> output : list) {
             try {

http://git-wip-us.apache.org/repos/asf/camel/blob/8c9184b6/camel-core/src/main/java/org/apache/camel/model/rest/RestBindingDefinition.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/model/rest/RestBindingDefinition.java b/camel-core/src/main/java/org/apache/camel/model/rest/RestBindingDefinition.java
index a376442..8c1c291 100644
--- a/camel-core/src/main/java/org/apache/camel/model/rest/RestBindingDefinition.java
+++ b/camel-core/src/main/java/org/apache/camel/model/rest/RestBindingDefinition.java
@@ -27,8 +27,8 @@ import javax.xml.bind.annotation.XmlTransient;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.Processor;
-import org.apache.camel.component.rest.RestConsumerBindingProcessor;
-import org.apache.camel.model.NoOutputDefinition;
+import org.apache.camel.model.OptionalIdentifiedDefinition;
+import org.apache.camel.processor.RestBindingAdvice;
 import org.apache.camel.spi.DataFormat;
 import org.apache.camel.spi.Metadata;
 import org.apache.camel.spi.RestConfiguration;
@@ -42,7 +42,7 @@ import org.apache.camel.util.IntrospectionSupport;
 @Metadata(label = "rest")
 @XmlRootElement(name = "restBinding")
 @XmlAccessorType(XmlAccessType.FIELD)
-public class RestBindingDefinition extends NoOutputDefinition<RestBindingDefinition> {
+public class RestBindingDefinition extends OptionalIdentifiedDefinition<RestBindingDefinition> {
 
     @XmlTransient
     private Map<String, String> defaultValues;
@@ -80,8 +80,7 @@ public class RestBindingDefinition extends NoOutputDefinition<RestBindingDefinit
         return "RestBinding";
     }
 
-    @Override
-    public Processor createProcessor(RouteContext routeContext) throws Exception {
+    public RestBindingAdvice createRestBindingAdvice(RouteContext routeContext) throws Exception {
 
         CamelContext context = routeContext.getCamelContext();
         RestConfiguration config = context.getRestConfiguration(component, true);
@@ -105,7 +104,7 @@ public class RestBindingDefinition extends NoOutputDefinition<RestBindingDefinit
 
         if (mode == null || "off".equals(mode)) {
             // binding mode is off, so create a off mode binding processor
-            return new RestConsumerBindingProcessor(context, null, null, null, null, consumes, produces, mode, skip, cors, corsHeaders, defaultValues);
+            return new RestBindingAdvice(context, null, null, null, null, consumes, produces, mode, skip, cors, corsHeaders, defaultValues);
         }
 
         // setup json data format
@@ -200,7 +199,7 @@ public class RestBindingDefinition extends NoOutputDefinition<RestBindingDefinit
             setAdditionalConfiguration(config, context, outJaxb, "xml.out.");
         }
 
-        return new RestConsumerBindingProcessor(context, json, jaxb, outJson, outJaxb, consumes, produces, mode, skip, cors, corsHeaders, defaultValues);
+        return new RestBindingAdvice(context, json, jaxb, outJson, outJaxb, consumes, produces, mode, skip, cors, corsHeaders, defaultValues);
     }
 
     private void setAdditionalConfiguration(RestConfiguration config, CamelContext context,
@@ -351,4 +350,9 @@ public class RestBindingDefinition extends NoOutputDefinition<RestBindingDefinit
     public void setEnableCORS(Boolean enableCORS) {
         this.enableCORS = enableCORS;
     }
+
+    @Override
+    public String getLabel() {
+        return "";
+    }
 }

http://git-wip-us.apache.org/repos/asf/camel/blob/8c9184b6/camel-core/src/main/java/org/apache/camel/model/rest/RestDefinition.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/model/rest/RestDefinition.java b/camel-core/src/main/java/org/apache/camel/model/rest/RestDefinition.java
index 68deb66..3cd6b2c 100644
--- a/camel-core/src/main/java/org/apache/camel/model/rest/RestDefinition.java
+++ b/camel-core/src/main/java/org/apache/camel/model/rest/RestDefinition.java
@@ -736,7 +736,7 @@ public class RestDefinition extends OptionalIdentifiedDefinition<RestDefinition>
                 }
             }
 
-            route.getOutputs().add(0, binding);
+            route.setRestBindingDefinition(binding);
 
             // create the from endpoint uri which is using the rest component
             String from = "rest:" + verb.asVerb() + ":" + buildUri(verb);

http://git-wip-us.apache.org/repos/asf/camel/blob/8c9184b6/camel-core/src/main/java/org/apache/camel/processor/ContractAdvice.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/processor/ContractAdvice.java b/camel-core/src/main/java/org/apache/camel/processor/ContractAdvice.java
index 15a3d61..a3dad33 100644
--- a/camel-core/src/main/java/org/apache/camel/processor/ContractAdvice.java
+++ b/camel-core/src/main/java/org/apache/camel/processor/ContractAdvice.java
@@ -1,3 +1,19 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 package org.apache.camel.processor;
 
 import org.apache.camel.CamelContext;
@@ -40,12 +56,15 @@ public class ContractAdvice implements CamelInternalProcessorAdvice {
     @Override
     public void after(Exchange exchange, Object data) throws Exception {
         Message target = exchange.hasOut() ? exchange.getOut() : exchange.getIn();
-        DataType from = getCurrentType(exchange, exchange.hasOut() ? Exchange.OUTPUT_TYPE : Exchange.INPUT_TYPE);
+        if (!exchange.hasOut() && exchange.getProperty(Exchange.OUTPUT_TYPE) == null) {
+            exchange.setProperty(Exchange.OUTPUT_TYPE, exchange.getProperty(Exchange.INPUT_TYPE));
+        }
+        DataType from = getCurrentType(exchange, Exchange.OUTPUT_TYPE);
         DataType to = contract.getOutputType();
         if (to != null && !to.equals(from)) {
             LOG.debug("Looking for transformer for OUTPUT: from='{}', to='{}'", from, to);
             doTransform(target, from, to);
-            exchange.setProperty(exchange.hasOut() ? Exchange.OUTPUT_TYPE : Exchange.INPUT_TYPE, to);
+            exchange.setProperty(Exchange.OUTPUT_TYPE, to);
         }
     }
     
@@ -61,7 +80,8 @@ public class ContractAdvice implements CamelInternalProcessorAdvice {
                 // Java->Java transformation just relies on TypeConverter if no explicit transformer
                 return;
             } else if (from == null) {
-                // {undefined}->Other transformation - assuming it's already in expected shape
+                // use body class as a from type, or do nothing with assuming it's already in expected shape
+                applyTransformerByClass(message, to);
                 return;
             } else if (applyTransformerByToModel(message, from, to)) {
                 // Java->Other transformation - found a transformer supports 'to' data model
@@ -85,8 +105,8 @@ public class ContractAdvice implements CamelInternalProcessorAdvice {
     private boolean convertIfRequired(Message message, DataType type) throws Exception {
         // TODO for better performance it may be better to add TypeConveterTransformer
         // into transformer registry automatically to avoid unnecessary scan in transformer registry
-        CamelContext context = message.getExchange().getContext();
         if (type != null && type.isJavaType() && type.getName() != null) {
+            CamelContext context = message.getExchange().getContext();
             Class<?> typeJava = getClazz(type.getName(), context);
             if (!typeJava.isAssignableFrom(message.getBody().getClass())) {
                 LOG.debug("Converting to '{}'", typeJava.getName());
@@ -110,6 +130,12 @@ public class ContractAdvice implements CamelInternalProcessorAdvice {
         return applyTransformer(transformer, message, from, to);
     }
     
+    private boolean applyTransformerByClass(Message message, DataType to) throws Exception {
+        DataType from = new DataType(message.getBody().getClass());
+        Transformer transformer = message.getExchange().getContext().resolveTransformer(from, to);
+        return applyTransformer(transformer, message, from, to);
+    }
+    
     private boolean applyTransformerByToModel(Message message, DataType from, DataType to) throws Exception {
         Transformer transformer = message.getExchange().getContext().resolveTransformer(to.getModel());
         return applyTransformer(transformer, message, from, to);

http://git-wip-us.apache.org/repos/asf/camel/blob/8c9184b6/camel-core/src/main/java/org/apache/camel/processor/RestBindingAdvice.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/processor/RestBindingAdvice.java b/camel-core/src/main/java/org/apache/camel/processor/RestBindingAdvice.java
new file mode 100644
index 0000000..7b81a19
--- /dev/null
+++ b/camel-core/src/main/java/org/apache/camel/processor/RestBindingAdvice.java
@@ -0,0 +1,418 @@
+/**
+ * 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.processor;
+
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.camel.AsyncProcessor;
+import org.apache.camel.CamelContext;
+import org.apache.camel.CamelContextAware;
+import org.apache.camel.Exchange;
+import org.apache.camel.Message;
+import org.apache.camel.processor.binding.BindingException;
+import org.apache.camel.spi.Contract;
+import org.apache.camel.spi.DataFormat;
+import org.apache.camel.spi.DataType;
+import org.apache.camel.spi.RestConfiguration;
+import org.apache.camel.spi.Transformer;
+import org.apache.camel.util.ExchangeHelper;
+import org.apache.camel.util.MessageHelper;
+import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.ServiceHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A {@link org.apache.camel.processor.CamelInternalProcessorAdvice} that binds the REST DSL incoming
+ * and outgoing messages from sources of json or xml to Java Objects.
+ * <p/>
+ * The binding uses {@link org.apache.camel.spi.DataFormat} for the actual work to transform
+ * from xml/json to Java Objects and reverse again.
+ * 
+ * @see CamelInternalProcessor, CamelInternalProcessorAdvice
+ */
+public class RestBindingAdvice implements CamelInternalProcessorAdvice<Map<String, Object>> {
+    private static final String STATE_KEY_DO_MARSHAL = "doMarshal";
+    private static final String STATE_KEY_ACCEPT = "accept";
+    private static final String STATE_JSON = "json";
+    private static final String STATE_XML = "xml";
+
+    private final AsyncProcessor jsonUnmarshal;
+    private final AsyncProcessor xmlUnmarshal;
+    private final AsyncProcessor jsonMarshal;
+    private final AsyncProcessor xmlMarshal;
+    private final String consumes;
+    private final String produces;
+    private final String bindingMode;
+    private final boolean skipBindingOnErrorCode;
+    private final boolean enableCORS;
+    private final Map<String, String> corsHeaders;
+    private final Map<String, String> queryDefaultValues;
+
+    public RestBindingAdvice(CamelContext camelContext, DataFormat jsonDataFormat, DataFormat xmlDataFormat,
+                             DataFormat outJsonDataFormat, DataFormat outXmlDataFormat,
+                             String consumes, String produces, String bindingMode,
+                             boolean skipBindingOnErrorCode, boolean enableCORS,
+                             Map<String, String> corsHeaders,
+                             Map<String, String> queryDefaultValues) throws Exception {
+
+        if (jsonDataFormat != null) {
+            this.jsonUnmarshal = new UnmarshalProcessor(jsonDataFormat);
+        } else {
+            this.jsonUnmarshal = null;
+        }
+        if (outJsonDataFormat != null) {
+            this.jsonMarshal = new MarshalProcessor(outJsonDataFormat);
+        } else if (jsonDataFormat != null) {
+            this.jsonMarshal = new MarshalProcessor(jsonDataFormat);
+        } else {
+            this.jsonMarshal = null;
+        }
+
+        if (xmlDataFormat != null) {
+            this.xmlUnmarshal = new UnmarshalProcessor(xmlDataFormat);
+        } else {
+            this.xmlUnmarshal = null;
+        }
+        if (outXmlDataFormat != null) {
+            this.xmlMarshal = new MarshalProcessor(outXmlDataFormat);
+        } else if (xmlDataFormat != null) {
+            this.xmlMarshal = new MarshalProcessor(xmlDataFormat);
+        } else {
+            this.xmlMarshal = null;
+        }
+
+        if (jsonMarshal != null) {
+            camelContext.addService(jsonMarshal, true);
+        }
+        if (jsonUnmarshal != null) {
+            camelContext.addService(jsonUnmarshal, true);
+        }
+        if (xmlMarshal instanceof CamelContextAware) {
+            camelContext.addService(xmlMarshal, true);
+        }
+        if (xmlUnmarshal instanceof CamelContextAware) {
+            camelContext.addService(xmlUnmarshal, true);
+        }
+
+        this.consumes = consumes;
+        this.produces = produces;
+        this.bindingMode = bindingMode;
+        this.skipBindingOnErrorCode = skipBindingOnErrorCode;
+        this.enableCORS = enableCORS;
+        this.corsHeaders = corsHeaders;
+        this.queryDefaultValues = queryDefaultValues;
+
+    }
+    
+    @Override
+    public Map<String, Object> before(Exchange exchange) throws Exception {
+        Map<String, Object> state = new HashMap<>();
+        if (isOptionsMethod(exchange, state)) {
+            return state;
+        }
+        unmarshal(exchange, state);
+        return state;
+    }
+    
+    @Override
+    public void after(Exchange exchange, Map<String, Object> state) throws Exception {
+        if (enableCORS) {
+            setCORSHeaders(exchange, state);
+        }
+        if (state.get(STATE_KEY_DO_MARSHAL) != null) {
+            marshal(exchange, state);
+        }
+    }
+
+    private boolean isOptionsMethod(Exchange exchange, Map<String, Object> state) {
+        String method = exchange.getIn().getHeader(Exchange.HTTP_METHOD, String.class);
+        if ("OPTIONS".equalsIgnoreCase(method)) {
+            // for OPTIONS methods then we should not route at all as its part of CORS
+            exchange.setProperty(Exchange.ROUTE_STOP, true);
+            return true;
+        }
+        return false;
+    }
+
+    private void unmarshal(Exchange exchange, Map<String, Object> state) throws Exception {
+        boolean isXml = false;
+        boolean isJson = false;
+
+        String contentType = ExchangeHelper.getContentType(exchange);
+        if (contentType != null) {
+            isXml = contentType.toLowerCase(Locale.ENGLISH).contains("xml");
+            isJson = contentType.toLowerCase(Locale.ENGLISH).contains("json");
+        }
+        // if content type could not tell us if it was json or xml, then fallback to if the binding was configured with
+        // that information in the consumes
+        if (!isXml && !isJson) {
+            isXml = consumes != null && consumes.toLowerCase(Locale.ENGLISH).contains("xml");
+            isJson = consumes != null && consumes.toLowerCase(Locale.ENGLISH).contains("json");
+        }
+
+        // set the INPUT_TYPE to indicate body type
+        if (isJson) {
+            exchange.setProperty(Exchange.INPUT_TYPE, new DataType("json"));
+        } else if (isXml) {
+            exchange.setProperty(Exchange.INPUT_TYPE, new DataType("xml"));
+        }
+
+        // only allow xml/json if the binding mode allows that
+        isXml &= bindingMode.equals("auto") || bindingMode.contains("xml");
+        isJson &= bindingMode.equals("auto") || bindingMode.contains("json");
+
+        // if we do not yet know if its xml or json, then use the binding mode to know the mode
+        if (!isJson && !isXml) {
+            isXml = bindingMode.equals("auto") || bindingMode.contains("xml");
+            isJson = bindingMode.equals("auto") || bindingMode.contains("json");
+        }
+
+        state.put(STATE_KEY_ACCEPT, exchange.getIn().getHeader("Accept", String.class));
+
+        String body = null;
+        if (exchange.getIn().getBody() != null) {
+
+            // okay we have a binding mode, so need to check for empty body as that can cause the marshaller to fail
+            // as they assume a non-empty body
+            if (isXml || isJson) {
+                // we have binding enabled, so we need to know if there body is empty or not
+                // so force reading the body as a String which we can work with
+                body = MessageHelper.extractBodyAsString(exchange.getIn());
+                if (body != null) {
+                    exchange.getIn().setBody(body);
+
+                    if (isXml && isJson) {
+                        // we have still not determined between xml or json, so check the body if its xml based or not
+                        isXml = body.startsWith("<");
+                        isJson = !isXml;
+                    }
+                }
+            }
+        }
+
+        // add missing default values which are mapped as headers
+        if (queryDefaultValues != null) {
+            for (Map.Entry<String, String> entry : queryDefaultValues.entrySet()) {
+                if (exchange.getIn().getHeader(entry.getKey()) == null) {
+                    exchange.getIn().setHeader(entry.getKey(), entry.getValue());
+                }
+            }
+        }
+
+        // favor json over xml
+        if (isJson && jsonUnmarshal != null) {
+            // add reverse operation
+            state.put(STATE_KEY_DO_MARSHAL, STATE_JSON);
+            if (ObjectHelper.isNotEmpty(body)) {
+                jsonUnmarshal.process(exchange);
+                ExchangeHelper.prepareOutToIn(exchange);
+                exchange.setProperty(Exchange.INPUT_TYPE, new DataType(exchange.getIn().getBody().getClass()));
+            }
+            return;
+        } else if (isXml && xmlUnmarshal != null) {
+            // add reverse operation
+            state.put(STATE_KEY_DO_MARSHAL, STATE_XML);
+            if (ObjectHelper.isNotEmpty(body)) {
+                xmlUnmarshal.process(exchange);
+                ExchangeHelper.prepareOutToIn(exchange);
+                exchange.setProperty(Exchange.INPUT_TYPE, new DataType(exchange.getIn().getBody().getClass()));
+            }
+            return;
+        }
+
+        // we could not bind
+        if ("off".equals(bindingMode) || bindingMode.equals("auto")) {
+            // okay for auto we do not mind if we could not bind
+            state.put(STATE_KEY_DO_MARSHAL, STATE_JSON);
+        } else {
+            if (bindingMode.contains("xml")) {
+                exchange.setException(new BindingException("Cannot bind to xml as message body is not xml compatible", exchange));
+            } else {
+                exchange.setException(new BindingException("Cannot bind to json as message body is not json compatible", exchange));
+            }
+        }
+        
+    }
+
+    private void marshal(Exchange exchange, Map<String, Object> state) {
+        // only marshal if there was no exception
+        if (exchange.getException() != null) {
+            return;
+        }
+
+        if (skipBindingOnErrorCode) {
+            Integer code = exchange.hasOut() ? exchange.getOut().getHeader(Exchange.HTTP_RESPONSE_CODE, Integer.class) : exchange.getIn().getHeader(Exchange.HTTP_RESPONSE_CODE, Integer.class);
+            // if there is a custom http error code then skip binding
+            if (code != null && code >= 300) {
+                return;
+            }
+        }
+
+        boolean isXml = false;
+        boolean isJson = false;
+
+        // accept takes precedence
+        String accept = (String)state.get(STATE_KEY_ACCEPT);
+        if (accept != null) {
+            isXml = accept.toLowerCase(Locale.ENGLISH).contains("xml");
+            isJson = accept.toLowerCase(Locale.ENGLISH).contains("json");
+        }
+        // fallback to content type if still undecided
+        if (!isXml && !isJson) {
+            String contentType = ExchangeHelper.getContentType(exchange);
+            if (contentType != null) {
+                isXml = contentType.toLowerCase(Locale.ENGLISH).contains("xml");
+                isJson = contentType.toLowerCase(Locale.ENGLISH).contains("json");
+            }
+        }
+        // if content type could not tell us if it was json or xml, then fallback to if the binding was configured with
+        // that information in the consumes
+        if (!isXml && !isJson) {
+            isXml = produces != null && produces.toLowerCase(Locale.ENGLISH).contains("xml");
+            isJson = produces != null && produces.toLowerCase(Locale.ENGLISH).contains("json");
+        }
+
+        // only allow xml/json if the binding mode allows that (when off we still want to know if its xml or json)
+        if (bindingMode != null) {
+            isXml &= bindingMode.equals("off") || bindingMode.equals("auto") || bindingMode.contains("xml");
+            isJson &= bindingMode.equals("off") || bindingMode.equals("auto") || bindingMode.contains("json");
+
+            // if we do not yet know if its xml or json, then use the binding mode to know the mode
+            if (!isJson && !isXml) {
+                isXml = bindingMode.equals("auto") || bindingMode.contains("xml");
+                isJson = bindingMode.equals("auto") || bindingMode.contains("json");
+            }
+        }
+
+        // in case we have not yet been able to determine if xml or json, then use the same as in the unmarshaller
+        if (isXml && isJson) {
+            isXml = state.get(STATE_KEY_DO_MARSHAL).equals(STATE_XML);
+            isJson = !isXml;
+        }
+
+        // need to prepare exchange first
+        ExchangeHelper.prepareOutToIn(exchange);
+
+        // ensure there is a content type header (even if binding is off)
+        ensureHeaderContentType(produces, isXml, isJson, exchange);
+
+        if (bindingMode == null || "off".equals(bindingMode)) {
+            // binding is off, so no message body binding
+            return;
+        }
+
+        // is there any marshaller at all
+        if (jsonMarshal == null && xmlMarshal == null) {
+            return;
+        }
+
+        // is the body empty
+        if ((exchange.hasOut() && exchange.getOut().getBody() == null) || (!exchange.hasOut() && exchange.getIn().getBody() == null)) {
+            return;
+        }
+
+        String contentType = exchange.getIn().getHeader(Exchange.CONTENT_TYPE, String.class);
+        // need to lower-case so the contains check below can match if using upper case
+        contentType = contentType.toLowerCase(Locale.US);
+        try {
+            // favor json over xml
+            if (isJson && jsonMarshal != null) {
+                // only marshal if its json content type
+                if (contentType.contains("json")) {
+                    jsonMarshal.process(exchange);
+                    exchange.setProperty(Exchange.OUTPUT_TYPE, new DataType("json"));
+                }
+            } else if (isXml && xmlMarshal != null) {
+                // only marshal if its xml content type
+                if (contentType.contains("xml")) {
+                    xmlMarshal.process(exchange);
+                    exchange.setProperty(Exchange.OUTPUT_TYPE, new DataType("xml"));
+                }
+            } else {
+                // we could not bind
+                if (bindingMode.equals("auto")) {
+                    // okay for auto we do not mind if we could not bind
+                } else {
+                    if (bindingMode.contains("xml")) {
+                        exchange.setException(new BindingException("Cannot bind to xml as message body is not xml compatible", exchange));
+                    } else {
+                        exchange.setException(new BindingException("Cannot bind to json as message body is not json compatible", exchange));
+                    }
+                }
+            }
+        } catch (Throwable e) {
+            exchange.setException(e);
+        }
+    }
+
+    private void ensureHeaderContentType(String contentType, boolean isXml, boolean isJson, Exchange exchange) {
+        // favor given content type
+        if (contentType != null) {
+            String type = ExchangeHelper.getContentType(exchange);
+            if (type == null) {
+                exchange.getIn().setHeader(Exchange.CONTENT_TYPE, contentType);
+            }
+        }
+
+        // favor json over xml
+        if (isJson) {
+            // make sure there is a content-type with json
+            String type = ExchangeHelper.getContentType(exchange);
+            if (type == null) {
+                exchange.getIn().setHeader(Exchange.CONTENT_TYPE, "application/json");
+            }
+        } else if (isXml) {
+            // make sure there is a content-type with xml
+            String type = ExchangeHelper.getContentType(exchange);
+            if (type == null) {
+                exchange.getIn().setHeader(Exchange.CONTENT_TYPE, "application/xml");
+            }
+        }
+    }
+
+    private void setCORSHeaders(Exchange exchange, Map<String, Object> state) {
+        // add the CORS headers after routing, but before the consumer writes the response
+        Message msg = exchange.hasOut() ? exchange.getOut() : exchange.getIn();
+
+        // use default value if none has been configured
+        String allowOrigin = corsHeaders != null ? corsHeaders.get("Access-Control-Allow-Origin") : null;
+        if (allowOrigin == null) {
+            allowOrigin = RestConfiguration.CORS_ACCESS_CONTROL_ALLOW_ORIGIN;
+        }
+        String allowMethods = corsHeaders != null ? corsHeaders.get("Access-Control-Allow-Methods") : null;
+        if (allowMethods == null) {
+            allowMethods = RestConfiguration.CORS_ACCESS_CONTROL_ALLOW_METHODS;
+        }
+        String allowHeaders = corsHeaders != null ? corsHeaders.get("Access-Control-Allow-Headers") : null;
+        if (allowHeaders == null) {
+            allowHeaders = RestConfiguration.CORS_ACCESS_CONTROL_ALLOW_HEADERS;
+        }
+        String maxAge = corsHeaders != null ? corsHeaders.get("Access-Control-Max-Age") : null;
+        if (maxAge == null) {
+            maxAge = RestConfiguration.CORS_ACCESS_CONTROL_MAX_AGE;
+        }
+
+        msg.setHeader("Access-Control-Allow-Origin", allowOrigin);
+        msg.setHeader("Access-Control-Allow-Methods", allowMethods);
+        msg.setHeader("Access-Control-Allow-Headers", allowHeaders);
+        msg.setHeader("Access-Control-Max-Age", maxAge);
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/camel/blob/8c9184b6/camel-core/src/main/java/org/apache/camel/spi/DataType.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/spi/DataType.java b/camel-core/src/main/java/org/apache/camel/spi/DataType.java
index 5d96839..973e5c4 100644
--- a/camel-core/src/main/java/org/apache/camel/spi/DataType.java
+++ b/camel-core/src/main/java/org/apache/camel/spi/DataType.java
@@ -62,7 +62,7 @@ public class DataType {
     @Override
     public String toString() {
         if (this.typeString == null) {
-            this.typeString = model + ":" + name;
+            this.typeString = name != null && !name.isEmpty() ? model + ":" + name : model;
         }
         return this.typeString;
     }

http://git-wip-us.apache.org/repos/asf/camel/blob/8c9184b6/camel-core/src/main/java/org/apache/camel/spi/RouteContext.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/spi/RouteContext.java b/camel-core/src/main/java/org/apache/camel/spi/RouteContext.java
index ce57112..54e6ad0 100644
--- a/camel-core/src/main/java/org/apache/camel/spi/RouteContext.java
+++ b/camel-core/src/main/java/org/apache/camel/spi/RouteContext.java
@@ -192,9 +192,4 @@ public interface RouteContext extends RuntimeConfiguration, EndpointAware {
      */
     int getAndIncrement(ProcessorDefinition<?> node);
 
-    /**
-     * Sets a {@link Contract} which declares input/output message type on the route.
-     * @param contract {@link Contract} for this route
-     */
-    void setContract(Contract contract);
 }

http://git-wip-us.apache.org/repos/asf/camel/blob/8c9184b6/camel-core/src/test/java/org/apache/camel/component/rest/FromRestGetEmbeddedRouteTest.java
----------------------------------------------------------------------
diff --git a/camel-core/src/test/java/org/apache/camel/component/rest/FromRestGetEmbeddedRouteTest.java b/camel-core/src/test/java/org/apache/camel/component/rest/FromRestGetEmbeddedRouteTest.java
index df9fe91..353dbcc 100644
--- a/camel-core/src/test/java/org/apache/camel/component/rest/FromRestGetEmbeddedRouteTest.java
+++ b/camel-core/src/test/java/org/apache/camel/component/rest/FromRestGetEmbeddedRouteTest.java
@@ -43,7 +43,7 @@ public class FromRestGetEmbeddedRouteTest extends ContextTestSupport {
         assertNotNull(rest);
         assertEquals("/say/hello", rest.getPath());
         assertEquals(1, rest.getVerbs().size());
-        ToDefinition to = assertIsInstanceOf(ToDefinition.class, rest.getVerbs().get(0).getRoute().getOutputs().get(1));
+        ToDefinition to = assertIsInstanceOf(ToDefinition.class, rest.getVerbs().get(0).getRoute().getOutputs().get(0));
         assertEquals("mock:hello", to.getUri());
 
         rest = context.getRestDefinitions().get(1);
@@ -51,7 +51,7 @@ public class FromRestGetEmbeddedRouteTest extends ContextTestSupport {
         assertEquals("/say/bye", rest.getPath());
         assertEquals(2, rest.getVerbs().size());
         assertEquals("application/json", rest.getVerbs().get(0).getConsumes());
-        to = assertIsInstanceOf(ToDefinition.class, rest.getVerbs().get(0).getRoute().getOutputs().get(1));
+        to = assertIsInstanceOf(ToDefinition.class, rest.getVerbs().get(0).getRoute().getOutputs().get(0));
         assertEquals("mock:bye", to.getUri());
 
         // the rest becomes routes and the input is a seda endpoint created by the DummyRestConsumerFactory

http://git-wip-us.apache.org/repos/asf/camel/blob/8c9184b6/camel-core/src/test/java/org/apache/camel/component/rest/FromRestGetEndPathTest.java
----------------------------------------------------------------------
diff --git a/camel-core/src/test/java/org/apache/camel/component/rest/FromRestGetEndPathTest.java b/camel-core/src/test/java/org/apache/camel/component/rest/FromRestGetEndPathTest.java
index 14f8b8d..7b89964 100644
--- a/camel-core/src/test/java/org/apache/camel/component/rest/FromRestGetEndPathTest.java
+++ b/camel-core/src/test/java/org/apache/camel/component/rest/FromRestGetEndPathTest.java
@@ -38,7 +38,7 @@ public class FromRestGetEndPathTest extends FromRestGetTest {
         assertEquals("/say/bye", rest.getPath());
         assertEquals(2, rest.getVerbs().size());
         assertEquals("application/json", rest.getVerbs().get(0).getConsumes());
-        to = assertIsInstanceOf(ToDefinition.class, rest.getVerbs().get(0).getRoute().getOutputs().get(1));
+        to = assertIsInstanceOf(ToDefinition.class, rest.getVerbs().get(0).getRoute().getOutputs().get(0));
         assertEquals("direct:bye", to.getUri());
 
         // the rest becomes routes and the input is a seda endpoint created by the DummyRestConsumerFactory

http://git-wip-us.apache.org/repos/asf/camel/blob/8c9184b6/camel-core/src/test/java/org/apache/camel/component/rest/FromRestGetInterceptTest.java
----------------------------------------------------------------------
diff --git a/camel-core/src/test/java/org/apache/camel/component/rest/FromRestGetInterceptTest.java b/camel-core/src/test/java/org/apache/camel/component/rest/FromRestGetInterceptTest.java
index 62ed09c..6df660d 100644
--- a/camel-core/src/test/java/org/apache/camel/component/rest/FromRestGetInterceptTest.java
+++ b/camel-core/src/test/java/org/apache/camel/component/rest/FromRestGetInterceptTest.java
@@ -32,7 +32,7 @@ public class FromRestGetInterceptTest extends ContextTestSupport {
     public void testFromRestModel() throws Exception {
         getMockEndpoint("mock:hello").expectedMessageCount(1);
         getMockEndpoint("mock:bar").expectedMessageCount(1);
-        getMockEndpoint("mock:intercept").expectedMessageCount(4);
+        getMockEndpoint("mock:intercept").expectedMessageCount(3);
 
         String out = template.requestBody("seda:get-say-hello", "I was here", String.class);
         assertEquals("Bye World", out);

http://git-wip-us.apache.org/repos/asf/camel/blob/8c9184b6/camel-core/src/test/java/org/apache/camel/management/ManagedTransformerRegistryTest.java
----------------------------------------------------------------------
diff --git a/camel-core/src/test/java/org/apache/camel/management/ManagedTransformerRegistryTest.java b/camel-core/src/test/java/org/apache/camel/management/ManagedTransformerRegistryTest.java
index f79b492..96918a6 100644
--- a/camel-core/src/test/java/org/apache/camel/management/ManagedTransformerRegistryTest.java
+++ b/camel-core/src/test/java/org/apache/camel/management/ManagedTransformerRegistryTest.java
@@ -107,8 +107,8 @@ public class ManagedTransformerRegistryTest extends ManagementTestSupport {
                 assertEquals("xml:test", to);
             } else if (description.startsWith("MyTransformer")) {
                 assertEquals("custom", scheme);
-                assertEquals("null:null", from);
-                assertEquals("null:null", to);
+                assertEquals(null, from);
+                assertEquals(null, to);
             } else {
                 fail("Unexpected transformer:" + description);
             }

http://git-wip-us.apache.org/repos/asf/camel/blob/8c9184b6/components/camel-jetty9/src/test/java/org/apache/camel/component/jetty/rest/RestJettyBindingModeJsonWithContractTest.java
----------------------------------------------------------------------
diff --git a/components/camel-jetty9/src/test/java/org/apache/camel/component/jetty/rest/RestJettyBindingModeJsonWithContractTest.java b/components/camel-jetty9/src/test/java/org/apache/camel/component/jetty/rest/RestJettyBindingModeJsonWithContractTest.java
new file mode 100644
index 0000000..d72dab1
--- /dev/null
+++ b/components/camel-jetty9/src/test/java/org/apache/camel/component/jetty/rest/RestJettyBindingModeJsonWithContractTest.java
@@ -0,0 +1,92 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.jetty.rest;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+import org.apache.camel.Converter;
+import org.apache.camel.Exchange;
+import org.apache.camel.TypeConverters;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.jetty.BaseJettyTest;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.model.rest.RestBindingMode;
+import org.apache.camel.model.rest.RestDefinition;
+import org.junit.Test;
+
+public class RestJettyBindingModeJsonWithContractTest extends BaseJettyTest {
+
+    @Test
+    public void testBindingModeJsonWithContract() throws Exception {
+        MockEndpoint mock = getMockEndpoint("mock:input");
+        mock.expectedMessageCount(1);
+        mock.message(0).body().isInstanceOf(UserPojoEx.class);
+
+        String body = "{\"id\": 123, \"name\": \"Donald Duck\"}";
+        Object answer = template.requestBody("http://localhost:" + getPort() + "/users/new", body);
+        assertNotNull(answer);
+        BufferedReader reader = new BufferedReader(new InputStreamReader((InputStream)answer));
+        String line;
+        String answerString = "";
+        while ((line  = reader.readLine()) != null) {
+            answerString += line;
+        }
+        assertTrue("Unexpected response: " + answerString, answerString.contains("\"active\":true"));
+
+        assertMockEndpointsSatisfied();
+
+        Object obj = mock.getReceivedExchanges().get(0).getIn().getBody();
+        assertEquals(UserPojoEx.class, obj.getClass());
+        UserPojoEx user = (UserPojoEx)obj;
+        assertNotNull(user);
+        assertEquals(123, user.getId());
+        assertEquals("Donald Duck", user.getName());
+        assertEquals(true, user.isActive());
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                context.getTypeConverterRegistry().addTypeConverters(new MyTypeConverters());
+                restConfiguration().component("jetty").host("localhost").port(getPort()).bindingMode(RestBindingMode.json);
+
+                rest("/users/")
+                    // REST binding converts from JSON to UserPojo
+                    .post("new").type(UserPojo.class)
+                    .route()
+                        // then contract advice converts from UserPojo to UserPojoEx
+                        .inputType(UserPojoEx.class)
+                        .to("mock:input");
+            }
+        };
+    }
+
+    public static class MyTypeConverters implements TypeConverters {
+        @Converter
+        public UserPojoEx toEx(UserPojo user) {
+            UserPojoEx ex = new UserPojoEx();
+            ex.setId(user.getId());
+            ex.setName(user.getName());
+            ex.setActive(true);
+            return ex;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/8c9184b6/components/camel-jetty9/src/test/java/org/apache/camel/component/jetty/rest/RestJettyBindingModeOffWithContractTest.java
----------------------------------------------------------------------
diff --git a/components/camel-jetty9/src/test/java/org/apache/camel/component/jetty/rest/RestJettyBindingModeOffWithContractTest.java b/components/camel-jetty9/src/test/java/org/apache/camel/component/jetty/rest/RestJettyBindingModeOffWithContractTest.java
new file mode 100644
index 0000000..fa17257
--- /dev/null
+++ b/components/camel-jetty9/src/test/java/org/apache/camel/component/jetty/rest/RestJettyBindingModeOffWithContractTest.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.component.jetty.rest;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+import org.apache.camel.Converter;
+import org.apache.camel.Exchange;
+import org.apache.camel.TypeConverters;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.jetty.BaseJettyTest;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.model.dataformat.JsonDataFormat;
+import org.apache.camel.model.dataformat.JsonLibrary;
+import org.apache.camel.model.rest.RestBindingMode;
+import org.apache.camel.model.rest.RestDefinition;
+import org.junit.Test;
+
+public class RestJettyBindingModeOffWithContractTest extends BaseJettyTest {
+
+    @Test
+    public void testBindingModeOffWithContract() throws Exception {
+        MockEndpoint mock = getMockEndpoint("mock:input");
+        mock.expectedMessageCount(1);
+        mock.message(0).body().isInstanceOf(UserPojoEx.class);
+
+        String body = "{\"id\": 123, \"name\": \"Donald Duck\"}";
+        Object answer = template.requestBodyAndHeader("http://localhost:" + getPort() + "/users/new", body, Exchange.CONTENT_TYPE, "application/json");
+        assertNotNull(answer);
+        BufferedReader reader = new BufferedReader(new InputStreamReader((InputStream)answer));
+        String line;
+        String answerString = "";
+        while ((line  = reader.readLine()) != null) {
+            answerString += line;
+        }
+        assertTrue("Unexpected response: " + answerString, answerString.contains("\"active\":true"));
+
+        assertMockEndpointsSatisfied();
+
+        Object obj = mock.getReceivedExchanges().get(0).getIn().getBody();
+        assertEquals(UserPojoEx.class, obj.getClass());
+        UserPojoEx user = (UserPojoEx)obj;
+        assertNotNull(user);
+        assertEquals(123, user.getId());
+        assertEquals("Donald Duck", user.getName());
+        assertEquals(true, user.isActive());
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                restConfiguration().component("jetty").host("localhost").port(getPort()).bindingMode(RestBindingMode.off);
+
+                JsonDataFormat jsondf = new JsonDataFormat();
+                jsondf.setLibrary(JsonLibrary.Jackson);
+                jsondf.setAllowUnmarshallType(true);
+                jsondf.setUnmarshalType(UserPojoEx.class);
+                transformer()
+                    .fromType("json")
+                    .toType(UserPojoEx.class)
+                    .withDataFormat(jsondf);
+                transformer()
+                    .fromType(UserPojoEx.class)
+                    .toType("json")
+                    .withDataFormat(jsondf);
+                rest("/users/")
+                    // REST binding does nothing
+                    .post("new")
+                    .route()
+                        // contract advice converts betweeen JSON and UserPojoEx directly
+                        .inputType(UserPojoEx.class)
+                        .outputType("json")
+                        .process(ex -> {
+                            ex.getIn().getBody(UserPojoEx.class).setActive(true);
+                        })
+                        .to("mock:input");
+            }
+        };
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/8c9184b6/components/camel-jetty9/src/test/java/org/apache/camel/component/jetty/rest/UserPojoEx.java
----------------------------------------------------------------------
diff --git a/components/camel-jetty9/src/test/java/org/apache/camel/component/jetty/rest/UserPojoEx.java b/components/camel-jetty9/src/test/java/org/apache/camel/component/jetty/rest/UserPojoEx.java
new file mode 100644
index 0000000..5ae627a
--- /dev/null
+++ b/components/camel-jetty9/src/test/java/org/apache/camel/component/jetty/rest/UserPojoEx.java
@@ -0,0 +1,48 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.jetty.rest;
+
+public class UserPojoEx {
+
+    private int id;
+    private String name;
+    private boolean active;
+
+    public int getId() {
+        return id;
+    }
+
+    public void setId(int id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public boolean isActive() {
+        return active;
+    }
+
+    public void setActive(boolean active) {
+        this.active = active;
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/8c9184b6/components/camel-netty4-http/src/test/java/org/apache/camel/component/netty4/http/rest/RestNettyHttpBindingModeJsonWithContractTest.java
----------------------------------------------------------------------
diff --git a/components/camel-netty4-http/src/test/java/org/apache/camel/component/netty4/http/rest/RestNettyHttpBindingModeJsonWithContractTest.java b/components/camel-netty4-http/src/test/java/org/apache/camel/component/netty4/http/rest/RestNettyHttpBindingModeJsonWithContractTest.java
new file mode 100644
index 0000000..d522064
--- /dev/null
+++ b/components/camel-netty4-http/src/test/java/org/apache/camel/component/netty4/http/rest/RestNettyHttpBindingModeJsonWithContractTest.java
@@ -0,0 +1,83 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.netty4.http.rest;
+
+import org.apache.camel.Converter;
+import org.apache.camel.Exchange;
+import org.apache.camel.TypeConverters;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.component.netty4.http.BaseNettyTest;
+import org.apache.camel.model.rest.RestBindingMode;
+import org.apache.camel.model.rest.RestDefinition;
+import org.junit.Test;
+
+public class RestNettyHttpBindingModeJsonWithContractTest extends BaseNettyTest {
+
+    @Test
+    public void testBindingModeJsonWithContract() throws Exception {
+        MockEndpoint mock = getMockEndpoint("mock:input");
+        mock.expectedMessageCount(1);
+        mock.message(0).body().isInstanceOf(UserPojoEx.class);
+
+        String body = "{\"id\": 123, \"name\": \"Donald Duck\"}";
+        Object answer = template.requestBody("netty4-http:http://localhost:" + getPort() + "/users/new", body);
+        assertNotNull(answer);
+        String answerString = new String((byte[])answer);
+        assertTrue("Unexpected response: " + answerString, answerString.contains("\"active\":true"));
+
+        assertMockEndpointsSatisfied();
+
+        Object obj = mock.getReceivedExchanges().get(0).getIn().getBody();
+        assertEquals(UserPojoEx.class, obj.getClass());
+        UserPojoEx user = (UserPojoEx)obj;
+        assertNotNull(user);
+        assertEquals(123, user.getId());
+        assertEquals("Donald Duck", user.getName());
+        assertEquals(true, user.isActive());
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                context.getTypeConverterRegistry().addTypeConverters(new MyTypeConverters());
+                restConfiguration().component("netty4-http").host("localhost").port(getPort()).bindingMode(RestBindingMode.json);
+
+                rest("/users/")
+                    // REST binding converts from JSON to UserPojo
+                    .post("new").type(UserPojo.class)
+                    .route()
+                        // then contract advice converts from UserPojo to UserPojoEx
+                        .inputType(UserPojoEx.class)
+                        .to("mock:input");
+            }
+        };
+    }
+
+    public static class MyTypeConverters implements TypeConverters {
+        @Converter
+        public UserPojoEx toEx(UserPojo user) {
+            UserPojoEx ex = new UserPojoEx();
+            ex.setId(user.getId());
+            ex.setName(user.getName());
+            ex.setActive(true);
+            return ex;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/8c9184b6/components/camel-netty4-http/src/test/java/org/apache/camel/component/netty4/http/rest/RestNettyHttpBindingModeOffWithContractTest.java
----------------------------------------------------------------------
diff --git a/components/camel-netty4-http/src/test/java/org/apache/camel/component/netty4/http/rest/RestNettyHttpBindingModeOffWithContractTest.java b/components/camel-netty4-http/src/test/java/org/apache/camel/component/netty4/http/rest/RestNettyHttpBindingModeOffWithContractTest.java
new file mode 100644
index 0000000..81ca1ad
--- /dev/null
+++ b/components/camel-netty4-http/src/test/java/org/apache/camel/component/netty4/http/rest/RestNettyHttpBindingModeOffWithContractTest.java
@@ -0,0 +1,90 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.netty4.http.rest;
+
+import org.apache.camel.Converter;
+import org.apache.camel.Exchange;
+import org.apache.camel.TypeConverters;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.component.netty4.http.BaseNettyTest;
+import org.apache.camel.model.dataformat.JsonDataFormat;
+import org.apache.camel.model.dataformat.JsonLibrary;
+import org.apache.camel.model.rest.RestBindingMode;
+import org.apache.camel.model.rest.RestDefinition;
+import org.junit.Test;
+
+public class RestNettyHttpBindingModeOffWithContractTest extends BaseNettyTest {
+
+    @Test
+    public void testBindingModeOffWithContract() throws Exception {
+        MockEndpoint mock = getMockEndpoint("mock:input");
+        mock.expectedMessageCount(1);
+        mock.message(0).body().isInstanceOf(UserPojoEx.class);
+
+        String body = "{\"id\": 123, \"name\": \"Donald Duck\"}";
+        Object answer = template.requestBodyAndHeader("netty4-http:http://localhost:" + getPort() + "/users/new", body, Exchange.CONTENT_TYPE, "application/json");
+        assertNotNull(answer);
+        String answerString = new String((byte[])answer);
+        assertTrue("Unexpected response: " + answerString, answerString.contains("\"active\":true"));
+
+        assertMockEndpointsSatisfied();
+
+        Object obj = mock.getReceivedExchanges().get(0).getIn().getBody();
+        assertEquals(UserPojoEx.class, obj.getClass());
+        UserPojoEx user = (UserPojoEx)obj;
+        assertNotNull(user);
+        assertEquals(123, user.getId());
+        assertEquals("Donald Duck", user.getName());
+        assertEquals(true, user.isActive());
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                restConfiguration().component("netty4-http").host("localhost").port(getPort()).bindingMode(RestBindingMode.off);
+
+                JsonDataFormat jsondf = new JsonDataFormat();
+                jsondf.setLibrary(JsonLibrary.Jackson);
+                jsondf.setAllowUnmarshallType(true);
+                jsondf.setUnmarshalType(UserPojoEx.class);
+                transformer()
+                    .fromType("json")
+                    .toType(UserPojoEx.class)
+                    .withDataFormat(jsondf);
+                transformer()
+                    .fromType(UserPojoEx.class)
+                    .toType("json")
+                    .withDataFormat(jsondf);
+                rest("/users/")
+                    // REST binding does nothing
+                    .post("new")
+                    .route()
+                        // contract advice converts betweeen JSON and UserPojoEx directly
+                        .inputType(UserPojoEx.class)
+                        .outputType("json")
+                        .process(ex -> {
+                            ex.getIn().getBody(UserPojoEx.class).setActive(true);
+                        })
+                        .to("mock:input");
+            }
+        };
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/8c9184b6/components/camel-netty4-http/src/test/java/org/apache/camel/component/netty4/http/rest/UserPojoEx.java
----------------------------------------------------------------------
diff --git a/components/camel-netty4-http/src/test/java/org/apache/camel/component/netty4/http/rest/UserPojoEx.java b/components/camel-netty4-http/src/test/java/org/apache/camel/component/netty4/http/rest/UserPojoEx.java
new file mode 100644
index 0000000..48d2466
--- /dev/null
+++ b/components/camel-netty4-http/src/test/java/org/apache/camel/component/netty4/http/rest/UserPojoEx.java
@@ -0,0 +1,48 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.netty4.http.rest;
+
+public class UserPojoEx {
+
+    private int id;
+    private String name;
+    private boolean active;
+
+    public int getId() {
+        return id;
+    }
+
+    public void setId(int id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public boolean isActive() {
+        return active;
+    }
+
+    public void setActive(boolean active) {
+        this.active = active;
+    }
+}


Mime
View raw message