/*
* 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 2008-2010 Sun Microsystems, Inc.
*/
package org.opends.server.util.cli;
import static org.opends.messages.AdminToolMessages.*;
import static org.opends.messages.DSConfigMessages.*;
import static org.opends.messages.QuickSetupMessages.*;
import static org.opends.messages.UtilityMessages.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
import java.io.BufferedReader;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.Reader;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.naming.NamingException;
import javax.naming.NoPermissionException;
import javax.naming.ldap.InitialLdapContext;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.TrustManager;
import org.opends.admin.ads.ServerDescriptor;
import org.opends.admin.ads.util.ApplicationTrustManager;
import org.opends.admin.ads.util.ConnectionUtils;
import org.opends.admin.ads.util.OpendsCertificateException;
import org.opends.messages.Message;
import org.opends.messages.MessageBuilder;
import org.opends.quicksetup.util.PlainTextProgressMessageFormatter;
import org.opends.quicksetup.util.ProgressMessageFormatter;
import org.opends.quicksetup.util.Utils;
import org.opends.server.protocols.ldap.LDAPResultCode;
import org.opends.server.tools.ClientException;
import org.opends.server.types.NullOutputStream;
import org.opends.server.util.PasswordReader;
import org.opends.server.util.SetupUtils;
/**
* This class provides an abstract base class which can be used as the
* basis of a console-based application.
*/
public abstract class ConsoleApplication {
/**
* A null reader.
*/
private static final class NullReader extends Reader {
/**
* {@inheritDoc}
*/
@Override
public void close() throws IOException {
// Do nothing.
}
/**
* {@inheritDoc}
*/
@Override
public int read(char[] cbuf, int off, int len) throws IOException {
return -1;
}
}
// The error stream which this application should use.
private final PrintStream err;
// The input stream reader which this application should use.
private final BufferedReader in;
// The output stream which this application should use.
private final PrintStream out;
/**
* The maximum number of times we try to confirm.
*/
protected final static int CONFIRMATION_MAX_TRIES = 5;
private static final String COMMENT_SHELL_UNIX = "# ";
private static final String COMMENT_BATCH_WINDOWS = "rem ";
/**
* The String used to write comments in a shell (or batch) script.
*/
protected static final String SHELL_COMMENT_SEPARATOR =
SetupUtils.isWindows() ?
COMMENT_BATCH_WINDOWS : COMMENT_SHELL_UNIX;
/**
* Creates a new console application instance.
*
* @param in
* The application input stream.
* @param out
* The application output stream.
* @param err
* The application error stream.
*/
protected ConsoleApplication(BufferedReader in, PrintStream out,
PrintStream err) {
if (in != null) {
this.in = in;
} else {
this.in = new BufferedReader(new NullReader());
}
if (out != null) {
this.out = out;
} else {
this.out = NullOutputStream.printStream();
}
if (err != null) {
this.err = out;
} else {
this.err = NullOutputStream.printStream();
}
}
/**
* Creates a new console application instance.
*
* @param in
* The application input stream.
* @param out
* The application output stream.
* @param err
* The application error stream.
*/
protected ConsoleApplication(InputStream in, OutputStream out,
OutputStream err) {
if (in != null) {
this.in = new BufferedReader(new InputStreamReader(in));
} else {
this.in = new BufferedReader(new NullReader());
}
if (out != null) {
this.out = new PrintStream(out);
} else {
this.out = NullOutputStream.printStream();
}
if (err != null) {
this.err = new PrintStream(err);
} else {
this.err = NullOutputStream.printStream();
}
}
/**
* Interactively confirms whether a user wishes to perform an
* action. If the application is non-interactive, then the provided
* default is returned automatically.
*
* @param prompt
* The prompt describing the action.
* @param defaultValue
* The default value for the confirmation message. This
* will be returned if the application is non-interactive
* or if the user just presses return.
* @return Returns true if the user wishes the action
* to be performed, or false if they refused,
* or if an exception occurred.
* @throws CLIException
* If the user's response could not be read from the
* console for some reason.
*/
public final boolean confirmAction(Message prompt, final boolean defaultValue)
throws CLIException {
if (!isInteractive()) {
return defaultValue;
}
final Message yes = INFO_GENERAL_YES.get();
final Message no = INFO_GENERAL_NO.get();
final Message errMsg = ERR_CONSOLE_APP_CONFIRM.get(yes, no);
prompt = INFO_MENU_PROMPT_CONFIRM.get(prompt, yes, no, defaultValue ? yes
: no);
ValidationCallback validator = new ValidationCallback() {
public Boolean validate(ConsoleApplication app, String input) {
String ninput = input.toLowerCase().trim();
if (ninput.length() == 0) {
return defaultValue;
} else if (no.toString().toLowerCase().startsWith(ninput)) {
return false;
} else if (yes.toString().toLowerCase().startsWith(ninput)) {
return true;
} else {
// Try again...
app.println();
app.println(errMsg);
app.println();
}
return null;
}
};
return readValidatedInput(prompt, validator, CONFIRMATION_MAX_TRIES);
}
/**
* Gets the application error stream.
*
* @return Returns the application error stream.
*/
public final PrintStream getErrorStream() {
return err;
}
/**
* Gets the application input stream.
*
* @return Returns the application input stream.
*/
public final BufferedReader getInputStream() {
return in;
}
/**
* Gets the application output stream.
*
* @return Returns the application output stream.
*/
public final PrintStream getOutputStream() {
return out;
}
/**
* Indicates whether or not the user has requested advanced mode.
*
* @return Returns true if the user has requested
* advanced mode.
*/
public abstract boolean isAdvancedMode();
/**
* Indicates whether or not the user has requested interactive
* behavior.
*
* @return Returns true if the user has requested
* interactive behavior.
*/
public abstract boolean isInteractive();
/**
* Indicates whether or not this console application is running in
* its menu-driven mode. This can be used to dictate whether output
* should go to the error stream or not. In addition, it may also
* dictate whether or not sub-menus should display a cancel option
* as well as a quit option.
*
* @return Returns true if this console application
* is running in its menu-driven mode.
*/
public abstract boolean isMenuDrivenMode();
/**
* Indicates whether or not the user has requested quiet output.
*
* @return Returns true if the user has requested
* quiet output.
*/
public abstract boolean isQuiet();
/**
* Indicates whether or not the user has requested script-friendly
* output.
*
* @return Returns true if the user has requested
* script-friendly output.
*/
public abstract boolean isScriptFriendly();
/**
* Indicates whether or not the user has requested verbose output.
*
* @return Returns true if the user has requested
* verbose output.
*/
public abstract boolean isVerbose();
/**
* Interactively prompts the user to press return to continue. This
* method should be called in situations where a user needs to be
* given a chance to read some documentation before continuing
* (continuing may cause the documentation to be scrolled out of
* view).
*/
public final void pressReturnToContinue() {
Message msg = INFO_MENU_PROMPT_RETURN_TO_CONTINUE.get();
try {
readLineOfInput(msg);
} catch (CLIException e) {
// Ignore the exception - applications don't care.
}
}
/**
* Displays a blank line to the error stream.
*/
public final void println() {
err.println();
}
/**
* Displays a message to the error stream.
*
* @param msg
* The message.
*/
public final void println(Message msg) {
err.println(wrapText(msg, MAX_LINE_WIDTH));
}
/**
* Displays a message to the error stream.
*
* @param msg
* The message.
*/
public final void print(Message msg) {
err.print(wrapText(msg, MAX_LINE_WIDTH));
}
/**
* Displays a blank line to the output stream if we are not in quiet mode.
*/
public final void printlnProgress() {
if (!isQuiet())
{
out.println();
}
}
/**
* Displays a message to the output stream if we are not in quiet mode.
*
* @param msg
* The message.
*/
public final void printProgress(Message msg) {
if (!isQuiet())
{
out.print(msg);
}
}
/**
* Displays a message to the error stream indented by the specified
* number of columns.
*
* @param msg
* The message.
* @param indent
* The number of columns to indent.
*/
public final void println(Message msg, int indent) {
err.println(wrapText(msg, MAX_LINE_WIDTH, indent));
}
/**
* Displays a message to the error stream if verbose mode is
* enabled.
*
* @param msg
* The verbose message.
*/
public final void printVerboseMessage(Message msg) {
if (isVerbose() || isInteractive()) {
err.println(wrapText(msg, MAX_LINE_WIDTH));
}
}
/**
* Interactively retrieves a line of input from the console.
*
* @param prompt
* The prompt.
* @return Returns the line of input, or null if the
* end of input has been reached.
* @throws CLIException
* If the line of input could not be retrieved for some
* reason.
*/
public final String readLineOfInput(Message prompt) throws CLIException {
if (prompt != null)
{
err.print(wrapText(prompt, MAX_LINE_WIDTH));
err.print(" ");
}
try {
String s = in.readLine();
if (s == null) {
throw CLIException
.adaptInputException(new EOFException("End of input"));
} else {
return s;
}
} catch (IOException e) {
throw CLIException.adaptInputException(e);
}
}
/**
* Commodity method that interactively prompts (on error 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.
*
* @throws CLIException
* If the line of input could not be retrieved for some
* reason.
* @return The string value read from the user.
*/
public String readInput(Message prompt, String defaultValue)
throws CLIException {
while (true) {
if (defaultValue != null) {
prompt = INFO_PROMPT_SINGLE_DEFAULT.get(prompt.toString(),
defaultValue);
}
String response = readLineOfInput(prompt);
if ("".equals(response)) {
if (defaultValue == null) {
println(INFO_ERROR_EMPTY_RESPONSE.get());
} else {
return defaultValue;
}
} else {
return response;
}
}
}
/**
* Commodity method that interactively prompts (on error 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).
* If an error occurs a message will be logged to the provided logger.
*
* @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 logger the Logger to be used to log the error message.
* @return The string value read from the user.
*/
public String readInput(Message prompt, String defaultValue, Logger logger)
{
String s = defaultValue;
try
{
s = readInput(prompt, defaultValue);
}
catch (CLIException ce)
{
logger.log(Level.WARNING, "Error reading input: "+ce, ce);
}
return s;
}
/**
* Interactively retrieves a password from the console.
*
* @param prompt
* The password prompt.
* @return Returns the password.
* @throws CLIException
* If the password could not be retrieved for some reason.
*/
public final String readPassword(Message prompt) throws CLIException {
err.print(wrapText(prompt + " ", MAX_LINE_WIDTH));
char[] pwChars;
try {
pwChars = PasswordReader.readPassword();
} catch (Exception e) {
throw CLIException.adaptInputException(e);
}
return new String(pwChars);
}
/**
* Commodity method that interactively retrieves a password from the
* console. If there is an error an error message is logged to the provided
* Logger and null is returned.
*
* @param prompt
* The password prompt.
* @param logger the Logger to be used to log the error message.
* @return Returns the password.
*/
protected final String readPassword(Message prompt, Logger logger)
{
String pwd = null;
try
{
pwd = readPassword(prompt);
}
catch (CLIException ce)
{
logger.log(Level.WARNING, "Error reading input: "+ce, ce);
}
return pwd;
}
/**
* Interactively retrieves a port value from the console.
*
* @param prompt
* The port prompt.
* @param defaultValue
* The port default value.
* @return Returns the port.
* @throws CLIException
* If the port could not be retrieved for some reason.
*/
public final int readPort(Message prompt, final int defaultValue)
throws CLIException
{
ValidationCallback callback = new ValidationCallback()
{
public Integer validate(ConsoleApplication app, String input)
throws CLIException
{
String ninput = input.trim();
if (ninput.length() == 0)
{
return defaultValue;
}
else
{
try
{
int i = Integer.parseInt(ninput);
if (i < 1 || i > 65535)
{
throw new NumberFormatException();
}
return i;
}
catch (NumberFormatException e)
{
// Try again...
app.println();
app.println(ERR_LDAP_CONN_BAD_PORT_NUMBER.get(ninput));
app.println();
return null;
}
}
}
};
if (defaultValue != -1) {
prompt = INFO_PROMPT_SINGLE_DEFAULT.get(prompt.toString(),
String.valueOf(defaultValue));
}
return readValidatedInput(prompt, callback, CONFIRMATION_MAX_TRIES);
}
/**
* Returns a message object for the given NamingException.
* @param ne the NamingException.
* @param hostPort the hostPort representation of the server we were
* contacting when the NamingException occurred.
* @return a message object for the given NamingException.
*/
protected Message getMessageForException(NamingException ne,
String hostPort)
{
return Utils.getMessageForException(ne, hostPort);
}
/**
* Commodity method used to repeatidly ask the user to provide a port value.
* @param prompt the prompt message.
* @param defaultValue the default value of the port to be proposed to the
* user.
* @param logger the logger where the errors will be written.
* @return the port value provided by the user.
*/
protected int askPort(Message prompt, int defaultValue, Logger logger)
{
int port = -1;
while (port == -1)
{
try
{
port = readPort(prompt, defaultValue);
}
catch (CLIException ce)
{
port = -1;
logger.log(Level.WARNING, "Error reading input: "+ce, ce);
}
}
return port;
}
/**
* Interactively prompts for user input and continues until valid
* input is provided.
*
* @param
* The type of decoded user input.
* @param prompt
* The interactive prompt which should be displayed on each
* input attempt.
* @param validator
* An input validator responsible for validating and
* decoding the user's response.
* @return Returns the decoded user's response.
* @throws CLIException
* If an unexpected error occurred which prevented
* validation.
*/
public final T readValidatedInput(Message prompt,
ValidationCallback validator) throws CLIException {
while (true) {
String response = readLineOfInput(prompt);
T value = validator.validate(this, response);
if (value != null) {
return value;
}
}
}
/**
* Interactively prompts for user input and continues until valid
* input is provided.
*
* @param
* The type of decoded user input.
* @param prompt
* The interactive prompt which should be displayed on each
* input attempt.
* @param validator
* An input validator responsible for validating and
* decoding the user's response.
* @param maxTries
* The maximum number of tries that we can make.
* @return Returns the decoded user's response.
* @throws CLIException
* If an unexpected error occurred which prevented
* validation or if the maximum number of tries was reached.
*/
public final T readValidatedInput(Message prompt,
ValidationCallback validator, int maxTries) throws CLIException {
int nTries = 0;
while (nTries < maxTries) {
String response = readLineOfInput(prompt);
T value = validator.validate(this, response);
if (value != null) {
return value;
}
nTries++;
}
throw new CLIException(ERR_TRIES_LIMIT_REACHED.get(maxTries));
}
/**
* Commodity method that interactively confirms whether a user wishes to
* perform an action. If the application is non-interactive, then the provided
* default is returned automatically. If there is an error an error message
* is logged to the provided Logger and the defaul value is returned.
*
* @param prompt
* The prompt describing the action.
* @param defaultValue
* The default value for the confirmation message. This
* will be returned if the application is non-interactive
* or if the user just presses return.
* @param logger the Logger to be used to log the error message.
* @return Returns true if the user wishes the action
* to be performed, or false if they refused.
* @throws CLIException if the user did not provide valid answer after
* a certain number of tries
* (ConsoleApplication.CONFIRMATION_MAX_TRIES)
*/
protected final boolean askConfirmation(Message prompt, boolean defaultValue,
Logger logger) throws CLIException
{
boolean v = defaultValue;
boolean done = false;
int nTries = 0;
while (!done && (nTries < CONFIRMATION_MAX_TRIES))
{
nTries++;
try
{
v = confirmAction(prompt, defaultValue);
done = true;
}
catch (CLIException ce)
{
if (ce.getMessageObject().getDescriptor().equals(
ERR_CONFIRMATION_TRIES_LIMIT_REACHED) ||
ce.getMessageObject().getDescriptor().equals(
ERR_TRIES_LIMIT_REACHED))
{
throw ce;
}
logger.log(Level.WARNING, "Error reading input: "+ce, ce);
// Try again...
println();
}
}
if (!done)
{
// This means we reached the maximum number of tries
throw new CLIException(ERR_CONFIRMATION_TRIES_LIMIT_REACHED.get(
CONFIRMATION_MAX_TRIES));
}
return v;
}
/**
* Returns an InitialLdapContext using the provided parameters. We try
* to guarantee that the connection is able to read the configuration.
* @param host the host name.
* @param port the port to connect.
* @param useSSL whether to use SSL or not.
* @param useStartTLS whether to use StartTLS or not.
* @param bindDn the bind dn to be used.
* @param pwd the password.
* @param connectTimeout the timeout in milliseconds to connect to the server.
* @param trustManager the trust manager.
* @return an InitialLdapContext connected.
* @throws NamingException if there was an error establishing the connection.
*/
protected InitialLdapContext createAdministrativeContext(String host,
int port, boolean useSSL, boolean useStartTLS, String bindDn, String pwd,
int connectTimeout, ApplicationTrustManager trustManager)
throws NamingException
{
InitialLdapContext ctx;
String ldapUrl = ConnectionUtils.getLDAPUrl(host, port, useSSL);
if (useSSL)
{
ctx = Utils.createLdapsContext(ldapUrl, bindDn, pwd,
connectTimeout, null, trustManager);
}
else if (useStartTLS)
{
ctx = Utils.createStartTLSContext(ldapUrl, bindDn, pwd,
connectTimeout, null, trustManager,
null);
}
else
{
ctx = Utils.createLdapContext(ldapUrl, bindDn, pwd,
connectTimeout, null);
}
if (!ConnectionUtils.connectedAsAdministrativeUser(ctx))
{
throw new NoPermissionException(
ERR_NOT_ADMINISTRATIVE_USER.get().toString());
}
return ctx;
}
/**
* Creates an Initial LDAP Context interacting with the user if the
* application is interactive.
* @param ci the LDAPConnectionConsoleInteraction object that is assumed
* to have been already run.
* @return the initial LDAP context or null if the user did
* not accept to trust the certificates.
* @throws ClientException if there was an error establishing the connection.
*/
protected InitialLdapContext createInitialLdapContextInteracting(
LDAPConnectionConsoleInteraction ci) throws ClientException
{
return createInitialLdapContextInteracting(ci, isInteractive() &&
ci.isTrustStoreInMemory());
}
/**
* Creates an Initial LDAP Context interacting with the user if the
* application is interactive.
* @param ci the LDAPConnectionConsoleInteraction object that is assumed
* to have been already run.
* @param promptForCertificate whether we should prompt for the certificate
* or not.
* @return the initial LDAP context or null if the user did
* not accept to trust the certificates.
* @throws ClientException if there was an error establishing the connection.
*/
protected InitialLdapContext createInitialLdapContextInteracting(
LDAPConnectionConsoleInteraction ci,
boolean promptForCertificate) throws ClientException
{
// Interact with the user though the console to get
// LDAP connection information
String hostName = ConnectionUtils.getHostNameForLdapUrl(ci.getHostName());
Integer portNumber = ci.getPortNumber();
String bindDN = ci.getBindDN();
String bindPassword = ci.getBindPassword();
TrustManager trustManager = ci.getTrustManager();
KeyManager keyManager = ci.getKeyManager();
InitialLdapContext ctx;
if (ci.useSSL())
{
String ldapsUrl = "ldaps://" + hostName + ":" + portNumber;
while (true)
{
try
{
ctx = ConnectionUtils.createLdapsContext(ldapsUrl, bindDN,
bindPassword, ci.getConnectTimeout(), null,
trustManager, keyManager);
ctx.reconnect(null);
break;
}
catch (NamingException e)
{
if (promptForCertificate)
{
OpendsCertificateException oce = getCertificateRootException(e);
if (oce != null)
{
String authType = null;
if (trustManager instanceof ApplicationTrustManager)
{
ApplicationTrustManager appTrustManager =
(ApplicationTrustManager)trustManager;
authType = appTrustManager.getLastRefusedAuthType();
}
if (ci.checkServerCertificate(oce.getChain(), authType,
hostName))
{
// If the certificate is trusted, update the trust manager.
trustManager = ci.getTrustManager();
// Try to connect again.
continue;
}
else
{
// Assume user canceled.
return null;
}
}
}
if (e.getCause() != null)
{
if (!isInteractive() &&
!ci.isTrustAll())
{
if (getCertificateRootException(e) != null ||
(e.getCause() instanceof SSLHandshakeException))
{
Message message =
ERR_DSCFG_ERROR_LDAP_FAILED_TO_CONNECT_NOT_TRUSTED.get(
hostName, String.valueOf(portNumber));
throw new ClientException(
LDAPResultCode.CLIENT_SIDE_CONNECT_ERROR, message);
}
}
if (e.getCause() instanceof SSLException)
{
Message message =
ERR_DSCFG_ERROR_LDAP_FAILED_TO_CONNECT_WRONG_PORT.get(
hostName, String.valueOf(portNumber));
throw new ClientException(
LDAPResultCode.CLIENT_SIDE_CONNECT_ERROR, message);
}
}
String hostPort =
ServerDescriptor.getServerRepresentation(hostName, portNumber);
Message message = Utils.getMessageForException(e, hostPort);
throw new ClientException(
LDAPResultCode.CLIENT_SIDE_CONNECT_ERROR, message);
}
}
}
else if (ci.useStartTLS())
{
String ldapUrl = "ldap://" + hostName + ":" + portNumber;
while (true)
{
try
{
ctx = ConnectionUtils.createStartTLSContext(ldapUrl, bindDN,
bindPassword, ConnectionUtils.getDefaultLDAPTimeout(), null,
trustManager, keyManager, null);
ctx.reconnect(null);
break;
}
catch (NamingException e)
{
if (promptForCertificate)
{
OpendsCertificateException oce = getCertificateRootException(e);
if (oce != null)
{
String authType = null;
if (trustManager instanceof ApplicationTrustManager)
{
ApplicationTrustManager appTrustManager =
(ApplicationTrustManager)trustManager;
authType = appTrustManager.getLastRefusedAuthType();
}
if (ci.checkServerCertificate(oce.getChain(), authType,
hostName))
{
// If the certificate is trusted, update the trust manager.
trustManager = ci.getTrustManager();
// Try to connect again.
continue ;
}
else
{
// Assume user cancelled.
return null;
}
}
else
{
Message message = ERR_DSCFG_ERROR_LDAP_FAILED_TO_CONNECT.get(
hostName, String.valueOf(portNumber));
throw new ClientException(
LDAPResultCode.CLIENT_SIDE_CONNECT_ERROR, message);
}
}
Message message = ERR_DSCFG_ERROR_LDAP_FAILED_TO_CONNECT.get(
hostName, String.valueOf(portNumber));
throw new ClientException(
LDAPResultCode.CLIENT_SIDE_CONNECT_ERROR, message);
}
}
}
else
{
String ldapUrl = "ldap://" + hostName + ":" + portNumber;
while (true)
{
try
{
ctx = ConnectionUtils.createLdapContext(ldapUrl, bindDN,
bindPassword, ConnectionUtils.getDefaultLDAPTimeout(), null);
ctx.reconnect(null);
break;
}
catch (NamingException e)
{
Message message = ERR_DSCFG_ERROR_LDAP_FAILED_TO_CONNECT.get(
hostName, String.valueOf(portNumber));
throw new ClientException(
LDAPResultCode.CLIENT_SIDE_CONNECT_ERROR, message);
}
}
}
return ctx;
}
/**
* Returns the message to be displayed in the file with the equivalent
* command-line with information about the current time.
* @return the message to be displayed in the file with the equivalent
* command-line with information about the current time.
*/
protected String getCurrentOperationDateMessage()
{
String date = formatDateTimeStringForEquivalentCommand(new Date());
return INFO_OPERATION_START_TIME_MESSAGE.get(date).
toString();
}
/**
* Formats a Date to String representation in "dd/MMM/yyyy:HH:mm:ss Z".
*
* @param date to format; null if date is null
* @return string representation of the date
*/
protected String formatDateTimeStringForEquivalentCommand(Date date)
{
String timeStr = null;
if (date != null)
{
SimpleDateFormat dateFormat =
new SimpleDateFormat(DATE_FORMAT_LOCAL_TIME);
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
timeStr = dateFormat.format(date);
}
return timeStr;
}
/**
* Prompts the user to give the Global Administrator UID.
* @param defaultValue the default value that will be proposed in the prompt
* message.
* @param logger the Logger to be used to log the error message.
* @return the Global Administrator UID as provided by the user.
*/
protected String askForAdministratorUID(String defaultValue,
Logger logger)
{
String s = defaultValue;
try
{
s = readInput(INFO_ADMINISTRATOR_UID_PROMPT.get(), defaultValue);
}
catch (CLIException ce)
{
logger.log(Level.WARNING, "Error reading input: "+ce, ce);
}
return s;
}
/**
* Prompts the user to give the Global Administrator password.
* @param logger the Logger to be used to log the error message.
* @return the Global Administrator password as provided by the user.
*/
protected String askForAdministratorPwd(Logger logger)
{
String pwd = readPassword(INFO_ADMINISTRATOR_PWD_PROMPT.get(), logger);
return pwd;
}
/**
* The default period time used to write points in the output.
*/
protected static final long DEFAULT_PERIOD_TIME = 3000;
/**
* Class used to add points periodically to the end of the output.
*
*/
protected class PointAdder implements Runnable
{
private Thread t;
private boolean stopPointAdder;
private boolean pointAdderStopped;
private long periodTime = DEFAULT_PERIOD_TIME;
private boolean isError;
private ProgressMessageFormatter formatter;
/**
* Default constructor.
* Creates a PointAdder that writes to the standard output with the default
* period time.
*/
public PointAdder()
{
this(DEFAULT_PERIOD_TIME, false, new PlainTextProgressMessageFormatter());
}
/**
* Default constructor.
* @param periodTime the time between printing two points.
* @param isError whether the points must be printed in error stream
* or output stream.
* @param formatter the text formatter.
*/
public PointAdder(long periodTime, boolean isError,
ProgressMessageFormatter formatter)
{
this.periodTime = periodTime;
this.isError = isError;
this.formatter = formatter;
}
/**
* Starts the PointAdder: points are added at the end of the logs
* periodically.
*/
public void start()
{
MessageBuilder mb = new MessageBuilder();
mb.append(formatter.getSpace());
for (int i=0; i< 5; i++)
{
mb.append(formatter.getFormattedPoint());
}
if (isError)
{
print(mb.toMessage());
}
else
{
printProgress(mb.toMessage());
}
t = new Thread(this);
t.start();
}
/**
* Stops the PointAdder: points are no longer added at the end of the logs
* periodically.
*/
public synchronized void stop()
{
stopPointAdder = true;
while (!pointAdderStopped)
{
try
{
t.interrupt();
// To allow the thread to set the boolean.
Thread.sleep(100);
}
catch (Throwable t)
{
}
}
}
/**
* {@inheritDoc}
*/
public void run()
{
while (!stopPointAdder)
{
try
{
Thread.sleep(periodTime);
if (isError)
{
print(formatter.getFormattedPoint());
}
else
{
printProgress(formatter.getFormattedPoint());
}
}
catch (Throwable t)
{
}
}
pointAdderStopped = true;
}
}
private OpendsCertificateException getCertificateRootException(Throwable t)
{
OpendsCertificateException oce = null;
while (t != null && oce == null)
{
t = t.getCause();
if (t instanceof OpendsCertificateException)
{
oce = (OpendsCertificateException)t;
}
}
return oce;
}
}