This is an automated email from the ASF dual-hosted git repository.

lburgazzoli 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 a481596  dsl: add support for JavaScript
a481596 is described below

commit a481596b49badb50d96cc064cc0cb1d73df4abd2
Author: Luca Burgazzoli <lburgazz...@gmail.com>
AuthorDate: Tue Mar 16 10:47:17 2021 +0100

    dsl: add support for JavaScript
---
 bom/camel-bom/pom.xml                              |   5 +
 camel-dependencies/pom.xml                         |   1 +
 .../main/java/org/apache/camel/spi/Resource.java   |  26 +++-
 .../org/apache/camel/builder/RouteBuilder.java     |  91 ++++++++---
 .../builder/endpoint/EndpointRouteBuilder.java     |  45 +++++-
 dsl/camel-js-dsl/pom.xml                           | 173 +++++++++++++++++++++
 .../services/org/apache/camel/routes-loader/js     |   2 +
 dsl/camel-js-dsl/src/main/docs/js-dsl.adoc         |  13 ++
 .../org/apache/camel/dsl/js/JavaScriptDSL.java     |  99 ++++++++++++
 .../dsl/js/JavaScriptRoutesBuilderLoader.java      | 115 ++++++++++++++
 .../dsl/js/JavaScriptRoutesBuilderLoaderTest.java  | 125 +++++++++++++++
 .../src/test/resources/log4j2-test.properties      |  31 ++++
 .../routes/routes-with-component-configuration.js  |   5 +
 .../routes/routes-with-context-configuration.js    |  18 +++
 .../resources/routes/routes-with-endpoint-dsl.js   |  22 +++
 .../resources/routes/routes-with-processors.js     |  15 ++
 .../routes/routes-with-rest-configuration.js       |   4 +
 .../test/resources/routes/routes-with-rest-dsl.js  |   7 +
 .../src/test/resources/routes/routes.js            |  18 +++
 dsl/pom.xml                                        |   1 +
 parent/pom.xml                                     |   6 +
 21 files changed, 796 insertions(+), 26 deletions(-)

diff --git a/bom/camel-bom/pom.xml b/bom/camel-bom/pom.xml
index a46f203..914c38d 100644
--- a/bom/camel-bom/pom.xml
+++ b/bom/camel-bom/pom.xml
@@ -1114,6 +1114,11 @@
       </dependency>
       <dependency>
         <groupId>org.apache.camel</groupId>
+        <artifactId>camel-js-dsl</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.camel</groupId>
         <artifactId>camel-jt400</artifactId>
         <version>${project.version}</version>
       </dependency>
diff --git a/camel-dependencies/pom.xml b/camel-dependencies/pom.xml
index 52ccc11..c2025a8 100644
--- a/camel-dependencies/pom.xml
+++ b/camel-dependencies/pom.xml
@@ -241,6 +241,7 @@
     <google-guava-version>19.0</google-guava-version>
     <google-mail-guava-version>17.0</google-mail-guava-version>
     <google-maps-services-version>0.10.1</google-maps-services-version>
+    <graaljs-version>21.0.0.2</graaljs-version>
     <graphql-java-version>14.0</graphql-java-version>
     <grizzly-websockets-version>2.3.25</grizzly-websockets-version>
     <grpc-google-auth-library-version>0.19.0</grpc-google-auth-library-version>
diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/Resource.java 
b/core/camel-api/src/main/java/org/apache/camel/spi/Resource.java
index fb336c7..e56f6af 100644
--- a/core/camel-api/src/main/java/org/apache/camel/spi/Resource.java
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/Resource.java
@@ -18,9 +18,13 @@ package org.apache.camel.spi;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
 import java.net.MalformedURLException;
 import java.net.URI;
 import java.net.URL;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
 
 /**
  * Describe a resource, such as a file or class path resource.
@@ -56,9 +60,29 @@ public interface Resource {
     }
 
     /**
-     * Returns an input stream that reads from the underlying resource.
+     * Returns an {@link InputStream} that reads from the underlying resource.
      * </p>
      * Each invocation must return a new {@link InputStream} instance.
      */
     InputStream getInputStream() throws IOException;
+
+    /**
+     * Returns a {@link Reader} that reads from the underlying resource using 
UTF-8 as charset.
+     * </p>
+     * Each invocation must return a new {@link Reader}, @see #getInputStream()
+     */
+    default Reader getReader() throws Exception {
+        return getReader(StandardCharsets.UTF_8);
+    }
+
+    /**
+     * Returns a {@link Reader} that reads from the underlying resource using 
the given {@link Charset}
+     * </p>
+     * Each invocation must return a new {@link Reader}, @see #getInputStream()
+     *
+     * @see #getInputStream()
+     */
+    default Reader getReader(Charset charset) throws Exception {
+        return new InputStreamReader(getInputStream(), charset);
+    }
 }
diff --git 
a/core/camel-core-model/src/main/java/org/apache/camel/builder/RouteBuilder.java
 
b/core/camel-core-model/src/main/java/org/apache/camel/builder/RouteBuilder.java
index 355fe43..9f3d919 100644
--- 
a/core/camel-core-model/src/main/java/org/apache/camel/builder/RouteBuilder.java
+++ 
b/core/camel-core-model/src/main/java/org/apache/camel/builder/RouteBuilder.java
@@ -16,12 +16,14 @@
  */
 package org.apache.camel.builder;
 
+import java.io.Reader;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Optional;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.apache.camel.CamelContext;
+import org.apache.camel.CamelContextAware;
 import org.apache.camel.Endpoint;
 import org.apache.camel.ExtendedCamelContext;
 import org.apache.camel.Ordered;
@@ -43,10 +45,13 @@ import org.apache.camel.model.rest.RestDefinition;
 import org.apache.camel.model.rest.RestsDefinition;
 import org.apache.camel.spi.OnCamelContextEvent;
 import org.apache.camel.spi.PropertiesComponent;
+import org.apache.camel.spi.Resource;
 import org.apache.camel.spi.RestConfiguration;
 import org.apache.camel.support.LifecycleStrategySupport;
 import org.apache.camel.util.ObjectHelper;
 import org.apache.camel.util.StringHelper;
+import org.apache.camel.util.function.ThrowingBiConsumer;
+import org.apache.camel.util.function.ThrowingConsumer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -56,14 +61,16 @@ import org.slf4j.LoggerFactory;
  */
 public abstract class RouteBuilder extends BuilderSupport implements 
RoutesBuilder, Ordered {
     protected Logger log = LoggerFactory.getLogger(getClass());
-    private AtomicBoolean initialized = new AtomicBoolean();
+
+    private final AtomicBoolean initialized = new AtomicBoolean();
+    private final List<RouteBuilderLifecycleStrategy> lifecycleInterceptors = 
new ArrayList<>();
+    private final List<TransformerBuilder> transformerBuilders = new 
ArrayList<>();
+    private final List<ValidatorBuilder> validatorBuilders = new ArrayList<>();
+
     private RestsDefinition restCollection = new RestsDefinition();
     private RestConfigurationDefinition restConfiguration;
-    private List<TransformerBuilder> transformerBuilders = new ArrayList<>();
-    private List<ValidatorBuilder> validatorBuilders = new ArrayList<>();
     private RoutesDefinition routeCollection = new RoutesDefinition();
     private RouteTemplatesDefinition routeTemplateCollection = new 
RouteTemplatesDefinition();
-    private final List<RouteBuilderLifecycleStrategy> lifecycleInterceptors = 
new ArrayList<>();
 
     public RouteBuilder() {
         this(null);
@@ -74,19 +81,6 @@ public abstract class RouteBuilder extends BuilderSupport 
implements RoutesBuild
     }
 
     /**
-     * Override this method to define ordering of {@link RouteBuilder} classes 
that are added to Camel from various
-     * runtimes such as camel-main, camel-spring-boot. This allows end users 
to control the ordering if some routes must
-     * be added and started before others.
-     * <p/>
-     * Use low numbers for higher priority. Normally the sorting will start 
from 0 and move upwards. So if you want to
-     * be last then use {@link Integer#MAX_VALUE} or eg {@link #LOWEST}.
-     */
-    @Override
-    public int getOrder() {
-        return LOWEST;
-    }
-
-    /**
      * Add routes to a context using a lambda expression. It can be used as 
following:
      *
      * <pre>
@@ -107,6 +101,56 @@ public abstract class RouteBuilder extends BuilderSupport 
implements RoutesBuild
         });
     }
 
+    /**
+     * Loads {@link RoutesBuilder} from {@link Resource} using the given 
consumer to create a {@link RouteBuilder}
+     * instance.
+     *
+     * @param  resource the resource to be loaded.
+     * @param  consumer the function used to create a {@link RoutesBuilder}
+     * @return          a {@link RoutesBuilder}
+     */
+    public static RouteBuilder loadRoutesBuilder(
+            Resource resource, ThrowingBiConsumer<Reader, RouteBuilder, 
Exception> consumer) {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                CamelContextAware.trySetCamelContext(resource, getContext());
+
+                try (Reader reader = resource.getReader()) {
+                    consumer.accept(reader, this);
+                }
+            }
+        };
+    }
+
+    /**
+     * Loads {@link RoutesBuilder} using the given consumer to create a {@link 
RouteBuilder} instance.
+     *
+     * @param  consumer the function used to create a {@link RoutesBuilder}
+     * @return          a {@link RoutesBuilder}
+     */
+    public static RouteBuilder 
loadRoutesBuilder(ThrowingConsumer<RouteBuilder, Exception> consumer) {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                consumer.accept(this);
+            }
+        };
+    }
+
+    /**
+     * Override this method to define ordering of {@link RouteBuilder} classes 
that are added to Camel from various
+     * runtimes such as camel-main, camel-spring-boot. This allows end users 
to control the ordering if some routes must
+     * be added and started before others.
+     * <p/>
+     * Use low numbers for higher priority. Normally the sorting will start 
from 0 and move upwards. So if you want to
+     * be last then use {@link Integer#MAX_VALUE} or eg {@link #LOWEST}.
+     */
+    @Override
+    public int getOrder() {
+        return LOWEST;
+    }
+
     @Override
     public String toString() {
         return getRouteCollection().toString();
@@ -593,22 +637,22 @@ public abstract class RouteBuilder extends BuilderSupport 
implements RoutesBuild
         return restCollection;
     }
 
-    public RestConfigurationDefinition getRestConfiguration() {
-        return restConfiguration;
-    }
-
     public void setRestCollection(RestsDefinition restCollection) {
         this.restCollection = restCollection;
     }
 
-    public void setRouteCollection(RoutesDefinition routeCollection) {
-        this.routeCollection = routeCollection;
+    public RestConfigurationDefinition getRestConfiguration() {
+        return restConfiguration;
     }
 
     public RoutesDefinition getRouteCollection() {
         return this.routeCollection;
     }
 
+    public void setRouteCollection(RoutesDefinition routeCollection) {
+        this.routeCollection = routeCollection;
+    }
+
     public RouteTemplatesDefinition getRouteTemplateCollection() {
         return routeTemplateCollection;
     }
@@ -628,5 +672,4 @@ public abstract class RouteBuilder extends BuilderSupport 
implements RoutesBuild
     protected void configureRouteTemplate(RouteTemplateDefinition 
routeTemplate) {
         // noop
     }
-
 }
diff --git 
a/core/camel-endpointdsl/src/main/java/org/apache/camel/builder/endpoint/EndpointRouteBuilder.java
 
b/core/camel-endpointdsl/src/main/java/org/apache/camel/builder/endpoint/EndpointRouteBuilder.java
index b8f3434..f9e9190 100644
--- 
a/core/camel-endpointdsl/src/main/java/org/apache/camel/builder/endpoint/EndpointRouteBuilder.java
+++ 
b/core/camel-endpointdsl/src/main/java/org/apache/camel/builder/endpoint/EndpointRouteBuilder.java
@@ -16,8 +16,13 @@
  */
 package org.apache.camel.builder.endpoint;
 
+import java.io.Reader;
+
 import org.apache.camel.CamelContext;
+import org.apache.camel.CamelContextAware;
 import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.spi.Resource;
+import org.apache.camel.util.function.ThrowingBiConsumer;
 import org.apache.camel.util.function.ThrowingConsumer;
 
 /**
@@ -34,7 +39,7 @@ public abstract class EndpointRouteBuilder extends 
RouteBuilder implements Endpo
 
     /**
      * Add routes to a context using a lambda expression. It can be used as 
following:
-     * 
+     *
      * <pre>
      * RouteBuilder.addRoutes(context, rb -&gt;
      *     rb.from("direct:inbound").bean(ProduceTemplateBean.class)));
@@ -54,4 +59,42 @@ public abstract class EndpointRouteBuilder extends 
RouteBuilder implements Endpo
         });
     }
 
+    /**
+     * Loads {@link EndpointRouteBuilder} from {@link Resource} using the 
given consumer to create an
+     * {@link EndpointRouteBuilder} instance.
+     *
+     * @param  resource the resource to be loaded.
+     * @param  consumer the function used to create a {@link 
EndpointRouteBuilder}
+     * @return          a {@link EndpointRouteBuilder}
+     */
+    public static EndpointRouteBuilder loadEndpointRoutesBuilder(
+            Resource resource, ThrowingBiConsumer<Reader, 
EndpointRouteBuilder, Exception> consumer) {
+        return new EndpointRouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                CamelContextAware.trySetCamelContext(resource, getContext());
+
+                try (Reader reader = resource.getReader()) {
+                    consumer.accept(reader, this);
+                }
+            }
+        };
+    }
+
+    /**
+     * Loads {@link EndpointRouteBuilder} from {@link Resource} using the 
given consumer to create an
+     * {@link EndpointRouteBuilder} instance.
+     *
+     * @param  consumer the function used to create a {@link 
EndpointRouteBuilder}
+     * @return          a {@link EndpointRouteBuilder}
+     */
+    public static EndpointRouteBuilder 
loadEndpointRoutesBuilder(ThrowingConsumer<EndpointRouteBuilder, Exception> 
consumer) {
+        return new EndpointRouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                consumer.accept(this);
+            }
+        };
+    }
+
 }
diff --git a/dsl/camel-js-dsl/pom.xml b/dsl/camel-js-dsl/pom.xml
new file mode 100644
index 0000000..21cdded
--- /dev/null
+++ b/dsl/camel-js-dsl/pom.xml
@@ -0,0 +1,173 @@
+<?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/maven-v4_0_0.xsd";>
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.camel</groupId>
+        <artifactId>dsl</artifactId>
+        <version>3.9.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>camel-js-dsl</artifactId>
+    <packaging>jar</packaging>
+    <name>Camel :: JavaScript DSL</name>
+    <description>Camel DSL with JavaScript</description>
+
+    <properties>
+        <firstVersion>3.9.0</firstVersion>
+        <sourcecheckExcludes>
+            **/resources/**/My*.java
+        </sourcecheckExcludes>
+        <sourcecheckExcludesComma>
+            ${sourcecheckExcludes},
+        </sourcecheckExcludesComma>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-support</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-core-model</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-endpointdsl</artifactId>
+        </dependency>
+
+        <!-- requires Java 11 or 14 -->
+
+        <dependency>
+            <groupId>org.graalvm.js</groupId>
+            <artifactId>js</artifactId>
+            <version>${graaljs-version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-main</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-direct</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-rest</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-mock</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-core-languages</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-bean</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-log</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-telegram</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-seda</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.assertj</groupId>
+            <artifactId>assertj-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-test-junit5</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.logging.log4j</groupId>
+            <artifactId>log4j-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.logging.log4j</groupId>
+            <artifactId>log4j-slf4j-impl</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.logging.log4j</groupId>
+            <artifactId>log4j-jcl</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.camel</groupId>
+                <artifactId>camel-package-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>generate-spi</id>
+                        <goals>
+                            <goal>generate-spi</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+    <!-- skip testing on java 8 -->
+    <profiles>
+        <profile>
+            <id>jdk8</id>
+            <activation>
+                <jdk>[,8)</jdk>
+            </activation>
+            <properties>
+                <maven.test.skip.exec>true</maven.test.skip.exec>
+            </properties>
+        </profile>
+    </profiles>
+
+</project>
\ No newline at end of file
diff --git 
a/dsl/camel-js-dsl/src/generated/resources/META-INF/services/org/apache/camel/routes-loader/js
 
b/dsl/camel-js-dsl/src/generated/resources/META-INF/services/org/apache/camel/routes-loader/js
new file mode 100644
index 0000000..8ae3a3c
--- /dev/null
+++ 
b/dsl/camel-js-dsl/src/generated/resources/META-INF/services/org/apache/camel/routes-loader/js
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.dsl.js.JavaScriptRoutesBuilderLoader
diff --git a/dsl/camel-js-dsl/src/main/docs/js-dsl.adoc 
b/dsl/camel-js-dsl/src/main/docs/js-dsl.adoc
new file mode 100644
index 0000000..595ce63
--- /dev/null
+++ b/dsl/camel-js-dsl/src/main/docs/js-dsl.adoc
@@ -0,0 +1,13 @@
+[[js-dsl-other]]
+= JavaScript DSL
+:docTitle: JavaScript Dsl
+:artifactId: camel-js-dsl
+:description: Camel DSL with JavaScript
+:supportLevel: Experimental
+:since: 3.
+:supportLevel: Preview
+include::{cq-version}@camel-quarkus:ROOT:partial$reference/others/js-dsl.adoc[opts=optional]
+//Manually maintained attributes
+:group: DSL
+
+See https://camel.apache.org/manual/latest/dsl.html
\ No newline at end of file
diff --git 
a/dsl/camel-js-dsl/src/main/java/org/apache/camel/dsl/js/JavaScriptDSL.java 
b/dsl/camel-js-dsl/src/main/java/org/apache/camel/dsl/js/JavaScriptDSL.java
new file mode 100644
index 0000000..295d82b
--- /dev/null
+++ b/dsl/camel-js-dsl/src/main/java/org/apache/camel/dsl/js/JavaScriptDSL.java
@@ -0,0 +1,99 @@
+/*
+ * 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.dsl.js;
+
+import java.util.function.Consumer;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+import org.apache.camel.builder.BuilderSupport;
+import org.apache.camel.builder.EndpointConsumerBuilder;
+import org.apache.camel.builder.ErrorHandlerBuilder;
+import org.apache.camel.builder.endpoint.EndpointBuilderFactory;
+import org.apache.camel.builder.endpoint.EndpointRouteBuilder;
+import org.apache.camel.model.InterceptDefinition;
+import org.apache.camel.model.InterceptFromDefinition;
+import org.apache.camel.model.InterceptSendToEndpointDefinition;
+import org.apache.camel.model.OnCompletionDefinition;
+import org.apache.camel.model.OnExceptionDefinition;
+import org.apache.camel.model.RouteDefinition;
+import org.apache.camel.model.rest.RestConfigurationDefinition;
+import org.apache.camel.model.rest.RestDefinition;
+import org.apache.camel.spi.Registry;
+
+public class JavaScriptDSL extends BuilderSupport implements 
EndpointBuilderFactory {
+    public final Registry registry;
+    public final EndpointRouteBuilder builder;
+    public final CamelContext context;
+
+    public JavaScriptDSL(EndpointRouteBuilder builder) {
+        super(builder.getContext());
+
+        this.registry = builder.getContext().getRegistry();
+        this.builder = builder;
+        this.context = builder.getContext();
+    }
+
+    public RouteDefinition from(String endpoint) {
+        return builder.from(endpoint);
+    }
+
+    public RouteDefinition from(EndpointConsumerBuilder endpoint) {
+        return builder.from(endpoint);
+    }
+
+    public RestDefinition rest(String path) {
+        return builder.rest(path);
+    }
+
+    public RestConfigurationDefinition restConfiguration() {
+        return builder.restConfiguration();
+    }
+
+    public OnExceptionDefinition onException(Class<? extends Throwable> 
exception) {
+        return builder.onException(exception);
+    }
+
+    public OnCompletionDefinition onCompletion() {
+        return builder.onCompletion();
+    }
+
+    public InterceptDefinition intercept() {
+        return builder.intercept();
+    }
+
+    public InterceptFromDefinition interceptFrom() {
+        return builder.interceptFrom();
+    }
+
+    public InterceptFromDefinition interceptFrom(String uri) {
+        return builder.interceptFrom(uri);
+    }
+
+    public InterceptSendToEndpointDefinition interceptSendToEndpoint(String 
uri) {
+        return builder.interceptSendToEndpoint(uri);
+    }
+
+    public void errorHandler(ErrorHandlerBuilder handler) {
+        builder.errorHandler(handler);
+    }
+
+    public Processor processor(Consumer<Exchange> consumer) {
+        return consumer::accept;
+    }
+}
diff --git 
a/dsl/camel-js-dsl/src/main/java/org/apache/camel/dsl/js/JavaScriptRoutesBuilderLoader.java
 
b/dsl/camel-js-dsl/src/main/java/org/apache/camel/dsl/js/JavaScriptRoutesBuilderLoader.java
new file mode 100644
index 0000000..6d9a052
--- /dev/null
+++ 
b/dsl/camel-js-dsl/src/main/java/org/apache/camel/dsl/js/JavaScriptRoutesBuilderLoader.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.dsl.js;
+
+import java.io.Reader;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.ExtendedCamelContext;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.StartupStep;
+import org.apache.camel.api.management.ManagedAttribute;
+import org.apache.camel.api.management.ManagedResource;
+import org.apache.camel.builder.endpoint.EndpointRouteBuilder;
+import org.apache.camel.spi.Resource;
+import org.apache.camel.spi.StartupStepRecorder;
+import org.apache.camel.spi.annotations.RoutesLoader;
+import org.apache.camel.support.LifecycleStrategySupport;
+import org.apache.camel.support.RoutesBuilderLoaderSupport;
+import org.graalvm.polyglot.Context;
+import org.graalvm.polyglot.Value;
+
+import static org.graalvm.polyglot.Source.newBuilder;
+
+@ManagedResource(description = "Managed JavaScriptRoutesBuilderLoader")
+@RoutesLoader(JavaScriptRoutesBuilderLoader.EXTENSION)
+public class JavaScriptRoutesBuilderLoader extends RoutesBuilderLoaderSupport {
+    public static final String EXTENSION = "js";
+    public static final String LANGUAGE_ID = "js";
+
+    private StartupStepRecorder recorder;
+
+    @Override
+    protected void doBuild() throws Exception {
+        super.doBuild();
+
+        if (getCamelContext() != null) {
+            this.recorder = 
getCamelContext().adapt(ExtendedCamelContext.class).getStartupStepRecorder();
+        }
+    }
+
+    @ManagedAttribute(description = "Supported file extension")
+    @Override
+    public String getSupportedExtension() {
+        return EXTENSION;
+    }
+
+    @Override
+    public RoutesBuilder loadRoutesBuilder(Resource resource) throws Exception 
{
+        StartupStep step = recorder != null
+                ? recorder.beginStep(JavaScriptRoutesBuilderLoader.class, 
resource.getLocation(), "Compiling RouteBuilder")
+                : null;
+
+        try {
+            return EndpointRouteBuilder.loadEndpointRoutesBuilder(resource, 
this::load);
+        } finally {
+            if (recorder != null) {
+                recorder.endStep(step);
+            }
+        }
+    }
+
+    private void load(Reader reader, EndpointRouteBuilder builder) {
+        final Context context = 
Context.newBuilder(LANGUAGE_ID).allowAllAccess(true).build();
+        final Value bindings = context.getBindings(LANGUAGE_ID);
+
+        // configure bindings
+        bindings.putMember("__dsl", new JavaScriptDSL(builder));
+
+        //
+        // Expose JavaScriptDSL methods to global scope.
+        //
+        context.eval(
+                LANGUAGE_ID,
+                String.join(
+                        "\n",
+                        "Object.setPrototypeOf(globalThis, new 
Proxy(Object.prototype, {",
+                        "    has(target, key) {",
+                        "        return key in __dsl || key in target;",
+                        "    },",
+                        "    get(target, key, receiver) {",
+                        "        return Reflect.get((key in __dsl) ? __dsl : 
target, key, receiver);",
+                        "    }",
+                        "}));"));
+
+        //
+        // Run the script.
+        //
+        context.eval(
+                newBuilder(LANGUAGE_ID, reader, "Unnamed").buildLiteral());
+
+        //
+        // Close the polyglot context when the camel context stops
+        //
+        builder.getContext().addLifecycleStrategy(new 
LifecycleStrategySupport() {
+            @Override
+            public void onContextStopping(CamelContext camelContext) {
+                context.close(true);
+            }
+        });
+    }
+}
diff --git 
a/dsl/camel-js-dsl/src/test/java/org/apache/camel/dsl/js/JavaScriptRoutesBuilderLoaderTest.java
 
b/dsl/camel-js-dsl/src/test/java/org/apache/camel/dsl/js/JavaScriptRoutesBuilderLoaderTest.java
new file mode 100644
index 0000000..5a2ae37
--- /dev/null
+++ 
b/dsl/camel-js-dsl/src/test/java/org/apache/camel/dsl/js/JavaScriptRoutesBuilderLoaderTest.java
@@ -0,0 +1,125 @@
+/*
+ * 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.dsl.js;
+
+import org.apache.camel.component.seda.SedaComponent;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.model.FromDefinition;
+import org.apache.camel.model.ToDefinition;
+import org.apache.camel.model.TransformDefinition;
+import org.apache.camel.spi.Resource;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class JavaScriptRoutesBuilderLoaderTest {
+    @ParameterizedTest
+    @ValueSource(strings = {
+            "/routes/routes.js",
+            "/routes/routes-with-endpoint-dsl.js",
+    })
+    void routesCanBeLoaded(String location) throws Exception {
+        try (DefaultCamelContext context = new DefaultCamelContext()) {
+            Resource resource = 
context.getResourceLoader().resolveResource(location);
+            context.getRoutesLoader().loadRoutes(resource);
+
+            assertThat(context.getRouteDefinitions())
+                    .hasSize(1)
+                    .first()
+                    .satisfies(rd -> {
+                        
assertThat(rd.getInput().getEndpointUri()).matches("timer:.*tick");
+                        
assertThat(rd.getOutputs().get(0)).isInstanceOf(ToDefinition.class);
+                    });
+        }
+    }
+
+    @Test
+    void componentsCanBeCustomized() throws Exception {
+        try (DefaultCamelContext context = new DefaultCamelContext()) {
+            Resource resource = 
context.getResourceLoader().resolveResource("/routes/routes-with-component-configuration.js");
+            context.getRoutesLoader().loadRoutes(resource);
+
+            assertThat(context.getComponent("seda", 
SedaComponent.class)).satisfies(c -> {
+                assertThat(c.getQueueSize()).isEqualTo(1234);
+            });
+        }
+    }
+
+    @Test
+    void contextCanBeCustomized() throws Exception {
+        try (DefaultCamelContext context = new DefaultCamelContext()) {
+            Resource resource = 
context.getResourceLoader().resolveResource("/routes/routes-with-context-configuration.js");
+            context.getRoutesLoader().loadRoutes(resource);
+
+            assertThat(context.isTypeConverterStatisticsEnabled()).isTrue();
+        }
+    }
+
+    @Test
+    void processorsCanBeCreated() throws Exception {
+        try (DefaultCamelContext context = new DefaultCamelContext()) {
+            Resource resource = 
context.getResourceLoader().resolveResource("/routes/routes-with-processors.js");
+            context.getRoutesLoader().loadRoutes(resource);
+
+            context.start();
+
+            
assertThat(context.createFluentProducerTemplate().to("direct:arrow").request(String.class))
+                    .isEqualTo("arrow");
+            
assertThat(context.createFluentProducerTemplate().to("direct:wrapper").request(String.class))
+                    .isEqualTo("wrapper");
+            
assertThat(context.createFluentProducerTemplate().to("direct:function").request(String.class))
+                    .isEqualTo("function");
+        }
+    }
+
+    @Test
+    void restCanBeConfigured() throws Exception {
+        try (DefaultCamelContext context = new DefaultCamelContext()) {
+            Resource resource = 
context.getResourceLoader().resolveResource("/routes/routes-with-rest-configuration.js");
+            context.getRoutesLoader().loadRoutes(resource);
+
+            assertThat(context.getRestConfiguration()).satisfies(c -> {
+                assertThat(c.getComponent()).isEqualTo("undertow");
+                assertThat(c.getPort()).isEqualTo(1234);
+            });
+        }
+    }
+
+    @Test
+    void restDslCanBeDefined() throws Exception {
+        try (DefaultCamelContext context = new DefaultCamelContext()) {
+            Resource resource = 
context.getResourceLoader().resolveResource("/routes/routes-with-rest-dsl.js");
+            context.getRoutesLoader().loadRoutes(resource);
+
+            assertThat(context.getRestDefinitions()).hasSize(1);
+            assertThat(context.getRouteDefinitions()).hasSize(1);
+
+            assertThat(context.getRestDefinitions()).first().satisfies(d -> {
+                assertThat(d.getProduces()).isEqualTo("text/plain");
+                assertThat(d.getVerbs()).first().satisfies(v -> {
+                    assertThat(v.getUri()).isEqualTo("/say/hello");
+                });
+            });
+            assertThat(context.getRouteDefinitions()).first().satisfies(d -> {
+                assertThat(d.getInput()).isInstanceOf(FromDefinition.class);
+                
assertThat(d.getOutputs()).first().isInstanceOf(TransformDefinition.class);
+            });
+        }
+    }
+}
diff --git a/dsl/camel-js-dsl/src/test/resources/log4j2-test.properties 
b/dsl/camel-js-dsl/src/test/resources/log4j2-test.properties
new file mode 100644
index 0000000..10bac20
--- /dev/null
+++ b/dsl/camel-js-dsl/src/test/resources/log4j2-test.properties
@@ -0,0 +1,31 @@
+## ---------------------------------------------------------------------------
+## 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-js-dsl-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
+
+rootLogger.level = INFO
+rootLogger.appenderRef.file.ref = file
+#rootLogger.appenderRef.out.ref = out
diff --git 
a/dsl/camel-js-dsl/src/test/resources/routes/routes-with-component-configuration.js
 
b/dsl/camel-js-dsl/src/test/resources/routes/routes-with-component-configuration.js
new file mode 100644
index 0000000..efe5696
--- /dev/null
+++ 
b/dsl/camel-js-dsl/src/test/resources/routes/routes-with-component-configuration.js
@@ -0,0 +1,5 @@
+
+const SedaType = Java.type("org.apache.camel.component.seda.SedaComponent");
+
+s = context.getComponent('seda', SedaType)
+s.setQueueSize(1234)
\ No newline at end of file
diff --git 
a/dsl/camel-js-dsl/src/test/resources/routes/routes-with-context-configuration.js
 
b/dsl/camel-js-dsl/src/test/resources/routes/routes-with-context-configuration.js
new file mode 100644
index 0000000..53817d7
--- /dev/null
+++ 
b/dsl/camel-js-dsl/src/test/resources/routes/routes-with-context-configuration.js
@@ -0,0 +1,18 @@
+/*
+ * 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.
+ */
+
+context.setTypeConverterStatisticsEnabled(true)
\ No newline at end of file
diff --git 
a/dsl/camel-js-dsl/src/test/resources/routes/routes-with-endpoint-dsl.js 
b/dsl/camel-js-dsl/src/test/resources/routes/routes-with-endpoint-dsl.js
new file mode 100644
index 0000000..fc8456d
--- /dev/null
+++ b/dsl/camel-js-dsl/src/test/resources/routes/routes-with-endpoint-dsl.js
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+f = timer("tick");
+t = log("info");
+
+from(f)
+    .to(t);
\ No newline at end of file
diff --git 
a/dsl/camel-js-dsl/src/test/resources/routes/routes-with-processors.js 
b/dsl/camel-js-dsl/src/test/resources/routes/routes-with-processors.js
new file mode 100644
index 0000000..a4fe408
--- /dev/null
+++ b/dsl/camel-js-dsl/src/test/resources/routes/routes-with-processors.js
@@ -0,0 +1,15 @@
+const Processor = Java.type("org.apache.camel.Processor");
+const p = Java.extend(Processor);
+const f = new p(function(e) { e.getMessage().setBody('function') });
+const a = new p(e => { e.getMessage().setBody('arrow') });
+const w = processor(e => { e.getMessage().setBody('wrapper') });
+
+from('direct:function')
+    .process(f);
+
+from('direct:arrow')
+    .process(a);
+
+from('direct:wrapper')
+    .process(w);
+
diff --git 
a/dsl/camel-js-dsl/src/test/resources/routes/routes-with-rest-configuration.js 
b/dsl/camel-js-dsl/src/test/resources/routes/routes-with-rest-configuration.js
new file mode 100644
index 0000000..c54a31b
--- /dev/null
+++ 
b/dsl/camel-js-dsl/src/test/resources/routes/routes-with-rest-configuration.js
@@ -0,0 +1,4 @@
+
+c = restConfiguration()
+c.setComponent('undertow')
+c.setPort('1234')
diff --git a/dsl/camel-js-dsl/src/test/resources/routes/routes-with-rest-dsl.js 
b/dsl/camel-js-dsl/src/test/resources/routes/routes-with-rest-dsl.js
new file mode 100644
index 0000000..195fca6
--- /dev/null
+++ b/dsl/camel-js-dsl/src/test/resources/routes/routes-with-rest-dsl.js
@@ -0,0 +1,7 @@
+
+rest('/')
+    .produces("text/plain")
+    .get('/say/hello')
+    .route()
+        .transform().constant("Hello World");
+
diff --git a/dsl/camel-js-dsl/src/test/resources/routes/routes.js 
b/dsl/camel-js-dsl/src/test/resources/routes/routes.js
new file mode 100644
index 0000000..ceac916
--- /dev/null
+++ b/dsl/camel-js-dsl/src/test/resources/routes/routes.js
@@ -0,0 +1,18 @@
+/*
+ * 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.
+ */
+from('timer:tick')
+    .to('log:info')
\ No newline at end of file
diff --git a/dsl/pom.xml b/dsl/pom.xml
index 622cf3d..00e9a80 100644
--- a/dsl/pom.xml
+++ b/dsl/pom.xml
@@ -39,6 +39,7 @@
         <module>camel-xml-jaxb-dsl</module>
         <module>camel-xml-jaxb-dsl-test</module>
         <module>camel-yaml-dsl</module>
+        <module>camel-js-dsl</module>
     </modules>
 
     <properties>
diff --git a/parent/pom.xml b/parent/pom.xml
index 9f67424..ed5a9de 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -220,6 +220,7 @@
         <google-cloud-bom-version>16.3.0</google-cloud-bom-version>
         <google-cloud-guava-version>30.0-jre</google-cloud-guava-version>
         <google-mail-guava-version>17.0</google-mail-guava-version>
+        <graaljs-version>21.0.0.2</graaljs-version>
         <graphql-java-version>14.0</graphql-java-version>
         <grizzly-websockets-version>2.3.25</grizzly-websockets-version>
         <grpc-version>1.28.1</grpc-version>
@@ -2702,6 +2703,11 @@
                                <artifactId>camel-yaml-dsl</artifactId>
                                <version>${project.version}</version>
                        </dependency>
+                       <dependency>
+                               <groupId>org.apache.camel</groupId>
+                               <artifactId>camel-js-dsl</artifactId>
+                               <version>${project.version}</version>
+                       </dependency>
 
             <!-- camel catalog -->
             <dependency>

Reply via email to