You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2020/12/05 10:05:18 UTC
[camel] branch master updated: [CAMEL-15804] - DataSonnet
Expression Language Support (#4561)
This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/master by this push:
new 892e225 [CAMEL-15804] - DataSonnet Expression Language Support (#4561)
892e225 is described below
commit 892e2259cc90ae8ffded74a7b7bc02b34f2ba9dc
Author: Jose Montoya <ja...@users.noreply.github.com>
AuthorDate: Sat Dec 5 04:04:49 2020 -0600
[CAMEL-15804] - DataSonnet Expression Language Support (#4561)
* datasonnet: adds language
* sets up cml library
* upgrade datasonnet mapper
* formatting
* refactors valuebuilder optional params
* renames mimetype to mediatype
also moves datasonnet builder to camel-datasonnet
* Revert delegating ValueBuilder to camel-datasonnet
This partially reverts commit d2202f7ee78daccacec2cec3f85925f4e8b8411e.
* refactor headers and exchange properties to functions
* defaults to output Document unless result specified
* bump datasonnet-mapper ver
* bump ds mapper version
* add dep
* fix scala directory
* rebase 3.7.0-SNAPSHOT
* fix cml library
* adds docs
* bump last ms3 ds version
* Refactor reifier and language
* adds ASF license where possible
* uses parent/pom for dep versions
* adds licenses in core-model
* addresses checkstyle
* align scala deps
* uses parent/pom for dep versions
* maven central mapper version
* supportlevel preview
* remvoe todos
* Removed System print line in test
* Replaced Spring DSL with XML DSL
* Removed ms3inc repository from camel-datasonnet pom
* clarify use of cml.properties in docs
* fixes issues with mapper version
* remove maven compiler properties
* fix language test
* moves classpath scanning to language
* converts CML.scala to java
* reverts scala deps changes
* address checkstyle errors
* fixed scala dep issue
* move datasonnet expression builder
* remove comment
* bump ds mapper version
Co-authored-by: Jake <jh...@ms3-inc.com>
---
bom/camel-bom/pom.xml | 5 +
camel-dependencies/pom.xml | 2 +
components/camel-datasonnet/pom.xml | 98 ++++++++
.../src/main/docs/datasonnet-language.adoc | 193 +++++++++++++++
.../org/apache/camel/language/datasonnet/CML.java | 115 +++++++++
.../camel/language/datasonnet/Datasonnet.java | 37 +++
.../language/datasonnet/DatasonnetConstants.java | 25 ++
.../language/datasonnet/DatasonnetExpression.java | 274 +++++++++++++++++++++
.../language/datasonnet/DatasonnetLanguage.java | 171 +++++++++++++
.../language/datasonnet/CamelDatasonnetTest.java | 164 ++++++++++++
.../language/datasonnet/ExpressionsInJavaTest.java | 118 +++++++++
.../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 | 172 +++++++++++++
.../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 +
core/camel-allcomponents/pom.xml | 4 +
.../org/apache/camel/builder/BuilderSupport.java | 35 +++
.../org/apache/camel/builder/ExpressionClause.java | 10 +
.../camel/builder/ExpressionClauseSupport.java | 17 +-
.../camel/model/language/DatasonnetExpression.java | 111 +++++++++
.../language/DatasonnetExpressionReifier.java | 62 +++++
.../camel/reifier/language/ExpressionReifier.java | 3 +
parent/pom.xml | 8 +
42 files changed, 2168 insertions(+), 3 deletions(-)
diff --git a/bom/camel-bom/pom.xml b/bom/camel-bom/pom.xml
index b330711b..253aa81 100644
--- a/bom/camel-bom/pom.xml
+++ b/bom/camel-bom/pom.xml
@@ -624,6 +624,11 @@
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
+ <artifactId>camel-datasonnet</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
<artifactId>camel-debezium-common</artifactId>
<version>${project.version}</version>
</dependency>
diff --git a/camel-dependencies/pom.xml b/camel-dependencies/pom.xml
index 8483389..b10538b 100644
--- a/camel-dependencies/pom.xml
+++ b/camel-dependencies/pom.xml
@@ -161,6 +161,7 @@
<cxf.codegen.jvmArgs></cxf.codegen.jvmArgs>
<cxf.codegenplugin.forkmode>once</cxf.codegenplugin.forkmode>
<cxf.xjc.jvmArgs></cxf.xjc.jvmArgs>
+ <datasonnet-mapper-version>2.1.1</datasonnet-mapper-version>
<debezium-mysql-connector-version>8.0.22</debezium-mysql-connector-version>
<debezium-version>1.3.1.Final</debezium-version>
<deltaspike-version>1.9.4</deltaspike-version>
@@ -492,6 +493,7 @@
<rxjava2-version>2.2.20</rxjava2-version>
<saxon-version>9.9.1-7</saxon-version>
<scala-version>2.11.7</scala-version>
+ <scala-datasonnet-version>2.13.3</scala-datasonnet-version>
<scribe-version>1.3.7</scribe-version>
<servicemix-specs-version>2.9.0</servicemix-specs-version>
<servlet-version-range>[3,4)</servlet-version-range>
diff --git a/components/camel-datasonnet/pom.xml b/components/camel-datasonnet/pom.xml
new file mode 100644
index 0000000..9187570
--- /dev/null
+++ b/components/camel-datasonnet/pom.xml
@@ -0,0 +1,98 @@
+<?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>3.7.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>
+ <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>
+ </dependencies>
+
+</project>
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 0000000..5f2ea2d
--- /dev/null
+++ b/components/camel-datasonnet/src/main/docs/datasonnet-language.adoc
@@ -0,0 +1,193 @@
+[[datasonnet-language]]
+= DataSonnet Language
+:docTitle: DataSonnet
+:artifactId: camel-datasonnet
+:description: To use DataSonnet scripts in Camel expressions or predicates.
+:since: 3.7
+:supportLevel: Preview
+include::{cq-version}@camel-quarkus:ROOT:partial$reference/languages/datasonnet.adoc[opts=optional]
+
+*Since Camel {since}*
+
+Camel supports https://datasonnet.com/[DataSonnet] transformations to allow an Expression or Predicate to be used in the Java DSL or xref:manual::xml-configuration.adoc[XML
+Configuration].
+
+To use a DataSonnet expression use the following Java code:
+[source,java]
+---------------------------------------
+... datasonnet("someDSExpression") ...
+---------------------------------------
+
+== 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)
+ .bodyMediaType("application/xml").outputMediaType("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 convenience bodyMediaType method was used, 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 convenience outputMediaType method was used, 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>
+---------------------------------------
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 0000000..0716ff4
--- /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.spi.DataFormatService;
+import com.datasonnet.spi.Library;
+import com.datasonnet.spi.PluginException;
+import org.apache.camel.Exchange;
+import sjsonnet.Materializer;
+import sjsonnet.Val;
+
+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 0000000..6e5de5f
--- /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 0000000..5ee7a7c
--- /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 0000000..1c1db0b
--- /dev/null
+++ b/components/camel-datasonnet/src/main/java/org/apache/camel/language/datasonnet/DatasonnetExpression.java
@@ -0,0 +1,274 @@
+/*
+ * 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.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.Objects;
+
+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 org.apache.camel.Exchange;
+import org.apache.camel.Expression;
+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 LOGGER = LoggerFactory.getLogger(DatasonnetExpression.class);
+
+ private String expression;
+ private Expression metaExpression;
+ private MediaType bodyMediaType;
+ private MediaType outputMediaType;
+ private Class<?> resultType;
+ private Collection<String> libraryPaths;
+
+ public DatasonnetExpression(String expression) {
+ this.expression = expression;
+ }
+
+ public DatasonnetExpression(Expression expression) {
+ this.metaExpression = expression;
+ }
+
+ public static DatasonnetExpression builder(String expression) {
+ DatasonnetExpression answer = new DatasonnetExpression(expression);
+ return answer;
+ }
+
+ public static DatasonnetExpression builder(Expression 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;
+ }
+
+ public static DatasonnetExpression builder(Expression 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 {
+ if (metaExpression != null) {
+ expression = metaExpression.evaluate(exchange, String.class);
+ }
+
+ Objects.requireNonNull(expression, "String expression property must be set!");
+
+ 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) {
+ if (bodyMediaType == null) {
+ //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) {
+ bodyMediaType = MediaType.valueOf(typeHeader);
+ }
+ }
+
+ Document<?> body;
+ if (exchange.getMessage().getBody() instanceof Document) {
+ body = (Document<?>) exchange.getMessage().getBody();
+ } else if (MediaTypes.APPLICATION_JAVA.equalsTypeAndSubtype(bodyMediaType)) {
+ body = new DefaultDocument<>(exchange.getMessage().getBody());
+ } else {
+ body = new DefaultDocument<>(MessageHelper.extractBodyAsString(exchange.getMessage()), bodyMediaType);
+ }
+
+ Map<String, Document<?>> inputs = Collections.singletonMap("body", body);
+
+ DatasonnetLanguage language = (DatasonnetLanguage) exchange.getContext().resolveLanguage("datasonnet");
+ Mapper mapper = language.computeIfMiss(expression, () -> new MapperBuilder(expression)
+ .withInputNames(inputs.keySet())
+ .withImports(resolveImports(language))
+ .withLibrary(CML.getInstance())
+ .withDefaultOutput(MediaTypes.APPLICATION_JAVA)
+ .build());
+
+ // pass exchange to CML lib using thread as context
+ CML.getInstance().getExchange().set(exchange);
+
+ if (outputMediaType == 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) {
+ outputMediaType = MediaType.valueOf(typeHeader);
+ } else {
+ outputMediaType = MediaTypes.ANY;
+ }
+ }
+
+ if (resultType == null || resultType.equals(Document.class)) {
+ return mapper.transform(body, inputs, outputMediaType, Object.class);
+ } else {
+ return mapper.transform(body, inputs, outputMediaType, resultType);
+ }
+ }
+
+ private Map<String, String> resolveImports(DatasonnetLanguage language) {
+ if (libraryPaths == null) {
+ return language.getClasspathImports();
+ }
+
+ Map<String, String> answer = new HashMap<>();
+ LOGGER.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());
+ Path relative = nextLibDir.toPath().relativize(file);
+ LOGGER.debug("Loading DataSonnet library: " + relative);
+ answer.put(relative.toString(), content);
+ }
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ } catch (IOException e) {
+ LOGGER.error("Unable to load libraries from " + nextPath, e);
+ }
+ }
+ }
+
+ return answer;
+ }
+
+ // 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 0000000..c7053f3
--- /dev/null
+++ b/components/camel-datasonnet/src/main/java/org/apache/camel/language/datasonnet/DatasonnetLanguage.java
@@ -0,0 +1,171 @@
+/*
+ * 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.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.LanguageSupport;
+import org.apache.camel.support.component.PropertyConfigurerSupport;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Language("datasonnet")
+public class DatasonnetLanguage extends LanguageSupport implements PropertyConfigurer {
+ private static final Logger LOGGER = LoggerFactory.getLogger(DatasonnetLanguage.class);
+
+ private static final Map<String, String> CLASSPATH_IMPORTS = new HashMap<>();
+
+ static {
+ LOGGER.debug("One time classpath search...");
+ try (ScanResult scanResult = new ClassGraph().whitelistPaths("/").scan()) {
+ scanResult.getResourcesWithExtension("libsonnet")
+ .forEachByteArray((resource, bytes) -> {
+ LOGGER.debug("Loading DataSonnet library: " + resource.getPath());
+ CLASSPATH_IMPORTS.put(resource.getPath(), new String(bytes, StandardCharsets.UTF_8));
+ });
+ }
+ }
+
+ // 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 Class<?> resultType;
+ 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, resultType));
+
+ 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;
+ }
+
+ public void setResultType(Class<?> targetType) {
+ this.resultType = targetType;
+ }
+}
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 0000000..a4f2938
--- /dev/null
+++ b/components/camel-datasonnet/src/test/java/org/apache/camel/language/datasonnet/CamelDatasonnetTest.java
@@ -0,0 +1,164 @@
+/*
+ * 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.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 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");
+ theGizmo.setDate(df.parse("2020-01-06"));
+
+ runCamelTest(theGizmo,
+ loadResourceAsString("javaTest.json"),
+ "direct:readJava");
+ }
+
+ @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);
+ }
+}
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 0000000..6d843a3
--- /dev/null
+++ b/components/camel-datasonnet/src/test/java/org/apache/camel/language/datasonnet/ExpressionsInJavaTest.java
@@ -0,0 +1,118 @@
+/*
+ * 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 org.skyscreamer.jsonassert.JSONAssert;
+
+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:chainExpressions")
+ protected ProducerTemplate chainExpressionsProducer;
+
+ @Produce("direct:fluentBuilder")
+ protected ProducerTemplate fluentBuilderProducer;
+
+ @Override
+ protected RouteBuilder createRouteBuilder() throws Exception {
+ return new RouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+ from("direct:chainExpressions")
+ .setHeader("ScriptHeader", constant("{ hello: \"World\"}"))
+ .setBody(datasonnet(simple("${header.ScriptHeader}", String.class)))
+ .to("mock:direct:response");
+
+ 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() throws Exception {
+ 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 testChainExpressions() throws Exception {
+ endEndpoint.expectedMessageCount(1);
+ chainExpressionsProducer.sendBody("{}");
+ Exchange exchange = endEndpoint.assertExchangeReceived(endEndpoint.getReceivedCounter() - 1);
+ String response = exchange.getIn().getBody().toString();
+ JSONAssert.assertEquals("{\"hello\":\"World\"}", response, true);
+ }
+
+ @Test
+ public void testFluentBuilder() throws Exception {
+ 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 0000000..743320e
--- /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 0000000..c903041
--- /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 0000000..be61462
--- /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() throws Exception {
+ return new RouteBuilder() {
+ public void configure() throws Exception {
+ 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 0000000..64c90c8
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 0000000..2dffc04
--- /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 0000000..d6fdda1
--- /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 0000000..8f2e7bb
--- /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 0000000..dbf0a4f
--- /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 0000000..be16794
--- /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 0000000..56fcae1
--- /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 0000000..0a9cac1
--- /dev/null
+++ b/components/camel-datasonnet/src/test/resources/org/apache/camel/language.datasonnet/camel-context.xml
@@ -0,0 +1,172 @@
+<?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"
+ 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
+">
+
+ <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="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>
+
+ </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 0000000..75676b4
--- /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 0000000..f65ee14
--- /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 0000000..4d908e3
--- /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 0000000..9150608
--- /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 0000000..3b4a4de
--- /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 0000000..0e4ca56
--- /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 0000000..a1faf25
--- /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 0000000..1ab6bf4
--- /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 0000000..e9489bd
--- /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 0000000..38878b3
--- /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 0000000..d1e98b7
--- /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 29770f7..b77caf8 100644
--- a/components/pom.xml
+++ b/components/pom.xml
@@ -175,6 +175,7 @@
<module>camel-crypto</module>
<module>camel-csimple-joor</module>
<module>camel-csv</module>
+ <module>camel-datasonnet</module>
<module>camel-debezium-common</module>
<module>camel-debezium-mongodb</module>
<module>camel-debezium-mysql</module>
diff --git a/core/camel-allcomponents/pom.xml b/core/camel-allcomponents/pom.xml
index 62183f5..dba0b8e 100644
--- a/core/camel-allcomponents/pom.xml
+++ b/core/camel-allcomponents/pom.xml
@@ -400,6 +400,10 @@
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
+ <artifactId>camel-datasonnet</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
<artifactId>camel-debezium-mongodb</artifactId>
</dependency>
<dependency>
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/builder/BuilderSupport.java b/core/camel-core-model/src/main/java/org/apache/camel/builder/BuilderSupport.java
index ec041bb..fd25daa 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/builder/BuilderSupport.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/builder/BuilderSupport.java
@@ -26,6 +26,7 @@ import org.apache.camel.Expression;
import org.apache.camel.NoSuchEndpointException;
import org.apache.camel.RuntimeCamelException;
import org.apache.camel.model.language.CSimpleExpression;
+import org.apache.camel.model.language.DatasonnetExpression;
import org.apache.camel.model.language.ExchangePropertyExpression;
import org.apache.camel.model.language.HeaderExpression;
import org.apache.camel.model.language.JoorExpression;
@@ -150,6 +151,40 @@ public abstract class BuilderSupport {
}
/**
+ * Returns a Datasonnet expression value builder
+ */
+ public ValueBuilder datasonnet(String value) {
+ DatasonnetExpression exp = new DatasonnetExpression(value);
+ return new ValueBuilder(exp);
+ }
+
+ /**
+ * Returns a Datasonnet expression value builder
+ */
+ public ValueBuilder datasonnet(Expression value) {
+ DatasonnetExpression exp = new DatasonnetExpression(value);
+ return new ValueBuilder(exp);
+ }
+
+ /**
+ * Returns a Datasonnet expression value builder
+ */
+ public ValueBuilder datasonnet(String value, Class<?> resultType) {
+ DatasonnetExpression exp = new DatasonnetExpression(value);
+ exp.setResultType(resultType);
+ return new ValueBuilder(exp);
+ }
+
+ /**
+ * Returns a Datasonnet expression value builder
+ */
+ public ValueBuilder datasonnet(Expression value, Class<?> resultType) {
+ DatasonnetExpression exp = new DatasonnetExpression(value);
+ exp.setResultType(resultType);
+ return new ValueBuilder(exp);
+ }
+
+ /**
* Returns a simple expression value builder
*/
public SimpleBuilder simple(String value) {
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/builder/ExpressionClause.java b/core/camel-core-model/src/main/java/org/apache/camel/builder/ExpressionClause.java
index 0dacb5e..b8bdbc5 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/builder/ExpressionClause.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/builder/ExpressionClause.java
@@ -344,6 +344,16 @@ public class ExpressionClause<T> implements Expression, Predicate {
}
/**
+ * Evaluates a <a href="http://camel.apache.org/datasonnet.html">Datasonnet expression</a>
+ *
+ * @param text the expression to be evaluated
+ * @return the builder to continue processing the DSL
+ */
+ public T datasonnet(String text) {
+ return delegate.datasonnet(text);
+ }
+
+ /**
* Evaluates a <a href="http://camel.apache.org/jsonpath.html">Json Path expression</a>
*
* @param text the expression to be evaluated
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/builder/ExpressionClauseSupport.java b/core/camel-core-model/src/main/java/org/apache/camel/builder/ExpressionClauseSupport.java
index b496134..2c84ab2 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/builder/ExpressionClauseSupport.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/builder/ExpressionClauseSupport.java
@@ -24,6 +24,7 @@ import org.apache.camel.Expression;
import org.apache.camel.ExpressionFactory;
import org.apache.camel.model.language.CSimpleExpression;
import org.apache.camel.model.language.ConstantExpression;
+import org.apache.camel.model.language.DatasonnetExpression;
import org.apache.camel.model.language.ExchangePropertyExpression;
import org.apache.camel.model.language.GroovyExpression;
import org.apache.camel.model.language.HeaderExpression;
@@ -326,6 +327,16 @@ public class ExpressionClauseSupport<T> implements ExpressionFactoryAware {
}
/**
+ * Evaluates a <a href="http://camel.apache.org/datasonnet.html">Datasonnet expression</a>
+ *
+ * @param text the expression to be evaluated
+ * @return the builder to continue processing the DSL
+ */
+ public T datasonnet(String text) {
+ return expression(new DatasonnetExpression(text));
+ }
+
+ /**
* Evaluates a <a href="http://camel.apache.org/jsonpath.html">Json Path expression</a>
*
* @param text the expression to be evaluated
@@ -803,7 +814,7 @@ public class ExpressionClauseSupport<T> implements ExpressionFactoryAware {
/**
* Evaluates an XML token expression on the message body with XML content
- *
+ *
* @param path the xpath like path notation specifying the child nodes to tokenize
* @param mode one of 'i', 'w', or 'u' to inject the namespaces to the token, to wrap the token with its
* ancestor contet, or to unwrap to its element child
@@ -836,7 +847,7 @@ public class ExpressionClauseSupport<T> implements ExpressionFactoryAware {
/**
* Evaluates an <a href="http://camel.apache.org/xpath.html">XPath expression</a> on the supplied header name's
* contents
- *
+ *
* @param text the expression to be evaluated
* @param headerName the name of the header to apply the expression to
* @return the builder to continue processing the DSL
@@ -965,7 +976,7 @@ public class ExpressionClauseSupport<T> implements ExpressionFactoryAware {
/**
* Evaluates an <a href="http://camel.apache.org/xquery.html">XQuery expression</a>
- *
+ *
* @param text the expression to be evaluated
* @param headerName the name of the header to apply the expression to
* @return the builder to continue processing the DSL
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/language/DatasonnetExpression.java b/core/camel-core-model/src/main/java/org/apache/camel/model/language/DatasonnetExpression.java
new file mode 100644
index 0000000..a08d768
--- /dev/null
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/language/DatasonnetExpression.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.model.language;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlTransient;
+
+import org.apache.camel.Expression;
+import org.apache.camel.spi.Metadata;
+
+/**
+ * To use DataSonnet scripts in Camel expressions or predicates.
+ */
+@Metadata(firstVersion = "3.7.0", label = "language,script", title = "DataSonnet")
+@XmlRootElement(name = "datasonnet")
+@XmlAccessorType(XmlAccessType.FIELD)
+public class DatasonnetExpression extends ExpressionDefinition {
+
+ @XmlAttribute(name = "bodyMediaType")
+ private String bodyMediaType;
+
+ @XmlAttribute(name = "outputMediaType")
+ private String outputMediaType;
+
+ @XmlAttribute(name = "resultType")
+ private String resultTypeName;
+
+ @XmlTransient
+ private Class<?> resultType;
+
+ public DatasonnetExpression() {
+ }
+
+ public DatasonnetExpression(String expression) {
+ super(expression);
+ }
+
+ public DatasonnetExpression(Expression expression) {
+ super(expression);
+ }
+
+ @Override
+ public String getLanguage() {
+ return "datasonnet";
+ }
+
+ public String getBodyMediaType() {
+ return bodyMediaType;
+ }
+
+ /**
+ * The String representation of the message's body MediaType
+ */
+ public void setBodyMediaType(String bodyMediaType) {
+ this.bodyMediaType = bodyMediaType;
+ }
+
+ public String getOutputMediaType() {
+ return outputMediaType;
+ }
+
+ /**
+ * The String representation of the MediaType to output
+ */
+ public void setOutputMediaType(String outputMediaType) {
+ this.outputMediaType = outputMediaType;
+ }
+
+ public Class<?> getResultType() {
+ return 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<?> resultType) {
+ this.resultType = resultType;
+ }
+
+ public String getResultTypeName() {
+ return resultTypeName;
+ }
+
+ /**
+ * Sets the class name of the result type (type from output)
+ * <p/>
+ * The default result type is com.datasonnet.document.Document
+ */
+ public void setResultTypeName(String resultTypeName) {
+ this.resultTypeName = resultTypeName;
+ }
+}
diff --git a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/language/DatasonnetExpressionReifier.java b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/language/DatasonnetExpressionReifier.java
new file mode 100644
index 0000000..904ff16
--- /dev/null
+++ b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/language/DatasonnetExpressionReifier.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.reifier.language;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Expression;
+import org.apache.camel.Predicate;
+import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.model.language.DatasonnetExpression;
+import org.apache.camel.model.language.ExpressionDefinition;
+import org.apache.camel.spi.Language;
+
+public class DatasonnetExpressionReifier extends ExpressionReifier<DatasonnetExpression> {
+
+ public DatasonnetExpressionReifier(CamelContext camelContext, ExpressionDefinition definition) {
+ super(camelContext, (DatasonnetExpression) definition);
+ }
+
+ @Override
+ protected void configureLanguage(Language language) {
+ if (definition.getResultType() == null && definition.getResultTypeName() != null) {
+ try {
+ Class<?> clazz = camelContext.getClassResolver().resolveMandatoryClass(definition.getResultTypeName());
+ definition.setResultType(clazz);
+ } catch (ClassNotFoundException e) {
+ throw RuntimeCamelException.wrapRuntimeException(e);
+ }
+ }
+ }
+
+ @Override
+ protected Expression createExpression(Language language, String exp) {
+ return language.createExpression(exp, createProperties());
+ }
+
+ @Override
+ protected Predicate createPredicate(Language language, String exp) {
+ return language.createPredicate(exp, createProperties());
+ }
+
+ private Object[] createProperties() {
+ Object[] properties = new Object[3];
+ properties[0] = definition.getResultType();
+ properties[1] = parseString(definition.getBodyMediaType());
+ properties[2] = parseString(definition.getOutputMediaType());
+ return properties;
+ }
+}
diff --git a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/language/ExpressionReifier.java b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/language/ExpressionReifier.java
index 5d1c918..dd775a1 100644
--- a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/language/ExpressionReifier.java
+++ b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/language/ExpressionReifier.java
@@ -29,6 +29,7 @@ import org.apache.camel.Predicate;
import org.apache.camel.model.ExpressionSubElementDefinition;
import org.apache.camel.model.language.CSimpleExpression;
import org.apache.camel.model.language.ConstantExpression;
+import org.apache.camel.model.language.DatasonnetExpression;
import org.apache.camel.model.language.ExchangePropertyExpression;
import org.apache.camel.model.language.ExpressionDefinition;
import org.apache.camel.model.language.GroovyExpression;
@@ -111,6 +112,8 @@ public class ExpressionReifier<T extends ExpressionDefinition> extends AbstractR
return new ExpressionReifier<>(camelContext, definition);
} else if (definition instanceof CSimpleExpression) {
return new CSimpleExpressionReifier(camelContext, definition);
+ } else if (definition instanceof DatasonnetExpression) {
+ return new DatasonnetExpressionReifier(camelContext, definition);
} else if (definition instanceof ExchangePropertyExpression) {
return new ExpressionReifier<>(camelContext, definition);
} else if (definition instanceof GroovyExpression) {
diff --git a/parent/pom.xml b/parent/pom.xml
index 8cc4d48..ce287b6 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -105,6 +105,7 @@
<cglib-version>3.2.12</cglib-version>
<chunk-templates-version>3.5.0</chunk-templates-version>
<citrus-version>2.8.0</citrus-version>
+ <classgraph-version>4.8.52</classgraph-version>
<cmis-version>1.1.0</cmis-version>
<cometd-java-client-version>4.0.4</cometd-java-client-version>
<cometd-java-server>4.0.4</cometd-java-server>
@@ -142,6 +143,7 @@
<!-- cxf-xjc is not released as often -->
<cxf-xjc-plugin-version>3.3.1</cxf-xjc-plugin-version>
<cxf-xjc-utils-version>3.3.1</cxf-xjc-utils-version>
+ <datasonnet-mapper-version>2.1.1</datasonnet-mapper-version>
<deltaspike-version>1.9.4</deltaspike-version>
<depends-maven-plugin-version>1.4.0</depends-maven-plugin-version>
<derby-version>10.14.2.0</derby-version>
@@ -480,6 +482,7 @@
<rxjava2-version>2.2.20</rxjava2-version>
<saxon-version>9.9.1-7</saxon-version>
<scala-version>2.11.7</scala-version>
+ <scala-datasonnet-version>2.13.3</scala-datasonnet-version>
<scribe-version>1.3.7</scribe-version>
<servicemix-specs-version>2.9.0</servicemix-specs-version>
<servlet-version-range>[3,4)</servlet-version-range>
@@ -1242,6 +1245,11 @@
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
+ <artifactId>camel-datasonnet</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
<artifactId>camel-debezium-common</artifactId>
<version>${project.version}</version>
</dependency>