blob: 3418f20eb6ee00e5d6d056ead7556d952334bc35 [file] [log] [blame]
/*
* =========================================================================
* Copyright (c) 2002-2014 Pivotal Software, Inc. All Rights Reserved.
* This product is protected by U.S. and international copyright
* and intellectual property laws. Pivotal products are covered by
* more patents listed at http://www.pivotal.io/patents.
* ========================================================================+
*/
package com.gemstone.gemfire.management.internal.cli;
import java.io.IOException;
import java.io.PrintStream;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.springframework.shell.core.ExitShellRequest;
import com.gemstone.gemfire.internal.GemFireVersion;
import com.gemstone.gemfire.internal.PureJavaMode;
import com.gemstone.gemfire.management.internal.cli.i18n.CliStrings;
import com.gemstone.gemfire.management.internal.cli.parser.SyntaxConstants;
import com.gemstone.gemfire.management.internal.cli.shell.Gfsh;
import com.gemstone.gemfire.management.internal.cli.shell.GfshConfig;
import joptsimple.OptionException;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
/**
* Launcher class for : <ul>
* <li> gfsh 7.0
* <li> server
* <li> locator
* <li> Tools (Pulse, VSD, JConsole, JVisualVM)
* <li> Running Command Line Interface (CLI) Commands from OS prompt like <ol>
* <li><ul>
* <li> compact offline-disk-store - Compact an offline disk store. If the disk store is large, additional memory may need to be allocated to the process using the --J=-Xmx??? parameter.
* <li> describe offline-disk-store - Display information about an offline disk store.
* <li> encrypt password - Encrypt a password for use in data source configuration.
* <li> run - Execute a set of GFSH commands. Commands that normally prompt for additional input will instead use default values.
* <li> start jconsole - Start the JDK's JConsole tool in a separate process. JConsole will be launched, but connecting to GemFire must be done manually.
* <li> start jvisualvm - Start the JDK's Java VisualVM (jvisualvm) tool in a separate process. Java VisualVM will be launched, but connecting to GemFire must be done manually.
* <li> start locator - Start a Locator.
* <li> start pulse - Open a new window in the default Web browser with the URL for the Pulse application.
* <li> start server - Start a GemFire Cache Server.
* <li> start vsd - Start VSD in a separate process.
* <li> status locator - Display the status of a Locator. Possible statuses are: started, online, offline or not responding.
* <li> status server - Display the status of a GemFire Cache Server.
* <li> stop locator - Stop a Locator.
* <li> stop server - Stop a GemFire Cache Server.
* <li> validate offline-disk-store - Scan the contents of a disk store to verify that it has no errors.
* <li> version - Display product version information.
* </ul></li>
* <li> multiple commands specified using an option "-e"
* </ol>
* </ul>
*
* @author Abhishek Chaudhari
* @author David Hoots
* @since 7.0
*/
public final class Launcher {
private static final String EXECUTE_OPTION = "execute";
private static final String HELP_OPTION = "help";
private static final String HELP = CliStrings.HELP;
private static final String MSG_INVALID_COMMAND_OR_OPTION = "Invalid command or option : {0}." + GfshParser.LINE_SEPARATOR
+ "Use 'gfsh help' to display additional information.";
private final Set<String> allowedCommandLineCommands;
private final OptionParser commandLineParser;
private StartupTimeLogHelper startupTimeLogHelper;
static {
// See 47325
System.setProperty(PureJavaMode.PURE_MODE_PROPERTY, "true");
}
public static void main(final String[] args) {
// first check whether required dependencies exist in the classpath
// should we start without tomcat/servlet jars?
String nonExistingDependency = CliUtil.cliDependenciesExist(true);
if (nonExistingDependency != null) {
System.err.println("Required (" + nonExistingDependency + ") libraries not found in the classpath. gfsh can't start.");
return;
}
Launcher launcher = new Launcher();
System.exit(launcher.parseCommandLine(args));
}
protected Launcher() {
this.startupTimeLogHelper = new StartupTimeLogHelper();
this.allowedCommandLineCommands = new HashSet<String>();
this.allowedCommandLineCommands.add(CliStrings.ENCRYPT);
this.allowedCommandLineCommands.add(CliStrings.RUN);
this.allowedCommandLineCommands.add(CliStrings.START_PULSE);
this.allowedCommandLineCommands.add(CliStrings.START_JCONSOLE);
this.allowedCommandLineCommands.add(CliStrings.START_JVISUALVM);
this.allowedCommandLineCommands.add(CliStrings.START_LOCATOR);
this.allowedCommandLineCommands.add(CliStrings.START_MANAGER);
this.allowedCommandLineCommands.add(CliStrings.START_SERVER);
this.allowedCommandLineCommands.add(CliStrings.START_VSD);
this.allowedCommandLineCommands.add(CliStrings.STATUS_LOCATOR);
this.allowedCommandLineCommands.add(CliStrings.STATUS_SERVER);
this.allowedCommandLineCommands.add(CliStrings.STOP_LOCATOR);
this.allowedCommandLineCommands.add(CliStrings.STOP_SERVER);
this.allowedCommandLineCommands.add(CliStrings.VERSION);
this.allowedCommandLineCommands.add(CliStrings.COMPACT_OFFLINE_DISK_STORE);
this.allowedCommandLineCommands.add(CliStrings.DESCRIBE_OFFLINE_DISK_STORE);
this.allowedCommandLineCommands.add(CliStrings.EXPORT_OFFLINE_DISK_STORE);
this.allowedCommandLineCommands.add(CliStrings.VALIDATE_DISK_STORE);
this.allowedCommandLineCommands.add(CliStrings.PDX_DELETE_FIELD);
this.allowedCommandLineCommands.add(CliStrings.PDX_RENAME);
this.commandLineParser = new OptionParser();
this.commandLineParser.accepts(EXECUTE_OPTION).withOptionalArg().ofType(String.class);
this.commandLineParser.accepts(HELP_OPTION).withOptionalArg().ofType(Boolean.class);
this.commandLineParser.posixlyCorrect(false);
}
private int parseCommandLineCommand(final String... args) {
Gfsh gfsh = null;
try {
gfsh = Gfsh.getInstance(false, args, new GfshConfig());
this.startupTimeLogHelper.logStartupTime();
} catch (ClassNotFoundException cnfex) {
log(cnfex, gfsh);
} catch (IOException ioex) {
log(ioex, gfsh);
} catch (IllegalStateException isex) {
System.err.println("ERROR : " + isex.getMessage());
}
ExitShellRequest exitRequest = ExitShellRequest.NORMAL_EXIT;
if (gfsh != null) {
final String commandLineCommand = combineStrings(args);
if (commandLineCommand.startsWith(HELP)) {
if (commandLineCommand.equals(HELP)) {
printUsage(gfsh, System.out);
} else {
// help is also available for commands which are not available under allowedCommandLineCommands
gfsh.executeCommand(commandLineCommand);
}
} else {
boolean commandIsAllowed = false;
for (String allowedCommandLineCommand : this.allowedCommandLineCommands) {
if (commandLineCommand.startsWith(allowedCommandLineCommand)) {
commandIsAllowed = true;
break;
}
}
if (!commandIsAllowed) {
System.err.println(CliStrings.format(MSG_INVALID_COMMAND_OR_OPTION, CliUtil.arrayToString(args)));
exitRequest = ExitShellRequest.FATAL_EXIT;
} else {
if (!gfsh.executeCommand(commandLineCommand)) {
if (gfsh.getLastExecutionStatus() != 0)
exitRequest = ExitShellRequest.FATAL_EXIT;
} else if (gfsh.getLastExecutionStatus() != 0) {
exitRequest = ExitShellRequest.FATAL_EXIT;
}
}
}
}
return exitRequest.getExitCode();
}
private int parseOptions(final String... args) {
OptionSet parsedOptions;
try {
parsedOptions = this.commandLineParser.parse(args);
} catch (OptionException e) {
System.err.println(CliStrings.format(MSG_INVALID_COMMAND_OR_OPTION, CliUtil.arrayToString(args)));
return ExitShellRequest.FATAL_EXIT.getExitCode();
}
boolean launchShell = true;
boolean onlyPrintUsage = parsedOptions.has(HELP_OPTION);
if (parsedOptions.has(EXECUTE_OPTION) || onlyPrintUsage) {
launchShell = false;
}
Gfsh gfsh = null;
try {
gfsh = Gfsh.getInstance(launchShell, args, new GfshConfig());
this.startupTimeLogHelper.logStartupTime();
} catch (ClassNotFoundException cnfex) {
log(cnfex, gfsh);
} catch (IOException ioex) {
log(ioex, gfsh);
} catch (IllegalStateException isex) {
System.err.println("ERROR : " + isex.getMessage());
}
ExitShellRequest exitRequest = ExitShellRequest.NORMAL_EXIT;
if (gfsh != null) {
try {
if (launchShell) {
gfsh.start();
gfsh.waitForComplete();
exitRequest = gfsh.getExitShellRequest();
} else if (onlyPrintUsage) {
printUsage(gfsh, System.out);
} else {
@SuppressWarnings("unchecked")
List<String> commandsToExecute = (List<String>) parsedOptions.valuesOf(EXECUTE_OPTION);
// Execute all of the commands in the list, one at a time.
for (int i = 0; i < commandsToExecute.size() && exitRequest == ExitShellRequest.NORMAL_EXIT; i++) {
String command = commandsToExecute.get(i);
System.out.println(GfshParser.LINE_SEPARATOR + "(" + (i + 1) + ") Executing - " + command
+ GfshParser.LINE_SEPARATOR);
if (!gfsh.executeCommand(command) || gfsh.getLastExecutionStatus() != 0) {
exitRequest = ExitShellRequest.FATAL_EXIT;
}
}
}
} catch (InterruptedException iex) {
log(iex, gfsh);
}
}
return exitRequest.getExitCode();
}
private int parseCommandLine(final String... args) {
if (args.length > 0 && !args[0].startsWith(SyntaxConstants.SHORT_OPTION_SPECIFIER)) {
return parseCommandLineCommand(args);
}
return parseOptions(args);
}
private void log(Throwable t, Gfsh gfsh) {
if (!(gfsh != null && gfsh.logToFile(t.getMessage(), t))) {
t.printStackTrace();
}
}
private String combineStrings(String... strings) {
StringBuilder stringBuilder = new StringBuilder();
for (String string : strings) {
stringBuilder.append(string).append(" ");
}
return stringBuilder.toString().trim();
}
private void printUsage(final Gfsh gfsh, final PrintStream stream) {
StringBuilder usageBuilder = new StringBuilder();
stream.print("Pivotal GemFire(R) v");
stream.print(GemFireVersion.getGemFireVersion());
stream.println(" Command Line Shell" + GfshParser.LINE_SEPARATOR);
stream.println("USAGE");
stream.println("gfsh [ <command> [option]* | <help> [command] | [--help | -h] | [-e \"<command> [option]*\"]* ]" + GfshParser.LINE_SEPARATOR);
stream.println("OPTIONS");
stream.println("-e Execute a command");
stream.println(Gfsh.wrapText("Commands may be any that are available from the interactive gfsh prompt. "
+ "For commands that require a Manager to complete, the first command in the list must be \"connect\".", 1));
stream.println(GfshParser.LINE_SEPARATOR + "AVAILABLE COMMANDS");
stream.print(gfsh.obtainHelp("", this.allowedCommandLineCommands));
stream.println("EXAMPLES");
stream.println("gfsh");
stream.println(Gfsh.wrapText("Start GFSH in interactive mode.", 1));
stream.println("gfsh -h");
stream.println(Gfsh.wrapText("Displays 'this' help. ('gfsh --help' or 'gfsh help' is equivalent)", 1));
stream.println("gfsh help start locator");
stream.println(Gfsh.wrapText("Display help for the \"start locator\" command.", 1));
stream.println("gfsh start locator --name=locator1");
stream.println(Gfsh.wrapText("Start a Locator with the name \"locator1\".", 1));
stream.println("gfsh -e \"connect\" -e \"list members\"");
stream.println(Gfsh.wrapText(
"Connect to a running Locator using the default connection information and run the \"list members\" command.", 1));
stream.println();
printExecuteUsage(stream);
stream.print(usageBuilder);
}
private void printExecuteUsage(final PrintStream printStream) {
StringBuilder usageBuilder = new StringBuilder();
printStream.print(usageBuilder);
}
private static class StartupTimeLogHelper {
private final long start = System.currentTimeMillis();
private long done;
public void logStartupTime() {
done = System.currentTimeMillis();
LogWrapper.getInstance().info("Startup done in " + ( (done - start) / 1000.0) + " seconds.");
}
@Override
public String toString() {
return StartupTimeLogHelper.class.getName() + " [start=" + start + ", done=" + done + "]";
}
}
}