blob: e2679045e5e1f117cec3b2acb430511df2bf3578 [file] [log] [blame]
/*
* 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.ratis.shell.cli;
import org.apache.commons.cli.CommandLine;
import org.apache.ratis.shell.cli.sh.command.Context;
import org.apache.ratis.thirdparty.com.google.common.io.Closer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Closeable;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
/**
* Abstract class for handling command line inputs.
*/
public abstract class AbstractShell implements Closeable {
private static final Logger LOG = LoggerFactory.getLogger(AbstractShell.class);
private final Map<String, Command> mCommands;
private final Closer closer;
/**
* Creates a new instance of {@link AbstractShell}.
*/
protected AbstractShell(Context context) {
closer = Closer.create();
mCommands = loadCommands(context);
// Register all loaded commands under closer.
mCommands.values().forEach(closer::register);
}
/**
* Handles the specified shell command request, displaying usage if the command format is invalid.
*
* @param argv [] Array of arguments given by the user's input from the terminal
* @return 0 if command is successful, -1 if an error occurred
*/
public int run(String... argv) {
if (argv.length == 0) {
printUsage();
return -1;
}
// Sanity check on the number of arguments
String cmd = argv[0];
Command command = mCommands.get(cmd);
if (command == null) {
// Unknown command (we didn't find the cmd in our dict)
System.err.printf("%s is an unknown command.%n", cmd);
printUsage();
return -1;
}
// Find the inner-most command and its argument line.
CommandLine cmdline;
try {
String[] currArgs = Arrays.copyOf(argv, argv.length);
while (command.hasSubCommand()) {
if (currArgs.length < 2) {
throw new IllegalArgumentException("No sub-command is specified");
}
if (!command.getSubCommands().containsKey(currArgs[1])) {
throw new IllegalArgumentException("Unknown sub-command: " + currArgs[1]);
}
command = command.getSubCommands().get(currArgs[1]);
currArgs = Arrays.copyOfRange(currArgs, 1, currArgs.length);
}
currArgs = Arrays.copyOfRange(currArgs, 1, currArgs.length);
cmdline = command.parseAndValidateArgs(currArgs);
} catch (IllegalArgumentException e) {
// It outputs a prompt message when passing wrong args to CLI
System.out.println(e.getMessage());
System.out.println("Usage: " + command.getUsage());
System.out.println(command.getDescription());
LOG.error("Invalid arguments for command {}:", command.getCommandName(), e);
return -1;
}
// Handle the command
try {
return command.run(cmdline);
} catch (Exception e) {
System.out.println(e.getMessage());
LOG.error("Error running" + Arrays.stream(argv).reduce("", (a, b) -> a + " " + b), e);
return -1;
}
}
/**
* @return all commands provided by this shell
*/
public Collection<Command> getCommands() {
return mCommands.values();
}
@Override
public void close() throws IOException {
closer.close();
}
/**
* @return name of the shell
*/
protected abstract String getShellName();
/**
* Map structure: Command name => {@link Command} instance.
*
* @return a set of commands which can be executed under this shell
*/
protected abstract Map<String, Command> loadCommands(Context context);
protected Closer getCloser() {
return closer;
}
/**
* Prints usage for all commands.
*/
protected void printUsage() {
System.out.println("Usage: ratis " + getShellName() + " [generic options]");
SortedSet<String> sortedCmds = new TreeSet<>(mCommands.keySet());
for (String cmd : sortedCmds) {
System.out.format("%-60s%n", "\t [" + mCommands.get(cmd).getUsage() + "]");
}
}
}