/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, * add the following below this CDDL HEADER, with the fields enclosed * by brackets "[]" replaced with your own identifying information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2013 ForgeRock AS */ package org.opends.server.tools.upgrade; import static org.opends.messages.ToolMessages.*; import static org.opends.server.tools.ToolConstants.*; import static org.opends.server.util.StaticUtils.filterExitCode; import static java.util.logging.Level.INFO; import static java.util.logging.Level.SEVERE; import static org.opends.server.tools.upgrade.FormattedNotificationCallback.*; import static org.opends.server.tools.upgrade.Upgrade.EXIT_CODE_ERROR; import static org.opends.server.tools.upgrade.Upgrade.EXIT_CODE_SUCCESS; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.List; import java.util.logging.Logger; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.ConfirmationCallback; import javax.security.auth.callback.TextOutputCallback; import javax.security.auth.callback.UnsupportedCallbackException; import org.opends.messages.Message; import org.opends.server.extensions.ConfigFileHandler; import org.opends.server.tools.ClientException; import org.opends.server.util.BuildVersion; import org.opends.server.util.ServerConstants; import org.opends.server.util.StaticUtils; import org.opends.server.util.args.ArgumentException; import org.opends.server.util.args.BooleanArgument; import org.opends.server.util.args.StringArgument; import org.opends.server.util.args.SubCommandArgumentParser; import org.opends.server.util.cli.CLIException; import org.opends.server.util.cli.ConsoleApplication; /** * This class provides the CLI used for upgrading the OpenDJ product. */ public final class UpgradeCli extends ConsoleApplication implements CallbackHandler { /** * Upgrade's logger. */ static private final Logger LOG = Logger .getLogger(UpgradeCli.class.getName()); // The command-line argument parser. private final SubCommandArgumentParser parser; // The argument which should be used to specify the config class. private StringArgument configClass; // The argument which should be used to specify the config file. private StringArgument configFile; //The argument which should be used to specify non interactive mode. private BooleanArgument noPrompt; private BooleanArgument ignoreErrors; private BooleanArgument force; private BooleanArgument quietMode; private BooleanArgument verbose; private BooleanArgument acceptLicense; // The argument which should be used to request usage information. private BooleanArgument showUsageArgument; // Flag indicating whether or not the global arguments have // already been initialized. private boolean globalArgumentsInitialized = false; private UpgradeCli(InputStream in, OutputStream out, OutputStream err) { super(in, out, err); this.parser = new SubCommandArgumentParser(this.getClass().getName(), INFO_UPGRADE_DESCRIPTION_CLI.get(), false); } /** * Provides the command-line arguments to the main application for processing. * * @param args * The set of command-line arguments provided to this program. */ public static void main(String[] args) { final int exitCode = main(args, true, System.out, System.err); if (exitCode != 0) { System.exit(filterExitCode(exitCode)); } } /** * Provides the command-line arguments to the main application for processing * and returns the exit code as an integer. * * @param args * The set of command-line arguments provided to this program. * @param initializeServer * Indicates whether to perform basic initialization (which should * not be done if the tool is running in the same JVM as the server). * @param outStream * The output stream for standard output. * @param errStream * The output stream for standard error. * @return Zero to indicate that the program completed successfully, or * non-zero to indicate that an error occurred. */ public static int main(String[] args, boolean initializeServer, OutputStream outStream, OutputStream errStream) { final UpgradeCli app = new UpgradeCli(System.in, outStream, errStream); // Run the application. return app.run(args, initializeServer); } /** {@inheritDoc} */ @Override public boolean isAdvancedMode() { return false; } /** {@inheritDoc} */ @Override public boolean isInteractive() { return !noPrompt.isPresent(); } /** {@inheritDoc} */ @Override public boolean isMenuDrivenMode() { return false; } /** {@inheritDoc} */ @Override public boolean isQuiet() { return quietMode.isPresent(); } /** {@inheritDoc} */ @Override public boolean isScriptFriendly() { return false; } /** {@inheritDoc} */ @Override public boolean isVerbose() { return verbose.isPresent(); } /** * Force the upgrade. All critical questions will be forced to 'yes'. * * @return {@code true} if the upgrade process is forced. */ public boolean isForceUpgrade() { return force.isPresent(); } /** * Force to ignore the errors during the upgrade process. * Continues rather than fails. * * @return {@code true} if the errors are forced to be ignored. */ public boolean isIgnoreErrors() { return ignoreErrors.isPresent(); } /** * Automatically accepts the license if it's present. * * @return {@code true} if license is accepted by default. */ public boolean isAcceptLicense() { return acceptLicense.isPresent(); } // Displays the provided message followed by a help usage reference. private void displayMessageAndUsageReference(final Message message) { println(message); println(); println(parser.getHelpUsageReference()); } // Initialize arguments provided by the command line. private void initializeGlobalArguments() throws ArgumentException { if (!globalArgumentsInitialized) { configClass = new StringArgument("configclass", OPTION_SHORT_CONFIG_CLASS, OPTION_LONG_CONFIG_CLASS, true, false, true, INFO_CONFIGCLASS_PLACEHOLDER.get(), ConfigFileHandler.class .getName(), null, INFO_DESCRIPTION_CONFIG_CLASS.get()); configClass.setHidden(true); configFile = new StringArgument("configfile", 'f', "configFile", true, false, true, INFO_CONFIGFILE_PLACEHOLDER.get(), null, null, INFO_DESCRIPTION_CONFIG_FILE.get()); configFile.setHidden(true); noPrompt = new BooleanArgument(OPTION_LONG_NO_PROMPT, OPTION_SHORT_NO_PROMPT, OPTION_LONG_NO_PROMPT, INFO_UPGRADE_DESCRIPTION_NO_PROMPT.get()); verbose = new BooleanArgument(OPTION_LONG_VERBOSE, OPTION_SHORT_VERBOSE, OPTION_LONG_VERBOSE, INFO_DESCRIPTION_VERBOSE.get()); quietMode = new BooleanArgument(OPTION_LONG_QUIET, OPTION_SHORT_QUIET, OPTION_LONG_QUIET, INFO_DESCRIPTION_QUIET.get()); ignoreErrors = new BooleanArgument(OPTION_LONG_IGNORE_ERRORS, null, OPTION_LONG_IGNORE_ERRORS, INFO_UPGRADE_OPTION_IGNORE_ERRORS .get()); force = new BooleanArgument(OPTION_LONG_FORCE_UPGRADE, null, OPTION_LONG_FORCE_UPGRADE, INFO_UPGRADE_OPTION_FORCE.get(OPTION_LONG_NO_PROMPT)); acceptLicense = new BooleanArgument(OPTION_LONG_ACCEPT_LICENSE, null, OPTION_LONG_ACCEPT_LICENSE, INFO_OPTION_ACCEPT_LICENSE.get()); showUsageArgument = new BooleanArgument("help", OPTION_SHORT_HELP, OPTION_LONG_HELP, INFO_DESCRIPTION_USAGE.get()); // Register the global arguments. parser.addGlobalArgument(showUsageArgument); parser.setUsageArgument(showUsageArgument, this.getOutputStream()); parser.addGlobalArgument(configClass); parser.addGlobalArgument(configFile); parser.addGlobalArgument(noPrompt); parser.addGlobalArgument(verbose); parser.addGlobalArgument(quietMode); parser.addGlobalArgument(force); parser.addGlobalArgument(ignoreErrors); parser.addGlobalArgument(acceptLicense); globalArgumentsInitialized = true; } } private int run(String[] args, boolean initializeServer) { // Initialize the arguments try { initializeGlobalArguments(); } catch (ArgumentException e) { final Message message = ERR_CANNOT_INITIALIZE_ARGS.get(e.getMessage()); this.getOutputStream().print(message); return EXIT_CODE_ERROR; } // Parse the command-line arguments provided to this program. try { parser.parseArguments(args); if (isInteractive() && isQuiet()) { final Message message = ERR_UPGRADE_INCOMPATIBLE_ARGS.get(OPTION_LONG_QUIET, "interactive mode"); this.getOutputStream().println(message); return EXIT_CODE_ERROR; } if (isInteractive() && isForceUpgrade()) { final Message message = ERR_UPGRADE_INCOMPATIBLE_ARGS.get(OPTION_LONG_FORCE_UPGRADE, "interactive mode"); this.getOutputStream().println(message); return EXIT_CODE_ERROR; } if (isQuiet() && isVerbose()) { final Message message = ERR_UPGRADE_INCOMPATIBLE_ARGS.get(OPTION_LONG_QUIET, OPTION_LONG_VERBOSE); this.getOutputStream().println(message); return EXIT_CODE_ERROR; } } catch (ArgumentException ae) { final Message message = ERR_ERROR_PARSING_ARGS.get(ae.getMessage()); displayMessageAndUsageReference(message); return EXIT_CODE_ERROR; } // If the usage/version argument was provided, then we don't need // to do anything else. if (parser.usageOrVersionDisplayed()) { return EXIT_CODE_SUCCESS; } // Main process try { // Creates the log file. UpgradeLog.initLogFileHandler(); // Upgrade's context. UpgradeContext context = new UpgradeContext(BuildVersion.instanceVersion(), BuildVersion .binaryVersion(), this); context.setIgnoreErrorsMode(isIgnoreErrors()); context.setAcceptLicenseMode(isAcceptLicense()); context.setInteractiveMode(isInteractive()); context.setForceUpgradeMode(isForceUpgrade()); // Starts upgrade. Upgrade.upgrade(context); } catch (ClientException ex) { LOG.log(SEVERE, ex.getMessage()); println(Style.ERROR, ex.getMessageObject(), 0); return ex.getExitCode(); } catch (Exception ex) { LOG.log(SEVERE, ERR_UPGRADE_MAIN_UPGRADE_PROCESS.get(ex .getMessage()).toString()); println(Style.ERROR, ERR_UPGRADE_MAIN_UPGRADE_PROCESS.get(ex .getMessage()), 0); return EXIT_CODE_ERROR; } return EXIT_CODE_SUCCESS; } /** {@inheritDoc} */ @Override public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { for (final Callback c : callbacks) { // Displays progress eg. for a task. if (c instanceof ProgressNotificationCallback) { final ProgressNotificationCallback pnc = (ProgressNotificationCallback) c; final Message msg = Message.raw(" " + pnc.getMessage()); printProgress(msg); printProgressBar(msg.length(), pnc.getProgress()); } else if (c instanceof FormattedNotificationCallback) { // Displays formatted notifications. final FormattedNotificationCallback fnc = (FormattedNotificationCallback) c; LOG.log(INFO, fnc.getMessage()); switch (fnc.getMessageSubType()) { case TITLE_CALLBACK: println(Style.TITLE, Message.raw(fnc.getMessage()), 0); break; case SUBTITLE_CALLBACK: println(Style.SUBTITLE, Message.raw(fnc.getMessage()), 4); break; case NOTICE_CALLBACK: println(Style.NOTICE, Message.raw(fnc.getMessage()), 1); break; case ERROR_CALLBACK: println(Style.ERROR, Message.raw(fnc.getMessage()), 1); break; case BREAKLINE: println(Style.BREAKLINE, Message.raw(fnc.getMessage()), 1); break; default: LOG.log(SEVERE, "Unsupported message type: " + fnc.getMessage()); throw new IOException("Unsupported message type: "); } } else if (c instanceof TextOutputCallback) { // Usual output text. final TextOutputCallback toc = (TextOutputCallback) c; if(toc.getMessageType() == TextOutputCallback.INFORMATION) { LOG.log(INFO, toc.getMessage()); printlnProgress(Message.raw(toc.getMessage())); } else { LOG.log(SEVERE, "Unsupported message type: " + toc.getMessage()); throw new IOException("Unsupported message type: "); } } else if (c instanceof ConfirmationCallback) { final ConfirmationCallback cc = (ConfirmationCallback) c; List<String> choices = new ArrayList<String>(); final String defaultOption = UpgradeContext.getDefaultOption(cc.getDefaultOption()); StringBuilder prompt = new StringBuilder(StaticUtils.wrapText(cc.getPrompt(), ServerConstants.MAX_LINE_WIDTH, 2)); // Default answers. final List<String> yesNoDefaultResponses = StaticUtils.arrayToList(new String[] { INFO_PROMPT_YES_COMPLETE_ANSWER.get().toString(), INFO_PROMPT_YES_FIRST_LETTER_ANSWER.get().toString(), INFO_PROMPT_NO_COMPLETE_ANSWER.get().toString(), INFO_PROMPT_NO_FIRST_LETTER_ANSWER.get().toString() }); // Generating prompt and possible answers list. prompt.append(" ").append("("); if (cc.getOptionType() == ConfirmationCallback.YES_NO_OPTION) { choices.addAll(yesNoDefaultResponses); prompt.append(INFO_PROMPT_YES_COMPLETE_ANSWER.get().toString()) .append("/") .append(INFO_PROMPT_NO_COMPLETE_ANSWER.get().toString()); } else if (cc.getOptionType() == ConfirmationCallback.YES_NO_CANCEL_OPTION) { choices.addAll(yesNoDefaultResponses); choices.addAll(StaticUtils .arrayToList(new String[] { INFO_TASKINFO_CMD_CANCEL_CHAR.get() .toString() })); prompt.append(" ").append("(").append( INFO_PROMPT_YES_COMPLETE_ANSWER.get().toString()).append("/") .append(INFO_PROMPT_NO_COMPLETE_ANSWER.get().toString()) .append("/").append( INFO_TASKINFO_CMD_CANCEL_CHAR.get().toString()); } prompt.append(")"); LOG.log(INFO, cc.getPrompt()); // Displays the output and // while it hasn't a valid response, question is repeated. if (isInteractive()) { while (true) { String value = null; try { value = readInput(Message.raw(prompt), defaultOption, Style.SUBTITLE); } catch (CLIException e) { LOG.log(SEVERE, e.getMessage()); break; } if ((value.toLowerCase().equals( INFO_PROMPT_YES_FIRST_LETTER_ANSWER.get().toString()) || value .toLowerCase().equals( INFO_PROMPT_YES_COMPLETE_ANSWER.get().toString())) && choices.contains(value)) { cc.setSelectedIndex(ConfirmationCallback.YES); break; } else if ((value.toLowerCase().equals( INFO_PROMPT_NO_FIRST_LETTER_ANSWER.get().toString()) || value .toLowerCase().equals( INFO_PROMPT_NO_COMPLETE_ANSWER.get().toString())) && choices.contains(value)) { cc.setSelectedIndex(ConfirmationCallback.NO); break; } else if ((value.toLowerCase().equals(INFO_TASKINFO_CMD_CANCEL_CHAR .get().toString())) && choices.contains(value)) { cc.setSelectedIndex(ConfirmationCallback.CANCEL); break; } LOG.log(INFO, value); } } else // Non interactive mode : { // Force mode. if (isForceUpgrade()) { cc.setSelectedIndex(ConfirmationCallback.YES); } else // Default non interactive mode. { cc.setSelectedIndex(cc.getDefaultOption()); } // Displays the prompt prompt.append(" ").append( UpgradeContext.getDefaultOption(cc.getSelectedIndex())); println(Style.SUBTITLE, Message.raw(prompt), 0); LOG.log(INFO, UpgradeContext.getDefaultOption(cc.getSelectedIndex())); } } else { LOG.log(SEVERE, "Unrecognized Callback"); throw new UnsupportedCallbackException(c, "Unrecognized Callback"); } } } }