| /* |
| * 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.geode.management.internal.cli; |
| |
| import static org.apache.geode.internal.util.ProductVersionUtil.getDistributionVersion; |
| |
| import java.io.PrintStream; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| |
| import joptsimple.OptionException; |
| import joptsimple.OptionParser; |
| import joptsimple.OptionSet; |
| import org.apache.commons.lang3.StringUtils; |
| import org.springframework.shell.core.ExitShellRequest; |
| |
| import org.apache.geode.internal.ExitCode; |
| import org.apache.geode.internal.util.ArgumentRedactor; |
| import org.apache.geode.internal.version.DistributionVersion; |
| import org.apache.geode.management.internal.cli.shell.Gfsh; |
| import org.apache.geode.management.internal.cli.shell.GfshConfig; |
| import org.apache.geode.management.internal.i18n.CliStrings; |
| |
| /** |
| * 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> |
| * |
| * @since GemFire 7.0 |
| */ |
| public 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."; |
| public static final String SEPARATOR = ", "; |
| |
| private final Set<String> allowedCommandLineCommands; |
| private final OptionParser commandLineParser; |
| private final StartupTimeLogHelper startupTimeLogHelper; |
| |
| protected Launcher() { |
| startupTimeLogHelper = new StartupTimeLogHelper(); |
| allowedCommandLineCommands = new HashSet<>(); |
| allowedCommandLineCommands.add(CliStrings.RUN); |
| allowedCommandLineCommands.add(CliStrings.START_PULSE); |
| allowedCommandLineCommands.add(CliStrings.START_JCONSOLE); |
| allowedCommandLineCommands.add(CliStrings.START_JVISUALVM); |
| allowedCommandLineCommands.add(CliStrings.START_LOCATOR); |
| allowedCommandLineCommands.add(CliStrings.START_MANAGER); |
| allowedCommandLineCommands.add(CliStrings.START_SERVER); |
| allowedCommandLineCommands.add(CliStrings.START_VSD); |
| allowedCommandLineCommands.add(CliStrings.STATUS_LOCATOR); |
| allowedCommandLineCommands.add(CliStrings.STATUS_SERVER); |
| allowedCommandLineCommands.add(CliStrings.STOP_LOCATOR); |
| allowedCommandLineCommands.add(CliStrings.STOP_SERVER); |
| allowedCommandLineCommands.add(CliStrings.VERSION); |
| allowedCommandLineCommands.add(CliStrings.COMPACT_OFFLINE_DISK_STORE); |
| allowedCommandLineCommands.add(CliStrings.DESCRIBE_OFFLINE_DISK_STORE); |
| allowedCommandLineCommands.add(CliStrings.EXPORT_OFFLINE_DISK_STORE); |
| allowedCommandLineCommands.add(CliStrings.VALIDATE_DISK_STORE); |
| allowedCommandLineCommands.add(CliStrings.PDX_DELETE_FIELD); |
| allowedCommandLineCommands.add(CliStrings.PDX_RENAME); |
| |
| commandLineParser = new OptionParser(); |
| commandLineParser.accepts(EXECUTE_OPTION).withOptionalArg().ofType(String.class); |
| commandLineParser.accepts(HELP_OPTION).withOptionalArg().ofType(Boolean.class); |
| commandLineParser.posixlyCorrect(false); |
| } |
| |
| 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 = CliUtils.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(); |
| int exitValue = launcher.parseCommandLine(args); |
| ExitCode.fromValue(exitValue).doSystemExit(); |
| } |
| |
| private int parseCommandLineCommand(final String... args) { |
| Gfsh gfsh = null; |
| try { |
| gfsh = Gfsh.getInstance(false, args, new GfshConfig()); |
| startupTimeLogHelper.logStartupTime(); |
| } 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 : allowedCommandLineCommands) { |
| if (commandLineCommand.startsWith(allowedCommandLineCommand)) { |
| commandIsAllowed = true; |
| break; |
| } |
| } |
| |
| if (!commandIsAllowed) { |
| System.err.println( |
| CliStrings.format(MSG_INVALID_COMMAND_OR_OPTION, StringUtils.join(args, SEPARATOR))); |
| exitRequest = ExitShellRequest.FATAL_EXIT; |
| } else { |
| if (!gfsh.executeScriptLine(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 = commandLineParser.parse(args); |
| } catch (OptionException e) { |
| System.err.println( |
| CliStrings.format(MSG_INVALID_COMMAND_OR_OPTION, StringUtils.join(args, SEPARATOR))); |
| 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()); |
| startupTimeLogHelper.logStartupTime(); |
| } 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); |
| // sanitize the output string to not show the password |
| System.out.println(GfshParser.LINE_SEPARATOR + "(" + (i + 1) + ") Executing - " |
| + ArgumentRedactor.redact(command) + GfshParser.LINE_SEPARATOR); |
| if (!gfsh.executeScriptLine(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(GfshParser.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) { |
| int terminalWidth = gfsh.getTerminalWidth(); |
| final DistributionVersion distributionVersion = getDistributionVersion(); |
| stream.print(distributionVersion.getName() + " v"); |
| stream.print(distributionVersion.getVersion()); |
| 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, terminalWidth)); |
| stream.println("EXAMPLES"); |
| stream.println("gfsh"); |
| stream.println(Gfsh.wrapText("Start GFSH in interactive mode.", 1, terminalWidth)); |
| stream.println("gfsh -h"); |
| stream.println(Gfsh.wrapText( |
| "Displays 'this' help. ('gfsh --help' or 'gfsh help' is equivalent)", 1, terminalWidth)); |
| stream.println("gfsh help start locator"); |
| stream.println( |
| Gfsh.wrapText("Display help for the \"start locator\" command.", 1, terminalWidth)); |
| stream.println("gfsh start locator --name=locator1"); |
| stream.println(Gfsh.wrapText("Start a Locator with the name \"locator1\".", 1, terminalWidth)); |
| 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, terminalWidth)); |
| stream.println(); |
| } |
| |
| private static class StartupTimeLogHelper { |
| private final long start = System.currentTimeMillis(); |
| private long done; |
| |
| public void logStartupTime() { |
| done = System.currentTimeMillis(); |
| LogWrapper.getInstance(null) |
| .info("Startup done in " + ((done - start) / 1000.0) + " seconds."); |
| } |
| |
| @Override |
| public String toString() { |
| return StartupTimeLogHelper.class.getName() + " [start=" + start + ", done=" + done + "]"; |
| } |
| } |
| } |