/*
|
* 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 legal-notices/CDDLv1_0.txt
|
* or http://forgerock.org/license/CDDLv1.0.html.
|
* 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 legal-notices/CDDLv1_0.txt.
|
* 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
|
*
|
*
|
* Portions Copyright 2013-2014 ForgeRock AS
|
*/
|
package org.opends.server.tools.upgrade;
|
|
import static org.opends.messages.ToolMessages.*;
|
import static org.opends.server.util.StaticUtils.filterExitCode;
|
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 static com.forgerock.opendj.cli.ArgumentConstants.*;
|
|
import java.io.IOException;
|
import java.io.InputStream;
|
import java.io.OutputStream;
|
import java.io.PrintStream;
|
import java.util.ArrayList;
|
import java.util.List;
|
|
import org.forgerock.i18n.LocalizableMessage;
|
import org.forgerock.i18n.slf4j.LocalizedLogger;
|
|
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.server.extensions.ConfigFileHandler;
|
import org.opends.server.util.ServerConstants;
|
import org.opends.server.util.StaticUtils;
|
|
import com.forgerock.opendj.cli.ArgumentException;
|
import com.forgerock.opendj.cli.BooleanArgument;
|
import com.forgerock.opendj.cli.CommonArguments;
|
import com.forgerock.opendj.cli.StringArgument;
|
import com.forgerock.opendj.cli.SubCommandArgumentParser;
|
import com.forgerock.opendj.cli.ClientException;
|
import com.forgerock.opendj.cli.ConsoleApplication;
|
|
/**
|
* This class provides the CLI used for upgrading the OpenDJ product.
|
*/
|
public final class UpgradeCli extends ConsoleApplication implements
|
CallbackHandler
|
{
|
/**
|
* Upgrade's logger.
|
*/
|
private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
|
|
// 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(new PrintStream(out), new PrintStream(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.
|
*/
|
private 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.
|
*/
|
private boolean isIgnoreErrors()
|
{
|
return ignoreErrors.isPresent();
|
}
|
|
/**
|
* Automatically accepts the license if it's present.
|
*
|
* @return {@code true} if license is accepted by default.
|
*/
|
private boolean isAcceptLicense()
|
{
|
return acceptLicense.isPresent();
|
}
|
|
// Displays the provided message followed by a help usage reference.
|
private void displayMessageAndUsageReference(final LocalizableMessage message)
|
{
|
println(message);
|
println();
|
println(parser.getHelpUsageReference());
|
}
|
|
// Initialize arguments provided by the command line.
|
private void initializeGlobalArguments() throws ArgumentException
|
{
|
if (!globalArgumentsInitialized)
|
{
|
configClass =
|
CommonArguments.getConfigClass(ConfigFileHandler.class.getName());
|
|
configFile = CommonArguments.getConfigFile();
|
|
noPrompt = CommonArguments.getNoPrompt();
|
|
verbose = CommonArguments.getVerbose();
|
|
quietMode = CommonArguments.getQuiet();
|
|
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 = CommonArguments.getAcceptLicense();
|
|
showUsageArgument = CommonArguments.getShowUsage();
|
|
|
// 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 LocalizableMessage 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 LocalizableMessage message =
|
ERR_UPGRADE_INCOMPATIBLE_ARGS.get(OPTION_LONG_QUIET,
|
"interactive mode");
|
this.getOutputStream().println(message);
|
return EXIT_CODE_ERROR;
|
}
|
if (isInteractive() && isForceUpgrade())
|
{
|
final LocalizableMessage message =
|
ERR_UPGRADE_INCOMPATIBLE_ARGS.get(OPTION_LONG_FORCE_UPGRADE,
|
"interactive mode");
|
this.getOutputStream().println(message);
|
return EXIT_CODE_ERROR;
|
}
|
if (isQuiet() && isVerbose())
|
{
|
final LocalizableMessage message =
|
ERR_UPGRADE_INCOMPATIBLE_ARGS.get(OPTION_LONG_QUIET,
|
OPTION_LONG_VERBOSE);
|
this.getOutputStream().println(message);
|
return EXIT_CODE_ERROR;
|
}
|
}
|
catch (ArgumentException ae)
|
{
|
final LocalizableMessage 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(this)
|
.setIgnoreErrorsMode(isIgnoreErrors())
|
.setAcceptLicenseMode(isAcceptLicense())
|
.setInteractiveMode(isInteractive())
|
.setForceUpgradeMode(isForceUpgrade());
|
|
// Starts upgrade.
|
Upgrade.upgrade(context);
|
}
|
catch (ClientException ex)
|
{
|
return ex.getReturnCode();
|
}
|
catch (Exception ex)
|
{
|
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 LocalizableMessage msg = LocalizableMessage.raw(" " + pnc.getMessage());
|
print(msg);
|
printProgressBar(msg.length(), pnc.getProgress());
|
}
|
else if (c instanceof FormattedNotificationCallback)
|
{
|
// Displays formatted notifications.
|
final FormattedNotificationCallback fnc =
|
(FormattedNotificationCallback) c;
|
switch (fnc.getMessageSubType())
|
{
|
case TITLE_CALLBACK:
|
println(Style.TITLE, LocalizableMessage.raw(fnc.getMessage()), 0);
|
logger.info(LocalizableMessage.raw(fnc.getMessage()));
|
break;
|
case SUBTITLE_CALLBACK:
|
println(Style.SUBTITLE, LocalizableMessage.raw(fnc.getMessage()),
|
4);
|
logger.info(LocalizableMessage.raw(fnc.getMessage()));
|
break;
|
case NOTICE_CALLBACK:
|
println(Style.NOTICE, LocalizableMessage.raw(fnc.getMessage()), 1);
|
logger.info(LocalizableMessage.raw(fnc.getMessage()));
|
break;
|
case ERROR_CALLBACK:
|
println(Style.ERROR, LocalizableMessage.raw(fnc.getMessage()), 1);
|
logger.error(LocalizableMessage.raw(fnc.getMessage()));
|
break;
|
case WARNING:
|
println(Style.WARNING, LocalizableMessage.raw(fnc.getMessage()), 2);
|
logger.warn(LocalizableMessage.raw(fnc.getMessage()));
|
break;
|
default:
|
logger.error(LocalizableMessage.raw("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) {
|
logger.info(LocalizableMessage.raw(toc.getMessage()));
|
println(LocalizableMessage.raw(toc.getMessage()));
|
} else {
|
logger.error(LocalizableMessage.raw("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 = 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())
|
.append("/")
|
.append(INFO_PROMPT_NO_COMPLETE_ANSWER.get());
|
}
|
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())
|
.append("/").append(INFO_PROMPT_NO_COMPLETE_ANSWER.get())
|
.append("/").append(INFO_TASKINFO_CMD_CANCEL_CHAR.get());
|
}
|
prompt.append(")");
|
|
logger.info(LocalizableMessage.raw(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(LocalizableMessage.raw(prompt), defaultOption,
|
Style.SUBTITLE);
|
}
|
catch (ClientException e)
|
{
|
logger.error(LocalizableMessage.raw(e.getMessage()));
|
break;
|
}
|
|
String valueLC = value.toLowerCase();
|
if ((valueLC.equals(INFO_PROMPT_YES_FIRST_LETTER_ANSWER.get().toString())
|
|| valueLC.equals(INFO_PROMPT_YES_COMPLETE_ANSWER.get().toString()))
|
&& choices.contains(value))
|
{
|
cc.setSelectedIndex(ConfirmationCallback.YES);
|
break;
|
}
|
else if ((valueLC.equals(INFO_PROMPT_NO_FIRST_LETTER_ANSWER.get().toString())
|
|| valueLC.equals(INFO_PROMPT_NO_COMPLETE_ANSWER.get().toString()))
|
&& choices.contains(value))
|
{
|
cc.setSelectedIndex(ConfirmationCallback.NO);
|
break;
|
}
|
else if (valueLC.equals(INFO_TASKINFO_CMD_CANCEL_CHAR.get().toString())
|
&& choices.contains(value))
|
{
|
cc.setSelectedIndex(ConfirmationCallback.CANCEL);
|
break;
|
}
|
logger.info(LocalizableMessage.raw(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(getDefaultOption(cc.getSelectedIndex()));
|
println(Style.SUBTITLE, LocalizableMessage.raw(prompt), 0);
|
logger.info(LocalizableMessage.raw(getDefaultOption(cc.getSelectedIndex())));
|
}
|
}
|
else
|
{
|
logger.error(LocalizableMessage.raw("Unrecognized Callback"));
|
throw new UnsupportedCallbackException(c, "Unrecognized Callback");
|
}
|
}
|
}
|
|
|
|
private static String getDefaultOption(final int defaultOption)
|
{
|
if (defaultOption == ConfirmationCallback.YES)
|
{
|
return INFO_PROMPT_YES_COMPLETE_ANSWER.get().toString();
|
}
|
else if (defaultOption == ConfirmationCallback.NO)
|
{
|
return INFO_PROMPT_NO_COMPLETE_ANSWER.get().toString();
|
}
|
else if (defaultOption == ConfirmationCallback.CANCEL)
|
{
|
return INFO_TASKINFO_CMD_CANCEL_CHAR.get().toString();
|
}
|
return null;
|
}
|
}
|