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

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

commit 96a4cc832fcfb36786c212b18ea9faaf6c9db796
Author: Claus Ibsen <[email protected]>
AuthorDate: Sat Feb 3 09:59:53 2024 +0100

    CAMEL-20378: Languages should be thread-safe and be configured only via 
properties array, all in the same way.
---
 .../org/apache/camel/language/xpath/XPath.java     |  13 ++-
 .../xpath/XPathAnnotationExpressionFactory.java    |  50 +++++---
 .../apache/camel/language/xpath/XPathBuilder.java  | 126 ++++-----------------
 .../apache/camel/language/xpath/XPathLanguage.java |  61 ++++------
 .../org/apache/camel/NoSuchHeaderException.java    |  12 +-
 .../org/apache/camel/NoSuchPropertyException.java  |   7 +-
 .../org/apache/camel/NoSuchVariableException.java  |   3 +
 .../reifier/language/XPathExpressionReifier.java   |  29 ++---
 .../camel/support/builder/ExpressionBuilder.java   | 111 ++++++++++++++++--
 9 files changed, 220 insertions(+), 192 deletions(-)

diff --git 
a/components/camel-xpath/src/main/java/org/apache/camel/language/xpath/XPath.java
 
b/components/camel-xpath/src/main/java/org/apache/camel/language/xpath/XPath.java
index 1235f488ecd..74165579346 100644
--- 
a/components/camel-xpath/src/main/java/org/apache/camel/language/xpath/XPath.java
+++ 
b/components/camel-xpath/src/main/java/org/apache/camel/language/xpath/XPath.java
@@ -54,16 +54,17 @@ public @interface XPath {
     Class<?> resultType() default Object.class;
 
     /**
-     * The name of the header we want to apply the XPath expression to. If 
this is empty then the XPath expression will
-     * be applied to the exchange property or the body instead.
+     * The name of the variable we want to apply the XPath expression to.
+     */
+    String variableName() default "";
+
+    /**
+     * The name of the message header we want to apply the XPath expression to.
      */
     String headerName() default "";
 
     /**
-     * The name of the header we want to apply the XPath expression to. If 
this is empty then the XPath expression will
-     * be applied to the body instead.
-     * <p>
-     * It has a lower precedent than the name of header if both are set.
+     * The name of the exchange propery we want to apply the XPath expression 
to.
      */
     String propertyName() default "";
 
diff --git 
a/components/camel-xpath/src/main/java/org/apache/camel/language/xpath/XPathAnnotationExpressionFactory.java
 
b/components/camel-xpath/src/main/java/org/apache/camel/language/xpath/XPathAnnotationExpressionFactory.java
index ff69b075d4b..f31bc1a3bc4 100644
--- 
a/components/camel-xpath/src/main/java/org/apache/camel/language/xpath/XPathAnnotationExpressionFactory.java
+++ 
b/components/camel-xpath/src/main/java/org/apache/camel/language/xpath/XPathAnnotationExpressionFactory.java
@@ -20,10 +20,10 @@ import java.lang.annotation.Annotation;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.Expression;
+import org.apache.camel.support.builder.ExpressionBuilder;
 import org.apache.camel.support.language.DefaultAnnotationExpressionFactory;
 import org.apache.camel.support.language.LanguageAnnotation;
 import org.apache.camel.support.language.NamespacePrefix;
-import org.apache.camel.util.ObjectHelper;
 
 /**
  * Factory for the XPath expression annotations.
@@ -51,14 +51,12 @@ public class XPathAnnotationExpressionFactory extends 
DefaultAnnotationExpressio
             }
         }
 
-        // Set the header name that we want the XPathBuilder to apply the 
XPath expression to
+        String variableName = getVariableName(annotation);
         String headerName = getHeaderName(annotation);
-        if (ObjectHelper.isNotEmpty(headerName)) {
-            builder.setHeaderName(headerName);
-        }
         String propertyName = getPropertyName(annotation);
-        if (ObjectHelper.isNotEmpty(propertyName)) {
-            builder.setPropertyName(propertyName);
+        if (variableName != null || headerName != null || propertyName != 
null) {
+            Expression source = 
ExpressionBuilder.singleInputExpression(variableName, headerName, propertyName);
+            builder.setSource(source);
         }
 
         return builder;
@@ -80,13 +78,16 @@ public class XPathAnnotationExpressionFactory extends 
DefaultAnnotationExpressio
      *         expression to. Otherwise, null will be returned
      */
     protected String getHeaderName(Annotation annotation) {
-        String headerValue = null;
+        String answer = null;
         try {
-            headerValue = (String) getAnnotationObjectValue(annotation, 
"headerName");
+            answer = (String) getAnnotationObjectValue(annotation, 
"headerName");
         } catch (Exception e) {
             // Do Nothing
         }
-        return headerValue;
+        if (answer != null && answer.isBlank()) {
+            return null;
+        }
+        return answer;
     }
 
     /**
@@ -97,13 +98,36 @@ public class XPathAnnotationExpressionFactory extends 
DefaultAnnotationExpressio
      *         expression to. Otherwise, null will be returned
      */
     protected String getPropertyName(Annotation annotation) {
-        String propertyValue = null;
+        String answer = null;
+        try {
+            answer = (String) getAnnotationObjectValue(annotation, 
"propertyName");
+        } catch (Exception e) {
+            // Do Nothing
+        }
+        if (answer != null && answer.isBlank()) {
+            return null;
+        }
+        return answer;
+    }
+
+    /**
+     * Extracts the value of the property method in the Annotation. For 
backwards compatibility this method will return
+     * null if the annotation's method is not found.
+     *
+     * @return If the annotation has the method 'variableName' then the name 
of the property we want to apply the XPath
+     *         expression to. Otherwise, null will be returned
+     */
+    protected String getVariableName(Annotation annotation) {
+        String answer = null;
         try {
-            propertyValue = (String) getAnnotationObjectValue(annotation, 
"propertyName");
+            answer = (String) getAnnotationObjectValue(annotation, 
"variableName");
         } catch (Exception e) {
             // Do Nothing
         }
-        return propertyValue;
+        if (answer != null && answer.isBlank()) {
+            return null;
+        }
+        return answer;
     }
 
     protected boolean isLogNamespaces(Annotation annotation) {
diff --git 
a/components/camel-xpath/src/main/java/org/apache/camel/language/xpath/XPathBuilder.java
 
b/components/camel-xpath/src/main/java/org/apache/camel/language/xpath/XPathBuilder.java
index e1e2d4e3312..8dbc7087b32 100644
--- 
a/components/camel-xpath/src/main/java/org/apache/camel/language/xpath/XPathBuilder.java
+++ 
b/components/camel-xpath/src/main/java/org/apache/camel/language/xpath/XPathBuilder.java
@@ -127,18 +127,7 @@ public class XPathBuilder extends ServiceSupport
     private volatile XPathFunction outHeaderFunction;
     private volatile XPathFunction propertiesFunction;
     private volatile XPathFunction simpleFunction;
-    /**
-     * The name of the header we want to apply the XPath expression to, which 
when set will cause the xpath to be
-     * evaluated on the required header, otherwise it will be applied to the 
value of the property or the body
-     */
-    private volatile String headerName;
-    /**
-     * The name of the property we want to apply the XPath expression to, 
which when set will cause the xpath to be
-     * evaluated on the required property, otherwise it will be applied to the 
body
-     * <p>
-     * It has a lower precedent than the name of header if both are set.
-     */
-    private volatile String propertyName;
+    private volatile Expression source;
 
     /**
      * @param text The XPath expression
@@ -563,22 +552,6 @@ public class XPathBuilder extends ServiceSupport
         this.resultQName = resultQName;
     }
 
-    public String getHeaderName() {
-        return headerName;
-    }
-
-    public void setHeaderName(String headerName) {
-        this.headerName = headerName;
-    }
-
-    public String getPropertyName() {
-        return propertyName;
-    }
-
-    public void setPropertyName(String propertyName) {
-        this.propertyName = propertyName;
-    }
-
     public boolean isThreadSafety() {
         return threadSafety;
     }
@@ -820,6 +793,14 @@ public class XPathBuilder extends ServiceSupport
         this.simpleFunction = simpleFunction;
     }
 
+    public Expression getSource() {
+        return source;
+    }
+
+    public void setSource(Expression source) {
+        this.source = source;
+    }
+
     @Override
     public String getExpressionText() {
         return text;
@@ -1017,42 +998,16 @@ public class XPathBuilder extends ServiceSupport
         // set exchange and variable resolver as thread locals for concurrency
         this.exchange.set(exchange);
 
-        // the underlying input stream, which we need to close to avoid locking
-        // files or other resources
+        Object payload = source != null ? source.evaluate(exchange, 
Object.class) : exchange.getMessage().getBody();
+        Object document;
         InputStream is = null;
+        if (isInputStreamNeededForObject(payload)) {
+            is = 
exchange.getContext().getTypeConverter().tryConvertTo(InputStream.class, 
exchange, payload);
+            document = getDocument(exchange, is);
+        } else {
+            document = getDocument(exchange, payload);
+        }
         try {
-            Object document;
-
-            // Check if we need to apply the XPath expression to a header
-            if (ObjectHelper.isNotEmpty(getHeaderName())) {
-                // only convert to input stream if really needed
-                if (isInputStreamNeeded(exchange, headerName)) {
-                    is = exchange.getIn().getHeader(headerName, 
InputStream.class);
-                    document = getDocument(exchange, is);
-                } else {
-                    Object headerObject = 
exchange.getIn().getHeader(getHeaderName());
-                    document = getDocument(exchange, headerObject);
-                }
-            } else if (ObjectHelper.isNotEmpty(getPropertyName())) {
-                // only convert to input stream if really needed
-                if (isInputStreamNeededForProperty(exchange, propertyName)) {
-                    is = exchange.getProperty(propertyName, InputStream.class);
-                    document = getDocument(exchange, is);
-                } else {
-                    Object headerObject = exchange.getProperty(propertyName);
-                    document = getDocument(exchange, headerObject);
-                }
-            } else {
-                // only convert to input stream if really needed
-                if (isInputStreamNeeded(exchange)) {
-                    is = exchange.getIn().getBody(InputStream.class);
-                    document = getDocument(exchange, is);
-                } else {
-                    Object body = exchange.getIn().getBody();
-                    document = getDocument(exchange, body);
-                }
-            }
-
             if (resultQName != null) {
                 if (document == null) {
                     document = new XMLConverterHelper().createDocument();
@@ -1079,22 +1034,11 @@ public class XPathBuilder extends ServiceSupport
             }
         } catch (ParserConfigurationException e) {
             String message = getText();
-            if (ObjectHelper.isNotEmpty(getHeaderName())) {
-                message = message + " with headerName " + getHeaderName();
-            } else if (ObjectHelper.isNotEmpty(getPropertyName())) {
-                message = message + " with propertyName " + getPropertyName();
-            }
             throw new RuntimeCamelException(message, e);
         } catch (XPathExpressionException e) {
             String message = getText();
-            if (ObjectHelper.isNotEmpty(getHeaderName())) {
-                message = message + " with headerName " + getHeaderName();
-            } else if (ObjectHelper.isNotEmpty(getPropertyName())) {
-                message = message + " with propertyName " + getPropertyName();
-            }
             throw new InvalidXPathException(message, e);
         } finally {
-            // IOHelper can handle if is is null
             IOHelper.close(is);
         }
 
@@ -1102,7 +1046,7 @@ public class XPathBuilder extends ServiceSupport
             try {
                 NodeList list = (NodeList) answer;
 
-                // when the result is NodeList and it has 1 or more elements 
then its not thread-safe to use concurrently
+                // when the result is NodeList and has 1 or more elements, 
then it is not thread-safe to use concurrently,
                 // and we need to clone each node and build a thread-safe list 
to be used instead
                 boolean threadSafetyNeeded = list.getLength() >= 1;
                 if (threadSafetyNeeded) {
@@ -1242,33 +1186,7 @@ public class XPathBuilder extends ServiceSupport
      */
     protected boolean isInputStreamNeeded(Exchange exchange) {
         Object body = exchange.getIn().getBody();
-        return isInputStreamNeededForObject(exchange, body);
-    }
-
-    /**
-     * Checks whether we need an {@link InputStream} to access the message 
header.
-     * <p/>
-     * Depending on the content in the message header, we may not need to 
convert to {@link InputStream}.
-     *
-     * @param  exchange the current exchange
-     * @return          <tt>true</tt> to convert to {@link InputStream} 
beforehand converting afterwards.
-     */
-    protected boolean isInputStreamNeeded(Exchange exchange, String 
headerName) {
-        Object header = exchange.getIn().getHeader(headerName);
-        return isInputStreamNeededForObject(exchange, header);
-    }
-
-    /**
-     * Checks whether we need an {@link InputStream} to access the exchange 
property.
-     * <p/>
-     * Depending on the content in the exchange property, we may not need to 
convert to {@link InputStream}.
-     *
-     * @param  exchange the current exchange
-     * @return          <tt>true</tt> to convert to {@link InputStream} 
beforehand converting afterwards.
-     */
-    protected boolean isInputStreamNeededForProperty(Exchange exchange, String 
propertyName) {
-        Object property = exchange.getProperty(propertyName);
-        return isInputStreamNeededForObject(exchange, property);
+        return isInputStreamNeededForObject(body);
     }
 
     /**
@@ -1276,10 +1194,10 @@ public class XPathBuilder extends ServiceSupport
      * <p/>
      * Depending on the content in the object, we may not need to convert to 
{@link InputStream}.
      *
-     * @param  exchange the current exchange
-     * @return          <tt>true</tt> to convert to {@link InputStream} 
beforehand converting afterwards.
+     * @param  obj the object
+     * @return     <tt>true</tt> to convert to {@link InputStream} beforehand 
converting afterwards.
      */
-    protected boolean isInputStreamNeededForObject(Exchange exchange, Object 
obj) {
+    protected boolean isInputStreamNeededForObject(Object obj) {
         if (obj == null) {
             return false;
         }
diff --git 
a/components/camel-xpath/src/main/java/org/apache/camel/language/xpath/XPathLanguage.java
 
b/components/camel-xpath/src/main/java/org/apache/camel/language/xpath/XPathLanguage.java
index b41cde8862d..028a445b53a 100644
--- 
a/components/camel-xpath/src/main/java/org/apache/camel/language/xpath/XPathLanguage.java
+++ 
b/components/camel-xpath/src/main/java/org/apache/camel/language/xpath/XPathLanguage.java
@@ -16,6 +16,8 @@
  */
 package org.apache.camel.language.xpath;
 
+import java.util.Map;
+
 import javax.xml.namespace.QName;
 import javax.xml.xpath.XPathFactory;
 
@@ -24,6 +26,7 @@ import org.apache.camel.Expression;
 import org.apache.camel.Predicate;
 import org.apache.camel.spi.PropertyConfigurer;
 import org.apache.camel.spi.annotations.Language;
+import org.apache.camel.support.ExpressionToPredicateAdapter;
 import org.apache.camel.support.SingleInputTypedLanguageSupport;
 import org.apache.camel.support.component.PropertyConfigurerSupport;
 
@@ -43,33 +46,25 @@ public class XPathLanguage extends 
SingleInputTypedLanguageSupport implements Pr
 
     @Override
     public Predicate createPredicate(String expression) {
-        expression = loadResource(expression);
-
-        XPathBuilder builder = XPathBuilder.xpath(expression);
-        configureBuilder(builder, null);
-        return builder;
+        return 
ExpressionToPredicateAdapter.toPredicate(createExpression(expression));
     }
 
     @Override
     public Expression createExpression(String expression) {
-        expression = loadResource(expression);
-
-        XPathBuilder builder = XPathBuilder.xpath(expression);
-        configureBuilder(builder, null);
-        return builder;
+        return createExpression(expression, null);
     }
 
     @Override
     public Predicate createPredicate(String expression, Object[] properties) {
-        return (Predicate) createExpression(expression, properties);
+        return 
ExpressionToPredicateAdapter.toPredicate(createExpression(expression, 
properties));
     }
 
     @Override
-    public Expression createExpression(String expression, Object[] properties) 
{
+    public Expression createExpression(Expression source, String expression, 
Object[] properties) {
         expression = loadResource(expression);
 
         XPathBuilder builder = XPathBuilder.xpath(expression);
-        configureBuilder(builder, properties);
+        configureBuilder(builder, properties, source);
         return builder;
     }
 
@@ -137,20 +132,22 @@ public class XPathLanguage extends 
SingleInputTypedLanguageSupport implements Pr
         this.preCompile = preCompile;
     }
 
-    protected void configureBuilder(XPathBuilder builder, Object[] properties) 
{
-        Class<?> clazz = property(Class.class, properties, 0, documentType);
+    protected void configureBuilder(XPathBuilder builder, Object[] properties, 
Expression source) {
+        builder.setSource(source);
+
+        Class<?> clazz = property(Class.class, properties, 4, documentType);
         if (clazz != null) {
             builder.setDocumentType(clazz);
         }
-        QName qname = property(QName.class, properties, 1, resultQName);
+        QName qname = property(QName.class, properties, 5, resultQName);
         if (qname != null) {
             builder.setResultQName(qname);
         }
-        clazz = property(Class.class, properties, 2, getResultType());
+        clazz = property(Class.class, properties, 0, getResultType());
         if (clazz != null) {
             builder.setResultType(clazz);
         }
-        Boolean bool = property(Boolean.class, properties, 3, saxon);
+        Boolean bool = property(Boolean.class, properties, 6, saxon);
         if (bool != null) {
             builder.setUseSaxon(bool);
             if (bool) {
@@ -159,34 +156,30 @@ public class XPathLanguage extends 
SingleInputTypedLanguageSupport implements Pr
         }
         if (!builder.isUseSaxon()) {
             // xpath factory can only be set if not saxon is enabled as saxon 
has its own factory and object model
-            XPathFactory fac = property(XPathFactory.class, properties, 4, 
xpathFactory);
+            XPathFactory fac = property(XPathFactory.class, properties, 7, 
xpathFactory);
             if (fac != null) {
                 builder.setXPathFactory(fac);
             }
-            String str = property(String.class, properties, 5, objectModelUri);
+            String str = property(String.class, properties, 8, objectModelUri);
             if (str != null) {
                 builder.setObjectModelUri(str);
             }
         }
-        bool = property(Boolean.class, properties, 6, threadSafety);
+        bool = property(Boolean.class, properties, 9, threadSafety);
         if (bool != null) {
             builder.setThreadSafety(bool);
         }
-        bool = property(Boolean.class, properties, 7, preCompile);
+        bool = property(Boolean.class, properties, 10, preCompile);
         if (bool != null) {
             builder.setPreCompile(bool);
         }
-        bool = property(Boolean.class, properties, 8, logNamespaces);
+        bool = property(Boolean.class, properties, 11, logNamespaces);
         if (bool != null) {
             builder.setLogNamespaces(bool);
         }
-        String str = property(String.class, properties, 9, getHeaderName());
-        if (str != null) {
-            builder.setHeaderName(str);
-        }
-        str = property(String.class, properties, 10, getPropertyName());
-        if (str != null) {
-            builder.setPropertyName(str);
+        Map<String, String> ns = property(Map.class, properties, 12, null);
+        if (ns != null && !ns.isEmpty()) {
+            builder.setNamespaces(ns);
         }
     }
 
@@ -227,14 +220,6 @@ public class XPathLanguage extends 
SingleInputTypedLanguageSupport implements Pr
             case "logNamespaces":
                 
setLogNamespaces(PropertyConfigurerSupport.property(camelContext, 
Boolean.class, value));
                 return true;
-            case "headername":
-            case "headerName":
-                setHeaderName(PropertyConfigurerSupport.property(camelContext, 
String.class, value));
-                return true;
-            case "propertyname":
-            case "propertyName":
-                
setPropertyName(PropertyConfigurerSupport.property(camelContext, String.class, 
value));
-                return true;
             case "preCompile":
             case "precompile":
                 setPreCompile(PropertyConfigurerSupport.property(camelContext, 
Boolean.class, value));
diff --git 
a/core/camel-api/src/main/java/org/apache/camel/NoSuchHeaderException.java 
b/core/camel-api/src/main/java/org/apache/camel/NoSuchHeaderException.java
index 39a2d0e8e53..b3bdfd5d8a0 100644
--- a/core/camel-api/src/main/java/org/apache/camel/NoSuchHeaderException.java
+++ b/core/camel-api/src/main/java/org/apache/camel/NoSuchHeaderException.java
@@ -26,8 +26,14 @@ public class NoSuchHeaderException extends 
CamelExchangeException {
     private final String headerName;
     private final transient Class<?> type;
 
+    public NoSuchHeaderException(String message, Exchange exchange, String 
headerName) {
+        super(message, exchange);
+        this.headerName = headerName;
+        this.type = null;
+    }
+
     public NoSuchHeaderException(Exchange exchange, String headerName, 
Class<?> type) {
-        super("No '" + headerName + "' header available of type: " + 
type.getName()
+        super("No '" + headerName + "' header available" + (type != null ? " 
of type: " + type.getName() : "")
               + reason(exchange, headerName), exchange);
         this.headerName = headerName;
         this.type = type;
@@ -41,8 +47,8 @@ public class NoSuchHeaderException extends 
CamelExchangeException {
         return type;
     }
 
-    protected static String reason(Exchange exchange, String propertyName) {
-        Object value = exchange.getProperty(propertyName);
+    protected static String reason(Exchange exchange, String headerName) {
+        Object value = exchange.getMessage().getHeader(headerName);
         return valueDescription(value);
     }
 
diff --git 
a/core/camel-api/src/main/java/org/apache/camel/NoSuchPropertyException.java 
b/core/camel-api/src/main/java/org/apache/camel/NoSuchPropertyException.java
index 527c4eb6fcb..30a242ed51b 100644
--- a/core/camel-api/src/main/java/org/apache/camel/NoSuchPropertyException.java
+++ b/core/camel-api/src/main/java/org/apache/camel/NoSuchPropertyException.java
@@ -20,15 +20,18 @@ package org.apache.camel;
  * An exception caused when a mandatory property is not available on a message 
{@link Exchange}
  *
  * @see org.apache.camel.support.ExchangeHelper#getMandatoryProperty(Exchange, 
String, Class)
- *
  */
 public class NoSuchPropertyException extends CamelExchangeException {
 
     private final String propertyName;
     private final transient Class<?> type;
 
+    public NoSuchPropertyException(Exchange exchange, String propertyName) {
+        this(exchange, propertyName, null);
+    }
+
     public NoSuchPropertyException(Exchange exchange, String propertyName, 
Class<?> type) {
-        super("No '" + propertyName + "' property available of type: " + 
type.getName()
+        super("No '" + propertyName + "' exchange property available" + (type 
!= null ? " of type: " + type.getName() : "")
               + reason(exchange, propertyName), exchange);
         this.propertyName = propertyName;
         this.type = type;
diff --git 
a/core/camel-api/src/main/java/org/apache/camel/NoSuchVariableException.java 
b/core/camel-api/src/main/java/org/apache/camel/NoSuchVariableException.java
index 8521c7d768f..25dc319d8b4 100644
--- a/core/camel-api/src/main/java/org/apache/camel/NoSuchVariableException.java
+++ b/core/camel-api/src/main/java/org/apache/camel/NoSuchVariableException.java
@@ -16,6 +16,9 @@
  */
 package org.apache.camel;
 
+/**
+ * An exception caused when a mandatory variable is not available
+ */
 public class NoSuchVariableException extends CamelExchangeException {
 
     private final String variableName;
diff --git 
a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/language/XPathExpressionReifier.java
 
b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/language/XPathExpressionReifier.java
index 8e0c852df60..c5907b6ad2b 100644
--- 
a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/language/XPathExpressionReifier.java
+++ 
b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/language/XPathExpressionReifier.java
@@ -62,26 +62,27 @@ public class XPathExpressionReifier extends 
ExpressionReifier<XPathExpression> {
     }
 
     protected Object[] createProperties() {
-        Object[] properties = new Object[12];
-        properties[0] = definition.getDocumentType();
+        Object[] properties = new Object[13];
         // resultType can either point to a QName or it can be a regular class 
that influence the qname
         // so we need this special logic to set resultQName and resultType 
accordingly
         Object qname = asQName(definition.getResultTypeName());
-        properties[1] = qname;
         if (definition.getResultType() == null && qname == null && 
definition.getResultTypeName() != null) {
-            properties[2] = definition.getResultTypeName();
+            properties[0] = definition.getResultTypeName();
         } else {
-            properties[2] = definition.getResultType();
+            properties[0] = definition.getResultType();
         }
-        properties[3] = parseBoolean(definition.getSaxon());
-        properties[4] = definition.getXPathFactory();
-        properties[5] = parseString(definition.getObjectModel());
-        properties[6] = parseBoolean(definition.getThreadSafety());
-        properties[7] = parseBoolean(definition.getPreCompile());
-        properties[8] = parseBoolean(definition.getLogNamespaces());
-        properties[9] = parseString(definition.getHeaderName());
-        properties[10] = parseString(definition.getPropertyName());
-        properties[11] = parseString(definition.getVariableName());
+        properties[1] = parseString(definition.getVariableName());
+        properties[2] = parseString(definition.getHeaderName());
+        properties[3] = parseString(definition.getPropertyName());
+        properties[4] = definition.getDocumentType();
+        properties[5] = qname;
+        properties[6] = parseBoolean(definition.getSaxon());
+        properties[7] = definition.getXPathFactory();
+        properties[8] = parseString(definition.getObjectModel());
+        properties[9] = parseBoolean(definition.getThreadSafety());
+        properties[10] = parseBoolean(definition.getPreCompile());
+        properties[11] = parseBoolean(definition.getLogNamespaces());
+        properties[12] = definition.getNamespaces();
         return properties;
     }
 
diff --git 
a/core/camel-support/src/main/java/org/apache/camel/support/builder/ExpressionBuilder.java
 
b/core/camel-support/src/main/java/org/apache/camel/support/builder/ExpressionBuilder.java
index 058ad8ce1dc..33b0a141879 100644
--- 
a/core/camel-support/src/main/java/org/apache/camel/support/builder/ExpressionBuilder.java
+++ 
b/core/camel-support/src/main/java/org/apache/camel/support/builder/ExpressionBuilder.java
@@ -34,7 +34,10 @@ import org.apache.camel.ExchangePropertyKey;
 import org.apache.camel.Expression;
 import org.apache.camel.InvalidPayloadException;
 import org.apache.camel.Message;
+import org.apache.camel.NoSuchHeaderException;
 import org.apache.camel.NoSuchLanguageException;
+import org.apache.camel.NoSuchPropertyException;
+import org.apache.camel.NoSuchVariableException;
 import org.apache.camel.Predicate;
 import org.apache.camel.RuntimeCamelException;
 import org.apache.camel.RuntimeExchangeException;
@@ -77,6 +80,19 @@ public class ExpressionBuilder {
         return headerExpression(simpleExpression(headerName));
     }
 
+    /**
+     * Returns an expression for the header value with the given name
+     * <p/>
+     * Will fallback and look in properties if not found in headers.
+     *
+     * @param  headerName the name of the header the expression will return
+     * @param  mandatory  whether the header is mandatory and if not present 
an exception is thrown
+     * @return            an expression object which will return the header 
value
+     */
+    public static Expression headerExpression(final String headerName, boolean 
mandatory) {
+        return headerExpression(simpleExpression(headerName));
+    }
+
     /**
      * Returns an expression for the header value with the given name
      * <p/>
@@ -86,16 +102,32 @@ public class ExpressionBuilder {
      * @return            an expression object which will return the header 
value
      */
     public static Expression headerExpression(final Expression headerName) {
+        return headerExpression(headerName, false);
+    }
+
+    /**
+     * Returns an expression for the header value with the given name
+     * <p/>
+     * Will fallback and look in properties if not found in headers.
+     *
+     * @param  headerName the name of the header the expression will return
+     * @param  mandatory  whether the header is mandatory and if not present 
an exception is thrown
+     * @return            an expression object which will return the header 
value
+     */
+    public static Expression headerExpression(final Expression headerName, 
final boolean mandatory) {
         return new ExpressionAdapter() {
             @Override
             public Object evaluate(Exchange exchange) {
-                String name = headerName.evaluate(exchange, String.class);
-                Object header = exchange.getIn().getHeader(name);
-                if (header == null) {
+                String key = headerName.evaluate(exchange, String.class);
+                Object answer = exchange.getIn().getHeader(key);
+                if (answer == null) {
                     // fall back on a property
-                    header = exchange.getProperty(name);
+                    answer = exchange.getProperty(key);
                 }
-                return header;
+                if (mandatory && answer == null) {
+                    throw RuntimeCamelException.wrapRuntimeCamelException(new 
NoSuchHeaderException(exchange, key, null));
+                }
+                return answer;
             }
 
             @Override
@@ -193,6 +225,17 @@ public class ExpressionBuilder {
         return variableExpression(simpleExpression(variableName));
     }
 
+    /**
+     * Returns an expression for the variable with the given name
+     *
+     * @param  variableName the name of the variable the expression will return
+     * @param  mandatory    whether the variable is mandatory and if not 
present an exception is thrown
+     * @return              an expression object which will return the 
variable value
+     */
+    public static Expression variableExpression(final String variableName, 
boolean mandatory) {
+        return variableExpression(simpleExpression(variableName), mandatory);
+    }
+
     /**
      * Returns an expression for the variable with the given name
      *
@@ -200,6 +243,17 @@ public class ExpressionBuilder {
      * @return              an expression object which will return the 
variable value
      */
     public static Expression variableExpression(final Expression variableName) 
{
+        return variableExpression(variableName, false);
+    }
+
+    /**
+     * Returns an expression for the variable with the given name
+     *
+     * @param  variableName the name of the variable the expression will return
+     * @param  mandatory    whether the variable is mandatory and if not 
present an exception is thrown
+     * @return              an expression object which will return the 
variable value
+     */
+    public static Expression variableExpression(final Expression variableName, 
final boolean mandatory) {
         return new ExpressionAdapter() {
             private VariableRepositoryFactory factory;
 
@@ -207,17 +261,22 @@ public class ExpressionBuilder {
             public Object evaluate(Exchange exchange) {
                 String key = variableName.evaluate(exchange, String.class);
                 String id = StringHelper.before(key, ":");
+                Object answer;
                 if (id != null) {
                     VariableRepository repo = 
factory.getVariableRepository(id);
                     if (repo != null) {
                         key = StringHelper.after(key, ":");
-                        return repo.getVariable(key);
+                        answer = repo.getVariable(key);
                     } else {
                         throw new IllegalArgumentException("VariableRepository 
with id: " + id + " does not exist");
                     }
                 } else {
-                    return exchange.getVariable(key);
+                    answer = exchange.getVariable(key);
                 }
+                if (mandatory && answer == null) {
+                    throw RuntimeCamelException.wrapRuntimeCamelException(new 
NoSuchVariableException(exchange, key));
+                }
+                return answer;
             }
 
             @Override
@@ -631,6 +690,17 @@ public class ExpressionBuilder {
         return exchangePropertyExpression(simpleExpression(propertyName));
     }
 
+    /**
+     * Returns an expression for the property value of exchange with the given 
name
+     *
+     * @param  propertyName the name of the property the expression will return
+     * @param  mandatory    whether the property is mandatory and if not 
present an exception is thrown
+     * @return              an expression object which will return the 
property value
+     */
+    public static Expression exchangePropertyExpression(final String 
propertyName, boolean mandatory) {
+        return exchangePropertyExpression(simpleExpression(propertyName), 
mandatory);
+    }
+
     /**
      * Returns an expression for the property value of exchange with the given 
name
      *
@@ -638,11 +708,26 @@ public class ExpressionBuilder {
      * @return              an expression object which will return the 
property value
      */
     public static Expression exchangePropertyExpression(final Expression 
propertyName) {
+        return exchangePropertyExpression(propertyName, false);
+    }
+
+    /**
+     * Returns an expression for the property value of exchange with the given 
name
+     *
+     * @param  propertyName the name of the property the expression will return
+     * @param  mandatory    whether the property is mandatory and if not 
present an exception is thrown
+     * @return              an expression object which will return the 
property value
+     */
+    public static Expression exchangePropertyExpression(final Expression 
propertyName, final boolean mandatory) {
         return new ExpressionAdapter() {
             @Override
             public Object evaluate(Exchange exchange) {
-                String text = propertyName.evaluate(exchange, String.class);
-                return exchange.getProperty(text);
+                String key = propertyName.evaluate(exchange, String.class);
+                Object answer = exchange.getProperty(key);
+                if (mandatory && answer == null) {
+                    throw RuntimeCamelException.wrapRuntimeCamelException(new 
NoSuchPropertyException(exchange, key));
+                }
+                return answer;
             }
 
             @Override
@@ -1216,6 +1301,8 @@ public class ExpressionBuilder {
     }
 
     /**
+     * Creates a source {@link Expression} for languages that can accept input 
from other sources than the message body.
+     *
      * @param  variableName the name of the variable from which the input data 
must be extracted if not empty.
      * @param  headerName   the name of the header from which the input data 
must be extracted if not empty.
      * @param  propertyName the name of the property from which the input data 
must be extracted if not empty and
@@ -1227,11 +1314,11 @@ public class ExpressionBuilder {
     public static Expression singleInputExpression(String variableName, String 
headerName, String propertyName) {
         final Expression exp;
         if (ObjectHelper.isNotEmpty(variableName)) {
-            exp = variableExpression(variableName);
+            exp = variableExpression(variableName, true);
         } else if (ObjectHelper.isNotEmpty(headerName)) {
-            exp = headerExpression(headerName);
+            exp = headerExpression(headerName, true);
         } else if (ObjectHelper.isNotEmpty(propertyName)) {
-            exp = exchangePropertyExpression(propertyName);
+            exp = exchangePropertyExpression(propertyName, true);
         } else {
             exp = bodyExpression();
         }


Reply via email to