This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch b64 in repository https://gitbox.apache.org/repos/asf/camel.git
commit dd158aa898a70a7c204f35e9a3b3f92887d0316b Author: Claus Ibsen <[email protected]> AuthorDate: Sat Jan 17 10:26:44 2026 +0100 CAMEL-22866: camel-core - Add base64 functions to simple --- .../org/apache/camel/catalog/languages/simple.json | 4 +- .../camel/attachment/SimpleAttachmentFunction.java | 36 ++---- components/camel-base64/pom.xml | 4 + .../camel/simple-function-factory/camel-base64 | 2 + .../dataformat/base64/Base64ExpressionBuilder.java | 111 ++++++++++++++++ .../dataformat/base64/SimpleBase64Function.java | 144 +++++++++++++++++++++ .../camel/dataformat/base64/SimpleBase64Test.java | 53 ++++++++ components/camel-csimple-joor/pom.xml | 5 + .../language/csimple/joor/CSimpleBase64Test.java | 52 ++++++++ .../org/apache/camel/language/simple/simple.json | 4 +- .../modules/languages/pages/simple-language.adoc | 14 ++ .../camel/language/simple/SimpleConstants.java | 6 + .../simple/ast/SimpleFunctionExpression.java | 48 ++++++- 13 files changed, 456 insertions(+), 27 deletions(-) diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/simple.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/simple.json index b08ffa5b4033..12c163c5398f 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/simple.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/simple.json @@ -101,6 +101,8 @@ "attachmentContentAs(type)": { "index": 75, "kind": "function", "displayName": "Attachment Content As", "group": "function", "label": "function", "required": false, "javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The content of the attachment, converted to the given type.", "ognl": false, "suffix": "}" }, "attachmentHeader(key,name)": { "index": 76, "kind": "function", "displayName": "Attachment Header", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The attachment header with the given name.", "ognl": false, "suffix": "}" }, "attachmentHeader(key,name,type)": { "index": 77, "kind": "function", "displayName": "Attachment Header", "group": "function", "label": "function", "required": false, "javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The attachment header with the given name, converted to the given type.", "ognl": false, "suffix": "}" }, - "attachment(key)": { "index": 78, "kind": "function", "displayName": "Attachment", "group": "function", "label": "function", "required": false, "javaType": "jakarta.activation.DataHandler", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The DataHandler for the given attachment.", "ognl": true, "suffix": "}" } + "attachment(key)": { "index": 78, "kind": "function", "displayName": "Attachment", "group": "function", "label": "function", "required": false, "javaType": "jakarta.activation.DataHandler", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The DataHandler for the given attachment.", "ognl": true, "suffix": "}" }, + "base64Encode(exp)": { "index": 79, "kind": "function", "displayName": "Base64 Encode", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Base64 encodes the message body (or expression)", "ognl": false, "suffix": "}" }, + "base64Decode(exp)": { "index": 80, "kind": "function", "displayName": "Base64 Decode", "group": "function", "label": "function", "required": false, "javaType": "byte[]", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Base64 decodes the message body (or expression)", "ognl": false, "suffix": "}" } } } 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 index c0accf73c8a6..2b3b965aa142 100644 --- 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 @@ -26,6 +26,8 @@ import org.apache.camel.util.OgnlHelper; import org.apache.camel.util.StringHelper; import org.apache.camel.util.StringQuoteHelper; +import static org.apache.camel.language.simple.ast.SimpleFunctionExpression.ifStartsWithReturnRemainder; + @JdkService(SimpleLanguageFunctionFactory.FACTORY + "/camel-attachments") public class SimpleAttachmentFunction implements SimpleLanguageFunctionFactory { @@ -40,7 +42,7 @@ public class SimpleAttachmentFunction implements SimpleLanguageFunctionFactory { String remainder = ifStartsWithReturnRemainder("attachmentContent(", function); if (remainder != null) { - String values = StringHelper.before(remainder, ")"); + String values = StringHelper.beforeLast(remainder, ")"); if (values == null || ObjectHelper.isEmpty(values)) { throw new SimpleParserException( "Valid syntax: ${attachmentContent(key)} was: " @@ -51,7 +53,7 @@ public class SimpleAttachmentFunction implements SimpleLanguageFunctionFactory { } remainder = ifStartsWithReturnRemainder("attachmentContentAs(", function); if (remainder != null) { - String values = StringHelper.before(remainder, ")"); + String values = StringHelper.beforeLast(remainder, ")"); if (values == null || ObjectHelper.isEmpty(values)) { throw new SimpleParserException( "Valid syntax: ${attachmentContentAs(key,type)} was: " @@ -69,7 +71,7 @@ public class SimpleAttachmentFunction implements SimpleLanguageFunctionFactory { } remainder = ifStartsWithReturnRemainder("attachmentContentAsText(", function); if (remainder != null) { - String values = StringHelper.before(remainder, ")"); + String values = StringHelper.beforeLast(remainder, ")"); if (values == null || ObjectHelper.isEmpty(values)) { throw new SimpleParserException( "Valid syntax: ${attachmentContentAsText(key)}} was: " @@ -81,7 +83,7 @@ public class SimpleAttachmentFunction implements SimpleLanguageFunctionFactory { remainder = ifStartsWithReturnRemainder("attachmentHeaderAs(", function); if (remainder != null) { - String values = StringHelper.before(remainder, ")"); + String values = StringHelper.beforeLast(remainder, ")"); if (values == null || ObjectHelper.isEmpty(values)) { throw new SimpleParserException( "Valid syntax: ${attachmentHeaderAs(key,name,type)} was: " + function, index); @@ -99,7 +101,7 @@ public class SimpleAttachmentFunction implements SimpleLanguageFunctionFactory { } remainder = ifStartsWithReturnRemainder("attachmentHeader(", function); if (remainder != null) { - String values = StringHelper.before(remainder, ")"); + String values = StringHelper.beforeLast(remainder, ")"); if (values == null || ObjectHelper.isEmpty(values)) { throw new SimpleParserException( "Valid syntax: ${attachmentHeader(key,name)} was: " @@ -119,7 +121,7 @@ public class SimpleAttachmentFunction implements SimpleLanguageFunctionFactory { remainder = ifStartsWithReturnRemainder("attachmentContentType(", function); if (remainder != null) { - String key = StringHelper.before(remainder, ")"); + String key = StringHelper.beforeLast(remainder, ")"); if (key == null || ObjectHelper.isEmpty(key)) { throw new SimpleParserException( "Valid syntax: ${attachmentContentType(key)} was: " @@ -168,7 +170,7 @@ public class SimpleAttachmentFunction implements SimpleLanguageFunctionFactory { String remainder = ifStartsWithReturnRemainder("attachmentContent(", function); if (remainder != null) { - String values = StringHelper.before(remainder, ")"); + String values = StringHelper.beforeLast(remainder, ")"); if (values == null || ObjectHelper.isEmpty(values)) { throw new SimpleParserException( "Valid syntax: ${attachmentContent(key)} was: " @@ -181,7 +183,7 @@ public class SimpleAttachmentFunction implements SimpleLanguageFunctionFactory { } remainder = ifStartsWithReturnRemainder("attachmentContentAs(", function); if (remainder != null) { - String values = StringHelper.before(remainder, ")"); + String values = StringHelper.beforeLast(remainder, ")"); if (values == null || ObjectHelper.isEmpty(values)) { throw new SimpleParserException( "Valid syntax: ${attachmentContentAs(key,type)} was: " @@ -204,7 +206,7 @@ public class SimpleAttachmentFunction implements SimpleLanguageFunctionFactory { } remainder = ifStartsWithReturnRemainder("attachmentContentAsText(", function); if (remainder != null) { - String values = StringHelper.before(remainder, ")"); + String values = StringHelper.beforeLast(remainder, ")"); if (values == null || ObjectHelper.isEmpty(values)) { throw new SimpleParserException( "Valid syntax: ${attachmentContentAsText(key)}} was: " @@ -217,7 +219,7 @@ public class SimpleAttachmentFunction implements SimpleLanguageFunctionFactory { } remainder = ifStartsWithReturnRemainder("attachmentContentType(", function); if (remainder != null) { - String key = StringHelper.before(remainder, ")"); + String key = StringHelper.beforeLast(remainder, ")"); if (key == null || ObjectHelper.isEmpty(key)) { throw new SimpleParserException( "Valid syntax: ${attachmentContentType(key)} was: " @@ -230,7 +232,7 @@ public class SimpleAttachmentFunction implements SimpleLanguageFunctionFactory { } remainder = ifStartsWithReturnRemainder("attachmentHeader(", function); if (remainder != null) { - String values = StringHelper.before(remainder, ")"); + String values = StringHelper.beforeLast(remainder, ")"); if (values == null || ObjectHelper.isEmpty(values)) { throw new SimpleParserException( "Valid syntax: ${attachmentHeader(key,name)} was: " @@ -252,7 +254,7 @@ public class SimpleAttachmentFunction implements SimpleLanguageFunctionFactory { } remainder = ifStartsWithReturnRemainder("attachmentHeaderAs(", function); if (remainder != null) { - String values = StringHelper.before(remainder, ")"); + String values = StringHelper.beforeLast(remainder, ")"); if (values == null || ObjectHelper.isEmpty(values)) { throw new SimpleParserException( "Valid syntax: ${attachmentHeaderAs(key,name,type)} was: " @@ -280,16 +282,6 @@ public class SimpleAttachmentFunction implements SimpleLanguageFunctionFactory { 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")) { diff --git a/components/camel-base64/pom.xml b/components/camel-base64/pom.xml index 5343177bc511..f3d4d9c5fe06 100644 --- a/components/camel-base64/pom.xml +++ b/components/camel-base64/pom.xml @@ -40,6 +40,10 @@ <groupId>org.apache.camel</groupId> <artifactId>camel-support</artifactId> </dependency> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-core-languages</artifactId> + </dependency> <dependency> <groupId>commons-codec</groupId> diff --git a/components/camel-base64/src/generated/resources/META-INF/services/org/apache/camel/simple-function-factory/camel-base64 b/components/camel-base64/src/generated/resources/META-INF/services/org/apache/camel/simple-function-factory/camel-base64 new file mode 100644 index 000000000000..191f6ac203e5 --- /dev/null +++ b/components/camel-base64/src/generated/resources/META-INF/services/org/apache/camel/simple-function-factory/camel-base64 @@ -0,0 +1,2 @@ +# Generated by camel build tools - do NOT edit this file! +class=org.apache.camel.dataformat.base64.SimpleBase64Function diff --git a/components/camel-base64/src/main/java/org/apache/camel/dataformat/base64/Base64ExpressionBuilder.java b/components/camel-base64/src/main/java/org/apache/camel/dataformat/base64/Base64ExpressionBuilder.java new file mode 100644 index 000000000000..44ce201fd787 --- /dev/null +++ b/components/camel-base64/src/main/java/org/apache/camel/dataformat/base64/Base64ExpressionBuilder.java @@ -0,0 +1,111 @@ +/* + * 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.dataformat.base64; + +import org.apache.camel.CamelContext; +import org.apache.camel.Exchange; +import org.apache.camel.Expression; +import org.apache.camel.support.ExpressionAdapter; +import org.apache.commons.codec.binary.Base64; + +public class Base64ExpressionBuilder { + + private static final int LINE_LENGTH = Base64.MIME_CHUNK_SIZE; + + /** + * Base 64 encodes the given expression + */ + public static Expression base64encode(final String expression) { + return new ExpressionAdapter() { + private Expression exp; + + @Override + public void init(CamelContext context) { + if (expression != null) { + exp = context.resolveLanguage("simple").createExpression(expression); + exp.init(context); + } + } + + @Override + public Object evaluate(Exchange exchange) { + byte[] value; + if (exp != null) { + value = exp.evaluate(exchange, byte[].class); + } else { + value = exchange.getMessage().getBody(byte[].class); + } + if (value != null) { + Base64 base64 = Base64.builder().setLineLength(LINE_LENGTH).get(); + return base64.encodeAsString(value); + } + return null; + } + + @Override + public String toString() { + if (expression != null) { + return "base64encode(" + expression + ")"; + } else { + return "base64encode()"; + } + } + }; + } + + /** + * Base 64 decodes the given expression + */ + public static Expression base64decode(final String expression) { + return new ExpressionAdapter() { + private Expression exp; + + @Override + public void init(CamelContext context) { + if (expression != null) { + exp = context.resolveLanguage("simple").createExpression(expression); + exp.init(context); + } + } + + @Override + public Object evaluate(Exchange exchange) { + byte[] value; + if (exp != null) { + value = exp.evaluate(exchange, byte[].class); + } else { + value = exchange.getMessage().getBody(byte[].class); + } + if (value != null) { + Base64 base64 = Base64.builder().setLineLength(LINE_LENGTH).get(); + return base64.decode(value); + } + return null; + } + + @Override + public String toString() { + if (expression != null) { + return "base64decode(" + expression + ")"; + } else { + return "base64decode()"; + } + } + }; + } + +} diff --git a/components/camel-base64/src/main/java/org/apache/camel/dataformat/base64/SimpleBase64Function.java b/components/camel-base64/src/main/java/org/apache/camel/dataformat/base64/SimpleBase64Function.java new file mode 100644 index 000000000000..27e2cb10ad96 --- /dev/null +++ b/components/camel-base64/src/main/java/org/apache/camel/dataformat/base64/SimpleBase64Function.java @@ -0,0 +1,144 @@ +/* + * 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.dataformat.base64; + +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.StringHelper; +import org.apache.camel.util.StringQuoteHelper; + +import static org.apache.camel.language.simple.ast.SimpleFunctionExpression.codeSplitSafe; +import static org.apache.camel.language.simple.ast.SimpleFunctionExpression.ifStartsWithReturnRemainder; + +@JdkService(SimpleLanguageFunctionFactory.FACTORY + "/camel-base64") +public class SimpleBase64Function implements SimpleLanguageFunctionFactory { + + private static final String ENCODE_CODE + = """ + byte[] data; + if (value != null) { + data = exchange.getContext().getTypeConverter().convertTo(byte[].class, exchange, value); + } else { + data = exchange.getMessage().getBody(byte[].class); + } + if (data != null) { + org.apache.commons.codec.binary.Base64 base64 + = org.apache.commons.codec.binary.Base64.builder().setLineLength(org.apache.commons.codec.binary.Base64.MIME_CHUNK_SIZE).get(); + return base64.encodeAsString(data); + } + return null + """; + + private static final String DECODE_CODE + = """ + byte[] data; + if (value != null) { + data = exchange.getContext().getTypeConverter().convertTo(byte[].class, exchange, value); + } else { + data = exchange.getMessage().getBody(byte[].class); + } + if (data != null) { + org.apache.commons.codec.binary.Base64 base64 + = org.apache.commons.codec.binary.Base64.builder().setLineLength(org.apache.commons.codec.binary.Base64.MIME_CHUNK_SIZE).get(); + return base64.decode(data); + } + return null + """; + + @Override + public Expression createFunction(CamelContext camelContext, String function, int index) { + String remainder = ifStartsWithReturnRemainder("base64Encode(", function); + if (remainder != null) { + String exp = null; + String value = StringHelper.beforeLast(remainder, ")"); + if (ObjectHelper.isNotEmpty(value)) { + exp = StringHelper.removeQuotes(value); + } + return Base64ExpressionBuilder.base64encode(exp); + } + + remainder = ifStartsWithReturnRemainder("base64Decode(", function); + if (remainder != null) { + String exp = null; + String value = StringHelper.beforeLast(remainder, ")"); + if (ObjectHelper.isNotEmpty(value)) { + exp = StringHelper.removeQuotes(value); + } + return Base64ExpressionBuilder.base64decode(exp); + } + + return null; + } + + @Override + public String createCode(CamelContext camelContext, String function, int index) { + String remainder = ifStartsWithReturnRemainder("base64Encode(", function); + if (remainder != null) { + String exp = null; + String values = StringHelper.beforeLast(remainder, ")"); + if (ObjectHelper.isNotEmpty(values)) { + String[] tokens = codeSplitSafe(values, ',', true, true); + if (tokens.length != 1) { + throw new SimpleParserException( + "Valid syntax: ${base64Encode(exp)} was: " + function, index); + } + // single quotes should be double quotes + String s = tokens[0]; + if (StringHelper.isSingleQuoted(s)) { + s = StringHelper.removeLeadingAndEndingQuotes(s); + s = StringQuoteHelper.doubleQuote(s); + } + exp = s; + } + if (ObjectHelper.isEmpty(exp)) { + exp = "null"; + } + return "Object value = " + exp + ";\n" + ENCODE_CODE; + } + + remainder = ifStartsWithReturnRemainder("base64Decode(", function); + if (remainder != null) { + String exp = null; + String values = StringHelper.beforeLast(remainder, ")"); + if (ObjectHelper.isNotEmpty(values)) { + String[] tokens = codeSplitSafe(values, ',', true, true); + if (tokens.length != 1) { + throw new SimpleParserException( + "Valid syntax: ${base64Decode(exp)} was: " + function, index); + } + // single quotes should be double quotes + String s = tokens[0]; + if (StringHelper.isSingleQuoted(s)) { + s = StringHelper.removeLeadingAndEndingQuotes(s); + s = StringQuoteHelper.doubleQuote(s); + } + exp = s; + } + if (ObjectHelper.isEmpty(exp)) { + exp = "null"; + } + return "Object value = " + exp + ";\n" + DECODE_CODE; + } + + return null; + } + +} diff --git a/components/camel-base64/src/test/java/org/apache/camel/dataformat/base64/SimpleBase64Test.java b/components/camel-base64/src/test/java/org/apache/camel/dataformat/base64/SimpleBase64Test.java new file mode 100644 index 000000000000..6c38bd21ae1f --- /dev/null +++ b/components/camel-base64/src/test/java/org/apache/camel/dataformat/base64/SimpleBase64Test.java @@ -0,0 +1,53 @@ +/* + * 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.dataformat.base64; + +import org.apache.camel.Expression; +import org.apache.camel.test.junit5.LanguageTestSupport; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class SimpleBase64Test extends LanguageTestSupport { + + @Override + protected String getLanguageName() { + return "simple"; + } + + @Test + public void testBase64Encode() throws Exception { + exchange.getMessage().setBody("Camel"); + + Expression expression = context.resolveLanguage("simple").createExpression("${base64Encode()}"); + String s = expression.evaluate(exchange, String.class); + assertEquals("Q2FtZWw=\r\n", s); + + expression = context.resolveLanguage("simple").createExpression("${base64Encode(${body})}"); + s = expression.evaluate(exchange, String.class); + assertEquals("Q2FtZWw=\r\n", s); + } + + @Test + public void testBase64Decode() throws Exception { + exchange.getMessage().setBody("Q2FtZWw="); + + assertExpression("${base64Decode()}", "Camel"); + assertExpression("${base64Decode(${body}}", "Camel"); + } + +} diff --git a/components/camel-csimple-joor/pom.xml b/components/camel-csimple-joor/pom.xml index cb5b54b57c22..74f3c00a910f 100644 --- a/components/camel-csimple-joor/pom.xml +++ b/components/camel-csimple-joor/pom.xml @@ -67,6 +67,11 @@ <artifactId>camel-attachments</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-base64</artifactId> + <scope>test</scope> + </dependency> <dependency> <groupId>org.hamcrest</groupId> <artifactId>hamcrest</artifactId> diff --git a/components/camel-csimple-joor/src/test/java/org/apache/camel/language/csimple/joor/CSimpleBase64Test.java b/components/camel-csimple-joor/src/test/java/org/apache/camel/language/csimple/joor/CSimpleBase64Test.java new file mode 100644 index 000000000000..3e4e2280a088 --- /dev/null +++ b/components/camel-csimple-joor/src/test/java/org/apache/camel/language/csimple/joor/CSimpleBase64Test.java @@ -0,0 +1,52 @@ +/* + * 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 org.apache.camel.Expression; +import org.apache.camel.test.junit5.LanguageTestSupport; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class CSimpleBase64Test extends LanguageTestSupport { + + @Override + protected String getLanguageName() { + return "csimple"; + } + + @Test + public void testBase64Encode() throws Exception { + exchange.getMessage().setBody("Camel"); + + Expression expression = context.resolveLanguage("csimple").createExpression("${base64Encode()}"); + String s = expression.evaluate(exchange, String.class); + assertEquals("Q2FtZWw=\r\n", s); + + expression = context.resolveLanguage("csimple").createExpression("${base64Encode(${body})}"); + s = expression.evaluate(exchange, String.class); + assertEquals("Q2FtZWw=\r\n", s); + } + + @Test + public void testBase64Decode() throws Exception { + exchange.getMessage().setBody("Q2FtZWw="); + + assertExpression("${base64Decode()}", "Camel"); + assertExpression("${base64Decode(${body}}", "Camel"); + } +} diff --git a/core/camel-core-languages/src/generated/resources/META-INF/org/apache/camel/language/simple/simple.json b/core/camel-core-languages/src/generated/resources/META-INF/org/apache/camel/language/simple/simple.json index b08ffa5b4033..12c163c5398f 100644 --- a/core/camel-core-languages/src/generated/resources/META-INF/org/apache/camel/language/simple/simple.json +++ b/core/camel-core-languages/src/generated/resources/META-INF/org/apache/camel/language/simple/simple.json @@ -101,6 +101,8 @@ "attachmentContentAs(type)": { "index": 75, "kind": "function", "displayName": "Attachment Content As", "group": "function", "label": "function", "required": false, "javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The content of the attachment, converted to the given type.", "ognl": false, "suffix": "}" }, "attachmentHeader(key,name)": { "index": 76, "kind": "function", "displayName": "Attachment Header", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The attachment header with the given name.", "ognl": false, "suffix": "}" }, "attachmentHeader(key,name,type)": { "index": 77, "kind": "function", "displayName": "Attachment Header", "group": "function", "label": "function", "required": false, "javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The attachment header with the given name, converted to the given type.", "ognl": false, "suffix": "}" }, - "attachment(key)": { "index": 78, "kind": "function", "displayName": "Attachment", "group": "function", "label": "function", "required": false, "javaType": "jakarta.activation.DataHandler", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The DataHandler for the given attachment.", "ognl": true, "suffix": "}" } + "attachment(key)": { "index": 78, "kind": "function", "displayName": "Attachment", "group": "function", "label": "function", "required": false, "javaType": "jakarta.activation.DataHandler", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The DataHandler for the given attachment.", "ognl": true, "suffix": "}" }, + "base64Encode(exp)": { "index": 79, "kind": "function", "displayName": "Base64 Encode", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Base64 encodes the message body (or expression)", "ognl": false, "suffix": "}" }, + "base64Decode(exp)": { "index": 80, "kind": "function", "displayName": "Base64 Decode", "group": "function", "label": "function", "required": false, "javaType": "byte[]", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Base64 decodes the message body (or expression)", "ognl": false, "suffix": "}" } } } 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 9238e4ef2cf4..e63ea699d1ea 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 @@ -376,6 +376,20 @@ This requires having `camel-attachments` JAR on the classpath. |attachment.*OGNL* | Object | refer to the foo attachment on the exchange and invoke its value using a Camel OGNL expression. |======================================================================= +== Base64 functions + +From *Camel 4.18* onwards then Camel has built-in base64 functions to make it easy to encode/decode. + +This requires having `camel-base64` JAR on the classpath. + +[width="100%",cols="10%,10%,80%",options="header",] +|======================================================================= +|Function |Type |Description + +|base64Encode(exp) |String |Base64 encodes the message body (or expression) +|base64Decode(exp) |byte[] |Base64 decodes the message body (or expression) +|======================================================================= + == OGNL expression support diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleConstants.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleConstants.java index 1c0ce7a142f4..6b622d7c12b8 100644 --- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleConstants.java +++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleConstants.java @@ -281,5 +281,11 @@ public final class SimpleConstants { @Metadata(description = "The DataHandler for the given attachment.", javaType = "jakarta.activation.DataHandler", label = "function,ognl") public static final String ATTACHMENT = "attachment(key)"; + @Metadata(description = "Base64 encodes the message body (or expression)", javaType = "String", + label = "function") + public static final String BASE64_ENCODE = "base64Encode(exp)"; + @Metadata(description = "Base64 decodes the message body (or expression)", javaType = "byte[]", + label = "function") + public static final String BASE64_DECODE = "base64Decode(exp)"; } 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 7c6ac3bf931c..88aeb7c48eed 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 @@ -344,6 +344,13 @@ public class SimpleFunctionExpression extends LiteralExpression { return exp; } } + // base64 + if ("base64Encode".equals(function) || "base64Decode".equals(function) || ifStartsWithReturnRemainder("base64Encode", function) != null || ifStartsWithReturnRemainder("base64Decode", function) != null) { + Expression exp = createSimpleBase64(camelContext, function); + if (exp != null) { + return exp; + } + } if (strict) { throw new SimpleParserException("Unknown function: " + function, token.getIndex()); @@ -366,6 +373,20 @@ public class SimpleFunctionExpression extends LiteralExpression { return factory.get().createFunction(camelContext, function, token.getIndex()); } + private Expression createSimpleBase64(CamelContext camelContext, String function) { + Optional<SimpleLanguageFunctionFactory> factory = ResolverHelper.resolveService( + camelContext, + camelContext.getCamelContextExtension().getBootstrapFactoryFinder(), + SimpleLanguageFunctionFactory.FACTORY + "/camel-base64", + SimpleLanguageFunctionFactory.class); + + if (factory.isEmpty()) { + throw new IllegalArgumentException( + "Cannot find SimpleLanguageFunctionFactory on classpath. Add camel-base64 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); @@ -1194,7 +1215,7 @@ public class SimpleFunctionExpression extends LiteralExpression { return null; } - private String ifStartsWithReturnRemainder(String prefix, String text) { + public static String ifStartsWithReturnRemainder(String prefix, String text) { if (text.startsWith(prefix)) { String remainder = text.substring(prefix.length()); if (!remainder.isEmpty()) { @@ -1428,6 +1449,13 @@ public class SimpleFunctionExpression extends LiteralExpression { return code; } } + // base64 + if ("base64Encode".equals(function) || "base64Decode".equals(function) || ifStartsWithReturnRemainder("base64Encode", function) != null || ifStartsWithReturnRemainder("base64Decode", function) != null) { + String code = createCodeBase64(camelContext, function); + if (code != null) { + return code; + } + } throw new SimpleParserException("Unknown function: " + function, token.getIndex()); } @@ -2048,6 +2076,20 @@ public class SimpleFunctionExpression extends LiteralExpression { return factory.get().createCode(camelContext, function, token.getIndex()); } + private String createCodeBase64(CamelContext camelContext, String function) { + Optional<SimpleLanguageFunctionFactory> factory = ResolverHelper.resolveService( + camelContext, + camelContext.getCamelContextExtension().getBootstrapFactoryFinder(), + SimpleLanguageFunctionFactory.FACTORY + "/camel-base64", + SimpleLanguageFunctionFactory.class); + + if (factory.isEmpty()) { + throw new IllegalArgumentException( + "Cannot find SimpleLanguageFunctionFactory on classpath. Add camel-base64 to classpath."); + } + return factory.get().createCode(camelContext, function, token.getIndex()); + } + private String createCodeExpressionMisc(String function) { String remainder; @@ -2801,7 +2843,7 @@ public class SimpleFunctionExpression extends LiteralExpression { return answer; } - private static String ognlCodeMethods(String remainder, String type) { + public static String ognlCodeMethods(String remainder, String type) { StringBuilder sb = new StringBuilder(256); if (remainder != null) { @@ -2880,7 +2922,7 @@ public class SimpleFunctionExpression extends LiteralExpression { } } - private static String[] codeSplitSafe(String input, char separator, boolean trim, boolean keepQuotes) { + public static String[] codeSplitSafe(String input, char separator, boolean trim, boolean keepQuotes) { if (input == null) { return null; }
