ReplicationCliMain.java revision 407bb81fb935e713a4a1ae1b9189b81488a944d5
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at legal-notices/CDDLv1_0.txt.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Copyright 2007-2010 Sun Microsystems, Inc.
* Portions Copyright 2011-2014 ForgeRock AS
* Portions Copyright 2012 profiq s.r.o.
*/
/**
* This class provides a tool that can be used to enable and disable replication
* and also to initialize the contents of a replicated suffix with the contents
* of another suffix. It also allows to display the replicated status of the
* different base DNs of the servers that are registered in the ADS.
*/
public class ReplicationCliMain extends ConsoleApplication
{
/** The fully-qualified name of this class. */
/** Prefix for log files. */
/** Suffix for log files. */
/**
* Property used to call the dsreplication script and ReplicationCliMain to
* know which are the java properties to be used (those of dsreplication or
* those of dsreplication.offline).
*/
private static final String SCRIPT_CALL_STATUS =
"org.opends.server.dsreplicationcallstatus";
/**
* The value set by the dsreplication script if it is called the first time.
*/
private boolean forceNonInteractive;
/** Always use SSL with the administration connector. */
private final boolean useSSL = true;
private final boolean useStartTLS = false;
/**
* The enumeration containing the different options we display when we ask
* the user to provide the subcommand interactively.
*/
private enum SubcommandChoice
{
/** Enable replication. */
/** Disable replication. */
/** Initialize replication. */
/** Initialize All. */
INITIALIZE_ALL(INITIALIZE_ALL_REPLICATION_SUBCMD_NAME, INFO_REPLICATION_INITIALIZE_ALL_MENU_PROMPT.get()),
/** Pre external initialization. */
/** Post external initialization. */
/** Replication status. */
/** Replication purge historical. */
PURGE_HISTORICAL(PURGE_HISTORICAL_SUBCMD_NAME, INFO_REPLICATION_PURGE_HISTORICAL_MENU_PROMPT.get()),
/** Cancel operation. */
private LocalizableMessage prompt;
{
}
private LocalizableMessage getPrompt()
{
return prompt;
}
{
return name;
}
{
SubcommandChoice[] f = values();
for (SubcommandChoice subCommand : f)
{
{
return subCommand;
}
}
return null;
}
}
/** The argument parser to be used. */
private ReplicationCliArgumentParser argParser;
private LDAPConnectionConsoleInteraction ci;
private CommandBuilder firstServerCommandBuilder;
/** The message formatter. */
private PlainTextProgressMessageFormatter formatter =
/**
* Constructor for the ReplicationCliMain object.
*
* @param out the print stream to use for standard output.
* @param err the print stream to use for standard error.
*/
{
}
/**
* The main method for the replication tool.
*
* @param args the command-line arguments provided to this program.
*/
{
}
/**
* Parses the provided command-line arguments and uses that information to
* run the replication tool.
*
* @param args the command-line arguments provided to this program.
*
* @return The error code.
*/
{
}
/**
* Parses the provided command-line arguments and uses that information to
* run the replication tool.
*
* @param args The command-line arguments provided to this
* program.
* @param initializeServer Indicates whether to initialize the server.
* @param outStream The output stream to use for standard output, or
* <CODE>null</CODE> if standard output is not
* needed.
* @param errStream The output stream to use for standard error, or
* <CODE>null</CODE> if standard error is not
* needed.
* @return The error code.
*/
{
try
{
} catch (Throwable t) {
t.printStackTrace();
}
}
/**
* Parses the provided command-line arguments and uses that information to
* run the replication tool.
*
* @param args the command-line arguments provided to this program.
* @param initializeServer Indicates whether to initialize the server.
*
* @return The error code.
*/
{
// Create the command-line argument parser for use with this
// program.
try
{
}
catch (ArgumentException ae)
{
}
if (returnValue == SUCCESSFUL_NOP)
{
try
{
}
catch (ConfigException ce)
{
// Ignore.
}
// Parse the command-line arguments provided to this program.
try
{
}
catch (ArgumentException ae)
{
println();
}
}
// If we should just display usage or version information,
// then print it and exit.
if (argParser.usageOrVersionDisplayed()) {
return 0;
}
// Checks the version - if upgrade required, the tool is unusable
try
{
}
catch (InitializationException e)
{
println(e.getMessageObject());
return 1;
}
if (!argParser.usageOrVersionDisplayed())
{
if (returnValue == SUCCESSFUL_NOP)
{
/* Check that the provided parameters are compatible.
*/
{
}
}
{
// Bootstrap definition classes.
try
{
{
}
// Switch off class name validation in client.
// Switch off attribute type name validation in client.
}
catch (InitializationException ie)
{
}
}
if (returnValue == SUCCESSFUL_NOP)
{
if (argParser.getSecureArgsList().
{
try
{
"adminPasswordFile",
OPTION_SHORT_BINDPWD_FILE, "adminPasswordFile", false, false,
}
catch (Throwable t)
{
throw new IllegalStateException("Unexpected error: "+t, t);
}
}
ci = new LDAPConnectionConsoleInteraction(this,
ci.setDisplayLdapIfSecureParameters(false);
}
if (returnValue == SUCCESSFUL_NOP)
{
boolean subcommandLaunched = true;
if (subcommandChoice != null)
{
}
else if (argParser.isInteractive())
{
{
}
else
{
// User canceled
}
if (subCommand != null)
{
// The server (if requested) has already been initialized.
}
}
else
{
"--"+OPTION_LONG_NO_PROMPT));
subcommandLaunched = false;
}
// Display the log file only if the operation is successful (when there
// is a critical error this is already displayed).
&& returnValue == SUCCESSFUL
{
{
println();
println();
}
}
}
}
return returnValue.getReturnCode();
}
{
if (subCommand != null)
{
}
return null;
}
private ReplicationCliReturnCode execute(SubcommandChoice subcommandChoice, ReplicationCliReturnCode defaultValue)
{
switch (subcommandChoice)
{
case ENABLE:
return enableReplication();
case DISABLE:
return disableReplication();
case INITIALIZE:
return initializeReplication();
case INITIALIZE_ALL:
return initializeAllReplication();
return preExternalInitialization();
return postExternalInitialization();
case STATUS:
return statusReplication();
case PURGE_HISTORICAL:
return purgeHistorical();
}
return defaultValue;
}
/**
* 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.
*/
{
try
{
}
catch (ClientException ce)
{
return defaultValue;
}
}
/**
* 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.
*/
{
try
{
}
catch (ClientException ex)
{
return null;
}
}
/**
* Commodity method used to repeatidly ask the user to provide an integer
* value.
*
* @param prompt
* the prompt message.
* @param defaultValue
* the default value to be proposed to the user.
* @param logger
* the logger where the errors will be written.
* @return the value provided by the user.
*/
{
int newInt = -1;
while (newInt == -1)
{
try
{
}
catch (ClientException ce)
{
newInt = -1;
}
}
return newInt;
}
/**
* Interactively retrieves an integer value from the console.
*
* @param prompt
* The message prompt.
* @param defaultValue
* The default value.
* @return Returns the value.
* @throws ClientException
* If the value could not be retrieved for some reason.
*/
public final int readInteger(
{
{
throws ClientException
{
{
return defaultValue;
}
try
{
if (i < 1)
{
throw new NumberFormatException();
}
return i;
}
catch (NumberFormatException e)
{
// Try again...
return null;
}
}
};
if (defaultValue != -1)
{
}
}
private boolean isFirstCallFromScript()
{
}
private void createArgumenParser() throws ArgumentException
{
}
/**
* Based on the data provided in the command-line it enables replication
* between two servers.
* @return the error code if the operation failed and 0 if it was successful.
*/
private ReplicationCliReturnCode enableReplication()
{
if (argParser.isInteractive())
{
try
{
if (promptIfRequired(uData))
{
return enableReplication(uData);
}
else
{
return USER_CANCELLED;
}
}
catch (ReplicationCliException rce)
{
println();
return rce.getErrorCode();
}
}
else
{
return enableReplication(uData);
}
}
/**
* Based on the data provided in the command-line it disables replication
* in the server.
* @return the error code if the operation failed and SUCCESSFUL if it was
* successful.
*/
private ReplicationCliReturnCode disableReplication()
{
if (argParser.isInteractive())
{
try
{
if (promptIfRequired(uData))
{
return disableReplication(uData);
}
else
{
return USER_CANCELLED;
}
}
catch (ReplicationCliException rce)
{
println();
return rce.getErrorCode();
}
}
else
{
return disableReplication(uData);
}
}
/**
* Based on the data provided in the command-line initialize the contents
* of the whole replication topology.
* @return the error code if the operation failed and SUCCESSFUL if it was
* successful.
*/
{
if (argParser.isInteractive())
{
if (promptIfRequired(uData))
{
return initializeAllReplication(uData);
}
else
{
return USER_CANCELLED;
}
}
else
{
return initializeAllReplication(uData);
}
}
/**
* Based on the data provided in the command-line execute the pre external
* initialization operation.
* @return the error code if the operation failed and SUCCESSFUL if it was
* successful.
*/
{
if (argParser.isInteractive())
{
{
return preExternalInitialization(uData);
}
else
{
return USER_CANCELLED;
}
}
else
{
return preExternalInitialization(uData);
}
}
/**
* Based on the data provided in the command-line execute the post external
* initialization operation.
* @return the error code if the operation failed and SUCCESSFUL if it was
* successful.
*/
{
if (argParser.isInteractive())
{
{
return postExternalInitialization(uData);
}
else
{
return USER_CANCELLED;
}
}
else
{
return postExternalInitialization(uData);
}
}
/**
* Based on the data provided in the command-line it displays replication
* status.
* @return the error code if the operation failed and SUCCESSFUL if it was
* successful.
*/
private ReplicationCliReturnCode statusReplication()
{
if (argParser.isInteractive())
{
try
{
if (promptIfRequired(uData))
{
return statusReplication(uData);
}
else
{
return USER_CANCELLED;
}
}
catch (ReplicationCliException rce)
{
println();
return rce.getErrorCode();
}
}
else
{
return statusReplication(uData);
}
}
/**
* Based on the data provided in the command-line it displays replication
* status.
* @return the error code if the operation failed and SUCCESSFUL if it was
* successful.
*/
private ReplicationCliReturnCode purgeHistorical()
{
if (argParser.isInteractive())
{
if (promptIfRequired(uData))
{
return purgeHistorical(uData);
}
else
{
return USER_CANCELLED;
}
}
else
{
return purgeHistorical(uData);
}
}
/**
* Initializes the contents of the provided purge historical replication user
* data object with what was provided in the command-line without prompting to
* the user.
* @param uData the purge historical replication user data object to be
* initialized.
*/
{
}
{
}
{
{
if (mustPrintCommandBuilder())
{
}
try
{
return purgeHistoricalLocallyTask(uData);
}
catch (ReplicationCliException rce)
{
println();
return rce.getErrorCode();
}
}
else
{
}
}
{
println();
println();
}
throws ReplicationCliException
{
if (isFirstCallFromScript())
{
// Launch the process: launch dsreplication in non-interactive mode with
// the recursive property set.
{
}
// Use the java args in the script.
try
{
{
if (c.getReturnCode() == code)
{
returnCode = c;
break;
}
}
}
catch (Exception e)
{
throw new ReplicationCliException(
}
}
else
{
if (returnCode == SUCCESSFUL)
{
}
}
return returnCode;
}
/**
* 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.
*/
throws NamingException
{
if (useSSL)
{
}
else if (useStartTLS)
{
}
else
{
}
if (!connectedAsAdministrativeUser(ctx))
{
}
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 <CODE>null</CODE> if the user did not
* accept to trust the certificates.
* @throws ClientException
* if there was an error establishing the connection.
*/
{
&& ci.isTrustStoreInMemory());
}
{
while (t != null)
{
t = t.getCause();
if (t instanceof OpendsCertificateException)
{
return (OpendsCertificateException) t;
}
}
return null;
}
/**
* 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 <CODE>null</CODE> if the user did not
* accept to trust the certificates.
* @throws ClientException
* if there was an error establishing the connection.
*/
throws ClientException
{
// Interact with the user though the console to get
// LDAP connection information
{
while (true)
{
try
{
break;
}
catch (NamingException e)
{
if (promptForCertificate)
{
{
if (trustManager instanceof ApplicationTrustManager)
{
}
{
// If the certificate is trusted, update the trust manager.
// Try to connect again.
continue;
}
else
{
// Assume user canceled.
return null;
}
}
}
{
if (!isInteractive()
&& !ci.isTrustAll()
&& (getCertificateRootException(e) != null
|| e.getCause() instanceof SSLHandshakeException))
{
}
if (e.getCause() instanceof SSLException)
{
throw new ClientException(
}
}
message);
}
}
}
else if (ci.useStartTLS())
{
while (true)
{
try
{
break;
}
catch (NamingException e)
{
if (promptForCertificate)
{
{
if (trustManager instanceof ApplicationTrustManager)
{
}
{
// If the certificate is trusted, update the trust manager.
// Try to connect again.
continue;
}
else
{
// Assume user cancelled.
return null;
}
}
else
{
throw new ClientException(
}
}
message);
}
}
}
else
{
while (true)
{
try
{
break;
}
catch (NamingException e)
{
message);
}
}
}
return ctx;
}
{
// Connect to the provided server
{
return ERROR_CONNECTING;
}
try
{
{
}
if (mustPrintCommandBuilder())
{
}
try
{
}
catch (ReplicationCliException rce)
{
println();
return rce.getErrorCode();
}
}
finally
{
}
}
{
}
private InitialLdapContext createAdministrativeContext(MonoServerReplicationUserData uData, final String bindDn)
{
try
{
}
catch (NamingException ne)
{
println();
return null;
}
}
{
println();
{
}
{
taskID));
}
{
}
else
{
taskID));
}
println();
}
/**
* Launches the purge historical operation using the
* provided connection.
* @param ctx the connection to the server.
* @throws ReplicationCliException if there is an error performing the
* operation.
*/
throws ReplicationCliException
{
boolean taskCreated = false;
boolean isOver = false;
while (!taskCreated)
{
try
{
taskCreated = true;
}
catch (NameAlreadyBoundException ex)
{
throw new ReplicationCliException(
}
catch (NamingException ne)
{
throw new ReplicationCliException(
}
}
// Wait until it is over
new String[] {
"ds-task-log-message",
"ds-task-state",
"ds-task-purge-conflicts-historical-purged-values-count",
"ds-task-purge-conflicts-historical-purge-completed-in-time",
"ds-task-purge-conflicts-historical-purge-completed-in-time",
"ds-task-purge-conflicts-historical-last-purged-changenumber"
});
// Polling only makes sense when we are recurrently scheduling a task
// or the task is being executed now.
{
sleepCatchInterrupt(500);
try
{
try
{
}
finally
{
}
{
lastLogMsg = logMsg;
}
{
isOver = true;
{
}
{
}
}
}
catch (NameNotFoundException x)
{
isOver = true;
}
catch (NamingException ne)
{
throw new ReplicationCliException(
}
}
if (returnCode == SUCCESSFUL)
{
}
return returnCode;
}
private LocalizableMessage getPurgeErrorMsg(String lastLogMsg, String state, InitialLdapContext ctx)
{
if (lastLogMsg != null)
{
}
}
/**
* Checks that historical can actually be purged in the provided baseDNs
* for the server.
* @param suffixes the suffixes provided by the user. This Collection is
* updated with the base DNs that the user provided interactively.
* @param ctx connection to the server.
* @param interactive whether to ask the user to provide interactively
* base DNs if none of the provided base DNs can be purged.
*/
{
}
/**
* Checks that historical can actually be purged in the provided baseDNs
* for the local server.
* @param suffixes the suffixes provided by the user. This Collection is
* updated with the base DNs that the user provided interactively.
* @param interactive whether to ask the user to provide interactively
* base DNs if none of the provided base DNs can be purged.
*/
boolean interactive)
{
}
{
{
{
{
}
else
{
}
}
}
return replicas;
}
private void checkSuffixesForPurgeHistorical(Collection<String> suffixes, Collection<ReplicaDescriptor> replicas,
boolean interactive)
{
{
if (rep.isReplicated())
{
}
else
{
}
}
}
boolean interactive)
{
if (availableSuffixes.isEmpty())
{
println();
}
else
{
// Verify that the provided suffixes are configured in the servers.
{
{
{
}
else
{
}
}
}
{
println();
}
if (interactive)
{
}
}
}
{
{
{
// In interactive mode we do not propose to manage the
// administration suffix.
println();
break;
}
println();
boolean confirmationLimitReached = askConfirmations(confirmationMsgPromt, availableSuffixes, suffixes);
{
break;
}
}
}
{
{
if (isSchemaOrAdminSuffix(suffix))
{
return true;
}
}
return false;
}
{
}
/**
* Based on the data provided in the command-line it initializes replication
* between two servers.
* @return the error code if the operation failed and SUCCESSFUL if it was
* successful.
*/
{
if (argParser.isInteractive())
{
if (promptIfRequired(uData))
{
return initializeReplication(uData);
}
else
{
return USER_CANCELLED;
}
}
else
{
return initializeReplication(uData);
}
}
/**
* Updates the contents of the provided PurgeHistoricalUserData
* object with the information provided in the command-line. If some
* information is missing, ask the user to provide valid data.
* We assume that if this method is called we are in interactive mode.
* @param uData the object to be updated.
* @return <CODE>true</CODE> if the object was successfully updated and
* <CODE>false</CODE> if the user canceled the operation.
*/
{
try
{
{
return false;
}
/* Prompt for maximum duration */
{
println();
}
{
}
else
{
}
{
return false;
}
{
try
{
interaction.run();
}
catch (ClientException ce)
{
return false;
}
}
return true;
}
finally
{
}
}
{
boolean firstTry = true;
while (true)
{
if (!promptForConnection)
{
if (serverRunning == null)
{
}
if (!serverRunning)
{
try
{
println();
}
catch (ClientException ce)
{
}
if (!promptForConnection)
{
return null;
}
}
}
try
{
{
}
return ctx;
}
catch (ClientException ce)
{
println();
println();
}
catch (ArgumentException ae)
{
println();
println();
return null;
}
firstTry = false;
}
}
{
{
}
return taskEntries;
}
/**
* Updates the contents of the provided EnableReplicationUserData object
* with the information provided in the command-line. If some information
* is missing, ask the user to provide valid data.
* We assume that if this method is called we are in interactive mode.
* @param uData the object to be updated.
* @return <CODE>true</CODE> if the object was successfully updated and
* <CODE>false</CODE> if the user cancelled the operation.
* @throws ReplicationCliException if a critical error occurs reading the
* ADS.
*/
throws ReplicationCliException
{
boolean cancelled = false;
boolean administratorDefined = false;
ci.setUseAdminOrBindDn(true);
/*
* Try to connect to the first server.
*/
{
}
{
}
{
{
}
}
/*
* Use a copy of the argument properties since the map might be cleared
* in initializeGlobalArguments.
*/
{
try
{
{
{
// If the explicit bind DN is not null, the password corresponds
// to that bind DN. We are in the case where the user provides
// bind DN on first server and admin UID globally.
}
}
{
cancelled = true;
}
}
catch (ClientException ce)
{
println();
println();
}
catch (ArgumentException ae)
{
println();
println();
cancelled = true;
}
}
if (!cancelled)
{
}
int replicationPort1 = -1;
boolean configureReplicationServer1 =
boolean configureReplicationDomain1 =
{
{
final LocalizableMessage msg =
if (!askConfirmation(msg, false))
{
cancelled = true;
}
}
// Try to get the replication port for server 1 only if it is required.
if (!cancelled
{
// Only ask if the replication domain will be configured (if not
// the replication server MUST be configured).
try
{
true, logger);
}
catch (ClientException ce)
{
cancelled = true;
}
}
if (!cancelled
{
while (replicationPort1 == -1)
{
if (tryWithDefault)
{
tryWithDefault = false;
}
else
{
println();
}
{
{
println();
println();
replicationPort1 = -1;
}
}
else if (replicationPort1 == port1)
{
// This is something that we must do in any case... this test is
// already included when we call SetupUtils.canUseAsPort
println();
println();
replicationPort1 = -1;
}
}
if (!secureReplication1)
{
try
{
false, logger);
}
catch (ClientException ce)
{
cancelled = true;
}
println();
}
}
if (!cancelled &&
{
// Only necessary to ask if the replication server will be configured
try
{
true, logger);
}
catch (ClientException ce)
{
cancelled = true;
}
}
// If the server contains an ADS. Try to load it and only load it: if
// there are issues with the ADS they will be encountered in the
// enableReplication(EnableReplicationUserData) method. Here we have
// to load the ADS to ask the user to accept the certificates and
// eventually admin authentication data.
if (!cancelled)
{
}
if (!cancelled)
{
{
}
}
}
if (mustPrintCommandBuilder())
{
}
/*
* Prompt for information on the second server.
*/
int port2 = -1;
boolean doNotDisplayFirstError = false;
if (!cancelled)
{
{
}
{
}
{
doNotDisplayFirstError = true;
{
}
}
/*
* Use a copy of the argument properties since the map might be cleared
* in initializeGlobalArguments.
*/
}
{
try
{
{
{
// If the explicit bind DN is not null, the password corresponds
// to that bind DN. We are in the case where the user provides
// bind DN on first server and admin UID globally.
}
}
boolean error = false;
{
port2 = -1;
println();
println();
error = true;
}
if (!error)
{
{
cancelled = true;
}
}
}
catch (ClientException ce)
{
if (!doNotDisplayFirstError)
{
println();
println();
}
else
{
// Reset only the credential parameters.
}
}
catch (ArgumentException ae)
{
println();
println();
cancelled = true;
}
finally
{
doNotDisplayFirstError = false;
}
}
if (!cancelled)
{
}
int replicationPort2 = -1;
boolean configureReplicationServer2 =
boolean configureReplicationDomain2 =
{
{
final LocalizableMessage prompt =
if (!askConfirmation(prompt, false))
{
cancelled = true;
}
}
// Try to get the replication port for server 2 only if it is required.
if (!cancelled
{
// Only ask if the replication domain will be configured (if not the
// replication server MUST be configured).
{
try
{
true, logger);
}
catch (ClientException ce)
{
cancelled = true;
}
}
if (!cancelled
{
while (replicationPort2 == -1)
{
if (tryWithDefault)
{
tryWithDefault = false;
}
else
{
println();
}
if (!argParser.skipReplicationPortCheck() &&
{
{
println();
println();
replicationPort2 = -1;
}
}
else if (replicationPort2 == port2)
{
// This is something that we must do in any case... this test is
// already included when we call SetupUtils.canUseAsPort
println();
replicationPort2 = -1;
}
&& replicationPort1 > 0
&& replicationPort1 == replicationPort2)
{
println();
println();
replicationPort2 = -1;
}
}
if (!secureReplication2)
{
try
{
}
catch (ClientException ce)
{
cancelled = true;
}
println();
}
}
}
if (!cancelled &&
{
// Only necessary to ask if the replication server will be configured
try
{
true, logger);
}
catch (ClientException ce)
{
cancelled = true;
}
}
// If the server contains an ADS. Try to load it and only load it: if
// there are issues with the ADS they will be encountered in the
// enableReplication(EnableReplicationUserData) method. Here we have
// to load the ADS to ask the user to accept the certificates.
if (!cancelled)
{
}
if (!cancelled)
{
}
}
// If the adminUid and adminPwd are not set in the EnableReplicationUserData
// object, that means that there are no administrators and that they
// must be created. The adminUId and adminPwd are updated inside
// loadADSAndAcceptCertificates.
boolean promptedForAdmin = false;
// There is a case where we haven't had need for the administrator
// credentials even if the administrators are defined: where all the servers
// can be accessed with another user (for instance if all the server have
// defined cn=directory manager and all the entries have the same password).
{
{
promptedForAdmin = true;
println();
}
}
{
}
{
int nPasswordPrompts = 0;
{
{
cancelled = true;
break;
}
nPasswordPrompts ++;
if (!promptedForAdmin)
{
println();
println();
}
{
println();
}
while (adminPwdConfirm == null)
{
try
{
}
catch (ClientException ex)
{
}
println();
}
{
println();
println();
}
}
}
if (!cancelled)
{
}
return !cancelled;
}
/**
* Updates the contents of the provided DisableReplicationUserData object
* with the information provided in the command-line. If some information
* is missing, ask the user to provide valid data.
* We assume that if this method is called we are in interactive mode.
* @param uData the object to be updated.
* @return <CODE>true</CODE> if the object was successfully updated and
* <CODE>false</CODE> if the user cancelled the operation.
* @throws ReplicationCliException if there is a critical error reading the
* ADS.
*/
throws ReplicationCliException
{
boolean cancelled = false;
// This is done because we want to ask explicitly for this
/*
* Try to connect to the server.
*/
{
try
{
ci.setUseAdminOrBindDn(true);
{
cancelled = true;
}
}
catch (ClientException ce)
{
println();
println();
}
catch (ArgumentException ae)
{
println();
println();
cancelled = true;
}
}
if (!cancelled)
{
}
{
// If the server contains an ADS, try to load it and only load it: if
// there are issues with the ADS they will be encountered in the
// disableReplication(DisableReplicationUserData) method. Here we have
// to load the ADS to ask the user to accept the certificates and
// eventually admin authentication data.
}
boolean disableReplicationServer =
if (disableAll ||
{
try
{
disableAll, logger);
}
catch (ClientException ce)
{
cancelled = true;
}
}
if (!disableAll
&& repPort > 0)
{
try
{
logger);
}
catch (ClientException ce)
{
cancelled = true;
}
}
{
disableReplicationServer = false;
final LocalizableMessage msg = INFO_REPLICATION_PROMPT_NO_REPLICATION_SERVER_TO_DISABLE.get(getHostPort(ctx));
try
{
}
catch (ClientException ce)
{
cancelled = true;
}
}
{
disableReplicationServer = true;
}
if (!cancelled && !disableAll)
{
{
try
{
INFO_REPLICATION_DISABLE_ALL_SUFFIXES_DISABLE_REPLICATION_SERVER.get(getHostPort(ctx), repPort), true,
logger));
}
catch (ClientException ce)
{
cancelled = true;
}
}
}
if (!cancelled)
{
// Ask for confirmation to disable if not already done.
boolean disableADS = false;
boolean disableSchema = false;
{
{
disableADS = true;
}
{
disableSchema = true;
}
}
if (disableADS)
{
println();
LocalizableMessage msg = INFO_REPLICATION_CONFIRM_DISABLE_ADS.get(ADSContext.getAdministrationSuffixDN());
println();
}
if (disableSchema)
{
println();
println();
}
if (!disableSchema && !disableADS)
{
println();
{
}
println();
}
}
return !cancelled;
}
/**
* Updates the contents of the provided InitializeAllReplicationUserData
* object with the information provided in the command-line. If some
* information is missing, ask the user to provide valid data.
* We assume that if this method is called we are in interactive mode.
* @param uData the object to be updated.
* @return <CODE>true</CODE> if the object was successfully updated and
* <CODE>false</CODE> if the user cancelled the operation.
*/
{
try
{
{
return false;
}
{
return false;
}
// Ask for confirmation to initialize.
println();
{
return false;
}
println();
return true;
}
finally
{
}
}
private LocalizableMessage getPrompt(InitializeAllReplicationUserData uData, InitialLdapContext ctx)
{
{
return INFO_REPLICATION_CONFIRM_INITIALIZE_ALL_ADS.get(ADSContext.getAdministrationSuffixDN(), hostPortSource);
}
}
{
try
{
}
catch (ClientException ce)
{
return false;
}
}
/**
* Updates the contents of the provided user data
* object with the information provided in the command-line.
* If some information is missing, ask the user to provide valid data.
* We assume that if this method is called we are in interactive mode.
* @param uData the object to be updated.
* @return <CODE>true</CODE> if the object was successfully updated and
* <CODE>false</CODE> if the user cancelled the operation.
*/
{
try
{
{
return false;
}
}
finally
{
}
}
{
// Try to connect to the server.
while (true)
{
try
{
if (uData instanceof InitializeAllReplicationUserData)
{
}
{
if (uData instanceof StatusReplicationUserData)
{
}
}
return ctx;
}
catch (ClientException ce)
{
println();
println();
}
catch (ArgumentException ae)
{
println();
println();
return null;
}
}
}
/**
* Updates the contents of the provided StatusReplicationUserData object
* with the information provided in the command-line. If some information
* is missing, ask the user to provide valid data.
* We assume that if this method is called we are in interactive mode.
* @param uData the object to be updated.
* @return <CODE>true</CODE> if the object was successfully updated and
* <CODE>false</CODE> if the user cancelled the operation.
* @throws ReplicationCliException if a critical error occurs reading the
* ADS.
*/
throws ReplicationCliException
{
try
{
{
return false;
}
// If the server contains an ADS, try to load it and only load it: if
// there are issues with the ADS they will be encountered in the
// statusReplication(StatusReplicationUserData) method. Here we have
// to load the ADS to ask the user to accept the certificates and
// eventually admin authentication data.
if (cancelled)
{
return false;
}
if (!cancelled)
{
}
return !cancelled;
}
finally
{
}
}
/**
* Updates the contents of the provided InitializeReplicationUserData object
* with the information provided in the command-line. If some information
* is missing, ask the user to provide valid data.
* We assume that if this method is called we are in interactive mode.
* @param uData the object to be updated.
* @return <CODE>true</CODE> if the object was successfully updated and
* <CODE>false</CODE> if the user cancelled the operation.
*/
{
boolean cancelled = false;
{
}
/*
* Use a copy of the argument properties since the map might be cleared
* in initializeGlobalArguments.
*/
/*
* Try to connect to the source server.
*/
{
try
{
{
cancelled = true;
}
}
catch (ClientException ce)
{
println();
println();
}
catch (ArgumentException ae)
{
println();
println();
cancelled = true;
}
}
if (!cancelled)
{
}
if (mustPrintCommandBuilder())
{
}
/* Prompt for destination server credentials */
/*
* Use a copy of the argument properties since the map might be cleared
* in initializeGlobalArguments.
*/
/*
* Try to connect to the destination server.
*/
{
try
{
boolean error = false;
&& portSource == portDestination)
{
portDestination = -1;
println();
println();
error = true;
}
if (!error)
{
if (ctxDestination == null)
{
cancelled = true;
}
}
}
catch (ClientException ce)
{
println();
println();
}
catch (ArgumentException ae)
{
println();
println();
cancelled = true;
}
}
if (!cancelled)
{
}
if (!cancelled)
{
}
if (!cancelled)
{
// Ask for confirmation to initialize.
println();
println();
}
return !cancelled;
}
private LocalizableMessage getPrompt(InitializeReplicationUserData uData, InitialLdapContext ctxSource,
{
{
return INFO_REPLICATION_CONFIRM_INITIALIZE_ADS.get(adminSuffixDN, hostPortDestination, hostPortSource);
}
}
{
{
{
return true;
}
}
return false;
}
/**
* Commodity method that simply checks if a provided value is null or not,
* if it is not <CODE>null</CODE> returns it and if it is <CODE>null</CODE>
* returns the provided default value.
* @param v the value to analyze.
* @param defaultValue the default value.
* @return if the provided value is not <CODE>null</CODE> returns it and if it
* is <CODE>null</CODE> returns the provided default value.
*/
{
return v != null ? v : defaultValue;
}
/**
* Commodity method that simply checks if a provided value is -1 or not,
* if it is not -1 returns it and if it is -1 returns the provided default
* value.
* @param v the value to analyze.
* @param defaultValue the default value.
* @return if the provided value is not -1 returns it and if it is -1 returns
* the provided default value.
*/
private int getValue(int v, int defaultValue)
{
return v != -1 ? v : defaultValue;
}
/**
* Returns the trust manager to be used by this application.
* @return the trust manager to be used by this application.
*/
private ApplicationTrustManager getTrustManager()
{
}
/**
* Initializes the contents of the provided enable replication user data
* object with what was provided in the command-line without prompting to the
* user.
* @param uData the enable replication user data object to be initialized.
*/
{
{
}
else
{
// Best-effort: try to use admin, if it does not work, use bind DN.
try
{
}
catch (Throwable t)
{
}
}
{
}
else
{
// Best-effort: try to use admin, if it does not work, use bind DN.
try
{
}
catch (Throwable t)
{
}
}
if (uData.configureReplicationServer1())
{
}
if (uData.configureReplicationServer2())
{
}
}
/**
* Initializes the contents of the provided initialize replication user data
* object with what was provided in the command-line without prompting to the
* user.
* @param uData the initialize replication user data object to be initialized.
*/
{
}
/**
* Initializes the contents of the provided disable replication user data
* object with what was provided in the command-line without prompting to the
* user.
* @param uData the disable replication user data object to be initialized.
*/
{
{
}
}
/**
* Initializes the contents of the provided user data object with what was
* provided in the command-line without prompting to the user.
* @param uData the user data object to be initialized.
*/
{
}
/**
* Initializes the contents of the provided status replication user data
* object with what was provided in the command-line without prompting to the
* user.
* @param uData the status replication user data object to be initialized.
*/
{
}
{
String adminUid = getValue(argParser.getAdministratorUID(), argParser.getDefaultAdministratorUID());
}
/**
* Tells whether the server to which the LdapContext is connected has a
* replication port or not.
* @param ctx the InitialLdapContext to be used.
* @return <CODE>true</CODE> if the replication port for the server could
* be found and <CODE>false</CODE> otherwise.
*/
{
}
/**
* Returns the replication port of server to which the LdapContext is
* connected and -1 if the replication port could not be found.
* @param ctx the InitialLdapContext to be used.
* @return the replication port of server to which the LdapContext is
* connected and -1 if the replication port could not be found.
*/
{
int replicationPort = -1;
try
{
if (sync.hasReplicationServer())
{
}
}
catch (Throwable t)
{
"Unexpected error retrieving the replication port: "+t, t));
}
return replicationPort;
}
/**
* Loads the ADS with the provided context. If there are certificates to
* be accepted we prompt them to the user. If there are errors loading the
* servers we display them to the user and we ask for confirmation. If the
* provided ctx is not using Global Administrator credentials, we prompt the
* user to provide them and update the provide ReplicationUserData
* accordingly.
* @param ctx the Ldap context to be used in an array: note the context
* may be modified with the new credentials provided by the user.
* @param uData the ReplicationUserData to be updated.
* @param isFirstOrSourceServer whether this is the first server in the
* enable replication subcommand or the source server in the initialize server
* subcommand.
* @throws ReplicationCliException if a critical error occurred.
* @return <CODE>true</CODE> if everything went fine and the user accepted
* all the certificates and confirmed everything. Returns <CODE>false</CODE>
* if the user did not accept a certificate or any of the confirmation
* messages.
*/
throws ReplicationCliException
{
boolean cancelled = false;
boolean triedWithUserProvidedAdmin = false;
if (getTrustManager() == null)
{
// This is required when the user did connect to the server using SSL or
// Start TLS. In this case LDAPConnectionConsoleInteraction.run does not
// initialize the keystore and the trust manager is null.
}
try
{
if (adsContext.hasAdminData())
{
boolean reloadTopology = true;
while (reloadTopology && !cancelled)
{
// We must recreate the cache because the trust manager in the
// LDAPConnectionConsoleInteraction object might have changed.
getTrustManager(), getConnectTimeout());
reloadTopology = false;
/* Analyze if we had any exception while loading servers. For the
* moment only throw the exception found if the user did not provide
* the Administrator DN and this caused a problem authenticating in
* one server or if there is a certificate problem.
*/
new HashSet<TopologyCacheException>();
{
if (e != null)
{
exceptions.add(e);
}
}
/* Check the exceptions and see if we throw them or not. */
boolean notGlobalAdministratorError = false;
for (TopologyCacheException e : exceptions)
{
{
break;
}
switch (e.getType())
{
case NOT_GLOBAL_ADMINISTRATOR:
notGlobalAdministratorError = true;
boolean connected = false;
boolean errorDisplayed = false;
while (!connected)
{
{
triedWithUserProvidedAdmin = true;
}
{
if (!errorDisplayed)
{
println();
errorDisplayed = true;
}
println();
println();
}
try
{
connected = true;
}
catch (Throwable t)
{
println();
println();
}
}
if (uData instanceof EnableReplicationUserData)
{
{
}
else
{
}
}
reloadTopology = true;
break;
if (isCertificateException(e.getCause()))
{
reloadTopology = true;
}
else
{
}
break;
default:
}
}
}
{
if (uData instanceof StatusReplicationUserData)
{
println();
}
else
{
}
}
}
}
catch (ADSContextException ace)
{
throw new ReplicationCliException(
}
catch (TopologyCacheException tce)
{
throw new ReplicationCliException(
}
return !cancelled;
}
/**
* Tells whether there is a Global Administrator defined in the server
* to which the InitialLdapContext is connected.
* @param ctx the InitialLdapContext.
* @return <CODE>true</CODE> if we could find an administrator and
* <CODE>false</CODE> otherwise.
*/
{
try
{
if (adsContext.hasAdminData())
{
return !administrators.isEmpty();
}
}
catch (Throwable t)
{
"Unexpected error retrieving the ADS data: "+t, t));
}
return false;
}
/**
* Tells whether there is a Global Administrator corresponding to the provided
* ReplicationUserData defined in the server to which the InitialLdapContext
* is connected.
* @param ctx the InitialLdapContext.
* @param uData the user data
* @return <CODE>true</CODE> if we could find an administrator and
* <CODE>false</CODE> otherwise.
*/
{
try
{
{
// If the administrator UID is null it means that we are just
// checking for the existence of an administrator
{
return true;
}
}
}
catch (Throwable t)
{
"Unexpected error retrieving the ADS data: "+t, t));
}
return false;
}
/**
* Helper type for the <CODE>getCommonSuffixes</CODE> method.
*/
private enum SuffixRelationType
{
}
/**
* Returns a Collection containing a list of suffixes that are defined in
* two servers at the same time (depending on the value of the argument
* replicated this list contains only the suffixes that are replicated
* between the servers or the list of suffixes that are not replicated
* between the servers).
* @param ctx1 the connection to the first server.
* @param ctx2 the connection to the second server.
* @param type whether to return a list with the suffixes that are
* replicated, fully replicated (replicas have exactly the same list of
* replication servers), not replicated or all the common suffixes.
* @return a Collection containing a list of suffixes that are replicated
* (or those that can be replicated) in two servers.
*/
{
try
{
filter.setSearchMonitoringInformation(false);
{
{
switch (type)
{
case NOT_REPLICATED:
{
}
break;
case FULLY_REPLICATED:
{
}
break;
case REPLICATED:
{
}
break;
case NOT_FULLY_REPLICATED:
{
}
break;
case ALL:
if (areDnsEqual)
{
}
break;
default:
}
}
}
}
catch (Throwable t)
{
"Unexpected error retrieving the server configuration: "+t, t));
}
return suffixes;
}
/**
* Tells whether the two provided replicas are fully replicated or not. The
* code in fact checks that both replicas have the same DN that they are
* replicated if both servers are replication servers and that both replicas
* make reference to the other replication server.
* @param rep1 the first replica.
* @param rep2 the second replica.
* @return <CODE>true</CODE> if we can assure that the two replicas are
* replicated using the replication server and replication port information
* and <CODE>false</CODE> otherwise.
*/
{
{
}
return false;
}
/**
* Tells whether the two provided replicas are replicated or not. The
* code in fact checks that both replicas have the same DN and that they
* have at least one common replication server referenced.
* @param rep1 the first replica.
* @param rep2 the second replica.
* @return <CODE>true</CODE> if we can assure that the two replicas are
* replicated and <CODE>false</CODE> otherwise.
*/
{
{
}
return false;
}
/**
* Returns a Collection containing a list of replicas in a server.
* @param ctx the connection to the server.
* @return a Collection containing a list of replicas in a server.
*/
{
new LinkedList<ReplicaDescriptor>();
filter.setSearchMonitoringInformation(false);
try
{
}
catch (Throwable t)
{
"Unexpected error retrieving the server configuration: "+t, t));
}
return suffixes;
}
/**
* Enables the replication between two servers using the parameters in the
* provided EnableReplicationUserData. This method does not prompt to the
* user for information if something is missing.
* @param uData the EnableReplicationUserData object.
* @return ReplicationCliReturnCode.SUCCESSFUL if the operation was
* successful and the replication could be enabled and an error code
* otherwise.
*/
{
try
{
println();
{
}
if (errorMessages.isEmpty())
{
// This done is for the message informing that we are connecting.
println();
// If we are not in interactive mode do some checks...
if (!argParser.isInteractive())
{
{
}
{
}
if (!hasReplicationPort1
&& isLocalHost(host1)
{
}
if (!hasReplicationPort2
&& isLocalHost(host2)
{
}
{
}
{
// This is something that we must do in any case... this test is
// already included when we call SetupUtils.canUseAsPort
{
}
{
}
}
}
{
}
}
if (errorMessages.isEmpty())
{
{
if (mustPrintCommandBuilder())
{
}
if (!isInteractive())
{
{
println();
}
{
println();
}
}
try
{
}
catch (ReplicationCliException rce)
{
println();
}
}
else
{
// The error messages are already displayed in the method
// checkSuffixesForEnableReplication.
}
}
{
println();
}
if (returnValue == SUCCESSFUL)
{
if (time1 != -1
&& time2 != -1
{
}
println();
println(INFO_REPLICATION_POST_ENABLE_INFO.get("dsreplication", INITIALIZE_REPLICATION_SUBCMD_NAME));
println();
}
return returnValue;
}
finally
{
}
}
private InitialLdapContext createAdministrativeContext(EnableReplicationUserData uData, boolean isFirstSetOfValues,
{
try
{
return createAdministrativeContext(
getConnectTimeout(), getTrustManager());
}
catch (NamingException ne)
{
return null;
}
}
/**
* Disables the replication in the server for the provided suffixes using the
* data in the DisableReplicationUserData object. This method does not prompt
* to the user for information if something is missing.
* @param uData the DisableReplicationUserData object.
* @return ReplicationCliReturnCode.SUCCESSFUL if the operation was
* successful and an error code otherwise.
*/
{
{
return ERROR_CONNECTING;
}
try
{
// This done is for the message informing that we are connecting.
println();
uData.disableAll())
{
if (!isInteractive())
{
{
uData.setDisableReplicationServer(true);
}
!uData.disableAll())
{
uData.setDisableReplicationServer(false);
println();
}
}
if (mustPrintCommandBuilder())
{
}
{
// Inform the user that the replication server will not be disabled.
// Inform also of the user of the disableReplicationServerArg
}
try
{
return SUCCESSFUL;
}
catch (ReplicationCliException rce)
{
println();
return rce.getErrorCode();
}
}
else
{
}
}
finally
{
}
}
/**
* Displays the replication status of the baseDNs specified in the
* StatusReplicationUserData object. This method does not prompt
* to the user for information if something is missing.
* @param uData the StatusReplicationUserData object.
* @return ReplicationCliReturnCode.SUCCESSFUL if the operation was
* successful and an error code otherwise.
*/
{
{
return ERROR_CONNECTING;
}
try
{
try
{
return SUCCESSFUL;
}
catch (ReplicationCliException rce)
{
println();
return rce.getErrorCode();
}
}
finally
{
}
}
/**
* Initializes the contents of one server with the contents of the other
* using the parameters in the provided InitializeReplicationUserData.
* This method does not prompt to the user for information if something is
* missing.
* @param uData the InitializeReplicationUserData object.
* @return ReplicationCliReturnCode.SUCCESSFUL if the operation was
* successful and an error code otherwise.
*/
{
try
{
{
return ERROR_CONNECTING;
}
{
}
if (mustPrintCommandBuilder())
{
}
{
try
{
println();
print(formatter.getFormattedProgress(INFO_PROGRESS_INITIALIZING_SUFFIX.get(baseDN, getHostPort(ctxSource))));
println();
}
catch (ReplicationCliException rce)
{
println();
}
}
return returnValue;
}
finally
{
}
}
private InitialLdapContext createAdministrativeContext(InitializeReplicationUserData uData, boolean isSource)
{
try
{
return createAdministrativeContext(
getConnectTimeout(), getTrustManager());
}
catch (NamingException ne)
{
println();
return null;
}
}
/**
* Initializes the contents of a whole topology with the contents of the other
* using the parameters in the provided InitializeAllReplicationUserData.
* This method does not prompt to the user for information if something is
* missing.
* @param uData the InitializeAllReplicationUserData object.
* @return ReplicationCliReturnCode.SUCCESSFUL if the operation was
* successful and an error code otherwise.
*/
{
{
return ERROR_CONNECTING;
}
try
{
{
}
if (mustPrintCommandBuilder())
{
}
{
try
{
println();
print(formatter.getFormattedProgress(INFO_PROGRESS_INITIALIZING_SUFFIX.get(baseDN, getHostPort(ctx))));
println();
}
catch (ReplicationCliException rce)
{
println();
}
}
return returnValue;
}
finally
{
}
}
/**
* Performs the operation that must be made before initializing the topology
* using the import-ldif command or the binary copy. The operation uses
* the parameters in the provided InitializeAllReplicationUserData.
* This method does not prompt to the user for information if something is
* missing.
* @param uData the PreExternalInitializationUserData object.
* @return ReplicationCliReturnCode.SUCCESSFUL if the operation was
* successful and an error code otherwise.
*/
{
{
return ERROR_CONNECTING;
}
try
{
{
}
if (mustPrintCommandBuilder())
{
}
{
try
{
println();
println();
}
catch (ReplicationCliException rce)
{
println();
}
}
println();
print(INFO_PROGRESS_PRE_INITIALIZATION_FINISHED_PROCEDURE.get(POST_EXTERNAL_INITIALIZATION_SUBCMD_NAME));
println();
return returnValue;
}
finally
{
}
}
/**
* Performs the operation that must be made after initializing the topology
* using the import-ldif command or the binary copy. The operation uses
* the parameters in the provided InitializeAllReplicationUserData.
* This method does not prompt to the user for information if something is
* missing.
* @param uData the PostExternalInitializationUserData object.
* @return ReplicationCliReturnCode.SUCCESSFUL if the operation was
* successful and an error code otherwise.
*/
{
{
return ERROR_CONNECTING;
}
try
{
{
}
if (mustPrintCommandBuilder())
{
}
{
try
{
println();
println();
}
catch (ReplicationCliException rce)
{
println();
}
}
println();
println();
return returnValue;
}
finally
{
}
}
/**
* Checks that replication can actually be enabled in the provided baseDNs
* for the two servers.
* @param suffixes the suffixes provided by the user. This Collection is
* updated by removing the base DNs that cannot be enabled and with the
* base DNs that the user provided interactively.
* @param ctx1 connection to the first server.
* @param ctx2 connection to the second server.
* @param interactive whether to ask the user to provide interactively
* base DNs if none of the provided base DNs can be enabled.
* @param uData the user data. This object will not be updated by this method
* but it is assumed that it contains information about whether the
* replication domains must be configured or not.
*/
{
if (uData.configureReplicationDomain1() &&
{
}
else if (uData.configureReplicationDomain1())
{
}
else if (uData.configureReplicationDomain2())
{
}
else
{
}
if (availableSuffixes.isEmpty())
{
println();
if (!uData.configureReplicationDomain1() &&
{
// Use a clarifying message: there is no replicated base DN.
}
else
{
}
{
{
{
}
}
}
{
println();
}
}
else
{
// Verify that the provided suffixes are configured in the servers.
{
{
{
}
else
{
}
}
}
{
println();
}
{
println();
}
if (interactive)
{
}
}
}
/**
* Checks that replication can actually be disabled in the provided baseDNs
* for the server.
* @param suffixes the suffixes provided by the user. This Collection is
* updated by removing the base DNs that cannot be disabled and with the
* base DNs that the user provided interactively.
* @param ctx connection to the server.
* @param interactive whether to ask the user to provide interactively
* base DNs if none of the provided base DNs can be disabled.
* @param displayErrors whether to display errors or not.
* @param areSuffixRequired whether the user must provide base DNs or not
* (if it is <CODE>false</CODE> the user will be proposed the suffixes
* only once).
*/
boolean areSuffixRequired)
{
{
if (rep.isReplicated())
{
}
else
{
}
}
if (availableSuffixes.isEmpty())
{
if (displayErrors)
{
println();
}
{
{
{
}
}
}
{
println();
}
}
else
{
// Verify that the provided suffixes are configured in the servers.
{
{
{
}
else
{
}
}
}
{
println();
}
{
println();
}
if (interactive)
{
{
{
// In interactive mode we do not propose to manage the
// administration suffix.
if (displayErrors)
{
println();
}
break;
}
if (areSuffixRequired)
{
println();
}
boolean confirmationLimitReached =
{
break;
}
if (!areSuffixRequired)
{
break;
}
}
}
}
}
{
{
if (!isSchemaOrAdminSuffix(dn))
{
try
{
{
}
}
catch (ClientException ce)
{
return true;
}
}
}
return false;
}
/**
* Checks that replication can actually be initialized in the provided baseDNs
* for the server.
* @param suffixes the suffixes provided by the user. This Collection is
* updated by removing the base DNs that cannot be initialized and with the
* base DNs that the user provided interactively.
* @param ctx connection to the server.
* @param interactive whether to ask the user to provide interactively
* base DNs if none of the provided base DNs can be initialized.
*/
private void checkSuffixesForInitializeReplication(
{
{
if (rep.isReplicated())
{
}
else
{
}
}
if (availableSuffixes.isEmpty())
{
println();
{
}
else
{
}
{
{
{
}
}
}
{
println();
}
}
else
{
// Verify that the provided suffixes are configured in the servers.
{
{
{
}
else
{
}
}
}
{
println();
}
{
println();
}
if (interactive)
{
boolean confirmationLimitReached = false;
{
println();
{
// In interactive mode we do not propose to manage the administration suffix.
{
}
else
{
}
break;
}
else
{
{
}
else if (argParser.isPreExternalInitializationSubcommand())
{
}
else if (argParser.isPostExternalInitializationSubcommand())
{
}
{
if (!isSchemaOrAdminSuffix(dn))
{
boolean addSuffix;
try
{
{
}
else if (argParser.isPostExternalInitializationSubcommand())
{
}
else
{
true, logger);
}
}
catch (ClientException ce)
{
confirmationLimitReached = true;
break;
}
if (addSuffix)
{
}
}
}
}
{
break;
}
}
}
}
}
{
}
/**
* Checks that we can initialize the provided baseDNs between the two servers.
* @param suffixes the suffixes provided by the user. This Collection is
* updated by removing the base DNs that cannot be enabled and with the
* base DNs that the user provided interactively.
* @param ctxSource connection to the source server.
* @param ctxDestination connection to the destination server.
* @param interactive whether to ask the user to provide interactively
* base DNs if none of the provided base DNs can be initialized.
*/
private void checkSuffixesForInitializeReplication(
{
if (availableSuffixes.isEmpty())
{
println();
}
else
{
// Verify that the provided suffixes are configured in the servers.
{
{
}
}
{
println();
}
if (interactive)
{
}
}
}
/**
* Updates the configuration in the two servers (and in other servers if
* they are referenced) to enable replication.
* @param ctx1 the connection to the first server.
* @param ctx2 the connection to the second server.
* @param uData the EnableReplicationUserData object containing the required
* parameters to update the configuration.
* @throws ReplicationCliException if there is an error.
*/
throws ReplicationCliException
{
filter.setSearchMonitoringInformation(false);
if (!argParser.isInteractive())
{
// Inform the user of the potential errors that we found in the already
// registered servers.
try
{
{
}
{
}
}
catch (TopologyCacheException tce)
{
throw new ReplicationCliException(
}
catch (ADSContextException adce)
{
throw new ReplicationCliException(
}
{
}
}
// Check whether there is more than one replication server in the
// topology.
{
}
else if (!baseDNsWithOneReplicationServer.isEmpty())
{
if (isInteractive())
{
try
{
if (!confirmAction(confirmMsg, false))
{
throw new ReplicationCliException(
}
}
catch (Throwable t)
{
throw new ReplicationCliException(
}
}
else
{
println();
}
}
// These are used to identify which server we use to initialize
// the contents of the other server (if any).
boolean adsAlreadyReplicated = false;
boolean adsMergeDone = false;
try
{
{
{
{
}
{
}
}
{
{
}
{
}
}
{
println();
adsMergeDone = true;
}
else
{
// They are already replicated: nothing to do in terms of ADS
// initialization or ADS update data
adsAlreadyReplicated = isBaseDNReplicated(server1, server2, ADSContext.getAdministrationSuffixDN());
if (!adsAlreadyReplicated)
{
// Try to merge if both are replicated
if (isADS1Replicated && isADS2Replicated)
{
// Merge
println();
adsMergeDone = true;
}
else if (isADS1Replicated || !isADS2Replicated)
{
// The case where only the first ADS is replicated or none
// is replicated.
{
}
{
}
}
else if (isADS2Replicated)
{
{
}
{
}
}
}
}
}
{
{
}
{
}
}
{
{
}
{
}
}
else
{
{
// This could occur if the user created an administrator without
// registering any server.
}
}
}
catch (ADSContextException adce)
{
throw new ReplicationCliException(
}
if (!adsAlreadyReplicated && !adsMergeDone)
{
try
{
}
catch (Throwable t)
{
throw new ReplicationCliException(
}
}
if (!adsMergeDone)
{
println();
}
if (!adsAlreadyReplicated
{
}
if (uData.replicateSchema())
{
}
try
{
{
}
{
}
}
catch (ADSContextException adce)
{
throw new ReplicationCliException(
}
catch (TopologyCacheException tce)
{
throw new ReplicationCliException(
}
if (server1.isReplicationServer())
{
}
else if (uData.configureReplicationServer1())
{
}
if (server2.isReplicationServer())
{
}
else if (uData.configureReplicationServer2())
{
}
{
{
{
}
}
{
{
}
}
}
{
allRepServers.addAll(v);
}
{
try
{
}
catch (OpenDsException ode)
{
throw new ReplicationCliException(
}
}
else if (server1.isReplicationServer())
{
try
{
}
catch (OpenDsException ode)
{
throw new ReplicationCliException(
}
{
// Inform the user that the provided value will be ignored
"Ignoring provided replication port for "
+ "first server (already configured with port "
}
}
{
try
{
}
catch (OpenDsException ode)
{
throw new ReplicationCliException(
}
}
else if (server2.isReplicationServer())
{
try
{
}
catch (OpenDsException ode)
{
throw new ReplicationCliException(
}
{
"Ignoring provided replication port for "
+ "second server (already configured with port "
}
}
{
{
try
{
}
catch (OpenDsException ode)
{
throw new ReplicationCliException(msg,
}
}
{
try
{
}
catch (OpenDsException ode)
{
throw new ReplicationCliException(msg,
}
}
{
}
{
}
}
// Now that replication is configured in all servers, simply try to
// initialize the contents of one ADS with the other (in the case where
// already both servers were replicating the same ADS there is nothing to be
// done).
if (adsMergeDone)
{
pointAdder.start();
try
{
}
finally
{
pointAdder.stop();
}
println();
}
{
println();
}
// If we must initialize the schema do so.
{
{
}
else
{
}
if (adsMergeDone)
{
pointAdder.start();
try
{
}
finally
{
pointAdder.stop();
}
}
else
{
}
println();
}
}
private TopologyCache createTopologyCache(ADSContext adsCtx, Set<PreferredConnection> cnx, ReplicationUserData uData)
{
if (adsCtx.hasAdminData())
{
return cache;
}
return null;
}
throws ReplicationCliException
{
try
{
}
catch (NamingException ne)
{
throw new ReplicationCliException(
}
}
/**
* Updates the configuration in the server (and in other servers if
* they are referenced) to disable replication.
* @param ctx the connection to the server.
* @param uData the DisableReplicationUserData object containing the required
* parameters to update the configuration.
* @throws ReplicationCliException if there is an error.
*/
{
filter.setSearchMonitoringInformation(false);
if (!uData.disableAll())
{
}
// Only try to update remote server if the user provided a Global
// Administrator to authenticate.
try
{
{
if (!uData.disableAll())
{
}
}
}
catch (ADSContextException adce)
{
throw new ReplicationCliException(
}
catch (TopologyCacheException tce)
{
throw new ReplicationCliException(
}
if (!argParser.isInteractive())
{
// Inform the user of the potential errors that we found.
{
}
{
}
}
{
// Figure out if this is the last replication server for a given
// topology (containing a different replica) or there will be only
// another replication server left (single point of failure).
{
{
// Do not display these suffixes.
continue;
}
{
{
}
else
{
}
}
}
// Inform the user
{
{
{
// Do not display these suffixes.
}
}
{
if (!isInteractive())
{
}
else
{
if (!askConfirmation(msg, false))
{
}
}
}
}
{
// Check that there are other replicas and that this message, really
// makes sense to be displayed.
{
boolean baseDNSpecified = false;
{
if (!isSchemaOrAdminSuffix(baseDN)
{
baseDNSpecified = true;
break;
}
}
if (!baseDNSpecified)
{
{
}
}
{
// If there is just one replica, it is the one in this server.
{
{
}
}
{
}
}
}
{
if (!isInteractive())
{
}
else
{
if (!askConfirmation(msg, false))
{
}
}
}
}
}
/**
* Try to figure out if we must explicitly disable replication on
* cn=admin data and cn=schema.
*/
boolean forceDisableSchema = false;
boolean forceDisableADS = false;
boolean schemaReplicated = false;
boolean adsReplicated = false;
{
if (rep.isReplicated())
{
{
adsReplicated = true;
}
{
schemaReplicated = true;
}
}
}
if (disableAllBaseDns &&
{
// Unregister the server from the ADS if no other server has dependencies
// with it (no replicated base DNs and no replication server).
try
{
// To be sure that the change gets propagated
sleepCatchInterrupt(2000);
}
catch (ADSContextException adce)
{
{
throw new ReplicationCliException(
}
}
}
if (uData.disableAll())
{
{
if (replica.isReplicated())
{
}
}
}
else
{
if (disableAllBaseDns &&
{
}
{
{
// The user already asked this to be explicitly disabled
forceDisableADS = false;
}
{
// The user already asked this to be explicitly disabled
forceDisableSchema = false;
}
}
if (forceDisableSchema)
{
}
if (forceDisableADS)
{
}
}
{
try
{
}
catch (OpenDsException ode)
{
throw new ReplicationCliException(msg,
}
}
boolean replicationServerDisabled = false;
{
new LinkedHashSet<ServerDescriptor>();
{
{
{
}
}
}
{
// Find references in all servers.
{
{
{
}
}
}
}
for (ServerDescriptor s : serversToUpdate)
{
}
{
// Disable replication server
replicationServerDisabled = true;
// Wait to be sure that changes are taken into account and reset the
// contents of the ADS.
sleepCatchInterrupt(5000);
}
}
{
// This can happen if we could not retrieve the TopologyCache
replicationServerDisabled = true;
}
if (uData.disableAll())
{
try
{
// Delete all contents from ADSContext.
println();
}
catch (ADSContextException adce)
{
throw new ReplicationCliException(
}
}
else if (disableAllBaseDns &&
{
// Unregister the servers from the ADS of the local server.
try
{
{
}
// To be sure that the change gets propagated
sleepCatchInterrupt(2000);
}
catch (ADSContextException adce)
{
// This is not critical, do not send an error
}
}
}
{
{
}
}
/**
* Displays the replication status of the different base DNs in the servers
* registered in the ADS.
* @param ctx the connection to the server.
* @param uData the StatusReplicationUserData object containing the required
* parameters to update the configuration.
* @throws ReplicationCliException if there is an error.
*/
{
boolean somethingDisplayed = false;
try
{
}
catch (TopologyCacheException tce)
{
throw new ReplicationCliException(
}
if (mustPrintCommandBuilder())
{
}
if (!argParser.isInteractive())
{
// Inform the user of the potential errors that we found.
{
}
}
boolean oneReplicated = false;
{
// If no base DNs where specified display all the base DNs but the schema
// and cn=admin data.
if (found)
{
boolean replicated = false;
{
if (replica.isReplicated())
{
replicated = true;
break;
}
}
if (replicated)
{
oneReplicated = true;
}
else
{
// Check if there are already some non replicated base DNs.
found = false;
{
if (!replica.isReplicated() &&
{
found = true;
break;
}
}
if (!found)
{
}
}
}
}
if (!oneReplicated && displayAll)
{
// Maybe there are some replication server configured...
{
if (server.isReplicationServer())
{
}
}
{
somethingDisplayed = true;
}
}
if (!replicaLists.isEmpty())
{
{
boolean inserted = false;
{
{
inserted = true;
}
}
if (!inserted)
{
}
}
new HashSet<ReplicaDescriptor>();
new HashSet<ServerDescriptor>();
cache.getServers(),
somethingDisplayed = true;
{
println();
if (!replicasWithNoReplicationServer.isEmpty() ||
{
println();
println();
}
println();
somethingDisplayed = true;
}
}
if (!somethingDisplayed)
{
if (displayAll)
{
println();
}
else
{
println();
}
}
}
/**
* Displays the replication status of the replicas provided. The code assumes
* that all the replicas have the same baseDN and that if they are replicated
* all the replicas are replicated with each other.
* Note: the code assumes that all the objects come from the same read of the
* topology cache. So comparisons in terms of pointers can be made.
* @param orderedReplicaLists the list of replicas that we are trying to
* display.
* @param scriptFriendly whether to display it on script-friendly mode or not.
* @param cnx the preferred connections used to connect to the server.
* @param servers all the servers configured in the topology.
* @param replicasWithNoReplicationServer the set of replicas that will be
* updated with all the replicas that have no replication server.
* @param serversWithNoReplica the set of servers that will be updated with
* all the servers that act as replication server in the topology but have
* no replica.
*/
private void displayStatus(
{
new LinkedHashSet<ReplicaDescriptor>();
{
{
}
{
{
{
}
}
}
{
{
}
}
}
/*
* The table has the following columns:
* - suffix DN;
* - server;
* - number of entries;
* - replication enabled indicator;
* - directory server instance ID;
* - replication server;
* - replication server ID;
* - missing changes;
* - age of the oldest change, and
* - security enabled indicator.
*/
/*
* Table headings.
*/
/*
* Table data.
*/
{
// Suffix DN
// Server port
// Number of entries
if (nEntries >= 0)
{
}
else
{
}
if (!replica.isReplicated())
{
}
else
{
// Replication enabled
// DS instance ID
// RS ID and port.
{
.getReplicationServerId()));
.getServer().getReplicationServerPort())));
}
else
{
if (scriptFriendly)
{
}
else
{
}
}
// Missing changes
if (missingChanges >= 0)
{
}
else
{
}
// Age of oldest missing change
if (ageOfOldestMissingChange > 0)
{
}
else
{
}
// Secure
{
}
else
{
}
}
}
{
// Suffix DN
// Server port
// Number of entries
if (scriptFriendly)
{
}
else
{
}
// Replication enabled
// DS ID
// RS ID
// Replication port
if (replicationPort >= 0)
{
}
else
{
}
// Missing changes
// Age of oldest change
// Secure
}
if (scriptFriendly)
{
}
else
{
}
}
{
boolean isDomain = false;
boolean isRepServer = false;
{
if (!isRepServer)
{
}
{
isDomain = true;
}
if (isDomain && isRepServer)
{
break;
}
}
return !isDomain && isRepServer;
}
/**
* Displays the replication status of the replication servers provided. The
* code assumes that all the servers have a replication server and that there
* are associated with no replication domain.
* @param servers the servers
* @param cnx the preferred connections used to connect to the server.
* @param scriptFriendly wheter to display it on script-friendly mode or not.
*/
{
{
// Server port
// Replication port
if (replicationPort >= 0)
{
}
else
{
}
// Secure
}
if (scriptFriendly)
{
println();
}
else
{
println();
for (int i=0; i<length; i++)
{
}
println();
}
}
/**
* Retrieves all the replication servers for a given baseDN. The
* ServerDescriptor is used to identify the server where the suffix is
* defined and it cannot be null. The TopologyCache is used to retrieve
* replication servers defined in other replicas but not in the one we
* get in the ServerDescriptor.
* @param baseDN the base DN.
* @param cache the TopologyCache (might be null).
* @param server the ServerDescriptor.
* @return a Set containing the replication servers currently being used
* to replicate the baseDN defined in the server described by the
* ServerDescriptor.
*/
{
{
{
{
// Test that at least we share one of the replication servers.
// If we do: we are dealing with the same replication topology
// (we must consider the case of disjoint replication topologies
// replicating the same base DN).
{
break;
}
else if (server.isReplicationServer()
{
// this server is acting as replication server with no domain.
break;
}
}
}
}
return servers;
}
{
{
if (s.equalsIgnoreCase(toFind))
{
return true;
}
}
return false;
}
{
{
if (toFind.equalsIgnoreCase(s))
{
return s;
}
}
return null;
}
/**
* Retrieves the suffix in the TopologyCache for a given baseDN. The
* ServerDescriptor is used to identify the server where the suffix is
* defined.
* @param baseDN the base DN.
* @param cache the TopologyCache.
* @param server the ServerDescriptor.
* @return the suffix in the TopologyCache for a given baseDN.
*/
{
if (server.isReplicationServer())
{
}
{
{
// Test that at least we share one of the replication servers.
// If we do: we are dealing with the same replication topology
// (we must consider the case of disjoint replication topologies
// replicating the same base DN).
{
return suffix;
}
{
}
}
}
return returnValue;
}
{
{
{
break;
}
}
return servers;
}
/**
* Retrieves all the replication domain IDs for a given baseDN in the
* ServerDescriptor.
* @param baseDN the base DN.
* @param server the ServerDescriptor.
* @return a Set containing the replication domain IDs for a given baseDN in
* the ServerDescriptor.
*/
{
{
if (replica.isReplicated()
{
break;
}
}
return ids;
}
/**
* Configures the server to which the provided InitialLdapContext is connected
* as a replication server. The replication server listens in the provided
* port.
* @param ctx the context connected to the server that we want to configure.
* @param replicationPort the replication port of the replication server.
* @param useSecureReplication whether to have encrypted communication with
* the replication port or not.
* @param replicationServers the list of replication servers to which the
* replication server will communicate with.
* @param usedReplicationServerIds the set of replication server IDs that
* are already in use. The set will be updated with the replication ID
* that will be used by the newly configured replication server.
* @throws OpenDsException if there is an error updating the configuration.
*/
int replicationPort, boolean useSecureReplication,
{
/*
* Configure Synchronization plugin.
*/
try
{
}
catch (ManagedObjectNotFoundException monfe)
{
logger.info(LocalizableMessage.raw("Synchronization server does not exist in " + getHostPort(ctx)));
}
{
"Multimaster Synchronization",
new ArrayList<PropertyException>());
getName());
}
{
}
/*
* Configure the replication server.
*/
boolean mustCommit = false;
if (!sync.hasReplicationServer())
{
{
}
new ArrayList<PropertyException>());
mustCommit = true;
}
else
{
{
mustCommit = true;
}
{
mustCommit = true;
}
}
if (mustCommit)
{
}
println();
}
/**
* Updates the configuration of the replication server with the list of
* replication servers provided.
* @param ctx the context connected to the server that we want to update.
* @param replicationServers the list of replication servers to which the
* replication server will communicate with.
* @throws OpenDsException if there is an error updating the configuration.
*/
{
boolean mustCommit = false;
{
mustCommit = true;
}
{
mustCommit = true;
}
if (mustCommit)
{
}
println();
}
/**
* Returns a Set containing all the replication server ids found in the
* servers of a given TopologyCache object.
* @param cache the TopologyCache object to use.
* @return a Set containing all the replication server ids found in a given
* TopologyCache object.
*/
{
{
if (server.isReplicationServer())
{
}
}
return ids;
}
/**
* Configures a replication domain for a given base DN in the server to which
* the provided InitialLdapContext is connected.
* @param ctx the context connected to the server that we want to configure.
* @param baseDN the base DN of the replication domain to configure.
* @param replicationServers the list of replication servers to which the
* replication domain will communicate with.
* @param usedReplicationDomainIds the set of replication domain IDs that
* are already in use. The set will be updated with the replication ID
* that will be used by the newly configured replication server.
* @throws OpenDsException if there is an error updating the configuration.
*/
{
boolean userSpecifiedAdminBaseDN = false;
if (l != null)
{
}
{
}
else
{
}
if (domainNames == null)
{
domainNames = new String[]{};
}
{
}
{
{
break;
}
}
boolean mustCommit = false;
{
new ArrayList<PropertyException>());
mustCommit = true;
}
else
{
{
mustCommit = true;
}
{
servers));
mustCommit = true;
}
}
if (mustCommit)
{
}
println();
}
/**
* Configures the baseDN to replicate in all the Replicas found in a Topology
* Cache that are replicated with the Replica of the same base DN in the
* provided ServerDescriptor object.
* @param baseDN the base DN to replicate.
* @param repServers the replication servers to be defined in the domain.
* @param usedIds the replication domain Ids already used. This Set is
* updated with the new domains that are used.
* @param cache the TopologyCache used to retrieve the different defined
* replicas.
* @param server the ServerDescriptor that is used to identify the
* replication topology that we are interested at (we only update the replicas
* that are already replicated with this server).
* @param alreadyConfiguredServers the list of already configured servers. If
* a server is in this list no updates are performed to the domain.
* @param alreadyConfiguredReplicationServers the list of already configured
* servers. If a server is in this list no updates are performed to the
* replication server.
* @throws ReplicationCliException if something goes wrong.
*/
throws ReplicationCliException
{
"' the replication servers are "+repServers));
new HashSet<ServerDescriptor>();
new HashSet<ServerDescriptor>();
{
{
{
}
}
}
// Now check the replication servers.
{
if (s.isReplicationServer()
// Check if it is part of the replication topology
{
}
}
for (ServerDescriptor s : allServers)
{
try
{
if (serversToConfigureDomain.contains(s))
{
}
{
}
}
catch (NamingException ne)
{
}
catch (OpenDsException ode)
{
throw new ReplicationCliException(msg,
}
finally
{
}
}
}
/**
* Returns the Map of properties to be used to update the ADS.
* This map uses the data provided by the user.
* @return the Map of properties to be used to update the ADS.
* This map uses the data provided by the user
*/
{
uData.getAdminUid());
uData.getAdminPwd());
return adminProperties;
}
throws ReplicationCliException
{
int replicationId = -1;
try
{
filter.setSearchMonitoringInformation(false);
{
{
break;
}
}
}
catch (NamingException ne)
{
}
if (replicationId == -1)
{
throw new ReplicationCliException(
}
{
{
if (newLogDetails != null
{
println();
}
}
});
int nTries = 5;
boolean initDone = false;
while (!initDone)
{
try
{
initDone = true;
}
catch (PeerNotFoundException pnfe)
{
if (nTries == 1)
{
throw new ReplicationCliException(
}
}
catch (ApplicationException ae)
{
}
nTries--;
}
}
/**
* Initializes all the replicas in the topology with the contents of a
* given replica.
* @param ctx the connection to the server where the source replica of the
* initialization is.
* @param baseDN the dn of the suffix.
* @param displayProgress whether we want to display progress or not.
* @throws ReplicationCliException if an unexpected error occurs.
*/
boolean displayProgress) throws ReplicationCliException
{
{
try
{
}
catch (ArgumentException ae)
{
}
}
int nTries = 5;
boolean initDone = false;
while (!initDone)
{
try
{
initDone = true;
}
catch (PeerNotFoundException pnfe)
{
if (nTries == 1)
{
throw new ReplicationCliException(
}
}
catch (ClientException ae)
{
}
nTries--;
}
}
/**
* Launches the pre external initialization operation using the provided
* connection on a given base DN.
* @param baseDN the base DN that we want to reset.
* @param ctx the connection to the server.
* @throws ReplicationCliException if there is an error performing the
* operation.
*/
private void preExternalInitialization(String baseDN, InitialLdapContext ctx) throws ReplicationCliException
{
}
/**
* Launches the post external initialization operation using the provided
* connection on a given base DN required for replication to work.
* @param baseDN the base DN that we want to reset.
* @param ctx the connection to the server.
* @throws ReplicationCliException if there is an error performing the
* operation.
*/
private void postExternalInitialization(String baseDN, InitialLdapContext ctx) throws ReplicationCliException
{
}
/**
* Launches the pre or post external initialization operation using the
* provided connection on a given base DN.
* @param baseDN the base DN that we want to reset.
* @param ctx the connection to the server.
* @param isPre whether this is the pre operation or the post operation.
* @throws ReplicationCliException if there is an error performing the
* operation.
*/
{
boolean taskCreated = false;
int i = 1;
boolean isOver = false;
"org.opends.server.tasks.SetGenerationIdTask");
if (isPre)
{
}
while (!taskCreated)
{
try
{
taskCreated = true;
}
catch (NameAlreadyBoundException x)
{
}
catch (NamingException ne)
{
throw new ReplicationCliException(
}
i++;
}
// Wait until it is over
new String[] {
"ds-task-log-message",
"ds-task-state"
});
while (!isOver)
{
sleepCatchInterrupt(500);
try
{
try
{
{
}
}
finally
{
}
{
lastLogMsg = logMsg;
}
{
isOver = true;
{
}
{
}
}
}
catch (NameNotFoundException x)
{
isOver = true;
}
catch (NamingException ne)
{
throw new ReplicationCliException(
}
}
}
private LocalizableMessage getPrePostErrorMsg(boolean isPre, String lastLogMsg, String state, InitialLdapContext ctx)
{
if (lastLogMsg == null)
{
return isPre
}
return isPre
}
private void sleepCatchInterrupt(long millis)
{
try
{
}
catch (InterruptedException e)
{
}
}
/**
* Initializes all the replicas in the topology with the contents of a
* given replica. This method will try to create the task only once.
* @param ctx the connection to the server where the source replica of the
* initialization is.
* @param baseDN the dn of the suffix.
* @param displayProgress whether we want to display progress or not.
* @throws ClientException if an unexpected error occurs.
* @throws PeerNotFoundException if the replication mechanism cannot find
* a peer.
*/
boolean displayProgress)
throws ClientException, PeerNotFoundException
{
boolean taskCreated = false;
int i = 1;
boolean isOver = false;
"org.opends.server.tasks.InitializeTargetTask");
while (!taskCreated)
{
try
{
taskCreated = true;
}
catch (NameAlreadyBoundException x)
{
}
catch (NamingException ne)
{
throw new ClientException(
}
i++;
}
// Wait until it is over
new String[] {
"ds-task-unprocessed-entry-count",
"ds-task-processed-entry-count",
"ds-task-log-message",
"ds-task-state"
});
long lastTimeMsgDisplayed = -1;
long lastTimeMsgLogged = -1;
long totalEntries = 0;
while (!isOver)
{
sleepCatchInterrupt(500);
try
{
try
{
{
}
}
finally
{
}
// Get the number of entries that have been handled and
// a percentage...
long processed = -1;
long unprocessed = -1;
if (sProcessed != null)
{
}
if (sUnprocessed != null)
{
}
{
/* Refresh period: to avoid having too many lines in the log */
{
}
if (displayProgress
{
println();
}
}
{
lastLogMsg = logMsg;
}
{
isOver = true;
{
println();
}
{
if (displayProgress)
{
}
}
{
null);
if (lastLogMsg == null
{
"Last Log Msg: "+lastLogMsg));
// Assume that this is a peer not found error.
throw new PeerNotFoundException(errorMsg);
}
else
{
throw ce;
}
}
else
{
if (displayProgress)
{
println();
}
}
}
}
catch (NameNotFoundException x)
{
isOver = true;
if (displayProgress)
{
println();
}
}
catch (NamingException ne)
{
throw new ClientException(
}
}
}
private LocalizableMessage getInitializeAllErrorMsg(String serverDisplay, String lastLogMsg, String state)
{
if (lastLogMsg != null)
{
}
}
{
{
{
}
else
{
// return INFO_NO_ENTRIES_TO_INITIALIZE.get();
return null;
}
}
else if (processed != -1)
{
}
else if (unprocessed != -1)
{
}
else
{
return lastDisplayedMsg;
}
}
private long getMinRefreshPeriod(long totalEntries)
{
if (totalEntries < 100)
{
return 0;
}
else if (totalEntries < 1000)
{
return 1000;
}
else if (totalEntries < 10000)
{
return 5000;
}
return 10000;
}
/**
* Removes the references to a replication server in the base DNs of a
* given server.
* @param server the server that we want to update.
* @param replicationServer the replication server whose references we want
* to remove.
* @param bindDn the bindDn that must be used to log to the server.
* @param pwd the password that must be used to log to the server.
* @param baseDNs the list of base DNs where we want to remove the references
* to the provided replication server.
* @param updateReplicationServers if references in the replication servers
* must be updated.
* @param cnx the preferred LDAP URLs to be used to connect to the
* server.
* @throws ReplicationCliException if there is an error updating the
* configuration.
*/
throws ReplicationCliException
{
filter.setSearchMonitoringInformation(false);
filter.setSearchBaseDNInformation(false);
try
{
try
{
}
catch (ManagedObjectNotFoundException monfe)
{
// It does not exist.
monfe));
}
{
if (domainNames != null)
{
{
{
lastBaseDN = baseDN;
{
hostPort)));
if (replServers != null)
{
if (replServer != null)
{
{
}
else
{
}
}
}
println();
}
}
}
}
{
if (replServers != null)
{
if (replServer != null)
{
{
rServerObj.commit();
}
else
{
}
}
}
}
}
}
catch (NamingException ne)
{
}
catch (OpenDsException ode)
{
if (lastBaseDN != null)
{
throw new ReplicationCliException(msg,
}
else
{
ode.getMessage());
}
}
finally
{
}
}
/**
* Deletes a replication domain in a server for a given base DN (disable
* replication of the base DN).
* @param ctx the connection to the server.
* @param baseDN the base DN of the replication domain that we want to
* delete.
* @throws ReplicationCliException if there is an error updating the
* configuration of the server.
*/
{
try
{
try
{
}
catch (ManagedObjectNotFoundException monfe)
{
// It does not exist.
monfe));
}
{
if (domainNames != null)
{
{
{
println();
}
}
}
}
}
catch (OpenDsException ode)
{
throw new ReplicationCliException(msg,
}
}
/**
* Disables the replication server for a given server.
* @param ctx the connection to the server.
* @throws ReplicationCliException if there is an error updating the
* configuration of the server.
*/
throws ReplicationCliException
{
try
{
try
{
if (sync.hasReplicationServer())
{
}
}
catch (ManagedObjectNotFoundException monfe)
{
// It does not exist.
monfe));
}
if (replicationServer != null)
{
hostPort)));
println();
}
}
catch (OpenDsException ode)
{
throw new ReplicationCliException(
ode);
}
}
/**
* Returns a message for a given OpenDsException (we assume that was an
* exception generated updating the configuration of the server) that
* occurred when we were configuring the replication server.
* @param hostPort the hostPort representation of the server we were
* contacting when the OpenDsException occurred.
* @return a message for a given OpenDsException (we assume that was an
* exception generated updating the configuration of the server) that
* occurred when we were configuring the replication server.
*/
{
}
/**
* Returns a message for a given OpenDsException (we assume that was an
* exception generated updating the configuration of the server) that
* occurred when we were configuring some replication domain (creating
* the replication domain or updating the list of replication servers of
* the replication domain).
* @param hostPort the hostPort representation of the server we were
* contacting when the OpenDsException occurred.
* @return a message for a given OpenDsException (we assume that was an
* exception generated updating the configuration of the server) that
* occurred when we were configuring some replication domain (creating
* the replication domain or updating the list of replication servers of
* the replication domain).
*/
{
}
/**
* Returns a message for a given OpenDsException (we assume that was an
* exception generated updating the configuration of the server) that
* occurred when we were configuring some replication domain (deleting
* the replication domain or updating the list of replication servers of
* the replication domain).
* @param hostPort the hostPort representation of the server we were
* contacting when the OpenDsException occurred.
* @return a message for a given OpenDsException (we assume that was an
* exception generated updating the configuration of the server) that
* occurred when we were configuring some replication domain (deleting
* the replication domain or updating the list of replication servers of
* the replication domain).
*/
{
}
/**
* Returns a message informing the user that the provided port cannot be used.
* @param port the port that cannot be used.
* @return a message informing the user that the provided port cannot be used.
*/
{
{
}
}
/**
* Convenience method used to know if one Set of replication servers equals
* another set of replication servers.
* @param s1 the first set of replication servers.
* @param s2 the second set of replication servers.
* @return <CODE>true</CODE> if the two sets represent the same replication
* servers and <CODE>false</CODE> otherwise.
*/
{
{
}
{
}
}
/**
* Convenience method used to merge two Sets of replication servers.
* @param s1 the first set of replication servers.
* @param s2 the second set of replication servers.
* @return a Set of replication servers containing all the replication servers
* specified in the provided Sets.
*/
{
{
}
{
}
return c1;
}
/**
* Returns the message that must be displayed to the user for a given
* exception. This is assumed to be a critical exception that stops all
* the processing.
* @param rce the ReplicationCliException.
* @return a message to be displayed to the user.
*/
{
{
}
// Check if the cause has already been included in the message
if (c != null)
{
String s;
if (c instanceof NamingException)
{
s = ((NamingException)c).toString(true);
}
else if (c instanceof OpenDsException)
{
{
}
else
{
s = c.toString();
}
}
else
{
s = c.toString();
}
{
}
}
}
{
boolean mustInitializeSchema = false;
if (!argParser.noSchemaReplication())
{
}
if (mustInitializeSchema)
{
// Check that both will contain replication data
}
return mustInitializeSchema;
}
/**
* This method registers a server in a given ADSContext. If the server was
* already registered it unregisters it and registers again (some properties
* might have changed).
* @param adsContext the ADS Context to be used.
* @param serverProperties the properties of the server to be registered.
* @throws ADSContextException if an error occurs during the registration or
* unregistration of the server.
*/
throws ADSContextException
{
try
{
}
catch (ADSContextException ade)
{
{
}
else
{
throw ade;
}
}
}
/** {@inheritDoc} */
public boolean isAdvancedMode() {
return false;
}
/** {@inheritDoc} */
public boolean isInteractive() {
}
/** {@inheritDoc} */
public boolean isMenuDrivenMode() {
return true;
}
/** {@inheritDoc} */
public boolean isQuiet()
{
}
/** {@inheritDoc} */
public boolean isScriptFriendly() {
return argParser.isScriptFriendly();
}
/** {@inheritDoc} */
public boolean isVerbose() {
return true;
}
/**
* Forces the initialization of the trust manager in the
* LDAPConnectionInteraction object.
*/
private void forceTrustManagerInitialization()
{
forceNonInteractive = true;
try
{
}
catch (ArgumentException ae)
{
}
forceNonInteractive = false;
}
/**
* Method used to compare two server registries.
* @param registry1 the first registry to compare.
* @param registry2 the second registry to compare.
* @return <CODE>true</CODE> if the registries are equal and
* <CODE>false</CODE> otherwise.
*/
private boolean areEqual(Set<Map<ServerProperty, Object>> registry1, Set<Map<ServerProperty, Object>> registry2)
{
}
{
{
{
}
}
return propertiesToCompare;
}
private boolean equals(Set<Map<ServerProperty, Object>> registry1, Set<Map<ServerProperty, Object>> registry2,
{
{
{
return false;
}
}
return true;
}
private boolean exists(Set<Map<ServerProperty, Object>> registry2, Map<ServerProperty, Object> server1,
{
{
{
return true;
}
}
return false;
}
{
{
{
return false;
}
}
return true;
}
{
{
}
}
/**
* Tells whether we are trying to disable all the replicated suffixes.
* @param uData the disable replication data provided by the user.
* @return <CODE>true</CODE> if we want to disable all the replicated suffixes
* and <CODE>false</CODE> otherwise.
*/
{
if (uData.disableAll())
{
return true;
}
{
if (rep.isReplicated())
{
}
}
{
{
return false;
}
}
return true;
}
{
{
{
return true;
}
}
return false;
}
/**
* Returns the host port representation of the server to be used in progress,
* status and error messages. It takes into account the fact the host and
* port provided by the user.
* @param server the ServerDescriptor.
* @param cnx the preferred connections list.
* @return the host port string representation of the provided server.
*/
{
{
{
}
{
}
}
{
return hostPort;
}
return server.getHostPort(true);
}
/**
* Prompts the user for the subcommand that should be executed.
* @return the subcommand choice of the user.
*/
private SubcommandChoice promptForSubcommand()
{
new MenuBuilder<SubcommandChoice>(this);
builder.addCancelOption(false);
{
{
}
}
try
{
if (m.isSuccess())
{
return m.getValue();
}
// The user cancelled
return SubcommandChoice.CANCEL;
}
catch (ClientException ce)
{
return SubcommandChoice.CANCEL;
}
}
private boolean mustPrintCommandBuilder()
{
return argParser.isInteractive() &&
}
/**
* Prints the contents of a command builder. This method has been created
* since SetPropSubCommandHandler calls it. All the logic of DSConfig is on
* this method. Currently it simply writes the content of the CommandBuilder
* to the standard output, but if we provide an option to write the content
* to a file only the implementation of this method must be changed.
* @param commandBuilder the command builder to be printed.
*/
{
try
{
{
println();
// We assume that the app we are running is this one.
}
{
// Write to the file.
try
{
}
catch (IOException ioe)
{
}
}
}
catch (Throwable t)
{
}
}
/**
* Creates a command builder with the global options: script friendly,
* verbose, etc. for a given subcommand name. It also adds systematically the
* no-prompt option.
* @param subcommandName the subcommand name.
* @param uData the user data.
* @return the command builder that has been created with the specified
* subcommandName.
*/
{
{
// All the arguments for enable replication are update here.
}
{
// All the arguments for initialize replication are update here.
}
{
// All the arguments for initialize replication are update here.
}
else
{
// Update the arguments used in the console interaction with the
// actual arguments of dsreplication.
}
{
if (disableData.disableAll())
{
}
else if (disableData.disableReplicationServer())
{
}
}
return commandBuilder;
}
private String getCommandName()
{
if (commandName != null)
{
return commandName;
}
return "dsreplication";
}
private void updateCommandBuilderWithConsoleInteraction(
{
{
{
{
}
{
}
else
{
}
}
}
}
{
{
{
uData.getTaskSchedule());
}
}
}
private void updateCommandBuilderWithTaskSchedule(
{
}
throws ArgumentException
{
null,
{
}
// Try to find some arguments and put them at the end.
String[] identifiersToMove ={
"adminPassword",
"adminPasswordFile",
};
{
{
}
}
{
if (toObfuscate)
{
}
else
{
}
}
{
}
if (argParser.isScriptFriendly())
{
}
{
}
{
}
}
{
{
{
return arg;
}
}
return null;
}
{
}
{
if (isObfuscated)
{
}
else
{
}
}
throws ArgumentException
{
// Update the arguments used in the console interaction with the
// actual arguments of dsreplication.
boolean adminInformationAdded = false;
if (firstServerCommandBuilder != null)
{
// This is required when both the bindDN and the admin UID are provided
// in the command-line.
boolean forceAddBindDN1 = false;
boolean forceAddBindPwdFile1 = false;
if (useAdminUID)
{
{
forceAddBindDN1 = true;
}
}
{
{
}
{
if (forceAddBindDN1)
{
if (forceAddBindPwdFile1)
{
"{password file}");
}
else
{
}
}
}
{
}
{
if (useAdminUID)
{
adminInformationAdded = true;
}
else
{
}
}
{
if (useAdminUID)
{
}
else
{
}
}
else
{
{
adminInformationAdded = true;
}
}
}
}
{
// This is required when both the bindDN and the admin UID are provided
// in the command-line.
boolean forceAddBindDN2 = false;
boolean forceAddBindPwdFile2 = false;
if (useAdminUID)
{
{
forceAddBindDN2 = true;
}
}
{
{
}
{
if (forceAddBindDN2)
{
if (forceAddBindPwdFile2)
{
"{password file}");
}
else
{
}
}
}
{
}
{
if (useAdminUID && !adminInformationAdded)
{
adminInformationAdded = true;
}
else if (hasBindDN)
{
}
}
{
if (useAdminUID && !adminInformationAdded)
{
adminInformationAdded = true;
}
else if (hasBindDN)
{
}
}
else
{
}
}
{
// Just check that the arguments have not already been added.
{
}
}
}
// Try to add the new administration information.
if (!adminInformationAdded)
{
{
OPTION_LONG_ADMIN_UID, false, false, true,
}
if (userProvidedAdminPwdFile != null)
{
}
{
}
}
if (uData.configureReplicationServer1() &&
{
argParser.onlyReplicationServer1Arg, INFO_DESCRIPTION_ENABLE_REPLICATION_ONLY_REPLICATION_SERVER1));
}
if (!uData.configureReplicationServer1() &&
{
}
if (uData.configureReplicationServer1() &&
{
"replicationPort1", 'r',
8989, null,
}
if (uData.isSecureReplication1())
{
"secureReplication1",
}
if (uData.configureReplicationServer2() &&
{
argParser.onlyReplicationServer2Arg, INFO_DESCRIPTION_ENABLE_REPLICATION_ONLY_REPLICATION_SERVER2));
}
if (!uData.configureReplicationServer2() &&
{
}
if (uData.configureReplicationServer2() &&
{
"replicationPort2", 'r',
}
if (uData.isSecureReplication2())
{
"secureReplication2",
}
if (!uData.replicateSchema())
{
}
{
"skipportcheck", 'S', "skipPortCheck",
}
{
"usesecondserverasschemasource", null,
"useSecondServerAsSchemaSource",
}
}
{
return new BooleanArgument(arg.getName(), arg.getShortIdentifier(), arg.getLongIdentifier(), msg.get());
}
{
return bindPasswordArg;
}
{
return bindPasswordArg;
}
{
return new FileBasedArgument(
}
{
return new FileBasedArgument(
}
{
StringArgument bindDN = new StringArgument("bindDN1", OPTION_SHORT_BINDDN, "bindDN1", false, false, true,
return bindDN;
}
throws ArgumentException
{
StringArgument bindDN = new StringArgument("bindDN2", shortIdentifier, "bindDN2", false, false, true,
return bindDN;
}
throws ArgumentException
{
// Update the arguments used in the console interaction with the
// actual arguments of dsreplication.
if (firstServerCommandBuilder != null)
{
{
{
}
{
}
{
}
{
}
else
{
}
}
}
{
{
{
}
{
}
}
}
}
{
return sArg;
}
{
return new StringArgument("adminPassword",
OPTION_SHORT_BINDPWD, "adminPassword", false, false, true,
}
{
return fbArg;
}
private IntegerArgument getPortArg(String longIdentifier, Character shortIdentifier, int value, Arg0 arg)
throws ArgumentException
{
IntegerArgument iArg = new IntegerArgument(longIdentifier, shortIdentifier, longIdentifier, false, false, true,
return iArg;
}
{
StringArgument sArg = new StringArgument(longIdentifier, shortIdentifier, longIdentifier, false, false, true,
return sArg;
}
private void updateAvailableAndReplicatedSuffixesForOneDomain(
{
{
{
if (replica.isReplicated())
{
}
}
{
}
else if (!replica.isReplicated())
{
}
{
}
else
{
}
}
}
private void updateAvailableAndReplicatedSuffixesForNoDomain(
{
{
}
{
}
{
}
}
{
try
{
if (adsContext.hasAdminData())
{
return cache;
}
}
catch (Throwable t)
{
logger.warn(LocalizableMessage.raw("Error loading topology cache in " + getLdapUrl(ctx) + ": " + t, t));
}
return null;
}
{
{
{
{
}
}
}
}
private void updateAvailableAndReplicatedSuffixesForNoDomainOneSense(
{
{
{
{
boolean isSecondReplicatedInSameTopology = false;
boolean isSecondReplicated = false;
boolean isFirstReplicated = false;
{
{
{
{
isSecondReplicated = true;
}
{
isFirstReplicated = true;
}
if (isFirstReplicated && isSecondReplicated)
{
isSecondReplicatedInSameTopology = true;
break;
}
}
break;
}
}
{
}
else
{
}
break;
}
}
}
}
{
if (uData.configureReplicationServer1() &&
{
return;
}
{
int nReplicationServers = 0;
{
{
{
if (uData.configureReplicationServer1() &&
{
}
if (uData.configureReplicationServer2() &&
{
}
}
}
}
if (uData.configureReplicationServer1())
{
}
if (uData.configureReplicationServer2())
{
}
if (nReplicationServers == 1)
{
}
else if (nReplicationServers == 0)
{
}
}
}
private void createTopologyCache(ADSContext adsCtx, ReplicationUserData uData, Set<SuffixDescriptor> suffixes)
{
try
{
if (adsCtx.hasAdminData())
{
}
}
catch (Throwable t)
{
}
}
/**
* Merge the contents of the two registries but only does it partially.
* Only one of the two ADSContext will be updated (in terms of data in
* cn=admin data), while the other registry's replication servers will have
* their truststore updated to be able to initialize all the contents.
*
* This method does NOT configure replication between topologies or initialize
* replication.
*
* @param adsCtx1 the ADSContext of the first registry.
* @param adsCtx2 the ADSContext of the second registry.
* @return <CODE>true</CODE> if the registry containing all the data is
* the first registry and <CODE>false</CODE> otherwise.
* @throws ReplicationCliException if there is a problem reading or updating
* the registries.
*/
throws ReplicationCliException
{
try
{
// Look for the cache with biggest number of replication servers:
// that one is going to be source.
int nRepServers1 = 0;
{
if (server.isReplicationServer())
{
nRepServers1 ++;
}
}
int nRepServers2 = 0;
{
if (server.isReplicationServer())
{
nRepServers2 ++;
}
}
if (nRepServers1 >= nRepServers2)
{
}
else
{
}
if (isInteractive())
{
if (!askConfirmation(msg, true))
{
}
}
else
{
println();
}
pointAdder.start();
if (!cache1Errors.isEmpty())
{
throw new ReplicationCliException(
}
if (!cache2Errors.isEmpty())
{
throw new ReplicationCliException(
}
{
if (server1.isReplicationServer())
{
boolean found = false;
{
if (server2.isReplicationServer()
{
server1.getHostPort(true),
server2.getHostPort(true),
found = true;
break;
}
}
if (found)
{
break;
}
}
}
{
{
if (replica1.isReplicated())
{
boolean found = false;
{
{
// Conflicting domain names must apply to same suffix.
continue;
}
{
if (replica2.isReplicated()
{
domain1Id));
found = true;
break;
}
}
if (found)
{
break;
}
}
}
}
}
{
if (!commonRepServerIDErrors.isEmpty())
{
}
if (!commonDomainIDErrors.isEmpty())
{
{
}
}
}
if (nRepServers1 >= nRepServers2)
{
}
else
{
}
try
{
}
catch (ADSContextException adce)
{
" with registry of "+
{
}
else
{
throw new ReplicationCliException(
}
}
try
{
{
if (server.isReplicationServer())
{
try
{
}
finally
{
}
}
}
}
catch (Throwable t)
{
}
pointAdder.stop();
println();
return adsCtxSource == adsCtx1;
}
finally
{
pointAdder.stop();
}
}
{
return (t instanceof OpenDsException) ?
}
throws ReplicationCliException
{
try
{
return cache;
}
catch (TopologyCacheException te)
{
throw new ReplicationCliException(ERR_REPLICATION_READING_ADS.get(te.getMessageObject()), ERROR_UPDATING_ADS, te);
}
}
throws NamingException
{
filter.setSearchMonitoringInformation(false);
filter.setSearchBaseDNInformation(false);
return loader.createContext();
}
/**
* Returns <CODE>true</CODE> if the provided baseDN is replicated in the
* provided server, <CODE>false</CODE> otherwise.
* @param server the server.
* @param baseDN the base DN.
* @return <CODE>true</CODE> if the provided baseDN is replicated in the
* provided server, <CODE>false</CODE> otherwise.
*/
{
}
/**
* Returns <CODE>true</CODE> if the provided baseDN is replicated between
* both servers, <CODE>false</CODE> otherwise.
* @param server1 the first server.
* @param server2 the second server.
* @param baseDN the base DN.
* @return <CODE>true</CODE> if the provided baseDN is replicated between
* both servers, <CODE>false</CODE> otherwise.
*/
{
{
{
{
// it is replicated in both
return true;
}
}
}
return false;
}
{
{
{
return replica;
}
}
return null;
}
{
}
/**
* 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();
}
/**
*/
private String getBinaryDir()
{
{
try
{
binDir = f.getCanonicalPath();
}
catch (Throwable t)
{
binDir = f.getAbsolutePath();
}
{
}
}
return binDir;
}
/**
* Returns the full path of the command-line for a given script name.
* @param scriptBasicName the script basic name (with no extension).
* @return the full path of the command-line for a given script name.
*/
{
if (isWindows())
{
}
return getBinaryDir() + scriptBasicName;
}
}
/** Class used to compare replication servers. */
{
/** {@inheritDoc} */
{
if (compare == 0)
{
{
return 1;
}
{
return -1;
}
}
return compare;
}
}
/** Class used to compare suffixes. */
{
/** {@inheritDoc} */
{
}
}
/** Class used to compare servers. */
{
/** {@inheritDoc} */
{
}
}