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(); }
