/*
* Copyright © 2012-2014 Cask Data, Inc.
*
* Licensed 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 co.cask.cdap.shell;
import co.cask.cdap.client.config.ClientConfig;
import co.cask.cdap.shell.command.CallCommandSet;
import co.cask.cdap.shell.command.ConnectCommand;
import co.cask.cdap.shell.command.CreateCommandSet;
import co.cask.cdap.shell.command.DeleteCommandSet;
import co.cask.cdap.shell.command.DeployCommandSet;
import co.cask.cdap.shell.command.DescribeCommandSet;
import co.cask.cdap.shell.command.ExecuteQueryCommand;
import co.cask.cdap.shell.command.ExitCommand;
import co.cask.cdap.shell.command.GetCommandSet;
import co.cask.cdap.shell.command.HelpCommand;
import co.cask.cdap.shell.command.ListCommandSet;
import co.cask.cdap.shell.command.SendCommandSet;
import co.cask.cdap.shell.command.SetCommandSet;
import co.cask.cdap.shell.command.StartProgramCommandSet;
import co.cask.cdap.shell.command.StopProgramCommandSet;
import co.cask.cdap.shell.command.TruncateCommandSet;
import co.cask.cdap.shell.command.VersionCommand;
import co.cask.cdap.shell.exception.InvalidCommandException;
import com.google.common.base.Objects;
import com.google.common.base.Splitter;
import com.google.common.base.Supplier;
import com.google.common.collect.Iterables;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import jline.console.ConsoleReader;
import jline.console.UserInterruptException;
import jline.console.completer.Completer;
import java.io.IOException;
import java.io.PrintStream;
import java.net.URISyntaxException;
import javax.net.ssl.SSLHandshakeException;
/**
* Main class for the CDAP CLI.
*/
public class CLIMain {
private final CommandSet commands;
private final CLIConfig cliConfig;
private final HelpCommand helpCommand;
private final ConsoleReader reader;
public CLIMain(final CLIConfig cliConfig) throws URISyntaxException, IOException {
this.reader = new ConsoleReader();
this.cliConfig = cliConfig;
this.cliConfig.addHostnameChangeListener(new CLIConfig.HostnameChangeListener() {
@Override
public void onHostnameChanged(String newHostname) {
reader.setPrompt("cdap (" + cliConfig.getURI() + ")> ");
}
});
this.helpCommand = new HelpCommand(new Supplier<CommandSet>() {
@Override
public CommandSet get() {
return getCommands();
}
}, cliConfig);
Injector injector = Guice.createInjector(
new AbstractModule() {
@Override
protected void configure() {
bind(CLIConfig.class).toInstance(cliConfig);
bind(ClientConfig.class).toInstance(cliConfig.getClientConfig());
}
}
);
this.commands = CommandSet.builder(null)
.addCommand(helpCommand)
.addCommand(injector.getInstance(ConnectCommand.class))
.addCommand(injector.getInstance(VersionCommand.class))
.addCommand(injector.getInstance(ExitCommand.class))
.addCommand(injector.getInstance(CallCommandSet.class))
.addCommand(injector.getInstance(CreateCommandSet.class))
.addCommand(injector.getInstance(DeleteCommandSet.class))
.addCommand(injector.getInstance(DeployCommandSet.class))
.addCommand(injector.getInstance(DescribeCommandSet.class))
.addCommand(injector.getInstance(ExecuteQueryCommand.class))
.addCommand(injector.getInstance(GetCommandSet.class))
.addCommand(injector.getInstance(ListCommandSet.class))
.addCommand(injector.getInstance(SendCommandSet.class))
.addCommand(injector.getInstance(SetCommandSet.class))
.addCommand(injector.getInstance(StartProgramCommandSet.class))
.addCommand(injector.getInstance(StopProgramCommandSet.class))
.addCommand(injector.getInstance(TruncateCommandSet.class))
.build();
}
/**
* Starts shell mode, which provides a shell to enter multiple commands and use autocompletion.
*
* @param output {@link PrintStream} to write to
* @throws Exception
*/
public void startShellMode(PrintStream output) throws Exception {
this.reader.setPrompt("cdap (" + cliConfig.getURI() + ")> ");
this.reader.setHandleUserInterrupt(true);
for (Completer completer : commands.getCompleters(null)) {
reader.addCompleter(completer);
}
while (true) {
String line;
try {
line = reader.readLine();
} catch (UserInterruptException e) {
continue;
}
if (line == null) {
output.println();
break;
}
if (line.length() > 0) {
String command = line.trim();
String[] commandArgs = Iterables.toArray(Splitter.on(" ").split(command), String.class);
try {
processArgs(commandArgs, output);
} catch (InvalidCommandException e) {
output.println("Invalid command: " + command + " (enter 'help' to list all available commands)");
} catch (SSLHandshakeException e) {
output.println("Error: " + e.getMessage());
output.println(String.format("To ignore this error, set -D%s=false when starting the CLI",
CLIConfig.PROP_VERIFY_SSL_CERT));
} catch (Exception e) {
output.println("Error: " + e.getMessage());
e.printStackTrace();
}
output.println();
}
}
}
/**
* Processes a command and writes to the provided output
* @param args the tokens of the command string (e.g. ["start", "flow", "SomeApp.SomeFlow"])
* @throws Exception
*/
public void processArgs(String[] args, PrintStream output) throws Exception {
commands.process(args, output);
}
private CommandSet getCommands() {
return commands;
}
public static void main(String[] args) throws Exception {
String hostname = Objects.firstNonNull(System.getenv(Constants.EV_HOSTNAME), "localhost");
CLIConfig config = new CLIConfig(hostname);
CLIMain shell = new CLIMain(config);
if (args.length == 0) {
shell.startShellMode(System.out);
} else {
shell.processArgs(args, System.out);
}
}
}