camel-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From davscl...@apache.org
Subject [3/4] camel git commit: CAMEL-10538 Add declarative validator according to input/output type
Date Wed, 22 Feb 2017 14:27:49 GMT
http://git-wip-us.apache.org/repos/asf/camel/blob/f9946b2e/camel-core/src/main/java/org/apache/camel/model/validator/CustomValidatorDefinition.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/model/validator/CustomValidatorDefinition.java b/camel-core/src/main/java/org/apache/camel/model/validator/CustomValidatorDefinition.java
new file mode 100644
index 0000000..5c1e164
--- /dev/null
+++ b/camel-core/src/main/java/org/apache/camel/model/validator/CustomValidatorDefinition.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.model.validator;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlType;
+import org.apache.camel.CamelContext;
+import org.apache.camel.spi.Metadata;
+import org.apache.camel.spi.Transformer;
+import org.apache.camel.spi.Validator;
+
+/**
+ * Represents a CustomValidator. One of the bean reference (ref) or fully qualified class name (className)
+ * of the custom {@link Validator} needs to be specified.
+ * 
+ * {@see ValidatorDefinition}
+ * {@see Validator}
+ */
+@Metadata(label = "validation")
+@XmlType(name = "customValidator")
+@XmlAccessorType(XmlAccessType.FIELD)
+public class CustomValidatorDefinition extends ValidatorDefinition {
+
+    @XmlAttribute
+    private String ref;
+    @XmlAttribute
+    private String className;
+
+    @Override
+    protected Validator doCreateValidator(CamelContext context) throws Exception {
+        if (ref == null && className == null) {
+            throw new IllegalArgumentException("'ref' or 'type' must be specified for customValidator");
+        }
+        Validator validator;
+        if (ref != null) {
+            validator = context.getRegistry().lookupByNameAndType(ref, Validator.class);
+            if (validator == null) {
+                throw new IllegalArgumentException("Cannot find validator with ref:" + ref);
+            }
+            if (validator.getType() != null) {
+                throw new IllegalArgumentException(String.format("Validator '%s' is already in use. Please check if duplicate validator exists.", ref));
+            }
+        } else {
+            Class<Validator> validatorClass = context.getClassResolver().resolveMandatoryClass(className, Validator.class);
+            if (validatorClass == null) {
+                throw new IllegalArgumentException("Cannot find validator class: " + className);
+            }
+            validator = context.getInjector().newInstance(validatorClass);
+
+        }
+        validator.setCamelContext(context);
+        return validator.setType(getType());
+    }
+
+    public String getRef() {
+        return ref;
+    }
+
+    /**
+     * Set a bean reference of the {@link Validator}
+     *
+     * @param ref the bean reference of the Transformer
+     */
+    public void setRef(String ref) {
+        this.ref = ref;
+    }
+
+    public String getClassName() {
+        return className;
+    }
+
+    /**
+     * Set a class name of the {@link Validator}
+     *
+     * @param className the class name of the Validator
+     */
+    public void setClassName(String className) {
+        this.className = className;
+    }
+
+}
+

http://git-wip-us.apache.org/repos/asf/camel/blob/f9946b2e/camel-core/src/main/java/org/apache/camel/model/validator/EndpointValidatorDefinition.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/model/validator/EndpointValidatorDefinition.java b/camel-core/src/main/java/org/apache/camel/model/validator/EndpointValidatorDefinition.java
new file mode 100644
index 0000000..537216b
--- /dev/null
+++ b/camel-core/src/main/java/org/apache/camel/model/validator/EndpointValidatorDefinition.java
@@ -0,0 +1,97 @@
+/**
+ * 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.model.validator;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlType;
+
+import org.w3c.dom.ls.LSResourceResolver;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Endpoint;
+import org.apache.camel.ExchangePattern;
+import org.apache.camel.component.validator.ValidatorEndpoint;
+import org.apache.camel.component.validator.ValidatorResourceResolverFactory;
+import org.apache.camel.impl.validator.ProcessorValidator;
+import org.apache.camel.processor.SendProcessor;
+import org.apache.camel.spi.Metadata;
+import org.apache.camel.spi.Validator;
+import org.apache.camel.util.ObjectHelper;
+
+/**
+ * Represents an endpoint {@link Validator} which leverages camel validator component such as
+ * <a href="http://camel.apache.org/validation.html">Validator Component</a> and 
+ * <a href="http://camel.apache.org/bean-validation.html">Bean Validator Component</a> to
+ * perform content validation. A {@link ProcessorValidator} will be created internally
+ * with a {@link SendProcessor} which forwards the message to the validator Endpoint.
+ * 
+ * {@see ValidatorDefinition}
+ * {@see Validator}
+ */
+@Metadata(label = "validation")
+@XmlType(name = "endpointValidator")
+@XmlAccessorType(XmlAccessType.FIELD)
+public class EndpointValidatorDefinition extends ValidatorDefinition {
+
+    @XmlAttribute
+    private String ref;
+    @XmlAttribute
+    private String uri;
+
+    @Override
+    protected Validator doCreateValidator(CamelContext context) throws Exception {
+        Endpoint endpoint = uri != null ? context.getEndpoint(uri)
+            : context.getRegistry().lookupByNameAndType(ref, Endpoint.class);
+        SendProcessor processor = new SendProcessor(endpoint, ExchangePattern.InOut);
+        return new ProcessorValidator(context)
+            .setProcessor(processor)
+            .setType(getType());
+    }
+
+    public String getRef() {
+        return ref;
+    }
+
+    /**
+     * Set the reference of the Endpoint.
+     *
+     * @param ref reference of the Endpoint
+     */
+    public void setRef(String ref) {
+        this.ref = ref;
+    }
+
+    public String getUri() {
+        return uri;
+    }
+
+    /**
+     * Set the URI of the Endpoint.
+     *
+     * @param uri URI of the Endpoint
+     */
+    public void setUri(String uri) {
+        this.uri = uri;
+    }
+
+}
+

http://git-wip-us.apache.org/repos/asf/camel/blob/f9946b2e/camel-core/src/main/java/org/apache/camel/model/validator/PredicateValidatorDefinition.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/model/validator/PredicateValidatorDefinition.java b/camel-core/src/main/java/org/apache/camel/model/validator/PredicateValidatorDefinition.java
new file mode 100644
index 0000000..89f72a5
--- /dev/null
+++ b/camel-core/src/main/java/org/apache/camel/model/validator/PredicateValidatorDefinition.java
@@ -0,0 +1,76 @@
+/**
+ * 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.model.validator;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElementRef;
+import javax.xml.bind.annotation.XmlType;
+import org.apache.camel.CamelContext;
+import org.apache.camel.Expression;
+import org.apache.camel.Predicate;
+import org.apache.camel.impl.validator.ProcessorValidator;
+import org.apache.camel.model.ExpressionNodeHelper;
+import org.apache.camel.model.language.ExpressionDefinition;
+import org.apache.camel.processor.validation.PredicateValidatingProcessor;
+import org.apache.camel.spi.Metadata;
+import org.apache.camel.spi.Transformer;
+import org.apache.camel.spi.Validator;
+
+/**
+ * Represents a predicate {@link Validator} which leverages expression or predicates to
+ * perform content validation. A {@link ProcessorValidator} will be created internally
+ * with a {@link PredicateValidatingProcessor} which validates the message according to specified expression/predicates.
+ * 
+ * {@see ValidatorDefinition}
+ * {@see Validator}
+ */
+@Metadata(label = "validation")
+@XmlType(name = "predicateValidator")
+@XmlAccessorType(XmlAccessType.FIELD)
+public class PredicateValidatorDefinition extends ValidatorDefinition {
+
+    @XmlElementRef
+    private ExpressionDefinition expression;
+
+    @Override
+    protected Validator doCreateValidator(CamelContext context) throws Exception {
+        Predicate pred = getExpression().createPredicate(context);
+        PredicateValidatingProcessor processor = new PredicateValidatingProcessor(pred);
+        return new ProcessorValidator(context)
+            .setProcessor(processor)
+            .setType(getType());
+    }
+
+    public ExpressionDefinition getExpression() {
+        return expression;
+    }
+
+    public void setExpression(ExpressionDefinition expression) {
+        // favour using the helper to set the expression as it can unwrap some unwanted builders when using Java DSL
+        if (expression instanceof Expression) {
+            this.expression = ExpressionNodeHelper.toExpressionDefinition((Expression) expression);
+        } else if (expression instanceof Predicate) {
+            this.expression = ExpressionNodeHelper.toExpressionDefinition((Predicate) expression);
+        } else {
+            this.expression = expression;
+        }
+    }
+
+}
+

http://git-wip-us.apache.org/repos/asf/camel/blob/f9946b2e/camel-core/src/main/java/org/apache/camel/model/validator/ValidatorDefinition.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/model/validator/ValidatorDefinition.java b/camel-core/src/main/java/org/apache/camel/model/validator/ValidatorDefinition.java
new file mode 100644
index 0000000..0a0db9f
--- /dev/null
+++ b/camel-core/src/main/java/org/apache/camel/model/validator/ValidatorDefinition.java
@@ -0,0 +1,80 @@
+/**
+ * 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.model.validator;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlType;
+import org.apache.camel.CamelContext;
+import org.apache.camel.model.InputTypeDefinition;
+import org.apache.camel.model.OutputTypeDefinition;
+import org.apache.camel.spi.DataType;
+import org.apache.camel.spi.Metadata;
+import org.apache.camel.spi.Validator;
+
+/**
+ * <p>Represents a {@link Validator} which declaratively validates message content
+ * according to the input type declared by {@link InputTypeDefinition} and/or output type
+ * declared by {@link OutputTypeDefinition}.</p>
+ * <p>If you specify type='xml:ABC', the validator
+ * will be picked up when current message type is 'xml:ABC'.
+ * If you specify type='json', then it will be picked up for all of json validation.
+ * 
+ * {@see Validator}
+ * {@see InputTypeDefinition}
+ * {@see OutputTypeDefinition}
+ */
+@Metadata(label = "validation")
+@XmlType(name = "validator")
+@XmlAccessorType(XmlAccessType.FIELD)
+public abstract class ValidatorDefinition {
+
+    @XmlAttribute
+    private String type;
+
+    public Validator createValidator(CamelContext context) throws Exception {
+        return doCreateValidator(context);
+    };
+
+    protected abstract Validator doCreateValidator(CamelContext context) throws Exception;
+
+    public String getType() {
+        return type;
+    }
+
+    /**
+     * Set the data type name.
+     * If you specify 'xml:XYZ', the validator will be picked up if message type is
+     * 'xml:XYZ'. If you specify just 'xml', the validator matches with all of
+     * 'xml' message type like 'xml:ABC' or 'xml:DEF'.
+     * 
+     * @param type data type name
+     */
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    /**
+     * Set the data type using Java class.
+     * @param clazz Java class
+     */
+    public void setType(Class<?> clazz) {
+        this.type = new DataType(clazz).toString();
+    }
+}
+

http://git-wip-us.apache.org/repos/asf/camel/blob/f9946b2e/camel-core/src/main/java/org/apache/camel/model/validator/ValidatorsDefinition.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/model/validator/ValidatorsDefinition.java b/camel-core/src/main/java/org/apache/camel/model/validator/ValidatorsDefinition.java
new file mode 100644
index 0000000..cb90c2c
--- /dev/null
+++ b/camel-core/src/main/java/org/apache/camel/model/validator/ValidatorsDefinition.java
@@ -0,0 +1,54 @@
+/**
+ * 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.model.validator;
+
+import java.util.List;
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElements;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.apache.camel.spi.Metadata;
+
+/**
+ * To configure validators.
+ */
+@Metadata(label = "validation", title = "Validations")
+@XmlRootElement(name = "validators")
+@XmlAccessorType(XmlAccessType.FIELD)
+public class ValidatorsDefinition {
+
+    @XmlElements({
+        @XmlElement(required = false, name = "endpointValidator", type = EndpointValidatorDefinition.class),
+        @XmlElement(required = false, name = "predicateValidator", type = PredicateValidatorDefinition.class),
+        @XmlElement(required = false, name = "customValidator", type = CustomValidatorDefinition.class)}
+        )
+    private List<ValidatorDefinition> validators;
+
+    /**
+     * The configured transformers
+     */
+    public void setValidators(List<ValidatorDefinition> validators) {
+        this.validators = validators;
+    }
+
+    public List<ValidatorDefinition> getValidators() {
+        return validators;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/f9946b2e/camel-core/src/main/java/org/apache/camel/model/validator/package-info.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/model/validator/package-info.java b/camel-core/src/main/java/org/apache/camel/model/validator/package-info.java
new file mode 100644
index 0000000..14173ec
--- /dev/null
+++ b/camel-core/src/main/java/org/apache/camel/model/validator/package-info.java
@@ -0,0 +1,24 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * The JAXB POJOs for the
+ * <a href="http://camel.apache.org/transformer.html">Transformers</a> used to transform message contents
+ * according to declared data types inside <a href="http://camel.apache.org/components.html">components</a>
+ */
+@javax.xml.bind.annotation.XmlSchema(namespace = "http://camel.apache.org/schema/spring", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
+package org.apache.camel.model.validator;

http://git-wip-us.apache.org/repos/asf/camel/blob/f9946b2e/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 a3dad33..6a53e7b 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
@@ -19,9 +19,11 @@ package org.apache.camel.processor;
 import org.apache.camel.CamelContext;
 import org.apache.camel.Exchange;
 import org.apache.camel.Message;
+import org.apache.camel.ValidationException;
 import org.apache.camel.spi.Contract;
 import org.apache.camel.spi.DataType;
 import org.apache.camel.spi.Transformer;
+import org.apache.camel.spi.Validator;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -29,7 +31,6 @@ import org.slf4j.LoggerFactory;
  * A {@code CamelInternalProcessorAdvice} which performs Transformation and Validation
  * according to the data type Contract.
  * 
- * TODO add declarative validation
  * @see CamelInternalProcessor, CamelInternalProcessorAdvice
  */
 public class ContractAdvice implements CamelInternalProcessorAdvice {
@@ -45,58 +46,66 @@ public class ContractAdvice implements CamelInternalProcessorAdvice {
     public Object before(Exchange exchange) throws Exception {
         DataType from = getCurrentType(exchange, Exchange.INPUT_TYPE);
         DataType to = contract.getInputType();
-        if (to != null && !to.equals(from)) {
-            LOG.debug("Looking for transformer for INPUT: from='{}', to='{}'", from, to);
-            doTransform(exchange.getIn(), from, to);
-            exchange.setProperty(Exchange.INPUT_TYPE, to);
+        if (to != null) {
+            if (!to.equals(from)) {
+                LOG.debug("Looking for transformer for INPUT: from='{}', to='{}'", from, to);
+                doTransform(exchange.getIn(), from, to);
+                exchange.setProperty(Exchange.INPUT_TYPE, to);
+            }
+            if (contract.isValidateInput()) {
+                doValidate(exchange.getIn(), to);
+            }
         }
         return null;
     }
     
     @Override
     public void after(Exchange exchange, Object data) throws Exception {
+        if (exchange.isFailed()) {
+            // TODO can we add FAULT_TYPE processing?
+            return;
+        }
+        
         Message target = exchange.hasOut() ? exchange.getOut() : exchange.getIn();
         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.OUTPUT_TYPE, to);
+        if (to != null) {
+            if (!to.equals(from)) {
+                LOG.debug("Looking for transformer for OUTPUT: from='{}', to='{}'", from, to);
+                doTransform(target, from, to);
+                exchange.setProperty(Exchange.OUTPUT_TYPE, to);
+            }
+            if (contract.isValidateOutput()) {
+                doValidate(target, to);
+            }
         }
     }
     
     private void doTransform(Message message, DataType from, DataType to) throws Exception {
+        if (from == null) {
+            // If 'from' is null, only Java-Java convertion is performed.
+            // It means if 'to' is other than Java, it's assumed to be already in expected shape.
+            convertIfRequired(message, to);
+            return;
+        }
+        
         // transform into 'from' type before performing declared transformation
         convertIfRequired(message, from);
         
-        if (applyExactlyMatchedTransformer(message, from, to)) {
-            // Found exactly matched transformer. Java-Java transformer is also allowed.
+        if (applyMatchedTransformer(message, from, to)) {
+            // Found matched transformer. Java-Java transformer is also allowed.
             return;
-        } else if (from == null || from.isJavaType()) {
+        } else if (from.isJavaType()) {
             if (convertIfRequired(message, to)) {
                 // Java->Java transformation just relies on TypeConverter if no explicit transformer
                 return;
-            } else if (from == null) {
-                // 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
-                return;
-            }
-        } else if (from != null) {
-            if (to.isJavaType()) {
-                if (applyTransformerByFromModel(message, from, to)) {
-                    // Other->Java transformation - found a transformer supprts 'from' data model
-                    return;
-                }
-            } else if (applyTransformerChain(message, from, to)) {
-                // Other->Other transformation - found a transformer chain
-                return;
             }
+        } else if (applyTransformerChain(message, from, to)) {
+            // Other->Other transformation - found a transformer chain
+            return;
         }
         
         throw new IllegalArgumentException("No Transformer found for [from='" + from + "', to='" + to + "']");
@@ -125,27 +134,11 @@ public class ContractAdvice implements CamelInternalProcessorAdvice {
         }
         return false;
     }
-    private boolean applyExactlyMatchedTransformer(Message message, DataType from, DataType to) throws Exception {
+    private boolean applyMatchedTransformer(Message message, DataType from, DataType to) throws Exception {
         Transformer transformer = message.getExchange().getContext().resolveTransformer(from, to);
         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);
-    }
-    
-    private boolean applyTransformerByFromModel(Message message, DataType from, DataType to) throws Exception {
-        Transformer transformer = message.getExchange().getContext().resolveTransformer(from.getModel());
-        return applyTransformer(transformer, message, from, to);
-    }
-    
     private boolean applyTransformerChain(Message message, DataType from, DataType to) throws Exception {
         CamelContext context = message.getExchange().getContext();
         Transformer fromTransformer = context.resolveTransformer(from.getModel());
@@ -175,4 +168,14 @@ public class ContractAdvice implements CamelInternalProcessorAdvice {
         }
         return null;
     }
+
+    private void doValidate(Message message, DataType type) throws ValidationException {
+        Validator validator = message.getExchange().getContext().resolveValidator(type);
+        if (validator != null) {
+            LOG.debug("Applying validator: type='{}', validator='{}'", type, validator);
+            validator.validate(message, type);
+        } else {
+            throw new ValidationException(message.getExchange(), String.format("No Validator found for '%s'", type));
+        }
+    }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/camel/blob/f9946b2e/camel-core/src/main/java/org/apache/camel/spi/Contract.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/spi/Contract.java b/camel-core/src/main/java/org/apache/camel/spi/Contract.java
index 8cc90c9..b9de3bf 100644
--- a/camel-core/src/main/java/org/apache/camel/spi/Contract.java
+++ b/camel-core/src/main/java/org/apache/camel/spi/Contract.java
@@ -23,6 +23,8 @@ public class Contract {
 
     private DataType inputType;
     private DataType outputType;
+    private boolean validateInput;
+    private boolean validateOutput;
     private String contractString;
     
     public DataType getInputType() {
@@ -69,6 +71,38 @@ public class Contract {
         this.contractString = null;
     }
     
+    /**
+     * 
+     * @return
+     */
+    public boolean isValidateInput() {
+        return validateInput;
+    }
+    
+    /**
+     * 
+     * @param validate
+     */
+    public void setValidateInput(boolean validate) {
+        this.validateInput = validate;
+    }
+    
+    /**
+     * 
+     * @return
+     */
+    public boolean isValidateOutput() {
+        return validateOutput;
+    }
+    
+    /**
+     * 
+     * @param validate
+     */
+    public void setValidateOutput(boolean validate) {
+        this.validateOutput = validate;
+    }
+    
     @Override
     public String toString() {
         if (contractString == null) {

http://git-wip-us.apache.org/repos/asf/camel/blob/f9946b2e/camel-core/src/main/java/org/apache/camel/spi/TransformerRegistry.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/spi/TransformerRegistry.java b/camel-core/src/main/java/org/apache/camel/spi/TransformerRegistry.java
index 85acd1c..c4d73d4 100644
--- a/camel-core/src/main/java/org/apache/camel/spi/TransformerRegistry.java
+++ b/camel-core/src/main/java/org/apache/camel/spi/TransformerRegistry.java
@@ -16,10 +16,12 @@
  */
 package org.apache.camel.spi;
 
+import java.util.List;
 import java.util.Map;
 
 import org.apache.camel.Endpoint;
 import org.apache.camel.StaticService;
+import org.apache.camel.model.transformer.TransformerDefinition;
 
 /**
  * Registry to cache transformers in memory.
@@ -40,6 +42,14 @@ import org.apache.camel.StaticService;
 public interface TransformerRegistry<K> extends Map<K, Transformer>, StaticService {
 
     /**
+     * Lookup a {@link Transformer} in the registry which supports the transformation for
+     * the data types represented by the key.
+     * @param key a key represents the from/to data types to transform
+     * @return {@link Transformer} if matched, otherwise null
+     */
+    Transformer resolveTransformer(K key);
+
+    /**
      * Number of transformers in the static registry.
      */
     int staticSize();
@@ -85,7 +95,7 @@ public interface TransformerRegistry<K> extends Map<K, Transformer>, StaticServi
     boolean isDynamic(String scheme);
 
     /**
-     * Whether the given transformer is stored in the dynamic cache
+     * Whether the given {@link Transformer} is stored in the dynamic cache
      *
      * @param from 'from' data type
      * @param to 'to' data type

http://git-wip-us.apache.org/repos/asf/camel/blob/f9946b2e/camel-core/src/main/java/org/apache/camel/spi/Validator.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/spi/Validator.java b/camel-core/src/main/java/org/apache/camel/spi/Validator.java
new file mode 100644
index 0000000..1ba2998
--- /dev/null
+++ b/camel-core/src/main/java/org/apache/camel/spi/Validator.java
@@ -0,0 +1,96 @@
+/**
+ * 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.spi;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.CamelContextAware;
+import org.apache.camel.Exchange;
+import org.apache.camel.Message;
+import org.apache.camel.ValidationException;
+import org.apache.camel.model.InputTypeDefinition;
+import org.apache.camel.model.OutputTypeDefinition;
+import org.apache.camel.support.ServiceSupport;
+
+/**
+ * <a href="http://camel.apache.org/transformer.html">Transformer</a>
+ * performs message transformation according to the declared data type.
+ * There are two Exchange property indicates current message type, {@link Exchange#INPUT_TYPE}
+ * holds input message type and {@link Exchange#OUTPUT_TYPE} holds output message type. If the
+ * input type and/or output type declared by {@link InputTypeDefinition}
+ * and/or {@link OutputTypeDefinition} in the route definition is different from those property
+ * at runtime, camel internal processor look for a Transformer which transforms from
+ * the current message type to the expected message type.
+ *  
+ * @see InputTypeDefinition
+ * @see OutputTypeDefinition
+ */
+public abstract class Validator extends ServiceSupport implements CamelContextAware {
+
+    private CamelContext camelContext;
+    private DataType type;
+
+    /**
+     * Perform data validation with specified type.
+     *
+     * @param message message to apply validation
+     * @param type the data type
+     * @throws ValidationException thrown if any validation error is detected
+     */
+    public abstract void validate(Message message, DataType type) throws ValidationException;
+
+    /**
+     * Get 'from' data type.
+     */
+    public DataType getType() {
+        return type;
+    };
+
+    /**
+     * Set data type.
+     *
+     * @param type data type
+     */
+    public Validator setType(String type) {
+        this.type = new DataType(type);
+        return this;
+    }
+
+    @Override
+    public CamelContext getCamelContext() {
+        return this.camelContext;
+    }
+
+    @Override
+    public void setCamelContext(CamelContext context) {
+        this.camelContext = context;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("%s[type='%s']", this.getClass().getSimpleName(), type);
+    }
+
+    @Override
+    protected void doStart() throws Exception {
+        // no-op
+    }
+
+    @Override
+    protected void doStop() throws Exception {
+        // no-op
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/f9946b2e/camel-core/src/main/java/org/apache/camel/spi/ValidatorRegistry.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/spi/ValidatorRegistry.java b/camel-core/src/main/java/org/apache/camel/spi/ValidatorRegistry.java
new file mode 100644
index 0000000..82d1020
--- /dev/null
+++ b/camel-core/src/main/java/org/apache/camel/spi/ValidatorRegistry.java
@@ -0,0 +1,93 @@
+/**
+ * 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.spi;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.camel.Endpoint;
+import org.apache.camel.StaticService;
+import org.apache.camel.model.validator.ValidatorDefinition;
+
+/**
+ * Registry to cache validators in memory.
+ * <p/>
+ * The registry contains two caches:
+ * <ul>
+ *     <li>static - which keeps all the validators in the cache for the entire lifecycle</li>
+ *     <li>dynamic - which keeps the validators in a {@link org.apache.camel.util.LRUCache} and may evict validators which hasn't been requested recently</li>
+ * </ul>
+ * The static cache stores all the validators that are created as part of setting up and starting routes.
+ * The static cache has no upper limit.
+ * <p/>
+ * The dynamic cache stores the validators that are created and used ad-hoc, such as from custom Java code that creates new validators etc.
+ * The dynamic cache has an upper limit, that by default is 1000 entries.
+ *
+ * @param <K> validator key
+ */
+public interface ValidatorRegistry<K> extends Map<K, Validator>, StaticService {
+
+    /**
+     * Lookup a {@link Validator} in the registry which supports the validation for
+     * the data type represented by the key.
+     * @param key a key represents the data type
+     * @return {@link Validator} if matched, otherwise null
+     */
+    Validator resolveValidator(K key);
+
+    /**
+     * Number of validators in the static registry.
+     */
+    int staticSize();
+
+    /**
+     * Number of validators in the dynamic registry
+     */
+    int dynamicSize();
+
+    /**
+     * Maximum number of entries to store in the dynamic registry
+     */
+    int getMaximumCacheSize();
+
+    /**
+     * Purges the cache (removes validators from the dynamic cache)
+     */
+    void purge();
+
+    /**
+     * Whether the given {@link Validator} is stored in the static cache
+     *
+     * @param type  the data type
+     * @return <tt>true</tt> if in static cache, <tt>false</tt> if not
+     */
+    boolean isStatic(DataType type);
+
+    /**
+     * Whether the given {@link Validator} is stored in the dynamic cache
+     *
+     * @param type the data type
+     * @return <tt>true</tt> if in dynamic cache, <tt>false</tt> if not
+     */
+    boolean isDynamic(DataType type);
+
+    /**
+     * Cleanup the cache (purging stale entries)
+     */
+    void cleanUp();
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/f9946b2e/camel-core/src/main/java/org/apache/camel/util/CamelContextHelper.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/util/CamelContextHelper.java b/camel-core/src/main/java/org/apache/camel/util/CamelContextHelper.java
index 8be19d7..a133b73 100644
--- a/camel-core/src/main/java/org/apache/camel/util/CamelContextHelper.java
+++ b/camel-core/src/main/java/org/apache/camel/util/CamelContextHelper.java
@@ -254,6 +254,68 @@ public final class CamelContextHelper {
     }
 
     /**
+     * Gets the maximum transformer cache size.
+     * <p/>
+     * Will use the property set on CamelContext with the key {@link Exchange#MAXIMUM_TRANSFORMER_CACHE_SIZE}.
+     * If no property has been set, then it will fallback to return a size of 1000.
+     *
+     * @param camelContext the camel context
+     * @return the maximum cache size
+     * @throws IllegalArgumentException is thrown if the property is illegal
+     */
+    public static int getMaximumTransformerCacheSize(CamelContext camelContext) throws IllegalArgumentException {
+        if (camelContext != null) {
+            String s = camelContext.getGlobalOption(Exchange.MAXIMUM_TRANSFORMER_CACHE_SIZE);
+            if (s != null) {
+                // we cannot use Camel type converters as they may not be ready this early
+                try {
+                    Integer size = Integer.valueOf(s);
+                    if (size == null || size <= 0) {
+                        throw new IllegalArgumentException("Property " + Exchange.MAXIMUM_TRANSFORMER_CACHE_SIZE + " must be a positive number, was: " + s);
+                    }
+                    return size;
+                } catch (NumberFormatException e) {
+                    throw new IllegalArgumentException("Property " + Exchange.MAXIMUM_TRANSFORMER_CACHE_SIZE + " must be a positive number, was: " + s, e);
+                }
+            }
+        }
+
+        // 1000 is the default fallback
+        return 1000;
+    }
+
+    /**
+     * Gets the maximum validator cache size.
+     * <p/>
+     * Will use the property set on CamelContext with the key {@link Exchange#MAXIMUM_VALIDATOR_CACHE_SIZE}.
+     * If no property has been set, then it will fallback to return a size of 1000.
+     *
+     * @param camelContext the camel context
+     * @return the maximum cache size
+     * @throws IllegalArgumentException is thrown if the property is illegal
+     */
+    public static int getMaximumValidatorCacheSize(CamelContext camelContext) throws IllegalArgumentException {
+        if (camelContext != null) {
+            String s = camelContext.getGlobalOption(Exchange.MAXIMUM_VALIDATOR_CACHE_SIZE);
+            if (s != null) {
+                // we cannot use Camel type converters as they may not be ready this early
+                try {
+                    Integer size = Integer.valueOf(s);
+                    if (size == null || size <= 0) {
+                        throw new IllegalArgumentException("Property " + Exchange.MAXIMUM_VALIDATOR_CACHE_SIZE + " must be a positive number, was: " + s);
+                    }
+                    return size;
+                } catch (NumberFormatException e) {
+                    throw new IllegalArgumentException("Property " + Exchange.MAXIMUM_VALIDATOR_CACHE_SIZE + " must be a positive number, was: " + s, e);
+                }
+            }
+        }
+
+        // 1000 is the default fallback
+        return 1000;
+    }
+
+    /**
      * Parses the given text and handling property placeholders as well
      *
      * @param camelContext the camel context

http://git-wip-us.apache.org/repos/asf/camel/blob/f9946b2e/camel-core/src/main/resources/org/apache/camel/model/validator/jaxb.index
----------------------------------------------------------------------
diff --git a/camel-core/src/main/resources/org/apache/camel/model/validator/jaxb.index b/camel-core/src/main/resources/org/apache/camel/model/validator/jaxb.index
new file mode 100644
index 0000000..7cdaef6
--- /dev/null
+++ b/camel-core/src/main/resources/org/apache/camel/model/validator/jaxb.index
@@ -0,0 +1,21 @@
+## ------------------------------------------------------------------------
+## 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.
+## ------------------------------------------------------------------------
+CustomValidatorDefinition
+EndpointValidatorDefinition
+PredicateValidatorDefinition
+ValidatorDefinition
+ValidatorsDefinition

http://git-wip-us.apache.org/repos/asf/camel/blob/f9946b2e/camel-core/src/test/java/org/apache/camel/builder/TransformerBuilderTest.java
----------------------------------------------------------------------
diff --git a/camel-core/src/test/java/org/apache/camel/builder/TransformerBuilderTest.java b/camel-core/src/test/java/org/apache/camel/builder/TransformerBuilderTest.java
index 9a7e895..1cdd30a 100644
--- a/camel-core/src/test/java/org/apache/camel/builder/TransformerBuilderTest.java
+++ b/camel-core/src/test/java/org/apache/camel/builder/TransformerBuilderTest.java
@@ -59,9 +59,11 @@ public class TransformerBuilderTest extends TestSupport {
             @Override
             public void configure() throws Exception {
                 transformer().fromType("xml:foo").toType("json:bar").withDataFormat(new StringDataFormat());
+                from("direct:input").log("test");
             }
         };
         ctx.addRoutes(builder);
+        ctx.start();
         Transformer transformer = ctx.resolveTransformer(new DataType("xml:foo"), new DataType("json:bar"));
         assertNotNull(transformer);
         assertEquals(DataFormatTransformer.class, transformer.getClass());
@@ -78,10 +80,11 @@ public class TransformerBuilderTest extends TestSupport {
             @Override
             public void configure() throws Exception {
                 transformer().fromType("json:foo").toType("xml:bar").withUri("direct:transformer");
-                from("direct:transformer");
+                from("direct:transformer").log("test");
             }
         };
         ctx.addRoutes(builder);
+        ctx.start();
         Transformer transformer = ctx.resolveTransformer(new DataType("json:foo"), new DataType("xml:bar"));
         assertNotNull(transformer);
         assertEquals(ProcessorTransformer.class, transformer.getClass());
@@ -100,9 +103,11 @@ public class TransformerBuilderTest extends TestSupport {
             @Override
             public void configure() throws Exception {
                 transformer().scheme("other").withJava(MyTransformer.class);
+                from("direct:input").log("test");
             }
         };
         ctx.addRoutes(builder);
+        ctx.start();
         Transformer transformer = ctx.resolveTransformer("other");
         assertNotNull(transformer);
         assertEquals(MyTransformer.class, transformer.getClass());

http://git-wip-us.apache.org/repos/asf/camel/blob/f9946b2e/camel-core/src/test/java/org/apache/camel/impl/MultipleLifecycleStrategyTest.java
----------------------------------------------------------------------
diff --git a/camel-core/src/test/java/org/apache/camel/impl/MultipleLifecycleStrategyTest.java b/camel-core/src/test/java/org/apache/camel/impl/MultipleLifecycleStrategyTest.java
index 2bab7f3..40ef701 100644
--- a/camel-core/src/test/java/org/apache/camel/impl/MultipleLifecycleStrategyTest.java
+++ b/camel-core/src/test/java/org/apache/camel/impl/MultipleLifecycleStrategyTest.java
@@ -51,7 +51,7 @@ public class MultipleLifecycleStrategyTest extends TestSupport {
         context.stop();
 
         List<String> expectedEvents = Arrays.asList("onContextStart", "onServiceAdd", "onServiceAdd", "onServiceAdd", "onServiceAdd", "onServiceAdd", "onServiceAdd", "onServiceAdd",
-                "onServiceAdd", "onServiceAdd", "onServiceAdd", "onServiceAdd", "onServiceAdd", "onComponentAdd", "onEndpointAdd", "onComponentRemove", "onContextStop");
+                "onServiceAdd", "onServiceAdd", "onServiceAdd", "onServiceAdd", "onServiceAdd", "onServiceAdd", "onComponentAdd", "onEndpointAdd", "onComponentRemove", "onContextStop");
         
         assertEquals(expectedEvents, dummy1.getEvents());
         assertEquals(expectedEvents, dummy2.getEvents());

http://git-wip-us.apache.org/repos/asf/camel/blob/f9946b2e/camel-core/src/test/java/org/apache/camel/impl/validator/ValidatorContractTest.java
----------------------------------------------------------------------
diff --git a/camel-core/src/test/java/org/apache/camel/impl/validator/ValidatorContractTest.java b/camel-core/src/test/java/org/apache/camel/impl/validator/ValidatorContractTest.java
new file mode 100644
index 0000000..8633010
--- /dev/null
+++ b/camel-core/src/test/java/org/apache/camel/impl/validator/ValidatorContractTest.java
@@ -0,0 +1,115 @@
+/**
+ * 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.impl.validator;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import org.apache.camel.ContextTestSupport;
+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.model.DataFormatDefinition;
+import org.apache.camel.spi.DataFormat;
+import org.apache.camel.spi.RouteContext;
+import org.junit.Test;
+
+public class ValidatorContractTest extends ContextTestSupport {
+
+    @Override
+    public boolean isUseRouteBuilder() {
+        return false;
+    }
+
+    @Test
+    public void testInputTypeOnly() throws Exception {
+        context.getTypeConverterRegistry().addTypeConverters(new MyTypeConverters());
+        context.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                validator()
+                    .type(A.class)
+                    .withUri("direct:validator");
+                from("direct:a")
+                    .inputType(A.class, true)
+                    .to("mock:a");
+                from("direct:validator")
+                    .to("mock:validator");
+            }
+        });
+        context.start();
+        
+        MockEndpoint mocka = context.getEndpoint("mock:a", MockEndpoint.class);
+        MockEndpoint mockv = context.getEndpoint("mock:validator", MockEndpoint.class);
+        mocka.setExpectedCount(1);
+        mockv.setExpectedCount(1);
+        Object answer = template.requestBody("direct:a", "foo");
+        mocka.assertIsSatisfied();
+        mockv.assertIsSatisfied();
+        Exchange exa = mocka.getExchanges().get(0);
+        assertEquals(A.class, exa.getIn().getBody().getClass());
+        Exchange exv = mockv.getExchanges().get(0);
+        assertEquals(A.class, exv.getIn().getBody().getClass());
+        assertEquals(A.class, answer.getClass());
+    }
+
+    @Test
+    public void testOutputTypeOnly() throws Exception {
+        context.getTypeConverterRegistry().addTypeConverters(new MyTypeConverters());
+        context.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                validator()
+                    .type(A.class)
+                    .withUri("direct:validator");
+                from("direct:a")
+                    .outputType(A.class, true)
+                    .to("mock:a");
+                from("direct:validator")
+                    .to("mock:validator");
+            }
+        });
+        context.start();
+        
+        MockEndpoint mocka = context.getEndpoint("mock:a", MockEndpoint.class);
+        MockEndpoint mockv = context.getEndpoint("mock:validator", MockEndpoint.class);
+        mocka.setExpectedCount(1);
+        mockv.setExpectedCount(1);
+        Object answer = template.requestBody("direct:a", "foo");
+        mocka.assertIsSatisfied();
+        mockv.assertIsSatisfied();
+        Exchange exa = mocka.getExchanges().get(0);
+        assertEquals("foo", exa.getIn().getBody());
+        Exchange exv = mockv.getExchanges().get(0);
+        assertEquals(A.class, exv.getIn().getBody().getClass());
+        assertEquals(A.class, answer.getClass());
+    }
+
+    public static class MyTypeConverters implements TypeConverters {
+        @Converter
+        public A toA(String in) {
+            return new A();
+        }
+    }
+
+    public static class A { }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/f9946b2e/camel-core/src/test/java/org/apache/camel/impl/validator/ValidatorRouteTest.java
----------------------------------------------------------------------
diff --git a/camel-core/src/test/java/org/apache/camel/impl/validator/ValidatorRouteTest.java b/camel-core/src/test/java/org/apache/camel/impl/validator/ValidatorRouteTest.java
new file mode 100644
index 0000000..3d3f7f1
--- /dev/null
+++ b/camel-core/src/test/java/org/apache/camel/impl/validator/ValidatorRouteTest.java
@@ -0,0 +1,187 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.impl.validator;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.util.Map;
+
+import org.apache.camel.AsyncCallback;
+import org.apache.camel.Consumer;
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.Converter;
+import org.apache.camel.Endpoint;
+import org.apache.camel.Exchange;
+import org.apache.camel.ExchangePattern;
+import org.apache.camel.Message;
+import org.apache.camel.Processor;
+import org.apache.camel.Producer;
+import org.apache.camel.TypeConverters;
+import org.apache.camel.ValidationException;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.impl.DefaultAsyncProducer;
+import org.apache.camel.impl.DefaultComponent;
+import org.apache.camel.impl.DefaultEndpoint;
+import org.apache.camel.impl.DefaultExchange;
+import org.apache.camel.model.DataFormatDefinition;
+import org.apache.camel.spi.DataFormat;
+import org.apache.camel.spi.DataType;
+import org.apache.camel.spi.RouteContext;
+import org.apache.camel.spi.Transformer;
+import org.apache.camel.spi.Validator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A ValidatorRouteTest demonstrates contract based declarative validation via Java DSL.
+ */
+public class ValidatorRouteTest extends ContextTestSupport {
+
+    protected static final Logger LOG = LoggerFactory.getLogger(ValidatorRouteTest.class);
+    private static final String VALIDATOR_INVOKED = "validator-invoked";
+
+    public void testPredicateValidator() throws Exception {
+        Exchange exchange = new DefaultExchange(context, ExchangePattern.InOut);
+        exchange.getIn().setBody("{name:XOrder}");
+        Exchange answerEx = template.send("direct:predicate", exchange);
+        if (answerEx.getException() != null) {
+            throw answerEx.getException();
+        }
+        assertEquals("{name:XOrderResponse}", answerEx.getOut().getBody(String.class));
+    }
+
+    public void testEndpointValidator() throws Exception {
+        Exchange exchange = new DefaultExchange(context, ExchangePattern.InOut);
+        exchange.getIn().setBody("<XOrder/>");
+        Exchange answerEx = template.send("direct:endpoint", exchange);
+        if (answerEx.getException() != null) {
+            throw answerEx.getException();
+        }
+        assertEquals("<XOrderResponse/>", answerEx.getOut().getBody(String.class));
+        assertEquals(MyXmlEndpoint.class, answerEx.getProperty(VALIDATOR_INVOKED));
+    }
+
+    public void testCustomValidator() throws Exception {
+        Exchange exchange = new DefaultExchange(context, ExchangePattern.InOut);
+        exchange.getIn().setBody("name=XOrder");
+        Exchange answerEx = template.send("direct:custom", exchange);
+        if (answerEx.getException() != null) {
+            throw answerEx.getException();
+        }
+        assertEquals("name=XOrderResponse", answerEx.getOut().getBody(String.class));
+        assertEquals(OtherXOrderResponseValidator.class, answerEx.getProperty(VALIDATOR_INVOKED));
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+
+                validator()
+                    .type("json")
+                    .withExpression(bodyAs(String.class).contains("{name:XOrder}"));
+                from("direct:predicate")
+                    .inputType("json:JsonXOrder", true)
+                    .outputType("json:JsonXOrderResponse")
+                    .setBody(simple("{name:XOrderResponse}"))
+                    .setProperty(Exchange.OUTPUT_TYPE, constant("json:JsonXOrderResponse"));
+                
+                context.addComponent("myxml", new MyXmlComponent());
+                validator()
+                    .type("xml:XmlXOrderResponse")
+                    .withUri("myxml:endpoint");
+                from("direct:endpoint")
+                    .inputType("xml:XmlXOrder")
+                    .outputType("xml:XmlXOrderResponse", true)
+                    .validate(exchangeProperty(VALIDATOR_INVOKED).isNull())
+                    .setBody(simple("<XOrderResponse/>"))
+                    .setProperty(Exchange.OUTPUT_TYPE, constant("xml:XmlXOrderResponse"));
+                
+                validator()
+                    .type("other:OtherXOrder")
+                    .withJava(OtherXOrderValidator.class);
+                validator()
+                    .type("other:OtherXOrderResponse")
+                    .withJava(OtherXOrderResponseValidator.class);
+                from("direct:custom")
+                    .inputType("other:OtherXOrder", true)
+                    .outputType("other:OtherXOrderResponse", true)
+                    .validate(exchangeProperty(VALIDATOR_INVOKED).isEqualTo(OtherXOrderValidator.class))
+                    .setBody(simple("name=XOrderResponse"))
+                    .setProperty(Exchange.OUTPUT_TYPE, constant("other:OtherXOrderResponse"));
+            }
+        };
+    }
+
+    public static class MyXmlComponent extends DefaultComponent {
+        @Override
+        protected Endpoint createEndpoint(String uri, String remaining, Map<String, Object> parameters) throws Exception {
+            return new MyXmlEndpoint();
+        }
+        
+    }
+    
+    public static class MyXmlEndpoint extends DefaultEndpoint {
+        @Override
+        public Producer createProducer() throws Exception {
+            return new DefaultAsyncProducer(this) {
+                @Override
+                public boolean process(Exchange exchange, AsyncCallback callback) {
+                    exchange.setProperty(VALIDATOR_INVOKED, MyXmlEndpoint.class);
+                    assertEquals("<XOrderResponse/>", exchange.getIn().getBody());
+                    callback.done(true);
+                    return true;
+                }
+            };
+        }
+        @Override
+        public Consumer createConsumer(Processor processor) throws Exception {
+            return null;
+        }
+        @Override
+        public boolean isSingleton() {
+            return false;
+        }
+        @Override
+        protected String createEndpointUri() {
+            return "myxml:endpoint";
+        }
+    }
+    
+    public static class OtherXOrderValidator extends Validator {
+        @Override
+        public void validate(Message message, DataType type) throws ValidationException {
+            message.getExchange().setProperty(VALIDATOR_INVOKED, OtherXOrderValidator.class);
+            assertEquals("name=XOrder", message.getBody());
+            LOG.info("Java validation: other XOrder");
+        }
+    }
+    
+    public static class OtherXOrderResponseValidator extends Validator {
+        @Override
+        public void validate(Message message, DataType type) throws ValidationException {
+            message.getExchange().setProperty(VALIDATOR_INVOKED, OtherXOrderResponseValidator.class);
+            assertEquals("name=XOrderResponse", message.getBody());
+            LOG.info("Java validation: other XOrderResponse");
+        }
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/f9946b2e/camel-core/src/test/java/org/apache/camel/management/ManagedNonManagedServiceTest.java
----------------------------------------------------------------------
diff --git a/camel-core/src/test/java/org/apache/camel/management/ManagedNonManagedServiceTest.java b/camel-core/src/test/java/org/apache/camel/management/ManagedNonManagedServiceTest.java
index c3fcaad..b24957b 100644
--- a/camel-core/src/test/java/org/apache/camel/management/ManagedNonManagedServiceTest.java
+++ b/camel-core/src/test/java/org/apache/camel/management/ManagedNonManagedServiceTest.java
@@ -48,7 +48,7 @@ public class ManagedNonManagedServiceTest extends ManagementTestSupport {
         MBeanServer mbeanServer = getMBeanServer();
 
         Set<ObjectName> set = mbeanServer.queryNames(new ObjectName("*:type=services,*"), null);
-        assertEquals(10, set.size());
+        assertEquals(11, set.size());
     }
 
     public void testNonManagedService() throws Exception {
@@ -69,7 +69,7 @@ public class ManagedNonManagedServiceTest extends ManagementTestSupport {
         MBeanServer mbeanServer = getMBeanServer();
 
         Set<ObjectName> set = mbeanServer.queryNames(new ObjectName("*:type=services,*"), null);
-        assertEquals(9, set.size());
+        assertEquals(10, set.size());
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/camel/blob/f9946b2e/camel-core/src/test/java/org/apache/camel/management/ManagedProducerRouteAddRemoveRegisterAlwaysTest.java
----------------------------------------------------------------------
diff --git a/camel-core/src/test/java/org/apache/camel/management/ManagedProducerRouteAddRemoveRegisterAlwaysTest.java b/camel-core/src/test/java/org/apache/camel/management/ManagedProducerRouteAddRemoveRegisterAlwaysTest.java
index 55bd69f..b2b58e5 100644
--- a/camel-core/src/test/java/org/apache/camel/management/ManagedProducerRouteAddRemoveRegisterAlwaysTest.java
+++ b/camel-core/src/test/java/org/apache/camel/management/ManagedProducerRouteAddRemoveRegisterAlwaysTest.java
@@ -29,7 +29,7 @@ import org.apache.camel.component.mock.MockEndpoint;
  */
 public class ManagedProducerRouteAddRemoveRegisterAlwaysTest extends ManagementTestSupport {
 
-    private int services = 9;
+    private int services = 10;
 
     @Override
     protected CamelContext createCamelContext() throws Exception {

http://git-wip-us.apache.org/repos/asf/camel/blob/f9946b2e/camel-core/src/test/java/org/apache/camel/management/ManagedRouteAddRemoveTest.java
----------------------------------------------------------------------
diff --git a/camel-core/src/test/java/org/apache/camel/management/ManagedRouteAddRemoveTest.java b/camel-core/src/test/java/org/apache/camel/management/ManagedRouteAddRemoveTest.java
index f00528f..f4804a6 100644
--- a/camel-core/src/test/java/org/apache/camel/management/ManagedRouteAddRemoveTest.java
+++ b/camel-core/src/test/java/org/apache/camel/management/ManagedRouteAddRemoveTest.java
@@ -33,7 +33,7 @@ import org.apache.camel.component.mock.MockEndpoint;
  */
 public class ManagedRouteAddRemoveTest extends ManagementTestSupport {
     
-    private int services = 9;
+    private int services = 10;
 
     @Override
     protected RouteBuilder createRouteBuilder() throws Exception {

http://git-wip-us.apache.org/repos/asf/camel/blob/f9946b2e/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 96918a6..e26d3c8 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
@@ -33,9 +33,6 @@ import org.apache.camel.spi.Transformer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-/**
- * @version 
- */
 public class ManagedTransformerRegistryTest extends ManagementTestSupport {
     private static final Logger LOG = LoggerFactory.getLogger(ManagedTransformerRegistryTest.class);
 
@@ -51,11 +48,6 @@ public class ManagedTransformerRegistryTest extends ManagementTestSupport {
 
         assertMockEndpointsSatisfied();
 
-        // resolve transformers explicitly as the route doesn't actually use them
-        context.resolveTransformer(new DataType("xml:foo"), new DataType("json:bar"));
-        context.resolveTransformer(new DataType(ManagedTransformerRegistryTest.class), new DataType("xml:test"));
-        context.resolveTransformer("custom");
-
         // get the stats for the route
         MBeanServer mbeanServer = getMBeanServer();
         Set<ObjectName> set = mbeanServer.queryNames(new ObjectName("*:type=services,*"), null);

http://git-wip-us.apache.org/repos/asf/camel/blob/f9946b2e/camel-core/src/test/java/org/apache/camel/management/ManagedValidatorRegistryTest.java
----------------------------------------------------------------------
diff --git a/camel-core/src/test/java/org/apache/camel/management/ManagedValidatorRegistryTest.java b/camel-core/src/test/java/org/apache/camel/management/ManagedValidatorRegistryTest.java
new file mode 100644
index 0000000..7ceb111
--- /dev/null
+++ b/camel-core/src/test/java/org/apache/camel/management/ManagedValidatorRegistryTest.java
@@ -0,0 +1,134 @@
+/**
+ * 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.management;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.TabularData;
+
+import org.apache.camel.Message;
+import org.apache.camel.ValidationException;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.model.dataformat.StringDataFormat;
+import org.apache.camel.spi.DataType;
+import org.apache.camel.spi.Validator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ManagedValidatorRegistryTest extends ManagementTestSupport {
+    private static final Logger LOG = LoggerFactory.getLogger(ManagedValidatorRegistryTest.class);
+
+    public void testManageValidatorRegistry() throws Exception {
+        // JMX tests dont work well on AIX CI servers (hangs them)
+        if (isPlatform("aix")) {
+            return;
+        }
+
+        getMockEndpoint("mock:result").expectedMessageCount(1);
+
+        template.sendBody("direct:start", "Hello World");
+
+        assertMockEndpointsSatisfied();
+
+        // get the stats for the route
+        MBeanServer mbeanServer = getMBeanServer();
+        Set<ObjectName> set = mbeanServer.queryNames(new ObjectName("*:type=services,*"), null);
+        List<ObjectName> list = new ArrayList<ObjectName>(set);
+        ObjectName on = null;
+        for (ObjectName name : list) {
+            if (name.getCanonicalName().contains("DefaultValidatorRegistry")) {
+                on = name;
+                break;
+            }
+        }
+
+        assertNotNull("Should have found ValidatorRegistry", on);
+
+        Integer max = (Integer) mbeanServer.getAttribute(on, "MaximumCacheSize");
+        assertEquals(1000, max.intValue());
+
+        Integer current = (Integer) mbeanServer.getAttribute(on, "Size");
+        assertEquals(3, current.intValue());
+
+        current = (Integer) mbeanServer.getAttribute(on, "StaticSize");
+        assertEquals(0, current.intValue());
+
+        current = (Integer) mbeanServer.getAttribute(on, "DynamicSize");
+        assertEquals(3, current.intValue());
+
+        String source = (String) mbeanServer.getAttribute(on, "Source");
+        assertTrue(source.startsWith("ValidatorRegistry"));
+        assertTrue(source.endsWith("capacity: 1000"));
+
+        
+        TabularData data = (TabularData) mbeanServer.invoke(on, "listValidators", null, null);
+        for (Object row : data.values()) {
+            CompositeData composite = (CompositeData)row;
+            String type = (String)composite.get("type");
+            String description = (String)composite.get("description");
+            boolean isStatic = (boolean)composite.get("static");
+            boolean isDynamic = (boolean)composite.get("dynamic");
+            LOG.info("[{}][{}][{}][{}]", type, isStatic, isDynamic, description);
+            if (description.startsWith("ProcessorValidator")) {
+                if (description.contains("direct://transformer")) {
+                    assertEquals("xml:foo", type);
+                } else if (description.contains("validate(simple{${body}} is not null")) {
+                    assertEquals("json:test", type);
+                } else {
+                    fail("Unexpected validator:" + description);
+                }
+            } else if (description.startsWith("MyValidator")) {
+                assertEquals("custom", type);
+            } else {
+                fail("Unexpected validator:" + description);
+            }
+        }
+        assertEquals(3, data.size());
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                validator()
+                    .type("xml:foo")
+                    .withUri("direct:transformer");
+                validator()
+                    .type("json:test")
+                    .withExpression(body().isNotNull());
+                validator()
+                    .type("custom")
+                    .withJava(MyValidator.class);
+                
+                from("direct:start").to("mock:result");
+            }
+        };
+    }
+
+    public static class MyValidator extends Validator {
+        @Override
+        public void validate(Message message, DataType type) throws ValidationException {
+            // empty
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/camel/blob/f9946b2e/components/camel-blueprint/src/main/java/org/apache/camel/blueprint/CamelContextFactoryBean.java
----------------------------------------------------------------------
diff --git a/components/camel-blueprint/src/main/java/org/apache/camel/blueprint/CamelContextFactoryBean.java b/components/camel-blueprint/src/main/java/org/apache/camel/blueprint/CamelContextFactoryBean.java
index 717665a..31b54de 100644
--- a/components/camel-blueprint/src/main/java/org/apache/camel/blueprint/CamelContextFactoryBean.java
+++ b/components/camel-blueprint/src/main/java/org/apache/camel/blueprint/CamelContextFactoryBean.java
@@ -64,6 +64,7 @@ import org.apache.camel.model.dataformat.DataFormatsDefinition;
 import org.apache.camel.model.rest.RestConfigurationDefinition;
 import org.apache.camel.model.rest.RestDefinition;
 import org.apache.camel.model.transformer.TransformersDefinition;
+import org.apache.camel.model.validator.ValidatorsDefinition;
 import org.apache.camel.spi.PackageScanFilter;
 import org.apache.camel.spi.Registry;
 import org.osgi.framework.BundleContext;
@@ -177,6 +178,8 @@ public class CamelContextFactoryBean extends AbstractCamelContextFactoryBean<Blu
     private DataFormatsDefinition dataFormats;
     @XmlElement(name = "transformers")
     private TransformersDefinition transformers;
+    @XmlElement(name = "validators")
+    private ValidatorsDefinition validators;
     @XmlElement(name = "redeliveryPolicyProfile")
     private List<CamelRedeliveryPolicyFactoryBean> redeliveryPolicies;
     @XmlElement(name = "onException")
@@ -696,6 +699,14 @@ public class CamelContextFactoryBean extends AbstractCamelContextFactoryBean<Blu
         return transformers;
     }
 
+    public void setValidators(ValidatorsDefinition validators) {
+        this.validators = validators;
+    }
+
+    public ValidatorsDefinition getValidators() {
+        return validators;
+    }
+
     public List<OnExceptionDefinition> getOnExceptions() {
         return onExceptions;
     }

http://git-wip-us.apache.org/repos/asf/camel/blob/f9946b2e/components/camel-cdi/src/main/java/org/apache/camel/cdi/xml/CamelContextFactoryBean.java
----------------------------------------------------------------------
diff --git a/components/camel-cdi/src/main/java/org/apache/camel/cdi/xml/CamelContextFactoryBean.java b/components/camel-cdi/src/main/java/org/apache/camel/cdi/xml/CamelContextFactoryBean.java
index d5336ed..d91605b 100644
--- a/components/camel-cdi/src/main/java/org/apache/camel/cdi/xml/CamelContextFactoryBean.java
+++ b/components/camel-cdi/src/main/java/org/apache/camel/cdi/xml/CamelContextFactoryBean.java
@@ -64,6 +64,7 @@ import org.apache.camel.model.dataformat.DataFormatsDefinition;
 import org.apache.camel.model.rest.RestConfigurationDefinition;
 import org.apache.camel.model.rest.RestDefinition;
 import org.apache.camel.model.transformer.TransformersDefinition;
+import org.apache.camel.model.validator.ValidatorsDefinition;
 import org.apache.camel.spi.PackageScanFilter;
 
 @XmlRootElement(name = "camelContext")
@@ -208,6 +209,9 @@ public class CamelContextFactoryBean extends AbstractCamelContextFactoryBean<Def
     @XmlElement(name = "transformers")
     private TransformersDefinition transformers;
 
+    @XmlElement(name = "validators")
+    private ValidatorsDefinition validators;
+
     @XmlElement(name = "redeliveryPolicyProfile")
     private List<RedeliveryPolicyFactoryBean> redeliveryPolicies;
 
@@ -702,6 +706,14 @@ public class CamelContextFactoryBean extends AbstractCamelContextFactoryBean<Def
         this.transformers = transformers;
     }
 
+    public ValidatorsDefinition getValidators() {
+        return validators;
+    }
+
+    public void setValidators(ValidatorsDefinition validators) {
+        this.validators = validators;
+    }
+
     public List<OnExceptionDefinition> getOnExceptions() {
         return onExceptions;
     }

http://git-wip-us.apache.org/repos/asf/camel/blob/f9946b2e/components/camel-core-xml/src/main/java/org/apache/camel/core/xml/AbstractCamelContextFactoryBean.java
----------------------------------------------------------------------
diff --git a/components/camel-core-xml/src/main/java/org/apache/camel/core/xml/AbstractCamelContextFactoryBean.java b/components/camel-core-xml/src/main/java/org/apache/camel/core/xml/AbstractCamelContextFactoryBean.java
index 0c2c8b9..1b89a39 100644
--- a/components/camel-core-xml/src/main/java/org/apache/camel/core/xml/AbstractCamelContextFactoryBean.java
+++ b/components/camel-core-xml/src/main/java/org/apache/camel/core/xml/AbstractCamelContextFactoryBean.java
@@ -75,6 +75,7 @@ import org.apache.camel.model.rest.RestConfigurationDefinition;
 import org.apache.camel.model.rest.RestContainer;
 import org.apache.camel.model.rest.RestDefinition;
 import org.apache.camel.model.transformer.TransformersDefinition;
+import org.apache.camel.model.validator.ValidatorsDefinition;
 import org.apache.camel.processor.interceptor.BacklogTracer;
 import org.apache.camel.processor.interceptor.HandleFault;
 import org.apache.camel.processor.interceptor.TraceFormatter;
@@ -776,6 +777,8 @@ public abstract class AbstractCamelContextFactoryBean<T extends ModelCamelContex
 
     public abstract TransformersDefinition getTransformers();
 
+    public abstract ValidatorsDefinition getValidators();
+
     public abstract List<OnExceptionDefinition> getOnExceptions();
 
     public abstract List<OnCompletionDefinition> getOnCompletions();
@@ -858,7 +861,10 @@ public abstract class AbstractCamelContextFactoryBean<T extends ModelCamelContex
             ctx.setDataFormats(getDataFormats().asMap());
         }
         if (getTransformers() != null) {
-            ctx.setTransformers(getTransformers().getTransforms());
+            ctx.setTransformers(getTransformers().getTransformers());
+        }
+        if (getValidators() != null) {
+            ctx.setValidators(getValidators().getValidators());
         }
         if (getTypeConverterStatisticsEnabled() != null) {
             ctx.setTypeConverterStatisticsEnabled(getTypeConverterStatisticsEnabled());

http://git-wip-us.apache.org/repos/asf/camel/blob/f9946b2e/components/camel-spring/src/main/java/org/apache/camel/spring/CamelContextFactoryBean.java
----------------------------------------------------------------------
diff --git a/components/camel-spring/src/main/java/org/apache/camel/spring/CamelContextFactoryBean.java b/components/camel-spring/src/main/java/org/apache/camel/spring/CamelContextFactoryBean.java
index 6c2f2d0..e0a3f07 100644
--- a/components/camel-spring/src/main/java/org/apache/camel/spring/CamelContextFactoryBean.java
+++ b/components/camel-spring/src/main/java/org/apache/camel/spring/CamelContextFactoryBean.java
@@ -63,6 +63,7 @@ import org.apache.camel.model.dataformat.DataFormatsDefinition;
 import org.apache.camel.model.rest.RestConfigurationDefinition;
 import org.apache.camel.model.rest.RestDefinition;
 import org.apache.camel.model.transformer.TransformersDefinition;
+import org.apache.camel.model.validator.ValidatorsDefinition;
 import org.apache.camel.spi.Metadata;
 import org.apache.camel.spi.PackageScanFilter;
 import org.apache.camel.spi.Registry;
@@ -186,6 +187,8 @@ public class CamelContextFactoryBean extends AbstractCamelContextFactoryBean<Spr
     private DataFormatsDefinition dataFormats;
     @XmlElement(name = "transformers")
     private TransformersDefinition transformers;
+    @XmlElement(name = "validators")
+    private ValidatorsDefinition validators;
     @XmlElement(name = "redeliveryPolicyProfile")
     private List<CamelRedeliveryPolicyFactoryBean> redeliveryPolicies;
     @XmlElement(name = "onException")
@@ -908,6 +911,17 @@ public class CamelContextFactoryBean extends AbstractCamelContextFactoryBean<Spr
     }
 
     /**
+     * Configuration of validators.
+     */
+    public void setValidators(ValidatorsDefinition validators) {
+        this.validators = validators;
+    }
+
+    public ValidatorsDefinition getValidators() {
+        return validators;
+    }
+
+    /**
      * Configuration of redelivery settings.
      */
     public void setRedeliveryPolicies(List<CamelRedeliveryPolicyFactoryBean> redeliveryPolicies) {

http://git-wip-us.apache.org/repos/asf/camel/blob/f9946b2e/components/camel-spring/src/main/java/org/apache/camel/spring/handler/CamelNamespaceHandler.java
----------------------------------------------------------------------
diff --git a/components/camel-spring/src/main/java/org/apache/camel/spring/handler/CamelNamespaceHandler.java b/components/camel-spring/src/main/java/org/apache/camel/spring/handler/CamelNamespaceHandler.java
index e0ef322..04737cf 100644
--- a/components/camel-spring/src/main/java/org/apache/camel/spring/handler/CamelNamespaceHandler.java
+++ b/components/camel-spring/src/main/java/org/apache/camel/spring/handler/CamelNamespaceHandler.java
@@ -384,6 +384,7 @@ public class CamelNamespaceHandler extends NamespaceHandlerSupport {
                 builder.addPropertyValue("interceptSendToEndpoints", factoryBean.getInterceptSendToEndpoints());
                 builder.addPropertyValue("dataFormats", factoryBean.getDataFormats());
                 builder.addPropertyValue("transformers", factoryBean.getTransformers());
+                builder.addPropertyValue("validators", factoryBean.getValidators());
                 builder.addPropertyValue("onCompletions", factoryBean.getOnCompletions());
                 builder.addPropertyValue("onExceptions", factoryBean.getOnExceptions());
                 builder.addPropertyValue("builderRefs", factoryBean.getBuilderRefs());

http://git-wip-us.apache.org/repos/asf/camel/blob/f9946b2e/components/camel-spring/src/test/java/org/apache/camel/spring/impl/validator/SpringValidatorRouteTest.java
----------------------------------------------------------------------
diff --git a/components/camel-spring/src/test/java/org/apache/camel/spring/impl/validator/SpringValidatorRouteTest.java b/components/camel-spring/src/test/java/org/apache/camel/spring/impl/validator/SpringValidatorRouteTest.java
new file mode 100644
index 0000000..56cb106
--- /dev/null
+++ b/components/camel-spring/src/test/java/org/apache/camel/spring/impl/validator/SpringValidatorRouteTest.java
@@ -0,0 +1,35 @@
+/**
+ * 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.spring.impl.validator;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+import org.apache.camel.impl.validator.ValidatorRouteTest;
+
+import static org.apache.camel.spring.processor.SpringTestHelper.createSpringCamelContext;
+
+/**
+ * A SpringValidatorRouteTest demonstrates contract based declarative validation via Spring DSL.
+ */
+public class SpringValidatorRouteTest extends ValidatorRouteTest {
+
+    protected CamelContext createCamelContext() throws Exception {
+        return createSpringCamelContext(this, "org/apache/camel/spring/impl/validator/SpringValidatorRouteTest.xml");
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/f9946b2e/components/camel-spring/src/test/resources/org/apache/camel/spring/impl/validator/SpringValidatorRouteTest.xml
----------------------------------------------------------------------
diff --git a/components/camel-spring/src/test/resources/org/apache/camel/spring/impl/validator/SpringValidatorRouteTest.xml b/components/camel-spring/src/test/resources/org/apache/camel/spring/impl/validator/SpringValidatorRouteTest.xml
new file mode 100644
index 0000000..4e6c090
--- /dev/null
+++ b/components/camel-spring/src/test/resources/org/apache/camel/spring/impl/validator/SpringValidatorRouteTest.xml
@@ -0,0 +1,68 @@
+<?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.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="
+       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+       http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd
+    ">
+
+    <bean id="myxml" class="org.apache.camel.impl.validator.ValidatorRouteTest.MyXmlComponent"/>
+    
+    <camelContext xmlns="http://camel.apache.org/schema/spring">
+    
+        <validators>
+            <predicateValidator type="json"><simple>${body} contains '{name:XOrder'</simple></predicateValidator>
+            <endpointValidator type="xml:XmlXOrderResponse" uri="myxml:endpoint"/>
+            <customValidator type="other:OtherXOrder" className="org.apache.camel.impl.validator.ValidatorRouteTest$OtherXOrderValidator"/>
+            <customValidator type="other:OtherXOrderResponse" className="org.apache.camel.impl.validator.ValidatorRouteTest$OtherXOrderResponseValidator"/>
+        </validators>
+        
+        <route>
+            <from uri="direct:predicate"/>
+            <inputType urn="json:JsonXOrder" validate="true"/>
+            <outputType urn="json:JsonXOrderResponse"/>
+            <setBody><constant>{name:XOrderResponse}</constant></setBody>
+            <setProperty propertyName="CamelOutputType"><constant>json:JsonXOrderResponse</constant></setProperty>
+        </route>
+
+        <route>
+            <from uri="direct:endpoint"/>
+            <inputType urn="xml:XmlXOrder"/>
+            <outputType urn="xml:XmlXOrderResponse" validate="true"/>
+            <validate>
+                <simple>${exchangeProperty.validator-invoked} == null</simple>
+            </validate>
+            <setBody><constant>&lt;XOrderResponse/&gt;</constant></setBody>
+            <setProperty propertyName="CamelOutputType"><constant>xml:XmlXOrderResponse</constant></setProperty>
+        </route>
+        
+        <route>
+            <from uri="direct:custom"/>
+            <inputType urn="other:OtherXOrder" validate="true"/>
+            <outputType urn="other:OtherXOrderResponse" validate="true"/>
+            <validate>
+                <simple>${exchangeProperty.validator-invoked} == 'org.apache.camel.impl.validator.ValidatorRouteTest$OtherXOrderValidator'</simple>
+            </validate>
+            <setBody><constant>name=XOrderResponse</constant></setBody>
+            <setProperty propertyName="CamelOutputType"><constant>other:OtherXOrderResponse</constant></setProperty>
+        </route>
+        
+    </camelContext>
+  
+</beans>


Mime
View raw message