/* * 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 * * * Copyright 2006-2010 Sun Microsystems, Inc. * Portions Copyright 2011 profiq s.r.o. * Portions Copyright 2011-2015 ForgeRock AS */ package org.opends.server.tools; import static org.forgerock.util.Utils.*; import static org.opends.messages.AdminToolMessages.*; import static org.opends.messages.QuickSetupMessages.*; import static org.opends.messages.ToolMessages.*; import static org.opends.messages.UtilityMessages.*; import static com.forgerock.opendj.cli.Utils.*; import static com.forgerock.opendj.util.OperatingSystem.*; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintStream; import java.security.KeyStoreException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.LinkedList; import java.util.List; import javax.naming.ldap.LdapName; import org.forgerock.i18n.LocalizableMessage; import org.forgerock.i18n.LocalizableMessageDescriptor.Arg0; import org.forgerock.i18n.LocalizableMessageDescriptor.Arg1; import org.forgerock.i18n.slf4j.LocalizedLogger; import org.forgerock.opendj.config.ManagedObjectDefinition; import org.forgerock.opendj.server.config.client.BackendCfgClient; import org.forgerock.opendj.server.config.server.BackendCfg; import org.opends.messages.QuickSetupMessages; import org.opends.messages.ToolMessages; import org.opends.quicksetup.ApplicationException; import org.opends.quicksetup.Constants; import org.opends.quicksetup.CurrentInstallStatus; import org.opends.quicksetup.Installation; import org.opends.quicksetup.LicenseFile; import org.opends.quicksetup.QuickSetupLog; import org.opends.quicksetup.SecurityOptions; import org.opends.quicksetup.UserData; import org.opends.quicksetup.UserDataException; import org.opends.quicksetup.event.ProgressUpdateEvent; import org.opends.quicksetup.event.ProgressUpdateListener; import org.opends.quicksetup.installer.NewSuffixOptions; import org.opends.quicksetup.installer.offline.OfflineInstaller; import org.opends.quicksetup.util.IncompatibleVersionException; import org.opends.quicksetup.util.PlainTextProgressMessageFormatter; import org.opends.quicksetup.util.Utils; import org.opends.server.types.InitializationException; import org.opends.server.types.NullOutputStream; import org.opends.server.util.CertificateManager; import org.opends.server.util.SetupUtils; import org.opends.server.util.StaticUtils; import com.forgerock.opendj.cli.ArgumentException; import com.forgerock.opendj.cli.ClientException; import com.forgerock.opendj.cli.ConsoleApplication; import com.forgerock.opendj.cli.IntegerArgument; import com.forgerock.opendj.cli.Menu; import com.forgerock.opendj.cli.MenuBuilder; import com.forgerock.opendj.cli.MenuResult; import com.forgerock.opendj.cli.StringArgument; /** * This class provides a very simple mechanism for installing the OpenDS * Directory Service. It performs the following tasks: * <UL> * <LI>Checks if the server is already installed and running</LI> * <LI>Ask the user what base DN should be used for the data</LI> * <LI>Ask the user whether to create the base entry, or to import LDIF</LI> * <LI>Ask the user for the administration port and make sure it's available * </LI> * <LI>Ask the user for the LDAP port and make sure it's available</LI> * <LI>Ask the user for the default root DN and password</LI> * <LI>Ask the user to enable SSL or not and for the type of certificate that * the server must use</LI> * <LI>Ask the user if they want to start the server when done installing</LI> * </UL> */ public class InstallDS extends ConsoleApplication { private final PlainTextProgressMessageFormatter formatter = new PlainTextProgressMessageFormatter(); /** Prefix for log files. */ public static final String TMP_FILE_PREFIX = "opendj-setup-"; /** Suffix for log files. */ public static final String LOG_FILE_SUFFIX = ".log"; /** * The enumeration containing the different return codes that the command-line * can have. */ private enum InstallReturnCode { SUCCESSFUL(0), /** We did no have an error but the setup was not executed (displayed version or usage). */ SUCCESSFUL_NOP(0), /** Unexpected error (potential bug). */ ERROR_UNEXPECTED(1), /** Cannot parse arguments or data provided by user is not valid. */ ERROR_USER_DATA(2), /** Error server already installed. */ ERROR_SERVER_ALREADY_INSTALLED(3), /** Error initializing server. */ ERROR_INITIALIZING_SERVER(4), /** The user failed providing password (for the keystore for instance). */ ERROR_PASSWORD_LIMIT(5), /** The user cancelled the setup. */ ERROR_USER_CANCELLED(6), /** The user doesn't accept the license. */ ERROR_LICENSE_NOT_ACCEPTED(7), /** Incompatible java version. */ JAVA_VERSION_INCOMPATIBLE(8); private int returnCode; private InstallReturnCode(int returnCode) { this.returnCode = returnCode; } /** * Get the corresponding return code value. * * @return The corresponding return code value. */ public int getReturnCode() { return returnCode; } } /** * Enumeration describing the different answer that the user can provide when * we ask to finalize the setup. Note that the code associated correspond to * the order in the confirmation menu that is displayed at the end of the * setup in interactive mode. */ private enum ConfirmCode { CONTINUE(1), PROVIDE_INFORMATION_AGAIN(2), PRINT_EQUIVALENT_COMMAND_LINE(3), CANCEL(3); private int returnCode; private ConfirmCode(int returnCode) { this.returnCode = returnCode; } /** * Get the corresponding return code value. * * @return The corresponding return code value. */ public int getReturnCode() { return returnCode; } } /** * The maximum number of times that we should ask the user to provide the * password to access to a keystore. */ public static final int LIMIT_KEYSTORE_PASSWORD_PROMPT = 7; private final BackendTypeHelper backendTypeHelper = new BackendTypeHelper(); /** Different variables we use when the user decides to provide data again. */ private NewSuffixOptions.Type lastResetPopulateOption; private ManagedObjectDefinition<? extends BackendCfgClient, ? extends BackendCfg> lastResetBackendType; private String lastResetImportFile; private String lastResetRejectedFile; private String lastResetSkippedFile; private Integer lastResetNumEntries; private Boolean lastResetEnableSSL; private Boolean lastResetEnableStartTLS; private SecurityOptions.CertificateType lastResetCertType; private String lastResetKeyStorePath; private Boolean lastResetEnableWindowsService; private Boolean lastResetStartServer; private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); /** The argument parser. */ private InstallDSArgumentParser argParser; /** * Constructor for the InstallDS object. * * @param out * the print stream to use for standard output. * @param err * the print stream to use for standard error. * @param in * the input stream to use for standard input. */ public InstallDS(PrintStream out, PrintStream err, InputStream in) { super(out, err); } /** * The main method for the InstallDS CLI tool. * * @param args * the command-line arguments provided to this program. */ public static void main(String[] args) { final int retCode = mainCLI(args, System.out, System.err, System.in); System.exit(retCode); } /** * Parses the provided command-line arguments and uses that information to run * the setup tool. * * @param args * the command-line arguments provided to this program. * @return The error code. */ public static int mainCLI(String[] args) { return mainCLI(args, System.out, System.err, System.in); } /** * Parses the provided command-line arguments and uses that information to run * the setup tool. * * @param args * The command-line arguments provided to this program. * @param outStream * The output stream to use for standard output, or <CODE>null</CODE> * if standard output is not needed. * @param errStream * The output stream to use for standard error, or <CODE>null</CODE> * if standard error is not needed. * @param inStream * The input stream to use for standard input. * @return The error code. */ public static int mainCLI(String[] args, OutputStream outStream, OutputStream errStream, InputStream inStream) { final PrintStream out = NullOutputStream.wrapOrNullStream(outStream); System.setProperty(Constants.CLI_JAVA_PROPERTY, "true"); final PrintStream err = NullOutputStream.wrapOrNullStream(errStream); try { QuickSetupLog.initLogFileHandler( QuickSetupLog.isInitialized() ? null : File.createTempFile(TMP_FILE_PREFIX, LOG_FILE_SUFFIX)); } catch (final Throwable t) { System.err.println("Unable to initialize log"); t.printStackTrace(); } final InstallDS install = new InstallDS(out, err, inStream); int retCode = install.execute(args); if (retCode == 0) { QuickSetupLog.closeAndDeleteLogFile(); } return retCode; } /** * Parses the provided command-line arguments and uses that information to run * the setup CLI. * * @param args * the command-line arguments provided to this program. * @return the return code (SUCCESSFUL, USER_DATA_ERROR or BUG). */ public int execute(String[] args) { argParser = new InstallDSArgumentParser(InstallDS.class.getName()); try { argParser.initializeArguments(); } catch (final ArgumentException ae) { println(ToolMessages.ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage())); return InstallReturnCode.ERROR_UNEXPECTED.getReturnCode(); } // Validate user provided data try { argParser.parseArguments(args); } catch (final ArgumentException ae) { argParser.displayMessageAndUsageReference(getErrStream(), ERR_ERROR_PARSING_ARGS.get(ae.getMessage())); return InstallReturnCode.ERROR_USER_DATA.getReturnCode(); } if (argParser.testOnlyArg.isPresent() && !testJVM()) { return InstallReturnCode.JAVA_VERSION_INCOMPATIBLE.getReturnCode(); } if (argParser.usageOrVersionDisplayed() || argParser.testOnlyArg.isPresent()) { return InstallReturnCode.SUCCESSFUL_NOP.getReturnCode(); } try { checkInstallStatus(); } catch (final InitializationException ie) { println(ie.getMessageObject()); return InstallReturnCode.ERROR_SERVER_ALREADY_INSTALLED.getReturnCode(); } if (!checkLicense()) { return InstallReturnCode.ERROR_LICENSE_NOT_ACCEPTED.getReturnCode(); } final UserData uData = new UserData(); InstallReturnCode fillUserDataRC; try { fillUserDataRC = fillUserData(uData, args); if (fillUserDataRC != InstallReturnCode.SUCCESSFUL) { return fillUserDataRC.getReturnCode(); } } catch (final UserDataException e) { return printAndReturnErrorCode(e.getMessageObject()).getReturnCode(); } System.setProperty(Constants.CLI_JAVA_PROPERTY, "true"); final OfflineInstaller installer = new OfflineInstaller(); installer.setUserData(uData); installer.setProgressMessageFormatter(formatter); installer.addProgressUpdateListener( new ProgressUpdateListener() { @Override public void progressUpdate(ProgressUpdateEvent ev) { if (ev.getNewLogs() != null) { print(ev.getNewLogs()); } } }); println(); installer.run(); printStatusCommand(); final ApplicationException ue = installer.getRunError(); if (ue != null) { return ue.getType().getReturnCode(); } return InstallReturnCode.SUCCESSFUL.getReturnCode(); } private InstallReturnCode fillUserData(UserData uData, String[] args) throws UserDataException { if (!isInteractive()) { initializeUserDataWithParser(uData); return InstallReturnCode.SUCCESSFUL; } boolean userApproved = false; while (!userApproved) { try { promptIfRequired(uData); } catch (final ClientException ce) { return printAndReturnErrorCode(ce.getMessageObject()); } boolean promptAgain = true; printSummary(uData); while (isInteractive() && promptAgain) { promptAgain = false; final ConfirmCode confirm = askForConfirmation(); switch (confirm) { case CONTINUE: userApproved = true; break; case CANCEL: logger.debug(LocalizableMessage.raw("User cancelled setup.")); return InstallReturnCode.ERROR_USER_CANCELLED; case PRINT_EQUIVALENT_COMMAND_LINE: printEquivalentCommandLine(uData); promptAgain = true; break; case PROVIDE_INFORMATION_AGAIN: // Reset the arguments try { resetArguments(uData); argParser.parseArguments(args); } catch (final Throwable t) { logger.warn(LocalizableMessage.raw("Error resetting arg parser: "+t, t)); } userApproved = false; } } } return InstallReturnCode.SUCCESSFUL; } private boolean testJVM() { // Delete the log file that does not contain any information. The test only // mode is called several times by the setup script and if we do not remove // it we have a lot of empty log files. try { QuickSetupLog.getLogFile().deleteOnExit(); } catch (final Throwable t) { logger.warn(LocalizableMessage.raw("Error while trying to update the contents of " + "the set-java-home file in test only mode: " + t, t)); } try { Utils.checkJavaVersion(); return true; } catch (final IncompatibleVersionException ive) { println(ive.getMessageObject()); return false; } } private boolean checkLicense() { if (!LicenseFile.exists()) { return true; } println(LocalizableMessage.raw(LicenseFile.getText())); // If the user asks for acceptLicense, license is displayed // and automatically accepted. if (!argParser.acceptLicense.isPresent()) { final String yes = INFO_LICENSE_CLI_ACCEPT_YES.get().toString(); final String no = INFO_LICENSE_CLI_ACCEPT_NO.get().toString(); final String yesShort = INFO_PROMPT_YES_FIRST_LETTER_ANSWER.get().toString(); final String noShort = INFO_PROMPT_NO_FIRST_LETTER_ANSWER.get().toString(); println(QuickSetupMessages.INFO_LICENSE_DETAILS_CLI_LABEL.get()); final BufferedReader in = new BufferedReader(new InputStreamReader(getInputStream())); // No-prompt arg automatically rejects the license. if (!argParser.noPromptArg.isPresent()) { while (true) { print(INFO_LICENSE_CLI_ACCEPT_QUESTION.get(yes, no, no)); try { final String response = in.readLine(); if (response == null || response.equalsIgnoreCase(no) || response.equalsIgnoreCase(noShort) || response.length() == 0) { return false; } else if (response.equalsIgnoreCase(yes) || response.equalsIgnoreCase(yesShort)) { LicenseFile.setApproval(true); break; } println(QuickSetupMessages.INFO_LICENSE_CLI_ACCEPT_INVALID_RESPONSE.get()); } catch (final IOException e) { println(QuickSetupMessages.INFO_LICENSE_CLI_ACCEPT_INVALID_RESPONSE.get()); } } } else { return false; } } else { print(INFO_LICENSE_ACCEPT.get()); print(INFO_PROMPT_YES_COMPLETE_ANSWER.get()); LicenseFile.setApproval(true); } return true; } private void printStatusCommand() { // Use this instead a call to Installation to avoid to launch a new JVM just to retrieve a path. final String binariesRelativePath = isWindows() ? Installation.WINDOWS_BINARIES_PATH_RELATIVE : Installation.UNIX_BINARIES_PATH_RELATIVE; final String statusCliFileName = isWindows() ? Installation.WINDOWS_STATUSCLI_FILE_NAME : Installation.UNIX_STATUSCLI_FILE_NAME; final String binDir = Utils.getPath(Utils.getInstallPathFromClasspath(), binariesRelativePath); final String cmd = Utils.getPath(binDir, statusCliFileName); println(); println(INFO_INSTALLDS_STATUS_COMMAND_LINE.get(cmd)); println(); } private InstallReturnCode printAndReturnErrorCode(LocalizableMessage message) { println(message); if (StaticUtils.hasDescriptor(message, ERR_INSTALLDS_TOO_MANY_KEYSTORE_PASSWORD_TRIES)) { return InstallReturnCode.ERROR_PASSWORD_LIMIT; } return InstallReturnCode.ERROR_USER_DATA; } /** * Checks if the server is installed or not. * * @throws InitializationException * if the server is already installed and configured or if the user * did not accept to overwrite the existing databases. */ private void checkInstallStatus() throws InitializationException { final CurrentInstallStatus installStatus = new CurrentInstallStatus(); if (installStatus.canOverwriteCurrentInstall()) { if (isInteractive()) { println(installStatus.getInstallationMsg()); try { if (!confirmAction(INFO_CLI_DO_YOU_WANT_TO_CONTINUE.get(), true)) { throw new InitializationException(LocalizableMessage.EMPTY); } } catch (final ClientException ce) { logger.error(LocalizableMessage.raw("Unexpected error: "+ce, ce)); throw new InitializationException(LocalizableMessage.EMPTY, ce); } } else { println(installStatus.getInstallationMsg()); } } else if (installStatus.isInstalled()) { throw new InitializationException(installStatus.getInstallationMsg()); } } /** {@inheritDoc} */ @Override public boolean isQuiet() { return argParser.quietArg.isPresent(); } /** {@inheritDoc} */ @Override public boolean isInteractive() { return !argParser.noPromptArg.isPresent(); } /** {@inheritDoc} */ @Override public boolean isMenuDrivenMode() { return true; } /** {@inheritDoc} */ @Override public boolean isScriptFriendly() { return false; } /** {@inheritDoc} */ @Override public boolean isAdvancedMode() { return false; } /** {@inheritDoc} */ @Override public boolean isVerbose() { return argParser.verboseArg.isPresent(); } /** * This method updates the contents of a UserData object with what the user * specified in the command-line. It assumes that it is being called in no * prompt mode. * * @param uData * the UserData object. * @throws UserDataException * if something went wrong checking the data. */ private void initializeUserDataWithParser(UserData uData) throws UserDataException { uData.setQuiet(isQuiet()); uData.setVerbose(isVerbose()); uData.setConnectTimeout(getConnectTimeout()); final List<LocalizableMessage> errorMessages = new LinkedList<>(); setBackendType(uData, errorMessages); final List<String> baseDNs = checkBaseDNs(errorMessages); setDirectoryManagerData(uData, errorMessages); setPorts(uData, errorMessages); setImportData(baseDNs, uData, errorMessages); setSecurityData(uData, errorMessages); if (!errorMessages.isEmpty()) { throw new UserDataException(null, Utils.getMessageFromCollection(errorMessages, formatter.getLineBreak().toString())); } } private void setBackendType(final UserData uData, final List<LocalizableMessage> errorMessages) { final String filledBackendType = argParser.backendTypeArg.getValue(); final ManagedObjectDefinition<? extends BackendCfgClient, ? extends BackendCfg> backend = backendTypeHelper.retrieveBackendTypeFromName(filledBackendType); if (backend != null) { uData.setBackendType(backend); } else { errorMessages.add( ERR_INSTALLDS_NO_SUCH_BACKEND_TYPE.get(filledBackendType, backendTypeHelper.getPrintableBackendTypeNames())); } } private List<String> checkBaseDNs(List<LocalizableMessage> errorMessages) { final List<String> baseDNs = argParser.baseDNArg.getValues(); if (baseDNs.isEmpty() && argParser.baseDNArg.getDefaultValue() != null) { baseDNs.add(argParser.baseDNArg.getDefaultValue()); } for (final String baseDN : baseDNs) { checkBaseDN(baseDN, errorMessages); } return baseDNs; } private void setDirectoryManagerData(UserData uData, List<LocalizableMessage> errorMessages) { final String dmDN = argParser.directoryManagerDNArg.getValue(); if (dmDN.trim().length() == 0) { errorMessages.add(ERR_INSTALLDS_EMPTY_DN_RESPONSE.get()); } checkBaseDN(dmDN, errorMessages); uData.setDirectoryManagerDn(argParser.directoryManagerDNArg.getValue()); // Check the validity of the directory manager password if (argParser.getDirectoryManagerPassword().isEmpty()) { errorMessages.add(INFO_EMPTY_PWD.get()); } uData.setDirectoryManagerPwd(argParser.getDirectoryManagerPassword()); } private void checkBaseDN(String baseDN, List<LocalizableMessage> errorMessages) { try { new LdapName(baseDN); } catch (final Exception e) { errorMessages.add(ERR_INSTALLDS_CANNOT_PARSE_DN.get(baseDN, e.getMessage())); } } private void setPorts(UserData uData, List<LocalizableMessage> errorMessages) { try { final int ldapPort = argParser.ldapPortArg.getIntValue(); uData.setServerPort(ldapPort); final int adminConnectorPort = argParser.adminConnectorPortArg.getIntValue(); uData.setAdminConnectorPort(adminConnectorPort); if (!argParser.skipPortCheckArg.isPresent()) { checkCanUsePort(ldapPort, errorMessages); checkCanUsePort(adminConnectorPort, errorMessages); } if (argParser.jmxPortArg.isPresent()) { final int jmxPort = argParser.jmxPortArg.getIntValue(); uData.setServerJMXPort(jmxPort); if (!argParser.skipPortCheckArg.isPresent()) { checkCanUsePort(jmxPort, errorMessages); } } } catch (final ArgumentException ae) { errorMessages.add(ae.getMessageObject()); } } private void setImportData(List<String> baseDNs, UserData uData, List<LocalizableMessage> errorMessages) { NewSuffixOptions dataOptions; if (argParser.importLDIFArg.isPresent()) { // Check that the files exist final List<String> nonExistingFiles = new LinkedList<>(); for (final String file : argParser.importLDIFArg.getValues()) { if (!Utils.fileExists(file)) { nonExistingFiles.add(file); } } if (!nonExistingFiles.isEmpty()) { errorMessages.add(ERR_INSTALLDS_NO_SUCH_LDIF_FILE.get(joinAsString(", ", nonExistingFiles))); } final String rejectedFile = argParser.rejectedImportFileArg.getValue(); if (rejectedFile != null && !canWrite(rejectedFile)) { errorMessages.add(ERR_INSTALLDS_CANNOT_WRITE_REJECTED.get(rejectedFile)); } final String skippedFile = argParser.skippedImportFileArg.getValue(); if (skippedFile != null && !canWrite(skippedFile)) { errorMessages.add(ERR_INSTALLDS_CANNOT_WRITE_SKIPPED.get(skippedFile)); } dataOptions = NewSuffixOptions.createImportFromLDIF(baseDNs, argParser.importLDIFArg.getValues(), rejectedFile, skippedFile); } else if (argParser.addBaseEntryArg.isPresent()) { dataOptions = NewSuffixOptions.createBaseEntry(baseDNs); } else if (argParser.sampleDataArg.isPresent()) { dataOptions = NewSuffixOptions.createAutomaticallyGenerated(baseDNs, Integer.valueOf(argParser.sampleDataArg.getValue())); } else { dataOptions = NewSuffixOptions.createEmpty(baseDNs); } uData.setNewSuffixOptions(dataOptions); } private void setSecurityData(UserData uData, List<LocalizableMessage> errorMessages) { final boolean enableSSL = argParser.ldapsPortArg.isPresent(); int sslPort = -1; try { sslPort = enableSSL ? argParser.ldapsPortArg.getIntValue() : -1; } catch (final ArgumentException ae) { errorMessages.add(ae.getMessageObject()); } if (enableSSL && !argParser.skipPortCheckArg.isPresent()) { checkCanUsePort(sslPort, errorMessages); } checkCertificate(sslPort, enableSSL, uData, errorMessages); uData.setEnableWindowsService(argParser.enableWindowsServiceArg.isPresent()); uData.setStartServer(!argParser.doNotStartArg.isPresent()); } private void checkCertificate(int sslPort, boolean enableSSL, UserData uData, List<LocalizableMessage> errorMessages) { final LinkedList<String> keystoreAliases = new LinkedList<>(); uData.setHostName(argParser.hostNameArg.getValue()); final boolean enableStartTLS = argParser.enableStartTLSArg.isPresent(); final String pwd = argParser.getKeyStorePassword(); SecurityOptions.CertificateType certType = null; String pathToCertificat = null; if (argParser.generateSelfSignedCertificateArg.isPresent()) { certType = SecurityOptions.CertificateType.SELF_SIGNED_CERTIFICATE; } else if (argParser.useJavaKeyStoreArg.isPresent()) { certType = SecurityOptions.CertificateType.JKS; pathToCertificat = argParser.useJavaKeyStoreArg.getValue(); } else if (argParser.useJCEKSArg.isPresent()) { certType = SecurityOptions.CertificateType.JCEKS; pathToCertificat = argParser.useJCEKSArg.getValue(); } else if (argParser.usePkcs11Arg.isPresent()) { certType = SecurityOptions.CertificateType.PKCS11; pathToCertificat = argParser.usePkcs11Arg.getValue(); } else if (argParser.usePkcs12Arg.isPresent()) { certType = SecurityOptions.CertificateType.PKCS12; pathToCertificat = argParser.usePkcs12Arg.getValue(); } else { certType = SecurityOptions.CertificateType.NO_CERTIFICATE; } Collection<String> certNicknames = argParser.certNicknameArg.getValues(); if (pathToCertificat != null) { checkCertificateInKeystore(certType, pathToCertificat, pwd, certNicknames, errorMessages, keystoreAliases); if (certNicknames.isEmpty() && !keystoreAliases.isEmpty()) { certNicknames = Arrays.asList(keystoreAliases.getFirst()); } } final SecurityOptions securityOptions = SecurityOptions.createOptionsForCertificatType( certType, pathToCertificat, pwd, enableSSL, enableStartTLS, sslPort, certNicknames); uData.setSecurityOptions(securityOptions); } private void checkCanUsePort(int port, List<LocalizableMessage> errorMessages) { if (!SetupUtils.canUseAsPort(port)) { errorMessages.add(getCannotBindErrorMessage(port)); } } private LocalizableMessage getCannotBindErrorMessage(int port) { if (SetupUtils.isPrivilegedPort(port)) { return ERR_INSTALLDS_CANNOT_BIND_TO_PRIVILEGED_PORT.get(port); } return ERR_INSTALLDS_CANNOT_BIND_TO_PORT.get(port); } /** * This method updates the contents of a UserData object with what the user * specified in the command-line. If the user did not provide explicitly some * data or if the provided data is not valid, it prompts the user to provide * it. * * @param uData * the UserData object to be updated. * @throws UserDataException * if the user did not manage to provide the keystore password after * a certain number of tries. * @throws ClientException * if something went wrong when reading inputs. */ private void promptIfRequired(UserData uData) throws UserDataException, ClientException { uData.setQuiet(isQuiet()); uData.setVerbose(isVerbose()); uData.setConnectTimeout(getConnectTimeout()); promptIfRequiredForDirectoryManager(uData); promptIfRequiredForPortData(uData); uData.setNewSuffixOptions(promptIfRequiredForImportData(uData)); uData.setSecurityOptions(promptIfRequiredForSecurityData(uData)); uData.setEnableWindowsService(promptIfRequiredForWindowsService()); uData.setStartServer(promptIfRequiredForStartServer()); } /** * This method updates the contents of a UserData object with what the user * specified in the command-line for the Directory Manager parameters. If the * user did not provide explicitly some data or if the provided data is not * valid, it prompts the user to provide it. * * @param uData * the UserData object to be updated. * @throws UserDataException * if something went wrong checking the data. * @throws ClientException * if something went wrong checking passwords. */ private void promptIfRequiredForDirectoryManager(UserData uData) throws UserDataException, ClientException { final LinkedList<String> dns = promptIfRequiredForDNs( argParser.directoryManagerDNArg, INFO_INSTALLDS_PROMPT_ROOT_DN.get(), true); uData.setDirectoryManagerDn(dns.getFirst()); int nTries = 0; String pwd = argParser.getDirectoryManagerPassword(); while (pwd == null) { if (nTries >= CONFIRMATION_MAX_TRIES) { throw new UserDataException(null, ERR_TRIES_LIMIT_REACHED.get(CONFIRMATION_MAX_TRIES)); } // Prompt for password and confirm. char[] pwd1 = readPassword(INFO_INSTALLDS_PROMPT_ROOT_PASSWORD.get()); while (pwd1 == null || pwd1.length == 0) { println(); println(INFO_EMPTY_PWD.get()); println(); pwd1 = readPassword(INFO_INSTALLDS_PROMPT_ROOT_PASSWORD.get()); } final char[] pwd2 = readPassword(INFO_INSTALLDS_PROMPT_CONFIRM_ROOT_PASSWORD.get()); if (Arrays.equals(pwd1, pwd2)) { pwd = String.valueOf(pwd1); } else { println(); println(ERR_INSTALLDS_PASSWORDS_DONT_MATCH.get()); } nTries++; } uData.setDirectoryManagerPwd(pwd); } /** * This method returns a list of DNs. It checks that the provided list of DNs * actually contain some values. If no valid values are found it prompts the * user to provide a valid DN. * * @param arg * the Argument that the user provided to specify the DNs. * @param promptMsg * the prompt message to be displayed. * @param includeLineBreak * whether to include a line break before the first prompt or not. * @return a list of valid DNs. * @throws UserDataException * if something went wrong checking the data. */ private LinkedList<String> promptIfRequiredForDNs(StringArgument arg, LocalizableMessage promptMsg, boolean includeLineBreak) throws UserDataException { final LinkedList<String> dns = new LinkedList<>(); boolean usedProvided = false; boolean firstPrompt = true; int nTries = 0; while (dns.isEmpty()) { if (nTries >= CONFIRMATION_MAX_TRIES) { throw new UserDataException(null, ERR_TRIES_LIMIT_REACHED.get(CONFIRMATION_MAX_TRIES)); } boolean prompted = false; if (usedProvided || !arg.isPresent()) { if (firstPrompt && includeLineBreak) { println(); } try { final String dn = readInput(promptMsg, arg.getDefaultValue()); firstPrompt = false; dns.add(dn); prompted = true; } catch (final ClientException ce) { logger.warn(LocalizableMessage.raw("Error reading input: "+ce, ce)); } } else { dns.addAll(arg.getValues()); usedProvided = true; } final List<String> toRemove = new LinkedList<>(); for (final String dn : dns) { try { new LdapName(dn); if (dn.trim().length() == 0) { toRemove.add(dn); println(ERR_INSTALLDS_EMPTY_DN_RESPONSE.get()); } } catch (final Exception e) { toRemove.add(dn); final LocalizableMessage message = prompted ? ERR_INSTALLDS_INVALID_DN_RESPONSE.get() : ERR_INSTALLDS_CANNOT_PARSE_DN.get(dn, e.getMessage()); println(message); } } if (!toRemove.isEmpty()) { println(); } dns.removeAll(toRemove); nTries++; } return dns; } /** * This method updates the contents of a UserData object with what the user * specified in the command-line for the administration connector, LDAP and * JMX port parameters. If the user did not provide explicitly some data or * if the provided data is not valid, it prompts the user to provide it. * Note: this method does not update nor check the LDAPS port. * * @param uData * the UserData object to be updated. */ private void promptIfRequiredForPortData(UserData uData) { uData.setHostName(promptForHostNameIfRequired()); final List<Integer> usedPorts = new LinkedList<>(); // Determine the LDAP port number. final int ldapPort = promptIfRequiredForPortData(argParser.ldapPortArg, INFO_INSTALLDS_PROMPT_LDAPPORT.get(), usedPorts, true); uData.setServerPort(ldapPort); usedPorts.add(ldapPort); // Determine the Admin Connector port number. final int adminConnectorPort = promptIfRequiredForPortData(argParser.adminConnectorPortArg, INFO_INSTALLDS_PROMPT_ADMINCONNECTORPORT.get(), usedPorts, true); uData.setAdminConnectorPort(adminConnectorPort); usedPorts.add(adminConnectorPort); if (argParser.jmxPortArg.isPresent()) { final int jmxPort = promptIfRequiredForPortData(argParser.jmxPortArg, INFO_INSTALLDS_PROMPT_JMXPORT.get(), usedPorts, true); uData.setServerJMXPort(jmxPort); } else { uData.setServerJMXPort(-1); } } /** * This method returns a valid port value. It checks that the provided * argument contains a valid port. If a valid port is not found it prompts the * user to provide a valid port. * * @param portArg * the Argument that the user provided to specify the port. * @param promptMsg * the prompt message to be displayed. * @param usedPorts * the list of ports the user provided before for other connection * handlers. * @param includeLineBreak * whether to include a line break before the first prompt or not. * @return a valid port number. */ private int promptIfRequiredForPortData(IntegerArgument portArg, LocalizableMessage promptMsg, Collection<Integer> usedPorts, boolean includeLineBreak) { int portNumber = -1; boolean usedProvided = false; boolean firstPrompt = true; while (portNumber == -1) { try { boolean prompted = false; if (usedProvided || !portArg.isPresent()) { int defaultValue = -1; if (portArg.getDefaultValue() != null) { defaultValue = Integer.parseInt(portArg.getDefaultValue()); } if (firstPrompt && includeLineBreak) { println(); } portNumber = -1; while (portNumber == -1) { try { portNumber = readPort(promptMsg, defaultValue); } catch (final ClientException ce) { portNumber = -1; logger.warn(LocalizableMessage.raw("Error reading input: "+ce, ce)); } } prompted = true; firstPrompt = false; } else { portNumber = portArg.getIntValue(); usedProvided = true; } if (!argParser.skipPortCheckArg.isPresent() && !SetupUtils.canUseAsPort(portNumber)) { final LocalizableMessage message = getCannotBindErrorMessage(portNumber); if (prompted || includeLineBreak) { println(); } println(message); if (!SetupUtils.isPrivilegedPort(portNumber)) { println(); } portNumber = -1; } if (portNumber != -1 && usedPorts.contains(portNumber)) { println(ERR_CONFIGDS_PORT_ALREADY_SPECIFIED.get(portNumber)); println(); portNumber = -1; } } catch (final ArgumentException ae) { println(ae.getMessageObject()); } } return portNumber; } /** * This method returns what the user specified in the command-line for the * base DN and data import parameters. If the user did not provide explicitly * some data or if the provided data is not valid, it prompts the user to * provide it. * * @param uData * The UserData object to be updated. * @return the NewSuffixOptions telling how to import data * @throws UserDataException * if something went wrong checking the data. */ private NewSuffixOptions promptIfRequiredForImportData(final UserData uData) throws UserDataException { boolean prompt = true; if (!argParser.baseDNArg.isPresent()) { println(); try { prompt = confirmAction(INFO_INSTALLDS_PROVIDE_BASE_DN_PROMPT.get(), true); } catch (final ClientException ce) { prompt = true; logger.warn(LocalizableMessage.raw("Error reading input: " + ce, ce)); } } if (!prompt) { return NewSuffixOptions.createEmpty(new LinkedList<String>()); } uData.setBackendType(getOrPromptForBackendType()); // Add default value for base DN on first prompt if (argParser.baseDNArg.getDefaultValue() == null) { argParser.baseDNArg.setDefaultValue(Installation.DEFAULT_INTERACTIVE_BASE_DN); } // Check the validity of the base DNs final List<String> baseDNs = promptIfRequiredForDNs(argParser.baseDNArg, INFO_INSTALLDS_PROMPT_BASEDN.get(), true); return promptIfRequiredForDataOptions(baseDNs); } private ManagedObjectDefinition<? extends BackendCfgClient, ? extends BackendCfg> getOrPromptForBackendType() { if (argParser.backendTypeArg.isPresent()) { final ManagedObjectDefinition<? extends BackendCfgClient, ? extends BackendCfg> backend = backendTypeHelper.retrieveBackendTypeFromName(argParser.backendTypeArg.getValue().toLowerCase()); if ( backend != null) { return backend; } println(); println(ERR_INSTALLDS_NO_SUCH_BACKEND_TYPE.get( argParser.backendTypeArg.getValue(), backendTypeHelper.getPrintableBackendTypeNames())); } return promptForBackendType(); } private ManagedObjectDefinition<? extends BackendCfgClient,? extends BackendCfg> promptForBackendType() { println(); int backendTypeIndex = 1; final List<ManagedObjectDefinition<? extends BackendCfgClient, ? extends BackendCfg>> backendTypes = backendTypeHelper.getBackendTypes(); if (backendTypes.size() == 1) { final ManagedObjectDefinition<? extends BackendCfgClient, ? extends BackendCfg> backendType = backendTypes.get(0); println(INFO_INSTALLDS_BACKEND_TYPE_USED.get(backendType.getUserFriendlyName())); return backendType; } try { final MenuResult<Integer> m = getBackendTypeMenu().run(); if (m.isSuccess()) { backendTypeIndex = m.getValue(); } } catch (final ClientException ce) { logger.warn(LocalizableMessage.raw("Error reading input: " + ce, ce)); } return backendTypes.get(backendTypeIndex - 1); } private Menu<Integer> getBackendTypeMenu() { final MenuBuilder<Integer> builder = new MenuBuilder<>(this); builder.setPrompt(INFO_INSTALLDS_PROMPT_BACKEND_TYPE.get()); int index = 1; for (final ManagedObjectDefinition<?, ?> backendType : backendTypeHelper.getBackendTypes()) { builder.addNumberedOption(backendType.getUserFriendlyName(), MenuResult.success(index++)); } final int printableIndex = getPromptedBackendTypeIndex(); builder.setDefault(LocalizableMessage.raw(Integer.toString(printableIndex)), MenuResult.success(printableIndex)); return builder.toMenu(); } private int getPromptedBackendTypeIndex() { if (lastResetBackendType != null) { return backendTypeHelper.getBackendTypes().indexOf(lastResetBackendType) + 1; } return 1; } private NewSuffixOptions promptIfRequiredForDataOptions(List<String> baseDNs) { NewSuffixOptions dataOptions; if (argParser.importLDIFArg.isPresent()) { // Check that the files exist final List<String> nonExistingFiles = new LinkedList<>(); final List<String> importLDIFFiles = new LinkedList<>(); for (final String file : argParser.importLDIFArg.getValues()) { if (!Utils.fileExists(file)) { nonExistingFiles.add(file); } else { importLDIFFiles.add(file); } } if (!nonExistingFiles.isEmpty()) { println(); println(ERR_INSTALLDS_NO_SUCH_LDIF_FILE.get(joinAsString(", ", nonExistingFiles))); } readImportLdifFile(importLDIFFiles, lastResetImportFile); String rejectedFile = readValidFilePath(argParser.rejectedImportFileArg, lastResetRejectedFile, ERR_INSTALLDS_CANNOT_WRITE_REJECTED, INFO_INSTALLDS_PROMPT_REJECTED_FILE); String skippedFile = readValidFilePath(argParser.skippedImportFileArg, lastResetSkippedFile, ERR_INSTALLDS_CANNOT_WRITE_SKIPPED, INFO_INSTALLDS_PROMPT_SKIPPED_FILE); dataOptions = NewSuffixOptions.createImportFromLDIF(baseDNs, importLDIFFiles, rejectedFile, skippedFile); } else if (argParser.addBaseEntryArg.isPresent()) { dataOptions = NewSuffixOptions.createBaseEntry(baseDNs); } else if (argParser.sampleDataArg.isPresent()) { int numUsers; try { numUsers = argParser.sampleDataArg.getIntValue(); } catch (final ArgumentException ae) { println(); println(ae.getMessageObject()); final LocalizableMessage message = INFO_INSTALLDS_PROMPT_NUM_ENTRIES.get(); numUsers = promptForInteger(message, 2000, 0, Integer.MAX_VALUE); } dataOptions = NewSuffixOptions.createAutomaticallyGenerated(baseDNs, numUsers); } else { final int POPULATE_TYPE_LEAVE_EMPTY = 1; final int POPULATE_TYPE_BASE_ONLY = 2; final int POPULATE_TYPE_IMPORT_FROM_LDIF = 3; final int POPULATE_TYPE_GENERATE_SAMPLE_DATA = 4; final int[] indexes = {POPULATE_TYPE_LEAVE_EMPTY, POPULATE_TYPE_BASE_ONLY, POPULATE_TYPE_IMPORT_FROM_LDIF, POPULATE_TYPE_GENERATE_SAMPLE_DATA}; final LocalizableMessage[] msgs = new LocalizableMessage[] { INFO_INSTALLDS_POPULATE_OPTION_LEAVE_EMPTY.get(), INFO_INSTALLDS_POPULATE_OPTION_BASE_ONLY.get(), INFO_INSTALLDS_POPULATE_OPTION_IMPORT_LDIF.get(), INFO_INSTALLDS_POPULATE_OPTION_GENERATE_SAMPLE.get() }; final MenuBuilder<Integer> builder = new MenuBuilder<>(this); builder.setPrompt(INFO_INSTALLDS_HEADER_POPULATE_TYPE.get()); for (int i=0; i<indexes.length; i++) { builder.addNumberedOption(msgs[i], MenuResult.success(indexes[i])); } if (lastResetPopulateOption == null) { builder.setDefault(LocalizableMessage.raw( String.valueOf(POPULATE_TYPE_LEAVE_EMPTY)), MenuResult.success(POPULATE_TYPE_LEAVE_EMPTY)); } else { switch (lastResetPopulateOption) { case LEAVE_DATABASE_EMPTY: builder.setDefault(LocalizableMessage.raw( String.valueOf(POPULATE_TYPE_LEAVE_EMPTY)), MenuResult.success(POPULATE_TYPE_LEAVE_EMPTY)); break; case IMPORT_FROM_LDIF_FILE: builder.setDefault(LocalizableMessage.raw( String.valueOf(POPULATE_TYPE_IMPORT_FROM_LDIF)), MenuResult.success(POPULATE_TYPE_IMPORT_FROM_LDIF)); break; case IMPORT_AUTOMATICALLY_GENERATED_DATA: builder.setDefault(LocalizableMessage.raw( String.valueOf(POPULATE_TYPE_GENERATE_SAMPLE_DATA)), MenuResult.success(POPULATE_TYPE_GENERATE_SAMPLE_DATA)); break; default: builder.setDefault(LocalizableMessage.raw( String.valueOf(POPULATE_TYPE_BASE_ONLY)), MenuResult.success(POPULATE_TYPE_BASE_ONLY)); } } final Menu<Integer> menu = builder.toMenu(); int populateType; try { final MenuResult<Integer> m = menu.run(); if (m.isSuccess()) { populateType = m.getValue(); } else { // Should never happen. throw new RuntimeException(); } } catch (final ClientException ce) { populateType = POPULATE_TYPE_BASE_ONLY; logger.warn(LocalizableMessage.raw("Error reading input: "+ce, ce)); } if (populateType == POPULATE_TYPE_IMPORT_FROM_LDIF) { final List<String> importLDIFFiles = new LinkedList<>(); readImportLdifFile(importLDIFFiles, null); String rejectedFile = readValidFilePath(argParser.rejectedImportFileArg, null, ERR_INSTALLDS_CANNOT_WRITE_REJECTED, INFO_INSTALLDS_PROMPT_REJECTED_FILE); String skippedFile = readValidFilePath(argParser.skippedImportFileArg, null, ERR_INSTALLDS_CANNOT_WRITE_SKIPPED, INFO_INSTALLDS_PROMPT_SKIPPED_FILE); dataOptions = NewSuffixOptions.createImportFromLDIF(baseDNs, importLDIFFiles, rejectedFile, skippedFile); } else if (populateType == POPULATE_TYPE_GENERATE_SAMPLE_DATA) { final LocalizableMessage message = INFO_INSTALLDS_PROMPT_NUM_ENTRIES.get(); int defaultValue = lastResetNumEntries != null ? lastResetNumEntries : 2000; final int numUsers = promptForInteger(message, defaultValue, 0, Integer.MAX_VALUE); dataOptions = NewSuffixOptions.createAutomaticallyGenerated(baseDNs, numUsers); } else if (populateType == POPULATE_TYPE_LEAVE_EMPTY) { dataOptions = NewSuffixOptions.createEmpty(baseDNs); } else if (populateType == POPULATE_TYPE_BASE_ONLY) { dataOptions = NewSuffixOptions.createBaseEntry(baseDNs); } else { throw new IllegalStateException("Unexpected populateType: " + populateType); } } return dataOptions; } private void readImportLdifFile(final List<String> importLDIFFiles, String defaultValue) { while (importLDIFFiles.isEmpty()) { println(); try { final String path = readInput(INFO_INSTALLDS_PROMPT_IMPORT_FILE.get(), defaultValue); if (Utils.fileExists(path)) { importLDIFFiles.add(path); } else { println(); println(ERR_INSTALLDS_NO_SUCH_LDIF_FILE.get(path)); } } catch (final ClientException ce) { logger.warn(LocalizableMessage.raw("Error reading input: "+ce, ce)); } } } private String readValidFilePath(StringArgument arg, String defaultValue, Arg1<Object> errCannotWriteFile, Arg0 infoPromptFile) { String file = arg.getValue(); if (file != null) { while (!canWrite(file)) { println(); println(errCannotWriteFile.get(file)); println(); try { file = readInput(infoPromptFile.get(), defaultValue); } catch (final ClientException ce) { logger.warn(LocalizableMessage.raw("Error reading input: "+ce, ce)); } } } return file; } /** * This method returns what the user specified in the command-line for the * security parameters. If the user did not provide explicitly some data or if * the provided data is not valid, it prompts the user to provide it. * * @param uData * the current UserData object. * @return the {@link SecurityOptions} to be used when starting the server * @throws UserDataException * if the user did not manage to provide the keystore password after * a certain number of tries. * @throws ClientException * If an error occurs when reading inputs. */ private SecurityOptions promptIfRequiredForSecurityData(UserData uData) throws UserDataException, ClientException { // Check that the security data provided is valid. boolean enableSSL = false; boolean enableStartTLS = false; int ldapsPort = -1; final List<Integer> usedPorts = new LinkedList<>(); usedPorts.add(uData.getServerPort()); if (uData.getServerJMXPort() != -1) { usedPorts.add(uData.getServerJMXPort()); } // Ask to enable SSL if (!argParser.ldapsPortArg.isPresent()) { println(); try { final boolean defaultValue = lastResetEnableSSL != null ? lastResetEnableSSL : false; enableSSL = confirmAction(INFO_INSTALLDS_PROMPT_ENABLE_SSL.get(), defaultValue); if (enableSSL) { ldapsPort = promptIfRequiredForPortData(argParser.ldapsPortArg, INFO_INSTALLDS_PROMPT_LDAPSPORT.get(), usedPorts, false); } } catch (final ClientException ce) { logger.warn(LocalizableMessage.raw("Error reading input: "+ce, ce)); } } else { ldapsPort = promptIfRequiredForPortData(argParser.ldapsPortArg, INFO_INSTALLDS_PROMPT_LDAPSPORT.get(), usedPorts, true); enableSSL = true; } // Ask to enable Start TLS if (!argParser.enableStartTLSArg.isPresent()) { println(); try { final boolean defaultValue = lastResetEnableStartTLS != null ? lastResetEnableStartTLS : false; enableStartTLS = confirmAction(INFO_INSTALLDS_ENABLE_STARTTLS.get(), defaultValue); } catch (final ClientException ce) { logger.warn(LocalizableMessage.raw("Error reading input: "+ce, ce)); } } else { enableStartTLS = true; } SecurityOptions securityOptions; if (argParser.generateSelfSignedCertificateArg.isPresent()) { securityOptions = SecurityOptions.createSelfSignedCertificateOptions( enableSSL, enableStartTLS, ldapsPort); } else if (argParser.useJavaKeyStoreArg.isPresent()) { securityOptions = createSecurityOptionsPrompting(SecurityOptions.CertificateType.JKS, enableSSL, enableStartTLS, ldapsPort); } else if (argParser.useJCEKSArg.isPresent()) { securityOptions = createSecurityOptionsPrompting(SecurityOptions.CertificateType.JCEKS, enableSSL, enableStartTLS, ldapsPort); } else if (argParser.usePkcs12Arg.isPresent()) { securityOptions = createSecurityOptionsPrompting(SecurityOptions.CertificateType.PKCS12, enableSSL, enableStartTLS, ldapsPort); } else if (argParser.usePkcs11Arg.isPresent()) { securityOptions = createSecurityOptionsPrompting(SecurityOptions.CertificateType.PKCS11, enableSSL, enableStartTLS, ldapsPort); } else if (!enableSSL && !enableStartTLS) { // If the user did not want to enable SSL or start TLS do not ask // to create a certificate. securityOptions = SecurityOptions.createNoCertificateOptions(); } else { final int SELF_SIGNED = 1; final int JKS = 2; final int JCEKS = 3; final int PKCS12 = 4; final int PKCS11 = 5; final int[] indexes = {SELF_SIGNED, JKS, JCEKS, PKCS12, PKCS11}; final LocalizableMessage[] msgs = { INFO_INSTALLDS_CERT_OPTION_SELF_SIGNED.get(), INFO_INSTALLDS_CERT_OPTION_JKS.get(), INFO_INSTALLDS_CERT_OPTION_JCEKS.get(), INFO_INSTALLDS_CERT_OPTION_PKCS12.get(), INFO_INSTALLDS_CERT_OPTION_PKCS11.get() }; final MenuBuilder<Integer> builder = new MenuBuilder<>(this); builder.setPrompt(INFO_INSTALLDS_HEADER_CERT_TYPE.get()); for (int i=0; i<indexes.length; i++) { builder.addNumberedOption(msgs[i], MenuResult.success(indexes[i])); } if (lastResetCertType == null) { builder.setDefault(LocalizableMessage.raw(String.valueOf(SELF_SIGNED)), MenuResult.success(SELF_SIGNED)); } else { switch (lastResetCertType) { case JKS: builder.setDefault(LocalizableMessage.raw(String.valueOf(JKS)), MenuResult.success(JKS)); break; case JCEKS: builder.setDefault(LocalizableMessage.raw(String.valueOf(JCEKS)), MenuResult.success(JCEKS)); break; case PKCS11: builder.setDefault(LocalizableMessage.raw(String.valueOf(PKCS11)), MenuResult.success(PKCS11)); break; case PKCS12: builder.setDefault(LocalizableMessage.raw(String.valueOf(PKCS12)), MenuResult.success(PKCS12)); break; default: builder.setDefault(LocalizableMessage.raw(String.valueOf(SELF_SIGNED)), MenuResult.success(SELF_SIGNED)); } } final Menu<Integer> menu = builder.toMenu(); int certType; try { final MenuResult<Integer> m = menu.run(); if (m.isSuccess()) { certType = m.getValue(); } else { // Should never happen. throw new RuntimeException(); } } catch (final ClientException ce) { logger.warn(LocalizableMessage.raw("Error reading input: "+ce, ce)); certType = SELF_SIGNED; } if (certType == SELF_SIGNED) { securityOptions = SecurityOptions.createSelfSignedCertificateOptions( enableSSL, enableStartTLS, ldapsPort); } else if (certType == JKS) { securityOptions = createSecurityOptionsPrompting(SecurityOptions.CertificateType.JKS, enableSSL, enableStartTLS, ldapsPort); } else if (certType == JCEKS) { securityOptions = createSecurityOptionsPrompting( SecurityOptions.CertificateType.JCEKS, enableSSL, enableStartTLS, ldapsPort); } else if (certType == PKCS12) { securityOptions = createSecurityOptionsPrompting( SecurityOptions.CertificateType.PKCS12, enableSSL, enableStartTLS, ldapsPort); } else if (certType == PKCS11) { securityOptions = createSecurityOptionsPrompting( SecurityOptions.CertificateType.PKCS11, enableSSL, enableStartTLS, ldapsPort); } else { throw new IllegalStateException("Unexpected cert type: "+ certType); } } return securityOptions; } /** * This method returns what the user specified in the command-line for the * Windows Service parameters. If the user did not provide explicitly the * data, it prompts the user to provide it. * * @return whether windows service should be enabled */ private boolean promptIfRequiredForWindowsService() { boolean enableService = false; // If we are in Windows ask if the server must run as a windows service. if (isWindows()) { if (argParser.enableWindowsServiceArg.isPresent()) { enableService = true; } else { println(); final LocalizableMessage message = INFO_INSTALLDS_PROMPT_ENABLE_SERVICE.get(); try { final boolean defaultValue = (lastResetEnableWindowsService == null) ? false : lastResetEnableWindowsService; enableService = confirmAction(message, defaultValue); } catch (final ClientException ce) { logger.warn(LocalizableMessage.raw("Error reading input: "+ce, ce)); } } } return enableService; } /** * This method returns what the user specified in the command-line for the * Directory Manager parameters. If the user did not provide explicitly the * data, it prompts the user to provide it. * * @return whether server should be started */ private boolean promptIfRequiredForStartServer() { boolean startServer = false; if (!argParser.doNotStartArg.isPresent()) { println(); final LocalizableMessage message = INFO_INSTALLDS_PROMPT_START_SERVER.get(); try { final boolean defaultValue = (lastResetStartServer == null) ? true : lastResetStartServer; startServer = confirmAction(message, defaultValue); } catch (final ClientException ce) { logger.warn(LocalizableMessage.raw("Error reading input: "+ce, ce)); startServer = true; } } return startServer; } /** * Checks that the provided parameters are valid to access an existing key * store. This method adds the encountered errors to the provided list of * LocalizableMessage. It also adds the alias (nicknames) found to the * provided list of String. * * @param type * the type of key store. * @param path * the path of the key store. * @param pwd * the password (PIN) to access the key store. * @param certNicknames * the certificate nicknames that we are looking for (or null if we * just one to get the one that is in the key store). * @param errorMessages * the list that will be updated with the errors encountered. * @param nicknameList * the list that will be updated with the nicknames found in the key * store. */ public static void checkCertificateInKeystore(SecurityOptions.CertificateType type, String path, String pwd, Collection<String> certNicknames, Collection<LocalizableMessage> errorMessages, Collection<String> nicknameList) { boolean errorWithPath = false; if (type != SecurityOptions.CertificateType.PKCS11) { final File f = new File(path); if (!f.exists()) { errorMessages.add(INFO_KEYSTORE_PATH_DOES_NOT_EXIST.get()); errorWithPath = true; } else if (!f.isFile()) { errorMessages.add(INFO_KEYSTORE_PATH_NOT_A_FILE.get()); errorWithPath = true; } } if (!errorWithPath) { try { CertificateManager certManager; switch (type) { case JKS: certManager = new CertificateManager( path, CertificateManager.KEY_STORE_TYPE_JKS, pwd); break; case JCEKS: certManager = new CertificateManager( path, CertificateManager.KEY_STORE_TYPE_JCEKS, pwd); break; case PKCS12: certManager = new CertificateManager( path, CertificateManager.KEY_STORE_TYPE_PKCS12, pwd); break; case PKCS11: certManager = new CertificateManager( CertificateManager.KEY_STORE_PATH_PKCS11, CertificateManager.KEY_STORE_TYPE_PKCS11, pwd); break; default: throw new IllegalArgumentException("Invalid type: "+type); } final String[] aliases = certManager.getCertificateAliases(); if (aliases == null || aliases.length == 0) { // Could not retrieve any certificate switch (type) { case JKS: errorMessages.add(INFO_JKS_KEYSTORE_DOES_NOT_EXIST.get()); break; case JCEKS: errorMessages.add(INFO_JCEKS_KEYSTORE_DOES_NOT_EXIST.get()); break; case PKCS12: errorMessages.add(INFO_PKCS12_KEYSTORE_DOES_NOT_EXIST.get()); break; case PKCS11: errorMessages.add(INFO_PKCS11_KEYSTORE_DOES_NOT_EXIST.get()); break; default: throw new IllegalArgumentException("Invalid type: "+type); } } else if (certManager.hasRealAliases()) { Collections.addAll(nicknameList, aliases); final String aliasString = joinAsString(", ", nicknameList); if (certNicknames.isEmpty() && aliases.length > 1) { errorMessages.add(ERR_INSTALLDS_MUST_PROVIDE_CERTNICKNAME.get(aliasString)); } for (String certNickname : certNicknames) { // Check if the certificate alias is in the list. boolean found = false; for (int i = 0; i < aliases.length && !found; i++) { found = aliases[i].equalsIgnoreCase(certNickname); } if (!found) { errorMessages.add(ERR_INSTALLDS_CERTNICKNAME_NOT_FOUND.get(aliasString)); } } } } catch (final KeyStoreException ke) { // issue OPENDJ-18, related to JDK bug if (StaticUtils.stackTraceContainsCause(ke, ArithmeticException.class)) { errorMessages.add(INFO_ERROR_ACCESSING_KEYSTORE_JDK_BUG.get()); } else { // Could not access to the key store: because the password is no good, // because the provided file is not a valid key store, etc. switch (type) { case JKS: errorMessages.add(INFO_ERROR_ACCESSING_JKS_KEYSTORE.get()); break; case JCEKS: errorMessages.add(INFO_ERROR_ACCESSING_JCEKS_KEYSTORE.get()); break; case PKCS12: errorMessages.add(INFO_ERROR_ACCESSING_PKCS12_KEYSTORE.get()); break; case PKCS11: errorMessages.add(INFO_ERROR_ACCESSING_PKCS11_KEYSTORE.get()); break; default: throw new IllegalArgumentException("Invalid type: " + type, ke); } } } } } /** * Creates a SecurityOptions object that corresponds to the provided * parameters. If the parameters are not valid, it prompts the user to provide * them. * * @param type * the keystore type. * @param enableSSL * whether to enable SSL or not. * @param enableStartTLS * whether to enable StartTLS or not. * @param ldapsPort * the LDAPS port to use. * @return a SecurityOptions object that corresponds to the provided * parameters (or to what the user provided after being prompted). * @throws UserDataException * if the user did not manage to provide the keystore password after * a certain number of tries. * @throws ClientException */ private SecurityOptions createSecurityOptionsPrompting(SecurityOptions.CertificateType type, boolean enableSSL, boolean enableStartTLS, int ldapsPort) throws UserDataException, ClientException { SecurityOptions securityOptions; String path; Collection<String> certNicknames = argParser.certNicknameArg.getValues(); String pwd = argParser.getKeyStorePassword(); if (pwd != null && pwd.length() == 0) { pwd = null; } LocalizableMessage pathPrompt; String defaultPathValue; switch (type) { case JKS: path = argParser.useJavaKeyStoreArg.getValue(); pathPrompt = INFO_INSTALLDS_PROMPT_JKS_PATH.get(); defaultPathValue = argParser.useJavaKeyStoreArg.getValue(); if (defaultPathValue == null) { defaultPathValue = lastResetKeyStorePath; } break; case JCEKS: path = argParser.useJCEKSArg.getValue(); pathPrompt = INFO_INSTALLDS_PROMPT_JCEKS_PATH.get(); defaultPathValue = argParser.useJCEKSArg.getValue(); if (defaultPathValue == null) { defaultPathValue = lastResetKeyStorePath; } break; case PKCS11: path = null; defaultPathValue = null; pathPrompt = null; break; case PKCS12: path = argParser.usePkcs12Arg.getValue(); defaultPathValue = argParser.usePkcs12Arg.getValue(); if (defaultPathValue == null) { defaultPathValue = lastResetKeyStorePath; } pathPrompt = INFO_INSTALLDS_PROMPT_PKCS12_PATH.get(); break; default: throw new IllegalStateException( "Called promptIfRequiredCertificate with invalid type: "+type); } final List<LocalizableMessage> errorMessages = new LinkedList<>(); final LinkedList<String> keystoreAliases = new LinkedList<>(); boolean firstTry = true; int nPasswordPrompts = 0; while (!errorMessages.isEmpty() || firstTry) { boolean prompted = false; if (!errorMessages.isEmpty()) { println(); println(Utils.getMessageFromCollection(errorMessages, formatter.getLineBreak().toString())); } if (type != SecurityOptions.CertificateType.PKCS11 && (containsKeyStorePathErrorMessage(errorMessages) || path == null)) { println(); try { path = readInput(pathPrompt, defaultPathValue); } catch (final ClientException ce) { path = ""; logger.warn(LocalizableMessage.raw("Error reading input: "+ce, ce)); } prompted = true; if (pwd != null) { errorMessages.clear(); keystoreAliases.clear(); checkCertificateInKeystore(type, path, pwd, certNicknames, errorMessages, keystoreAliases); if (!errorMessages.isEmpty()) { // Reset password: this might be a new keystore pwd = null; } } } if (containsKeyStorePasswordErrorMessage(errorMessages) || pwd == null) { if (!prompted) { println(); } pwd = null; while (pwd == null) { if (nPasswordPrompts > LIMIT_KEYSTORE_PASSWORD_PROMPT) { throw new UserDataException(null, ERR_INSTALLDS_TOO_MANY_KEYSTORE_PASSWORD_TRIES.get(LIMIT_KEYSTORE_PASSWORD_PROMPT)); } pwd = String.valueOf(readPassword(INFO_INSTALLDS_PROMPT_KEYSTORE_PASSWORD.get())); nPasswordPrompts ++; } } if (containsCertNicknameErrorMessage(errorMessages)) { if (!prompted) { println(); } certNicknames = promptForCertificateNickname(keystoreAliases); } errorMessages.clear(); keystoreAliases.clear(); checkCertificateInKeystore(type, path, pwd, certNicknames, errorMessages, keystoreAliases); firstTry = false; } if (certNicknames.isEmpty() && !keystoreAliases.isEmpty()) { certNicknames = Arrays.asList(keystoreAliases.getFirst()); } switch (type) { case JKS: return SecurityOptions.createJKSCertificateOptions(path, pwd, enableSSL, enableStartTLS, ldapsPort, certNicknames); case JCEKS: return SecurityOptions.createJCEKSCertificateOptions(path, pwd, enableSSL, enableStartTLS, ldapsPort, certNicknames); case PKCS12: return SecurityOptions.createPKCS12CertificateOptions(path, pwd, enableSSL, enableStartTLS, ldapsPort, certNicknames); case PKCS11: return SecurityOptions.createPKCS11CertificateOptions(pwd, enableSSL, enableStartTLS, ldapsPort, certNicknames); default: throw new IllegalStateException("Called createSecurityOptionsPrompting with invalid type: " + type); } } /** * Tells if any of the error messages provided corresponds to a problem with * the key store path. * * @param msgs * the messages to analyze. * @return <CODE>true</CODE> if any of the error messages provided corresponds * to a problem with the key store path and <CODE>false</CODE> * otherwise. */ public static boolean containsKeyStorePathErrorMessage(Collection<LocalizableMessage> msgs) { for (final LocalizableMessage msg : msgs) { if (StaticUtils.hasDescriptor(msg, INFO_KEYSTORE_PATH_DOES_NOT_EXIST) || StaticUtils.hasDescriptor(msg, INFO_KEYSTORE_PATH_NOT_A_FILE) || StaticUtils.hasDescriptor(msg, INFO_JKS_KEYSTORE_DOES_NOT_EXIST) || StaticUtils.hasDescriptor(msg, INFO_JCEKS_KEYSTORE_DOES_NOT_EXIST) || StaticUtils.hasDescriptor(msg, INFO_PKCS12_KEYSTORE_DOES_NOT_EXIST) || StaticUtils.hasDescriptor(msg, INFO_PKCS11_KEYSTORE_DOES_NOT_EXIST) || StaticUtils.hasDescriptor(msg, INFO_ERROR_ACCESSING_JKS_KEYSTORE) || StaticUtils.hasDescriptor(msg, INFO_ERROR_ACCESSING_JCEKS_KEYSTORE) || StaticUtils.hasDescriptor(msg, INFO_ERROR_ACCESSING_PKCS12_KEYSTORE) || StaticUtils.hasDescriptor(msg, INFO_ERROR_ACCESSING_PKCS11_KEYSTORE)) { return true; } } return false; } /** * Tells if any of the error messages provided corresponds to a problem with * the key store password. * * @param msgs * the messages to analyze. * @return <CODE>true</CODE> if any of the error messages provided corresponds * to a problem with the key store password and <CODE>false</CODE> * otherwise. */ public static boolean containsKeyStorePasswordErrorMessage(Collection<LocalizableMessage> msgs) { for (final LocalizableMessage msg : msgs) { if (StaticUtils.hasDescriptor(msg, INFO_JKS_KEYSTORE_DOES_NOT_EXIST) || StaticUtils.hasDescriptor(msg, INFO_JCEKS_KEYSTORE_DOES_NOT_EXIST) || StaticUtils.hasDescriptor(msg, INFO_PKCS12_KEYSTORE_DOES_NOT_EXIST) || StaticUtils.hasDescriptor(msg, INFO_PKCS11_KEYSTORE_DOES_NOT_EXIST) || StaticUtils.hasDescriptor(msg, INFO_ERROR_ACCESSING_JKS_KEYSTORE) || StaticUtils.hasDescriptor(msg, INFO_ERROR_ACCESSING_JCEKS_KEYSTORE) || StaticUtils.hasDescriptor(msg, INFO_ERROR_ACCESSING_PKCS12_KEYSTORE) || StaticUtils.hasDescriptor(msg, INFO_ERROR_ACCESSING_PKCS11_KEYSTORE) || StaticUtils.hasDescriptor(msg, INFO_ERROR_ACCESSING_KEYSTORE_JDK_BUG)) { return true; } } return false; } /** * Tells if any of the error messages provided corresponds to a problem with * the certificate nickname. * * @param msgs * the messages to analyze. * @return <CODE>true</CODE> if any of the error messages provided corresponds * to a problem with the certificate nickname and <CODE>false</CODE> * otherwise. */ public static boolean containsCertNicknameErrorMessage( Collection<LocalizableMessage> msgs) { boolean found = false; for (final LocalizableMessage msg : msgs) { if (StaticUtils.hasDescriptor(msg, ERR_INSTALLDS_CERTNICKNAME_NOT_FOUND) || StaticUtils.hasDescriptor(msg, ERR_INSTALLDS_MUST_PROVIDE_CERTNICKNAME)) { found = true; break; } } return found; } /** * Interactively prompts (on standard output) the user to provide an integer * value. The answer provided must be parseable as an integer, and may be * required to be within a given set of bounds. It will keep prompting until * an acceptable value is given. * * @param prompt * The prompt to present to the user. * @param defaultValue * The default value to assume if the user presses ENTER without * typing anything, or <CODE>null</CODE> if there should not be a * default and the user must explicitly provide a value. * @param lowerBound * The lower bound that should be enforced, or <CODE>null</CODE> if * there is none. * @param upperBound * The upper bound that should be enforced, or <CODE>null</CODE> if * there is none. * @return The <CODE>int</CODE> value read from the user input. */ private int promptForInteger(LocalizableMessage prompt, Integer defaultValue, Integer lowerBound, Integer upperBound) { int returnValue = -1; while (returnValue == -1) { String s; try { s = readInput(prompt, String.valueOf(defaultValue)); } catch (final ClientException ce) { s = ""; logger.warn(LocalizableMessage.raw("Error reading input: "+ce, ce)); } if ("".equals(s)) { if (defaultValue == null) { println(ERR_INSTALLDS_INVALID_INTEGER_RESPONSE.get()); println(); } else { returnValue = defaultValue; } } else { try { final int intValue = Integer.parseInt(s); if (lowerBound != null && intValue < lowerBound) { println(ERR_INSTALLDS_INTEGER_BELOW_LOWER_BOUND.get(lowerBound)); println(); } else if (upperBound != null && intValue > upperBound) { println(ERR_INSTALLDS_INTEGER_ABOVE_UPPER_BOUND.get(upperBound)); println(); } else { returnValue = intValue; } } catch (final NumberFormatException nfe) { println(ERR_INSTALLDS_INVALID_INTEGER_RESPONSE.get()); println(); } } } return returnValue; } /** * Prompts the user to accept on the certificates that appears on the list and * returns the chosen certificate nickname. * * @param nicknames * the list of certificates the user must choose from. * @return the chosen certificate nickname. */ private Collection<String> promptForCertificateNickname(List<String> nicknames) { Collection<String> choosenNicknames = new ArrayList<>(); while (choosenNicknames.isEmpty()) { for (final String n : nicknames) { try { if (confirmAction(INFO_INSTALLDS_PROMPT_CERTNICKNAME.get(n), true)) { choosenNicknames.add(n); } } catch (final ClientException ce) { logger.warn(LocalizableMessage.raw("Error reading input: "+ce, ce)); } } } return choosenNicknames; } /** * It displays the information provided by the user. * * @param uData * the UserData that the user provided. */ private void printSummary(UserData uData) { println(); println(); println(INFO_INSTALLDS_SUMMARY.get()); final LocalizableMessage[] labels = { INFO_SERVER_PORT_LABEL.get(), INFO_ADMIN_CONNECTOR_PORT_LABEL.get(), INFO_INSTALLDS_SERVER_JMXPORT_LABEL.get(), INFO_SERVER_SECURITY_LABEL.get(), INFO_SERVER_DIRECTORY_MANAGER_DN_LABEL.get(), INFO_DIRECTORY_DATA_LABEL.get() }; final int jmxPort = uData.getServerJMXPort(); final LocalizableMessage[] values = { LocalizableMessage.raw(String.valueOf(uData.getServerPort())), LocalizableMessage.raw(String.valueOf(uData.getAdminConnectorPort())), LocalizableMessage.raw(jmxPort != -1 ? String.valueOf(jmxPort) : ""), LocalizableMessage.raw( Utils.getSecurityOptionsString(uData.getSecurityOptions(), false)), LocalizableMessage.raw(uData.getDirectoryManagerDn()), LocalizableMessage.raw(Utils.getDataDisplayString(uData)), }; int maxWidth = 0; for (final LocalizableMessage l : labels) { maxWidth = Math.max(maxWidth, l.length()); } for (int i=0; i<labels.length; i++) { StringBuilder sb = new StringBuilder(); if (values[i] != null) { final LocalizableMessage l = labels[i]; sb.append(l).append(" "); final String[] lines = values[i].toString().split(Constants.LINE_SEPARATOR); for (int j=0; j<lines.length; j++) { if (j != 0) { for (int k=0; k <= maxWidth; k++) { sb.append(" "); } } else { for (int k=0; k<maxWidth - l.length(); k++) { sb.append(" "); } } sb.append(lines[j]); println(LocalizableMessage.raw(sb)); sb = new StringBuilder(); } } } println(); if (uData.getStartServer()) { println(INFO_INSTALLDS_START_SERVER.get()); } else { println(INFO_INSTALLDS_DO_NOT_START_SERVER.get()); } if (isWindows()) { if (uData.getEnableWindowsService()) { println(INFO_INSTALLDS_ENABLE_WINDOWS_SERVICE.get()); } else { println(INFO_INSTALLDS_DO_NOT_ENABLE_WINDOWS_SERVICE.get()); } } } private void printEquivalentCommandLine(UserData uData) { println(); println(INFO_INSTALL_SETUP_EQUIVALENT_COMMAND_LINE.get()); println(); final List<String> cmd = Utils.getSetupEquivalentCommandLine(uData); println(LocalizableMessage.raw(Utils.getFormattedEquivalentCommandLine(cmd, formatter))); } /** * This method asks the user to confirm to continue the setup. It basically * displays the information provided by the user and at the end proposes a * menu with the different options to choose from. * * @return the answer provided by the user: cancel setup, continue setup or * provide information again. */ private ConfirmCode askForConfirmation() { ConfirmCode returnValue; println(); println(); final LocalizableMessage[] msgs = new LocalizableMessage[] { INFO_INSTALLDS_CONFIRM_INSTALL.get(), INFO_INSTALLDS_PROVIDE_DATA_AGAIN.get(), INFO_INSTALLDS_PRINT_EQUIVALENT_COMMAND_LINE.get(), INFO_INSTALLDS_CANCEL.get() }; final MenuBuilder<ConfirmCode> builder = new MenuBuilder<>(this); builder.setPrompt(INFO_INSTALLDS_CONFIRM_INSTALL_PROMPT.get()); int i=0; for (final ConfirmCode code : ConfirmCode.values()) { builder.addNumberedOption(msgs[i], MenuResult.success(code)); i++; } builder.setDefault(LocalizableMessage.raw( String.valueOf(ConfirmCode.CONTINUE.getReturnCode())), MenuResult.success(ConfirmCode.CONTINUE)); final Menu<ConfirmCode> menu = builder.toMenu(); try { final MenuResult<ConfirmCode> m = menu.run(); if (m.isSuccess()) { returnValue = m.getValue(); } else { // Should never happen. throw new RuntimeException(); } } catch (final ClientException ce) { returnValue = ConfirmCode.CANCEL; logger.warn(LocalizableMessage.raw("Error reading input: "+ce, ce)); } return returnValue; } private void resetArguments(UserData uData) { argParser = new InstallDSArgumentParser(InstallDS.class.getName()); try { argParser.initializeArguments(); argParser.directoryManagerDNArg.setDefaultValue(uData.getDirectoryManagerDn()); argParser.ldapPortArg.setDefaultValue(String.valueOf(uData.getServerPort())); argParser.adminConnectorPortArg.setDefaultValue(String.valueOf(uData.getAdminConnectorPort())); final int jmxPort = uData.getServerJMXPort(); if (jmxPort != -1) { argParser.jmxPortArg.setDefaultValue(String.valueOf(jmxPort)); } final LinkedList<String> baseDNs = uData.getNewSuffixOptions().getBaseDns(); if (!baseDNs.isEmpty()) { argParser.baseDNArg.setDefaultValue(baseDNs.getFirst()); } final NewSuffixOptions suffixOptions = uData.getNewSuffixOptions(); lastResetPopulateOption = suffixOptions.getType(); if (NewSuffixOptions.Type.IMPORT_AUTOMATICALLY_GENERATED_DATA == lastResetPopulateOption) { lastResetNumEntries = suffixOptions.getNumberEntries(); } else if (NewSuffixOptions.Type.IMPORT_FROM_LDIF_FILE == lastResetPopulateOption) { lastResetImportFile = suffixOptions.getLDIFPaths().getFirst(); lastResetRejectedFile = suffixOptions.getRejectedFile(); lastResetSkippedFile = suffixOptions.getSkippedFile(); } final SecurityOptions sec = uData.getSecurityOptions(); if (sec.getEnableSSL()) { argParser.ldapsPortArg.setDefaultValue(String.valueOf(sec.getSslPort())); } lastResetEnableSSL = sec.getEnableSSL(); lastResetEnableStartTLS = sec.getEnableStartTLS(); lastResetCertType = sec.getCertificateType(); if (SecurityOptions.CertificateType.JKS == lastResetCertType || SecurityOptions.CertificateType.JCEKS == lastResetCertType || SecurityOptions.CertificateType.PKCS12 == lastResetCertType) { lastResetKeyStorePath = sec.getKeystorePath(); } else { lastResetKeyStorePath = null; } lastResetEnableWindowsService = uData.getEnableWindowsService(); lastResetStartServer = uData.getStartServer(); lastResetBackendType = uData.getBackendType(); } catch (final Throwable t) { logger.warn(LocalizableMessage.raw("Error resetting arguments: " + t, t)); } } private String promptForHostNameIfRequired() { String hostName = null; if (argParser.hostNameArg.isPresent()) { hostName = argParser.hostNameArg.getValue(); } else { println(); while (hostName == null) { try { hostName = readInput(INFO_INSTALLDS_PROMPT_HOST_NAME.get(), argParser.hostNameArg.getDefaultValue()); } catch (final ClientException ce) { logger.warn(LocalizableMessage.raw("Error reading input: "+ce, ce)); } } } return hostName; } /** * Returns the timeout to be used to connect in milliseconds. The method must * be called after parsing the arguments. * * @return the timeout to be used to connect in milliseconds. Returns * {@code 0} if there is no timeout. */ private int getConnectTimeout() { return argParser.getConnectTimeout(); } }