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 2021/12/25 15:06:48 UTC

[camel] branch main updated (32d1e6c -> 365e2c2)

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

davsclaus pushed a change to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git.


    from 32d1e6c  CAMEL-17354: Deprecate a number of components that are not active mantained by 3rd party
     new f417456  CAMEL-17382: camel-jsh-dsl - JavaShell DSL support
     new 365e2c2  CAMEL-17382: camel-jsh-dsl - JavaShell DSL support

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 bom/camel-bom/pom.xml                              |   5 +
 docs/components/modules/others/nav.adoc            |   1 +
 docs/components/modules/others/pages/jsh-dsl.adoc  |   1 +
 dsl/camel-jsh-dsl/pom.xml                          | 145 +++++++++++++++++++++
 .../services/org/apache/camel/routes-loader/jsh    |   2 +
 dsl/camel-jsh-dsl/src/main/docs/jsh-dsl.adoc       |  12 ++
 .../main/java/org/apache/camel/dsl/jsh/Jsh.java    | 135 +++++++++++++++++++
 .../org/apache/camel/dsl/jsh/JshClassLoader.java   |  52 ++++++++
 .../apache/camel/dsl/jsh/JshLoaderDelegate.java    |  80 ++++++++++++
 .../camel/dsl/jsh/JshRoutesBuilderLoader.java      | 112 ++++++++++++++++
 .../apache/camel/dsl/jsh/JshSourceLoaderTest.java  |  60 +++++++++
 .../src/test/resources/log4j2-test.properties      |  31 +++++
 .../src/test/resources/routes/MyRoute.jsh          |  20 +++
 dsl/pom.xml                                        |   1 +
 parent/pom.xml                                     |   5 +
 15 files changed, 662 insertions(+)
 create mode 120000 docs/components/modules/others/pages/jsh-dsl.adoc
 create mode 100644 dsl/camel-jsh-dsl/pom.xml
 create mode 100644 dsl/camel-jsh-dsl/src/generated/resources/META-INF/services/org/apache/camel/routes-loader/jsh
 create mode 100644 dsl/camel-jsh-dsl/src/main/docs/jsh-dsl.adoc
 create mode 100644 dsl/camel-jsh-dsl/src/main/java/org/apache/camel/dsl/jsh/Jsh.java
 create mode 100644 dsl/camel-jsh-dsl/src/main/java/org/apache/camel/dsl/jsh/JshClassLoader.java
 create mode 100644 dsl/camel-jsh-dsl/src/main/java/org/apache/camel/dsl/jsh/JshLoaderDelegate.java
 create mode 100644 dsl/camel-jsh-dsl/src/main/java/org/apache/camel/dsl/jsh/JshRoutesBuilderLoader.java
 create mode 100644 dsl/camel-jsh-dsl/src/test/java/org/apache/camel/dsl/jsh/JshSourceLoaderTest.java
 create mode 100644 dsl/camel-jsh-dsl/src/test/resources/log4j2-test.properties
 create mode 100644 dsl/camel-jsh-dsl/src/test/resources/routes/MyRoute.jsh

[camel] 01/02: CAMEL-17382: camel-jsh-dsl - JavaShell DSL support

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git

commit f417456dd0981c96dafe83976f73eadb506a89e9
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Sat Dec 25 16:03:30 2021 +0100

    CAMEL-17382: camel-jsh-dsl - JavaShell DSL support
---
 dsl/camel-jsh-dsl/pom.xml                          | 145 +++++++++++++++++++++
 .../services/org/apache/camel/routes-loader/jsh    |   2 +
 dsl/camel-jsh-dsl/src/main/docs/jsh-dsl.adoc       |  12 ++
 .../main/java/org/apache/camel/dsl/jsh/Jsh.java    | 135 +++++++++++++++++++
 .../org/apache/camel/dsl/jsh/JshClassLoader.java   |  52 ++++++++
 .../apache/camel/dsl/jsh/JshLoaderDelegate.java    |  80 ++++++++++++
 .../camel/dsl/jsh/JshRoutesBuilderLoader.java      | 112 ++++++++++++++++
 .../apache/camel/dsl/jsh/JshSourceLoaderTest.java  |  60 +++++++++
 .../src/test/resources/log4j2-test.properties      |  31 +++++
 .../src/test/resources/routes/MyRoute.jsh          |  20 +++
 dsl/pom.xml                                        |   1 +
 parent/pom.xml                                     |   5 +
 12 files changed, 655 insertions(+)

diff --git a/dsl/camel-jsh-dsl/pom.xml b/dsl/camel-jsh-dsl/pom.xml
new file mode 100644
index 0000000..861ddda
--- /dev/null
+++ b/dsl/camel-jsh-dsl/pom.xml
@@ -0,0 +1,145 @@
+<?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.15.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>camel-jsh-dsl</artifactId>
+    <packaging>jar</packaging>
+    <name>Camel :: JavaShell DSL</name>
+    <description>Camel DSL with JavaShell</description>
+
+    <properties>
+        <firstVersion>3.15.0</firstVersion>
+        <supportLevel>Experimental</supportLevel>
+        <sourcecheckExcludes>
+            **/resources/**/My*.java
+        </sourcecheckExcludes>
+        <sourcecheckExcludesComma>
+            ${sourcecheckExcludes},
+        </sourcecheckExcludesComma>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-endpointdsl-support</artifactId>
+        </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>
+
+</project>
\ No newline at end of file
diff --git a/dsl/camel-jsh-dsl/src/generated/resources/META-INF/services/org/apache/camel/routes-loader/jsh b/dsl/camel-jsh-dsl/src/generated/resources/META-INF/services/org/apache/camel/routes-loader/jsh
new file mode 100644
index 0000000..f8cf758
--- /dev/null
+++ b/dsl/camel-jsh-dsl/src/generated/resources/META-INF/services/org/apache/camel/routes-loader/jsh
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.dsl.jsh.JshRoutesBuilderLoader
diff --git a/dsl/camel-jsh-dsl/src/main/docs/jsh-dsl.adoc b/dsl/camel-jsh-dsl/src/main/docs/jsh-dsl.adoc
new file mode 100644
index 0000000..d71a63b
--- /dev/null
+++ b/dsl/camel-jsh-dsl/src/main/docs/jsh-dsl.adoc
@@ -0,0 +1,12 @@
+= JavaShell DSL Component
+//TODO there is no .json file for this doc page, so it is not updated automatically by UpdateReadmeMojo.
+//Header attributes written by hand.
+:doctitle: JavaShell DSL
+:artifactid: camel-jsh-dsl
+:description: Camel DSL with JavaShell
+:supportlevel: Experimental/Preview
+:since: 3.15
+//Manually maintained attributes
+:group: DSL
+
+See xref:manual:ROOT:dsl.adoc[DSL]
diff --git a/dsl/camel-jsh-dsl/src/main/java/org/apache/camel/dsl/jsh/Jsh.java b/dsl/camel-jsh-dsl/src/main/java/org/apache/camel/dsl/jsh/Jsh.java
new file mode 100644
index 0000000..8b29d71
--- /dev/null
+++ b/dsl/camel-jsh-dsl/src/main/java/org/apache/camel/dsl/jsh/Jsh.java
@@ -0,0 +1,135 @@
+/*
+ * 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.jsh;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.script.ScriptException;
+
+import jdk.jshell.JShell;
+import jdk.jshell.Snippet;
+import jdk.jshell.SnippetEvent;
+import jdk.jshell.SourceCodeAnalysis;
+import jdk.jshell.spi.ExecutionControl;
+import jdk.jshell.spi.ExecutionControlProvider;
+import jdk.jshell.spi.ExecutionEnv;
+import org.apache.camel.util.ObjectHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class Jsh {
+    private static final Logger LOGGER = LoggerFactory.getLogger(Jsh.class);
+    private static final ThreadLocal<Map<String, Object>> BINDINGS = ThreadLocal.withInitial(ConcurrentHashMap::new);
+
+    private Jsh() {
+        // no-op
+    }
+
+    public static List<String> compile(JShell jshell, String script) throws ScriptException {
+        List<String> snippets = new ArrayList<>();
+
+        while (!script.isEmpty()) {
+            SourceCodeAnalysis.CompletionInfo ci = jshell.sourceCodeAnalysis().analyzeCompletion(script);
+            if (!ci.completeness().isComplete()) {
+                throw new ScriptException("Incomplete script:\n" + script);
+            }
+
+            snippets.add(ci.source());
+            script = ci.remaining();
+        }
+
+        return snippets;
+    }
+
+    public static void setBinding(JShell jshell, String name, Object value) throws ScriptException {
+        ObjectHelper.notNull(jshell, "jshell");
+        ObjectHelper.notNull(name, "name");
+        ObjectHelper.notNull(value, "value");
+
+        setBinding(jshell, name, value, value.getClass());
+    }
+
+    public static <T> void setBinding(JShell jshell, String name, T value, Class<? extends T> type) throws ScriptException {
+        ObjectHelper.notNull(jshell, "jshell");
+        ObjectHelper.notNull(name, "name");
+        ObjectHelper.notNull(value, "value");
+        ObjectHelper.notNull(type, "type");
+
+        setBinding(name, value);
+
+        // As JShell leverages LocalExecutionControl as execution engine and thus JShell
+        // runs in the current process it is possible to access to local classes, we use
+        // such capability to inject bindings as variables.
+        String snippet = String.format(
+                "var %s = %s.getBinding(\"%s\", %s.class);",
+                name,
+                Jsh.class.getName(),
+                name,
+                type.getName());
+
+        eval(jshell, snippet);
+    }
+
+    public static Object getBinding(String name) {
+        return BINDINGS.get().get(name);
+    }
+
+    public static <T> T getBinding(String name, Class<T> type) {
+        Object answer = BINDINGS.get().get(name);
+        return answer != null ? type.cast(answer) : null;
+    }
+
+    public static void setBinding(String name, Object value) {
+        BINDINGS.get().put(name, value);
+    }
+
+    public static void clearBindings() {
+        BINDINGS.get().clear();
+    }
+
+    public static void eval(JShell jshell, String snippet) throws ScriptException {
+        LOGGER.debug("Evaluating {}", snippet);
+
+        List<SnippetEvent> events = jshell.eval(snippet);
+
+        for (SnippetEvent event : events) {
+            if (event.exception() != null) {
+                throw new ScriptException(event.exception());
+            }
+            if (event.status() != Snippet.Status.VALID) {
+                throw new ScriptException("Error evaluating snippet:\n" + event.snippet().source());
+            }
+        }
+    }
+
+    public static ExecutionControlProvider wrapExecutionControl(String name, ExecutionControl delegate) {
+        return new ExecutionControlProvider() {
+            @Override
+            public String name() {
+                return name;
+            }
+
+            @Override
+            public ExecutionControl generate(ExecutionEnv env, Map<String, String> parameters) throws Throwable {
+                return delegate;
+            }
+        };
+    }
+}
diff --git a/dsl/camel-jsh-dsl/src/main/java/org/apache/camel/dsl/jsh/JshClassLoader.java b/dsl/camel-jsh-dsl/src/main/java/org/apache/camel/dsl/jsh/JshClassLoader.java
new file mode 100644
index 0000000..530ba8f
--- /dev/null
+++ b/dsl/camel-jsh-dsl/src/main/java/org/apache/camel/dsl/jsh/JshClassLoader.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.dsl.jsh;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import jdk.jshell.spi.ExecutionControl;
+
+/**
+ * An implementation of a {@link ClassLoader} that allow hold class bytecode.
+ */
+final class JshClassLoader extends ClassLoader {
+    private final Map<String, ExecutionControl.ClassBytecodes> types;
+
+    JshClassLoader(ClassLoader parent) {
+        super(parent);
+        this.types = new HashMap<>();
+    }
+
+    void addClassBytecodes(ExecutionControl.ClassBytecodes classBytecodes) {
+        types.put(toResourceString(classBytecodes.name()), classBytecodes);
+    }
+
+    @Override
+    protected Class<?> findClass(String name) throws ClassNotFoundException {
+        final String key = toResourceString(name);
+        final ExecutionControl.ClassBytecodes cb = types.get(key);
+
+        return cb == null
+                ? super.findClass(name)
+                : super.defineClass(name, cb.bytecodes(), 0, cb.bytecodes().length);
+    }
+
+    private static String toResourceString(String name) {
+        return name.replace('.', '/') + ".class";
+    }
+}
diff --git a/dsl/camel-jsh-dsl/src/main/java/org/apache/camel/dsl/jsh/JshLoaderDelegate.java b/dsl/camel-jsh-dsl/src/main/java/org/apache/camel/dsl/jsh/JshLoaderDelegate.java
new file mode 100644
index 0000000..e50009d
--- /dev/null
+++ b/dsl/camel-jsh-dsl/src/main/java/org/apache/camel/dsl/jsh/JshLoaderDelegate.java
@@ -0,0 +1,80 @@
+/*
+ * 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.jsh;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import jdk.jshell.execution.LoaderDelegate;
+import jdk.jshell.spi.ExecutionControl;
+
+/**
+ * A simple implementation of {@link LoaderDelegate} tailored for camel-k use case.
+ */
+final class JshLoaderDelegate implements LoaderDelegate {
+    private final JshClassLoader loader;
+    private final Map<String, Class<?>> types;
+
+    public JshLoaderDelegate(JshClassLoader loader) {
+        this.loader = loader;
+        this.types = new HashMap<>();
+    }
+
+    @SuppressWarnings("PMD.PreserveStackTrace")
+    @Override
+    public void load(ExecutionControl.ClassBytecodes[] cbs)
+            throws ExecutionControl.ClassInstallException, ExecutionControl.EngineTerminationException {
+
+        boolean[] loaded = new boolean[cbs.length];
+        try {
+            for (ExecutionControl.ClassBytecodes cb : cbs) {
+                loader.addClassBytecodes(cb);
+            }
+            for (int i = 0; i < cbs.length; ++i) {
+                Class<?> type = loader.loadClass(cbs[i].name());
+                type.getDeclaredMethods();
+
+                types.put(cbs[i].name(), type);
+
+                loaded[i] = true;
+            }
+        } catch (Throwable ex) {
+            throw new ExecutionControl.ClassInstallException("load: " + ex.getMessage(), loaded);
+        }
+    }
+
+    @Override
+    public void classesRedefined(ExecutionControl.ClassBytecodes[] cbcs) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public void addToClasspath(String cp)
+            throws ExecutionControl.EngineTerminationException, ExecutionControl.InternalException {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public Class<?> findClass(String name) throws ClassNotFoundException {
+        Class<?> type = types.get(name);
+        if (type != null) {
+            return type;
+        }
+
+        throw new ClassNotFoundException(name + " not found");
+    }
+}
diff --git a/dsl/camel-jsh-dsl/src/main/java/org/apache/camel/dsl/jsh/JshRoutesBuilderLoader.java b/dsl/camel-jsh-dsl/src/main/java/org/apache/camel/dsl/jsh/JshRoutesBuilderLoader.java
new file mode 100644
index 0000000..7e21669
--- /dev/null
+++ b/dsl/camel-jsh-dsl/src/main/java/org/apache/camel/dsl/jsh/JshRoutesBuilderLoader.java
@@ -0,0 +1,112 @@
+/*
+ * 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.jsh;
+
+import java.io.Reader;
+
+import javax.script.ScriptException;
+
+import jdk.jshell.JShell;
+import jdk.jshell.execution.DirectExecutionControl;
+import jdk.jshell.execution.LoaderDelegate;
+import jdk.jshell.spi.ExecutionControl;
+import jdk.jshell.spi.ExecutionControlProvider;
+import org.apache.camel.CamelContext;
+import org.apache.camel.Experimental;
+import org.apache.camel.RoutesBuilder;
+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.Registry;
+import org.apache.camel.spi.Resource;
+import org.apache.camel.spi.RoutesBuilderLoader;
+import org.apache.camel.spi.annotations.RoutesLoader;
+import org.apache.camel.support.RoutesBuilderLoaderSupport;
+import org.apache.camel.util.IOHelper;
+
+/**
+ * A {@link RoutesBuilderLoader} implementation based on {@link JShell}.
+ */
+@ManagedResource(description = "Managed JShell RoutesBuilderLoader")
+@Experimental
+@RoutesLoader("jsh")
+public class JshRoutesBuilderLoader extends RoutesBuilderLoaderSupport {
+    public static final String EXTENSION = "jsh";
+
+    @ManagedAttribute(description = "Supported file extension")
+    @Override
+    public String getSupportedExtension() {
+        return EXTENSION;
+    }
+
+    @Override
+    public RoutesBuilder loadRoutesBuilder(Resource resource) throws Exception {
+        return EndpointRouteBuilder.loadEndpointRoutesBuilder(resource, JshRoutesBuilderLoader::eval);
+    }
+
+    private static void eval(Reader reader, EndpointRouteBuilder builder) throws Exception {
+        final ClassLoader tccl = Thread.currentThread().getContextClassLoader();
+        final String content = IOHelper.toString(reader);
+
+        //
+        // By default the jdk.jshell.execution.DefaultLoaderDelegate uses a
+        // custom URL class-loader and does not provide any option to set the
+        // parent which causes the ThreadLocal hack used to inject bindings
+        // to fail as there are two copies fo the Jsh class (one from the
+        // Quarkus class loader and one for the custom one).
+        //
+        final JshClassLoader jshcl = new JshClassLoader(tccl);
+        final LoaderDelegate delegate = new JshLoaderDelegate(jshcl);
+        final ExecutionControl control = new DirectExecutionControl(delegate);
+        final ExecutionControlProvider provider = Jsh.wrapExecutionControl("jsh-direct", control);
+
+        Thread.currentThread().setContextClassLoader(jshcl);
+
+        //
+        // Leverage DirectExecutionControl as execution engine to make JShell running
+        // in the current process and give a chance to bind variables to the script
+        // using ThreadLocal hack.
+        //
+        try (JShell jshell = JShell.builder().executionEngine(provider, null).build()) {
+            //
+            // since we can't set a base class for the snippet as we do for other
+            // languages (groovy, kotlin) we need to introduce a top level variable
+            // that users need to use to access the RouteBuilder, like:
+            //
+            //     builder.from("timer:tick")
+            //         .to("log:info")
+            //
+            // context and thus registry can easily be retrieved from the registered
+            // variable `builder` but for a better UX, add them as top level vars.
+            //
+            Jsh.setBinding(jshell, "builder", builder, EndpointRouteBuilder.class);
+            Jsh.setBinding(jshell, "context", builder.getContext(), CamelContext.class);
+            Jsh.setBinding(jshell, "registry", builder.getContext().getRegistry(), Registry.class);
+
+            for (String snippet : Jsh.compile(jshell, content)) {
+                Jsh.eval(jshell, snippet);
+            }
+        } catch (ScriptException e) {
+            throw new RuntimeException(e);
+        } finally {
+            // remove contextual bindings once the snippet has been evaluated
+            Jsh.clearBindings();
+            // restore original TCCL
+            Thread.currentThread().setContextClassLoader(tccl);
+        }
+    }
+}
diff --git a/dsl/camel-jsh-dsl/src/test/java/org/apache/camel/dsl/jsh/JshSourceLoaderTest.java b/dsl/camel-jsh-dsl/src/test/java/org/apache/camel/dsl/jsh/JshSourceLoaderTest.java
new file mode 100644
index 0000000..e10859f
--- /dev/null
+++ b/dsl/camel-jsh-dsl/src/test/java/org/apache/camel/dsl/jsh/JshSourceLoaderTest.java
@@ -0,0 +1,60 @@
+/*
+ * 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.jsh;
+
+import java.util.Collection;
+
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.model.ProcessorDefinition;
+import org.apache.camel.model.ToDefinition;
+import org.apache.camel.spi.Resource;
+import org.assertj.core.api.Assertions;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class JshSourceLoaderTest {
+
+    @ParameterizedTest
+    @ValueSource(strings = {
+            "/routes/MyRoute.jsh"
+    })
+    void testLoadRoutes(String location) throws Exception {
+        try (DefaultCamelContext context = new DefaultCamelContext()) {
+            Resource resource = context.getResourceLoader().resolveResource(location);
+            Collection<RoutesBuilder> builders = context.getRoutesLoader().findRoutesBuilders(resource);
+
+            assertThat(builders).hasSize(1);
+
+            RouteBuilder builder = (RouteBuilder) builders.iterator().next();
+            builder.setContext(context);
+            builder.configure();
+
+            Assertions.assertThat(builder.getRouteCollection().getRoutes())
+                    .hasSize(1)
+                    .first()
+                    .satisfies(rd -> {
+                        Assertions.assertThat(rd.getInput().getEndpointUri()).matches("timer:.*tick");
+                        Assertions.assertThat(rd.getOutputs().get(0)).isInstanceOf(ProcessorDefinition.class);
+                        Assertions.assertThat(rd.getOutputs().get(1)).isInstanceOf(ToDefinition.class);
+                    });
+        }
+    }
+}
diff --git a/dsl/camel-jsh-dsl/src/test/resources/log4j2-test.properties b/dsl/camel-jsh-dsl/src/test/resources/log4j2-test.properties
new file mode 100644
index 0000000..55f5c41
--- /dev/null
+++ b/dsl/camel-jsh-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-jsh-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-jsh-dsl/src/test/resources/routes/MyRoute.jsh b/dsl/camel-jsh-dsl/src/test/resources/routes/MyRoute.jsh
new file mode 100644
index 0000000..99bfe4f
--- /dev/null
+++ b/dsl/camel-jsh-dsl/src/test/resources/routes/MyRoute.jsh
@@ -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.
+ */
+
+builder.from("timer:tick")
+    .process(e -> {})
+    .to("log:info");
\ No newline at end of file
diff --git a/dsl/pom.xml b/dsl/pom.xml
index 592aa6e..4a1224d 100644
--- a/dsl/pom.xml
+++ b/dsl/pom.xml
@@ -45,6 +45,7 @@
         <module>camel-xml-jaxb-dsl-test</module>
         <module>camel-yaml-dsl</module>
         <module>camel-js-dsl</module>
+        <module>camel-jsh-dsl</module>
         <module>camel-kotlin-dsl</module>
         <module>camel-kamelet-main</module>
         <module>camel-jbang</module>
diff --git a/parent/pom.xml b/parent/pom.xml
index 0ae244a..dfdadc0 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -2809,6 +2809,11 @@
 				<artifactId>camel-js-dsl</artifactId>
 				<version>${project.version}</version>
 			</dependency>
+            <dependency>
+                <groupId>org.apache.camel</groupId>
+                <artifactId>camel-jsh-dsl</artifactId>
+                <version>${project.version}</version>
+            </dependency>
 			<dependency>
 				<groupId>org.apache.camel</groupId>
 				<artifactId>camel-kotlin-dsl</artifactId>

[camel] 02/02: CAMEL-17382: camel-jsh-dsl - JavaShell DSL support

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 365e2c23da92ab50df13496e680ca524f3da9504
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Sat Dec 25 16:06:10 2021 +0100

    CAMEL-17382: camel-jsh-dsl - JavaShell DSL support
---
 bom/camel-bom/pom.xml                             | 5 +++++
 docs/components/modules/others/nav.adoc           | 1 +
 docs/components/modules/others/pages/jsh-dsl.adoc | 1 +
 3 files changed, 7 insertions(+)

diff --git a/bom/camel-bom/pom.xml b/bom/camel-bom/pom.xml
index 5f323d1..79f35dc 100644
--- a/bom/camel-bom/pom.xml
+++ b/bom/camel-bom/pom.xml
@@ -1189,6 +1189,11 @@
       </dependency>
       <dependency>
         <groupId>org.apache.camel</groupId>
+        <artifactId>camel-jsh-dsl</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.camel</groupId>
         <artifactId>camel-jslt</artifactId>
         <version>${project.version}</version>
       </dependency>
diff --git a/docs/components/modules/others/nav.adoc b/docs/components/modules/others/nav.adoc
index d4c3848..c716fe3 100644
--- a/docs/components/modules/others/nav.adoc
+++ b/docs/components/modules/others/nav.adoc
@@ -13,6 +13,7 @@
 *** xref:groovy-dsl.adoc[Groovy Dsl]
 *** xref:java-joor-dsl.adoc[Java Joor Dsl]
 *** xref:js-dsl.adoc[JavaScript Dsl]
+*** xref:jsh-dsl.adoc[JavaShell DSL]
 *** xref:java-xml-jaxb-dsl.adoc[Jaxb XML Dsl]
 *** xref:kamelet-main.adoc[Kamelet Main]
 *** xref:kotlin-dsl.adoc[Kotlin Dsl]
diff --git a/docs/components/modules/others/pages/jsh-dsl.adoc b/docs/components/modules/others/pages/jsh-dsl.adoc
new file mode 120000
index 0000000..829109a
--- /dev/null
+++ b/docs/components/modules/others/pages/jsh-dsl.adoc
@@ -0,0 +1 @@
+../../../../../dsl/camel-jsh-dsl/src/main/docs/jsh-dsl.adoc
\ No newline at end of file