This is an automated email from the ASF dual-hosted git repository.
nanda pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ozone.git
The following commit(s) were added to refs/heads/master by this push:
new 8526f2e81f HDDS-11826. Interactive mode for ozone shell. (#7515)
8526f2e81f is described below
commit 8526f2e81f73b45cc6701ccb750fcb42c8823025
Author: Doroszlai, Attila <[email protected]>
AuthorDate: Fri Dec 6 08:32:34 2024 +0100
HDDS-11826. Interactive mode for ozone shell. (#7515)
---
.../hadoop/hdds/cli/ExtensibleParentCommand.java | 2 +-
.../org/apache/hadoop/hdds/cli/GenericCli.java | 11 ++-
hadoop-hdds/tools/pom.xml | 16 ++--
hadoop-ozone/dist/src/main/license/bin/LICENSE.txt | 2 +
hadoop-ozone/dist/src/main/license/jar-report.txt | 2 +
hadoop-ozone/tools/pom.xml | 27 +++++--
.../reconfig/AbstractReconfigureSubCommand.java | 1 +
.../org/apache/hadoop/ozone/shell/Handler.java | 2 +-
.../org/apache/hadoop/ozone/shell/OzoneRatis.java | 3 +-
.../java/org/apache/hadoop/ozone/shell/REPL.java | 90 ++++++++++++++++++++++
.../java/org/apache/hadoop/ozone/shell/Shell.java | 45 ++++++++++-
pom.xml | 11 +++
12 files changed, 189 insertions(+), 23 deletions(-)
diff --git
a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/cli/ExtensibleParentCommand.java
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/cli/ExtensibleParentCommand.java
index 45bbb44005..d4fde1b75c 100644
---
a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/cli/ExtensibleParentCommand.java
+++
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/cli/ExtensibleParentCommand.java
@@ -42,7 +42,7 @@ public interface ExtensibleParentCommand {
ServiceLoader<?> subcommands =
ServiceLoader.load(parentCommand.subcommandType());
for (Object subcommand : subcommands) {
final CommandLine.Command commandAnnotation =
subcommand.getClass().getAnnotation(CommandLine.Command.class);
- CommandLine subcommandCommandLine = new CommandLine(subcommand);
+ CommandLine subcommandCommandLine = new CommandLine(subcommand,
cli.getFactory());
cli.addSubcommand(commandAnnotation.name(), subcommandCommandLine);
}
}
diff --git
a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/cli/GenericCli.java
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/cli/GenericCli.java
index a8ff931b23..a64c4bd84a 100644
---
a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/cli/GenericCli.java
+++
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/cli/GenericCli.java
@@ -56,15 +56,20 @@ public class GenericCli implements Callable<Void>,
GenericParentCommand {
}
public GenericCli(Class<?> type) {
- cmd = new CommandLine(this);
+ this(type, CommandLine.defaultFactory());
+ }
+
+ public GenericCli(Class<?> type, CommandLine.IFactory factory) {
+ cmd = new CommandLine(this, factory);
cmd.setExecutionExceptionHandler((ex, commandLine, parseResult) -> {
printError(ex);
return EXECUTION_ERROR_EXIT_CODE;
});
if (type != null) {
- addSubcommands(getCmd(), type);
+ addSubcommands(cmd, type);
}
+
ExtensibleParentCommand.addSubcommands(cmd);
}
@@ -75,7 +80,7 @@ public class GenericCli implements Callable<Void>,
GenericParentCommand {
if (subcommand.getParentType().equals(type)) {
final Command commandAnnotation =
subcommand.getClass().getAnnotation(Command.class);
- CommandLine subcommandCommandLine = new CommandLine(subcommand);
+ CommandLine subcommandCommandLine = new CommandLine(subcommand,
cli.getFactory());
addSubcommands(subcommandCommandLine, subcommand.getClass());
cli.addSubcommand(commandAnnotation.name(), subcommandCommandLine);
}
diff --git a/hadoop-hdds/tools/pom.xml b/hadoop-hdds/tools/pom.xml
index 583c801bcd..5b77f394c9 100644
--- a/hadoop-hdds/tools/pom.xml
+++ b/hadoop-hdds/tools/pom.xml
@@ -179,20 +179,20 @@ https://maven.apache.org/xsd/maven-4.0.0.xsd">
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
- <path>
- <groupId>org.apache.ozone</groupId>
- <artifactId>hdds-config</artifactId>
- <version>${hdds.version}</version>
- </path>
<path>
<groupId>org.kohsuke.metainf-services</groupId>
<artifactId>metainf-services</artifactId>
<version>${metainf-services.version}</version>
</path>
+ <path>
+ <groupId>info.picocli</groupId>
+ <artifactId>picocli-codegen</artifactId>
+ <version>${picocli.version}</version>
+ </path>
</annotationProcessorPaths>
<annotationProcessors>
-
<annotationProcessor>org.apache.hadoop.hdds.conf.ConfigFileGenerator</annotationProcessor>
<annotationProcessor>org.kohsuke.metainf_services.AnnotationProcessorImpl</annotationProcessor>
+
<annotationProcessor>picocli.codegen.aot.graalvm.processor.NativeImageConfigGeneratorProcessor</annotationProcessor>
</annotationProcessors>
</configuration>
</plugin>
@@ -207,8 +207,10 @@ https://maven.apache.org/xsd/maven-4.0.0.xsd">
<restrictImports>
<reason>Only selected annotation processors are enabled, see
configuration of maven-compiler-plugin.</reason>
<bannedImports>
-
<bannedImport>org.apache.hadoop.ozone.om.request.validation.RequestFeatureValidator</bannedImport>
+
<bannedImport>org.apache.hadoop.hdds.conf.Config</bannedImport>
+
<bannedImport>org.apache.hadoop.hdds.conf.ConfigGroup</bannedImport>
<bannedImport>org.apache.hadoop.hdds.scm.metadata.Replicate</bannedImport>
+
<bannedImport>org.apache.hadoop.ozone.om.request.validation.RequestFeatureValidator</bannedImport>
</bannedImports>
</restrictImports>
</rules>
diff --git a/hadoop-ozone/dist/src/main/license/bin/LICENSE.txt
b/hadoop-ozone/dist/src/main/license/bin/LICENSE.txt
index 1c70fa0401..b291afc568 100644
--- a/hadoop-ozone/dist/src/main/license/bin/LICENSE.txt
+++ b/hadoop-ozone/dist/src/main/license/bin/LICENSE.txt
@@ -315,6 +315,7 @@ Apache License 2.0
commons-validator:commons-validator
commons-fileupload:commons-fileupload
info.picocli:picocli
+ info.picocli:picocli-shell-jline3
io.dropwizard.metrics:metrics-core
io.grpc:grpc-api
io.grpc:grpc-context
@@ -479,6 +480,7 @@ BSD 3-Clause
com.google.re2j:re2j
com.jcraft:jsch
com.thoughtworks.paranamer:paranamer
+ org.jline:jline3
org.ow2.asm:asm
org.ow2.asm:asm-analysis
org.ow2.asm:asm-commons
diff --git a/hadoop-ozone/dist/src/main/license/jar-report.txt
b/hadoop-ozone/dist/src/main/license/jar-report.txt
index e6d9a1d2a2..be48c1d1fe 100644
--- a/hadoop-ozone/dist/src/main/license/jar-report.txt
+++ b/hadoop-ozone/dist/src/main/license/jar-report.txt
@@ -151,6 +151,7 @@ share/ozone/lib/jgrapht-core.jar
share/ozone/lib/jgrapht-ext.jar
share/ozone/lib/jgraphx.jar
share/ozone/lib/jheaps.jar
+share/ozone/lib/jline.jar
share/ozone/lib/jmespath-java.jar
share/ozone/lib/jna.jar
share/ozone/lib/jna-platform.jar
@@ -236,6 +237,7 @@ share/ozone/lib/ozone-s3gateway.jar
share/ozone/lib/ozone-tools.jar
share/ozone/lib/perfmark-api.jar
share/ozone/lib/picocli.jar
+share/ozone/lib/picocli-shell-jline3.jar
share/ozone/lib/protobuf-java.jar
share/ozone/lib/protobuf-java.jar
share/ozone/lib/protobuf-java-util.jar
diff --git a/hadoop-ozone/tools/pom.xml b/hadoop-ozone/tools/pom.xml
index 9244081225..8ea8ded01c 100644
--- a/hadoop-ozone/tools/pom.xml
+++ b/hadoop-ozone/tools/pom.xml
@@ -173,6 +173,14 @@ https://maven.apache.org/xsd/maven-4.0.0.xsd">
<groupId>info.picocli</groupId>
<artifactId>picocli</artifactId>
</dependency>
+ <dependency>
+ <groupId>info.picocli</groupId>
+ <artifactId>picocli-shell-jline3</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jline</groupId>
+ <artifactId>jline</artifactId>
+ </dependency>
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
@@ -275,21 +283,24 @@ https://maven.apache.org/xsd/maven-4.0.0.xsd">
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
- <path>
- <groupId>org.apache.ozone</groupId>
- <artifactId>hdds-config</artifactId>
- <version>${hdds.version}</version>
- </path>
<path>
<groupId>org.kohsuke.metainf-services</groupId>
<artifactId>metainf-services</artifactId>
<version>${metainf-services.version}</version>
</path>
+ <path>
+ <groupId>info.picocli</groupId>
+ <artifactId>picocli-codegen</artifactId>
+ <version>${picocli.version}</version>
+ </path>
</annotationProcessorPaths>
<annotationProcessors>
-
<annotationProcessor>org.apache.hadoop.hdds.conf.ConfigFileGenerator</annotationProcessor>
<annotationProcessor>org.kohsuke.metainf_services.AnnotationProcessorImpl</annotationProcessor>
+
<annotationProcessor>picocli.codegen.aot.graalvm.processor.NativeImageConfigGeneratorProcessor</annotationProcessor>
</annotationProcessors>
+ <compilerArgs>
+ <arg>-Aproject=${project.groupId}/${project.artifactId}</arg>
+ </compilerArgs>
</configuration>
</plugin>
<plugin>
@@ -303,8 +314,10 @@ https://maven.apache.org/xsd/maven-4.0.0.xsd">
<restrictImports>
<reason>Only selected annotation processors are enabled, see
configuration of maven-compiler-plugin.</reason>
<bannedImports>
-
<bannedImport>org.apache.hadoop.ozone.om.request.validation.RequestFeatureValidator</bannedImport>
+
<bannedImport>org.apache.hadoop.hdds.conf.Config</bannedImport>
+
<bannedImport>org.apache.hadoop.hdds.conf.ConfigGroup</bannedImport>
<bannedImport>org.apache.hadoop.hdds.scm.metadata.Replicate</bannedImport>
+
<bannedImport>org.apache.hadoop.ozone.om.request.validation.RequestFeatureValidator</bannedImport>
</bannedImports>
</restrictImports>
</rules>
diff --git
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/admin/reconfig/AbstractReconfigureSubCommand.java
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/admin/reconfig/AbstractReconfigureSubCommand.java
index 0a2666d30e..b8ea45898d 100644
---
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/admin/reconfig/AbstractReconfigureSubCommand.java
+++
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/admin/reconfig/AbstractReconfigureSubCommand.java
@@ -28,6 +28,7 @@ import java.util.concurrent.Executors;
/**
* An abstract Class use to ReconfigureSubCommand.
*/
[email protected]
public abstract class AbstractReconfigureSubCommand implements Callable<Void> {
@CommandLine.ParentCommand
private ReconfigureCommands parent;
diff --git
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/Handler.java
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/Handler.java
index 92484936f2..d1755a6880 100644
---
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/Handler.java
+++
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/Handler.java
@@ -111,7 +111,7 @@ public abstract class Handler implements Callable<Void> {
if (!enabled) {
err().printf("Error: '%s' operation works only when security is " +
"enabled. To enable security set ozone.security.enabled to " +
- "true.%n", spec.qualifiedName());
+ "true.%n", spec.qualifiedName().trim());
}
return enabled;
}
diff --git
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/OzoneRatis.java
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/OzoneRatis.java
index 5bc9826806..f471911c1f 100644
---
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/OzoneRatis.java
+++
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/OzoneRatis.java
@@ -17,6 +17,7 @@
*/
package org.apache.hadoop.ozone.shell;
+import org.apache.hadoop.hdds.cli.GenericCli;
import org.apache.hadoop.hdds.cli.HddsVersionProvider;
import org.apache.hadoop.hdds.tracing.TracingUtil;
import org.apache.ratis.shell.cli.sh.RatisShell;
@@ -30,7 +31,7 @@ import picocli.CommandLine;
description = "Shell for running Ratis commands",
versionProvider = HddsVersionProvider.class,
mixinStandardHelpOptions = true)
-public class OzoneRatis extends Shell {
+public class OzoneRatis extends GenericCli {
public OzoneRatis() {
super(OzoneRatis.class);
diff --git
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/REPL.java
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/REPL.java
new file mode 100644
index 0000000000..1484884634
--- /dev/null
+++ b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/REPL.java
@@ -0,0 +1,90 @@
+/*
+ * 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.hadoop.ozone.shell;
+
+import org.jline.console.SystemRegistry;
+import org.jline.console.impl.SystemRegistryImpl;
+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.UserInterruptException;
+import org.jline.reader.impl.DefaultParser;
+import org.jline.terminal.Terminal;
+import org.jline.terminal.TerminalBuilder;
+import org.jline.widget.TailTipWidgets;
+import org.jline.widget.TailTipWidgets.TipType;
+import picocli.CommandLine;
+import picocli.shell.jline3.PicocliCommands;
+import picocli.shell.jline3.PicocliCommands.PicocliCommandsFactory;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.function.Supplier;
+
+/**
+ * Interactive shell for Ozone commands.
+ * (REPL = Read-Eval-Print Loop)
+ */
+class REPL {
+
+ REPL(Shell shell, CommandLine cmd, PicocliCommandsFactory factory) {
+ Parser parser = new DefaultParser();
+ Supplier<Path> workDir = () -> Paths.get(System.getProperty("user.dir"));
+ TerminalBuilder terminalBuilder = TerminalBuilder.builder()
+ .dumb(true);
+ try (Terminal terminal = terminalBuilder.build()) {
+ factory.setTerminal(terminal);
+
+ PicocliCommands picocliCommands = new PicocliCommands(cmd);
+ picocliCommands.name(shell.name());
+ SystemRegistry registry = new SystemRegistryImpl(parser, terminal,
workDir, null);
+ registry.setCommandRegistries(picocliCommands);
+ registry.register("help", picocliCommands);
+
+ LineReader reader = LineReaderBuilder.builder()
+ .terminal(terminal)
+ .completer(registry.completer())
+ .parser(parser)
+ .variable(LineReader.LIST_MAX, 50)
+ .build();
+
+ TailTipWidgets widgets = new TailTipWidgets(reader,
registry::commandDescription, 5, TipType.COMPLETER);
+ widgets.enable();
+
+ String prompt = shell.prompt() + "> ";
+
+ while (true) {
+ try {
+ registry.cleanUp();
+ String line = reader.readLine(prompt, null, (MaskingCallback) null,
null);
+ registry.execute(line);
+ } catch (UserInterruptException ignored) {
+ // ignore
+ } catch (EndOfFileException e) {
+ return;
+ } catch (Exception e) {
+ registry.trace(e);
+ }
+ }
+ } catch (Exception e) {
+ shell.printError(e);
+ }
+ }
+}
diff --git
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/Shell.java
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/Shell.java
index 97e160651b..44893f78e6 100644
--- a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/Shell.java
+++ b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/Shell.java
@@ -20,6 +20,8 @@ package org.apache.hadoop.ozone.shell;
import org.apache.hadoop.hdds.cli.GenericCli;
import org.apache.hadoop.ozone.om.exceptions.OMException;
+import picocli.CommandLine;
+import picocli.shell.jline3.PicocliCommands.PicocliCommandsFactory;
/**
* Ozone user interface commands.
@@ -27,6 +29,7 @@ import org.apache.hadoop.ozone.om.exceptions.OMException;
* This class uses dispatch method to make calls
* to appropriate handlers that execute the ozone functions.
*/
[email protected]
public abstract class Shell extends GenericCli {
public static final String OZONE_URI_DESCRIPTION =
@@ -46,15 +49,52 @@ public abstract class Shell extends GenericCli {
"Any unspecified information will be identified from\n" +
"the config files.\n";
+ private String name;
+
+ @CommandLine.Spec
+ private CommandLine.Model.CommandSpec spec;
+
+ @CommandLine.Option(names = { "--interactive" }, description = "Run in
interactive mode")
+ private boolean interactive;
+
public Shell() {
+ this(null);
}
public Shell(Class<?> type) {
- super(type);
+ super(type, new PicocliCommandsFactory());
+ }
+
+ public String name() {
+ return name;
+ }
+
+ // override if custom prompt is needed
+ public String prompt() {
+ return name();
+ }
+
+ @Override
+ public void run(String[] argv) {
+ name = spec.name();
+
+ try {
+ // parse args to check if interactive mode is requested
+ getCmd().parseArgs(argv);
+ } catch (Exception ignored) {
+ // failure will be reported by regular, non-interactive run
+ }
+
+ if (interactive) {
+ spec.name(""); // use short name (e.g. "token get" instead of "ozone sh
token get")
+ new REPL(this, getCmd(), (PicocliCommandsFactory) getCmd().getFactory());
+ } else {
+ super.run(argv);
+ }
}
@Override
- protected void printError(Throwable errorArg) {
+ public void printError(Throwable errorArg) {
OMException omException = null;
if (errorArg instanceof OMException) {
@@ -77,4 +117,3 @@ public abstract class Shell extends GenericCli {
}
}
}
-
diff --git a/pom.xml b/pom.xml
index 16023cb28b..869afebf49 100644
--- a/pom.xml
+++ b/pom.xml
@@ -301,6 +301,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xs
<restrict-imports.enforcer.version>2.6.0</restrict-imports.enforcer.version>
<jgrapht.version>1.4.0</jgrapht.version>
<jgraphx.version>3.9.12</jgraphx.version>
+ <jline.version>3.23.0</jline.version> <!-- should match version via
picocli-shell-jline3 -->
<spring.version>5.3.39</spring.version>
<jooq.version>3.11.10</jooq.version>
@@ -323,6 +324,16 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xs
<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>org.apache.derby</groupId>
<artifactId>derby</artifactId>
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]