Skip to content

Commit 8526f2e

Browse files
authored
HDDS-11826. Interactive mode for ozone shell. (#7515)
1 parent fc63710 commit 8526f2e

12 files changed

Lines changed: 189 additions & 23 deletions

File tree

hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/cli/ExtensibleParentCommand.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ static void addSubcommands(CommandLine cli) {
4242
ServiceLoader<?> subcommands = ServiceLoader.load(parentCommand.subcommandType());
4343
for (Object subcommand : subcommands) {
4444
final CommandLine.Command commandAnnotation = subcommand.getClass().getAnnotation(CommandLine.Command.class);
45-
CommandLine subcommandCommandLine = new CommandLine(subcommand);
45+
CommandLine subcommandCommandLine = new CommandLine(subcommand, cli.getFactory());
4646
cli.addSubcommand(commandAnnotation.name(), subcommandCommandLine);
4747
}
4848
}

hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/cli/GenericCli.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,15 +56,20 @@ public GenericCli() {
5656
}
5757

5858
public GenericCli(Class<?> type) {
59-
cmd = new CommandLine(this);
59+
this(type, CommandLine.defaultFactory());
60+
}
61+
62+
public GenericCli(Class<?> type, CommandLine.IFactory factory) {
63+
cmd = new CommandLine(this, factory);
6064
cmd.setExecutionExceptionHandler((ex, commandLine, parseResult) -> {
6165
printError(ex);
6266
return EXECUTION_ERROR_EXIT_CODE;
6367
});
6468

6569
if (type != null) {
66-
addSubcommands(getCmd(), type);
70+
addSubcommands(cmd, type);
6771
}
72+
6873
ExtensibleParentCommand.addSubcommands(cmd);
6974
}
7075

@@ -75,7 +80,7 @@ private void addSubcommands(CommandLine cli, Class<?> type) {
7580
if (subcommand.getParentType().equals(type)) {
7681
final Command commandAnnotation =
7782
subcommand.getClass().getAnnotation(Command.class);
78-
CommandLine subcommandCommandLine = new CommandLine(subcommand);
83+
CommandLine subcommandCommandLine = new CommandLine(subcommand, cli.getFactory());
7984
addSubcommands(subcommandCommandLine, subcommand.getClass());
8085
cli.addSubcommand(commandAnnotation.name(), subcommandCommandLine);
8186
}

hadoop-hdds/tools/pom.xml

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -179,20 +179,20 @@ https://maven.apache.org/xsd/maven-4.0.0.xsd">
179179
<artifactId>maven-compiler-plugin</artifactId>
180180
<configuration>
181181
<annotationProcessorPaths>
182-
<path>
183-
<groupId>org.apache.ozone</groupId>
184-
<artifactId>hdds-config</artifactId>
185-
<version>${hdds.version}</version>
186-
</path>
187182
<path>
188183
<groupId>org.kohsuke.metainf-services</groupId>
189184
<artifactId>metainf-services</artifactId>
190185
<version>${metainf-services.version}</version>
191186
</path>
187+
<path>
188+
<groupId>info.picocli</groupId>
189+
<artifactId>picocli-codegen</artifactId>
190+
<version>${picocli.version}</version>
191+
</path>
192192
</annotationProcessorPaths>
193193
<annotationProcessors>
194-
<annotationProcessor>org.apache.hadoop.hdds.conf.ConfigFileGenerator</annotationProcessor>
195194
<annotationProcessor>org.kohsuke.metainf_services.AnnotationProcessorImpl</annotationProcessor>
195+
<annotationProcessor>picocli.codegen.aot.graalvm.processor.NativeImageConfigGeneratorProcessor</annotationProcessor>
196196
</annotationProcessors>
197197
</configuration>
198198
</plugin>
@@ -207,8 +207,10 @@ https://maven.apache.org/xsd/maven-4.0.0.xsd">
207207
<restrictImports>
208208
<reason>Only selected annotation processors are enabled, see configuration of maven-compiler-plugin.</reason>
209209
<bannedImports>
210-
<bannedImport>org.apache.hadoop.ozone.om.request.validation.RequestFeatureValidator</bannedImport>
210+
<bannedImport>org.apache.hadoop.hdds.conf.Config</bannedImport>
211+
<bannedImport>org.apache.hadoop.hdds.conf.ConfigGroup</bannedImport>
211212
<bannedImport>org.apache.hadoop.hdds.scm.metadata.Replicate</bannedImport>
213+
<bannedImport>org.apache.hadoop.ozone.om.request.validation.RequestFeatureValidator</bannedImport>
212214
</bannedImports>
213215
</restrictImports>
214216
</rules>

hadoop-ozone/dist/src/main/license/bin/LICENSE.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,7 @@ Apache License 2.0
315315
commons-validator:commons-validator
316316
commons-fileupload:commons-fileupload
317317
info.picocli:picocli
318+
info.picocli:picocli-shell-jline3
318319
io.dropwizard.metrics:metrics-core
319320
io.grpc:grpc-api
320321
io.grpc:grpc-context
@@ -479,6 +480,7 @@ BSD 3-Clause
479480
com.google.re2j:re2j
480481
com.jcraft:jsch
481482
com.thoughtworks.paranamer:paranamer
483+
org.jline:jline3
482484
org.ow2.asm:asm
483485
org.ow2.asm:asm-analysis
484486
org.ow2.asm:asm-commons

hadoop-ozone/dist/src/main/license/jar-report.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ share/ozone/lib/jgrapht-core.jar
151151
share/ozone/lib/jgrapht-ext.jar
152152
share/ozone/lib/jgraphx.jar
153153
share/ozone/lib/jheaps.jar
154+
share/ozone/lib/jline.jar
154155
share/ozone/lib/jmespath-java.jar
155156
share/ozone/lib/jna.jar
156157
share/ozone/lib/jna-platform.jar
@@ -236,6 +237,7 @@ share/ozone/lib/ozone-s3gateway.jar
236237
share/ozone/lib/ozone-tools.jar
237238
share/ozone/lib/perfmark-api.jar
238239
share/ozone/lib/picocli.jar
240+
share/ozone/lib/picocli-shell-jline3.jar
239241
share/ozone/lib/protobuf-java.jar
240242
share/ozone/lib/protobuf-java.jar
241243
share/ozone/lib/protobuf-java-util.jar

hadoop-ozone/tools/pom.xml

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,14 @@ https://maven.apache.org/xsd/maven-4.0.0.xsd">
173173
<groupId>info.picocli</groupId>
174174
<artifactId>picocli</artifactId>
175175
</dependency>
176+
<dependency>
177+
<groupId>info.picocli</groupId>
178+
<artifactId>picocli-shell-jline3</artifactId>
179+
</dependency>
180+
<dependency>
181+
<groupId>org.jline</groupId>
182+
<artifactId>jline</artifactId>
183+
</dependency>
176184
<dependency>
177185
<groupId>jakarta.xml.bind</groupId>
178186
<artifactId>jakarta.xml.bind-api</artifactId>
@@ -275,21 +283,24 @@ https://maven.apache.org/xsd/maven-4.0.0.xsd">
275283
<artifactId>maven-compiler-plugin</artifactId>
276284
<configuration>
277285
<annotationProcessorPaths>
278-
<path>
279-
<groupId>org.apache.ozone</groupId>
280-
<artifactId>hdds-config</artifactId>
281-
<version>${hdds.version}</version>
282-
</path>
283286
<path>
284287
<groupId>org.kohsuke.metainf-services</groupId>
285288
<artifactId>metainf-services</artifactId>
286289
<version>${metainf-services.version}</version>
287290
</path>
291+
<path>
292+
<groupId>info.picocli</groupId>
293+
<artifactId>picocli-codegen</artifactId>
294+
<version>${picocli.version}</version>
295+
</path>
288296
</annotationProcessorPaths>
289297
<annotationProcessors>
290-
<annotationProcessor>org.apache.hadoop.hdds.conf.ConfigFileGenerator</annotationProcessor>
291298
<annotationProcessor>org.kohsuke.metainf_services.AnnotationProcessorImpl</annotationProcessor>
299+
<annotationProcessor>picocli.codegen.aot.graalvm.processor.NativeImageConfigGeneratorProcessor</annotationProcessor>
292300
</annotationProcessors>
301+
<compilerArgs>
302+
<arg>-Aproject=${project.groupId}/${project.artifactId}</arg>
303+
</compilerArgs>
293304
</configuration>
294305
</plugin>
295306
<plugin>
@@ -303,8 +314,10 @@ https://maven.apache.org/xsd/maven-4.0.0.xsd">
303314
<restrictImports>
304315
<reason>Only selected annotation processors are enabled, see configuration of maven-compiler-plugin.</reason>
305316
<bannedImports>
306-
<bannedImport>org.apache.hadoop.ozone.om.request.validation.RequestFeatureValidator</bannedImport>
317+
<bannedImport>org.apache.hadoop.hdds.conf.Config</bannedImport>
318+
<bannedImport>org.apache.hadoop.hdds.conf.ConfigGroup</bannedImport>
307319
<bannedImport>org.apache.hadoop.hdds.scm.metadata.Replicate</bannedImport>
320+
<bannedImport>org.apache.hadoop.ozone.om.request.validation.RequestFeatureValidator</bannedImport>
308321
</bannedImports>
309322
</restrictImports>
310323
</rules>

hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/admin/reconfig/AbstractReconfigureSubCommand.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
/**
2929
* An abstract Class use to ReconfigureSubCommand.
3030
*/
31+
@CommandLine.Command
3132
public abstract class AbstractReconfigureSubCommand implements Callable<Void> {
3233
@CommandLine.ParentCommand
3334
private ReconfigureCommands parent;

hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/Handler.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ protected boolean securityEnabled() {
111111
if (!enabled) {
112112
err().printf("Error: '%s' operation works only when security is " +
113113
"enabled. To enable security set ozone.security.enabled to " +
114-
"true.%n", spec.qualifiedName());
114+
"true.%n", spec.qualifiedName().trim());
115115
}
116116
return enabled;
117117
}

hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/OzoneRatis.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
*/
1818
package org.apache.hadoop.ozone.shell;
1919

20+
import org.apache.hadoop.hdds.cli.GenericCli;
2021
import org.apache.hadoop.hdds.cli.HddsVersionProvider;
2122
import org.apache.hadoop.hdds.tracing.TracingUtil;
2223
import org.apache.ratis.shell.cli.sh.RatisShell;
@@ -30,7 +31,7 @@
3031
description = "Shell for running Ratis commands",
3132
versionProvider = HddsVersionProvider.class,
3233
mixinStandardHelpOptions = true)
33-
public class OzoneRatis extends Shell {
34+
public class OzoneRatis extends GenericCli {
3435

3536
public OzoneRatis() {
3637
super(OzoneRatis.class);
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package org.apache.hadoop.ozone.shell;
19+
20+
import org.jline.console.SystemRegistry;
21+
import org.jline.console.impl.SystemRegistryImpl;
22+
import org.jline.reader.EndOfFileException;
23+
import org.jline.reader.LineReader;
24+
import org.jline.reader.LineReaderBuilder;
25+
import org.jline.reader.MaskingCallback;
26+
import org.jline.reader.Parser;
27+
import org.jline.reader.UserInterruptException;
28+
import org.jline.reader.impl.DefaultParser;
29+
import org.jline.terminal.Terminal;
30+
import org.jline.terminal.TerminalBuilder;
31+
import org.jline.widget.TailTipWidgets;
32+
import org.jline.widget.TailTipWidgets.TipType;
33+
import picocli.CommandLine;
34+
import picocli.shell.jline3.PicocliCommands;
35+
import picocli.shell.jline3.PicocliCommands.PicocliCommandsFactory;
36+
37+
import java.nio.file.Path;
38+
import java.nio.file.Paths;
39+
import java.util.function.Supplier;
40+
41+
/**
42+
* Interactive shell for Ozone commands.
43+
* (REPL = Read-Eval-Print Loop)
44+
*/
45+
class REPL {
46+
47+
REPL(Shell shell, CommandLine cmd, PicocliCommandsFactory factory) {
48+
Parser parser = new DefaultParser();
49+
Supplier<Path> workDir = () -> Paths.get(System.getProperty("user.dir"));
50+
TerminalBuilder terminalBuilder = TerminalBuilder.builder()
51+
.dumb(true);
52+
try (Terminal terminal = terminalBuilder.build()) {
53+
factory.setTerminal(terminal);
54+
55+
PicocliCommands picocliCommands = new PicocliCommands(cmd);
56+
picocliCommands.name(shell.name());
57+
SystemRegistry registry = new SystemRegistryImpl(parser, terminal, workDir, null);
58+
registry.setCommandRegistries(picocliCommands);
59+
registry.register("help", picocliCommands);
60+
61+
LineReader reader = LineReaderBuilder.builder()
62+
.terminal(terminal)
63+
.completer(registry.completer())
64+
.parser(parser)
65+
.variable(LineReader.LIST_MAX, 50)
66+
.build();
67+
68+
TailTipWidgets widgets = new TailTipWidgets(reader, registry::commandDescription, 5, TipType.COMPLETER);
69+
widgets.enable();
70+
71+
String prompt = shell.prompt() + "> ";
72+
73+
while (true) {
74+
try {
75+
registry.cleanUp();
76+
String line = reader.readLine(prompt, null, (MaskingCallback) null, null);
77+
registry.execute(line);
78+
} catch (UserInterruptException ignored) {
79+
// ignore
80+
} catch (EndOfFileException e) {
81+
return;
82+
} catch (Exception e) {
83+
registry.trace(e);
84+
}
85+
}
86+
} catch (Exception e) {
87+
shell.printError(e);
88+
}
89+
}
90+
}

0 commit comments

Comments
 (0)