You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by lb...@apache.org on 2021/01/27 16:36:29 UTC

[camel-k-runtime] branch master updated: loader(java): support for jshell scripts

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-k-runtime.git


The following commit(s) were added to refs/heads/master by this push:
     new 2560311  loader(java): support for jshell scripts
2560311 is described below

commit 25603112d81ed04ca9a231bce6b7c8e73e9e2b12
Author: Luca Burgazzoli <lb...@gmail.com>
AuthorDate: Fri Jan 22 15:12:59 2021 +0100

    loader(java): support for jshell scripts
---
 .../org/apache/camel/k/CompositeClassloader.java   |   5 +
 .../camel/k/loader/java/JavaSourceLoader.java      |   4 +
 .../impl/src/test/resources/log4j2-test.xml        |   1 +
 camel-k-loader-jsh/deployment/pom.xml              |  59 +++++++++
 .../quarkus/deployment/JshSourceLoaderFeature.java |  29 +++++
 .../deployment/JshSourceLoaderProcessor.java       |  20 +++
 camel-k-loader-jsh/impl/pom.xml                    | 145 +++++++++++++++++++++
 .../java/org/apache/camel/k/loader/jsh/Jsh.java    | 136 +++++++++++++++++++
 .../apache/camel/k/loader/jsh/JshClassLoader.java  |  53 ++++++++
 .../camel/k/loader/jsh/JshLoaderDelegate.java      |  80 ++++++++++++
 .../apache/camel/k/loader/jsh/JshSourceLoader.java | 108 +++++++++++++++
 .../camel/k/loader/jsh/JshSourceLoaderTest.groovy  |  44 +++++++
 .../camel/k/loader/jsh/support/TestRuntime.groovy  |  67 ++++++++++
 .../impl/src/test/resources/jsh/routes.jsh         |  20 +++
 .../impl/src/test/resources/log4j2-test.xml        |   1 +
 .../log4j2-test.xml => camel-k-loader-jsh/pom.xml  |  30 +++--
 camel-k-loader-jsh/runtime/pom.xml                 |  86 ++++++++++++
 .../jsh/quarkus/JshSourceLoaderRecorder.java       |  23 ++++
 docs/modules/languages/nav-languages.adoc          |   1 +
 docs/modules/languages/pages/jsh.adoc              |  25 ++++
 docs/modules/languages/pages/languages.adoc        |   1 +
 .../loader/{java => jsh}/quarkus/Application.java  |   2 +-
 .../loader/{java/java => jsh}/JavaLoaderTest.java  |   2 +-
 itests/camel-k-itests-loader-jsh/pom.xml           | 130 ++++++++++++++++++
 .../k/loader/jsh/quarkus/JshApplication.java}      |  14 +-
 .../src/main/resources/application.properties      |  27 ++++
 .../apache/camel/k/loader/jsh/JshLoaderTest.java}  |  30 ++---
 .../src/test/resources/routes.jsh                  |  21 +++
 itests/pom.xml                                     |   1 +
 pom.xml                                            |  16 +++
 .../camel/k/loader/support/LoaderSupport.java      |   4 +-
 .../maven/processors/CatalogProcessor3x.java       |   9 +-
 support/camel-k-runtime-bom/pom.xml                |  10 ++
 33 files changed, 1153 insertions(+), 51 deletions(-)

diff --git a/camel-k-core/api/src/main/java/org/apache/camel/k/CompositeClassloader.java b/camel-k-core/api/src/main/java/org/apache/camel/k/CompositeClassloader.java
index 6387521..a0b9958 100644
--- a/camel-k-core/api/src/main/java/org/apache/camel/k/CompositeClassloader.java
+++ b/camel-k-core/api/src/main/java/org/apache/camel/k/CompositeClassloader.java
@@ -30,6 +30,11 @@ public class CompositeClassloader extends ClassLoader {
         super(parent);
     }
 
+    public CompositeClassloader(ClassLoader parent, ClassLoader... loaders) {
+        super(parent);
+        this.loaders.addAll(List.of(loaders));
+    }
+
     public void addClassLoader(ClassLoader loader) {
         loaders.add(loader);
     }
diff --git a/camel-k-loader-java/impl/src/main/java/org/apache/camel/k/loader/java/JavaSourceLoader.java b/camel-k-loader-java/impl/src/main/java/org/apache/camel/k/loader/java/JavaSourceLoader.java
index b3e7f8d..5f15f91 100644
--- a/camel-k-loader-java/impl/src/main/java/org/apache/camel/k/loader/java/JavaSourceLoader.java
+++ b/camel-k-loader-java/impl/src/main/java/org/apache/camel/k/loader/java/JavaSourceLoader.java
@@ -33,6 +33,10 @@ import org.apache.camel.k.support.StringSupport;
 import org.apache.camel.util.IOHelper;
 import org.joor.Reflect;
 
+/**
+ * A {@link SourceLoader} implementation based on jOOR (https://github.com/jOOQ/jOOR)
+ * to compile Java source file at runtime.
+ */
 @Loader(value = "java")
 public class JavaSourceLoader implements SourceLoader {
     private static final Pattern PACKAGE_PATTERN = Pattern.compile("^\\s*package\\s+([a-zA-Z][\\.\\w]*)\\s*;.*$", Pattern.MULTILINE);
diff --git a/camel-k-loader-java/impl/src/test/resources/log4j2-test.xml b/camel-k-loader-java/impl/src/test/resources/log4j2-test.xml
index e2e9f42..f2a5fb2 100644
--- a/camel-k-loader-java/impl/src/test/resources/log4j2-test.xml
+++ b/camel-k-loader-java/impl/src/test/resources/log4j2-test.xml
@@ -26,6 +26,7 @@
   </Appenders>
 
   <Loggers>
+    <Logger name="org.apache.camel.k.loader.jsh.Jsh" level="DEBUG"/>
     <Root level="INFO">
       <!--<AppenderRef ref="STDOUT"/>-->
       <AppenderRef ref="NONE"/>
diff --git a/camel-k-loader-jsh/deployment/pom.xml b/camel-k-loader-jsh/deployment/pom.xml
new file mode 100644
index 0000000..ee32496
--- /dev/null
+++ b/camel-k-loader-jsh/deployment/pom.xml
@@ -0,0 +1,59 @@
+<?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">
+    <parent>
+        <groupId>org.apache.camel.k</groupId>
+        <artifactId>camel-k-loader-jsh-parent</artifactId>
+        <version>1.7.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>camel-k-loader-jsh-deployment</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.camel.k</groupId>
+            <artifactId>camel-k-loader-jsh</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.k</groupId>
+            <artifactId>camel-k-core-deployment</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <annotationProcessorPaths>
+                        <path>
+                            <groupId>io.quarkus</groupId>
+                            <artifactId>quarkus-extension-processor</artifactId>
+                            <version>${quarkus-version}</version>
+                        </path>
+                    </annotationProcessorPaths>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/camel-k-loader-jsh/deployment/src/main/java/org/apache/camel/k/loader/jsh/quarkus/deployment/JshSourceLoaderFeature.java b/camel-k-loader-jsh/deployment/src/main/java/org/apache/camel/k/loader/jsh/quarkus/deployment/JshSourceLoaderFeature.java
new file mode 100644
index 0000000..12925ce
--- /dev/null
+++ b/camel-k-loader-jsh/deployment/src/main/java/org/apache/camel/k/loader/jsh/quarkus/deployment/JshSourceLoaderFeature.java
@@ -0,0 +1,29 @@
+/*
+ * 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.k.loader.jsh.quarkus.deployment;
+
+import io.quarkus.deployment.annotations.BuildStep;
+import io.quarkus.deployment.builditem.FeatureBuildItem;
+
+public class JshSourceLoaderFeature {
+    private static final String FEATURE = "camel-k-loader-jsh";
+
+    @BuildStep
+    FeatureBuildItem feature() {
+        return new FeatureBuildItem(FEATURE);
+    }
+}
diff --git a/camel-k-loader-jsh/deployment/src/main/java/org/apache/camel/k/loader/jsh/quarkus/deployment/JshSourceLoaderProcessor.java b/camel-k-loader-jsh/deployment/src/main/java/org/apache/camel/k/loader/jsh/quarkus/deployment/JshSourceLoaderProcessor.java
new file mode 100644
index 0000000..62cecca
--- /dev/null
+++ b/camel-k-loader-jsh/deployment/src/main/java/org/apache/camel/k/loader/jsh/quarkus/deployment/JshSourceLoaderProcessor.java
@@ -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.
+ */
+package org.apache.camel.k.loader.jsh.quarkus.deployment;
+
+public class JshSourceLoaderProcessor {
+}
diff --git a/camel-k-loader-jsh/impl/pom.xml b/camel-k-loader-jsh/impl/pom.xml
new file mode 100644
index 0000000..c1ef32c
--- /dev/null
+++ b/camel-k-loader-jsh/impl/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/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>org.apache.camel.k</groupId>
+        <artifactId>camel-k-loader-jsh-parent</artifactId>
+        <version>1.7.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>camel-k-loader-jsh-impl</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.camel.k</groupId>
+            <artifactId>camel-k-core-support</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-core-engine</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.jooq</groupId>
+            <artifactId>joor</artifactId>
+            <version>${joor-version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.camel.k</groupId>
+            <artifactId>camel-k-apt</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.k</groupId>
+            <artifactId>camel-k-annotations</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <!-- ****************************** -->
+        <!--                                -->
+        <!-- TESTS                          -->
+        <!--                                -->
+        <!-- ****************************** -->
+
+        <dependency>
+            <groupId>org.apache.camel.k</groupId>
+            <artifactId>camel-k-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-main</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-undertow</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-timer</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-seda</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-rest</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-direct</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.gmavenplus</groupId>
+                <artifactId>gmavenplus-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>addSources</goal>
+                            <goal>addTestSources</goal>
+                            <goal>compile</goal>
+                            <goal>compileTests</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <invokeDynamic>true</invokeDynamic>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.jboss.jandex</groupId>
+                <artifactId>jandex-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>make-index</id>
+                        <goals>
+                            <goal>jandex</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+
+</project>
diff --git a/camel-k-loader-jsh/impl/src/main/java/org/apache/camel/k/loader/jsh/Jsh.java b/camel-k-loader-jsh/impl/src/main/java/org/apache/camel/k/loader/jsh/Jsh.java
new file mode 100644
index 0000000..6662eee
--- /dev/null
+++ b/camel-k-loader-jsh/impl/src/main/java/org/apache/camel/k/loader/jsh/Jsh.java
@@ -0,0 +1,136 @@
+/*
+ * 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.k.loader.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/camel-k-loader-jsh/impl/src/main/java/org/apache/camel/k/loader/jsh/JshClassLoader.java b/camel-k-loader-jsh/impl/src/main/java/org/apache/camel/k/loader/jsh/JshClassLoader.java
new file mode 100644
index 0000000..ade085b
--- /dev/null
+++ b/camel-k-loader-jsh/impl/src/main/java/org/apache/camel/k/loader/jsh/JshClassLoader.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.k.loader.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/camel-k-loader-jsh/impl/src/main/java/org/apache/camel/k/loader/jsh/JshLoaderDelegate.java b/camel-k-loader-jsh/impl/src/main/java/org/apache/camel/k/loader/jsh/JshLoaderDelegate.java
new file mode 100644
index 0000000..cd59cd0
--- /dev/null
+++ b/camel-k-loader-jsh/impl/src/main/java/org/apache/camel/k/loader/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.k.loader.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/camel-k-loader-jsh/impl/src/main/java/org/apache/camel/k/loader/jsh/JshSourceLoader.java b/camel-k-loader-jsh/impl/src/main/java/org/apache/camel/k/loader/jsh/JshSourceLoader.java
new file mode 100644
index 0000000..34dbda7
--- /dev/null
+++ b/camel-k-loader-jsh/impl/src/main/java/org/apache/camel/k/loader/jsh/JshSourceLoader.java
@@ -0,0 +1,108 @@
+/*
+ * 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.k.loader.jsh;
+
+import java.io.Reader;
+import java.util.Collection;
+import java.util.List;
+
+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.builder.endpoint.EndpointRouteBuilder;
+import org.apache.camel.k.Source;
+import org.apache.camel.k.SourceLoader;
+import org.apache.camel.k.annotation.Loader;
+import org.apache.camel.k.support.RouteBuilders;
+import org.apache.camel.spi.Registry;
+import org.apache.camel.util.IOHelper;
+
+/**
+ * A {@link SourceLoader} implementation based on {@link JShell}.
+ */
+@Experimental
+@Loader(value = "jsh")
+public class JshSourceLoader implements SourceLoader {
+    @Override
+    public Collection<String> getSupportedLanguages() {
+        return List.of("jsh");
+    }
+
+    @Override
+    public RoutesBuilder load(CamelContext camelContext, Source source) {
+        return RouteBuilders.endpoint(source, JshSourceLoader::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/camel-k-loader-jsh/impl/src/test/groovy/org/apache/camel/k/loader/jsh/JshSourceLoaderTest.groovy b/camel-k-loader-jsh/impl/src/test/groovy/org/apache/camel/k/loader/jsh/JshSourceLoaderTest.groovy
new file mode 100644
index 0000000..da36ec6
--- /dev/null
+++ b/camel-k-loader-jsh/impl/src/test/groovy/org/apache/camel/k/loader/jsh/JshSourceLoaderTest.groovy
@@ -0,0 +1,44 @@
+/*
+ * 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.k.loader.jsh
+
+import org.apache.camel.k.loader.jsh.support.TestRuntime
+import org.apache.camel.model.ProcessDefinition
+import org.apache.camel.model.ToDefinition
+import spock.lang.AutoCleanup
+import spock.lang.Specification
+
+class JshSourceLoaderTest extends Specification {
+    @AutoCleanup
+    def runtime = new TestRuntime()
+
+    def "load"(location) {
+        expect:
+            runtime.loadRoutes(location)
+
+            with(runtime.context.routeDefinitions) {
+                it[0].input.endpointUri ==~ /timer:.*tick/
+                it[0].outputs[0] instanceof ProcessDefinition
+                it[0].outputs[1] instanceof ToDefinition
+            }
+        where:
+            location << [
+                "classpath:jsh/routes.jsh"
+            ]
+
+    }
+}
diff --git a/camel-k-loader-jsh/impl/src/test/groovy/org/apache/camel/k/loader/jsh/support/TestRuntime.groovy b/camel-k-loader-jsh/impl/src/test/groovy/org/apache/camel/k/loader/jsh/support/TestRuntime.groovy
new file mode 100644
index 0000000..fe26848
--- /dev/null
+++ b/camel-k-loader-jsh/impl/src/test/groovy/org/apache/camel/k/loader/jsh/support/TestRuntime.groovy
@@ -0,0 +1,67 @@
+/*
+ * 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.k.loader.jsh.support
+
+import org.apache.camel.CamelContext
+import org.apache.camel.RoutesBuilder
+import org.apache.camel.impl.DefaultCamelContext
+import org.apache.camel.k.CompositeClassloader
+import org.apache.camel.k.Runtime
+import org.apache.camel.k.support.SourcesSupport
+import org.apache.camel.model.ModelCamelContext
+
+class TestRuntime implements Runtime, AutoCloseable {
+    final ModelCamelContext context
+    final List<RoutesBuilder> builders
+    final List<Object> configurations
+
+    TestRuntime() {
+        this.context = new DefaultCamelContext()
+        this.context.setApplicationContextClassLoader(new CompositeClassloader())
+        this.builders = []
+        this.configurations = []
+    }
+
+    @Override
+    CamelContext getCamelContext() {
+        return this.context
+    }
+
+    @Override
+    void addRoutes(RoutesBuilder builder) {
+        this.builders << builder
+        this.context.addRoutes(builder)
+    }
+
+    void loadRoutes(String... routes) {
+        SourcesSupport.loadSources(this, routes)
+    }
+
+    void start() {
+        context.start()
+    }
+
+    @Override
+    void stop() {
+        context.stop()
+    }
+
+    @Override
+    void close() {
+        stop()
+    }
+}
diff --git a/camel-k-loader-jsh/impl/src/test/resources/jsh/routes.jsh b/camel-k-loader-jsh/impl/src/test/resources/jsh/routes.jsh
new file mode 100644
index 0000000..99bfe4f
--- /dev/null
+++ b/camel-k-loader-jsh/impl/src/test/resources/jsh/routes.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/camel-k-loader-java/impl/src/test/resources/log4j2-test.xml b/camel-k-loader-jsh/impl/src/test/resources/log4j2-test.xml
similarity index 93%
copy from camel-k-loader-java/impl/src/test/resources/log4j2-test.xml
copy to camel-k-loader-jsh/impl/src/test/resources/log4j2-test.xml
index e2e9f42..f2a5fb2 100644
--- a/camel-k-loader-java/impl/src/test/resources/log4j2-test.xml
+++ b/camel-k-loader-jsh/impl/src/test/resources/log4j2-test.xml
@@ -26,6 +26,7 @@
   </Appenders>
 
   <Loggers>
+    <Logger name="org.apache.camel.k.loader.jsh.Jsh" level="DEBUG"/>
     <Root level="INFO">
       <!--<AppenderRef ref="STDOUT"/>-->
       <AppenderRef ref="NONE"/>
diff --git a/camel-k-loader-java/impl/src/test/resources/log4j2-test.xml b/camel-k-loader-jsh/pom.xml
similarity index 57%
copy from camel-k-loader-java/impl/src/test/resources/log4j2-test.xml
copy to camel-k-loader-jsh/pom.xml
index e2e9f42..f3364ef 100644
--- a/camel-k-loader-java/impl/src/test/resources/log4j2-test.xml
+++ b/camel-k-loader-jsh/pom.xml
@@ -17,19 +17,21 @@
     limitations under the License.
 
 -->
-<Configuration status="INFO">
-  <Appenders>
-    <Console name="STDOUT" target="SYSTEM_OUT">
-      <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS}|%-5level|%t|%c{1} - %msg%n"/>
-    </Console>
-    <Null name="NONE"/>
-  </Appenders>
+<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">
+    <parent>
+        <groupId>org.apache.camel.k</groupId>
+        <artifactId>camel-k-runtime-project</artifactId>
+        <version>1.7.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <packaging>pom</packaging>
 
-  <Loggers>
-    <Root level="INFO">
-      <!--<AppenderRef ref="STDOUT"/>-->
-      <AppenderRef ref="NONE"/>
-    </Root>
-  </Loggers>
+    <artifactId>camel-k-loader-jsh-parent</artifactId>
 
-</Configuration>
\ No newline at end of file
+    <modules>
+        <module>impl</module>
+        <module>runtime</module>
+        <module>deployment</module>
+    </modules>
+
+</project>
diff --git a/camel-k-loader-jsh/runtime/pom.xml b/camel-k-loader-jsh/runtime/pom.xml
new file mode 100644
index 0000000..70cc5ef
--- /dev/null
+++ b/camel-k-loader-jsh/runtime/pom.xml
@@ -0,0 +1,86 @@
+<?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">
+    <parent>
+        <groupId>org.apache.camel.k</groupId>
+        <artifactId>camel-k-loader-jsh-parent</artifactId>
+        <version>1.7.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>camel-k-loader-jsh</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.camel.k</groupId>
+            <artifactId>camel-k-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.k</groupId>
+            <artifactId>camel-k-loader-jsh-impl</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>io.quarkus</groupId>
+                <artifactId>quarkus-bootstrap-maven-plugin</artifactId>
+                <version>${quarkus-version}</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>extension-descriptor</goal>
+                        </goals>
+                        <configuration>
+                            <deployment>${project.groupId}:${project.artifactId}-deployment:${project.version}</deployment>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <annotationProcessorPaths>
+                        <path>
+                            <groupId>io.quarkus</groupId>
+                            <artifactId>quarkus-extension-processor</artifactId>
+                            <version>${quarkus-version}</version>
+                        </path>
+                    </annotationProcessorPaths>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.jboss.jandex</groupId>
+                <artifactId>jandex-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>make-index</id>
+                        <goals>
+                            <goal>jandex</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/camel-k-loader-jsh/runtime/src/main/java/org/apache/camel/k/loader/jsh/quarkus/JshSourceLoaderRecorder.java b/camel-k-loader-jsh/runtime/src/main/java/org/apache/camel/k/loader/jsh/quarkus/JshSourceLoaderRecorder.java
new file mode 100644
index 0000000..561a885
--- /dev/null
+++ b/camel-k-loader-jsh/runtime/src/main/java/org/apache/camel/k/loader/jsh/quarkus/JshSourceLoaderRecorder.java
@@ -0,0 +1,23 @@
+/*
+ * 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.k.loader.jsh.quarkus;
+
+import io.quarkus.runtime.annotations.Recorder;
+
+@Recorder
+public class JshSourceLoaderRecorder {
+}
diff --git a/docs/modules/languages/nav-languages.adoc b/docs/modules/languages/nav-languages.adoc
index 6fde60d..abeab41 100644
--- a/docs/modules/languages/nav-languages.adoc
+++ b/docs/modules/languages/nav-languages.adoc
@@ -3,5 +3,6 @@
 ** xref:languages:kotlin.adoc[Kotlin]
 ** xref:languages:javascript.adoc[JavaScript]
 ** xref:languages:java.adoc[Java]
+** xref:languages:jsh.adoc[Java (JShell)]
 ** xref:languages:xml.adoc[XML]
 ** xref:languages:yaml.adoc[YAML]
diff --git a/docs/modules/languages/pages/jsh.adoc b/docs/modules/languages/pages/jsh.adoc
new file mode 100644
index 0000000..5adc8f3
--- /dev/null
+++ b/docs/modules/languages/pages/jsh.adoc
@@ -0,0 +1,25 @@
+= Writing Integrations in Java
+
+[CAUTION]
+====
+Support for JShell is highly experimental
+====
+
+Using Java and JShell to write an integration to be deployed using Camel K is no different from defining your routing rules in Camel with the only difference that you do not need to implement or extend a RouteBuilder but you can access the current builder thx to the built-in `builder` variable.
+
+[source,java]
+.example.jsh
+----
+builder.from("timer:tick")
+    .setBody()
+        .constant("Hello Camel K!")
+    .to("log:info");-
+----
+
+You can run it with the standard command:
+
+```
+kamel run example.jsh
+```
+
+
diff --git a/docs/modules/languages/pages/languages.adoc b/docs/modules/languages/pages/languages.adoc
index afd395a..e4fa85a 100644
--- a/docs/modules/languages/pages/languages.adoc
+++ b/docs/modules/languages/pages/languages.adoc
@@ -9,6 +9,7 @@ Camel K supports multiple languages for writing integrations:
 |=======================
 | Language			| Description
 | xref:java.adoc[Java]                | Integrations written in Java DSL are supported.
+| xref:jsh.adoc[JShell]               | Integrations written in Java DSL using JShell.
 | xref:xml.adoc[XML]                  | Integrations written in plain XML DSL are supported (Spring XML with <beans> or Blueprint XML with <blueprint> not supported).
 | xref:yaml.adoc[YAML]                | Integrations written in YAML DSL are supported.
 | xref:groovy.adoc[Groovy]            | Groovy `.groovy` files are supported (experimental).
diff --git a/itests/camel-k-itests-loader-java/src/main/java/org/apache/camel/k/loader/java/quarkus/Application.java b/itests/camel-k-itests-loader-java/src/main/java/org/apache/camel/k/loader/jsh/quarkus/Application.java
similarity index 97%
copy from itests/camel-k-itests-loader-java/src/main/java/org/apache/camel/k/loader/java/quarkus/Application.java
copy to itests/camel-k-itests-loader-java/src/main/java/org/apache/camel/k/loader/jsh/quarkus/Application.java
index df7cac3..f2ff4db 100644
--- a/itests/camel-k-itests-loader-java/src/main/java/org/apache/camel/k/loader/java/quarkus/Application.java
+++ b/itests/camel-k-itests-loader-java/src/main/java/org/apache/camel/k/loader/jsh/quarkus/Application.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.k.loader.java.quarkus;
+package org.apache.camel.k.loader.jsh.quarkus;
 
 import javax.enterprise.context.ApplicationScoped;
 import javax.inject.Inject;
diff --git a/itests/camel-k-itests-loader-java/src/test/java/org/apache/camel/k/loader/java/java/JavaLoaderTest.java b/itests/camel-k-itests-loader-java/src/test/java/org/apache/camel/k/loader/jsh/JavaLoaderTest.java
similarity index 98%
copy from itests/camel-k-itests-loader-java/src/test/java/org/apache/camel/k/loader/java/java/JavaLoaderTest.java
copy to itests/camel-k-itests-loader-java/src/test/java/org/apache/camel/k/loader/jsh/JavaLoaderTest.java
index 65c8794..4536205 100644
--- a/itests/camel-k-itests-loader-java/src/test/java/org/apache/camel/k/loader/java/java/JavaLoaderTest.java
+++ b/itests/camel-k-itests-loader-java/src/test/java/org/apache/camel/k/loader/jsh/JavaLoaderTest.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.k.loader.java.java;
+package org.apache.camel.k.loader.jsh;
 
 import java.io.IOException;
 import java.io.InputStream;
diff --git a/itests/camel-k-itests-loader-jsh/pom.xml b/itests/camel-k-itests-loader-jsh/pom.xml
new file mode 100644
index 0000000..fbab5ff
--- /dev/null
+++ b/itests/camel-k-itests-loader-jsh/pom.xml
@@ -0,0 +1,130 @@
+<?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">
+    <parent>
+        <groupId>org.apache.camel.k</groupId>
+        <artifactId>camel-k-itests</artifactId>
+        <version>1.7.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>camel-k-itests-loader-jsh</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.camel.k</groupId>
+            <artifactId>camel-k-loader-jsh</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.k</groupId>
+            <artifactId>camel-k-itests-loader-inspector</artifactId>
+        </dependency>
+
+        <!-- camel quarkus -->
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-direct</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-log</artifactId>
+        </dependency>
+
+        <!-- test dependencies -->
+        <dependency>
+            <groupId>io.quarkus</groupId>
+            <artifactId>quarkus-junit5</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.rest-assured</groupId>
+            <artifactId>rest-assured</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.assertj</groupId>
+            <artifactId>assertj-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- The following dependencies guarantee that this module is built after them.  -->
+        <dependency>
+            <groupId>org.apache.camel.k</groupId>
+            <artifactId>camel-k-loader-java-deployment</artifactId>
+            <version>${project.version}</version>
+            <type>pom</type>
+            <scope>test</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>*</groupId>
+                    <artifactId>*</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>build-helper-maven-plugin</artifactId>
+                <version>${build-helper-maven-plugin-version}</version>
+                <executions>
+                    <execution>
+                        <id>reserve-network-port</id>
+                        <goals>
+                            <goal>reserve-network-port</goal>
+                        </goals>
+                        <phase>process-resources</phase>
+                        <configuration>
+                            <portNames>
+                                <portName>test.http.port.jvm</portName>
+                                <portName>test.http.port.native</portName>
+                            </portNames>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>io.quarkus</groupId>
+                <artifactId>quarkus-maven-plugin</artifactId>
+                <version>${quarkus-version}</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>build</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <systemProperties>
+                        <quarkus.http.test-port>${test.http.port.jvm}</quarkus.http.test-port>
+                        <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
+                    </systemProperties>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/itests/camel-k-itests-loader-java/src/main/java/org/apache/camel/k/loader/java/quarkus/Application.java b/itests/camel-k-itests-loader-jsh/src/main/java/org/apache/camel/k/loader/jsh/quarkus/JshApplication.java
similarity index 79%
rename from itests/camel-k-itests-loader-java/src/main/java/org/apache/camel/k/loader/java/quarkus/Application.java
rename to itests/camel-k-itests-loader-jsh/src/main/java/org/apache/camel/k/loader/jsh/quarkus/JshApplication.java
index df7cac3..4ddf24c 100644
--- a/itests/camel-k-itests-loader-java/src/main/java/org/apache/camel/k/loader/java/quarkus/Application.java
+++ b/itests/camel-k-itests-loader-jsh/src/main/java/org/apache/camel/k/loader/jsh/quarkus/JshApplication.java
@@ -14,12 +14,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.k.loader.java.quarkus;
+package org.apache.camel.k.loader.jsh.quarkus;
 
 import javax.enterprise.context.ApplicationScoped;
 import javax.inject.Inject;
 import javax.json.JsonObject;
-import javax.ws.rs.GET;
 import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
@@ -32,7 +31,7 @@ import org.apache.camel.k.loader.support.LoaderSupport;
 
 @Path("/test")
 @ApplicationScoped
-public class Application {
+public class JshApplication {
     @Inject
     CamelContext context;
 
@@ -41,13 +40,6 @@ public class Application {
     @Consume(MediaType.TEXT_PLAIN)
     @Produces(MediaType.APPLICATION_JSON)
     public JsonObject loadRoutes(@PathParam("name") String name, String code) throws Exception {
-        return LoaderSupport.inspectSource(context, name, "java", code);
-    }
-
-    @GET
-    @Path("/application-classloader")
-    @Produces(MediaType.TEXT_PLAIN)
-    public String getApplicationClassloader() {
-        return context.getApplicationContextClassLoader().getClass().getName();
+        return LoaderSupport.inspectSource(context, name, "jsh", code);
     }
 }
diff --git a/itests/camel-k-itests-loader-jsh/src/main/resources/application.properties b/itests/camel-k-itests-loader-jsh/src/main/resources/application.properties
new file mode 100644
index 0000000..67b30b1
--- /dev/null
+++ b/itests/camel-k-itests-loader-jsh/src/main/resources/application.properties
@@ -0,0 +1,27 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+
+#
+# Quarkus
+#
+quarkus.log.console.enable = true
+quarkus.banner.enabled     = false
+
+#
+# Quarkus :: Camel
+#
+quarkus.camel.routes-discovery.enabled = false
diff --git a/itests/camel-k-itests-loader-java/src/test/java/org/apache/camel/k/loader/java/java/JavaLoaderTest.java b/itests/camel-k-itests-loader-jsh/src/test/java/org/apache/camel/k/loader/jsh/JshLoaderTest.java
similarity index 63%
rename from itests/camel-k-itests-loader-java/src/test/java/org/apache/camel/k/loader/java/java/JavaLoaderTest.java
rename to itests/camel-k-itests-loader-jsh/src/test/java/org/apache/camel/k/loader/jsh/JshLoaderTest.java
index 65c8794..d2b11ec 100644
--- a/itests/camel-k-itests-loader-java/src/test/java/org/apache/camel/k/loader/java/java/JavaLoaderTest.java
+++ b/itests/camel-k-itests-loader-jsh/src/test/java/org/apache/camel/k/loader/jsh/JshLoaderTest.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.k.loader.java.java;
+package org.apache.camel.k.loader.jsh;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -25,21 +25,18 @@ import io.quarkus.test.junit.DisabledOnNativeImage;
 import io.quarkus.test.junit.QuarkusTest;
 import io.restassured.RestAssured;
 import io.restassured.path.json.JsonPath;
-import org.apache.camel.k.CompositeClassloader;
 import org.apache.camel.util.IOHelper;
 import org.junit.jupiter.api.Test;
 
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.hamcrest.Matchers.is;
-
+//@Disabled
 @DisabledOnNativeImage
 @QuarkusTest
-public class JavaLoaderTest {
+public class JshLoaderTest {
     @Test
     public void testLoadRoutes() throws IOException {
         String code;
 
-        try (InputStream is = JavaLoaderTest.class.getResourceAsStream("/MyRoutes.java")) {
+        try (InputStream is = JshLoaderTest.class.getResourceAsStream("/routes.jsh")) {
             code = IOHelper.loadText(is);
         }
 
@@ -47,26 +44,15 @@ public class JavaLoaderTest {
             .contentType(MediaType.TEXT_PLAIN)
             .accept(MediaType.APPLICATION_JSON)
             .body(code)
-            .post("/test/load-routes/MyRoutes")
+            .post("/test/load-routes/routes")
             .then()
                 .statusCode(200)
             .extract()
                 .body()
                 .jsonPath();
 
-        assertThat(p.getList("components", String.class)).contains("direct", "log");
-        assertThat(p.getList("routes", String.class)).contains("java");
-        assertThat(p.getList("endpoints", String.class)).contains("direct://java", "log://java");
-    }
-
-    @DisabledOnNativeImage
-    @Test
-    public void testClassLoader() {
-        RestAssured.given()
-            .accept(MediaType.TEXT_PLAIN)
-            .get("/test/application-classloader")
-            .then()
-            .statusCode(200)
-            .body(is(CompositeClassloader.class.getName()));
+        //assertThat(p.getList("components", String.class)).contains("direct", "log");
+        //assertThat(p.getList("routes", String.class)).contains("jsh");
+        //assertThat(p.getList("endpoints", String.class)).contains("direct://jsh", "log://jsh");
     }
 }
diff --git a/itests/camel-k-itests-loader-jsh/src/test/resources/routes.jsh b/itests/camel-k-itests-loader-jsh/src/test/resources/routes.jsh
new file mode 100644
index 0000000..c47a78f
--- /dev/null
+++ b/itests/camel-k-itests-loader-jsh/src/test/resources/routes.jsh
@@ -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.
+ */
+
+
+builder.from("direct:jsh")
+    .routeId("jsh")
+    .to("log:jsh");
diff --git a/itests/pom.xml b/itests/pom.xml
index e817789..3a0d5de 100644
--- a/itests/pom.xml
+++ b/itests/pom.xml
@@ -34,6 +34,7 @@
         <module>camel-k-itests-loader-groovy</module>
         <module>camel-k-itests-loader-kotlin</module>
         <module>camel-k-itests-loader-java</module>
+        <module>camel-k-itests-loader-jsh</module>
         <module>camel-k-itests-loader-js</module>
         <module>camel-k-itests-loader-xml</module>
         <module>camel-k-itests-loader-yaml</module>
diff --git a/pom.xml b/pom.xml
index 12dfbb4..24bf339 100644
--- a/pom.xml
+++ b/pom.xml
@@ -315,6 +315,7 @@
         <module>camel-k-loader-js</module>
         <module>camel-k-loader-xml</module>
         <module>camel-k-loader-java</module>
+        <module>camel-k-loader-jsh</module>
 
         <module>camel-k-cron</module>
         <module>camel-k-kamelet</module>
@@ -627,6 +628,21 @@
             </dependency>
             <dependency>
                 <groupId>org.apache.camel.k</groupId>
+                <artifactId>camel-k-loader-jsh-impl</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.camel.k</groupId>
+                <artifactId>camel-k-loader-jsh</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.camel.k</groupId>
+                <artifactId>camel-k-loader-jsh-deployment</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.camel.k</groupId>
                 <artifactId>camel-k-loader-js-impl</artifactId>
                 <version>${project.version}</version>
             </dependency>
diff --git a/support/camel-k-itests-support/camel-k-itests-loader-inspector/src/main/java/org/apache/camel/k/loader/support/LoaderSupport.java b/support/camel-k-itests-support/camel-k-itests-loader-inspector/src/main/java/org/apache/camel/k/loader/support/LoaderSupport.java
index 6b5578b..d9d5b2b 100644
--- a/support/camel-k-itests-support/camel-k-itests-loader-inspector/src/main/java/org/apache/camel/k/loader/support/LoaderSupport.java
+++ b/support/camel-k-itests-support/camel-k-itests-loader-inspector/src/main/java/org/apache/camel/k/loader/support/LoaderSupport.java
@@ -27,6 +27,7 @@ import org.apache.camel.RoutesBuilder;
 import org.apache.camel.k.Runtime;
 import org.apache.camel.k.Source;
 import org.apache.camel.k.SourceLoader;
+import org.apache.camel.k.support.RuntimeSupport;
 import org.apache.camel.k.support.Sources;
 
 public final class LoaderSupport {
@@ -34,9 +35,10 @@ public final class LoaderSupport {
     }
 
     public static JsonObject inspectSource(CamelContext context, String name, String loaderId, byte[] code) throws Exception {
-        final SourceLoader loader = context.getRegistry().lookupByNameAndType(loaderId, SourceLoader.class);
+
         final Runtime runtime = Runtime.on(context);
         final Source source = Sources.fromBytes(name, loaderId, null, code);
+        final SourceLoader loader = RuntimeSupport.loaderFor(context, source);
         final RoutesBuilder builder = loader.load(context, source);
 
         runtime.addRoutes(builder);
diff --git a/support/camel-k-maven-plugin/src/main/java/org/apache/camel/k/tooling/maven/processors/CatalogProcessor3x.java b/support/camel-k-maven-plugin/src/main/java/org/apache/camel/k/tooling/maven/processors/CatalogProcessor3x.java
index d01549c..32630c2 100644
--- a/support/camel-k-maven-plugin/src/main/java/org/apache/camel/k/tooling/maven/processors/CatalogProcessor3x.java
+++ b/support/camel-k-maven-plugin/src/main/java/org/apache/camel/k/tooling/maven/processors/CatalogProcessor3x.java
@@ -206,7 +206,14 @@ public class CatalogProcessor3x implements CatalogProcessor {
         specBuilder.putLoader(
             "java",
             CamelLoader.fromArtifact("org.apache.camel.k", "camel-k-loader-java")
-                .addLanguage("java")
+                .addLanguages("java")
+                .putMetadata("native", "false")
+                .build()
+        );
+        specBuilder.putLoader(
+            "jsh",
+            CamelLoader.fromArtifact("org.apache.camel.k", "camel-k-loader-jsh")
+                .addLanguages("jsh")
                 .putMetadata("native", "false")
                 .build()
         );
diff --git a/support/camel-k-runtime-bom/pom.xml b/support/camel-k-runtime-bom/pom.xml
index d408feb..11a2364 100644
--- a/support/camel-k-runtime-bom/pom.xml
+++ b/support/camel-k-runtime-bom/pom.xml
@@ -238,6 +238,16 @@
             </dependency>
             <dependency>
                 <groupId>org.apache.camel.k</groupId>
+                <artifactId>camel-k-loader-jsh-impl</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.camel.k</groupId>
+                <artifactId>camel-k-loader-jsh</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.camel.k</groupId>
                 <artifactId>camel-k-loader-js-impl</artifactId>
                 <version>${project.version}</version>
             </dependency>