You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2025/02/04 14:51:43 UTC
(camel) 02/02: CAMEL-21703: Add external functions to simple language such as camel-attachments
This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch sa
in repository https://gitbox.apache.org/repos/asf/camel.git
commit 8cb9fd6242f825b616f8b59966661f8de01ea604
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Tue Feb 4 15:51:23 2025 +0100
CAMEL-21703: Add external functions to simple language such as camel-attachments
---
components/camel-attachments/pom.xml | 11 +
.../simple-function-factory/camel-attachments | 2 +
.../src/main/docs/attachments.adoc | 2 +-
.../attachment/AttachmentExpressionBuilder.java | 225 +++++++++++++++
.../camel/attachment/CSimpleAttachmentHelper.java | 95 +++++++
.../camel/attachment/CamelFileDataSource.java | 7 +
.../attachment/NoSuchAttachmentException.java | 62 +++++
.../camel/attachment/SimpleAttachmentFunction.java | 301 +++++++++++++++++++++
components/camel-attachments/src/test/data/123.txt | 1 +
.../camel/attachment/SimpleAttachmentTest.java | 137 ++++++++++
components/camel-csimple-joor/pom.xml | 5 +
.../language/csimple/joor/JoorCSimpleCompiler.java | 30 +-
.../camel-csimple-joor/src/test/data/123.txt | 1 +
.../camel-csimple-joor/src/test/data/message1.xml | 24 ++
.../camel-csimple-joor/src/test/data/message2.xml | 24 ++
.../csimple/joor/CSimpleAttachmentsTest.java | 107 ++++++++
.../org/apache/camel/spi/BeanProxyFactory.java | 2 +-
.../camel/spi/SimpleLanguageFunctionFactory.java | 55 ++++
.../modules/languages/pages/simple-language.adoc | 25 ++
.../language/csimple/CSimpleCodeGenerator.java | 19 +-
.../language/csimple/CSimpleExpressionParser.java | 7 +-
.../camel/language/csimple/CSimpleHelper.java | 4 +
.../language/csimple/CSimplePredicateParser.java | 7 +-
.../language/simple/SimpleExpressionBuilder.java | 8 +-
.../language/simple/SimpleExpressionParser.java | 2 +-
.../language/simple/SimplePredicateParser.java | 2 +-
.../camel/language/simple/ast/BaseSimpleNode.java | 6 +-
.../language/simple/ast/BinaryExpression.java | 10 +-
.../language/simple/ast/BooleanExpression.java | 2 +-
.../camel/language/simple/ast/CompositeNodes.java | 6 +-
.../camel/language/simple/ast/DoubleQuoteEnd.java | 2 +-
.../language/simple/ast/DoubleQuoteStart.java | 4 +-
.../language/simple/ast/LiteralExpression.java | 2 +-
.../language/simple/ast/LogicalExpression.java | 10 +-
.../camel/language/simple/ast/NullExpression.java | 2 +-
.../language/simple/ast/NumericExpression.java | 2 +-
.../language/simple/ast/SimpleFunctionEnd.java | 2 +-
.../simple/ast/SimpleFunctionExpression.java | 65 ++++-
.../language/simple/ast/SimpleFunctionStart.java | 18 +-
.../camel/language/simple/ast/SimpleNode.java | 3 +-
.../camel/language/simple/ast/SingleQuoteEnd.java | 2 +-
.../language/simple/ast/SingleQuoteStart.java | 4 +-
.../camel/language/simple/ast/UnaryExpression.java | 8 +-
.../apache/camel/language/simple/SimpleTest.java | 2 +
.../camel/support/builder/ExpressionBuilder.java | 34 +++
45 files changed, 1290 insertions(+), 59 deletions(-)
diff --git a/components/camel-attachments/pom.xml b/components/camel-attachments/pom.xml
index f6849893c7d..d9e98326aaa 100644
--- a/components/camel-attachments/pom.xml
+++ b/components/camel-attachments/pom.xml
@@ -44,6 +44,11 @@
<artifactId>camel-support</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-core-languages</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<!-- attachments api -->
<dependency>
@@ -58,6 +63,12 @@
<artifactId>camel-test-junit5</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-bean</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
</project>
diff --git a/components/camel-attachments/src/generated/resources/META-INF/services/org/apache/camel/simple-function-factory/camel-attachments b/components/camel-attachments/src/generated/resources/META-INF/services/org/apache/camel/simple-function-factory/camel-attachments
new file mode 100644
index 00000000000..7e9b7cb00c9
--- /dev/null
+++ b/components/camel-attachments/src/generated/resources/META-INF/services/org/apache/camel/simple-function-factory/camel-attachments
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.attachment.SimpleAttachmentFunction
diff --git a/components/camel-attachments/src/main/docs/attachments.adoc b/components/camel-attachments/src/main/docs/attachments.adoc
index 1c552cf1124..5cd8c5cf159 100644
--- a/components/camel-attachments/src/main/docs/attachments.adoc
+++ b/components/camel-attachments/src/main/docs/attachments.adoc
@@ -27,5 +27,5 @@ And if you want to add an attachment, to a Camel `Message` you can do as shown:
[source,java]
----
AttachmentMessage attMsg = exchange.getIn(AttachmentMessage.class);
-attMsg.addAttachment("message1.xml", new DataHandler(new FileDataSource(new File("myMessage1.xml"))));
+attMsg.addAttachment("message1.xml", new DataHandler(new CamelFileDataSource(new File("myMessage1.xml"))));
----
diff --git a/components/camel-attachments/src/main/java/org/apache/camel/attachment/AttachmentExpressionBuilder.java b/components/camel-attachments/src/main/java/org/apache/camel/attachment/AttachmentExpressionBuilder.java
new file mode 100644
index 00000000000..d86f06e6664
--- /dev/null
+++ b/components/camel-attachments/src/main/java/org/apache/camel/attachment/AttachmentExpressionBuilder.java
@@ -0,0 +1,225 @@
+/*
+ * 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.attachment;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.CamelExecutionException;
+import org.apache.camel.Exchange;
+import org.apache.camel.Expression;
+import org.apache.camel.NoTypeConversionAvailableException;
+import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.language.simple.SimpleExpressionBuilder;
+import org.apache.camel.support.ExpressionAdapter;
+import org.apache.camel.support.ObjectHelper;
+
+import static org.apache.camel.support.builder.ExpressionBuilder.simpleExpression;
+
+public class AttachmentExpressionBuilder {
+
+ public static Expression attachments() {
+ return new ExpressionAdapter() {
+ @Override
+ public Object evaluate(Exchange exchange) {
+ if (exchange.getMessage() instanceof AttachmentMessage am) {
+ return am.getAttachments();
+ }
+ return null;
+ }
+ };
+ }
+
+ public static Expression attachmentsSize() {
+ return new ExpressionAdapter() {
+ @Override
+ public Object evaluate(Exchange exchange) {
+ if (exchange.getMessage() instanceof AttachmentMessage am) {
+ return am.getAttachments().size();
+ }
+ return 0;
+ }
+ };
+ }
+
+ public static Expression attachmentContent(final String key, final String type) {
+ return new ExpressionAdapter() {
+ private Class<?> clazz;
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ Object answer = null;
+ if (exchange.getMessage() instanceof AttachmentMessage am) {
+ var dh = am.getAttachment(key);
+ try {
+ answer = dh.getContent();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ if (answer != null && clazz != null) {
+ try {
+ answer = exchange.getContext().getTypeConverter().mandatoryConvertTo(clazz, answer);
+ } catch (NoTypeConversionAvailableException e) {
+ throw CamelExecutionException.wrapCamelExecutionException(exchange, e);
+ }
+ }
+ }
+ return answer;
+ }
+
+ @Override
+ public void init(CamelContext context) {
+ if (type != null) {
+ try {
+ clazz = context.getClassResolver().resolveMandatoryClass(type);
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ };
+ }
+
+ public static Expression attachmentContentHeader(final String key, final String name, final String type) {
+ return new ExpressionAdapter() {
+ private Class<?> clazz;
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ Object answer = null;
+ if (exchange.getMessage() instanceof AttachmentMessage am) {
+ var ao = am.getAttachmentObject(key);
+ if (ao != null) {
+ answer = ao.getHeader(name);
+ if (answer != null && clazz != null) {
+ try {
+ answer = exchange.getContext().getTypeConverter().mandatoryConvertTo(clazz, answer);
+ } catch (NoTypeConversionAvailableException e) {
+ throw CamelExecutionException.wrapCamelExecutionException(exchange, e);
+ }
+ }
+ }
+ }
+ return answer;
+ }
+
+ @Override
+ public void init(CamelContext context) {
+ if (type != null) {
+ try {
+ clazz = context.getClassResolver().resolveMandatoryClass(type);
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ };
+ }
+
+ public static Expression attachmentContentType(final String key) {
+ return new ExpressionAdapter() {
+ @Override
+ public Object evaluate(Exchange exchange) {
+ if (exchange.getMessage() instanceof AttachmentMessage am) {
+ var dh = am.getAttachment(key);
+ if (dh != null) {
+ return dh.getContentType();
+ }
+ }
+ return null;
+ }
+ };
+ }
+
+ /**
+ * Returns an expression for the attachment value of exchange with the given name
+ *
+ * @param attachmentName the name of the attachment the expression will return
+ * @return an expression object which will return the property value
+ */
+ public static Expression attachmentExpression(final String attachmentName) {
+ return attachmentExpression(simpleExpression(attachmentName), false);
+ }
+
+ /**
+ * Returns an expression for the attachment value of exchange with the given name
+ *
+ * @param attachmentName the name of the attachment the expression will return
+ * @param mandatory whether the attachment is mandatory and if not present an exception is thrown
+ * @return an expression object which will return the attachment value
+ */
+ public static Expression attachmentExpression(final Expression attachmentName, final boolean mandatory) {
+ return new ExpressionAdapter() {
+ @Override
+ public Object evaluate(Exchange exchange) {
+ if (exchange.getMessage() instanceof AttachmentMessage am) {
+ String key = attachmentName.evaluate(exchange, String.class);
+ Object answer = am.getAttachment(key);
+ if (mandatory && answer == null) {
+ throw RuntimeCamelException.wrapRuntimeCamelException(new NoSuchAttachmentException(exchange, key));
+ }
+ return answer;
+ }
+ return null;
+ }
+
+ @Override
+ public void init(CamelContext context) {
+ super.init(context);
+ attachmentName.init(context);
+ }
+
+ @Override
+ public String toString() {
+ return "attachment(" + attachmentName + ")";
+ }
+ };
+ }
+
+ /**
+ * Returns an expression for the attachment value of exchange with the given name invoking methods defined in a
+ * simple OGNL notation
+ *
+ * @param ognl methods to invoke on the attachment in a simple OGNL syntax
+ */
+ public static Expression attachmentOgnlExpression(final String ognl) {
+ return new SimpleExpressionBuilder.KeyedOgnlExpressionAdapter(
+ ognl, "attachmentOgnl(" + ognl + ")",
+ (exchange, exp) -> {
+ if (exchange.getMessage() instanceof AttachmentMessage am) {
+ String text = exp.evaluate(exchange, String.class);
+ var dh = am.getAttachment(text);
+ if (dh == null && ObjectHelper.isNumber(text)) {
+ try {
+ // fallback to lookup by numeric index
+ int idx = Integer.parseInt(text);
+ if (idx < am.getAttachments().size()) {
+ var it = am.getAttachments().values().iterator();
+ for (int i = 0; i < idx; i++) {
+ it.next();
+ }
+ dh = it.next();
+ }
+ } catch (NumberFormatException e) {
+ // ignore
+ }
+ }
+ return dh;
+ }
+ return null;
+ });
+ }
+
+}
diff --git a/components/camel-attachments/src/main/java/org/apache/camel/attachment/CSimpleAttachmentHelper.java b/components/camel-attachments/src/main/java/org/apache/camel/attachment/CSimpleAttachmentHelper.java
new file mode 100644
index 00000000000..1252657eadd
--- /dev/null
+++ b/components/camel-attachments/src/main/java/org/apache/camel/attachment/CSimpleAttachmentHelper.java
@@ -0,0 +1,95 @@
+/*
+ * 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.attachment;
+
+import java.util.Map;
+
+import jakarta.activation.DataHandler;
+
+import org.apache.camel.Exchange;
+
+public class CSimpleAttachmentHelper {
+
+ public static Map<String, DataHandler> attachments(Exchange exchange) {
+ if (exchange.getMessage() instanceof AttachmentMessage am) {
+ return am.getAttachments();
+ }
+ return null;
+ }
+
+ public static int attachmentsSize(Exchange exchange) {
+ if (exchange.getMessage() instanceof AttachmentMessage am) {
+ return am.getAttachments().size();
+ }
+ return 0;
+ }
+
+ public static Object attachmentContent(Exchange exchange, String key) throws Exception {
+ if (exchange.getMessage() instanceof AttachmentMessage am) {
+ var dh = am.getAttachments().get(key);
+ if (dh != null) {
+ return dh.getContent();
+ }
+ }
+ return null;
+ }
+
+ public static Object attachmentContentAsText(Exchange exchange, String key) throws Exception {
+ Object data = attachmentContent(exchange, key);
+ if (data != null) {
+ return exchange.getContext().getTypeConverter().convertTo(String.class, exchange, data);
+ }
+ return null;
+ }
+
+ public static <T> T attachmentContentAs(Exchange exchange, String key, Class<T> type) throws Exception {
+ Object data = attachmentContent(exchange, key);
+ if (data != null) {
+ return exchange.getContext().getTypeConverter().convertTo(type, exchange, data);
+ }
+ return null;
+ }
+
+ public static String attachmentContentType(Exchange exchange, String key) {
+ if (exchange.getMessage() instanceof AttachmentMessage am) {
+ var dh = am.getAttachments().get(key);
+ if (dh != null) {
+ return dh.getContentType();
+ }
+ }
+ return null;
+ }
+
+ public static String attachmentHeader(Exchange exchange, String key, String name) {
+ if (exchange.getMessage() instanceof AttachmentMessage am) {
+ var ao = am.getAttachmentObjects().get(key);
+ if (ao != null) {
+ return ao.getHeader(name);
+ }
+ }
+ return null;
+ }
+
+ public static <T> T attachmentHeaderAs(Exchange exchange, String key, String name, Class<T> type) {
+ String data = attachmentHeader(exchange, key, name);
+ if (data != null) {
+ return exchange.getContext().getTypeConverter().convertTo(type, exchange, data);
+ }
+ return null;
+ }
+
+}
diff --git a/components/camel-attachments/src/main/java/org/apache/camel/attachment/CamelFileDataSource.java b/components/camel-attachments/src/main/java/org/apache/camel/attachment/CamelFileDataSource.java
index 1f26d9d6db7..3c2f1676f9c 100644
--- a/components/camel-attachments/src/main/java/org/apache/camel/attachment/CamelFileDataSource.java
+++ b/components/camel-attachments/src/main/java/org/apache/camel/attachment/CamelFileDataSource.java
@@ -21,10 +21,17 @@ import java.io.File;
import jakarta.activation.FileDataSource;
import jakarta.activation.FileTypeMap;
+/**
+ * A {@link FileDataSource} that uses the file name/extension to determine the content-type.
+ */
public class CamelFileDataSource extends FileDataSource {
private final String fileName;
private FileTypeMap typeMap;
+ public CamelFileDataSource(File file) {
+ this(file, file.getName());
+ }
+
public CamelFileDataSource(File file, String fileName) {
super(file);
this.fileName = fileName;
diff --git a/components/camel-attachments/src/main/java/org/apache/camel/attachment/NoSuchAttachmentException.java b/components/camel-attachments/src/main/java/org/apache/camel/attachment/NoSuchAttachmentException.java
new file mode 100644
index 00000000000..d1452e0b962
--- /dev/null
+++ b/components/camel-attachments/src/main/java/org/apache/camel/attachment/NoSuchAttachmentException.java
@@ -0,0 +1,62 @@
+/*
+ * 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.attachment;
+
+import org.apache.camel.CamelExchangeException;
+import org.apache.camel.Exchange;
+
+/**
+ * An exception caused when a mandatory attachment is not available on a message {@link Exchange}
+ *
+ * @see org.apache.camel.support.ExchangeHelper#getMandatoryProperty(Exchange, String, Class)
+ */
+public class NoSuchAttachmentException extends CamelExchangeException {
+
+ private final String attachmentName;
+ private final transient Class<?> type;
+
+ public NoSuchAttachmentException(Exchange exchange, String attachmentName) {
+ this(exchange, attachmentName, null);
+ }
+
+ public NoSuchAttachmentException(Exchange exchange, String attachmentName, Class<?> type) {
+ super("No '" + attachmentName + "' attachment available" + (type != null ? " of type: " + type.getName() : "")
+ + reason(exchange, attachmentName), exchange);
+ this.attachmentName = attachmentName;
+ this.type = type;
+ }
+
+ public String getAttachmentName() {
+ return attachmentName;
+ }
+
+ public Class<?> getType() {
+ return type;
+ }
+
+ protected static String reason(Exchange exchange, String propertyName) {
+ Object value = exchange.getProperty(propertyName);
+ return valueDescription(value);
+ }
+
+ static String valueDescription(Object value) {
+ if (value == null) {
+ return "";
+ }
+ return " but has type: " + value.getClass().getCanonicalName();
+ }
+}
diff --git a/components/camel-attachments/src/main/java/org/apache/camel/attachment/SimpleAttachmentFunction.java b/components/camel-attachments/src/main/java/org/apache/camel/attachment/SimpleAttachmentFunction.java
new file mode 100644
index 00000000000..c0accf73c8a
--- /dev/null
+++ b/components/camel-attachments/src/main/java/org/apache/camel/attachment/SimpleAttachmentFunction.java
@@ -0,0 +1,301 @@
+/*
+ * 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.attachment;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Expression;
+import org.apache.camel.language.simple.types.SimpleParserException;
+import org.apache.camel.spi.SimpleLanguageFunctionFactory;
+import org.apache.camel.spi.annotations.JdkService;
+import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.OgnlHelper;
+import org.apache.camel.util.StringHelper;
+import org.apache.camel.util.StringQuoteHelper;
+
+@JdkService(SimpleLanguageFunctionFactory.FACTORY + "/camel-attachments")
+public class SimpleAttachmentFunction implements SimpleLanguageFunctionFactory {
+
+ @Override
+ public Expression createFunction(CamelContext camelContext, String function, int index) {
+ if ("attachments".equals(function)) {
+ return AttachmentExpressionBuilder.attachments();
+ } else if ("attachments.size".equals(function) || "attachments.size()".equals(function)
+ || "attachments.length".equals(function) || "attachments.length()".equals(function)) {
+ return AttachmentExpressionBuilder.attachmentsSize();
+ }
+
+ String remainder = ifStartsWithReturnRemainder("attachmentContent(", function);
+ if (remainder != null) {
+ String values = StringHelper.before(remainder, ")");
+ if (values == null || ObjectHelper.isEmpty(values)) {
+ throw new SimpleParserException(
+ "Valid syntax: ${attachmentContent(key)} was: "
+ + function,
+ index);
+ }
+ return AttachmentExpressionBuilder.attachmentContent(values, null);
+ }
+ remainder = ifStartsWithReturnRemainder("attachmentContentAs(", function);
+ if (remainder != null) {
+ String values = StringHelper.before(remainder, ")");
+ if (values == null || ObjectHelper.isEmpty(values)) {
+ throw new SimpleParserException(
+ "Valid syntax: ${attachmentContentAs(key,type)} was: "
+ + function,
+ index);
+ }
+ String[] tokens = StringQuoteHelper.splitSafeQuote(values, ',', false);
+ if (tokens.length > 2) {
+ throw new SimpleParserException(
+ "Valid syntax: ${attachmentContentAs(key,type)} was: " + function, index);
+ }
+ String key = tokens[0];
+ String type = tokens[1];
+ return AttachmentExpressionBuilder.attachmentContent(key, type);
+ }
+ remainder = ifStartsWithReturnRemainder("attachmentContentAsText(", function);
+ if (remainder != null) {
+ String values = StringHelper.before(remainder, ")");
+ if (values == null || ObjectHelper.isEmpty(values)) {
+ throw new SimpleParserException(
+ "Valid syntax: ${attachmentContentAsText(key)}} was: "
+ + function,
+ index);
+ }
+ return AttachmentExpressionBuilder.attachmentContent(values, "String");
+ }
+
+ remainder = ifStartsWithReturnRemainder("attachmentHeaderAs(", function);
+ if (remainder != null) {
+ String values = StringHelper.before(remainder, ")");
+ if (values == null || ObjectHelper.isEmpty(values)) {
+ throw new SimpleParserException(
+ "Valid syntax: ${attachmentHeaderAs(key,name,type)} was: " + function, index);
+ }
+ String[] tokens = StringQuoteHelper.splitSafeQuote(values, ',', false);
+ if (tokens.length != 3) {
+ throw new SimpleParserException(
+ "Valid syntax: ${attachmentHeaderAs(key,name,type)} was: " + function,
+ index);
+ }
+ String key = tokens[0];
+ String name = tokens[1];
+ String type = tokens[2];
+ return AttachmentExpressionBuilder.attachmentContentHeader(key, name, type);
+ }
+ remainder = ifStartsWithReturnRemainder("attachmentHeader(", function);
+ if (remainder != null) {
+ String values = StringHelper.before(remainder, ")");
+ if (values == null || ObjectHelper.isEmpty(values)) {
+ throw new SimpleParserException(
+ "Valid syntax: ${attachmentHeader(key,name)} was: "
+ + function,
+ index);
+ }
+ String[] tokens = StringQuoteHelper.splitSafeQuote(values, ',', false);
+ if (tokens.length != 2) {
+ throw new SimpleParserException(
+ "Valid syntax: ${attachmentHeader(key,name)} was: " + function,
+ index);
+ }
+ String key = tokens[0];
+ String name = tokens[1];
+ return AttachmentExpressionBuilder.attachmentContentHeader(key, name, null);
+ }
+
+ remainder = ifStartsWithReturnRemainder("attachmentContentType(", function);
+ if (remainder != null) {
+ String key = StringHelper.before(remainder, ")");
+ if (key == null || ObjectHelper.isEmpty(key)) {
+ throw new SimpleParserException(
+ "Valid syntax: ${attachmentContentType(key)} was: "
+ + function,
+ index);
+ }
+ return AttachmentExpressionBuilder.attachmentContentType(key);
+ }
+
+ remainder = ifStartsWithReturnRemainder("attachment", function);
+ if (remainder != null) {
+ // remove leading character (dot, colon or ?)
+ if (remainder.startsWith(".") || remainder.startsWith(":") || remainder.startsWith("?")) {
+ remainder = remainder.substring(1);
+ }
+ // remove starting and ending brackets
+ if (remainder.startsWith("[") && remainder.endsWith("]")) {
+ remainder = remainder.substring(1, remainder.length() - 1);
+ }
+
+ // validate syntax
+ boolean invalid = OgnlHelper.isInvalidValidOgnlExpression(remainder);
+ if (invalid) {
+ throw new SimpleParserException("Valid syntax: ${attachment.OGNL} was: " + function, index);
+ }
+
+ if (OgnlHelper.isValidOgnlExpression(remainder)) {
+ // ognl based attachment
+ return AttachmentExpressionBuilder.attachmentOgnlExpression(remainder);
+ } else {
+ // regular attachment
+ return AttachmentExpressionBuilder.attachmentExpression(remainder);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String createCode(CamelContext camelContext, String function, int index) {
+ if ("attachments".equals(function)) {
+ return "attachments(exchange)";
+ } else if ("attachments.size".equals(function) || "attachments.size()".equals(function)
+ || "attachments.length".equals(function) || "attachments.length()".equals(function)) {
+ return "attachmentsSize(exchange)";
+ }
+
+ String remainder = ifStartsWithReturnRemainder("attachmentContent(", function);
+ if (remainder != null) {
+ String values = StringHelper.before(remainder, ")");
+ if (values == null || ObjectHelper.isEmpty(values)) {
+ throw new SimpleParserException(
+ "Valid syntax: ${attachmentContent(key)} was: "
+ + function,
+ index);
+ }
+ String key = StringHelper.removeQuotes(values);
+ key = key.trim();
+ return "attachmentContent(exchange, \"" + key + "\")";
+ }
+ remainder = ifStartsWithReturnRemainder("attachmentContentAs(", function);
+ if (remainder != null) {
+ String values = StringHelper.before(remainder, ")");
+ if (values == null || ObjectHelper.isEmpty(values)) {
+ throw new SimpleParserException(
+ "Valid syntax: ${attachmentContentAs(key,type)} was: "
+ + function,
+ index);
+ }
+ String[] tokens = StringQuoteHelper.splitSafeQuote(values, ',', false);
+ if (tokens.length > 2) {
+ throw new SimpleParserException(
+ "Valid syntax: ${attachmentContentAs(key,type)} was: " + function, index);
+ }
+ String key = tokens[0];
+ String type = tokens[1];
+ key = StringHelper.removeQuotes(key);
+ key = key.trim();
+ type = appendClass(type);
+ type = type.replace('$', '.');
+ type = type.trim();
+ return "attachmentContentAs(exchange, \"" + key + "\", " + type + ")";
+ }
+ remainder = ifStartsWithReturnRemainder("attachmentContentAsText(", function);
+ if (remainder != null) {
+ String values = StringHelper.before(remainder, ")");
+ if (values == null || ObjectHelper.isEmpty(values)) {
+ throw new SimpleParserException(
+ "Valid syntax: ${attachmentContentAsText(key)}} was: "
+ + function,
+ index);
+ }
+ String key = StringHelper.removeQuotes(values);
+ key = key.trim();
+ return "attachmentContentAsText(exchange, \"" + key + "\")";
+ }
+ remainder = ifStartsWithReturnRemainder("attachmentContentType(", function);
+ if (remainder != null) {
+ String key = StringHelper.before(remainder, ")");
+ if (key == null || ObjectHelper.isEmpty(key)) {
+ throw new SimpleParserException(
+ "Valid syntax: ${attachmentContentType(key)} was: "
+ + function,
+ index);
+ }
+ key = StringHelper.removeQuotes(key);
+ key = key.trim();
+ return "attachmentContentType(exchange, \"" + key + "\")";
+ }
+ remainder = ifStartsWithReturnRemainder("attachmentHeader(", function);
+ if (remainder != null) {
+ String values = StringHelper.before(remainder, ")");
+ if (values == null || ObjectHelper.isEmpty(values)) {
+ throw new SimpleParserException(
+ "Valid syntax: ${attachmentHeader(key,name)} was: "
+ + function,
+ index);
+ }
+ String[] tokens = StringQuoteHelper.splitSafeQuote(values, ',', false);
+ if (tokens.length != 2) {
+ throw new SimpleParserException(
+ "Valid syntax: ${attachmentHeader(key,name)} was: " + function, index);
+ }
+ String key = tokens[0];
+ String name = tokens[1];
+ key = StringHelper.removeQuotes(key);
+ key = key.trim();
+ name = StringHelper.removeQuotes(name);
+ name = name.trim();
+ return "attachmentHeader(exchange, \"" + key + "\", \"" + name + "\")";
+ }
+ remainder = ifStartsWithReturnRemainder("attachmentHeaderAs(", function);
+ if (remainder != null) {
+ String values = StringHelper.before(remainder, ")");
+ if (values == null || ObjectHelper.isEmpty(values)) {
+ throw new SimpleParserException(
+ "Valid syntax: ${attachmentHeaderAs(key,name,type)} was: "
+ + function,
+ index);
+ }
+ String[] tokens = StringQuoteHelper.splitSafeQuote(values, ',', false);
+ if (tokens.length != 3) {
+ throw new SimpleParserException(
+ "Valid syntax: ${attachmentHeaderAs(key,name,type)} was: " + function, index);
+ }
+ String key = tokens[0];
+ String name = tokens[1];
+ String type = tokens[2];
+ key = StringHelper.removeQuotes(key);
+ key = key.trim();
+ name = StringHelper.removeQuotes(name);
+ name = name.trim();
+ type = appendClass(type);
+ type = type.replace('$', '.');
+ type = type.trim();
+ return "attachmentHeaderAs(exchange, \"" + key + "\", \"" + name + "\", " + type + ")";
+ }
+
+ return null;
+ }
+
+ private String ifStartsWithReturnRemainder(String prefix, String text) {
+ if (text.startsWith(prefix)) {
+ String remainder = text.substring(prefix.length());
+ if (!remainder.isEmpty()) {
+ return remainder;
+ }
+ }
+ return null;
+ }
+
+ private static String appendClass(String type) {
+ type = StringHelper.removeQuotes(type);
+ if (!type.endsWith(".class")) {
+ type = type + ".class";
+ }
+ return type;
+ }
+
+}
diff --git a/components/camel-attachments/src/test/data/123.txt b/components/camel-attachments/src/test/data/123.txt
new file mode 100644
index 00000000000..ee2b8364542
--- /dev/null
+++ b/components/camel-attachments/src/test/data/123.txt
@@ -0,0 +1 @@
+456
\ No newline at end of file
diff --git a/components/camel-attachments/src/test/java/org/apache/camel/attachment/SimpleAttachmentTest.java b/components/camel-attachments/src/test/java/org/apache/camel/attachment/SimpleAttachmentTest.java
new file mode 100644
index 00000000000..97221b87bd0
--- /dev/null
+++ b/components/camel-attachments/src/test/java/org/apache/camel/attachment/SimpleAttachmentTest.java
@@ -0,0 +1,137 @@
+/*
+ * 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.attachment;
+
+import java.io.File;
+
+import jakarta.activation.DataHandler;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.test.junit5.LanguageTestSupport;
+import org.junit.jupiter.api.Test;
+
+public class SimpleAttachmentTest extends LanguageTestSupport {
+
+ @Override
+ protected String getLanguageName() {
+ return "simple";
+ }
+
+ @Override
+ protected void populateExchange(Exchange exchange) {
+ super.populateExchange(exchange);
+
+ DefaultAttachment da
+ = new DefaultAttachment(new DataHandler(new CamelFileDataSource(new File("src/test/data/message1.xml"))));
+ da.addHeader("orderId", "123");
+ exchange.getIn(AttachmentMessage.class).addAttachmentObject("message1.xml", da);
+
+ da = new DefaultAttachment(new DataHandler(new CamelFileDataSource(new File("src/test/data/message2.xml"))));
+ da.addHeader("orderId", "456");
+ exchange.getIn(AttachmentMessage.class).addAttachmentObject("message2.xml", da);
+
+ da = new DefaultAttachment(new DataHandler(new CamelFileDataSource(new File("src/test/data/123.txt"))));
+ da.addHeader("orderId", "0");
+ exchange.getIn(AttachmentMessage.class).addAttachmentObject("123.txt", da);
+ }
+
+ @Test
+ public void testAttachments() throws Exception {
+ var map = exchange.getMessage(AttachmentMessage.class).getAttachments();
+ assertExpression("${attachments}", map);
+ assertExpression("${attachments.size}", 3);
+ assertExpression("${attachments.length}", 3);
+
+ exchange.getMessage(AttachmentMessage.class).removeAttachment("message1.xml");
+ assertExpression("${attachments.size}", 2);
+ assertExpression("${attachments.length}", 2);
+
+ exchange.getMessage(AttachmentMessage.class).removeAttachment("message2.xml");
+ assertExpression("${attachments.size}", 1);
+ assertExpression("${attachments.length}", 1);
+
+ exchange.getMessage(AttachmentMessage.class).removeAttachment("123.txt");
+ assertExpression("${attachments.size}", 0);
+ assertExpression("${attachments.length}", 0);
+ }
+
+ @Test
+ public void testAttachmentContent() throws Exception {
+ var map = exchange.getMessage(AttachmentMessage.class).getAttachments();
+ Object is1 = map.get("message1.xml").getContent();
+ String xml1 = context.getTypeConverter().convertTo(String.class, is1);
+ Object is2 = map.get("message2.xml").getContent();
+ String xml2 = context.getTypeConverter().convertTo(String.class, is2);
+
+ assertExpression("${attachmentContent(message1.xml)}", xml1);
+ assertExpression("${attachmentContentAs(message2.xml,String)}", xml2);
+ assertExpression("${attachmentContentAsText(message2.xml)}", xml2);
+ assertExpression("${attachmentContent(123.txt)}", "456");
+ assertExpression("${attachmentContentAs(123.txt,String)}", "456");
+ assertExpression("${attachmentContentAsText(123.txt)}", "456");
+ }
+
+ @Test
+ public void testAttachmentContentHeader() throws Exception {
+ assertExpression("${attachmentHeader(message1.xml,orderId)}", "123");
+ assertExpression("${attachmentHeader(message1.xml,foo)}", null);
+ assertExpression("${attachmentHeaderAs(message2.xml,orderId,int)}", 456);
+ assertExpression("${attachmentHeader(123.txt,orderId)}", 0);
+ assertExpression("${attachmentHeader(123.txt,unknown)}", null);
+ }
+
+ @Test
+ public void testAttachmentContentType() throws Exception {
+ assertExpression("${attachmentContentType(message1.xml)}", "application/octet-stream");
+ assertExpression("${attachmentContentType(message2.xml)}", "application/octet-stream");
+ assertExpression("${attachmentContentType(123.txt)}", "application/octet-stream");
+ }
+
+ @Test
+ public void testAttachmentOgnlByName() throws Exception {
+ var map = exchange.getMessage(AttachmentMessage.class).getAttachments();
+ Object is1 = map.get("message1.xml").getContent();
+ String xml1 = context.getTypeConverter().convertTo(String.class, is1);
+ Object is2 = map.get("message2.xml").getContent();
+ String xml2 = context.getTypeConverter().convertTo(String.class, is2);
+
+ assertExpression("${attachment[message1.xml].name}", "message1.xml");
+ assertExpression("${attachment[message1.xml].contentType}", "application/octet-stream");
+ assertExpression("${attachment[message1.xml].content}", xml1);
+
+ assertExpression("${attachment[message2.xml].name}", "message2.xml");
+ assertExpression("${attachment[message2.xml].contentType}", "application/octet-stream");
+ assertExpression("${attachment[message2.xml].content}", xml2);
+
+ assertExpression("${attachment[123.txt].name}", "123.txt");
+ assertExpression("${attachment[123.txt].contentType}", "application/octet-stream");
+ assertExpression("${attachment[123.txt].content}", "456");
+ }
+
+ @Test
+ public void testAttachmentOgnlByIndex() throws Exception {
+ assertExpression("${attachment[0].name}", "message1.xml");
+ assertExpression("${attachment[0].contentType}", "application/octet-stream");
+
+ assertExpression("${attachment[1].name}", "message2.xml");
+ assertExpression("${attachment[1].contentType}", "application/octet-stream");
+
+ assertExpression("${attachment[2].name}", "123.txt");
+ assertExpression("${attachment[2].contentType}", "application/octet-stream");
+ assertExpression("${attachment[2].content}", "456");
+ }
+}
diff --git a/components/camel-csimple-joor/pom.xml b/components/camel-csimple-joor/pom.xml
index 5ee38dd7216..79a9946b6f2 100644
--- a/components/camel-csimple-joor/pom.xml
+++ b/components/camel-csimple-joor/pom.xml
@@ -62,6 +62,11 @@
<type>test-jar</type>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-attachments</artifactId>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest</artifactId>
diff --git a/components/camel-csimple-joor/src/main/java/org/apache/camel/language/csimple/joor/JoorCSimpleCompiler.java b/components/camel-csimple-joor/src/main/java/org/apache/camel/language/csimple/joor/JoorCSimpleCompiler.java
index 4ef12b57527..1ba78af5813 100644
--- a/components/camel-csimple-joor/src/main/java/org/apache/camel/language/csimple/joor/JoorCSimpleCompiler.java
+++ b/components/camel-csimple-joor/src/main/java/org/apache/camel/language/csimple/joor/JoorCSimpleCompiler.java
@@ -17,17 +17,21 @@
package org.apache.camel.language.csimple.joor;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.camel.CamelContext;
+import org.apache.camel.CamelContextAware;
import org.apache.camel.StaticService;
import org.apache.camel.language.csimple.CSimpleCodeGenerator;
import org.apache.camel.language.csimple.CSimpleCompiler;
import org.apache.camel.language.csimple.CSimpleExpression;
import org.apache.camel.language.csimple.CSimpleGeneratedCode;
+import org.apache.camel.spi.SimpleLanguageFunctionFactory;
import org.apache.camel.spi.annotations.JdkService;
+import org.apache.camel.support.ResolverHelper;
import org.apache.camel.support.ScriptHelper;
import org.apache.camel.support.service.ServiceSupport;
import org.apache.camel.util.StopWatch;
@@ -40,15 +44,26 @@ import org.slf4j.LoggerFactory;
* jOOR compiler for csimple language.
*/
@JdkService(CSimpleCompiler.FACTORY)
-public class JoorCSimpleCompiler extends ServiceSupport implements CSimpleCompiler, StaticService {
+public class JoorCSimpleCompiler extends ServiceSupport implements CSimpleCompiler, CamelContextAware, StaticService {
private static final Logger LOG = LoggerFactory.getLogger(JoorCSimpleCompiler.class);
private static final AtomicInteger UUID = new AtomicInteger();
+ private CamelContext camelContext;
private Set<String> imports = new TreeSet<>();
private Map<String, String> aliases;
private int counter;
private long taken;
+ @Override
+ public CamelContext getCamelContext() {
+ return camelContext;
+ }
+
+ @Override
+ public void setCamelContext(CamelContext camelContext) {
+ this.camelContext = camelContext;
+ }
+
public Set<String> getImports() {
return imports;
}
@@ -127,6 +142,7 @@ public class JoorCSimpleCompiler extends ServiceSupport implements CSimpleCompil
script = script.trim();
CSimpleCodeGenerator generator = new CSimpleCodeGenerator();
+ generator.setCamelContext(camelContext);
if (aliases != null && !aliases.isEmpty()) {
generator.setAliases(aliases);
}
@@ -150,7 +166,17 @@ public class JoorCSimpleCompiler extends ServiceSupport implements CSimpleCompil
@Override
protected void doStart() throws Exception {
- // noop
+ // check if camel-attachment is on classpath which then includes custom csimple functions
+ if (camelContext != null) {
+ Optional<SimpleLanguageFunctionFactory> factory = ResolverHelper.resolveService(
+ camelContext,
+ camelContext.getCamelContextExtension().getBootstrapFactoryFinder(),
+ SimpleLanguageFunctionFactory.FACTORY + "/camel-attachments",
+ SimpleLanguageFunctionFactory.class);
+ if (factory.isPresent()) {
+ addImport("import static org.apache.camel.attachment.CSimpleAttachmentHelper.*");
+ }
+ }
}
@Override
diff --git a/components/camel-csimple-joor/src/test/data/123.txt b/components/camel-csimple-joor/src/test/data/123.txt
new file mode 100644
index 00000000000..ee2b8364542
--- /dev/null
+++ b/components/camel-csimple-joor/src/test/data/123.txt
@@ -0,0 +1 @@
+456
\ No newline at end of file
diff --git a/components/camel-csimple-joor/src/test/data/message1.xml b/components/camel-csimple-joor/src/test/data/message1.xml
new file mode 100644
index 00000000000..1a85d067b42
--- /dev/null
+++ b/components/camel-csimple-joor/src/test/data/message1.xml
@@ -0,0 +1,24 @@
+<?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.
+
+-->
+<person user="james">
+ <firstName>James</firstName>
+ <lastName>Strachan</lastName>
+ <city>London</city>
+</person>
\ No newline at end of file
diff --git a/components/camel-csimple-joor/src/test/data/message2.xml b/components/camel-csimple-joor/src/test/data/message2.xml
new file mode 100644
index 00000000000..73c88bfa2e5
--- /dev/null
+++ b/components/camel-csimple-joor/src/test/data/message2.xml
@@ -0,0 +1,24 @@
+<?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.
+
+-->
+<person user="hiram">
+ <firstName>Hiram</firstName>
+ <lastName>Chirino</lastName>
+ <city>Tampa</city>
+</person>
\ No newline at end of file
diff --git a/components/camel-csimple-joor/src/test/java/org/apache/camel/language/csimple/joor/CSimpleAttachmentsTest.java b/components/camel-csimple-joor/src/test/java/org/apache/camel/language/csimple/joor/CSimpleAttachmentsTest.java
new file mode 100644
index 00000000000..ded77bf8737
--- /dev/null
+++ b/components/camel-csimple-joor/src/test/java/org/apache/camel/language/csimple/joor/CSimpleAttachmentsTest.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.language.csimple.joor;
+
+import java.io.File;
+
+import jakarta.activation.DataHandler;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.attachment.AttachmentMessage;
+import org.apache.camel.attachment.CamelFileDataSource;
+import org.apache.camel.attachment.DefaultAttachment;
+import org.apache.camel.test.junit5.LanguageTestSupport;
+import org.junit.jupiter.api.Test;
+
+public class CSimpleAttachmentsTest extends LanguageTestSupport {
+
+ @Override
+ protected String getLanguageName() {
+ return "csimple";
+ }
+
+ @Override
+ protected void populateExchange(Exchange exchange) {
+ super.populateExchange(exchange);
+
+ DefaultAttachment da
+ = new DefaultAttachment(new DataHandler(new CamelFileDataSource(new File("src/test/data/message1.xml"))));
+ da.addHeader("orderId", "123");
+ exchange.getIn(AttachmentMessage.class).addAttachmentObject("message1.xml", da);
+
+ da = new DefaultAttachment(new DataHandler(new CamelFileDataSource(new File("src/test/data/message2.xml"))));
+ da.addHeader("orderId", "456");
+ exchange.getIn(AttachmentMessage.class).addAttachmentObject("message2.xml", da);
+
+ da = new DefaultAttachment(new DataHandler(new CamelFileDataSource(new File("src/test/data/123.txt"))));
+ da.addHeader("orderId", "0");
+ exchange.getIn(AttachmentMessage.class).addAttachmentObject("123.txt", da);
+ }
+
+ @Test
+ public void testAttachments() throws Exception {
+ var map = exchange.getMessage(AttachmentMessage.class).getAttachments();
+ assertExpression("${attachments}", map);
+ assertExpression("${attachments.size}", 3);
+ assertExpression("${attachments.length}", 3);
+
+ exchange.getMessage(AttachmentMessage.class).removeAttachment("message1.xml");
+ assertExpression("${attachments.size}", 2);
+ assertExpression("${attachments.length}", 2);
+
+ exchange.getMessage(AttachmentMessage.class).removeAttachment("message2.xml");
+ assertExpression("${attachments.size}", 1);
+ assertExpression("${attachments.length}", 1);
+
+ exchange.getMessage(AttachmentMessage.class).removeAttachment("123.txt");
+ assertExpression("${attachments.size}", 0);
+ assertExpression("${attachments.length}", 0);
+ }
+
+ @Test
+ public void testAttachmentContent() throws Exception {
+ var map = exchange.getMessage(AttachmentMessage.class).getAttachments();
+ Object is1 = map.get("message1.xml").getContent();
+ String xml1 = context.getTypeConverter().convertTo(String.class, is1);
+ Object is2 = map.get("message2.xml").getContent();
+ String xml2 = context.getTypeConverter().convertTo(String.class, is2);
+
+ assertExpression("${attachmentContent(message1.xml)}", xml1);
+ assertExpression("${attachmentContentAs(message2.xml,String)}", xml2);
+ assertExpression("${attachmentContentAsText(message2.xml)}", xml2);
+ assertExpression("${attachmentContent(123.txt)}", "456");
+ assertExpression("${attachmentContentAs(123.txt,String)}", "456");
+ assertExpression("${attachmentContentAsText(123.txt)}", "456");
+ }
+
+ @Test
+ public void testAttachmentContentHeader() throws Exception {
+ assertExpression("${attachmentHeader(message1.xml,orderId)}", "123");
+ assertExpression("${attachmentHeader(message1.xml,foo)}", null);
+ assertExpression("${attachmentHeaderAs(message2.xml,orderId,int)}", 456);
+ assertExpression("${attachmentHeader(123.txt,orderId)}", 0);
+ assertExpression("${attachmentHeader(123.txt,unknown)}", null);
+ }
+
+ @Test
+ public void testAttachmentContentType() throws Exception {
+ assertExpression("${attachmentContentType(message1.xml)}", "application/octet-stream");
+ assertExpression("${attachmentContentType(message2.xml)}", "application/octet-stream");
+ assertExpression("${attachmentContentType(123.txt)}", "text/plain");
+ }
+
+}
diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/BeanProxyFactory.java b/core/camel-api/src/main/java/org/apache/camel/spi/BeanProxyFactory.java
index 424ba9c9955..0ac6bd76cec 100644
--- a/core/camel-api/src/main/java/org/apache/camel/spi/BeanProxyFactory.java
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/BeanProxyFactory.java
@@ -31,7 +31,7 @@ public interface BeanProxyFactory {
String FACTORY = "bean-proxy-factory";
/**
- * Creates a proxy bean facaded with the interfaces that when invoked will send the data as a message to a Camel
+ * Creates a proxy bean facade with the interfaces that when invoked will send the data as a message to a Camel
* endpoint.
*
* @param endpoint the endpoint to send to when the proxy is invoked
diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/SimpleLanguageFunctionFactory.java b/core/camel-api/src/main/java/org/apache/camel/spi/SimpleLanguageFunctionFactory.java
new file mode 100644
index 00000000000..3eb0b27c9ce
--- /dev/null
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/SimpleLanguageFunctionFactory.java
@@ -0,0 +1,55 @@
+/*
+ * 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.Expression;
+
+/**
+ * A factory for extending the simple language with functions from external components.
+ * <p/>
+ * This requires to have the external component JAR on the classpath.
+ */
+public interface SimpleLanguageFunctionFactory {
+
+ /**
+ * Service factory key.
+ */
+ String FACTORY = "simple-function-factory";
+
+ /**
+ * Creates the {@link Expression} that performs the function.
+ *
+ * @param camelContext the camel context
+ * @param function the function
+ * @param index index of the function in the literal input
+ *
+ * @return the created function as an expression, or <tt>null</tt> if not supported by this factory.
+ */
+ Expression createFunction(CamelContext camelContext, String function, int index);
+
+ /**
+ * Creates the Java source code that performs the function (for csimple).
+ *
+ * @param camelContext the camel context
+ * @param function the function
+ * @param index index of the function in the literal input
+ * @return the source code or <tt>null</tt> if not supported by this factory.
+ */
+ String createCode(CamelContext camelContext, String function, int index);
+
+}
diff --git a/core/camel-core-languages/src/main/docs/modules/languages/pages/simple-language.adoc b/core/camel-core-languages/src/main/docs/modules/languages/pages/simple-language.adoc
index e90f73efa7c..3d9afef38d0 100644
--- a/core/camel-core-languages/src/main/docs/modules/languages/pages/simple-language.adoc
+++ b/core/camel-core-languages/src/main/docs/modules/languages/pages/simple-language.adoc
@@ -128,6 +128,7 @@ Camel OGNL expression.
classname
|headers |Map |refer to the headers
+|headers.size |int |The number of headers
|variable.foo |Object |refer to the foo variable
@@ -140,6 +141,7 @@ value using a Camel OGNL expression.
classname
|variables |Map |refer to the variables
+|variables.size |int |The number of variables
|exchangeProperty.foo |Object |refer to the foo property on the exchange
@@ -321,6 +323,29 @@ true, otherwise the value of `falseExp` is returned. This function is similar to
|=======================================================================
+== Attachment functions
+
+From *Camel 4.10* onwards then Camel has built-in attachment functions making it easy to obtain
+details from attachments stored on the Camel Message such as from HTTP file uploads, email with file attachments etc.
+
+This requires having `camel-attachments` JAR on the classpath.
+
+[width="100%",cols="10%,10%,80%",options="header",]
+|=======================================================================
+|Function |Type |Description
+
+|attachments |Map | All the attachments as a `Map<String,DataHandler>`.
+|attachments.size | int | The number of attachments. Is 0 if there are no attachments.
+|attachmentContentAsText(key) | String | The content of the attachment as text (ie `String`).
+|attachmentContent(key) | Object | The content of the attachment.
+|attachmentContentAs(key,_type_) | Object | The content of the attachment, converted to the given type.
+|attachmentHeader(key,name) | Object | The attachment header with the given name.
+|attachmentHeaderAs(key,name,_type_) | Object | The attachment header with the given name, converted to the given type.
+|attachment[key] | DataHandler | The `DataHandler` for the given attachment.
+|attachment.*OGNL* | Object | refer to the foo attachment on the exchange and invoke its value using a Camel OGNL expression.
+|=======================================================================
+
+
== OGNL expression support
The xref:simple-language.adoc[Simple] and xref:simple-language.adoc[Bean] languages support a Camel xref:ognl-language.adoc[OGNL] notation for invoking beans in a chain like fashion.
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/csimple/CSimpleCodeGenerator.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/csimple/CSimpleCodeGenerator.java
index eaf25ee5fb7..f53be1bed93 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/csimple/CSimpleCodeGenerator.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/csimple/CSimpleCodeGenerator.java
@@ -22,6 +22,8 @@ import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicInteger;
+import org.apache.camel.CamelContext;
+import org.apache.camel.CamelContextAware;
import org.apache.camel.util.StringHelper;
/**
@@ -29,13 +31,24 @@ import org.apache.camel.util.StringHelper;
*
* @see CSimpleGeneratedCode
*/
-public class CSimpleCodeGenerator {
+public class CSimpleCodeGenerator implements CamelContextAware {
private static final AtomicInteger UUID = new AtomicInteger();
+ private CamelContext camelContext;
private Set<String> imports = new TreeSet<>();
private Map<String, String> aliases = new HashMap<>();
+ @Override
+ public CamelContext getCamelContext() {
+ return camelContext;
+ }
+
+ @Override
+ public void setCamelContext(CamelContext camelContext) {
+ this.camelContext = camelContext;
+ }
+
public Set<String> getImports() {
return imports;
}
@@ -127,14 +140,14 @@ public class CSimpleCodeGenerator {
if (predicate) {
CSimplePredicateParser parser = new CSimplePredicateParser();
- script = parser.parsePredicate(script);
+ script = parser.parsePredicate(camelContext, script);
if (script.isBlank()) {
// a predicate that is whitespace is regarded as false
script = "false";
}
} else {
CSimpleExpressionParser parser = new CSimpleExpressionParser();
- script = parser.parseExpression(script);
+ script = parser.parseExpression(camelContext, script);
if (script.isBlank()) {
// an expression can be whitespace, but then we need to wrap this in quotes
script = "\"" + script + "\"";
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/csimple/CSimpleExpressionParser.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/csimple/CSimpleExpressionParser.java
index 0922212d514..bc06af83f13 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/csimple/CSimpleExpressionParser.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/csimple/CSimpleExpressionParser.java
@@ -16,6 +16,7 @@
*/
package org.apache.camel.language.csimple;
+import org.apache.camel.CamelContext;
import org.apache.camel.language.simple.SimpleExpressionParser;
/**
@@ -24,8 +25,12 @@ import org.apache.camel.language.simple.SimpleExpressionParser;
public class CSimpleExpressionParser {
public String parseExpression(String expression) {
+ return parseExpression(null, expression);
+ }
+
+ public String parseExpression(CamelContext camelContext, String expression) {
// reuse simple language parser but output the result as java code
- SimpleExpressionParser parser = new SimpleExpressionParser(null, expression, true, null);
+ SimpleExpressionParser parser = new SimpleExpressionParser(camelContext, expression, true, null);
return parser.parseCode();
}
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/csimple/CSimpleHelper.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/csimple/CSimpleHelper.java
index e62891dba99..428f79be280 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/csimple/CSimpleHelper.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/csimple/CSimpleHelper.java
@@ -205,6 +205,10 @@ public final class CSimpleHelper {
return exchange.getVariables();
}
+ public static int variablesSize(Exchange exchange) {
+ return exchange.getVariables().size();
+ }
+
public static String bodyOneLine(Exchange exchange) {
String body = exchange.getIn().getBody(String.class);
if (body == null) {
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/csimple/CSimplePredicateParser.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/csimple/CSimplePredicateParser.java
index 3ba6f1a5a02..b2ce514f111 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/csimple/CSimplePredicateParser.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/csimple/CSimplePredicateParser.java
@@ -16,6 +16,7 @@
*/
package org.apache.camel.language.csimple;
+import org.apache.camel.CamelContext;
import org.apache.camel.language.simple.SimplePredicateParser;
/**
@@ -24,8 +25,12 @@ import org.apache.camel.language.simple.SimplePredicateParser;
public class CSimplePredicateParser {
public String parsePredicate(String predicate) {
+ return parsePredicate(null, predicate);
+ }
+
+ public String parsePredicate(CamelContext camelContext, String predicate) {
// reuse simple language parser but output the result as java code
- SimplePredicateParser parser = new SimplePredicateParser(null, predicate, true, null);
+ SimplePredicateParser parser = new SimplePredicateParser(camelContext, predicate, true, null);
return parser.parseCode();
}
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionBuilder.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionBuilder.java
index dab6fdb1ec4..e616167f8d1 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionBuilder.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionBuilder.java
@@ -1125,9 +1125,7 @@ public final class SimpleExpressionBuilder {
public String toString() {
return "type:" + name;
}
- }
-
- ;
+ };
}
/**
@@ -1197,8 +1195,8 @@ public final class SimpleExpressionBuilder {
private Expression ognlExpression;
private Language beanLanguage;
- KeyedOgnlExpressionAdapter(String ognl, String toStringValue,
- KeyedEntityRetrievalStrategy keyedEntityRetrievalStrategy) {
+ public KeyedOgnlExpressionAdapter(String ognl, String toStringValue,
+ KeyedEntityRetrievalStrategy keyedEntityRetrievalStrategy) {
this.ognl = ognl;
this.toStringValue = toStringValue;
this.keyedEntityRetrievalStrategy = keyedEntityRetrievalStrategy;
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionParser.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionParser.java
index 56260a8b7b7..1ee19757064 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionParser.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionParser.java
@@ -206,7 +206,7 @@ public class SimpleExpressionParser extends BaseSimpleParser {
StringBuilder sb = new StringBuilder(256);
boolean firstIsLiteral = false;
for (SimpleNode node : nodes) {
- String exp = node.createCode(expression);
+ String exp = node.createCode(camelContext, expression);
if (exp != null) {
if (sb.isEmpty() && node instanceof LiteralNode) {
firstIsLiteral = true;
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimplePredicateParser.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimplePredicateParser.java
index 66eef5fd785..32386e86852 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimplePredicateParser.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimplePredicateParser.java
@@ -165,7 +165,7 @@ public class SimplePredicateParser extends BaseSimpleParser {
protected String doParseCode() {
StringBuilder sb = new StringBuilder(256);
for (SimpleNode node : nodes) {
- String exp = node.createCode(expression);
+ String exp = node.createCode(camelContext, expression);
SimpleExpressionParser.parseLiteralNode(sb, node, exp);
}
String code = sb.toString();
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/BaseSimpleNode.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/BaseSimpleNode.java
index 374212071f3..ff77e75cb40 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/BaseSimpleNode.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/BaseSimpleNode.java
@@ -16,6 +16,7 @@
*/
package org.apache.camel.language.simple.ast;
+import org.apache.camel.CamelContext;
import org.apache.camel.language.simple.types.SimpleParserException;
import org.apache.camel.language.simple.types.SimpleToken;
@@ -40,10 +41,11 @@ public abstract class BaseSimpleNode implements SimpleNode {
return token.getText();
}
- protected static String createCode(String expression, CompositeNodes block) throws SimpleParserException {
+ protected static String createCode(CamelContext camelContext, String expression, CompositeNodes block)
+ throws SimpleParserException {
String answer = null;
if (block != null) {
- answer = block.createCode(expression);
+ answer = block.createCode(camelContext, expression);
}
// use double quote as this become used as string literals in the generated code
if (answer == null) {
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/BinaryExpression.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/BinaryExpression.java
index e6349c533d7..64fec4a9d5b 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/BinaryExpression.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/BinaryExpression.java
@@ -282,16 +282,16 @@ public class BinaryExpression extends BaseSimpleNode {
}
@Override
- public String createCode(String expression) throws SimpleParserException {
- return BaseSimpleParser.CODE_START + doCreateCode(expression) + BaseSimpleParser.CODE_END;
+ public String createCode(CamelContext camelContext, String expression) throws SimpleParserException {
+ return BaseSimpleParser.CODE_START + doCreateCode(camelContext, expression) + BaseSimpleParser.CODE_END;
}
- private String doCreateCode(String expression) throws SimpleParserException {
+ private String doCreateCode(CamelContext camelContext, String expression) throws SimpleParserException {
org.apache.camel.util.ObjectHelper.notNull(left, "left node", this);
org.apache.camel.util.ObjectHelper.notNull(right, "right node", this);
- final String leftExp = left.createCode(expression);
- final String rightExp = right.createCode(expression);
+ final String leftExp = left.createCode(camelContext, expression);
+ final String rightExp = right.createCode(camelContext, expression);
if (operator == BinaryOperatorType.EQ) {
return "isEqualTo(exchange, " + leftExp + ", " + rightExp + ")";
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/BooleanExpression.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/BooleanExpression.java
index eec56f05212..ea26653b471 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/BooleanExpression.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/BooleanExpression.java
@@ -53,7 +53,7 @@ public class BooleanExpression extends BaseSimpleNode {
}
@Override
- public String createCode(String expression) throws SimpleParserException {
+ public String createCode(CamelContext camelContext, String expression) throws SimpleParserException {
return value ? "true" : "false";
}
}
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/CompositeNodes.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/CompositeNodes.java
index 3182e8b56fb..b7cf251c8dd 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/CompositeNodes.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/CompositeNodes.java
@@ -69,15 +69,15 @@ public class CompositeNodes extends BaseSimpleNode {
}
@Override
- public String createCode(String expression) throws SimpleParserException {
+ public String createCode(CamelContext camelContext, String expression) throws SimpleParserException {
if (children.isEmpty()) {
return null;
} else if (children.size() == 1) {
- return children.get(0).createCode(expression);
+ return children.get(0).createCode(camelContext, expression);
} else {
StringBuilder sb = new StringBuilder(256);
for (SimpleNode child : children) {
- String code = child.createCode(expression);
+ String code = child.createCode(camelContext, expression);
if (code != null) {
sb.append(code);
}
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/DoubleQuoteEnd.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/DoubleQuoteEnd.java
index 721652022d0..62e0e802b0e 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/DoubleQuoteEnd.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/DoubleQuoteEnd.java
@@ -36,7 +36,7 @@ public class DoubleQuoteEnd extends BaseSimpleNode implements BlockEnd {
}
@Override
- public String createCode(String expression) throws SimpleParserException {
+ public String createCode(CamelContext camelContext, String expression) throws SimpleParserException {
return null;
}
}
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/DoubleQuoteStart.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/DoubleQuoteStart.java
index bd4428db5b6..1a35af02c39 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/DoubleQuoteStart.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/DoubleQuoteStart.java
@@ -60,7 +60,7 @@ public class DoubleQuoteStart extends BaseSimpleNode implements BlockStart {
}
@Override
- public String createCode(String expression) throws SimpleParserException {
- return BaseSimpleNode.createCode(expression, block);
+ public String createCode(CamelContext camelContext, String expression) throws SimpleParserException {
+ return BaseSimpleNode.createCode(camelContext, expression, block);
}
}
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/LiteralExpression.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/LiteralExpression.java
index 60167e82152..5c59c101e1a 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/LiteralExpression.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/LiteralExpression.java
@@ -62,7 +62,7 @@ public class LiteralExpression extends BaseSimpleNode implements LiteralNode {
}
@Override
- public String createCode(String expression) throws SimpleParserException {
+ public String createCode(CamelContext camelContext, String expression) throws SimpleParserException {
return getText();
}
}
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/LogicalExpression.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/LogicalExpression.java
index 9fc06ecd133..afbcd2efec0 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/LogicalExpression.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/LogicalExpression.java
@@ -123,16 +123,16 @@ public class LogicalExpression extends BaseSimpleNode {
}
@Override
- public String createCode(String expression) throws SimpleParserException {
- return BaseSimpleParser.CODE_START + doCreateCode(expression) + BaseSimpleParser.CODE_END;
+ public String createCode(CamelContext camelContext, String expression) throws SimpleParserException {
+ return BaseSimpleParser.CODE_START + doCreateCode(camelContext, expression) + BaseSimpleParser.CODE_END;
}
- private String doCreateCode(String expression) throws SimpleParserException {
+ private String doCreateCode(CamelContext camelContext, String expression) throws SimpleParserException {
ObjectHelper.notNull(left, "left node", this);
ObjectHelper.notNull(right, "right node", this);
- final String leftExp = left.createCode(expression);
- final String rightExp = right.createCode(expression);
+ final String leftExp = left.createCode(camelContext, expression);
+ final String rightExp = right.createCode(camelContext, expression);
if (operator == LogicalOperatorType.AND) {
return leftExp + " && " + rightExp;
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/NullExpression.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/NullExpression.java
index 46fb25dec4b..00faa5cff1a 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/NullExpression.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/NullExpression.java
@@ -49,7 +49,7 @@ public class NullExpression extends BaseSimpleNode {
}
@Override
- public String createCode(String expression) throws SimpleParserException {
+ public String createCode(CamelContext camelContext, String expression) throws SimpleParserException {
return "null";
}
}
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/NumericExpression.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/NumericExpression.java
index ceda57ef05b..9f39a63e4f4 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/NumericExpression.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/NumericExpression.java
@@ -72,7 +72,7 @@ public class NumericExpression extends BaseSimpleNode {
}
@Override
- public String createCode(String expression) throws SimpleParserException {
+ public String createCode(CamelContext camelContext, String expression) throws SimpleParserException {
// Double, Long or Integer
if (number instanceof Double) {
return number + "d";
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionEnd.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionEnd.java
index 5dac2d21c4e..bde21cff2a9 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionEnd.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionEnd.java
@@ -36,7 +36,7 @@ public class SimpleFunctionEnd extends BaseSimpleNode implements BlockEnd {
}
@Override
- public String createCode(String expression) throws SimpleParserException {
+ public String createCode(CamelContext camelContext, String expression) throws SimpleParserException {
return null;
}
}
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionExpression.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionExpression.java
index 3c2a07311c9..55c6701ce6d 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionExpression.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionExpression.java
@@ -20,6 +20,7 @@ import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import org.apache.camel.CamelContext;
import org.apache.camel.Expression;
@@ -29,6 +30,8 @@ import org.apache.camel.language.simple.SimpleExpressionBuilder;
import org.apache.camel.language.simple.types.SimpleParserException;
import org.apache.camel.language.simple.types.SimpleToken;
import org.apache.camel.spi.Language;
+import org.apache.camel.spi.SimpleLanguageFunctionFactory;
+import org.apache.camel.support.ResolverHelper;
import org.apache.camel.support.builder.ExpressionBuilder;
import org.apache.camel.util.ObjectHelper;
import org.apache.camel.util.OgnlHelper;
@@ -325,6 +328,14 @@ public class SimpleFunctionExpression extends LiteralExpression {
return misc;
}
+ // attachments
+ if ("attachments".equals(function) || ifStartsWithReturnRemainder("attachment", function) != null) {
+ Expression exp = createSimpleAttachments(camelContext, function);
+ if (exp != null) {
+ return exp;
+ }
+ }
+
if (strict) {
throw new SimpleParserException("Unknown function: " + function, token.getIndex());
} else {
@@ -332,6 +343,20 @@ public class SimpleFunctionExpression extends LiteralExpression {
}
}
+ private Expression createSimpleAttachments(CamelContext camelContext, String function) {
+ Optional<SimpleLanguageFunctionFactory> factory = ResolverHelper.resolveService(
+ camelContext,
+ camelContext.getCamelContextExtension().getBootstrapFactoryFinder(),
+ SimpleLanguageFunctionFactory.FACTORY + "/camel-attachments",
+ SimpleLanguageFunctionFactory.class);
+
+ if (factory.isEmpty()) {
+ throw new IllegalArgumentException(
+ "Cannot find SimpleLanguageFunctionFactory on classpath. Add camel-attachments to classpath.");
+ }
+ return factory.get().createFunction(camelContext, function, token.getIndex());
+ }
+
private Expression createSimpleExpressionMessage(CamelContext camelContext, String function, boolean strict) {
// messageAs
String remainder = ifStartsWithReturnRemainder("messageAs(", function);
@@ -435,6 +460,9 @@ public class SimpleFunctionExpression extends LiteralExpression {
// headers function
if ("in.headers".equals(function) || "headers".equals(function)) {
return ExpressionBuilder.headersExpression();
+ } else if ("headers.size".equals(function) || "headers.size()".equals(function)
+ || "headers.length".equals(function) || "headers.length()".equals(function)) {
+ return ExpressionBuilder.headersSizeExpression();
}
// in header function
@@ -492,6 +520,9 @@ public class SimpleFunctionExpression extends LiteralExpression {
// variables function
if ("variables".equals(function)) {
return ExpressionBuilder.variablesExpression();
+ } else if ("variables.size".equals(function) || "variables.size()".equals(function)
+ || "variables.length".equals(function) || "variables.length()".equals(function)) {
+ return ExpressionBuilder.variablesSizeExpression();
}
// variable function
@@ -874,11 +905,11 @@ public class SimpleFunctionExpression extends LiteralExpression {
}
@Override
- public String createCode(String expression) throws SimpleParserException {
- return BaseSimpleParser.CODE_START + doCreateCode(expression) + BaseSimpleParser.CODE_END;
+ public String createCode(CamelContext camelContext, String expression) throws SimpleParserException {
+ return BaseSimpleParser.CODE_START + doCreateCode(camelContext, expression) + BaseSimpleParser.CODE_END;
}
- private String doCreateCode(String expression) throws SimpleParserException {
+ private String doCreateCode(CamelContext camelContext, String expression) throws SimpleParserException {
String function = getText();
// return the function directly if we can create function without analyzing the prefix
@@ -1090,6 +1121,14 @@ public class SimpleFunctionExpression extends LiteralExpression {
return misc;
}
+ // attachments
+ if ("attachments".equals(function) || ifStartsWithReturnRemainder("attachment", function) != null) {
+ String code = createCodeAttachments(camelContext, function);
+ if (code != null) {
+ return code;
+ }
+ }
+
throw new SimpleParserException("Unknown function: " + function, token.getIndex());
}
@@ -1373,6 +1412,9 @@ public class SimpleFunctionExpression extends LiteralExpression {
// headers function
if ("in.headers".equals(function) || "headers".equals(function)) {
return "message.getHeaders()";
+ } else if ("headers.size".equals(function) || "headers.size()".equals(function)
+ || "headers.length".equals(function) || "headers.length()".equals(function)) {
+ return "message.getHeaders().size()";
}
// in header function
@@ -1485,6 +1527,9 @@ public class SimpleFunctionExpression extends LiteralExpression {
// variables function
if ("variables".equals(function)) {
return "variables(exchange)";
+ } else if ("variables.size".equals(function) || "variables.size()".equals(function)
+ || "variables.length".equals(function) || "variables.length()".equals(function)) {
+ return "variablesSize(exchange)";
}
// variable
@@ -1687,6 +1732,20 @@ public class SimpleFunctionExpression extends LiteralExpression {
throw new SimpleParserException("Unknown file language syntax: " + remainder, token.getIndex());
}
+ private String createCodeAttachments(CamelContext camelContext, String function) {
+ Optional<SimpleLanguageFunctionFactory> factory = ResolverHelper.resolveService(
+ camelContext,
+ camelContext.getCamelContextExtension().getBootstrapFactoryFinder(),
+ SimpleLanguageFunctionFactory.FACTORY + "/camel-attachments",
+ SimpleLanguageFunctionFactory.class);
+
+ if (factory.isEmpty()) {
+ throw new IllegalArgumentException(
+ "Cannot find SimpleLanguageFunctionFactory on classpath. Add camel-attachments to classpath.");
+ }
+ return factory.get().createCode(camelContext, function, token.getIndex());
+ }
+
private String createCodeExpressionMisc(String function) {
String remainder;
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionStart.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionStart.java
index 68e8950a730..852a6d64db2 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionStart.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionStart.java
@@ -149,25 +149,25 @@ public class SimpleFunctionStart extends BaseSimpleNode implements BlockStart {
}
@Override
- public String createCode(String expression) throws SimpleParserException {
+ public String createCode(CamelContext camelContext, String expression) throws SimpleParserException {
String answer;
// a function can either be a simple literal function or contain nested functions
if (block.getChildren().size() == 1 && block.getChildren().get(0) instanceof LiteralNode) {
- answer = doCreateLiteralCode(expression);
+ answer = doCreateLiteralCode(camelContext, expression);
} else {
- answer = doCreateCompositeCode(expression);
+ answer = doCreateCompositeCode(camelContext, expression);
}
return answer;
}
- private String doCreateLiteralCode(String expression) {
+ private String doCreateLiteralCode(CamelContext camelContext, String expression) {
SimpleFunctionExpression function = new SimpleFunctionExpression(this.getToken(), cacheExpression);
LiteralNode literal = (LiteralNode) block.getChildren().get(0);
function.addText(literal.getText());
- return function.createCode(expression);
+ return function.createCode(camelContext, expression);
}
- private String doCreateCompositeCode(String expression) {
+ private String doCreateCompositeCode(CamelContext camelContext, String expression) {
StringBuilder sb = new StringBuilder(256);
boolean quoteEmbeddedFunctions = false;
@@ -181,7 +181,7 @@ public class SimpleFunctionStart extends BaseSimpleNode implements BlockStart {
} else if (child instanceof SingleQuoteStart || child instanceof DoubleQuoteStart) {
try {
// pass in null when we evaluate the nested expressions
- String text = child.createCode(null);
+ String text = child.createCode(camelContext, null);
if (text != null) {
if (quoteEmbeddedFunctions && !StringHelper.isQuoted(text)) {
sb.append("'").append(text).append("'");
@@ -195,7 +195,7 @@ public class SimpleFunctionStart extends BaseSimpleNode implements BlockStart {
}
} else if (child instanceof SimpleFunctionStart) {
// inlined function
- String inlined = child.createCode(expression);
+ String inlined = child.createCode(camelContext, expression);
sb.append(inlined);
}
}
@@ -206,7 +206,7 @@ public class SimpleFunctionStart extends BaseSimpleNode implements BlockStart {
SimpleFunctionExpression function = new SimpleFunctionExpression(token, cacheExpression);
function.addText(exp);
try {
- return function.createCode(exp);
+ return function.createCode(camelContext, exp);
} catch (SimpleParserException e) {
// must rethrow parser exception as illegal syntax with details about the location
throw new SimpleIllegalSyntaxException(expression, e.getIndex(), e.getMessage(), e);
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleNode.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleNode.java
index ed79da3df38..559af1791b6 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleNode.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleNode.java
@@ -46,10 +46,11 @@ public interface SimpleNode {
/**
* Creates Java code based on this model.
*
+ * @param camelContext the camel context
* @param expression the input string
* @return the created Java code
* @throws org.apache.camel.language.simple.types.SimpleParserException should be thrown if error parsing the model
*/
- String createCode(String expression) throws SimpleParserException;
+ String createCode(CamelContext camelContext, String expression) throws SimpleParserException;
}
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SingleQuoteEnd.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SingleQuoteEnd.java
index 3e5b97f6734..081edaa09ee 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SingleQuoteEnd.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SingleQuoteEnd.java
@@ -36,7 +36,7 @@ public class SingleQuoteEnd extends BaseSimpleNode implements BlockEnd {
}
@Override
- public String createCode(String expression) throws SimpleParserException {
+ public String createCode(CamelContext camelContext, String expression) throws SimpleParserException {
return null;
}
}
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SingleQuoteStart.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SingleQuoteStart.java
index ce23e4246d6..a7a907ec07f 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SingleQuoteStart.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SingleQuoteStart.java
@@ -64,7 +64,7 @@ public class SingleQuoteStart extends BaseSimpleNode implements BlockStart {
}
@Override
- public String createCode(String expression) throws SimpleParserException {
- return BaseSimpleNode.createCode(expression, block);
+ public String createCode(CamelContext camelContext, String expression) throws SimpleParserException {
+ return BaseSimpleNode.createCode(camelContext, expression, block);
}
}
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/UnaryExpression.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/UnaryExpression.java
index e39ab05d16c..0aeb332caa0 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/UnaryExpression.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/UnaryExpression.java
@@ -147,14 +147,14 @@ public class UnaryExpression extends BaseSimpleNode {
}
@Override
- public String createCode(String expression) throws SimpleParserException {
- return BaseSimpleParser.CODE_START + doCreateCode(expression) + BaseSimpleParser.CODE_END;
+ public String createCode(CamelContext camelContext, String expression) throws SimpleParserException {
+ return BaseSimpleParser.CODE_START + doCreateCode(camelContext, expression) + BaseSimpleParser.CODE_END;
}
- private String doCreateCode(String expression) throws SimpleParserException {
+ private String doCreateCode(CamelContext camelContext, String expression) throws SimpleParserException {
ObjectHelper.notNull(left, "left node", this);
- final String number = left.createCode(expression);
+ final String number = left.createCode(camelContext, expression);
if (operator == UnaryOperatorType.INC) {
return "increment(exchange, " + number + ")";
diff --git a/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleTest.java b/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleTest.java
index ae9bdc6147d..fbcc49b19a1 100644
--- a/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleTest.java
+++ b/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleTest.java
@@ -889,6 +889,7 @@ public class SimpleTest extends LanguageTestSupport {
assertExpression("${headers}", headers);
assertExpression("${in.headers}", headers);
+ assertExpression("${headers.size}", 2);
}
@Test
@@ -952,6 +953,7 @@ public class SimpleTest extends LanguageTestSupport {
assertEquals(3, variables.size());
assertExpression("${variables}", variables);
+ assertExpression("${variables.size}", 3);
}
@Test
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 8508272a029..8181e3b0e7c 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
@@ -367,6 +367,23 @@ public class ExpressionBuilder {
};
}
+ /**
+ * Returns an expression for the size of the message headers
+ */
+ public static Expression headersSizeExpression() {
+ return new ExpressionAdapter() {
+ @Override
+ public Object evaluate(Exchange exchange) {
+ return exchange.getIn().getHeaders().size();
+ }
+
+ @Override
+ public String toString() {
+ return "headers.size";
+ }
+ };
+ }
+
/**
* Returns an expression for the {@link Exchange} variables
*
@@ -386,6 +403,23 @@ public class ExpressionBuilder {
};
}
+ /**
+ * Returns an expression for the number of {@link Exchange} variables
+ */
+ public static Expression variablesSizeExpression() {
+ return new ExpressionAdapter() {
+ @Override
+ public Object evaluate(Exchange exchange) {
+ return exchange.getVariables().size();
+ }
+
+ @Override
+ public String toString() {
+ return "variables.size";
+ }
+ };
+ }
+
/**
* Returns an expression for the exchange pattern
*