This is an automated email from the ASF dual-hosted git repository. gnodet pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push: new 12e0af7249f CAMEL-21443: Camel JBang shell command (#16182) 12e0af7249f is described below commit 12e0af7249fe081fcccd74451a008028fe89239c Author: Guillaume Nodet <gno...@gmail.com> AuthorDate: Tue Nov 12 16:51:49 2024 +0100 CAMEL-21443: Camel JBang shell command (#16182) --- .../modules/ROOT/pages/camel-jbang.adoc | 9 ++ dsl/camel-jbang/camel-jbang-core/pom.xml | 15 +++ .../dsl/jbang/core/commands/CamelJBangMain.java | 4 + .../camel/dsl/jbang/core/commands/Shell.java | 102 +++++++++++++++++++++ parent/pom.xml | 1 + 5 files changed, 131 insertions(+) diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc b/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc index 498b68d01cb..741af075c3e 100644 --- a/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc +++ b/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc @@ -110,6 +110,15 @@ To make it permanent, run: echo 'source <(camel completion)' >> ~/.bashrc ---- +== REPL loop + +A simple read-eval-print loop is available, you can launch it with: + +[source, bash] +---- +camel shell +---- + == Creating and running Camel routes You can create a new basic routes with the `init` command. diff --git a/dsl/camel-jbang/camel-jbang-core/pom.xml b/dsl/camel-jbang/camel-jbang-core/pom.xml index c3dfdfe784d..769c54532a2 100644 --- a/dsl/camel-jbang/camel-jbang-core/pom.xml +++ b/dsl/camel-jbang/camel-jbang-core/pom.xml @@ -87,6 +87,16 @@ <artifactId>picocli</artifactId> <version>${picocli-version}</version> </dependency> + <dependency> + <groupId>info.picocli</groupId> + <artifactId>picocli-shell-jline3</artifactId> + <version>${picocli-version}</version> + </dependency> + <dependency> + <groupId>org.jline</groupId> + <artifactId>jline</artifactId> + <version>${jline-version}</version> + </dependency> <dependency> <groupId>com.github.freva</groupId> <artifactId>ascii-table</artifactId> @@ -136,6 +146,11 @@ <version>${jansi-version}</version> </dependency> + <dependency> + <groupId>org.codehaus.plexus</groupId> + <artifactId>plexus-xml</artifactId> + </dependency> + <!-- test dependencies --> <dependency> <groupId>org.apache.camel</groupId> diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java index 2dd90261654..9d470e9e0e7 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java @@ -72,6 +72,7 @@ public class CamelJBangMain implements Callable<Integer> { } commandLine = new CommandLine(main) + .addSubcommand("shell", new CommandLine(new Shell(main))) .addSubcommand("init", new CommandLine(new Init(main))) .addSubcommand("run", new CommandLine(new Run(main))) .addSubcommand("debug", new CommandLine(new Debug(main))) @@ -219,4 +220,7 @@ public class CamelJBangMain implements Callable<Integer> { return this; } + public static CommandLine getCommandLine() { + return commandLine; + } } diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Shell.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Shell.java new file mode 100644 index 00000000000..9f54f5ec451 --- /dev/null +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Shell.java @@ -0,0 +1,102 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.dsl.jbang.core.commands; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.function.Supplier; + +import org.jline.builtins.ConfigurationPath; +import org.jline.console.SystemRegistry; +import org.jline.console.impl.Builtins; +import org.jline.console.impl.SystemRegistryImpl; +import org.jline.keymap.KeyMap; +import org.jline.reader.Binding; +import org.jline.reader.EndOfFileException; +import org.jline.reader.LineReader; +import org.jline.reader.LineReaderBuilder; +import org.jline.reader.MaskingCallback; +import org.jline.reader.Parser; +import org.jline.reader.Reference; +import org.jline.reader.UserInterruptException; +import org.jline.reader.impl.DefaultParser; +import org.jline.terminal.Terminal; +import org.jline.terminal.TerminalBuilder; +import org.jline.widget.TailTipWidgets; +import picocli.CommandLine; +import picocli.shell.jline3.PicocliCommands; + +@CommandLine.Command(name = "shell", + description = "Interactive Camel JBang shell. Hit @|magenta <TAB>|@ to see available commands.", + footer = "Press Ctrl-D to exit.") +public class Shell extends CamelCommand { + + public Shell(CamelJBangMain main) { + super(main); + } + + @Override + public Integer doCall() throws Exception { + Supplier<Path> workDir = () -> Paths.get(System.getProperty("user.dir")); + // set up JLine built-in commands + Builtins builtins = new Builtins(workDir, new ConfigurationPath(workDir.get(), workDir.get()), null); + + PicocliCommands.PicocliCommandsFactory factory = new PicocliCommands.PicocliCommandsFactory(); + PicocliCommands picocliCommands = new PicocliCommands(CamelJBangMain.getCommandLine()); + + Parser parser = new DefaultParser(); + try (Terminal terminal = TerminalBuilder.builder().build()) { + SystemRegistry systemRegistry = new SystemRegistryImpl(parser, terminal, workDir, null); + systemRegistry.setCommandRegistries(builtins, picocliCommands); + systemRegistry.register("help", picocliCommands); + + LineReader reader = LineReaderBuilder.builder() + .terminal(terminal) + .completer(systemRegistry.completer()) + .parser(parser) + .variable(LineReader.LIST_MAX, 50) // max tab completion candidates + .build(); + builtins.setLineReader(reader); + factory.setTerminal(terminal); + TailTipWidgets widgets + = new TailTipWidgets(reader, systemRegistry::commandDescription, 5, TailTipWidgets.TipType.COMPLETER); + widgets.enable(); + KeyMap<Binding> keyMap = reader.getKeyMaps().get("main"); + keyMap.bind(new Reference("tailtip-toggle"), KeyMap.alt("s")); + String prompt = "camel> "; + String rightPrompt = null; + + // start the shell and process input until the user quits with Ctrl-D + String line; + while (true) { + try { + systemRegistry.cleanUp(); + line = reader.readLine(prompt, rightPrompt, (MaskingCallback) null, null); + systemRegistry.execute(line); + } catch (UserInterruptException e) { + // Ignore + } catch (EndOfFileException e) { + break; + } catch (Exception e) { + systemRegistry.trace(e); + } + } + + } + return 0; + } +} diff --git a/parent/pom.xml b/parent/pom.xml index 84de4b85754..eba36e28e8b 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -285,6 +285,7 @@ <jgroups-raft-leveldbjni-version>1.8</jgroups-raft-leveldbjni-version> <jgroups-raft-mapdb-version>1.0.8</jgroups-raft-mapdb-version> <jira-rest-client-api-version>6.0.1</jira-rest-client-api-version> + <jline-version>3.27.1</jline-version> <libthrift-version>0.21.0</libthrift-version> <jodatime2-version>2.13.0</jodatime2-version> <jolokia-version>2.1.1</jolokia-version>