/*
* 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 2006-2010 Sun Microsystems, Inc.
* Portions Copyright 2011-2013 ForgeRock AS
* Portions Copyright 2011 profiq s.r.o.
*/
package org.opends.server.tools;
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 java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.security.KeyStoreException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.naming.ldap.LdapName;
import org.opends.messages.Message;
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.args.ArgumentException;
import org.opends.server.util.args.IntegerArgument;
import org.opends.server.util.args.StringArgument;
import org.opends.server.util.cli.CLIException;
import org.opends.server.util.cli.ConsoleApplication;
import org.opends.server.util.cli.Menu;
import org.opends.server.util.cli.MenuBuilder;
import org.opends.server.util.cli.MenuResult;
/**
* This class provides a very simple mechanism for installing the OpenDS
* Directory Service. It performs the following tasks:
*
* - Checks if the server is already installed and running
* - Ask the user what base DN should be used for the data
* - Ask the user whether to create the base entry, or to import LDIF
* - Ask the user for the administration port and make sure it's available
*
* - Ask the user for the LDAP port and make sure it's available
* - Ask the user for the default root DN and password
* - Ask the user to enable SSL or not and for the type of certificate that
* the server must use
* - Ask the user if they want to start the server when done installing
*
*/
public class InstallDS extends ConsoleApplication
{
private final PlainTextProgressMessageFormatter formatter =
new PlainTextProgressMessageFormatter();
/** Prefix for log files. */
static public 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.
*
*/
enum ErrorReturnCode
{
/**
* Successful setup.
*/
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 ErrorReturnCode(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 with the install
CONTINUE(1),
// Provide information again
PROVIDE_INFORMATION_AGAIN(2),
// Display equivalent command-line
PRINT_EQUIVALENT_COMMAND_LINE(3),
// Cancel the install
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;
// Different variables we use when the user decides to provide data again.
private NewSuffixOptions.Type lastResetPopulateOption = null;
private String lastResetImportFile = null;
private String lastResetRejectedFile = null;
private String lastResetSkippedFile = null;
private Integer lastResetNumEntries = null;
private Boolean lastResetEnableSSL = null;
private Boolean lastResetEnableStartTLS = null;
private SecurityOptions.CertificateType lastResetCertType = null;
private String lastResetKeyStorePath = null;
private Boolean lastResetEnableWindowsService = null;
private Boolean lastResetStartServer = null;
/**
* The Logger.
*/
static private final Logger LOG = Logger.getLogger(InstallDS.class.getName());
// 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(in, 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)
{
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
* null
if standard output is not
* needed.
* @param errStream The output stream to use for standard error, or
* null
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)
{
PrintStream out;
if (outStream == null)
{
out = NullOutputStream.printStream();
}
else
{
out = new PrintStream(outStream);
}
System.setProperty(Constants.CLI_JAVA_PROPERTY, "true");
PrintStream err;
if (errStream == null)
{
err = NullOutputStream.printStream();
}
else
{
err = new PrintStream(errStream);
}
try {
QuickSetupLog.initLogFileHandler(
QuickSetupLog.isInitialized() ? null :
File.createTempFile(TMP_FILE_PREFIX, LOG_FILE_SUFFIX));
} catch (Throwable t) {
System.err.println("Unable to initialize log");
t.printStackTrace();
}
InstallDS install = new InstallDS(out, err, inStream);
return install.execute(args);
}
/**
* 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 (ArgumentException ae)
{
Message message =
ToolMessages.ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage());
println(message);
return ErrorReturnCode.ERROR_UNEXPECTED.getReturnCode();
}
// Validate user provided data
try
{
argParser.parseArguments(args);
}
catch (ArgumentException ae)
{
Message message = ERR_ERROR_PARSING_ARGS.get(ae.getMessage());
println(message);
println();
println(Message.raw(argParser.getUsage()));
return ErrorReturnCode.ERROR_USER_DATA.getReturnCode();
}
// 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.
if (argParser.testOnlyArg.isPresent())
{
try
{
QuickSetupLog.getLogFile().deleteOnExit();
}
catch (Throwable t)
{
LOG.log(Level.WARNING, "Error while trying to update the contents of "+
"the set-java-home file in test only mode: "+t, t);
}
// Test that we are running a compatible java 1.6 version.
try
{
Utils.checkJavaVersion();
}
catch (IncompatibleVersionException ive)
{
println(ive.getMessageObject());
return ErrorReturnCode.JAVA_VERSION_INCOMPATIBLE.getReturnCode();
}
}
// If either the showUsage or testOnly or version arguments were provided,
// then we're done.
if (argParser.usageOrVersionDisplayed() ||
argParser.testOnlyArg.isPresent())
{
return ErrorReturnCode.SUCCESSFUL_NOP.getReturnCode();
}
try
{
checkInstallStatus();
}
catch (InitializationException ie)
{
println(ie.getMessageObject());
return ErrorReturnCode.ERROR_SERVER_ALREADY_INSTALLED.getReturnCode();
}
// Check license
if (LicenseFile.exists())
{
PrintStream printStreamOut = getOutputStream();
String licenseString = LicenseFile.getText();
printStreamOut.println(licenseString);
// If the user asks for acceptLicense, license is displayed
// and automatically accepted.
if (! argParser.acceptLicense.isPresent())
{
String yes = INFO_LICENSE_CLI_ACCEPT_YES.get().toString();
String no = INFO_LICENSE_CLI_ACCEPT_NO.get().toString();
String yesShort = INFO_PROMPT_YES_FIRST_LETTER_ANSWER.get().toString();
String noShort = INFO_PROMPT_NO_FIRST_LETTER_ANSWER.get().toString();
println(QuickSetupMessages.
INFO_LICENSE_DETAILS_CLI_LABEL.get());
BufferedReader in = 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
{
String response = in.readLine();
if ((response == null) || (response.equalsIgnoreCase(no))
|| (response.equalsIgnoreCase(noShort))
|| (response.length() == 0))
{
return ErrorReturnCode.ERROR_LICENSE_NOT_ACCEPTED
.getReturnCode();
}
else if (response.equalsIgnoreCase(yes)
|| response.equalsIgnoreCase(yesShort))
{
LicenseFile.setApproval(true);
break;
}
else
{
println(
QuickSetupMessages.INFO_LICENSE_CLI_ACCEPT_INVALID_RESPONSE
.get());
}
}
catch (IOException e)
{
println(
QuickSetupMessages.INFO_LICENSE_CLI_ACCEPT_INVALID_RESPONSE
.get());
}
}
}
else
{
return ErrorReturnCode.ERROR_LICENSE_NOT_ACCEPTED.getReturnCode();
}
}
else
{
print(INFO_LICENSE_ACCEPT.get());
print(INFO_PROMPT_YES_COMPLETE_ANSWER.get());
LicenseFile.setApproval(true);
}
}
boolean userApproved = false;
UserData uData = new UserData();
while (!userApproved)
{
try
{
if (isInteractive())
{
promptIfRequired(uData);
}
else
{
initializeUserDataWithParser(uData);
}
}
catch (UserDataException ude)
{
println(ude.getMessageObject());
if (isPasswordTriesError(ude.getMessageObject()))
{
return ErrorReturnCode.ERROR_PASSWORD_LIMIT.getReturnCode();
}
else
{
return ErrorReturnCode.ERROR_USER_DATA.getReturnCode();
}
}
boolean promptAgain = true;
if (!isInteractive())
{
userApproved = true;
}
else
{
printSummary(uData);
}
while (isInteractive() && promptAgain)
{
promptAgain = false;
ConfirmCode confirm = askForConfirmation();
switch (confirm)
{
case CONTINUE:
userApproved = true;
break;
case CANCEL:
LOG.log(Level.INFO, "User cancelled setup.");
return ErrorReturnCode.ERROR_USER_CANCELLED.getReturnCode();
case PRINT_EQUIVALENT_COMMAND_LINE:
printEquivalentCommandLine(uData);
promptAgain = true;
break;
default:
// Reset the arguments
try
{
resetArguments(uData);
argParser.parseArguments(args);
}
catch (Throwable t)
{
LOG.log(Level.WARNING, "Error resetting arg parser: "+t, t);
}
userApproved = false;
}
}
if (!isInteractive())
{
userApproved = true;
}
}
System.setProperty(Constants.CLI_JAVA_PROPERTY, "true");
OfflineInstaller installer = new OfflineInstaller();
installer.setUserData(uData);
installer.setProgressMessageFormatter(formatter);
installer.addProgressUpdateListener(
new ProgressUpdateListener() {
public void progressUpdate(ProgressUpdateEvent ev) {
if (ev.getNewLogs() != null)
{
printProgress(ev.getNewLogs());
}
}
});
printlnProgress();
installer.run();
ApplicationException ue = installer.getRunError();
String cmd;
// Use this instead a call to Installation to avoid to launch a new JVM
// just to retrieve a path.
String root = Utils.getInstallPathFromClasspath();
if (SetupUtils.isWindows())
{
String binDir = Utils.getPath(root,
Installation.WINDOWS_BINARIES_PATH_RELATIVE);
cmd = Utils.getPath(binDir, Installation.WINDOWS_STATUSCLI_FILE_NAME);
}
else
{
String binDir = Utils.getPath(root,
Installation.UNIX_BINARIES_PATH_RELATIVE);
cmd = Utils.getPath(binDir, Installation.UNIX_STATUSCLI_FILE_NAME);
}
printlnProgress();
printProgress(INFO_INSTALLDS_STATUS_COMMAND_LINE.get(cmd));
printlnProgress();
if (ue != null)
{
return ue.getType().getReturnCode();
}
else
{
return ErrorReturnCode.SUCCESSFUL.getReturnCode();
}
}
/**
* 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
{
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(Message.EMPTY, null);
}
}
catch (CLIException ce)
{
LOG.log(Level.SEVERE, "Unexpected error: "+ce, ce);
throw new InitializationException(Message.EMPTY, null);
}
}
else
{
println(installStatus.getInstallationMsg());
}
}
else if (installStatus.isInstalled())
{
throw new InitializationException(installStatus.getInstallationMsg(),
null);
}
}
/**
* {@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
{
LinkedList errorMessages = new LinkedList();
uData.setQuiet(isQuiet());
uData.setVerbose(isVerbose());
uData.setConnectTimeout(getConnectTimeout());
// Check the validity of the directory manager DNs
String dmDN = argParser.directoryManagerDNArg.getValue();
try
{
new LdapName(dmDN);
if (dmDN.trim().length() == 0)
{
errorMessages.add(ERR_INSTALLDS_EMPTY_DN_RESPONSE.get());
}
}
catch (Exception e)
{
Message message =
ERR_INSTALLDS_CANNOT_PARSE_DN.get(dmDN, e.getMessage());
errorMessages.add(message);
}
uData.setDirectoryManagerDn(dmDN);
uData.setDirectoryManagerPwd(argParser.getDirectoryManagerPassword());
// Check the validity of the base DNs
LinkedList baseDNs = argParser.baseDNArg.getValues();
if (baseDNs.isEmpty() && argParser.baseDNArg.getDefaultValue() != null)
{
baseDNs.add(argParser.baseDNArg.getDefaultValue());
}
for (String baseDN : baseDNs)
{
try
{
new LdapName(baseDN);
}
catch (Exception e)
{
Message message =
ERR_INSTALLDS_CANNOT_PARSE_DN.get(baseDN, e.getMessage());
errorMessages.add(message);
}
}
try
{
int ldapPort = argParser.ldapPortArg.getIntValue();
uData.setServerPort(ldapPort);
int adminConnectorPort = argParser.adminConnectorPortArg.getIntValue();
uData.setAdminConnectorPort(adminConnectorPort);
if (!argParser.skipPortCheckArg.isPresent())
{
// Check if the port can be used.
if (!SetupUtils.canUseAsPort(ldapPort))
{
Message message;
if (SetupUtils.isPriviledgedPort(ldapPort))
{
message = ERR_INSTALLDS_CANNOT_BIND_TO_PRIVILEGED_PORT.get(
ldapPort);
}
else
{
message = ERR_INSTALLDS_CANNOT_BIND_TO_PORT.get(ldapPort);
}
errorMessages.add(message);
}
// Check if the port can be used.
if (!SetupUtils.canUseAsPort(adminConnectorPort))
{
Message message;
if (SetupUtils.isPriviledgedPort(adminConnectorPort))
{
message = ERR_INSTALLDS_CANNOT_BIND_TO_PRIVILEGED_PORT.get(
adminConnectorPort);
}
else
{
message = ERR_INSTALLDS_CANNOT_BIND_TO_PORT.get(adminConnectorPort);
}
errorMessages.add(message);
}
}
if (argParser.jmxPortArg.isPresent())
{
int jmxPort = argParser.jmxPortArg.getIntValue();
uData.setServerJMXPort(jmxPort);
// Check if the port can be used.
if (!argParser.skipPortCheckArg.isPresent())
{
if (!SetupUtils.canUseAsPort(jmxPort))
{
Message message;
if (SetupUtils.isPriviledgedPort(jmxPort))
{
message = ERR_INSTALLDS_CANNOT_BIND_TO_PRIVILEGED_PORT.get(
jmxPort);
}
else
{
message = ERR_INSTALLDS_CANNOT_BIND_TO_PORT.get(jmxPort);
}
errorMessages.add(message);
}
}
}
}
catch (ArgumentException ae)
{
errorMessages.add(ae.getMessageObject());
}
NewSuffixOptions dataOptions;
if (argParser.importLDIFArg.isPresent())
{
// Check that the files exist
LinkedList nonExistingFiles = new LinkedList();
for (String file : argParser.importLDIFArg.getValues())
{
if (!Utils.fileExists(file))
{
nonExistingFiles.add(file);
}
}
if (nonExistingFiles.size() > 0)
{
errorMessages.add(ERR_INSTALLDS_NO_SUCH_LDIF_FILE.get(
Utils.getStringFromCollection(nonExistingFiles, ", ")));
}
String rejectedFile = argParser.rejectedImportFileArg.getValue();
if (rejectedFile != null)
{
if (!Utils.canWrite(rejectedFile))
{
errorMessages.add(
ERR_INSTALLDS_CANNOT_WRITE_REJECTED.get(rejectedFile));
}
}
String skippedFile = argParser.skippedImportFileArg.getValue();
if (skippedFile != null)
{
if (!Utils.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,
new Integer(argParser.sampleDataArg.getValue()));
}
else
{
dataOptions = NewSuffixOptions.createEmpty(baseDNs);
}
uData.setNewSuffixOptions(dataOptions);
// Check that the security data provided is valid.
String certNickname = argParser.certNicknameArg.getValue();
String pwd = argParser.getKeyStorePassword();
boolean enableSSL = argParser.ldapsPortArg.isPresent();
boolean enableStartTLS = argParser.enableStartTLSArg.isPresent();
int ldapsPort = -1;
try
{
ldapsPort = enableSSL ? argParser.ldapsPortArg.getIntValue() : -1;
}
catch (ArgumentException ae)
{
errorMessages.add(ae.getMessageObject());
}
if (enableSSL)
{
if (!argParser.skipPortCheckArg.isPresent())
{
if (!SetupUtils.canUseAsPort(ldapsPort))
{
if (SetupUtils.isPriviledgedPort(ldapsPort))
{
errorMessages.add(
ERR_INSTALLDS_CANNOT_BIND_TO_PRIVILEGED_PORT.get(ldapsPort));
}
else
{
errorMessages.add(ERR_INSTALLDS_CANNOT_BIND_TO_PORT.get(ldapsPort));
}
}
}
}
SecurityOptions securityOptions;
LinkedList keystoreAliases = new LinkedList();
uData.setHostName(argParser.hostNameArg.getValue());
if (argParser.generateSelfSignedCertificateArg.isPresent())
{
securityOptions = SecurityOptions.createSelfSignedCertificateOptions(
enableSSL, enableStartTLS, ldapsPort);
}
else if (argParser.useJavaKeyStoreArg.isPresent())
{
String path = argParser.useJavaKeyStoreArg.getValue();
checkCertificateInKeystore(SecurityOptions.CertificateType.JKS, path, pwd,
certNickname, errorMessages, keystoreAliases);
if ((certNickname == null) && !keystoreAliases.isEmpty())
{
certNickname = keystoreAliases.getFirst();
}
securityOptions = SecurityOptions.createJKSCertificateOptions(
path, pwd, enableSSL, enableStartTLS, ldapsPort, certNickname);
}
else if (argParser.useJCEKSArg.isPresent())
{
String path = argParser.useJCEKSArg.getValue();
checkCertificateInKeystore(SecurityOptions.CertificateType.JCEKS, path,
pwd, certNickname, errorMessages, keystoreAliases);
if ((certNickname == null) && !keystoreAliases.isEmpty())
{
certNickname = keystoreAliases.getFirst();
}
securityOptions = SecurityOptions.createJCEKSCertificateOptions(
path, pwd, enableSSL, enableStartTLS, ldapsPort, certNickname);
}
else if (argParser.usePkcs12Arg.isPresent())
{
String path = argParser.usePkcs12Arg.getValue();
checkCertificateInKeystore(SecurityOptions.CertificateType.PKCS12, path,
pwd, certNickname, errorMessages, keystoreAliases);
if ((certNickname == null) && !keystoreAliases.isEmpty())
{
certNickname = keystoreAliases.getFirst();
}
securityOptions = SecurityOptions.createPKCS12CertificateOptions(
path, pwd, enableSSL, enableStartTLS, ldapsPort, certNickname);
}
else if (argParser.usePkcs11Arg.isPresent())
{
checkCertificateInKeystore(SecurityOptions.CertificateType.PKCS11, null,
pwd, certNickname, errorMessages, keystoreAliases);
if ((certNickname == null) && !keystoreAliases.isEmpty())
{
certNickname = keystoreAliases.getFirst();
}
securityOptions = SecurityOptions.createPKCS11CertificateOptions(
pwd, enableSSL, enableStartTLS, ldapsPort, certNickname);
}
else
{
securityOptions = SecurityOptions.createNoCertificateOptions();
}
uData.setSecurityOptions(securityOptions);
uData.setEnableWindowsService(
argParser.enableWindowsServiceArg.isPresent());
uData.setStartServer(!argParser.doNotStartArg.isPresent());
if (errorMessages.size() > 0)
{
throw new UserDataException(null,
Utils.getMessageFromCollection(errorMessages,
formatter.getLineBreak().toString()));
}
}
/**
* 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.
*/
private void promptIfRequired(UserData uData) throws UserDataException
{
uData.setQuiet(isQuiet());
uData.setVerbose(isVerbose());
uData.setConnectTimeout(getConnectTimeout());
promptIfRequiredForDirectoryManager(uData);
promptIfRequiredForPortData(uData);
promptIfRequiredForImportData(uData);
promptIfRequiredForSecurityData(uData);
promptIfRequiredForWindowsService(uData);
promptIfRequiredForStartServer(uData);
}
/**
* 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.
*/
private void promptIfRequiredForDirectoryManager(UserData uData)
throws UserDataException
{
LinkedList dns = promptIfRequiredForDNs(
argParser.directoryManagerDNArg, INFO_INSTALLDS_PROMPT_ROOT_DN.get(),
true);
uData.setDirectoryManagerDn(dns.getFirst());
String pwd = argParser.getDirectoryManagerPassword();
int nTries = 0;
while (pwd == null)
{
if (nTries >= CONFIRMATION_MAX_TRIES)
{
throw new UserDataException(null,
ERR_TRIES_LIMIT_REACHED.get(CONFIRMATION_MAX_TRIES));
}
String pwd1 = null;
// Prompt for password and confirm.
while (pwd1 == null)
{
pwd1 = readPassword(INFO_INSTALLDS_PROMPT_ROOT_PASSWORD.get(), LOG);
if ((pwd1 == null) || "".equals(pwd1))
{
pwd1 = null;
println();
println(INFO_EMPTY_PWD.get());
println();
}
}
String pwd2 =
readPassword(INFO_INSTALLDS_PROMPT_CONFIRM_ROOT_PASSWORD.get(), LOG);
if (pwd1.equals(pwd2))
{
pwd = 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 promptIfRequiredForDNs(StringArgument arg,
Message promptMsg, boolean includeLineBreak) throws UserDataException
{
LinkedList 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
{
String dn = readInput(promptMsg, arg.getDefaultValue());
firstPrompt = false;
dns.add(dn);
prompted = true;
}
catch (CLIException ce)
{
LOG.log(Level.WARNING, "Error reading input: "+ce, ce);
}
}
else
{
dns.addAll(arg.getValues());
usedProvided = true;
}
LinkedList toRemove = new LinkedList();
for (String dn : dns)
{
try
{
new LdapName(dn);
if (dn.trim().length() == 0)
{
toRemove.add(dn);
println(ERR_INSTALLDS_EMPTY_DN_RESPONSE.get());
}
}
catch (Exception e)
{
toRemove.add(dn);
Message message = prompted ? ERR_INSTALLDS_INVALID_DN_RESPONSE.get() :
ERR_INSTALLDS_CANNOT_PARSE_DN.get(dn, e.getMessage());
println(message);
}
}
if (toRemove.size() > 0)
{
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)
{
String hostName = promptForHostNameIfRequired();
uData.setHostName(hostName);
LinkedList usedPorts = new LinkedList();
// Determine the LDAP port number.
int ldapPort = promptIfRequiredForPortData(argParser.ldapPortArg,
INFO_INSTALLDS_PROMPT_LDAPPORT.get(), usedPorts, true);
uData.setServerPort(ldapPort);
usedPorts.add(ldapPort);
// Determine the Admin Connector port number.
int adminConnectorPort =
promptIfRequiredForPortData(argParser.adminConnectorPortArg,
INFO_INSTALLDS_PROMPT_ADMINCONNECTORPORT.get(), usedPorts, true);
uData.setAdminConnectorPort(adminConnectorPort);
usedPorts.add(adminConnectorPort);
if (argParser.jmxPortArg.isPresent())
{
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,
Message promptMsg, Collection 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 (CLIException ce)
{
portNumber = -1;
LOG.log(Level.WARNING, "Error reading input: "+ce, ce);
}
}
prompted = true;
firstPrompt = false;
}
else
{
portNumber = portArg.getIntValue();
usedProvided = true;
}
if (!argParser.skipPortCheckArg.isPresent())
{
// Check if the port can be used.
if (!SetupUtils.canUseAsPort(portNumber))
{
Message message;
if (SetupUtils.isPriviledgedPort(portNumber))
{
if (prompted || includeLineBreak)
{
println();
}
message = ERR_INSTALLDS_CANNOT_BIND_TO_PRIVILEGED_PORT.get(
portNumber);
println(message);
portNumber = -1;
}
else
{
if (prompted || includeLineBreak)
{
println();
}
message = ERR_INSTALLDS_CANNOT_BIND_TO_PORT.get(portNumber);
println(message);
println();
portNumber = -1;
}
}
}
if (portNumber != -1)
{
if (usedPorts.contains(portNumber))
{
Message message = ERR_CONFIGDS_PORT_ALREADY_SPECIFIED.get(
String.valueOf(portNumber));
println(message);
println();
portNumber = -1;
}
}
}
catch (ArgumentException ae)
{
println(ae.getMessageObject());
}
}
return portNumber;
}
/**
* This method updates the contents of a UserData object with 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.
* @throws UserDataException if something went wrong checking the data.
*/
private void promptIfRequiredForImportData(UserData uData)
throws UserDataException
{
boolean prompt = true;
if (!argParser.baseDNArg.isPresent())
{
println();
try
{
prompt = confirmAction(INFO_INSTALLDS_PROVIDE_BASE_DN_PROMPT.get(),
true);
}
catch (CLIException ce)
{
prompt = true;
LOG.log(Level.WARNING, "Error reading input: "+ce, ce);
}
}
NewSuffixOptions dataOptions;
if (!prompt)
{
LinkedList baseDNs = new LinkedList();
dataOptions = NewSuffixOptions.createEmpty(baseDNs);
}
else
{
// Check the validity of the base DNs
LinkedList baseDNs = promptIfRequiredForDNs(
argParser.baseDNArg, INFO_INSTALLDS_PROMPT_BASEDN.get(), true);
dataOptions = promptIfRequiredForDataOptions(baseDNs);
}
uData.setNewSuffixOptions(dataOptions);
}
private NewSuffixOptions promptIfRequiredForDataOptions(
LinkedList baseDNs)
{
NewSuffixOptions dataOptions;
if (argParser.importLDIFArg.isPresent())
{
// Check that the files exist
LinkedList nonExistingFiles = new LinkedList();
LinkedList importLDIFFiles = new LinkedList();
for (String file : argParser.importLDIFArg.getValues())
{
if (!Utils.fileExists(file))
{
nonExistingFiles.add(file);
}
else
{
importLDIFFiles.add(file);
}
}
if (nonExistingFiles.size() > 0)
{
println();
println(ERR_INSTALLDS_NO_SUCH_LDIF_FILE.get(
Utils.getStringFromCollection(nonExistingFiles, ", ")));
}
while (importLDIFFiles.isEmpty())
{
println();
try
{
String path = readInput(INFO_INSTALLDS_PROMPT_IMPORT_FILE.get(),
lastResetImportFile);
if (!Utils.fileExists(path))
{
println();
println(ERR_INSTALLDS_NO_SUCH_LDIF_FILE.get(path));
}
else
{
importLDIFFiles.add(path);
}
}
catch (CLIException ce)
{
LOG.log(Level.WARNING, "Error reading input: "+ce, ce);
}
}
String rejectedFile = argParser.rejectedImportFileArg.getValue();
if (rejectedFile != null)
{
while (!Utils.canWrite(rejectedFile))
{
println();
println(ERR_INSTALLDS_CANNOT_WRITE_REJECTED.get(rejectedFile));
println();
try
{
rejectedFile =
readInput(INFO_INSTALLDS_PROMPT_REJECTED_FILE.get(),
lastResetRejectedFile);
}
catch (CLIException ce)
{
LOG.log(Level.WARNING, "Error reading input: "+ce, ce);
}
}
}
String skippedFile = argParser.skippedImportFileArg.getValue();
if (skippedFile != null)
{
while (!Utils.canWrite(skippedFile))
{
println();
println(ERR_INSTALLDS_CANNOT_WRITE_SKIPPED.get(skippedFile));
println();
try
{
skippedFile =
readInput(INFO_INSTALLDS_PROMPT_SKIPPED_FILE.get(),
lastResetSkippedFile);
}
catch (CLIException ce)
{
LOG.log(Level.WARNING, "Error reading input: "+ce, ce);
}
}
}
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 (ArgumentException ae)
{
println();
println(ae.getMessageObject());
Message 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_BASE_ONLY = 1;
final int POPULATE_TYPE_LEAVE_EMPTY = 2;
final int POPULATE_TYPE_IMPORT_FROM_LDIF = 3;
final int POPULATE_TYPE_GENERATE_SAMPLE_DATA = 4;
int[] indexes = {POPULATE_TYPE_BASE_ONLY, POPULATE_TYPE_LEAVE_EMPTY,
POPULATE_TYPE_IMPORT_FROM_LDIF, POPULATE_TYPE_GENERATE_SAMPLE_DATA};
Message[] msgs = new Message[] {
INFO_INSTALLDS_POPULATE_OPTION_BASE_ONLY.get(),
INFO_INSTALLDS_POPULATE_OPTION_LEAVE_EMPTY.get(),
INFO_INSTALLDS_POPULATE_OPTION_IMPORT_LDIF.get(),
INFO_INSTALLDS_POPULATE_OPTION_GENERATE_SAMPLE.get()
};
MenuBuilder builder = new MenuBuilder(this);
builder.setPrompt(INFO_INSTALLDS_HEADER_POPULATE_TYPE.get());
for (int i=0; i menu = builder.toMenu();
int populateType;
try
{
MenuResult m = menu.run();
if (m.isSuccess())
{
populateType = m.getValue();
}
else
{
// Should never happen.
throw new RuntimeException();
}
}
catch (CLIException ce)
{
populateType = POPULATE_TYPE_BASE_ONLY;
LOG.log(Level.WARNING, "Error reading input: "+ce, ce);
}
if (populateType == POPULATE_TYPE_IMPORT_FROM_LDIF)
{
LinkedList importLDIFFiles = new LinkedList();
while (importLDIFFiles.isEmpty())
{
Message message = INFO_INSTALLDS_PROMPT_IMPORT_FILE.get();
println();
try
{
String path = readInput(message, null);
if (Utils.fileExists(path))
{
importLDIFFiles.add(path);
}
else
{
message = ERR_INSTALLDS_NO_SUCH_LDIF_FILE.get(path);
println();
println(message);
}
}
catch (CLIException ce)
{
LOG.log(Level.WARNING, "Error reading input: "+ce, ce);
}
}
String rejectedFile = argParser.rejectedImportFileArg.getValue();
if (rejectedFile != null)
{
while (!Utils.canWrite(rejectedFile))
{
println();
println(
ERR_INSTALLDS_CANNOT_WRITE_REJECTED.get(rejectedFile));
println();
try
{
rejectedFile =
readInput(INFO_INSTALLDS_PROMPT_REJECTED_FILE.get(), null);
}
catch (CLIException ce)
{
LOG.log(Level.WARNING, "Error reading input: "+ce, ce);
}
}
}
String skippedFile = argParser.skippedImportFileArg.getValue();
if (skippedFile != null)
{
while (!Utils.canWrite(skippedFile))
{
println();
println(ERR_INSTALLDS_CANNOT_WRITE_SKIPPED.get(skippedFile));
println();
try
{
skippedFile =
readInput(INFO_INSTALLDS_PROMPT_SKIPPED_FILE.get(), null);
}
catch (CLIException ce)
{
LOG.log(Level.WARNING, "Error reading input: "+ce, ce);
}
}
}
dataOptions = NewSuffixOptions.createImportFromLDIF(baseDNs,
importLDIFFiles, rejectedFile, skippedFile);
}
else if (populateType == POPULATE_TYPE_GENERATE_SAMPLE_DATA)
{
Message message = INFO_INSTALLDS_PROMPT_NUM_ENTRIES.get();
int defaultValue;
if (lastResetNumEntries == null)
{
defaultValue = 2000;
}
else
{
defaultValue = lastResetNumEntries;
}
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;
}
/**
* This method updates the contents of a UserData object with 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 UserData object to be updated.
* @throws UserDataException if the user did not manage to provide the
* keystore password after a certain number of tries.
*/
private void promptIfRequiredForSecurityData(UserData uData)
throws UserDataException
{
// Check that the security data provided is valid.
boolean enableSSL = false;
boolean enableStartTLS = false;
int ldapsPort = -1;
LinkedList 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
{
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 (CLIException ce)
{
LOG.log(Level.WARNING, "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
{
boolean defaultValue = lastResetEnableStartTLS != null ?
lastResetEnableStartTLS : false;
enableStartTLS = confirmAction(INFO_INSTALLDS_ENABLE_STARTTLS.get(),
defaultValue);
}
catch (CLIException ce)
{
LOG.log(Level.WARNING, "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;
int[] indexes = {SELF_SIGNED, JKS, JCEKS, PKCS12, PKCS11};
Message[] 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()
};
MenuBuilder builder = new MenuBuilder(this);
builder.setPrompt(INFO_INSTALLDS_HEADER_CERT_TYPE.get());
for (int i=0; i menu = builder.toMenu();
int certType;
try
{
MenuResult m = menu.run();
if (m.isSuccess())
{
certType = m.getValue();
}
else
{
// Should never happen.
throw new RuntimeException();
}
}
catch (CLIException ce)
{
LOG.log(Level.WARNING, "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);
}
}
}
uData.setSecurityOptions(securityOptions);
}
/**
* This method updates the contents of a UserData object with 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.
* @param uData the UserData object to be updated.
*/
private void promptIfRequiredForWindowsService(UserData uData)
{
boolean enableService = false;
// If we are in Windows ask if the server must run as a windows service.
if (SetupUtils.isWindows())
{
if (argParser.enableWindowsServiceArg.isPresent())
{
enableService = true;
}
else
{
println();
Message message = INFO_INSTALLDS_PROMPT_ENABLE_SERVICE.get();
try
{
boolean defaultValue = (lastResetEnableWindowsService == null) ?
false : lastResetEnableWindowsService;
enableService = confirmAction(message, defaultValue);
}
catch (CLIException ce)
{
LOG.log(Level.WARNING, "Error reading input: "+ce, ce);
}
}
}
uData.setEnableWindowsService(enableService);
}
/**
* 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 the data, it prompts the user to
* provide it.
* @param uData the UserData object to be updated.
*/
private void promptIfRequiredForStartServer(UserData uData)
{
boolean startServer = false;
if (!argParser.doNotStartArg.isPresent())
{
println();
Message message = INFO_INSTALLDS_PROMPT_START_SERVER.get();
try
{
boolean defaultValue = (lastResetStartServer == null) ?
true : lastResetStartServer;
startServer = confirmAction(message, defaultValue);
}
catch (CLIException ce)
{
LOG.log(Level.WARNING, "Error reading input: "+ce, ce);
startServer = true;
}
}
uData.setStartServer(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 Message. 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 certNickname the certificate nickname 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, String certNickname,
Collection errorMessages, Collection nicknameList)
{
boolean errorWithPath = false;
if (type != SecurityOptions.CertificateType.PKCS11)
{
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;
}
}
boolean pwdProvided = true;
if (pwd == null)
{
pwdProvided = false;
errorMessages.add(INFO_ERROR_NO_KEYSTORE_PASSWORD.get());
}
else if (pwd.length() == 0)
{
pwdProvided = false;
errorMessages.add(INFO_ERROR_EMPTY_KEYSTORE_PASSWORD.get());
}
if (!errorWithPath && pwdProvided)
{
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);
}
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);
String aliasString = Utils.getStringFromCollection(nicknameList,
", ");
if (certNickname != null)
{
// Check if the certificate alias is in the list.
boolean found = false;
for (int i=0; i 1)
{
errorMessages.add(ERR_INSTALLDS_MUST_PROVIDE_CERTNICKNAME.get(
aliasString));
}
}
}
catch (KeyStoreException ke)
{
// 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);
}
}
}
}
/**
* 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.
*/
private SecurityOptions createSecurityOptionsPrompting(
SecurityOptions.CertificateType type, boolean enableSSL,
boolean enableStartTLS, int ldapsPort) throws UserDataException
{
SecurityOptions securityOptions;
String path;
String certNickname = argParser.certNicknameArg.getValue();
String pwd = argParser.getKeyStorePassword();
if (pwd != null)
{
if (pwd.length() == 0)
{
pwd = null;
}
}
Message 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);
}
LinkedList errorMessages = new LinkedList();
LinkedList keystoreAliases = new LinkedList();
boolean firstTry = true;
int nPasswordPrompts = 0;
while ((errorMessages.size() > 0) || firstTry)
{
boolean prompted = false;
if (errorMessages.size() > 0)
{
println();
println(Utils.getMessageFromCollection(errorMessages,
formatter.getLineBreak().toString()));
}
if (type != SecurityOptions.CertificateType.PKCS11)
{
if (containsKeyStorePathErrorMessage(errorMessages) || (path == null))
{
println();
try
{
path = readInput(pathPrompt, defaultPathValue);
}
catch (CLIException ce)
{
path = "";
LOG.log(Level.WARNING, "Error reading input: "+ce, ce);
}
prompted = true;
if (pwd != null)
{
errorMessages.clear();
keystoreAliases.clear();
checkCertificateInKeystore(type, path, pwd, certNickname,
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(
String.valueOf(LIMIT_KEYSTORE_PASSWORD_PROMPT)));
}
pwd = readPassword(
INFO_INSTALLDS_PROMPT_KEYSTORE_PASSWORD.get(), LOG);
nPasswordPrompts ++;
}
}
if (containsCertNicknameErrorMessage(errorMessages))
{
if (!prompted)
{
println();
}
certNickname = promptForCertificateNickname(keystoreAliases);
}
errorMessages.clear();
keystoreAliases.clear();
checkCertificateInKeystore(type, path, pwd, certNickname, errorMessages,
keystoreAliases);
firstTry = false;
}
if ((certNickname == null) && !keystoreAliases.isEmpty())
{
certNickname = keystoreAliases.getFirst();
}
switch (type)
{
case JKS:
securityOptions = SecurityOptions.createJKSCertificateOptions(
path, pwd, enableSSL, enableStartTLS, ldapsPort, certNickname);
break;
case JCEKS:
securityOptions = SecurityOptions.createJCEKSCertificateOptions(
path, pwd, enableSSL, enableStartTLS, ldapsPort, certNickname);
break;
case PKCS12:
securityOptions = SecurityOptions.createPKCS12CertificateOptions(
path, pwd, enableSSL, enableStartTLS, ldapsPort, certNickname);
break;
case PKCS11:
securityOptions = SecurityOptions.createPKCS11CertificateOptions(
pwd, enableSSL, enableStartTLS, ldapsPort, certNickname);
break;
default:
throw new IllegalStateException(
"Called createSecurityOptionsPrompting with invalid type: "+type);
}
return securityOptions;
}
/**
* Tells if any of the error messages provided corresponds to a problem
* with the key store path.
* @param msgs the messages to analyze.
* @return true
if any of the error messages provided corresponds
* to a problem with the key store path and false
otherwise.
*/
public static boolean containsKeyStorePathErrorMessage(
Collection msgs)
{
boolean found = false;
for (Message msg : msgs)
{
if (msg.getDescriptor().equals(INFO_KEYSTORE_PATH_DOES_NOT_EXIST) ||
msg.getDescriptor().equals(INFO_KEYSTORE_PATH_NOT_A_FILE) ||
msg.getDescriptor().equals(INFO_JKS_KEYSTORE_DOES_NOT_EXIST) ||
msg.getDescriptor().equals(INFO_JCEKS_KEYSTORE_DOES_NOT_EXIST) ||
msg.getDescriptor().equals(INFO_PKCS12_KEYSTORE_DOES_NOT_EXIST) ||
msg.getDescriptor().equals(INFO_PKCS11_KEYSTORE_DOES_NOT_EXIST) ||
msg.getDescriptor().equals(INFO_ERROR_ACCESSING_JKS_KEYSTORE) ||
msg.getDescriptor().equals(INFO_ERROR_ACCESSING_JCEKS_KEYSTORE) ||
msg.getDescriptor().equals(INFO_ERROR_ACCESSING_PKCS12_KEYSTORE) ||
msg.getDescriptor().equals(INFO_ERROR_ACCESSING_PKCS11_KEYSTORE))
{
found = true;
break;
}
}
return found;
}
/**
* Tells if any of the error messages provided corresponds to a problem
* with the key store password.
* @param msgs the messages to analyze.
* @return true
if any of the error messages provided corresponds
* to a problem with the key store password and false
otherwise.
*/
public static boolean containsKeyStorePasswordErrorMessage(
Collection msgs)
{
boolean found = false;
for (Message msg : msgs)
{
if (msg.getDescriptor().equals(INFO_JKS_KEYSTORE_DOES_NOT_EXIST) ||
msg.getDescriptor().equals(INFO_JCEKS_KEYSTORE_DOES_NOT_EXIST) ||
msg.getDescriptor().equals(INFO_PKCS12_KEYSTORE_DOES_NOT_EXIST) ||
msg.getDescriptor().equals(INFO_PKCS11_KEYSTORE_DOES_NOT_EXIST) ||
msg.getDescriptor().equals(INFO_ERROR_ACCESSING_JKS_KEYSTORE) ||
msg.getDescriptor().equals(INFO_ERROR_ACCESSING_JCEKS_KEYSTORE) ||
msg.getDescriptor().equals(INFO_ERROR_ACCESSING_PKCS12_KEYSTORE) ||
msg.getDescriptor().equals(INFO_ERROR_ACCESSING_PKCS11_KEYSTORE) ||
msg.getDescriptor().equals(INFO_ERROR_NO_KEYSTORE_PASSWORD) ||
msg.getDescriptor().equals(INFO_ERROR_EMPTY_KEYSTORE_PASSWORD))
{
found = true;
break;
}
}
return found;
}
/**
* Tells if any of the error messages provided corresponds to a problem
* with the certificate nickname.
* @param msgs the messages to analyze.
* @return true
if any of the error messages provided corresponds
* to a problem with the certificate nickname and false
* otherwise.
*/
public static boolean containsCertNicknameErrorMessage(
Collection msgs)
{
boolean found = false;
for (Message msg : msgs)
{
if (msg.getDescriptor().equals(ERR_INSTALLDS_CERTNICKNAME_NOT_FOUND) ||
msg.getDescriptor().equals(ERR_INSTALLDS_MUST_PROVIDE_CERTNICKNAME))
{
found = true;
break;
}
}
return found;
}
/**
* Tells if the error messages provided corresponds to a problem with the
* password tries.
* @param msg the message to analyze.
* @return true
if the error message provided corresponds to a
* problem with the password tries and false
otherwise.
*/
private boolean isPasswordTriesError(Message msg)
{
return msg.getDescriptor().equals(
ERR_INSTALLDS_TOO_MANY_KEYSTORE_PASSWORD_TRIES);
}
/**
* 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 null
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
* null
if there is none.
* @param upperBound The upper bound that should be enforced, or
* null
if there is none.
*
* @return The int
value read from the user input.
*/
private int promptForInteger(Message prompt, Integer defaultValue,
Integer lowerBound, Integer upperBound)
{
int returnValue = -1;
while (returnValue == -1)
{
String s;
try
{
s = readInput(prompt, String.valueOf(defaultValue));
}
catch (CLIException ce)
{
s = "";
LOG.log(Level.WARNING, "Error reading input: "+ce, ce);
}
if (s.equals(""))
{
if (defaultValue == null)
{
Message message = ERR_INSTALLDS_INVALID_INTEGER_RESPONSE.get();
println(message);
println();
}
else
{
returnValue = defaultValue;
}
}
else
{
try
{
int intValue = Integer.parseInt(s);
if ((lowerBound != null) && (intValue < lowerBound))
{
Message message =
ERR_INSTALLDS_INTEGER_BELOW_LOWER_BOUND.get(lowerBound);
println(message);
println();
}
else if ((upperBound != null) && (intValue > upperBound))
{
Message message =
ERR_INSTALLDS_INTEGER_ABOVE_UPPER_BOUND.get(upperBound);
println(message);
println();
}
else
{
returnValue = intValue;
}
}
catch (NumberFormatException nfe)
{
Message message = ERR_INSTALLDS_INVALID_INTEGER_RESPONSE.get();
println(message);
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 String promptForCertificateNickname(LinkedList nicknames)
{
String nickname = null;
while (nickname == null)
{
for (String n : nicknames)
{
try
{
if (confirmAction(INFO_INSTALLDS_PROMPT_CERTNICKNAME.get(n), true))
{
nickname = n;
break;
}
}
catch (CLIException ce)
{
LOG.log(Level.WARNING, "Error reading input: "+ce, ce);
}
}
}
return nickname;
}
/**
* 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());
Message[] 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()
};
int jmxPort = uData.getServerJMXPort();
Message[] values =
{
Message.raw(String.valueOf(uData.getServerPort())),
Message.raw(String.valueOf(uData.getAdminConnectorPort())),
Message.raw(jmxPort != -1 ? String.valueOf(jmxPort) : null),
Message.raw(
Utils.getSecurityOptionsString(uData.getSecurityOptions(), false)),
Message.raw(uData.getDirectoryManagerDn()),
Message.raw(Utils.getDataDisplayString(uData)),
};
int maxWidth = 0;
for (Message l : labels)
{
maxWidth = Math.max(maxWidth, l.length());
}
for (int i=0; i cmd = Utils.getSetupEquivalentCommandLine(uData);
println(Message.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();
Message[] msgs = new Message[] {
INFO_INSTALLDS_CONFIRM_INSTALL.get(),
INFO_INSTALLDS_PROVIDE_DATA_AGAIN.get(),
INFO_INSTALLDS_PRINT_EQUIVALENT_COMMAND_LINE.get(),
INFO_INSTALLDS_CANCEL.get()
};
MenuBuilder builder = new MenuBuilder(this);
builder.setPrompt(INFO_INSTALLDS_CONFIRM_INSTALL_PROMPT.get());
int i=0;
for (ConfirmCode code : ConfirmCode.values())
{
builder.addNumberedOption(msgs[i], MenuResult.success(code));
i++;
}
builder.setDefault(Message.raw(
String.valueOf(ConfirmCode.CONTINUE.getReturnCode())),
MenuResult.success(ConfirmCode.CONTINUE));
Menu menu = builder.toMenu();
try
{
MenuResult m = menu.run();
if (m.isSuccess())
{
returnValue = m.getValue();
}
else
{
// Should never happen.
throw new RuntimeException();
}
}
catch (CLIException ce)
{
returnValue = ConfirmCode.CANCEL;
LOG.log(Level.WARNING, "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()));
int jmxPort = uData.getServerJMXPort();
if (jmxPort != -1)
{
argParser.jmxPortArg.setDefaultValue(String.valueOf(jmxPort));
}
LinkedList baseDNs = uData.getNewSuffixOptions().getBaseDns();
if (!baseDNs.isEmpty())
{
argParser.baseDNArg.setDefaultValue(baseDNs.getFirst());
}
NewSuffixOptions suffixOptions = uData.getNewSuffixOptions();
lastResetPopulateOption = suffixOptions.getType();
if (lastResetPopulateOption ==
NewSuffixOptions.Type.IMPORT_AUTOMATICALLY_GENERATED_DATA)
{
lastResetNumEntries = suffixOptions.getNumberEntries();
}
else if (lastResetPopulateOption ==
NewSuffixOptions.Type.IMPORT_FROM_LDIF_FILE)
{
lastResetImportFile = suffixOptions.getLDIFPaths().getFirst();
lastResetRejectedFile = suffixOptions.getRejectedFile();
lastResetSkippedFile = suffixOptions.getSkippedFile();
}
SecurityOptions sec = uData.getSecurityOptions();
if (sec.getEnableSSL())
{
argParser.ldapsPortArg.setDefaultValue(
String.valueOf(sec.getSslPort()));
}
lastResetEnableSSL = sec.getEnableSSL();
lastResetEnableStartTLS = sec.getEnableStartTLS();
lastResetCertType = sec.getCertificateType();
if (lastResetCertType == SecurityOptions.CertificateType.JKS ||
lastResetCertType == SecurityOptions.CertificateType.JCEKS ||
lastResetCertType == SecurityOptions.CertificateType.PKCS12)
{
lastResetKeyStorePath = sec.getKeystorePath();
}
else
{
lastResetKeyStorePath = null;
}
lastResetEnableWindowsService = uData.getEnableWindowsService();
lastResetStartServer = uData.getStartServer();
}
catch (Throwable t)
{
LOG.log(Level.WARNING, "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 (CLIException ce)
{
LOG.log(Level.WARNING, "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();
}
}