/* * 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 * * * Portions Copyright 2006-2007 Sun Microsystems, Inc. */ package org.opends.server.tools; import java.io.ByteArrayOutputStream; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.LinkedList; import java.util.Set; import org.opends.server.core.DirectoryServer; import org.opends.server.extensions.ConfigFileHandler; import org.opends.server.types.DN; import org.opends.server.types.ExistingFileBehavior; import org.opends.server.types.LDIFExportConfig; import org.opends.server.util.SetupUtils; import org.opends.server.util.LDIFWriter; import org.opends.server.util.PasswordReader; import org.opends.server.util.args.ArgumentException; import org.opends.server.util.args.ArgumentParser; import org.opends.server.util.args.BooleanArgument; import org.opends.server.util.args.FileBasedArgument; import org.opends.server.util.args.IntegerArgument; import org.opends.server.util.args.StringArgument; import static org.opends.server.messages.MessageHandler.*; import static org.opends.server.messages.ToolMessages.*; import static org.opends.server.util.ServerConstants.*; import static org.opends.server.util.StaticUtils.*; import static org.opends.server.tools.ToolConstants.*; /** * This class provides a very simple mechanism for installing the OpenDS * Directory Service. It performs the following tasks: *
installMain method with the provided arguments.
*
* @param args The command-line arguments to use for this program.
*/
public static void main(String[] args)
{
int exitCode = installMain(args);
if (exitCode != 0)
{
System.exit(filterExitCode(exitCode));
}
}
/**
* Prompts the user for the necessary information, installs the OpenDS
* software in the appropriate location, and gives it the desired
* configuration.
*
* @param args The command-line arguments to use for this program.
*
* @return A value of zero if the installation process was successful, or a
* nonzero value if a problem occurred.
*/
public static int installMain(String[] args)
{
// Construct the product version string and the setup filename.
versionString = DirectoryServer.getVersionString();
if (isWindows)
{
programName = "setup.bat";
}
else
{
programName = "setup";
}
// Create and initialize the argument parser for this program.
String toolDescription = getMessage(MSGID_INSTALLDS_TOOL_DESCRIPTION);
ArgumentParser argParser = new ArgumentParser(CLASS_NAME, toolDescription,
false);
BooleanArgument addBaseEntry;
BooleanArgument cliMode;
BooleanArgument testOnly;
BooleanArgument showUsage;
BooleanArgument silentInstall;
BooleanArgument skipPortCheck;
BooleanArgument enableWindowsService;
FileBasedArgument rootPWFile;
IntegerArgument ldapPort;
IntegerArgument jmxPort;
IntegerArgument sampleData;
StringArgument baseDN;
StringArgument configClass;
StringArgument configFile;
StringArgument importLDIF;
StringArgument progName;
StringArgument rootDN;
StringArgument rootPWString;
try
{
testOnly = new BooleanArgument("test", 't', "testOnly",
MSGID_INSTALLDS_DESCRIPTION_TESTONLY);
testOnly.setHidden(true);
argParser.addArgument(testOnly);
progName = new StringArgument("progname", 'P', "programName", false,
false, true, "{programName}", programName,
null, MSGID_INSTALLDS_DESCRIPTION_PROGNAME);
progName.setHidden(true);
argParser.addArgument(progName);
configFile = new StringArgument("configfile", 'c', "configFile", false,
false, true, "{configFile}", null, null,
MSGID_DESCRIPTION_CONFIG_FILE);
configFile.setHidden(true);
argParser.addArgument(configFile);
configClass = new StringArgument("configclass", OPTION_SHORT_CONFIG_CLASS,
OPTION_LONG_CONFIG_CLASS, false,
false, true, OPTION_VALUE_CONFIG_CLASS,
ConfigFileHandler.class.getName(), null,
MSGID_DESCRIPTION_CONFIG_CLASS);
configClass.setHidden(true);
argParser.addArgument(configClass);
// NOTE: This argument isn't actually used for anything, but it provides
// consistency with the setup script, which does take a --cli option.
cliMode = new BooleanArgument("cli", null, OPTION_LONG_CLI,
MSGID_INSTALLDS_DESCRIPTION_CLI);
argParser.addArgument(cliMode);
silentInstall = new BooleanArgument("silent", 's', "silentInstall",
MSGID_INSTALLDS_DESCRIPTION_SILENT);
argParser.addArgument(silentInstall);
baseDN = new StringArgument("basedn", OPTION_SHORT_BASEDN,
OPTION_LONG_BASEDN, false, true, true,
OPTION_VALUE_BASEDN,
"dc=example,dc=com", null,
MSGID_INSTALLDS_DESCRIPTION_BASEDN);
argParser.addArgument(baseDN);
addBaseEntry = new BooleanArgument("addbase", 'a', "addBaseEntry",
MSGID_INSTALLDS_DESCRIPTION_ADDBASE);
argParser.addArgument(addBaseEntry);
importLDIF = new StringArgument("importldif", OPTION_SHORT_LDIF_FILE,
OPTION_LONG_LDIF_FILE, false,
true, true, OPTION_VALUE_LDIF_FILE,
null, null,
MSGID_INSTALLDS_DESCRIPTION_IMPORTLDIF);
argParser.addArgument(importLDIF);
sampleData = new IntegerArgument("sampledata", 'd', "sampleData", false,
false, true, "{numEntries}", 0, null,
true, 0, false, 0,
MSGID_INSTALLDS_DESCRIPTION_SAMPLE_DATA);
argParser.addArgument(sampleData);
ldapPort = new IntegerArgument("ldapport", OPTION_SHORT_PORT,
"ldapPort", false, false,
true, OPTION_VALUE_PORT, 389,
null, true, 1, true, 65535,
MSGID_INSTALLDS_DESCRIPTION_LDAPPORT);
argParser.addArgument(ldapPort);
jmxPort = new IntegerArgument("jmxport", 'x', "jmxPort", false, false,
true, "{jmxPort}",
SetupUtils.getDefaultJMXPort(), null, true,
1, true, 65535,
MSGID_INSTALLDS_DESCRIPTION_JMXPORT);
argParser.addArgument(jmxPort);
skipPortCheck = new BooleanArgument("skipportcheck", 'S', "skipPortCheck",
MSGID_INSTALLDS_DESCRIPTION_SKIPPORT);
argParser.addArgument(skipPortCheck);
rootDN = new StringArgument("rootdn",OPTION_SHORT_ROOT_USER_DN,
OPTION_LONG_ROOT_USER_DN, false, true,
true, OPTION_VALUE_ROOT_USER_DN,
"cn=Directory Manager",
null, MSGID_INSTALLDS_DESCRIPTION_ROOTDN);
argParser.addArgument(rootDN);
rootPWString = new StringArgument("rootpwstring", OPTION_SHORT_BINDPWD,
"rootUserPassword",
false, false, true,
"{password}", null,
null,
MSGID_INSTALLDS_DESCRIPTION_ROOTPW);
argParser.addArgument(rootPWString);
rootPWFile = new FileBasedArgument("rootpwfile",
OPTION_SHORT_BINDPWD_FILE,
"rootUserPasswordFile", false, false,
OPTION_VALUE_BINDPWD_FILE,
null, null, MSGID_INSTALLDS_DESCRIPTION_ROOTPWFILE);
argParser.addArgument(rootPWFile);
enableWindowsService = new BooleanArgument("enablewindowsservice", 'e',
"enableWindowsService",
MSGID_INSTALLDS_DESCRIPTION_ENABLE_WINDOWS_SERVICE);
if (SetupUtils.isWindows())
{
argParser.addArgument(enableWindowsService);
}
showUsage = new BooleanArgument("help", OPTION_SHORT_HELP,
OPTION_LONG_HELP,
MSGID_INSTALLDS_DESCRIPTION_HELP);
argParser.addArgument(showUsage);
argParser.setUsageArgument(showUsage);
}
catch (ArgumentException ae)
{
System.err.println(wrapText(ae.getMessage(), MAX_LINE_WIDTH));
System.err.println(argParser.getUsage());
return 1;
}
// Parse all of the configuration arguments.
try
{
argParser.parseArguments(args);
}
catch (ArgumentException ae)
{
System.err.println(wrapText(ae.getMessage(), MAX_LINE_WIDTH));
System.err.println(argParser.getUsage());
return 1;
}
// If either the showUsage or testOnly or version arguments were provided,
// then we're done.
if (argParser.usageOrVersionDisplayed() || testOnly.isPresent())
{
return 0;
}
try
{
Setnull if
* there should not be a default and the user must
* explicitly provide a value.
*
* @return The boolean value read from the user input.
*/
private static boolean promptForBoolean(String prompt, Boolean defaultValue)
{
String wrappedPrompt = wrapText(prompt, MAX_LINE_WIDTH);
while (true)
{
System.out.println();
System.out.println(wrappedPrompt);
if (defaultValue == null)
{
System.out.print(": ");
}
else
{
System.out.print("[");
if (defaultValue)
{
System.out.print(getMessage(MSGID_INSTALLDS_PROMPT_VALUE_YES));
}
else
{
System.out.print(getMessage(MSGID_INSTALLDS_PROMPT_VALUE_NO));
}
System.out.print("]: ");
}
System.out.flush();
String response = toLowerCase(readLine());
if (response.equals("true") || response.equals("yes") ||
response.equals("t") || response.equals("y"))
{
return true;
}
else if (response.equals("false") || response.equals("no") ||
response.equals("f") || response.equals("n"))
{
return false;
}
else if (response.equals(""))
{
if (defaultValue == null)
{
String message = getMessage(MSGID_INSTALLDS_INVALID_YESNO_RESPONSE);
System.err.println(wrapText(message, MAX_LINE_WIDTH));
}
else
{
return defaultValue;
}
}
else
{
String message = getMessage(MSGID_INSTALLDS_INVALID_YESNO_RESPONSE);
System.err.println(wrapText(message, MAX_LINE_WIDTH));
}
}
}
/**
* 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 static int promptForInteger(String prompt, Integer defaultValue,
Integer lowerBound, Integer upperBound)
{
String wrappedPrompt = wrapText(prompt, MAX_LINE_WIDTH);
while (true)
{
System.out.println();
System.out.println(wrappedPrompt);
if (defaultValue == null)
{
System.out.print(": ");
}
else
{
System.out.print("[");
System.out.print(defaultValue);
System.out.print("]: ");
}
System.out.flush();
String response = readLine();
if (response.equals(""))
{
if (defaultValue == null)
{
String message = getMessage(MSGID_INSTALLDS_INVALID_INTEGER_RESPONSE);
System.err.println(wrapText(message, MAX_LINE_WIDTH));
}
else
{
return defaultValue;
}
}
else
{
try
{
int intValue = Integer.parseInt(response);
if ((lowerBound != null) && (intValue < lowerBound))
{
String message =
getMessage(MSGID_INSTALLDS_INTEGER_BELOW_LOWER_BOUND,
lowerBound);
System.err.println(wrapText(message, MAX_LINE_WIDTH));
}
else if ((upperBound != null) && (intValue > upperBound))
{
String message =
getMessage(MSGID_INSTALLDS_INTEGER_ABOVE_UPPER_BOUND,
upperBound);
System.err.println(wrapText(message, MAX_LINE_WIDTH));
}
else
{
return intValue;
}
}
catch (NumberFormatException nfe)
{
String message = getMessage(MSGID_INSTALLDS_INVALID_INTEGER_RESPONSE);
System.err.println(wrapText(message, MAX_LINE_WIDTH));
}
}
}
}
/**
* Interactively prompts (on standard output) the user to provide a DN value.
* Any non-empty string will be allowed if it can be parsed as a valid DN (the
* empty string will indicate that the default should be used, if there is
* one).
*
* @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.
*
* @return The DN value read from the user.
*/
private static DN promptForDN(String prompt, String defaultValue)
{
String wrappedPrompt = wrapText(prompt, MAX_LINE_WIDTH);
while (true)
{
System.out.println();
System.out.println(wrappedPrompt);
if (defaultValue == null)
{
System.out.print(": ");
}
else
{
System.out.print("[");
System.out.print(defaultValue);
System.out.print("]: ");
}
System.out.flush();
String response = readLine();
if (response.equals(""))
{
if (defaultValue == null)
{
String message = getMessage(MSGID_INSTALLDS_INVALID_DN_RESPONSE);
System.err.println(wrapText(message, MAX_LINE_WIDTH));
}
else
{
try
{
return DN.decode(defaultValue);
}
catch (Exception e)
{
String message = getMessage(MSGID_INSTALLDS_INVALID_DN_RESPONSE);
System.err.println(wrapText(message, MAX_LINE_WIDTH));
}
}
}
else
{
try
{
return DN.decode(response);
}
catch (Exception e)
{
String message = getMessage(MSGID_INSTALLDS_INVALID_DN_RESPONSE);
System.err.println(wrapText(message, MAX_LINE_WIDTH));
}
}
}
}
/**
* Interactively prompts (on standard output) the user to provide a string
* value. Any non-empty string will be allowed (the empty string will
* indicate that the default should be used, if there is one).
*
* @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.
*
* @return The string value read from the user.
*/
private static String promptForString(String prompt, String defaultValue)
{
System.out.println();
String wrappedPrompt = wrapText(prompt, MAX_LINE_WIDTH);
while (true)
{
System.out.println(wrappedPrompt);
if (defaultValue == null)
{
System.out.print(": ");
}
else
{
System.out.print("[");
System.out.print(defaultValue);
System.out.print("]: ");
}
System.out.flush();
String response = readLine();
if (response.equals(""))
{
if (defaultValue == null)
{
String message = getMessage(MSGID_INSTALLDS_INVALID_STRING_RESPONSE);
System.err.println(wrapText(message, MAX_LINE_WIDTH));
}
else
{
return defaultValue;
}
}
else
{
return response;
}
}
}
/**
* Interactively prompts (on standard output) the user to provide a string
* value. The response that the user provides will not be echoed, and it must
* be entered twice for confirmation. No default value will be allowed, and
* the string entered must contain at least one character.
*
* @param initialPrompt The initial prompt to present to the user.
* @param reEntryPrompt The prompt to present to the user when requesting
* that the value be re-entered for confirmation.
*
* @return The string value read from the user.
*/
private static char[] promptForPassword(String initialPrompt,
String reEntryPrompt)
{
String wrappedInitialPrompt = wrapText(initialPrompt, MAX_LINE_WIDTH);
String wrappedReEntryPrompt = wrapText(reEntryPrompt, MAX_LINE_WIDTH);
while (true)
{
System.out.println();
System.out.print(wrappedInitialPrompt);
System.out.print(": ");
System.out.flush();
char[] password = PasswordReader.readPassword();
if ((password == null) || (password.length == 0))
{
String message = getMessage(MSGID_INSTALLDS_INVALID_PASSWORD_RESPONSE);
System.err.println(wrapText(message, MAX_LINE_WIDTH));
}
else
{
System.out.print(wrappedReEntryPrompt);
System.out.print(": ");
System.out.flush();
char[] confirmedPassword = PasswordReader.readPassword();
if ((confirmedPassword == null) ||
(! Arrays.equals(password, confirmedPassword)))
{
String message = getMessage(MSGID_INSTALLDS_PASSWORDS_DONT_MATCH);
System.err.println(wrapText(message, MAX_LINE_WIDTH));
}
else
{
return password;
}
}
}
}
/**
* Reads a line of text from standard input.
*
* @return The line of text read from standard input, or null
* if the end of the stream is reached or an error occurs while
* attempting to read the response.
*/
private static String readLine()
{
try
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while (true)
{
int b = System.in.read();
if ((b < 0) || (b == '\n'))
{
break;
}
else if (b == '\r')
{
int b2 = System.in.read();
if (b2 == '\n')
{
break;
}
else
{
baos.write(b);
baos.write(b2);
}
}
else
{
baos.write(b);
}
}
return new String(baos.toByteArray(), "UTF-8");
}
catch (Exception e)
{
String message = getMessage(MSGID_INSTALLDS_ERROR_READING_FROM_STDIN,
String.valueOf(e));
System.err.println(wrapText(message, MAX_LINE_WIDTH));
return null;
}
}
}