This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new dbdce79d2cd Camel DataSonnet support (#9209)
dbdce79d2cd is described below
commit dbdce79d2cdd3999e8a3af2ecd3985f71c00fba3
Author: javaduke <[email protected]>
AuthorDate: Fri Jan 27 08:06:56 2023 -0700
Camel DataSonnet support (#9209)
---
camel-dependencies/pom.xml | 4 +-
.../org/apache/camel/catalog/languages.properties | 1 +
.../apache/camel/catalog/languages/datasonnet.json | 26 ++
components/camel-datasonnet/pom.xml | 110 ++++++++
.../services/org/apache/camel/language.properties | 7 +
.../services/org/apache/camel/language/datasonnet | 2 +
.../camel/language/datasonnet/datasonnet.json | 26 ++
.../src/main/docs/datasonnet-language.adoc | 208 +++++++++++++++
.../org/apache/camel/language/datasonnet/CML.java | 115 +++++++++
.../camel/language/datasonnet/Datasonnet.java | 37 +++
.../language/datasonnet/DatasonnetConstants.java | 25 ++
.../language/datasonnet/DatasonnetExpression.java | 281 +++++++++++++++++++++
.../language/datasonnet/DatasonnetLanguage.java | 172 +++++++++++++
.../src/test/java/library/TestLib.java | 72 ++++++
.../camel/language/DatasonnetLanguageTest.java | 36 +++
.../datasonnet/CamelDatasonnetJavaDslTest.java | 128 ++++++++++
.../language/datasonnet/CamelDatasonnetTest.java | 195 ++++++++++++++
.../language/datasonnet/ExpressionsInJavaTest.java | 100 ++++++++
.../apache/camel/language/datasonnet/Gizmo.java | 114 +++++++++
.../camel/language/datasonnet/Manufacturer.java | 68 +++++
.../camel/language/datasonnet/PropertiesTest.java | 52 ++++
.../camel-datasonnet/src/test/resources/dslibs.jar | Bin 0 -> 786 bytes
.../src/test/resources/javaTest.json | 15 ++
.../test/resources/libraries/testlib4.libsonnet | 21 ++
.../src/test/resources/log4j2.properties | 32 +++
.../src/test/resources/namedImports.ds | 28 ++
.../src/test/resources/namedImportsFS.ds | 24 ++
.../src/test/resources/namedImports_result.json | 1 +
.../camel/language.datasonnet/camel-context.xml | 205 +++++++++++++++
.../src/test/resources/payload.csv | 2 +
.../src/test/resources/payload.xml | 22 ++
.../src/test/resources/readCSVTest.ds | 20 ++
.../src/test/resources/readJavaTest.ds | 30 +++
.../src/test/resources/readXMLExtTest.ds | 23 ++
.../src/test/resources/readXMLExtTest.json | 13 +
.../src/test/resources/simpleMapping.ds | 25 ++
.../src/test/resources/simpleMapping_payload.json | 4 +
.../src/test/resources/simpleMapping_result.json | 1 +
.../src/test/resources/testlib3.libsonnet | 21 ++
.../src/test/resources/writeJavaTest.ds | 30 +++
components/pom.xml | 1 +
parent/pom.xml | 4 +-
42 files changed, 2297 insertions(+), 4 deletions(-)
diff --git a/camel-dependencies/pom.xml b/camel-dependencies/pom.xml
index d7a41f2a203..ee8556ff5eb 100644
--- a/camel-dependencies/pom.xml
+++ b/camel-dependencies/pom.xml
@@ -137,8 +137,8 @@
<cxf-codegen-plugin-version>4.0.0</cxf-codegen-plugin-version>
<!-- cxf-xjc is not released as often -->
<cxf-xjc-plugin-version>4.0.0</cxf-xjc-plugin-version>
- <cxf-xjc-utils-version>4.0.0</cxf-xjc-utils-version>
- <datasonnet-mapper-version>2.2.0</datasonnet-mapper-version>
+ <cxf-xjc-utils-version>4.0.0</cxf-xjc-utils-version>
+ <datasonnet-mapper-version>2.5-jakarta4</datasonnet-mapper-version>
<deltaspike-version>1.9.5</deltaspike-version>
<depends-maven-plugin-version>1.4.0</depends-maven-plugin-version>
<derby-version>10.14.2.0</derby-version>
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages.properties
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages.properties
index 7fa18846a17..8aa9f4d1a08 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages.properties
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages.properties
@@ -1,6 +1,7 @@
bean
constant
csimple
+datasonnet
exchangeProperty
file
groovy
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/datasonnet.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/datasonnet.json
new file mode 100644
index 00000000000..4b7f41e6e4c
--- /dev/null
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/datasonnet.json
@@ -0,0 +1,26 @@
+{
+ "language": {
+ "kind": "language",
+ "name": "datasonnet",
+ "title": "DataSonnet",
+ "description": "To use DataSonnet scripts for message transformations.",
+ "deprecated": false,
+ "firstVersion": "3.7.0",
+ "label": "language,transformation",
+ "javaType": "org.apache.camel.language.datasonnet.DatasonnetLanguage",
+ "supportLevel": "Stable",
+ "groupId": "org.apache.camel",
+ "artifactId": "camel-datasonnet",
+ "version": "4.0.0-SNAPSHOT",
+ "modelName": "datasonnet",
+ "modelJavaType": "org.apache.camel.model.language.DatasonnetExpression"
+ },
+ "properties": {
+ "expression": { "kind": "value", "displayName": "Expression", "required":
true, "type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": false, "description": "The expression value in
your chosen language syntax" },
+ "bodyMediaType": { "kind": "attribute", "displayName": "Body Media Type",
"required": false, "type": "string", "javaType": "java.lang.String",
"deprecated": false, "autowired": false, "secret": false, "description": "The
String representation of the message's body MediaType" },
+ "outputMediaType": { "kind": "attribute", "displayName": "Output Media
Type", "required": false, "type": "string", "javaType": "java.lang.String",
"deprecated": false, "autowired": false, "secret": false, "description": "The
String representation of the MediaType to output" },
+ "resultType": { "kind": "attribute", "displayName": "Result Type",
"required": false, "type": "string", "javaType": "java.lang.String",
"deprecated": false, "autowired": false, "secret": false, "description": "Sets
the class of the result type (type from output)" },
+ "trim": { "kind": "attribute", "displayName": "Trim", "label": "advanced",
"required": false, "type": "boolean", "javaType": "java.lang.Boolean",
"deprecated": false, "autowired": false, "secret": false, "defaultValue": true,
"description": "Whether to trim the value to remove leading and trailing
whitespaces and line breaks" },
+ "id": { "kind": "attribute", "displayName": "Id", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": false, "description": "Sets the id of this node" }
+ }
+}
diff --git a/components/camel-datasonnet/pom.xml
b/components/camel-datasonnet/pom.xml
new file mode 100644
index 00000000000..33e7ab5ca24
--- /dev/null
+++ b/components/camel-datasonnet/pom.xml
@@ -0,0 +1,110 @@
+<?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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>components</artifactId>
+ <version>4.0.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>camel-datasonnet</artifactId>
+ <packaging>jar</packaging>
+
+ <name>Camel :: DataSonnet</name>
+ <description>Camel DataSonnet support</description>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <additionalClasspathElements>
+
<additionalClasspathElement>${project.basedir}/src/test/resources/dslibs.jar</additionalClasspathElement>
+ </additionalClasspathElements>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.scala-lang</groupId>
+ <artifactId>scala-library</artifactId>
+ <version>${scala-datasonnet-version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-support</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.datasonnet</groupId>
+ <artifactId>datasonnet-mapper</artifactId>
+ <version>${datasonnet-mapper-version}</version>
+ </dependency>
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.github.classgraph</groupId>
+ <artifactId>classgraph</artifactId>
+ <version>${classgraph-version}</version>
+ </dependency>
+
+ <!-- testing -->
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-test-spring-junit5</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-slf4j-impl</artifactId>
+ <version>${log4j2-version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.skyscreamer</groupId>
+ <artifactId>jsonassert</artifactId>
+ <version>${jsonassert-version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-core</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git
a/components/camel-datasonnet/src/generated/resources/META-INF/services/org/apache/camel/language.properties
b/components/camel-datasonnet/src/generated/resources/META-INF/services/org/apache/camel/language.properties
new file mode 100644
index 00000000000..d4075203c7c
--- /dev/null
+++
b/components/camel-datasonnet/src/generated/resources/META-INF/services/org/apache/camel/language.properties
@@ -0,0 +1,7 @@
+# Generated by camel build tools - do NOT edit this file!
+languages=datasonnet
+groupId=org.apache.camel
+artifactId=camel-datasonnet
+version=4.0.0-SNAPSHOT
+projectName=Camel :: DataSonnet
+projectDescription=Camel DataSonnet support
diff --git
a/components/camel-datasonnet/src/generated/resources/META-INF/services/org/apache/camel/language/datasonnet
b/components/camel-datasonnet/src/generated/resources/META-INF/services/org/apache/camel/language/datasonnet
new file mode 100644
index 00000000000..7a46c5b1e2a
--- /dev/null
+++
b/components/camel-datasonnet/src/generated/resources/META-INF/services/org/apache/camel/language/datasonnet
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.language.datasonnet.DatasonnetLanguage
diff --git
a/components/camel-datasonnet/src/generated/resources/org/apache/camel/language/datasonnet/datasonnet.json
b/components/camel-datasonnet/src/generated/resources/org/apache/camel/language/datasonnet/datasonnet.json
new file mode 100644
index 00000000000..4b7f41e6e4c
--- /dev/null
+++
b/components/camel-datasonnet/src/generated/resources/org/apache/camel/language/datasonnet/datasonnet.json
@@ -0,0 +1,26 @@
+{
+ "language": {
+ "kind": "language",
+ "name": "datasonnet",
+ "title": "DataSonnet",
+ "description": "To use DataSonnet scripts for message transformations.",
+ "deprecated": false,
+ "firstVersion": "3.7.0",
+ "label": "language,transformation",
+ "javaType": "org.apache.camel.language.datasonnet.DatasonnetLanguage",
+ "supportLevel": "Stable",
+ "groupId": "org.apache.camel",
+ "artifactId": "camel-datasonnet",
+ "version": "4.0.0-SNAPSHOT",
+ "modelName": "datasonnet",
+ "modelJavaType": "org.apache.camel.model.language.DatasonnetExpression"
+ },
+ "properties": {
+ "expression": { "kind": "value", "displayName": "Expression", "required":
true, "type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": false, "description": "The expression value in
your chosen language syntax" },
+ "bodyMediaType": { "kind": "attribute", "displayName": "Body Media Type",
"required": false, "type": "string", "javaType": "java.lang.String",
"deprecated": false, "autowired": false, "secret": false, "description": "The
String representation of the message's body MediaType" },
+ "outputMediaType": { "kind": "attribute", "displayName": "Output Media
Type", "required": false, "type": "string", "javaType": "java.lang.String",
"deprecated": false, "autowired": false, "secret": false, "description": "The
String representation of the MediaType to output" },
+ "resultType": { "kind": "attribute", "displayName": "Result Type",
"required": false, "type": "string", "javaType": "java.lang.String",
"deprecated": false, "autowired": false, "secret": false, "description": "Sets
the class of the result type (type from output)" },
+ "trim": { "kind": "attribute", "displayName": "Trim", "label": "advanced",
"required": false, "type": "boolean", "javaType": "java.lang.Boolean",
"deprecated": false, "autowired": false, "secret": false, "defaultValue": true,
"description": "Whether to trim the value to remove leading and trailing
whitespaces and line breaks" },
+ "id": { "kind": "attribute", "displayName": "Id", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": false, "description": "Sets the id of this node" }
+ }
+}
diff --git a/components/camel-datasonnet/src/main/docs/datasonnet-language.adoc
b/components/camel-datasonnet/src/main/docs/datasonnet-language.adoc
new file mode 100644
index 00000000000..ae2d95fff4e
--- /dev/null
+++ b/components/camel-datasonnet/src/main/docs/datasonnet-language.adoc
@@ -0,0 +1,208 @@
+= DataSonnet Language
+:doctitle: DataSonnet
+:shortname: datasonnet
+:artifactid: camel-datasonnet
+:description: To use DataSonnet scripts for message transformations.
+:since: 3.7
+:supportlevel: Stable
+//Manually maintained attributes
+:camel-spring-boot-name: datasonnet
+
+*Since Camel {since}*
+
+Camel supports https://datasonnet.com/[DataSonnet] transformations to allow an
+xref:manual::expression.adoc[Expression] or
xref:manual::predicate.adoc[Predicate] to be
+used in the xref:manual::dsl.adoc[DSL].
+
+For example, you could use DataSonnet to create a
+Predicate in a xref:eips:filter-eip.adoc[Message
+Filter] or as an Expression for a
+xref:eips:recipientList-eip.adoc[Recipient List].
+
+To use a DataSonnet expression use the following Java code:
+
+[source,java]
+---------------------------------------
+datasonnet("someDSExpression")
+---------------------------------------
+
+== DataSonnet Options
+
+// language options: START
+include::partial$language-options.adoc[]
+// language options: END
+
+== Example
+
+Here is a simple example using a DataSonnet expression as a predicate in a
Message Filter:
+
+[source,java]
+------------------------------------------------------------------------------------------------
+// lets route if a line item is over $100
+from("queue:foo")
+ .filter(datasonnet("ds.arrays.firstWith(body.lineItems, function(item)
item > 100) != null"))
+ .to("queue:bar")
+------------------------------------------------------------------------------------------------
+
+And the XML DSL:
+
+[source,xml]
+-----------------------------------------------------------------------------
+<route>
+ <from uri="queue:foo"/>
+ <filter>
+ <datasonnet>ds.arrays.firstWith(body.lineItems, function(item) item >
100) != null</datasonnet>
+ <to uri="queue:bar"/>
+ </filter>
+</route>
+-----------------------------------------------------------------------------
+
+Here is an example of a simple DataSonnet expression as a transformation EIP.
This example will transform an XML body with
+`lineItems` into JSON while filtering out lines that are under 100.
+
+[source,java]
+------------------------------------------------------------------------------------------------
+from("queue:foo")
+ .transform(datasonnet("ds.filter(body.lineItems, function(item) item >
100)", String.class, "application/xml", "application/json"))
+ .to("queue:bar")
+------------------------------------------------------------------------------------------------
+
+And the XML DSL:
+
+[source,xml]
+-----------------------------------------------------------------------------
+<route>
+ <from uri="queue:foo"/>
+ <filter>
+ <datasonnet bodyMediaType="application/xml"
outputMediaType="application/json" resultTypeName="java.lang.String" >
+ ds.filter(body.lineItems, function(item) item > 100)
+ </datasonnet>
+ <to uri="queue:bar"/>
+ </filter>
+</route>
+-----------------------------------------------------------------------------
+
+== Setting result type
+
+The xref:datasonnet-language.adoc[DataSonnet] expression will return a
`com.datasonnet.document.Document` by default. The
+document preserves the content type metadata along with the contents of the
result of the transformation. In predicates,
+however, the Document will be automatically unwrapped and the boolean content
will be returned. Similarly any times you
+want the content in a specific result type like a String. To do this you have
to instruct the
+xref:datasonnet-language.adoc[DataSonnet] which result type to return.
+
+In Java DSL:
+
+[source,java]
+----
+datasonnet("body.foo", String.class)
+----
+
+In XML DSL you use the *resultType* attribute to provide a fully
+qualified classname:
+
+[source,xml]
+----
+<datasonnet resultType="java.lang.String">body.foo</datasonnet>
+----
+
+If the expression results in an array, or an object, you can instruct the
expression to return you `List.class`
+or `Map.class`, respectively. However, you must also set the output media type
to `application/x-java-object`.
+
+NOTE: The default `Document` object is useful in situations where there are
intermediate transformation steps, and so
+retaining the content metadata through a route execution is valuable.
+
+== Specifying Media Types
+
+Traditionally the input and output media types are specified through the
+https://datasonnet.s3-us-west-2.amazonaws.com/docs-ci/primary/master/datasonnet/1.0-SNAPSHOT/headers.html[DataSonnet
Header]
+The xref:datasonnet-language.adoc[DataSonnet] expression provides convenience
options for specifying the body and output
+media types without the need for a Header, this is useful if the
transformation is a one-liner, for example.
+
+The DataSonnet expression will look for a body media type in the following
order:
+
+1. If the body is a `Document` it will use the metadata in the object
+2. If the bodyMediaType parameter was provided in the DSL, it will use its
value
+3. A "CamelDatasonnetBodyMediaType" exchange property
+4. A "Content-Type" message header
+5. The DataSonnet Header payload media type directive
+6. `application/x-java-object`
+
+And for output media type:
+
+1. If the outputMediaType parameter was provided in the DSL, it will use its
value
+2. A "CamelDatasonnetOutputMediaType" exchange property
+3. A "CamelDatasonnetOutputMediaType" message header
+4. The DataSonnet Header output media type directive
+5. `application/x-java-object`
+
+== Functions
+
+Camel adds the following DataSonnet functions that can be used to access the
+exchange:
+
+[width="100%",cols="10%,10%,10%,70%",options="header",]
+|===
+|Function |Argument |Type |Description
+
+|cml.properties |key for property |String |To lookup a property using the
+xref:ROOT:properties-component.adoc[Properties] component (property
placeholders).
+
+|cml.header |the header name |String |Will return the message header.
+
+|cml.exchangeProperty |key for property |String |Will return the exchange
property.
+|===
+
+Here's an example showing some of these functions in use:
+
+[source,java]
+------------------------------------------------------------------------------------------------
+from("direct:in")
+ .setBody(datasonnet("'hello, ' + cml.properties('toGreet')", String.class))
+ .to("mock:camel");
+------------------------------------------------------------------------------------------------
+
+And the XML DSL:
+
+[source,xml]
+-----------------------------------------------------------------------------
+<route>
+ <from uri="direct:in"/>
+ <setBody>
+ <datasonnet resultTypeName="java.lang.String">'hello, ' +
cml.properties('toGreet')</datasonnet>
+ </setBody>
+ <to uri="mock:camel"/>
+</route>
+-----------------------------------------------------------------------------
+
+== Loading script from external resource
+
+You can externalize the script and have Camel load it from a resource
+such as `"classpath:"`, `"file:"`, or `"http:"`. +
+This is done using the following syntax: `"resource:scheme:location"`,
+eg to refer to a file on the classpath you can do:
+
+[source,java]
+-------------------------------------------------------------------
+.setHeader("myHeader").datasonnet("resource:classpath:mydatasonnet.ds")
+-------------------------------------------------------------------
+
+== Dependencies
+
+To use scripting languages in your camel routes you need to add a
+dependency on *camel-datasonnet*.
+
+If you use Maven you could just add the following to your `pom.xml`,
+substituting the version number for the latest and greatest release (see
+the download page for the latest versions).
+
+[source,xml]
+---------------------------------------
+<dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-datasonnet</artifactId>
+ <version>x.x.x</version>
+</dependency>
+---------------------------------------
+
+
+include::spring-boot:partial$starter.adoc[]
diff --git
a/components/camel-datasonnet/src/main/java/org/apache/camel/language/datasonnet/CML.java
b/components/camel-datasonnet/src/main/java/org/apache/camel/language/datasonnet/CML.java
new file mode 100644
index 00000000000..756bacfbc3b
--- /dev/null
+++
b/components/camel-datasonnet/src/main/java/org/apache/camel/language/datasonnet/CML.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.language.datasonnet;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import com.datasonnet.document.DefaultDocument;
+import com.datasonnet.document.Document;
+import com.datasonnet.document.MediaTypes;
+import com.datasonnet.header.Header;
+import com.datasonnet.jsonnet.Materializer;
+import com.datasonnet.jsonnet.Val;
+import com.datasonnet.spi.DataFormatService;
+import com.datasonnet.spi.Library;
+import com.datasonnet.spi.PluginException;
+import org.apache.camel.Exchange;
+
+public final class CML extends Library {
+ private static final CML INSTANCE = new CML();
+ private final ThreadLocal<Exchange> exchange = new ThreadLocal<>();
+
+ private CML() {
+ }
+
+ public static CML getInstance() {
+ return INSTANCE;
+ }
+
+ public ThreadLocal<Exchange> getExchange() {
+ return exchange;
+ }
+
+ @Override
+ public String namespace() {
+ return "cml";
+ }
+
+ @Override
+ public Set<String> libsonnets() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public Map<String, Val.Func> functions(DataFormatService dataFormats,
Header header) {
+ Map<String, Val.Func> answer = new HashMap<>();
+ answer.put("properties", makeSimpleFunc(
+ Collections.singletonList("key"), //parameters list
+ params -> properties(params.get(0))));
+ answer.put("header", makeSimpleFunc(
+ Collections.singletonList("key"), //parameters list
+ params -> header(params.get(0), dataFormats)));
+ answer.put("exchangeProperty", makeSimpleFunc(
+ Collections.singletonList("key"), //parameters list
+ params -> exchangeProperty(params.get(0), dataFormats)));
+
+ return answer;
+ }
+
+ public Map<String, Val.Obj> modules(DataFormatService dataFormats, Header
header) {
+ return Collections.emptyMap();
+ }
+
+ private Val properties(Val key) {
+ if (key instanceof Val.Str) {
+ return new
Val.Str(exchange.get().getContext().resolvePropertyPlaceholders("{{" +
((Val.Str) key).value() + "}}"));
+ }
+ throw new IllegalArgumentException("Expected String got: " +
key.prettyName());
+ }
+
+ private Val header(Val key, DataFormatService dataformats) {
+ if (key instanceof Val.Str) {
+ return valFrom(exchange.get().getMessage().getHeader(((Val.Str)
key).value()), dataformats);
+ }
+ throw new IllegalArgumentException("Expected String got: " +
key.prettyName());
+ }
+
+ private Val exchangeProperty(Val key, DataFormatService dataformats) {
+ if (key instanceof Val.Str) {
+ return valFrom(exchange.get().getProperty(((Val.Str)
key).value()), dataformats);
+ }
+ throw new IllegalArgumentException("Expected String got: " +
key.prettyName());
+ }
+
+ private Val valFrom(Object obj, DataFormatService dataformats) {
+ Document doc;
+ if (obj instanceof Document) {
+ doc = (Document) obj;
+ } else {
+ doc = new DefaultDocument(obj, MediaTypes.APPLICATION_JAVA);
+ }
+
+ try {
+ return Materializer.reverse(dataformats.mandatoryRead(doc));
+ } catch (PluginException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+}
diff --git
a/components/camel-datasonnet/src/main/java/org/apache/camel/language/datasonnet/Datasonnet.java
b/components/camel-datasonnet/src/main/java/org/apache/camel/language/datasonnet/Datasonnet.java
new file mode 100644
index 00000000000..6e5de5fa328
--- /dev/null
+++
b/components/camel-datasonnet/src/main/java/org/apache/camel/language/datasonnet/Datasonnet.java
@@ -0,0 +1,37 @@
+/*
+ * 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.datasonnet;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.apache.camel.support.language.LanguageAnnotation;
+
+/**
+ * Used to inject a DataSonnet expression into a field, property, method or
parameter when using
+ * <a href="http://camel.apache.org/bean-integration.html">Bean
Integration</a>.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER })
+@LanguageAnnotation(language = "datasonnet")
+public @interface Datasonnet {
+ String value();
+}
diff --git
a/components/camel-datasonnet/src/main/java/org/apache/camel/language/datasonnet/DatasonnetConstants.java
b/components/camel-datasonnet/src/main/java/org/apache/camel/language/datasonnet/DatasonnetConstants.java
new file mode 100644
index 00000000000..5ee7a7c98a2
--- /dev/null
+++
b/components/camel-datasonnet/src/main/java/org/apache/camel/language/datasonnet/DatasonnetConstants.java
@@ -0,0 +1,25 @@
+/*
+ * 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.datasonnet;
+
+public final class DatasonnetConstants {
+ public static final String BODY_MEDIATYPE = "CamelDatasonnetBodyMediaType";
+ public static final String OUTPUT_MEDIATYPE =
"CamelDatasonnetOutputMediaType";
+
+ private DatasonnetConstants() {
+ }
+}
diff --git
a/components/camel-datasonnet/src/main/java/org/apache/camel/language/datasonnet/DatasonnetExpression.java
b/components/camel-datasonnet/src/main/java/org/apache/camel/language/datasonnet/DatasonnetExpression.java
new file mode 100644
index 00000000000..b1c72374551
--- /dev/null
+++
b/components/camel-datasonnet/src/main/java/org/apache/camel/language/datasonnet/DatasonnetExpression.java
@@ -0,0 +1,281 @@
+/*
+ * 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.datasonnet;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import com.datasonnet.Mapper;
+import com.datasonnet.MapperBuilder;
+import com.datasonnet.document.DefaultDocument;
+import com.datasonnet.document.Document;
+import com.datasonnet.document.MediaType;
+import com.datasonnet.document.MediaTypes;
+import com.datasonnet.header.Header;
+import com.datasonnet.spi.Library;
+import org.apache.camel.CamelContext;
+import org.apache.camel.Exchange;
+import org.apache.camel.RuntimeExpressionException;
+import org.apache.camel.spi.ExpressionResultTypeAware;
+import org.apache.camel.support.ExchangeHelper;
+import org.apache.camel.support.ExpressionAdapter;
+import org.apache.camel.support.MessageHelper;
+import org.apache.commons.io.IOUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DatasonnetExpression extends ExpressionAdapter implements
ExpressionResultTypeAware {
+ private static final Logger LOG =
LoggerFactory.getLogger(DatasonnetExpression.class);
+
+ private final String expression;
+ private MediaType bodyMediaType;
+ private MediaType outputMediaType;
+ private Class<?> resultType;
+ private Collection<String> libraryPaths;
+ private transient DatasonnetLanguage language;
+
+ public DatasonnetExpression(String expression) {
+ this.expression = expression;
+ }
+
+ public static DatasonnetExpression builder(String expression) {
+ DatasonnetExpression answer = new DatasonnetExpression(expression);
+ return answer;
+ }
+
+ public static DatasonnetExpression builder(String expression, Class<?>
resultType) {
+ DatasonnetExpression answer = new DatasonnetExpression(expression);
+ answer.setResultType(resultType);
+ return answer;
+ }
+
+ @Override
+ public boolean matches(Exchange exchange) {
+ this.outputMediaType = MediaTypes.APPLICATION_JAVA;
+ return evaluate(exchange, Boolean.class);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T> T evaluate(Exchange exchange, Class<T> type) {
+ try {
+ // pass exchange to CML lib using thread as context
+ CML.getInstance().getExchange().set(exchange);
+
+ Document<?> result = doEvaluate(exchange);
+ if (!type.equals(Object.class)) {
+ return ExchangeHelper.convertToType(exchange, type,
result.getContent());
+ } else if (resultType == null ||
resultType.equals(Document.class)) {
+ return (T) result;
+ } else {
+ return (T) result.getContent();
+ }
+ } catch (Exception e) {
+ throw new RuntimeExpressionException("Unable to evaluate
DataSonnet expression: " + expression, e);
+ } finally {
+ CML.getInstance().getExchange().remove();
+ }
+ }
+
+ private Document<?> doEvaluate(Exchange exchange) {
+ MediaType bodyMT = bodyMediaType;
+ if (bodyMT == null &&
!expression.startsWith(Header.DATASONNET_HEADER)) {
+ //Try to auto-detect input mime type if it was not explicitly set
+ String typeHeader =
exchange.getProperty(DatasonnetConstants.BODY_MEDIATYPE,
+ exchange.getIn().getHeader(Exchange.CONTENT_TYPE),
String.class);
+ if (typeHeader != null) {
+ bodyMT = MediaType.valueOf(typeHeader);
+ }
+ }
+
+ Document<?> body;
+
+ String bodyAsString =
MessageHelper.extractBodyAsString(exchange.getMessage());
+
+ if (exchange.getMessage().getBody() instanceof Document) {
+ body = (Document<?>) exchange.getMessage().getBody();
+ } else if (exchange.getMessage().getBody() == null ||
"".equals(bodyAsString)) {
+ //Empty body, force type to be application/java
+ body = new DefaultDocument<>("", MediaTypes.APPLICATION_JAVA);
+ } else if (MediaTypes.APPLICATION_JAVA.equalsTypeAndSubtype(bodyMT) ||
bodyMT == null) {
+ body = new DefaultDocument<>(exchange.getMessage().getBody());
+ } else {
+ body = new DefaultDocument<>(bodyAsString, bodyMT);
+ }
+
+ // the mapper is pre initialized
+ Mapper mapper = language.lookup(expression)
+ .orElseThrow(() -> new IllegalStateException("Datasonnet
expression not initialized"));
+
+ MediaType outMT = outputMediaType;
+ if (outMT == null) {
+ //Try to auto-detect output mime type if it was not explicitly set
+ String typeHeader =
exchange.getProperty(DatasonnetConstants.OUTPUT_MEDIATYPE,
+
exchange.getIn().getHeader(DatasonnetConstants.OUTPUT_MEDIATYPE), String.class);
+ if (typeHeader != null) {
+ outMT = MediaType.valueOf(typeHeader);
+ } else {
+ outMT = MediaTypes.ANY;
+ }
+ }
+
+ Map<String, Document<?>> inputs = Collections.singletonMap("body",
body);
+ if (resultType == null || resultType.equals(Document.class)) {
+ return mapper.transform(body, inputs, outMT, Object.class);
+ } else {
+ return mapper.transform(body, inputs, outMT, resultType);
+ }
+ }
+
+ private Map<String, String> resolveImports(DatasonnetLanguage language) {
+ if (libraryPaths == null) {
+ return language.getClasspathImports();
+ }
+
+ Map<String, String> answer = new HashMap<>();
+ LOG.debug("Explicit library path is: {}", libraryPaths);
+ for (String nextPath : libraryPaths) {
+ final File nextLibDir = new File(nextPath);
+ if (nextLibDir.isDirectory()) {
+ try {
+ Files.walkFileTree(nextLibDir.toPath(), new
SimpleFileVisitor<Path>() {
+ @Override
+ public FileVisitResult visitFile(Path file,
BasicFileAttributes attrs) throws IOException {
+ File f = file.toFile();
+ if (!f.isDirectory() &&
f.getName().toLowerCase().endsWith(".libsonnet")) {
+ String content =
IOUtils.toString(file.toUri(), Charset.defaultCharset());
+ Path relative =
nextLibDir.toPath().relativize(file);
+ LOG.debug("Loading DataSonnet library: {}",
relative);
+ answer.put(relative.toString(), content);
+ }
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ } catch (IOException e) {
+ LOG.warn("Unable to load DataSonnet library from: " +
nextPath, e);
+ }
+ }
+ }
+
+ return answer;
+ }
+
+ @Override
+ public void init(CamelContext context) {
+ super.init(context);
+
+ language = (DatasonnetLanguage) context.resolveLanguage("datasonnet");
+ // initialize mapper eager
+ language.computeIfMiss(expression, () -> {
+ MapperBuilder builder = new MapperBuilder(expression)
+ .withInputNames("body")
+ .withImports(resolveImports(language))
+ .withLibrary(CML.getInstance())
+ .withDefaultOutput(MediaTypes.APPLICATION_JAVA);
+
+ Set<Library> additionalLibraries =
context.getRegistry().findByType(com.datasonnet.spi.Library.class);
+ for (Library lib : additionalLibraries) {
+ builder = builder.withLibrary(lib);
+ }
+ return builder.build();
+ });
+ }
+
+ // Getter/Setter methods
+ //
-------------------------------------------------------------------------
+
+ public MediaType getBodyMediaType() {
+ return bodyMediaType;
+ }
+
+ /**
+ * The message's body MediaType
+ */
+ public void setBodyMediaType(MediaType inputMimeType) {
+ this.bodyMediaType = inputMimeType;
+ }
+
+ public MediaType getOutputMediaType() {
+ return outputMediaType;
+ }
+
+ /**
+ * The MediaType to output
+ */
+ public void setOutputMediaType(MediaType outputMimeType) {
+ this.outputMediaType = outputMimeType;
+ }
+
+ public Collection<String> getLibraryPaths() {
+ return libraryPaths;
+ }
+
+ /**
+ * The paths to search for .libsonnet files
+ */
+ public void setLibraryPaths(Collection<String> libraryPaths) {
+ this.libraryPaths = libraryPaths;
+ }
+
+ @Override
+ public String getExpressionText() {
+ return this.expression;
+ }
+
+ @Override
+ public Class<?> getResultType() {
+ return this.resultType;
+ }
+
+ /**
+ * Sets the class of the result type (type from output).
+ * <p/>
+ * The default result type is com.datasonnet.document.Document
+ */
+ public void setResultType(Class<?> targetType) {
+ this.resultType = targetType;
+ }
+
+ // Fluent builder methods
+ //
-------------------------------------------------------------------------
+ public DatasonnetExpression bodyMediaType(MediaType bodyMediaType) {
+ setBodyMediaType(bodyMediaType);
+ return this;
+ }
+
+ public DatasonnetExpression outputMediaType(MediaType outputMediaType) {
+ setOutputMediaType(outputMediaType);
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return "datasonnet: " + expression;
+ }
+
+}
diff --git
a/components/camel-datasonnet/src/main/java/org/apache/camel/language/datasonnet/DatasonnetLanguage.java
b/components/camel-datasonnet/src/main/java/org/apache/camel/language/datasonnet/DatasonnetLanguage.java
new file mode 100644
index 00000000000..1249e5018a9
--- /dev/null
+++
b/components/camel-datasonnet/src/main/java/org/apache/camel/language/datasonnet/DatasonnetLanguage.java
@@ -0,0 +1,172 @@
+/*
+ * 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.datasonnet;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Supplier;
+
+import com.datasonnet.Mapper;
+import com.datasonnet.document.MediaType;
+import io.github.classgraph.ClassGraph;
+import io.github.classgraph.ScanResult;
+import org.apache.camel.CamelContext;
+import org.apache.camel.Expression;
+import org.apache.camel.Predicate;
+import org.apache.camel.spi.PropertyConfigurer;
+import org.apache.camel.spi.annotations.Language;
+import org.apache.camel.support.LRUCacheFactory;
+import org.apache.camel.support.TypedLanguageSupport;
+import org.apache.camel.support.component.PropertyConfigurerSupport;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Language("datasonnet")
+public class DatasonnetLanguage extends TypedLanguageSupport implements
PropertyConfigurer {
+ private static final Logger LOG =
LoggerFactory.getLogger(DatasonnetLanguage.class);
+
+ private static final Map<String, String> CLASSPATH_IMPORTS = new
HashMap<>();
+
+ static {
+ LOG.debug("One time classpath search...");
+ try (ScanResult scanResult = new ClassGraph().acceptPaths("/").scan())
{
+ try {
+ scanResult.getResourcesWithExtension("libsonnet")
+ .forEachByteArrayThrowingIOException((resource, bytes)
-> {
+ LOG.debug("Loading DataSonnet library: {}",
resource.getPath());
+ CLASSPATH_IMPORTS.put(resource.getPath(), new
String(bytes, StandardCharsets.UTF_8));
+ });
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ LOG.debug("One time classpath search done");
+ }
+
+ // Cache used to stores the Mappers
+ // See: {@link GroovyLanguage}
+ private final Map<String, Mapper> mapperCache =
LRUCacheFactory.newLRUSoftCache(16, 1000, true);
+
+ private MediaType bodyMediaType;
+ private MediaType outputMediaType;
+ private Collection<String> libraryPaths;
+
+ @Override
+ public Predicate createPredicate(String expression) {
+ return createPredicate(expression, null);
+ }
+
+ @Override
+ public Expression createExpression(String expression) {
+ return createExpression(expression, null);
+ }
+
+ @Override
+ public Predicate createPredicate(String expression, Object[] properties) {
+ return (Predicate) createExpression(expression, properties);
+ }
+
+ @Override
+ public Expression createExpression(String expression, Object[] properties)
{
+ expression = loadResource(expression);
+
+ DatasonnetExpression answer = new DatasonnetExpression(expression);
+ answer.setResultType(property(Class.class, properties, 0,
getResultType()));
+
+ String stringBodyMediaType = property(String.class, properties, 1,
null);
+ answer.setBodyMediaType(stringBodyMediaType != null ?
MediaType.valueOf(stringBodyMediaType) : bodyMediaType);
+ String stringOutputMediaType = property(String.class, properties, 2,
null);
+ answer.setOutputMediaType(stringOutputMediaType != null ?
MediaType.valueOf(stringOutputMediaType) : outputMediaType);
+
+ return answer;
+ }
+
+ Optional<Mapper> lookup(String script) {
+ return Optional.ofNullable(mapperCache.get(script));
+ }
+
+ Mapper computeIfMiss(String script, Supplier<Mapper> mapperSupplier) {
+ return mapperCache.computeIfAbsent(script, k -> mapperSupplier.get());
+ }
+
+ public Map<String, String> getClasspathImports() {
+ return CLASSPATH_IMPORTS;
+ }
+
+ @Override
+ public boolean configure(CamelContext camelContext, Object target, String
name, Object value, boolean ignoreCase) {
+ if (target != this) {
+ throw new IllegalStateException("Can only configure our own
instance !");
+ }
+
+ switch (ignoreCase ? name.toLowerCase() : name) {
+ case "bodyMediaType":
+ case "bodymediatype":
+
setBodyMediaType(PropertyConfigurerSupport.property(camelContext, String.class,
value));
+ return true;
+ case "outputMediaType":
+ case "outputmediatype":
+
setOutputMediaType(PropertyConfigurerSupport.property(camelContext,
String.class, value));
+ return true;
+ case "resultType":
+ case "resulttype":
+ setResultType(PropertyConfigurerSupport.property(camelContext,
Class.class, value));
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ // Getter/Setter methods
+ //
-------------------------------------------------------------------------
+
+ public MediaType getBodyMediaType() {
+ return bodyMediaType;
+ }
+
+ public void setBodyMediaType(MediaType bodyMediaType) {
+ this.bodyMediaType = bodyMediaType;
+ }
+
+ public void setBodyMediaType(String bodyMediaType) {
+ this.bodyMediaType = MediaType.valueOf(bodyMediaType);
+ }
+
+ public MediaType getOutputMediaType() {
+ return outputMediaType;
+ }
+
+ public void setOutputMediaType(MediaType outputMediaType) {
+ this.outputMediaType = outputMediaType;
+ }
+
+ public void setOutputMediaType(String outputMediaType) {
+ this.outputMediaType = MediaType.valueOf(outputMediaType);
+ }
+
+ public Collection<String> getLibraryPaths() {
+ return libraryPaths;
+ }
+
+ public void setLibraryPaths(Collection<String> libraryPaths) {
+ this.libraryPaths = libraryPaths;
+ }
+}
diff --git a/components/camel-datasonnet/src/test/java/library/TestLib.java
b/components/camel-datasonnet/src/test/java/library/TestLib.java
new file mode 100644
index 00000000000..a4a9bb00ff6
--- /dev/null
+++ b/components/camel-datasonnet/src/test/java/library/TestLib.java
@@ -0,0 +1,72 @@
+/*
+ * 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 library;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+
+import com.datasonnet.header.Header;
+import com.datasonnet.jsonnet.Val;
+import com.datasonnet.spi.DataFormatService;
+import com.datasonnet.spi.Library;
+import org.springframework.stereotype.Component;
+
+@Component
+public class TestLib extends Library {
+
+ private static final TestLib INSTANCE = new TestLib();
+
+ public TestLib() {
+ }
+
+ public static TestLib getInstance() {
+ return INSTANCE;
+ }
+
+ @Override
+ public String namespace() {
+ return "testlib";
+ }
+
+ @Override
+ public Map<String, Val.Func> functions(DataFormatService dataFormats,
Header header) {
+ Map<String, Val.Func> answer = new HashMap<>();
+ answer.put("sayHello", makeSimpleFunc(
+ Collections.emptyList(), //parameters list
+ new Function<List<Val>, Val>() {
+ @Override
+ public Val apply(List<Val> vals) {
+ return new Val.Str("Hello, World");
+ }
+ }));
+ return answer;
+ }
+
+ @Override
+ public Map<String, Val.Obj> modules(DataFormatService dataFormats, Header
header) {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public Set<String> libsonnets() {
+ return Collections.emptySet();
+ }
+}
diff --git
a/components/camel-datasonnet/src/test/java/org/apache/camel/language/DatasonnetLanguageTest.java
b/components/camel-datasonnet/src/test/java/org/apache/camel/language/DatasonnetLanguageTest.java
new file mode 100644
index 00000000000..6d7f0c562b2
--- /dev/null
+++
b/components/camel-datasonnet/src/test/java/org/apache/camel/language/DatasonnetLanguageTest.java
@@ -0,0 +1,36 @@
+/*
+ * 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;
+
+import org.apache.camel.builder.LanguageBuilderFactory;
+import org.apache.camel.model.language.DatasonnetExpression;
+import org.junit.jupiter.api.Disabled;
+
+/**
+ * Ensures that the "datasonnet" language is compliant with the typed language
expectations.
+ */
+class DatasonnetLanguageTest extends
AbstractTypedLanguageTest<DatasonnetExpression.Builder, DatasonnetExpression> {
+
+ DatasonnetLanguageTest() {
+ super("body", LanguageBuilderFactory::datasonnet);
+ }
+
+ @Disabled("In this case a DefaultDocument is received and this type has no
equal method implemented")
+ @Override
+ void testExpressionOnly() {
+ }
+}
diff --git
a/components/camel-datasonnet/src/test/java/org/apache/camel/language/datasonnet/CamelDatasonnetJavaDslTest.java
b/components/camel-datasonnet/src/test/java/org/apache/camel/language/datasonnet/CamelDatasonnetJavaDslTest.java
new file mode 100644
index 00000000000..fe72518cdfc
--- /dev/null
+++
b/components/camel-datasonnet/src/test/java/org/apache/camel/language/datasonnet/CamelDatasonnetJavaDslTest.java
@@ -0,0 +1,128 @@
+/*
+ * 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.datasonnet;
+
+import java.io.InputStream;
+import java.nio.charset.Charset;
+
+import com.datasonnet.document.Document;
+import com.datasonnet.document.MediaTypes;
+import library.TestLib;
+import org.apache.camel.BindToRegistry;
+import org.apache.camel.Exchange;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.support.ExchangeHelper;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.apache.commons.io.IOUtils;
+import org.junit.jupiter.api.Test;
+import org.skyscreamer.jsonassert.JSONAssert;
+
+public class CamelDatasonnetJavaDslTest extends CamelTestSupport {
+ private MockEndpoint mock;
+
+ @BindToRegistry
+ public TestLib testLib() {
+ return TestLib.getInstance();
+ }
+
+ @Test
+ public void testTransform() throws Exception {
+ runCamelTest(loadResourceAsString("simpleMapping_payload.json"),
+ loadResourceAsString("simpleMapping_result.json"),
+ "direct:basicTransform");
+ }
+
+ @Test
+ public void testTransformXML() throws Exception {
+ runCamelTest(loadResourceAsString("payload.xml"),
+ loadResourceAsString("readXMLExtTest.json"),
+ "direct:transformXML");
+ }
+
+ @Test
+ public void testTransformCSV() throws Exception {
+ runCamelTest(loadResourceAsString("payload.csv"),
+ "{\"account\":\"123\"}",
+ "direct:transformCSV");
+ }
+
+ @Test
+ public void testRegistryLibraries() throws Exception {
+ runCamelTest("{}",
+ "{ \"test\":\"Hello, World\"}",
+ "direct:registryLibraries");
+ }
+
+ @Override
+ protected RoutesBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:basicTransform")
+ .routeId("basicTransform")
+ .setProperty("test", constant("HelloWorld"))
+ .setProperty("count", simple("1", Integer.class))
+ .setProperty("isActive", simple("true", Boolean.class))
+ .setProperty("1. Full Name", constant("DataSonnet"))
+
.transform(datasonnet("resource:classpath:simpleMapping.ds", String.class,
+ MediaTypes.APPLICATION_JSON_VALUE,
MediaTypes.APPLICATION_JSON_VALUE))
+ .to("mock:direct:end");
+
+ from("direct:transformXML")
+ .routeId("transformXML")
+
.transform(datasonnet("resource:classpath:readXMLExtTest.ds", String.class,
+ MediaTypes.APPLICATION_XML_VALUE,
MediaTypes.APPLICATION_JSON_VALUE))
+ .to("mock:direct:end");
+
+ from("direct:transformCSV")
+ .routeId("transformCSV")
+
.transform(datasonnet("resource:classpath:readCSVTest.ds", String.class,
+ MediaTypes.APPLICATION_CSV_VALUE,
MediaTypes.APPLICATION_JSON_VALUE))
+ .to("mock:direct:end");
+
+ from("direct:registryLibraries")
+ .routeId("registryLibraries")
+ .transform(datasonnet("{test: testlib.sayHello()}",
String.class,
+ MediaTypes.APPLICATION_JSON_VALUE,
MediaTypes.APPLICATION_JSON_VALUE))
+ .to("mock:direct:end");
+
+ }
+ };
+ }
+
+ private void runCamelTest(Object payload, String expectedJson, String uri)
throws Exception {
+ template.sendBody(uri, payload);
+ mock = getMockEndpoint("mock:direct:end");
+ Exchange exchange =
mock.assertExchangeReceived(mock.getReceivedCounter() - 1);
+ Object body = exchange.getMessage().getBody();
+ String response;
+ if (body instanceof Document) {
+ response = ExchangeHelper.convertToMandatoryType(exchange,
String.class, ((Document<?>) body).getContent());
+ } else {
+ response = exchange.getMessage().getBody(String.class);
+
+ }
+ JSONAssert.assertEquals(expectedJson, response, true);
+ }
+
+ private String loadResourceAsString(String name) throws Exception {
+ InputStream is = getClass().getClassLoader().getResourceAsStream(name);
+ return IOUtils.toString(is, Charset.defaultCharset());
+ }
+}
diff --git
a/components/camel-datasonnet/src/test/java/org/apache/camel/language/datasonnet/CamelDatasonnetTest.java
b/components/camel-datasonnet/src/test/java/org/apache/camel/language/datasonnet/CamelDatasonnetTest.java
new file mode 100644
index 00000000000..22486236499
--- /dev/null
+++
b/components/camel-datasonnet/src/test/java/org/apache/camel/language/datasonnet/CamelDatasonnetTest.java
@@ -0,0 +1,195 @@
+/*
+ * 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.datasonnet;
+
+import java.io.InputStream;
+import java.nio.charset.Charset;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.TimeZone;
+
+import com.datasonnet.document.Document;
+import org.apache.camel.Exchange;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.support.ExchangeHelper;
+import org.apache.camel.test.spring.junit5.CamelSpringTestSupport;
+import org.apache.commons.io.IOUtils;
+import org.junit.jupiter.api.Test;
+import org.skyscreamer.jsonassert.JSONAssert;
+import org.springframework.context.support.AbstractApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class CamelDatasonnetTest extends CamelSpringTestSupport {
+ private MockEndpoint mock;
+
+ @Override
+ protected AbstractApplicationContext createApplicationContext() {
+ return new
ClassPathXmlApplicationContext("org/apache/camel/language.datasonnet/camel-context.xml");
+ }
+
+ @Test
+ public void testTransform() throws Exception {
+ runCamelTest(loadResourceAsString("simpleMapping_payload.json"),
+ loadResourceAsString("simpleMapping_result.json"),
+ "direct:basicTransform");
+ }
+
+ @Test
+ public void testTransformXML() throws Exception {
+ runCamelTest(loadResourceAsString("payload.xml"),
+ loadResourceAsString("readXMLExtTest.json"),
+ "direct:transformXML");
+ }
+
+ @Test
+ public void testTransformCSV() throws Exception {
+ runCamelTest(loadResourceAsString("payload.csv"),
+ "{\"account\":\"123\"}",
+ "direct:transformCSV");
+ }
+
+ @Test
+ public void testDatasonnetScript() throws Exception {
+ runCamelTest(loadResourceAsString("simpleMapping_payload.json"),
+ loadResourceAsString("simpleMapping_result.json"),
+ "direct:datasonnetScript");
+ }
+
+ @Test
+ public void testNamedImports() throws Exception {
+ runCamelTest("{}",
+ loadResourceAsString("namedImports_result.json"),
+ "direct:namedImports");
+ }
+
+ @Test
+ public void testExpressionLanguage() throws Exception {
+ runCamelTest("World",
+ "{ \"test\":\"Hello, World\"}",
+ "direct:expressionLanguage");
+ }
+
+ @Test
+ public void testNullInput() throws Exception {
+ runCamelTest("",
+ "{ \"test\":\"Hello, World\"}",
+ "direct:nullInput");
+ runCamelTest(null,
+ "{ \"test\":\"Hello, World\"}",
+ "direct:nullInput");
+ }
+
+ @Test
+ public void testRegistryLibraries() throws Exception {
+ runCamelTest("",
+ "{ \"test\":\"Hello, World\"}",
+ "direct:registryLibraries");
+ }
+
+ @Test
+ public void testReadJava() throws Exception {
+ Gizmo theGizmo = new Gizmo();
+ theGizmo.setName("gizmo");
+ theGizmo.setQuantity(123);
+ theGizmo.setInStock(true);
+ theGizmo.setColors(Arrays.asList("red", "white", "blue"));
+
+ Manufacturer manufacturer = new Manufacturer();
+ manufacturer.setManufacturerName("ACME Corp.");
+ manufacturer.setManufacturerCode("ACME123");
+ theGizmo.setManufacturer(manufacturer);
+
+ SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
+ df.setTimeZone(TimeZone.getTimeZone("UTC"));
+ theGizmo.setDate(df.parse("2020-01-06"));
+
+ runCamelTest(theGizmo,
+ loadResourceAsString("javaTest.json"),
+ "direct:readJava");
+ }
+
+ @Test
+ public void testReadJavaDatasonnetHeader() throws Exception {
+ Gizmo theGizmo = new Gizmo();
+ theGizmo.setName("gizmo");
+ theGizmo.setQuantity(123);
+ theGizmo.setInStock(true);
+ theGizmo.setColors(Arrays.asList("red", "white", "blue"));
+
+ Manufacturer manufacturer = new Manufacturer();
+ manufacturer.setManufacturerName("ACME Corp.");
+ manufacturer.setManufacturerCode("ACME123");
+ theGizmo.setManufacturer(manufacturer);
+
+ SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
+ df.setTimeZone(TimeZone.getTimeZone("UTC"));
+ theGizmo.setDate(df.parse("2020-01-06"));
+
+ runCamelTest(theGizmo,
+ loadResourceAsString("javaTest.json"),
+ "direct:readJavaDatasonnetHeader");
+ }
+
+ @Test
+ public void testWriteJava() throws Exception {
+ Gizmo theGizmo = new Gizmo();
+ theGizmo.setName("gizmo");
+ theGizmo.setQuantity(123);
+ theGizmo.setInStock(true);
+ theGizmo.setColors(Arrays.asList("red", "white", "blue"));
+
+ Manufacturer manufacturer = new Manufacturer();
+ manufacturer.setManufacturerName("ACME Corp.");
+ manufacturer.setManufacturerCode("ACME123");
+ theGizmo.setManufacturer(manufacturer);
+
+ SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
+ df.setTimeZone(TimeZone.getTimeZone("UTC"));
+ theGizmo.setDate(df.parse("2020-01-06"));
+
+ String payload = loadResourceAsString("javaTest.json");
+
+ template.sendBody("direct:writeJava", payload);
+ mock = getMockEndpoint("mock:direct:end");
+ Exchange exchange =
mock.assertExchangeReceived(mock.getReceivedCounter() - 1);
+ Object response = exchange.getIn().getBody();
+
+ assertEquals(theGizmo, response);
+ }
+
+ private void runCamelTest(Object payload, String expectedJson, String uri)
throws Exception {
+ template.sendBody(uri, payload);
+ mock = getMockEndpoint("mock:direct:end");
+ Exchange exchange =
mock.assertExchangeReceived(mock.getReceivedCounter() - 1);
+ Object body = exchange.getMessage().getBody();
+ String response;
+ if (body instanceof Document) {
+ response = ExchangeHelper.convertToMandatoryType(exchange,
String.class, ((Document<?>) body).getContent());
+ } else {
+ response = exchange.getMessage().getBody(String.class);
+
+ }
+ JSONAssert.assertEquals(expectedJson, response, true);
+ }
+
+ private String loadResourceAsString(String name) throws Exception {
+ InputStream is = getClass().getClassLoader().getResourceAsStream(name);
+ return IOUtils.toString(is, Charset.defaultCharset());
+ }
+}
diff --git
a/components/camel-datasonnet/src/test/java/org/apache/camel/language/datasonnet/ExpressionsInJavaTest.java
b/components/camel-datasonnet/src/test/java/org/apache/camel/language/datasonnet/ExpressionsInJavaTest.java
new file mode 100644
index 00000000000..ce21eaf1350
--- /dev/null
+++
b/components/camel-datasonnet/src/test/java/org/apache/camel/language/datasonnet/ExpressionsInJavaTest.java
@@ -0,0 +1,100 @@
+/*
+ * 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.datasonnet;
+
+import java.util.Collections;
+import java.util.List;
+
+import com.datasonnet.document.MediaTypes;
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Exchange;
+import org.apache.camel.Produce;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class ExpressionsInJavaTest extends CamelTestSupport {
+ @EndpointInject("mock:direct:response")
+ protected MockEndpoint endEndpoint;
+
+ @Produce("direct:expressionsInJava")
+ protected ProducerTemplate expressionsInJavaProducer;
+
+ @Produce("direct:fluentBuilder")
+ protected ProducerTemplate fluentBuilderProducer;
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:expressionsInJava")
+ .choice()
+ .when(datasonnet("payload == 'World'"))
+ .setBody(datasonnet("'Hello, ' +
payload", String.class))
+ .otherwise()
+ .setBody(datasonnet("'Good bye, ' +
payload", String.class))
+ .end()
+ .to("mock:direct:response");
+
+ from("direct:fluentBuilder")
+ // no optional params, look in header
+ .setHeader(Exchange.CONTENT_TYPE,
constant(MediaTypes.APPLICATION_JAVA_VALUE))
+ .setBody(DatasonnetExpression.builder("payload"))
+ .removeHeader(Exchange.CONTENT_TYPE)
+
+ // override output
+ .transform(DatasonnetExpression.builder("payload",
String.class)
+ .outputMediaType(MediaTypes.APPLICATION_JSON))
+
+ // override input
+ .transform(
+ DatasonnetExpression.builder("payload",
List.class).bodyMediaType(MediaTypes.APPLICATION_JSON))
+
+ // override both
+ .setHeader(Exchange.CONTENT_TYPE,
constant(MediaTypes.APPLICATION_JSON_VALUE))
+ .setBody(constant("<root>some-value</root>"))
+
.transform(DatasonnetExpression.builder("payload.root['$']", String.class)
+ .bodyMediaType(MediaTypes.APPLICATION_XML)
+ .outputMediaType(MediaTypes.APPLICATION_JSON))
+ .to("mock:direct:response");
+ }
+ };
+ }
+
+ @Test
+ public void testExpressionLanguageInJava() {
+ endEndpoint.expectedMessageCount(1);
+ expressionsInJavaProducer.sendBody("World");
+ Exchange exchange =
endEndpoint.assertExchangeReceived(endEndpoint.getReceivedCounter() - 1);
+ String response = exchange.getIn().getBody().toString();
+ assertEquals("Hello, World", response);
+ }
+
+ @Test
+ public void testFluentBuilder() {
+ endEndpoint.expectedMessageCount(1);
+
fluentBuilderProducer.sendBody(Collections.singletonList("datasonnet"));
+ Exchange exchange =
endEndpoint.assertExchangeReceived(endEndpoint.getReceivedCounter() - 1);
+ String response = exchange.getMessage().getBody(String.class);
+ assertEquals("\"some-value\"", response);
+ }
+}
diff --git
a/components/camel-datasonnet/src/test/java/org/apache/camel/language/datasonnet/Gizmo.java
b/components/camel-datasonnet/src/test/java/org/apache/camel/language/datasonnet/Gizmo.java
new file mode 100644
index 00000000000..743320eaf78
--- /dev/null
+++
b/components/camel-datasonnet/src/test/java/org/apache/camel/language/datasonnet/Gizmo.java
@@ -0,0 +1,114 @@
+/*
+ * 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.datasonnet;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Objects;
+
+public class Gizmo {
+ private String name;
+ private int quantity;
+ private List<String> colors;
+ private boolean inStock;
+ private Manufacturer manufacturer;
+ private Date date;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public int getQuantity() {
+ return quantity;
+ }
+
+ public void setQuantity(int quantity) {
+ this.quantity = quantity;
+ }
+
+ public List<String> getColors() {
+ return colors;
+ }
+
+ public void setColors(List<String> colors) {
+ this.colors = colors;
+ }
+
+ public boolean isInStock() {
+ return inStock;
+ }
+
+ public void setInStock(boolean inStock) {
+ this.inStock = inStock;
+ }
+
+ public Manufacturer getManufacturer() {
+ return manufacturer;
+ }
+
+ public void setManufacturer(Manufacturer manufacturer) {
+ this.manufacturer = manufacturer;
+ }
+
+ public Date getDate() {
+ return date;
+ }
+
+ public void setDate(Date date) {
+ this.date = date;
+ }
+
+ @Override
+ public String toString() {
+ return "Gizmo{" +
+ "name='" + name + '\'' +
+ ", quantity=" + quantity +
+ ", colors=" + colors +
+ ", inStock=" + inStock +
+ ", manufacturer=" + manufacturer +
+ ", date=" + date +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ Gizmo gizmo = (Gizmo) o;
+ return getQuantity() == gizmo.getQuantity() &&
+ isInStock() == gizmo.isInStock() &&
+ Objects.equals(getName(), gizmo.getName()) &&
+ Objects.equals(getColors(), gizmo.getColors()) &&
+ Objects.equals(date, gizmo.getDate()) &&
+ Objects.equals(getManufacturer(), gizmo.getManufacturer());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(getName(), getQuantity(), getColors(),
isInStock(), getManufacturer(), getDate());
+ }
+}
diff --git
a/components/camel-datasonnet/src/test/java/org/apache/camel/language/datasonnet/Manufacturer.java
b/components/camel-datasonnet/src/test/java/org/apache/camel/language/datasonnet/Manufacturer.java
new file mode 100644
index 00000000000..c9030411de1
--- /dev/null
+++
b/components/camel-datasonnet/src/test/java/org/apache/camel/language/datasonnet/Manufacturer.java
@@ -0,0 +1,68 @@
+/*
+ * 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.datasonnet;
+
+import java.util.Objects;
+
+public class Manufacturer {
+ private String manufacturerName;
+ private String manufacturerCode;
+
+ public String getManufacturerName() {
+ return manufacturerName;
+ }
+
+ public void setManufacturerName(String manufacturerName) {
+ this.manufacturerName = manufacturerName;
+ }
+
+ public String getManufacturerCode() {
+ return manufacturerCode;
+ }
+
+ public void setManufacturerCode(String manufacturerCode) {
+ this.manufacturerCode = manufacturerCode;
+ }
+
+ @Override
+ public String toString() {
+ return "Manufacturer{" +
+ "manufacturerName='" + manufacturerName + '\'' +
+ ", manufacturerCode='" + manufacturerCode + '\'' +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ Manufacturer that = (Manufacturer) o;
+ return Objects.equals(getManufacturerName(),
that.getManufacturerName()) &&
+ Objects.equals(getManufacturerCode(),
that.getManufacturerCode());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(getManufacturerName(), getManufacturerCode());
+ }
+}
diff --git
a/components/camel-datasonnet/src/test/java/org/apache/camel/language/datasonnet/PropertiesTest.java
b/components/camel-datasonnet/src/test/java/org/apache/camel/language/datasonnet/PropertiesTest.java
new file mode 100644
index 00000000000..7b856b42719
--- /dev/null
+++
b/components/camel-datasonnet/src/test/java/org/apache/camel/language/datasonnet/PropertiesTest.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.datasonnet;
+
+import java.util.Properties;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.support.DefaultExchange;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+public class PropertiesTest extends CamelTestSupport {
+ @Test
+ public void testPropertiesBuiltin() throws Exception {
+ MockEndpoint mock = getMockEndpoint("mock:camel");
+ mock.expectedBodiesReceived("bar");
+
+ template.send("direct:in", new DefaultExchange(context));
+
+ mock.assertIsSatisfied();
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ public void configure() {
+ Properties prop = new Properties();
+ prop.setProperty("foo", "bar");
+ context.getPropertiesComponent().setInitialProperties(prop);
+
+ from("direct:in")
+ .setBody(datasonnet("cml.properties('foo')",
String.class))
+ .to("mock:camel");
+ }
+ };
+ }
+}
diff --git a/components/camel-datasonnet/src/test/resources/dslibs.jar
b/components/camel-datasonnet/src/test/resources/dslibs.jar
new file mode 100644
index 00000000000..64c90c8edb2
Binary files /dev/null and
b/components/camel-datasonnet/src/test/resources/dslibs.jar differ
diff --git a/components/camel-datasonnet/src/test/resources/javaTest.json
b/components/camel-datasonnet/src/test/resources/javaTest.json
new file mode 100644
index 00000000000..2dffc040e79
--- /dev/null
+++ b/components/camel-datasonnet/src/test/resources/javaTest.json
@@ -0,0 +1,15 @@
+{
+ "pojoColors": [
+ "red",
+ "white",
+ "blue"
+ ],
+ "pojoInStock": true,
+ "pojoManufacturer": {
+ "manufacturerCode": "ACME123",
+ "manufacturerName": "ACME Corp."
+ },
+ "pojoName": "gizmo",
+ "pojoQuantity": 123,
+ "pojoDate": "2020-01-06"
+}
diff --git
a/components/camel-datasonnet/src/test/resources/libraries/testlib4.libsonnet
b/components/camel-datasonnet/src/test/resources/libraries/testlib4.libsonnet
new file mode 100644
index 00000000000..d6fdda1188f
--- /dev/null
+++
b/components/camel-datasonnet/src/test/resources/libraries/testlib4.libsonnet
@@ -0,0 +1,21 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+{
+ sayBye(name)::
+ "Bye, " + name + " : TestLib4"
+}
\ No newline at end of file
diff --git a/components/camel-datasonnet/src/test/resources/log4j2.properties
b/components/camel-datasonnet/src/test/resources/log4j2.properties
new file mode 100644
index 00000000000..8f2e7bb154d
--- /dev/null
+++ b/components/camel-datasonnet/src/test/resources/log4j2.properties
@@ -0,0 +1,32 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+
+appender.file.type = File
+appender.file.name = file
+appender.file.fileName = target/camel-datasonnet-test.log
+appender.file.layout.type = PatternLayout
+appender.file.layout.pattern = %d [%-15.15t] %-5p %-30.30c{1} - %m%n
+appender.out.type = Console
+appender.out.name = out
+appender.out.layout.type = PatternLayout
+appender.out.layout.pattern = [%30.30t] %-30.30c{1} %-5p %m%n
+logger.springframework.name = org.springframework
+logger.springframework.level = WARN
+logger.datasonnet.name=org.apache.camel.language.datasonnet
+logger.datasonnet.level=DEBUG
+rootLogger.level = INFO
+rootLogger.appenderRef.file.ref = file
diff --git a/components/camel-datasonnet/src/test/resources/namedImports.ds
b/components/camel-datasonnet/src/test/resources/namedImports.ds
new file mode 100644
index 00000000000..dbf0a4f611f
--- /dev/null
+++ b/components/camel-datasonnet/src/test/resources/namedImports.ds
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+local testlib1 = import 'testlib.libsonnet';
+local testlib2 = import 'libraries/testlib2.libsonnet';
+local testlib3 = import 'testlib3.libsonnet';
+local testlib4 = import 'libraries/testlib4.libsonnet';
+
+{
+ "Lib1JAR": testlib1.sayHello("World"),
+ "Lib2JAR": testlib2.sayBye("World"),
+ "Lib3FS": testlib3.sayHello("World"),
+ "Lib4FS": testlib4.sayBye("World")
+}
\ No newline at end of file
diff --git a/components/camel-datasonnet/src/test/resources/namedImportsFS.ds
b/components/camel-datasonnet/src/test/resources/namedImportsFS.ds
new file mode 100644
index 00000000000..be167943e58
--- /dev/null
+++ b/components/camel-datasonnet/src/test/resources/namedImportsFS.ds
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+local testlib3 = import 'testlib3.libsonnet';
+local testlib4 = import 'libraries/testlib4.libsonnet';
+
+{
+ "Lib3FS": testlib3.sayHello("World"),
+ "Lib4FS": testlib4.sayBye("World")
+}
\ No newline at end of file
diff --git
a/components/camel-datasonnet/src/test/resources/namedImports_result.json
b/components/camel-datasonnet/src/test/resources/namedImports_result.json
new file mode 100644
index 00000000000..56fcae153c0
--- /dev/null
+++ b/components/camel-datasonnet/src/test/resources/namedImports_result.json
@@ -0,0 +1 @@
+{"Lib1JAR":"Hello, World","Lib2JAR":"Bye, World","Lib3FS":"Hello, World :
TestLib3","Lib4FS":"Bye, World : TestLib4"}
\ No newline at end of file
diff --git
a/components/camel-datasonnet/src/test/resources/org/apache/camel/language.datasonnet/camel-context.xml
b/components/camel-datasonnet/src/test/resources/org/apache/camel/language.datasonnet/camel-context.xml
new file mode 100644
index 00000000000..a7ca6d87cdc
--- /dev/null
+++
b/components/camel-datasonnet/src/test/resources/org/apache/camel/language.datasonnet/camel-context.xml
@@ -0,0 +1,205 @@
+<?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.
+
+-->
+<!-- Configures the Camel Context-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:context="http://www.springframework.org/schema/context"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://camel.apache.org/schema/spring
http://camel.apache.org/schema/spring/camel-spring.xsd
+ http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
+">
+ <context:component-scan base-package="library"/>
+
+ <camelContext id="main" xmlns="http://camel.apache.org/schema/spring">
+ <route id="basicTransform">
+ <from uri="direct:basicTransform"/>
+
+ <setProperty name="test">
+ <constant>HelloWorld</constant>
+ </setProperty>
+ <setProperty name="count">
+ <simple resultType="java.lang.Integer">1</simple>
+ </setProperty>
+ <setProperty name="isActive">
+ <simple resultType="java.lang.Boolean">true</simple>
+ </setProperty>
+ <setProperty name="1. Full Name">
+ <constant>DataSonnet</constant>
+ </setProperty>
+
+ <transform>
+ <datasonnet bodyMediaType="application/json"
outputMediaType="application/json"
+
resultType="java.lang.String">resource:classpath:simpleMapping.ds</datasonnet>
+ </transform>
+ <to uri="mock:direct:end"/>
+ </route>
+
+ <route id="transformXML">
+ <from uri="direct:transformXML"/>
+ <transform>
+ <datasonnet bodyMediaType="application/xml"
outputMediaType="application/json"
+
resultType="java.lang.String">resource:classpath:readXMLExtTest.ds</datasonnet>
+ </transform>
+ <to uri="mock:direct:end"/>
+ </route>
+
+ <route id="transformCSV">
+ <from uri="direct:transformCSV"/>
+ <transform>
+ <datasonnet bodyMediaType="application/csv"
outputMediaType="application/json"
+
resultType="java.lang.String">resource:classpath:readCSVTest.ds</datasonnet>
+ </transform>
+ <to uri="mock:direct:end"/>
+ </route>
+
+ <route id="datasonnetScript">
+ <from uri="direct:datasonnetScript"/>
+
+ <setProperty name="test">
+ <simple>HelloWorld</simple>
+ </setProperty>
+ <setProperty name="count">
+ <simple resultType="java.lang.Integer">1</simple>
+ </setProperty>
+ <setProperty name="isActive">
+ <simple resultType="java.lang.Boolean">true</simple>
+ </setProperty>
+ <setProperty name="1. Full Name">
+ <constant>DataSonnet</constant>
+ </setProperty>
+
+ <transform>
+ <datasonnet bodyMediaType="application/json"
outputMediaType="application/json"
+ resultType="java.lang.String"><![CDATA[
+{
+ "uid": payload.userId,
+ "uname": payload.name,
+ "testVar": cml.exchangeProperty('test'),
+ "isActive": cml.exchangeProperty('isActive'),
+ "count": cml.exchangeProperty('count'),
+ "fullName": cml.exchangeProperty('1. Full Name')
+}
+]]>
+ </datasonnet>
+ </transform>
+
+ <to uri="mock:direct:end"/>
+ </route>
+
+ <route id="namedImports">
+ <from uri="direct:namedImports"/>
+ <transform>
+ <datasonnet bodyMediaType="application/json"
outputMediaType="application/json"
+
resultType="java.lang.String">resource:classpath:namedImports.ds</datasonnet>
+ </transform>
+ <to uri="mock:direct:end"/>
+ </route>
+
+ <route id="readJava">
+ <from uri="direct:readJava"/>
+ <transform>
+ <datasonnet bodyMediaType="application/x-java-object"
outputMediaType="application/json"
+
resultType="java.lang.String">resource:classpath:readJavaTest.ds</datasonnet>
+ </transform>
+ <to uri="mock:direct:end"/>
+ </route>
+
+ <route id="readJavaDatasonnetHeader">
+ <from uri="direct:readJavaDatasonnetHeader"/>
+ <transform>
+ <datasonnet outputMediaType="application/json"
resultType="java.lang.String">resource:classpath:readJavaTest.ds</datasonnet>
+ </transform>
+ <to uri="mock:direct:end"/>
+ </route>
+
+ <route id="writeJava">
+ <from uri="direct:writeJava"/>
+ <transform>
+ <datasonnet bodyMediaType="application/json"
outputMediaType="application/x-java-object"
+
resultType="org.apache.camel.language.datasonnet.Gizmo">resource:classpath:writeJavaTest.ds</datasonnet>
+ </transform>
+ <to uri="mock:direct:end"/>
+ </route>
+
+ <route id="expressionLanguage">
+ <from uri="direct:expressionLanguage"/>
+
+ <setHeader name="CamelDatasonnetOutputMediaType">
+ <constant>text/plain</constant>
+ </setHeader>
+ <setHeader name="HelloHeader">
+ <language language="datasonnet">"Hello, " + payload</language>
+ </setHeader>
+
+ <setHeader name="CamelDatasonnetOutputMediaType">
+ <constant>application/json</constant>
+ </setHeader>
+ <setBody>
+ <language language="datasonnet">
+ <![CDATA[
+ {
+ test: cml.header('HelloHeader')
+ }
+ ]]>
+ </language>
+ </setBody>
+
+ <to uri="mock:direct:end"/>
+ </route>
+
+ <route id="nullInput">
+ <from uri="direct:nullInput"/>
+ <setBody>
+ <datasonnet outputMediaType="application/json"
resultType="java.lang.String">
+ {
+ test: "Hello, World"
+ }
+ </datasonnet>
+ </setBody>
+
+ <to uri="mock:direct:end"/>
+ </route>
+
+ <route id="registryLibraries">
+ <from uri="direct:registryLibraries"/>
+ <setBody>
+ <datasonnet outputMediaType="application/json"
resultType="java.lang.String">
+ {
+ test: testlib.sayHello()
+ }
+ </datasonnet>
+ </setBody>
+
+ <to uri="mock:direct:end"/>
+ </route>
+
+ <route id="transformEmptyBody">
+ <from uri="direct:transformEmptyBody"/>
+ <transform>
+ <datasonnet bodyMediaType="application/xml"
outputMediaType="application/json"
+ resultType="java.lang.String">{ "hello": "world"
}</datasonnet>
+ </transform>
+ <to uri="mock:direct:end"/>
+ </route>
+
+ </camelContext>
+
+</beans>
diff --git a/components/camel-datasonnet/src/test/resources/payload.csv
b/components/camel-datasonnet/src/test/resources/payload.csv
new file mode 100644
index 00000000000..75676b43329
--- /dev/null
+++ b/components/camel-datasonnet/src/test/resources/payload.csv
@@ -0,0 +1,2 @@
+account,firstName,lastName
+123,Joe,Doe
diff --git a/components/camel-datasonnet/src/test/resources/payload.xml
b/components/camel-datasonnet/src/test/resources/payload.xml
new file mode 100644
index 00000000000..f65ee1444c8
--- /dev/null
+++ b/components/camel-datasonnet/src/test/resources/payload.xml
@@ -0,0 +1,22 @@
+<?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.
+
+-->
+<test:root xmlns:test="http://www.modusbox.com">
+ <test:datasonnet version="1.0">Hello World</test:datasonnet>
+</test:root>
diff --git a/components/camel-datasonnet/src/test/resources/readCSVTest.ds
b/components/camel-datasonnet/src/test/resources/readCSVTest.ds
new file mode 100644
index 00000000000..4d908e3c58b
--- /dev/null
+++ b/components/camel-datasonnet/src/test/resources/readCSVTest.ds
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+{
+ account: payload[0]["account"]
+}
diff --git a/components/camel-datasonnet/src/test/resources/readJavaTest.ds
b/components/camel-datasonnet/src/test/resources/readJavaTest.ds
new file mode 100644
index 00000000000..9150608c64b
--- /dev/null
+++ b/components/camel-datasonnet/src/test/resources/readJavaTest.ds
@@ -0,0 +1,30 @@
+/** DataSonnet
+version=2.0
+input payload application/x-java-object; DateFormat=yyyy-MM-dd
+*/
+
+/*
+ * 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.
+ */
+
+{
+ "pojoName": payload.name,
+ "pojoQuantity": payload.quantity,
+ "pojoInStock": payload.inStock,
+ "pojoColors": payload.colors,
+ "pojoManufacturer": payload.manufacturer,
+ "pojoDate": payload.date
+}
diff --git a/components/camel-datasonnet/src/test/resources/readXMLExtTest.ds
b/components/camel-datasonnet/src/test/resources/readXMLExtTest.ds
new file mode 100644
index 00000000000..3b4a4de2e19
--- /dev/null
+++ b/components/camel-datasonnet/src/test/resources/readXMLExtTest.ds
@@ -0,0 +1,23 @@
+/** DataSonnet
+version=2.0
+input payload application/xml; NamespaceSeparator=%; TextValueKey=__text;
AttributeCharacter=*
+*/
+
+/*
+ * 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.
+ */
+
+payload
diff --git a/components/camel-datasonnet/src/test/resources/readXMLExtTest.json
b/components/camel-datasonnet/src/test/resources/readXMLExtTest.json
new file mode 100644
index 00000000000..0e4ca5613c4
--- /dev/null
+++ b/components/camel-datasonnet/src/test/resources/readXMLExtTest.json
@@ -0,0 +1,13 @@
+{
+ "test%root": {
+ "*xmlns": {
+ "test": "http://www.modusbox.com"
+ },
+ "test%datasonnet": {
+ "*version": "1.0",
+ "__text": "Hello World",
+ "~": 1
+ },
+ "~": 1
+ }
+}
diff --git a/components/camel-datasonnet/src/test/resources/simpleMapping.ds
b/components/camel-datasonnet/src/test/resources/simpleMapping.ds
new file mode 100644
index 00000000000..a1faf255c38
--- /dev/null
+++ b/components/camel-datasonnet/src/test/resources/simpleMapping.ds
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+{
+ "uid": payload.userId,
+ "uname": payload.name,
+ "testVar": cml.exchangeProperty('test'),
+ "isActive": cml.exchangeProperty('isActive'),
+ "count": cml.exchangeProperty('count'),
+ "fullName": cml.exchangeProperty('1. Full Name')
+}
\ No newline at end of file
diff --git
a/components/camel-datasonnet/src/test/resources/simpleMapping_payload.json
b/components/camel-datasonnet/src/test/resources/simpleMapping_payload.json
new file mode 100644
index 00000000000..1ab6bf4b10b
--- /dev/null
+++ b/components/camel-datasonnet/src/test/resources/simpleMapping_payload.json
@@ -0,0 +1,4 @@
+{
+ "userId": 123,
+ "name": "JavaDuke"
+}
\ No newline at end of file
diff --git
a/components/camel-datasonnet/src/test/resources/simpleMapping_result.json
b/components/camel-datasonnet/src/test/resources/simpleMapping_result.json
new file mode 100644
index 00000000000..e9489bd470d
--- /dev/null
+++ b/components/camel-datasonnet/src/test/resources/simpleMapping_result.json
@@ -0,0 +1 @@
+{"count":1,"isActive":true,"testVar":"HelloWorld","uid":123,"uname":"JavaDuke","fullName":
"DataSonnet"}
\ No newline at end of file
diff --git a/components/camel-datasonnet/src/test/resources/testlib3.libsonnet
b/components/camel-datasonnet/src/test/resources/testlib3.libsonnet
new file mode 100644
index 00000000000..38878b352bf
--- /dev/null
+++ b/components/camel-datasonnet/src/test/resources/testlib3.libsonnet
@@ -0,0 +1,21 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+{
+ sayHello(name)::
+ "Hello, " + name + " : TestLib3"
+}
\ No newline at end of file
diff --git a/components/camel-datasonnet/src/test/resources/writeJavaTest.ds
b/components/camel-datasonnet/src/test/resources/writeJavaTest.ds
new file mode 100644
index 00000000000..d1e98b74b4c
--- /dev/null
+++ b/components/camel-datasonnet/src/test/resources/writeJavaTest.ds
@@ -0,0 +1,30 @@
+/** DataSonnet
+version=2.0
+output application/x-java-object; DateFormat=yyyy-MM-dd
+*/
+
+/*
+ * 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.
+ */
+
+{
+ "name": payload.pojoName,
+ "quantity": payload.pojoQuantity,
+ "inStock": payload.pojoInStock,
+ "colors": payload.pojoColors,
+ "manufacturer": payload.pojoManufacturer,
+ "date": payload.pojoDate
+}
diff --git a/components/pom.xml b/components/pom.xml
index 10a50888a5d..06e4823274c 100644
--- a/components/pom.xml
+++ b/components/pom.xml
@@ -111,6 +111,7 @@
<module>camel-crypto</module>
<module>camel-csimple-joor</module>
<module>camel-csv</module>
+ <module>camel-datasonnet</module>
<module>camel-debug</module>
<module>camel-debezium</module>
<module>camel-digitalocean</module>
diff --git a/parent/pom.xml b/parent/pom.xml
index 13d65b00cd7..57aa10eecf2 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -132,8 +132,8 @@
<cxf-codegen-plugin-version>4.0.0</cxf-codegen-plugin-version>
<!-- cxf-xjc is not released as often -->
<cxf-xjc-plugin-version>4.0.0</cxf-xjc-plugin-version>
- <cxf-xjc-utils-version>4.0.0</cxf-xjc-utils-version>
- <datasonnet-mapper-version>2.2.0</datasonnet-mapper-version>
+ <cxf-xjc-utils-version>4.0.0</cxf-xjc-utils-version>
+ <datasonnet-mapper-version>2.5-jakarta4</datasonnet-mapper-version>
<deltaspike-version>1.9.5</deltaspike-version>
<depends-maven-plugin-version>1.4.0</depends-maven-plugin-version>
<derby-version>10.14.2.0</derby-version>