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 638752160..a0b9958f8 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 CompositeClassloader(ClassLoader parent) { 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 b3e7f8de0..5f15f91a0 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.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 e2e9f4226..f2a5fb238 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 000000000..ee3249600 --- /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 000000000..12925ce64 --- /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 000000000..62ceccaa4 --- /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 000000000..c1ef32c2a --- /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 000000000..6662eee02 --- /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 000000000..ade085b76 --- /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 000000000..cd59cd0b4 --- /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 000000000..34dbda70a --- /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 000000000..da36ec63e --- /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 000000000..fe26848fc --- /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 000000000..99bfe4f50 --- /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-jsh/impl/src/test/resources/log4j2-test.xml b/camel-k-loader-jsh/impl/src/test/resources/log4j2-test.xml new file mode 100644 index 000000000..f2a5fb238 --- /dev/null +++ b/camel-k-loader-jsh/impl/src/test/resources/log4j2-test.xml @@ -0,0 +1,36 @@ +<?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. + +--> +<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> + + <Loggers> + <Logger name="org.apache.camel.k.loader.jsh.Jsh" level="DEBUG"/> + <Root level="INFO"> + <!--<AppenderRef ref="STDOUT"/>--> + <AppenderRef ref="NONE"/> + </Root> + </Loggers> + +</Configuration> \ No newline at end of file diff --git a/camel-k-loader-jsh/pom.xml b/camel-k-loader-jsh/pom.xml new file mode 100644 index 000000000..f3364ef11 --- /dev/null +++ b/camel-k-loader-jsh/pom.xml @@ -0,0 +1,37 @@ +<?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-runtime-project</artifactId> + <version>1.7.0-SNAPSHOT</version> + </parent> + <modelVersion>4.0.0</modelVersion> + <packaging>pom</packaging> + + <artifactId>camel-k-loader-jsh-parent</artifactId> + + <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 000000000..70cc5efd0 --- /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 000000000..561a885e9 --- /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 6fde60dfd..abeab4171 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 000000000..5adc8f35c --- /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 afd395ac4..e4fa85a95 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% 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-java/src/main/java/org/apache/camel/k/loader/jsh/quarkus/Application.java index df7cac3ef..f2ff4db57 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% 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-java/src/test/java/org/apache/camel/k/loader/jsh/JavaLoaderTest.java index 65c8794c8..4536205df 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 000000000..fbab5ff82 --- /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-jsh/src/main/java/org/apache/camel/k/loader/jsh/quarkus/JshApplication.java b/itests/camel-k-itests-loader-jsh/src/main/java/org/apache/camel/k/loader/jsh/quarkus/JshApplication.java new file mode 100644 index 000000000..4ddf24cf2 --- /dev/null +++ b/itests/camel-k-itests-loader-jsh/src/main/java/org/apache/camel/k/loader/jsh/quarkus/JshApplication.java @@ -0,0 +1,45 @@ +/* + * 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 javax.enterprise.context.ApplicationScoped; +import javax.inject.Inject; +import javax.json.JsonObject; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +import org.apache.camel.CamelContext; +import org.apache.camel.Consume; +import org.apache.camel.k.loader.support.LoaderSupport; + +@Path("/test") +@ApplicationScoped +public class JshApplication { + @Inject + CamelContext context; + + @POST + @Path("/load-routes/{name}") + @Consume(MediaType.TEXT_PLAIN) + @Produces(MediaType.APPLICATION_JSON) + public JsonObject loadRoutes(@PathParam("name") String name, String code) throws Exception { + 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 000000000..67b30b144 --- /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-jsh/src/test/java/org/apache/camel/k/loader/jsh/JshLoaderTest.java b/itests/camel-k-itests-loader-jsh/src/test/java/org/apache/camel/k/loader/jsh/JshLoaderTest.java new file mode 100644 index 000000000..d2b11ec20 --- /dev/null +++ b/itests/camel-k-itests-loader-jsh/src/test/java/org/apache/camel/k/loader/jsh/JshLoaderTest.java @@ -0,0 +1,58 @@ +/* + * 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.IOException; +import java.io.InputStream; + +import javax.ws.rs.core.MediaType; + +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.util.IOHelper; +import org.junit.jupiter.api.Test; + +//@Disabled +@DisabledOnNativeImage +@QuarkusTest +public class JshLoaderTest { + @Test + public void testLoadRoutes() throws IOException { + String code; + + try (InputStream is = JshLoaderTest.class.getResourceAsStream("/routes.jsh")) { + code = IOHelper.loadText(is); + } + + JsonPath p = RestAssured.given() + .contentType(MediaType.TEXT_PLAIN) + .accept(MediaType.APPLICATION_JSON) + .body(code) + .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("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 000000000..c47a78f30 --- /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 e81778924..3a0d5de75 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 12dfbb4b9..24bf339d2 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> @@ -625,6 +626,21 @@ <artifactId>camel-k-loader-java-deployment</artifactId> <version>${project.version}</version> </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> 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 6b5578b06..d9d5b2b1c 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.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 @@ private 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 bb239a2f1..1baa5e338 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 @@ -216,7 +216,14 @@ private static void processLoaders(CamelCatalogSpec.Builder specBuilder) { 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 d408feb6f..11a2364fb 100644 --- a/support/camel-k-runtime-bom/pom.xml +++ b/support/camel-k-runtime-bom/pom.xml @@ -236,6 +236,16 @@ <artifactId>camel-k-loader-java</artifactId> <version>${project.version}</version> </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>