/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
* or http://forgerock.org/license/CDDLv1.0.html.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at legal-notices/CDDLv1_0.txt.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Copyright 2007-2010 Sun Microsystems, Inc.
* Portions Copyright 2011-2015 ForgeRock AS
* Portions Copyright 2012 profiq s.r.o.
*/
package org.opends.server.tools.dsreplication;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicReference;
import javax.naming.NameAlreadyBoundException;
import javax.naming.NameNotFoundException;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.NoPermissionException;
import javax.naming.directory.BasicAttributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapName;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.TrustManager;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.LocalizableMessageBuilder;
import org.forgerock.i18n.LocalizableMessageDescriptor.Arg0;
import org.forgerock.i18n.LocalizableMessageDescriptor.Arg1;
import org.forgerock.i18n.LocalizableMessageDescriptor.Arg2;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.opendj.config.server.ConfigException;
import org.opends.admin.ads.*;
import org.opends.admin.ads.ADSContext.ADSPropertySyntax;
import org.opends.admin.ads.ADSContext.AdministratorProperty;
import org.opends.admin.ads.ADSContext.ServerProperty;
import org.opends.admin.ads.util.ApplicationTrustManager;
import org.opends.admin.ads.util.OpendsCertificateException;
import org.opends.admin.ads.util.PreferredConnection;
import org.opends.admin.ads.util.ServerLoader;
import org.opends.guitools.controlpanel.datamodel.BackendDescriptor;
import org.opends.guitools.controlpanel.datamodel.BaseDNDescriptor;
import org.opends.guitools.controlpanel.util.*;
import org.opends.quicksetup.ApplicationException;
import org.opends.quicksetup.Constants;
import org.opends.quicksetup.Installation;
import org.opends.quicksetup.event.ProgressUpdateEvent;
import org.opends.quicksetup.event.ProgressUpdateListener;
import org.opends.quicksetup.installer.Installer;
import org.opends.quicksetup.installer.InstallerHelper;
import org.opends.quicksetup.installer.PeerNotFoundException;
import org.opends.quicksetup.installer.offline.OfflineInstaller;
import org.opends.quicksetup.util.PlainTextProgressMessageFormatter;
import org.opends.server.admin.*;
import org.opends.server.admin.client.ManagementContext;
import org.opends.server.admin.client.ldap.JNDIDirContextAdaptor;
import org.opends.server.admin.client.ldap.LDAPManagementContext;
import org.opends.server.admin.std.client.*;
import org.opends.server.admin.std.meta.ReplicationDomainCfgDefn;
import org.opends.server.admin.std.meta.ReplicationServerCfgDefn;
import org.opends.server.admin.std.meta.ReplicationSynchronizationProviderCfgDefn;
import org.opends.server.core.DirectoryServer;
import org.opends.server.tasks.PurgeConflictsHistoricalTask;
import org.opends.server.tools.dsreplication.EnableReplicationUserData.EnableReplicationServerData;
import org.opends.server.tools.tasks.TaskEntry;
import org.opends.server.tools.tasks.TaskScheduleInteraction;
import org.opends.server.tools.tasks.TaskScheduleUserData;
import org.opends.server.types.DN;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.HostPort;
import org.opends.server.types.InitializationException;
import org.opends.server.types.NullOutputStream;
import org.opends.server.types.OpenDsException;
import org.opends.server.util.BuildVersion;
import org.opends.server.util.ServerConstants;
import org.opends.server.util.SetupUtils;
import org.opends.server.util.StaticUtils;
import org.opends.server.util.cli.LDAPConnectionConsoleInteraction;
import org.opends.server.util.cli.PointAdder;
import com.forgerock.opendj.cli.Argument;
import com.forgerock.opendj.cli.ArgumentException;
import com.forgerock.opendj.cli.BooleanArgument;
import com.forgerock.opendj.cli.CliConstants;
import com.forgerock.opendj.cli.ClientException;
import com.forgerock.opendj.cli.CommandBuilder;
import com.forgerock.opendj.cli.ConsoleApplication;
import com.forgerock.opendj.cli.FileBasedArgument;
import com.forgerock.opendj.cli.IntegerArgument;
import com.forgerock.opendj.cli.MenuBuilder;
import com.forgerock.opendj.cli.MenuResult;
import com.forgerock.opendj.cli.ReturnCode;
import com.forgerock.opendj.cli.StringArgument;
import com.forgerock.opendj.cli.SubCommand;
import com.forgerock.opendj.cli.TabSeparatedTablePrinter;
import com.forgerock.opendj.cli.TableBuilder;
import com.forgerock.opendj.cli.TablePrinter;
import com.forgerock.opendj.cli.TextTablePrinter;
import com.forgerock.opendj.cli.ValidationCallback;
import static com.forgerock.opendj.cli.ArgumentConstants.*;
import static com.forgerock.opendj.cli.CliMessages.*;
import static com.forgerock.opendj.cli.Utils.*;
import static com.forgerock.opendj.util.OperatingSystem.*;
import static java.util.Collections.*;
import static org.forgerock.util.Utils.*;
import static org.opends.admin.ads.util.ConnectionUtils.*;
import static org.opends.admin.ads.util.PreferredConnection.*;
import static org.opends.admin.ads.ServerDescriptor.getReplicationServer;
import static org.opends.admin.ads.ServerDescriptor.getServerRepresentation;
import static org.opends.admin.ads.ServerDescriptor.getSuffixDisplay;
import static org.opends.messages.AdminToolMessages.*;
import static org.opends.messages.QuickSetupMessages.*;
import static org.opends.messages.ToolMessages.INFO_TASK_TOOL_TASK_SUCESSFULL;
import static org.opends.messages.ToolMessages.INFO_TASK_TOOL_TASK_SCHEDULED_FUTURE;
import static org.opends.messages.ToolMessages.INFO_TASK_TOOL_RECURRING_TASK_SCHEDULED;
import static org.opends.quicksetup.util.Utils.*;
import static org.opends.server.tools.dsreplication.ReplicationCliArgumentParser.*;
import static org.opends.server.tools.dsreplication.ReplicationCliReturnCode.*;
import static org.opends.server.util.StaticUtils.*;
/**
* 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
{
private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
/** The fully-qualified name of this class. */
private static final String CLASS_NAME = ReplicationCliMain.class.getName();
/** Prefix for log files. */
public static final String LOG_FILE_PREFIX = "opendj-replication-";
/** Suffix for log files. */
public static final String LOG_FILE_SUFFIX = ".log";
/**
* 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 static final String FIRST_SCRIPT_CALL = "firstcall";
private static final LocalizableMessage EMPTY_MSG = LocalizableMessage.raw("");
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. */
ENABLE(ENABLE_REPLICATION_SUBCMD_NAME, INFO_REPLICATION_ENABLE_MENU_PROMPT.get()),
/** Disable replication. */
DISABLE(DISABLE_REPLICATION_SUBCMD_NAME, INFO_REPLICATION_DISABLE_MENU_PROMPT.get()),
/** Initialize replication. */
INITIALIZE(INITIALIZE_REPLICATION_SUBCMD_NAME, INFO_REPLICATION_INITIALIZE_MENU_PROMPT.get()),
/** Initialize All. */
INITIALIZE_ALL(INITIALIZE_ALL_REPLICATION_SUBCMD_NAME, INFO_REPLICATION_INITIALIZE_ALL_MENU_PROMPT.get()),
/** Pre external initialization. */
PRE_EXTERNAL_INITIALIZATION(PRE_EXTERNAL_INITIALIZATION_SUBCMD_NAME,
INFO_REPLICATION_PRE_EXTERNAL_INITIALIZATION_MENU_PROMPT.get()),
/** Post external initialization. */
POST_EXTERNAL_INITIALIZATION(POST_EXTERNAL_INITIALIZATION_SUBCMD_NAME,
INFO_REPLICATION_POST_EXTERNAL_INITIALIZATION_MENU_PROMPT.get()),
/** Replication status. */
STATUS(STATUS_REPLICATION_SUBCMD_NAME, INFO_REPLICATION_STATUS_MENU_PROMPT.get()),
/** Replication purge historical. */
PURGE_HISTORICAL(PURGE_HISTORICAL_SUBCMD_NAME, INFO_REPLICATION_PURGE_HISTORICAL_MENU_PROMPT.get()),
/** Set changelog change number from another server. */
RESET_CHANGE_NUMBER(ReplicationCliArgumentParser.RESET_CHANGE_NUMBER_SUBCMD_NAME,
INFO_DESCRIPTION_RESET_CHANGE_NUMBER.get()),
/** Cancel operation. */
CANCEL(null, null);
private final String name;
private LocalizableMessage prompt;
private SubcommandChoice(String name, LocalizableMessage prompt)
{
this.name = name;
this.prompt = prompt;
}
private LocalizableMessage getPrompt()
{
return prompt;
}
private String getName()
{
return name;
}
private static SubcommandChoice fromName(String subCommandName)
{
SubcommandChoice[] f = values();
for (SubcommandChoice subCommand : f)
{
if (subCommand.name.equals(subCommandName))
{
return subCommand;
}
}
return null;
}
}
/** Abstract some of the operations when two servers must be queried for information. */
private interface OperationBetweenSourceAndDestinationServers
{
/**
* Returns whether we should stop processing after asking the user for additional information.
* Might connect to servers to run configuration checks.
* @param baseDNs user specified baseDNs
* @param source the source server
* @param dest the destination server
* @param interactive if user has to input information
* @return whether we should stop
*/
boolean continueAfterUserInput(Collection<String> baseDNs, InitialLdapContext source, InitialLdapContext dest,
boolean interactive);
/**
* Confirm with the user whether the current task should continue.
*
* @param uData servers address and authentication parameters
* @param ctxSource LDAP Context for the destination server
* @param ctxDestination LDAP Context for the source server
* @param defaultValue default yes or no
* @return whether the current task should be interrupted
*/
boolean confirmOperation(SourceDestinationServerUserData uData, InitialLdapContext ctxSource,
InitialLdapContext ctxDestination, final boolean defaultValue);
}
/** The argument parser to be used. */
private ReplicationCliArgumentParser argParser;
private FileBasedArgument userProvidedAdminPwdFile;
private LDAPConnectionConsoleInteraction sourceServerCI;
private CommandBuilder firstServerCommandBuilder;
/** The message formatter. */
private PlainTextProgressMessageFormatter formatter = new PlainTextProgressMessageFormatter();
/**
* 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.
*/
public ReplicationCliMain(PrintStream out, PrintStream err)
{
super(out, err);
}
/**
* The main method for the replication tool.
*
* @param args the command-line arguments provided to this program.
*/
public static void main(String[] args)
{
int retCode = mainCLI(args, true, System.out, System.err);
System.exit(retCode);
}
/**
* 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.
*/
public static int mainCLI(String[] args)
{
return mainCLI(args, true, System.out, System.err);
}
/**
* 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.
*/
public static int mainCLI(String[] args, boolean initializeServer,
OutputStream outStream, OutputStream errStream)
{
PrintStream out = NullOutputStream.wrapOrNullStream(outStream);
PrintStream err = NullOutputStream.wrapOrNullStream(errStream);
try
{
ControlPanelLog.initLogFileHandler(
File.createTempFile(LOG_FILE_PREFIX, LOG_FILE_SUFFIX));
} catch (Throwable t) {
System.err.println("Unable to initialize log");
t.printStackTrace();
}
ReplicationCliMain replicationCli = new ReplicationCliMain(out, err);
ReplicationCliReturnCode result = replicationCli.execute(args, initializeServer);
if (result.getReturnCode() == 0)
{
// Delete the temp log file, in case of success.
ControlPanelLog.closeAndDeleteLogFile();
}
return result.getReturnCode();
}
/**
* 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.
*/
public ReplicationCliReturnCode execute(String[] args, boolean initializeServer)
{
// Create the command-line argument parser for use with this program.
try
{
createArgumenParser();
}
catch (ArgumentException ae)
{
errPrintln(ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage()));
logger.error(LocalizableMessage.raw("Complete error stack:"), ae);
return CANNOT_INITIALIZE_ARGS;
}
try
{
argParser.getSecureArgsList().initArgumentsWithConfiguration();
}
catch (ConfigException ce)
{
// Ignore.
}
// Parse the command-line arguments provided to this program.
try
{
argParser.parseArguments(args);
}
catch (ArgumentException ae)
{
argParser.displayMessageAndUsageReference(getErrStream(), ERR_ERROR_PARSING_ARGS.get(ae.getMessage()));
logger.error(LocalizableMessage.raw("Complete error stack:"), ae);
return ERROR_USER_DATA;
}
// If we should just display usage or version information, then print it and exit.
if (argParser.usageOrVersionDisplayed())
{
return SUCCESSFUL_NOP;
}
// Checks the version - if upgrade required, the tool is unusable
try
{
BuildVersion.checkVersionMismatch();
}
catch (InitializationException e)
{
errPrintln(e.getMessageObject());
return CANNOT_INITIALIZE_ARGS;
}
// Check that the provided parameters are compatible.
LocalizableMessageBuilder buf = new LocalizableMessageBuilder();
argParser.validateOptions(buf);
if (buf.length() > 0)
{
errPrintln(buf.toMessage());
errPrintln(LocalizableMessage.raw(argParser.getUsage()));
return ERROR_USER_DATA;
}
if (initializeServer)
{
DirectoryServer.bootstrapClient();
// Bootstrap definition classes.
try
{
if (!ClassLoaderProvider.getInstance().isEnabled())
{
ClassLoaderProvider.getInstance().enable();
}
// Switch off class name validation in client.
ClassPropertyDefinition.setAllowClassValidation(false);
// Switch off attribute type name validation in client.
AttributeTypePropertyDefinition.setCheckSchema(false);
}
catch (InitializationException ie)
{
errPrintln(ie.getMessageObject());
return ERROR_INITIALIZING_ADMINISTRATION_FRAMEWORK;
}
}
if (argParser.getSecureArgsList().bindPasswordFileArg.isPresent())
{
try
{
userProvidedAdminPwdFile = new FileBasedArgument(
"adminPasswordFile", OPTION_SHORT_BINDPWD_FILE, "adminPasswordFile", false, false,
INFO_BINDPWD_FILE_PLACEHOLDER.get(), null, null,
INFO_DESCRIPTION_REPLICATION_ADMIN_BINDPASSWORDFILE.get());
userProvidedAdminPwdFile.getNameToValueMap().putAll(
argParser.getSecureArgsList().bindPasswordFileArg.getNameToValueMap());
}
catch (Throwable t)
{
throw new IllegalStateException("Unexpected error: " + t, t);
}
}
sourceServerCI = new LDAPConnectionConsoleInteraction(this, argParser.getSecureArgsList());
sourceServerCI.setDisplayLdapIfSecureParameters(false);
ReplicationCliReturnCode returnValue = SUCCESSFUL_NOP;
String subCommand = null;
final SubcommandChoice subcommandChoice = getSubcommandChoice(argParser.getSubCommand());
if (subcommandChoice != null)
{
subCommand = subcommandChoice.getName();
returnValue = execute(subcommandChoice);
}
else if (argParser.isInteractive())
{
final SubcommandChoice subCommandChoice = promptForSubcommand();
if (subCommandChoice == null || SubcommandChoice.CANCEL.equals(subCommandChoice))
{
return USER_CANCELLED;
}
subCommand = subCommandChoice.getName();
if (subCommand != null)
{
String[] newArgs = new String[args.length + 1];
newArgs[0] = subCommand;
System.arraycopy(args, 0, newArgs, 1, args.length);
// The server (if requested) has already been initialized.
return execute(newArgs, false);
}
}
else
{
errPrintln(ERR_REPLICATION_VALID_SUBCOMMAND_NOT_FOUND.get("--" + OPTION_LONG_NO_PROMPT));
errPrintln(LocalizableMessage.raw(argParser.getUsage()));
return ERROR_USER_DATA;
}
// Display the log file only if the operation is successful (when there
// is a critical error this is already displayed).
if (returnValue == SUCCESSFUL && displayLogFileAtEnd(subCommand))
{
File logFile = ControlPanelLog.getLogFile();
if (logFile != null)
{
println();
println(INFO_GENERAL_SEE_FOR_DETAILS.get(logFile.getPath()));
println();
}
}
return returnValue;
}
private SubcommandChoice getSubcommandChoice(SubCommand subCommand)
{
if (subCommand != null)
{
return SubcommandChoice.fromName(subCommand.getName());
}
return null;
}
private ReplicationCliReturnCode execute(SubcommandChoice subcommandChoice)
{
switch (subcommandChoice)
{
case ENABLE:
return enableReplication();
case DISABLE:
return disableReplication();
case INITIALIZE:
return initializeReplication();
case INITIALIZE_ALL:
return initializeAllReplication();
case PRE_EXTERNAL_INITIALIZATION:
return preExternalInitialization();
case POST_EXTERNAL_INITIALIZATION:
return postExternalInitialization();
case STATUS:
return statusReplication();
case PURGE_HISTORICAL:
return purgeHistorical();
case RESET_CHANGE_NUMBER:
return resetChangeNumber();
default:
return SUCCESSFUL_NOP;
}
}
/**
* 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.
*/
private String askForAdministratorUID(String defaultValue, LocalizedLogger logger)
{
return ask(logger, INFO_ADMINISTRATOR_UID_PROMPT.get(), 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.
*/
private String askForAdministratorPwd(LocalizedLogger logger)
{
try
{
return new String(readPassword(INFO_ADMINISTRATOR_PWD_PROMPT.get()));
}
catch (ClientException ex)
{
logger.warn(LocalizableMessage.raw("Error reading input: " + ex, ex));
return null;
}
}
private String ask(LocalizedLogger logger, LocalizableMessage msgPrompt, String defaultValue)
{
try
{
return readInput(msgPrompt, defaultValue);
}
catch (ClientException ce)
{
logger.warn(LocalizableMessage.raw("Error reading input: " + ce, ce));
return defaultValue;
}
}
/**
* 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.
*/
private int askInteger(LocalizableMessage prompt, int defaultValue, LocalizedLogger logger)
{
int newInt = -1;
while (newInt == -1)
{
try
{
newInt = readInteger(prompt, defaultValue);
}
catch (ClientException ce)
{
newInt = -1;
logger.warn(LocalizableMessage.raw("Error reading input: " + ce, ce));
}
}
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(
LocalizableMessage prompt, final int defaultValue) throws ClientException
{
ValidationCallback<Integer> callback = new ValidationCallback<Integer>()
{
@Override
public Integer validate(ConsoleApplication app, String input)
throws ClientException
{
String ninput = input.trim();
if (ninput.length() == 0)
{
return defaultValue;
}
try
{
int i = Integer.parseInt(ninput);
if (i < 1)
{
throw new NumberFormatException();
}
return i;
}
catch (NumberFormatException e)
{
// Try again...
app.errPrintln();
app.errPrintln(ERR_BAD_INTEGER.get(ninput));
app.errPrintln();
return null;
}
}
};
if (defaultValue != -1)
{
prompt = INFO_PROMPT_SINGLE_DEFAULT.get(prompt, defaultValue);
}
return readValidatedInput(prompt, callback, CONFIRMATION_MAX_TRIES);
}
private boolean isFirstCallFromScript()
{
return FIRST_SCRIPT_CALL.equals(System.getProperty(SCRIPT_CALL_STATUS));
}
private void createArgumenParser() throws ArgumentException
{
argParser = new ReplicationCliArgumentParser(CLASS_NAME);
argParser.initializeParser(getOutputStream());
}
/**
* 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()
{
EnableReplicationUserData uData = new EnableReplicationUserData();
if (argParser.isInteractive())
{
try
{
if (promptIfRequired(uData))
{
return enableReplication(uData);
}
else
{
return USER_CANCELLED;
}
}
catch (ReplicationCliException rce)
{
errPrintln();
errPrintln(getCriticalExceptionMessage(rce));
return rce.getErrorCode();
}
}
else
{
initializeWithArgParser(uData);
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()
{
DisableReplicationUserData uData = new DisableReplicationUserData();
if (argParser.isInteractive())
{
try
{
if (promptIfRequired(uData))
{
return disableReplication(uData);
}
else
{
return USER_CANCELLED;
}
}
catch (ReplicationCliException rce)
{
errPrintln();
errPrintln(getCriticalExceptionMessage(rce));
return rce.getErrorCode();
}
}
else
{
initializeWithArgParser(uData);
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.
*/
private ReplicationCliReturnCode initializeAllReplication()
{
InitializeAllReplicationUserData uData =
new InitializeAllReplicationUserData();
if (argParser.isInteractive())
{
if (promptIfRequired(uData))
{
return initializeAllReplication(uData);
}
else
{
return USER_CANCELLED;
}
}
else
{
initializeWithArgParser(uData);
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.
*/
private ReplicationCliReturnCode preExternalInitialization()
{
PreExternalInitializationUserData uData =
new PreExternalInitializationUserData();
if (argParser.isInteractive())
{
if (promptIfRequiredForPreOrPost(uData))
{
return preExternalInitialization(uData);
}
else
{
return USER_CANCELLED;
}
}
else
{
initializeWithArgParser(uData);
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.
*/
private ReplicationCliReturnCode postExternalInitialization()
{
PostExternalInitializationUserData uData =
new PostExternalInitializationUserData();
if (argParser.isInteractive())
{
if (promptIfRequiredForPreOrPost(uData))
{
return postExternalInitialization(uData);
}
else
{
return USER_CANCELLED;
}
}
else
{
initializeWithArgParser(uData);
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()
{
StatusReplicationUserData uData = new StatusReplicationUserData();
if (argParser.isInteractive())
{
try
{
if (promptIfRequired(uData))
{
return statusReplication(uData);
}
else
{
return USER_CANCELLED;
}
}
catch (ReplicationCliException rce)
{
errPrintln();
errPrintln(getCriticalExceptionMessage(rce));
return rce.getErrorCode();
}
}
else
{
initializeWithArgParser(uData);
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()
{
final PurgeHistoricalUserData uData = new PurgeHistoricalUserData();
if (argParser.isInteractive())
{
if (promptIfRequired(uData))
{
return purgeHistorical(uData);
}
else
{
return USER_CANCELLED;
}
}
else
{
initializeWithArgParser(uData);
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.
*/
private void initializeWithArgParser(PurgeHistoricalUserData uData)
{
PurgeHistoricalUserData.initializeWithArgParser(uData, argParser);
}
private ReplicationCliReturnCode purgeHistorical(PurgeHistoricalUserData uData)
{
return uData.isOnline()
? purgeHistoricalRemotely(uData)
: purgeHistoricalLocally(uData);
}
private ReplicationCliReturnCode purgeHistoricalLocally(
PurgeHistoricalUserData uData)
{
List<String> baseDNs = uData.getBaseDNs();
checkSuffixesForLocalPurgeHistorical(baseDNs, false);
if (!baseDNs.isEmpty())
{
uData.setBaseDNs(baseDNs);
if (mustPrintCommandBuilder())
{
printNewCommandBuilder(PURGE_HISTORICAL_SUBCMD_NAME, uData);
}
try
{
return purgeHistoricalLocallyTask(uData);
}
catch (ReplicationCliException rce)
{
errPrintln();
errPrintln(getCriticalExceptionMessage(rce));
logger.error(LocalizableMessage.raw("Complete error stack:"), rce);
return rce.getErrorCode();
}
}
else
{
return HISTORICAL_CANNOT_BE_PURGED_ON_BASEDN;
}
}
private void printPurgeProgressMessage(PurgeHistoricalUserData uData)
{
String separator = formatter.getLineBreak().toString() + formatter.getTab();
println();
LocalizableMessage msg = formatter.getFormattedProgress(
INFO_PROGRESS_PURGE_HISTORICAL.get(separator, joinAsString(separator, uData.getBaseDNs())));
print(msg);
println();
}
private ReplicationCliReturnCode purgeHistoricalLocallyTask(PurgeHistoricalUserData uData)
throws ReplicationCliException
{
ReplicationCliReturnCode returnCode = SUCCESSFUL;
if (isFirstCallFromScript())
{
// Launch the process: launch dsreplication in non-interactive mode with
// the recursive property set.
ArrayList<String> args = new ArrayList<>();
args.add(getCommandLinePath(getCommandName()));
args.add(PURGE_HISTORICAL_SUBCMD_NAME);
args.add("--"+argParser.noPromptArg.getLongIdentifier());
args.add("--"+argParser.maximumDurationArg.getLongIdentifier());
args.add(String.valueOf(uData.getMaximumDuration()));
for (String baseDN : uData.getBaseDNs())
{
args.add("--"+argParser.baseDNsArg.getLongIdentifier());
args.add(baseDN);
}
ProcessBuilder pb = new ProcessBuilder(args);
// Use the java args in the script.
Map<String, String> env = pb.environment();
env.put("RECURSIVE_LOCAL_CALL", "true");
try
{
Process process = pb.start();
ProcessReader outReader =
new ProcessReader(process, getOutputStream(), false);
ProcessReader errReader =
new ProcessReader(process, getErrorStream(), true);
outReader.startReading();
errReader.startReading();
int code = process.waitFor();
for (ReplicationCliReturnCode c : ReplicationCliReturnCode.values())
{
if (c.getReturnCode() == code)
{
returnCode = c;
break;
}
}
}
catch (Exception e)
{
LocalizableMessage msg = ERR_LAUNCHING_PURGE_HISTORICAL.get();
ReplicationCliReturnCode code = ERROR_LAUNCHING_PURGE_HISTORICAL;
throw new ReplicationCliException(
getThrowableMsg(msg, e), code, e);
}
}
else
{
printPurgeProgressMessage(uData);
LocalPurgeHistorical localPurgeHistorical =
new LocalPurgeHistorical(uData, this, formatter,
argParser.getConfigFile(),
argParser.getConfigClass());
returnCode = localPurgeHistorical.execute();
if (returnCode == SUCCESSFUL)
{
printSuccessMessage(uData, null);
}
}
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.
*/
private InitialLdapContext createAdministrativeContext(String host,
int port, boolean useSSL, boolean useStartTLS, String bindDn, String pwd,
int connectTimeout, ApplicationTrustManager trustManager)
throws NamingException
{
InitialLdapContext ctx;
String ldapUrl = getLDAPUrl(host, port, useSSL);
if (useSSL)
{
ctx = createLdapsContext(ldapUrl, bindDn, pwd, connectTimeout, null, trustManager, null);
}
else if (useStartTLS)
{
ctx = createStartTLSContext(ldapUrl, bindDn, pwd, connectTimeout, null, trustManager, null);
}
else
{
ctx = createLdapContext(ldapUrl, bindDn, pwd, connectTimeout, null);
}
if (!connectedAsAdministrativeUser(ctx))
{
throw new NoPermissionException(ERR_NOT_ADMINISTRATIVE_USER.get().toString());
}
return ctx;
}
/**
* Creates an Initial LDAP Context interacting with the user if the
* application is interactive.
*
* @param ci
* the LDAPConnectionConsoleInteraction object that is assumed to
* have been already run.
* @return the initial LDAP context or <CODE>null</CODE> if the user did not
* accept to trust the certificates.
* @throws ClientException
* if there was an error establishing the connection.
*/
private InitialLdapContext createInitialLdapContextInteracting(LDAPConnectionConsoleInteraction ci)
throws ClientException
{
return createInitialLdapContextInteracting(ci, isInteractive() && ci.isTrustStoreInMemory());
}
private OpendsCertificateException getCertificateRootException(Throwable t)
{
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.
*/
private InitialLdapContext createInitialLdapContextInteracting(LDAPConnectionConsoleInteraction ci,
boolean promptForCertificate) throws ClientException
{
// Interact with the user though the console to get
// LDAP connection information
String hostName = getHostNameForLdapUrl(ci.getHostName());
Integer portNumber = ci.getPortNumber();
String bindDN = ci.getBindDN();
String bindPassword = ci.getBindPassword();
TrustManager trustManager = ci.getTrustManager();
KeyManager keyManager = ci.getKeyManager();
InitialLdapContext ctx;
if (ci.useSSL())
{
String ldapsUrl = "ldaps://" + hostName + ":" + portNumber;
while (true)
{
try
{
ctx = createLdapsContext(ldapsUrl, bindDN, bindPassword, ci.getConnectTimeout(),
null, trustManager, keyManager);
ctx.reconnect(null);
break;
}
catch (NamingException e)
{
if (promptForCertificate)
{
OpendsCertificateException oce = getCertificateRootException(e);
if (oce != null)
{
String authType = null;
if (trustManager instanceof ApplicationTrustManager)
{
ApplicationTrustManager appTrustManager =
(ApplicationTrustManager) trustManager;
authType = appTrustManager.getLastRefusedAuthType();
}
if (ci.checkServerCertificate(oce.getChain(), authType, hostName))
{
// If the certificate is trusted, update the trust manager.
trustManager = ci.getTrustManager();
// Try to connect again.
continue;
}
else
{
// Assume user canceled.
return null;
}
}
}
if (e.getCause() != null)
{
if (!isInteractive()
&& !ci.isTrustAll()
&& (getCertificateRootException(e) != null
|| e.getCause() instanceof SSLHandshakeException))
{
LocalizableMessage message =
ERR_FAILED_TO_CONNECT_NOT_TRUSTED.get(hostName, portNumber);
throw new ClientException(ReturnCode.CLIENT_SIDE_CONNECT_ERROR, message);
}
if (e.getCause() instanceof SSLException)
{
LocalizableMessage message =
ERR_FAILED_TO_CONNECT_WRONG_PORT.get(hostName, portNumber);
throw new ClientException(
ReturnCode.CLIENT_SIDE_CONNECT_ERROR, message);
}
}
String hostPort =
ServerDescriptor.getServerRepresentation(hostName, portNumber);
LocalizableMessage message = getMessageForException(e, hostPort);
throw new ClientException(ReturnCode.CLIENT_SIDE_CONNECT_ERROR, message);
}
}
}
else if (ci.useStartTLS())
{
String ldapUrl = "ldap://" + hostName + ":" + portNumber;
while (true)
{
try
{
ctx = createStartTLSContext(ldapUrl, bindDN,
bindPassword, CliConstants.DEFAULT_LDAP_CONNECT_TIMEOUT, null,
trustManager, keyManager, null);
ctx.reconnect(null);
break;
}
catch (NamingException e)
{
if (promptForCertificate)
{
OpendsCertificateException oce = getCertificateRootException(e);
if (oce != null)
{
String authType = null;
if (trustManager instanceof ApplicationTrustManager)
{
ApplicationTrustManager appTrustManager =
(ApplicationTrustManager) trustManager;
authType = appTrustManager.getLastRefusedAuthType();
}
if (ci.checkServerCertificate(oce.getChain(), authType, hostName))
{
// If the certificate is trusted, update the trust manager.
trustManager = ci.getTrustManager();
// Try to connect again.
continue;
}
else
{
// Assume user cancelled.
return null;
}
}
else
{
LocalizableMessage message =
ERR_FAILED_TO_CONNECT.get(hostName, portNumber);
throw new ClientException(
ReturnCode.CLIENT_SIDE_CONNECT_ERROR, message);
}
}
LocalizableMessage message =
ERR_FAILED_TO_CONNECT.get(hostName, portNumber);
throw new ClientException(ReturnCode.CLIENT_SIDE_CONNECT_ERROR,
message);
}
}
}
else
{
String ldapUrl = "ldap://" + hostName + ":" + portNumber;
while (true)
{
try
{
ctx = createLdapContext(ldapUrl, bindDN, bindPassword,
CliConstants.DEFAULT_LDAP_CONNECT_TIMEOUT, null);
ctx.reconnect(null);
break;
}
catch (NamingException e)
{
LocalizableMessage message =
ERR_FAILED_TO_CONNECT.get(hostName, portNumber);
throw new ClientException(ReturnCode.CLIENT_SIDE_CONNECT_ERROR,
message);
}
}
}
return ctx;
}
private ReplicationCliReturnCode purgeHistoricalRemotely(
PurgeHistoricalUserData uData)
{
// Connect to the provided server
InitialLdapContext ctx = createAdministrativeContext(uData);
if (ctx == null)
{
return ERROR_CONNECTING;
}
try
{
List<String> baseDNs = uData.getBaseDNs();
checkSuffixesForPurgeHistorical(baseDNs, ctx, false);
if (baseDNs.isEmpty())
{
return HISTORICAL_CANNOT_BE_PURGED_ON_BASEDN;
}
uData.setBaseDNs(baseDNs);
if (mustPrintCommandBuilder())
{
printNewCommandBuilder(PURGE_HISTORICAL_SUBCMD_NAME, uData);
}
try
{
return purgeHistoricalRemoteTask(ctx, uData);
}
catch (ReplicationCliException rce)
{
errPrintln();
errPrintln(getCriticalExceptionMessage(rce));
logger.error(LocalizableMessage.raw("Complete error stack:"), rce);
return rce.getErrorCode();
}
}
finally
{
close(ctx);
}
}
private ReplicationCliReturnCode resetChangeNumber()
{
final String changeNumber;
final SourceDestinationServerUserData uData = new SourceDestinationServerUserData();
if (!argParser.isInteractive())
{
initializeWithArgParser(uData);
return resetChangeNumber(uData);
}
OperationBetweenSourceAndDestinationServers
resetChangeNumberOperations = new OperationBetweenSourceAndDestinationServers()
{
@Override
public boolean continueAfterUserInput(Collection<String> baseDNs, InitialLdapContext source,
InitialLdapContext dest, boolean interactive)
{
TopologyCacheFilter filter = new TopologyCacheFilter();
filter.setSearchMonitoringInformation(false);
if (!argParser.resetChangeNumber.isPresent())
{
String cn = getNewestChangeNumber(source);
if (cn.isEmpty())
{
return true;
}
argParser.setResetChangeNumber(
ask(logger, INFO_RESET_CHANGE_NUMBER_TO.get(uData.getSource(), uData.getDestination()), cn));
}
return false;
}
@Override
public boolean confirmOperation(SourceDestinationServerUserData uData, InitialLdapContext ctxSource,
InitialLdapContext ctxDestination, boolean defaultValue)
{
return !askConfirmation(INFO_RESET_CHANGE_NUMBER_CONFIRM_RESET.get(uData.getDestinationHostPort()),
defaultValue);
}
};
return promptIfRequired(uData, resetChangeNumberOperations) ? resetChangeNumber(uData) : USER_CANCELLED;
}
private ReplicationCliReturnCode resetChangeNumber(SourceDestinationServerUserData uData)
{
InitialLdapContext ctxSource = createAdministrativeContext(uData, uData.getSource());
InitialLdapContext ctxDest = createAdministrativeContext(uData, uData.getDestination());
if (!getCommonSuffixes(ctxSource, ctxDest, SuffixRelationType.NOT_FULLY_REPLICATED).isEmpty())
{
errPrintln(ERROR_RESET_CHANGE_NUMBER_SERVERS_BASEDNS_DIFFER.get(uData.getSourceHostPort(),
uData.getDestinationHostPort()));
return ERROR_RESET_CHANGE_NUMBER_BASEDNS_SHOULD_EQUAL;
}
if (mustPrintCommandBuilder())
{
printNewCommandBuilder(RESET_CHANGE_NUMBER_SUBCMD_NAME, uData);
}
try
{
String newStartCN;
if (argParser.resetChangeNumber.isPresent())
{
newStartCN = String.valueOf(argParser.getResetChangeNumber());
}
else
{
newStartCN = getNewestChangeNumber(ctxSource);
if (newStartCN.isEmpty())
{
return ERROR_UNKNOWN_CHANGE_NUMBER;
}
argParser.setResetChangeNumber(newStartCN);
}
SearchControls ctls = new SearchControls();
ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
ctls.setReturningAttributes(
new String[] {
"changeNumber",
"replicationCSN",
"targetDN"
});
NamingEnumeration<SearchResult> listeners = ctxSource.search(new LdapName("cn=changelog"),
"(changeNumber=" + newStartCN + ")", ctls);
if (!listeners.hasMore())
{
errPrintln(ERROR_RESET_CHANGE_NUMBER_UNKNOWN_NUMBER.get(newStartCN, uData.getSourceHostPort()));
return ERROR_UNKNOWN_CHANGE_NUMBER;
}
SearchResult sr = listeners.next();
String newStartCSN = getFirstValue(sr, "replicationCSN");
if (newStartCSN == null)
{
errPrintln(ERROR_RESET_CHANGE_NUMBER_NO_CSN_FOUND.get(newStartCN, uData.getSourceHostPort()));
return ERROR_RESET_CHANGE_NUMBER_NO_CSN;
}
String targetDN = getFirstValue(sr, "targetDN");
DN targetBaseDN = DN.rootDN();
try
{
for (String adn : getCommonSuffixes(ctxSource, ctxDest, SuffixRelationType.REPLICATED))
{
DN dn = DN.valueOf(adn);
if (DN.valueOf(targetDN).isDescendantOf(dn) && dn.isDescendantOf(targetBaseDN))
{
targetBaseDN = dn;
}
}
}
catch (DirectoryException de)
{
errPrintln(ERROR_RESET_CHANGE_NUMBER_EXCEPTION.get(de.getLocalizedMessage()));
return ERROR_RESET_CHANGE_NUMBER_PROBLEM;
}
if (targetBaseDN.isRootDN())
{
errPrintln(ERROR_RESET_CHANGE_NUMBER_NO_BASEDN.get(newStartCN, targetDN, newStartCSN));
return ERROR_RESET_CHANGE_NUMBER_UNKNOWN_BASEDN;
}
logger.info(INFO_RESET_CHANGE_NUMBER_INFO.get(uData.getDestinationHostPort(),
newStartCN, newStartCSN, targetBaseDN.toString()));
Map<String, String> taskAttrs = new TreeMap<>();
taskAttrs.put("ds-task-reset-change-number-to", newStartCN);
taskAttrs.put("ds-task-reset-change-number-csn", newStartCSN);
taskAttrs.put("ds-task-reset-change-number-base-dn", targetBaseDN.toString());
String taskDN = createServerTask(ctxDest,
"ds-task-reset-change-number", "org.opends.server.tasks.ResetChangeNumberTask", "dsreplication-reset-cn",
taskAttrs);
waitUntilResetChangeNumberTaskEnds(ctxDest, taskDN);
return SUCCESSFUL;
}
catch (ReplicationCliException | NamingException | NullPointerException e)
{
errPrintln(ERROR_RESET_CHANGE_NUMBER_EXCEPTION.get(e.getLocalizedMessage()));
return ERROR_RESET_CHANGE_NUMBER_PROBLEM;
}
}
private String getNewestChangeNumber(InitialLdapContext source)
{
try
{
SearchControls ctls = new SearchControls();
ctls.setSearchScope(SearchControls.OBJECT_SCOPE);
ctls.setReturningAttributes(new String[] {"lastChangeNumber"});
NamingEnumeration<SearchResult> results = source.search(new LdapName(""), "objectclass=*", ctls);
if (results.hasMore()) {
return getFirstValue(results.next(), "lastChangeNumber");
}
}
catch (NamingException e)
{
errPrintln(ERROR_RESET_CHANGE_NUMBER_EXCEPTION.get(e.getLocalizedMessage()));
}
return "";
}
private void waitUntilResetChangeNumberTaskEnds(InitialLdapContext server, String taskDN)
throws ReplicationCliException
{
String lastLogMsg = null;
while (true)
{
sleepCatchInterrupt(500);
try
{
SearchResult sr = getLastSearchResult(server, taskDN, "ds-task-log-message", "ds-task-state" );
String logMsg = getFirstValue(sr, "ds-task-log-message");
if (logMsg != null && !logMsg.equals(lastLogMsg))
{
logger.info(LocalizableMessage.raw(logMsg));
lastLogMsg = logMsg;
}
InstallerHelper helper = new InstallerHelper();
String state = getFirstValue(sr, "ds-task-state");
if (helper.isDone(state) || helper.isStoppedByError(state))
{
LocalizableMessage errorMsg = ERR_UNEXPECTED_DURING_TASK_WITH_LOG.get(lastLogMsg, state, server);
if (helper.isCompletedWithErrors(state))
{
logger.warn(LocalizableMessage.raw("Completed with error: " + errorMsg));
errPrintln(errorMsg);
}
else if (!helper.isSuccessful(state) || helper.isStoppedByError(state))
{
logger.warn(LocalizableMessage.raw("Error: " + errorMsg));
throw new ReplicationCliException(errorMsg, ERROR_LAUNCHING_RESET_CHANGE_NUMBER, null);
}
else
{
print(INFO_RESET_CHANGE_NUMBER_TASK_FINISHED.get());
println();
}
return;
}
}
catch (NameNotFoundException x)
{
return;
}
catch (NamingException ne)
{
throw new ReplicationCliException(getThrowableMsg(ERR_READING_SERVER_TASK_PROGRESS.get(), ne),
ERROR_CONNECTING, ne);
}
}
}
private InitialLdapContext createAdministrativeContext(MonoServerReplicationUserData uData)
{
final String bindDn = getAdministratorDN(uData.getAdminUid());
return createAdministrativeContext(uData, bindDn);
}
private InitialLdapContext createAdministrativeContext(MonoServerReplicationUserData uData, final String bindDn)
{
try
{
return createAdministrativeContext(uData.getHostName(), uData.getPort(),
useSSL, useStartTLS, bindDn,
uData.getAdminPwd(), getConnectTimeout(), getTrustManager(sourceServerCI));
}
catch (NamingException ne)
{
String hostPort = getServerRepresentation(uData.getHostName(), uData.getPort());
errPrintln();
errPrintln(getMessageForException(ne, hostPort));
logger.error(LocalizableMessage.raw("Complete error stack:"), ne);
return null;
}
}
private void printSuccessMessage(PurgeHistoricalUserData uData, String taskID)
{
println();
if (!uData.isOnline())
{
print(
INFO_PROGRESS_PURGE_HISTORICAL_FINISHED_PROCEDURE.get());
}
else if (uData.getTaskSchedule().isStartNow())
{
print(INFO_TASK_TOOL_TASK_SUCESSFULL.get(
INFO_PURGE_HISTORICAL_TASK_NAME.get(),
taskID));
}
else if (uData.getTaskSchedule().getStartDate() != null)
{
print(INFO_TASK_TOOL_TASK_SCHEDULED_FUTURE.get(
INFO_PURGE_HISTORICAL_TASK_NAME.get(),
taskID,
StaticUtils.formatDateTimeString(
uData.getTaskSchedule().getStartDate())));
}
else
{
print(INFO_TASK_TOOL_RECURRING_TASK_SCHEDULED.get(
INFO_PURGE_HISTORICAL_TASK_NAME.get(),
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.
*/
private ReplicationCliReturnCode purgeHistoricalRemoteTask(
InitialLdapContext ctx,
PurgeHistoricalUserData uData)
throws ReplicationCliException
{
printPurgeProgressMessage(uData);
ReplicationCliReturnCode returnCode = SUCCESSFUL;
boolean taskCreated = false;
boolean isOver = false;
String dn = null;
String taskID = null;
while (!taskCreated)
{
BasicAttributes attrs = PurgeHistoricalUserData.getTaskAttributes(uData);
dn = PurgeHistoricalUserData.getTaskDN(attrs);
taskID = PurgeHistoricalUserData.getTaskID(attrs);
try
{
DirContext dirCtx = ctx.createSubcontext(dn, attrs);
taskCreated = true;
logger.info(LocalizableMessage.raw("created task entry: "+attrs));
dirCtx.close();
}
catch (NamingException ne)
{
logger.error(LocalizableMessage.raw("Error creating task "+attrs, ne));
LocalizableMessage msg = ERR_LAUNCHING_PURGE_HISTORICAL.get();
ReplicationCliReturnCode code = ERROR_LAUNCHING_PURGE_HISTORICAL;
throw new ReplicationCliException(
getThrowableMsg(msg, ne), code, ne);
}
}
// Polling only makes sense when we are recurrently scheduling a task
// or the task is being executed now.
String lastLogMsg = null;
while (!isOver && uData.getTaskSchedule().getStartDate() == null)
{
sleepCatchInterrupt(500);
try
{
SearchResult sr = getFirstSearchResult(ctx, dn,
"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");
String logMsg = getFirstValue(sr, "ds-task-log-message");
if (logMsg != null && !logMsg.equals(lastLogMsg))
{
logger.info(LocalizableMessage.raw(logMsg));
lastLogMsg = logMsg;
}
InstallerHelper helper = new InstallerHelper();
String state = getFirstValue(sr, "ds-task-state");
if (helper.isDone(state) || helper.isStoppedByError(state))
{
isOver = true;
LocalizableMessage errorMsg = getPurgeErrorMsg(lastLogMsg, state, ctx);
if (helper.isCompletedWithErrors(state))
{
logger.warn(LocalizableMessage.raw("Completed with error: "+errorMsg));
errPrintln(errorMsg);
}
else if (!helper.isSuccessful(state) ||
helper.isStoppedByError(state))
{
logger.warn(LocalizableMessage.raw("Error: "+errorMsg));
ReplicationCliReturnCode code = ERROR_LAUNCHING_PURGE_HISTORICAL;
throw new ReplicationCliException(errorMsg, code, null);
}
}
}
catch (NameNotFoundException x)
{
isOver = true;
}
catch (NamingException ne)
{
LocalizableMessage msg = ERR_READING_SERVER_TASK_PROGRESS.get();
throw new ReplicationCliException(
getThrowableMsg(msg, ne), ERROR_CONNECTING, ne);
}
}
if (returnCode == SUCCESSFUL)
{
printSuccessMessage(uData, taskID);
}
return returnCode;
}
private SearchResult getFirstSearchResult(InitialLdapContext ctx, String dn, String... returnedAttributes)
throws NamingException
{
SearchControls searchControls = new SearchControls();
searchControls.setCountLimit(1);
searchControls.setSearchScope(SearchControls.OBJECT_SCOPE);
searchControls.setReturningAttributes(returnedAttributes);
NamingEnumeration<SearchResult> res = ctx.search(dn, "objectclass=*", searchControls);
try
{
SearchResult sr = null;
sr = res.next();
return sr;
}
finally
{
res.close();
}
}
private LocalizableMessage getPurgeErrorMsg(String lastLogMsg, String state, InitialLdapContext ctx)
{
String server = getHostPort(ctx);
if (lastLogMsg != null)
{
return ERR_UNEXPECTED_DURING_TASK_WITH_LOG.get(lastLogMsg, state, server);
}
return ERR_UNEXPECTED_DURING_TASK_NO_LOG.get(state, server);
}
/**
* 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.
*/
private void checkSuffixesForPurgeHistorical(Collection<String> suffixes,
InitialLdapContext ctx, boolean interactive)
{
checkSuffixesForPurgeHistorical(suffixes, getReplicas(ctx), interactive);
}
/**
* 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.
*/
private void checkSuffixesForLocalPurgeHistorical(Collection<String> suffixes,
boolean interactive)
{
checkSuffixesForPurgeHistorical(suffixes, getLocalReplicas(), interactive);
}
private Collection<ReplicaDescriptor> getLocalReplicas()
{
Collection<ReplicaDescriptor> replicas = new ArrayList<>();
ConfigFromFile configFromFile = new ConfigFromFile();
configFromFile.readConfiguration();
Collection<BackendDescriptor> backends = configFromFile.getBackends();
for (BackendDescriptor backend : backends)
{
for (BaseDNDescriptor baseDN : backend.getBaseDns())
{
SuffixDescriptor suffix = new SuffixDescriptor();
suffix.setDN(baseDN.getDn().toString());
ReplicaDescriptor replica = new ReplicaDescriptor();
if (baseDN.getType() == BaseDNDescriptor.Type.REPLICATED)
{
replica.setReplicationId(baseDN.getReplicaID());
}
else
{
replica.setReplicationId(-1);
}
replica.setBackendName(backend.getBackendID());
replica.setSuffix(suffix);
suffix.setReplicas(singleton(replica));
replicas.add(replica);
}
}
return replicas;
}
private void checkSuffixesForPurgeHistorical(Collection<String> suffixes, Collection<ReplicaDescriptor> replicas,
boolean interactive)
{
TreeSet<String> availableSuffixes = new TreeSet<>();
TreeSet<String> notReplicatedSuffixes = new TreeSet<>();
for (ReplicaDescriptor rep : replicas)
{
String dn = rep.getSuffix().getDN();
if (rep.isReplicated())
{
availableSuffixes.add(dn);
}
else
{
notReplicatedSuffixes.add(dn);
}
}
checkSuffixesForPurgeHistorical(suffixes, availableSuffixes, notReplicatedSuffixes, interactive);
}
private void checkSuffixesForPurgeHistorical(Collection<String> suffixes,
Collection<String> availableSuffixes,
Collection<String> notReplicatedSuffixes,
boolean interactive)
{
if (availableSuffixes.isEmpty())
{
errPrintln();
errPrintln(ERR_NO_SUFFIXES_AVAILABLE_TO_PURGE_HISTORICAL.get());
suffixes.clear();
}
else
{
// Verify that the provided suffixes are configured in the servers.
TreeSet<String> notFound = new TreeSet<>();
TreeSet<String> alreadyNotReplicated = new TreeSet<>();
for (String dn : suffixes)
{
if (!containsDN(availableSuffixes, dn))
{
if (containsDN(notReplicatedSuffixes, dn))
{
alreadyNotReplicated.add(dn);
}
else
{
notFound.add(dn);
}
}
}
suffixes.removeAll(notFound);
suffixes.removeAll(alreadyNotReplicated);
if (!notFound.isEmpty())
{
errPrintln();
errPrintln(ERR_REPLICATION_PURGE_SUFFIXES_NOT_FOUND.get(toSingleLine(notFound)));
}
if (interactive)
{
askConfirmations(suffixes, availableSuffixes,
ERR_NO_SUFFIXES_AVAILABLE_TO_PURGE_HISTORICAL,
ERR_NO_SUFFIXES_SELECTED_TO_PURGE_HISTORICAL,
INFO_REPLICATION_PURGE_HISTORICAL_PROMPT);
}
}
}
private void askConfirmations(Collection<String> suffixes,
Collection<String> availableSuffixes, Arg0 noSuffixAvailableMsg,
Arg0 noSuffixSelectedMsg, Arg1<Object> confirmationMsgPromt)
{
if (containsOnlySchemaOrAdminSuffix(availableSuffixes))
{
// In interactive mode we do not propose to manage the administration suffix.
errPrintln();
errPrintln(noSuffixAvailableMsg.get());
return;
}
while (suffixes.isEmpty())
{
errPrintln();
errPrintln(noSuffixSelectedMsg.get());
boolean confirmationLimitReached = askConfirmations(confirmationMsgPromt, availableSuffixes, suffixes);
if (confirmationLimitReached)
{
suffixes.clear();
break;
}
}
}
private boolean containsOnlySchemaOrAdminSuffix(Collection<String> suffixes)
{
for (String suffix : suffixes)
{
if (!isSchemaOrInternalAdminSuffix(suffix))
{
return false;
}
}
return true;
}
private boolean isSchemaOrInternalAdminSuffix(String suffix)
{
return areDnsEqual(suffix, ADSContext.getAdministrationSuffixDN())
|| areDnsEqual(suffix, Constants.SCHEMA_DN)
|| areDnsEqual(suffix, Constants.REPLICATION_CHANGES_DN);
}
/**
* 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.
*/
private ReplicationCliReturnCode initializeReplication()
{
SourceDestinationServerUserData uData = new SourceDestinationServerUserData();
if (!argParser.isInteractive())
{
initializeWithArgParser(uData);
return initializeReplication(uData);
}
OperationBetweenSourceAndDestinationServers
initializeReplicationOperations = new OperationBetweenSourceAndDestinationServers()
{
@Override
public boolean continueAfterUserInput(Collection<String> baseDNs, InitialLdapContext source,
InitialLdapContext dest, boolean interactive)
{
checkSuffixesForInitializeReplication(baseDNs, source, dest, interactive);
return baseDNs.isEmpty();
}
@Override
public boolean confirmOperation(SourceDestinationServerUserData uData, InitialLdapContext ctxSource,
InitialLdapContext ctxDestination, boolean defaultValue)
{
return !askConfirmation(getInitializeReplicationPrompt(uData, ctxSource, ctxDestination), defaultValue);
}
};
return promptIfRequired(uData, initializeReplicationOperations) ? initializeReplication(uData) : USER_CANCELLED;
}
/**
* 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.
*/
private boolean promptIfRequired(PurgeHistoricalUserData uData)
{
InitialLdapContext ctx = null;
try
{
ctx = getInitialLdapContext(uData);
if (ctx == null)
{
return false;
}
/* Prompt for maximum duration */
int maximumDuration = argParser.getMaximumDuration();
if (!argParser.maximumDurationArg.isPresent())
{
println();
maximumDuration = askInteger(INFO_REPLICATION_PURGE_HISTORICAL_MAXIMUM_DURATION_PROMPT.get(),
getDefaultValue(argParser.maximumDurationArg), logger);
}
uData.setMaximumDuration(maximumDuration);
LinkedList<String> suffixes = argParser.getBaseDNs();
if (uData.isOnline())
{
checkSuffixesForPurgeHistorical(suffixes, ctx, true);
}
else
{
checkSuffixesForLocalPurgeHistorical(suffixes, true);
}
if (suffixes.isEmpty())
{
return false;
}
uData.setBaseDNs(suffixes);
if (uData.isOnline())
{
List<? extends TaskEntry> taskEntries = getAvailableTaskEntries(ctx);
TaskScheduleInteraction interaction =
new TaskScheduleInteraction(uData.getTaskSchedule(), argParser.taskArgs, this,
INFO_PURGE_HISTORICAL_TASK_NAME.get());
interaction.setFormatter(formatter);
interaction.setTaskEntries(taskEntries);
try
{
interaction.run();
}
catch (ClientException ce)
{
errPrintln(ce.getMessageObject());
return false;
}
}
return true;
}
finally
{
close(ctx);
}
}
private InitialLdapContext getInitialLdapContext(PurgeHistoricalUserData uData)
{
boolean firstTry = true;
Boolean serverRunning = null;
while (true)
{
boolean promptForConnection = firstTry && argParser.connectionArgumentsPresent();
if (!promptForConnection)
{
if (serverRunning == null)
{
serverRunning = Utilities.isServerRunning(Installation.getLocal().getInstanceDirectory());
}
if (!serverRunning)
{
try
{
println();
promptForConnection = !askConfirmation(
INFO_REPLICATION_PURGE_HISTORICAL_LOCAL_PROMPT.get(), true, logger);
}
catch (ClientException ce)
{
errPrintln(ce.getMessageObject());
}
if (!promptForConnection)
{
uData.setOnline(false);
return null;
}
}
}
try
{
sourceServerCI.run();
InitialLdapContext ctx = createInitialLdapContextInteracting(sourceServerCI);
if (ctx != null)
{
uData.setOnline(true);
uData.setHostName(sourceServerCI.getHostName());
uData.setPort(sourceServerCI.getPortNumber());
uData.setAdminUid(sourceServerCI.getAdministratorUID());
uData.setAdminPwd(sourceServerCI.getBindPassword());
}
return ctx;
}
catch (ClientException ce)
{
logger.warn(LocalizableMessage.raw("Client exception " + ce));
errPrintln();
errPrintln(ce.getMessageObject());
errPrintln();
sourceServerCI.resetConnectionArguments();
}
catch (ArgumentException ae)
{
logger.warn(LocalizableMessage.raw("Argument exception " + ae));
argParser.displayMessageAndUsageReference(getErrStream(), ae.getMessageObject());
return null;
}
firstTry = false;
}
}
private List<? extends TaskEntry> getAvailableTaskEntries(
InitialLdapContext ctx)
{
List<TaskEntry> taskEntries = new ArrayList<>();
List<OpenDsException> exceptions = new ArrayList<>();
ConfigFromDirContext cfg = new ConfigFromDirContext();
cfg.updateTaskInformation(ctx, exceptions, taskEntries);
for (OpenDsException ode : exceptions)
{
logger.warn(LocalizableMessage.raw("Error retrieving task entries: "+ode, ode));
}
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.
*/
private boolean promptIfRequired(EnableReplicationUserData uData)
throws ReplicationCliException
{
boolean cancelled = false;
boolean administratorDefined = false;
sourceServerCI.setUseAdminOrBindDn(true);
String adminPwd = argParser.getBindPasswordAdmin();
String adminUid = argParser.getAdministratorUID();
/* Try to connect to the first server. */
String host1 = getValue(argParser.server1.hostNameArg);
int port1 = getValue(argParser.server1.portArg);
String bindDn1 = getValue(argParser.server1.bindDnArg);
String pwd1 = argParser.server1.getBindPassword();
String pwd = null;
Map<String, String> pwdFile = null;
if (argParser.server1.bindPasswordArg.isPresent())
{
pwd = argParser.server1.bindPasswordArg.getValue();
}
else if (argParser.server1.bindPasswordFileArg.isPresent())
{
pwdFile = argParser.server1.bindPasswordFileArg.getNameToValueMap();
}
else if (bindDn1 == null)
{
pwd = adminPwd;
if (argParser.getSecureArgsList().bindPasswordFileArg.isPresent())
{
pwdFile = argParser.getSecureArgsList().bindPasswordFileArg.
getNameToValueMap();
}
}
/*
* Use a copy of the argument properties since the map might be cleared
* in initializeGlobalArguments.
*/
sourceServerCI.initializeGlobalArguments(host1, port1, adminUid, bindDn1, pwd,
pwdFile == null ? null : new LinkedHashMap<String, String>(pwdFile));
InitialLdapContext ctx1 = null;
while (ctx1 == null && !cancelled)
{
try
{
sourceServerCI.setHeadingMessage(INFO_REPLICATION_ENABLE_HOST1_CONNECTION_PARAMETERS.get());
sourceServerCI.run();
host1 = sourceServerCI.getHostName();
port1 = sourceServerCI.getPortNumber();
if (sourceServerCI.getProvidedAdminUID() != null)
{
adminUid = sourceServerCI.getProvidedAdminUID();
if (sourceServerCI.getProvidedBindDN() == null)
{
// 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.
adminPwd = sourceServerCI.getBindPassword();
}
}
bindDn1 = sourceServerCI.getBindDN();
pwd1 = sourceServerCI.getBindPassword();
ctx1 = createInitialLdapContextInteracting(sourceServerCI);
if (ctx1 == null)
{
cancelled = true;
}
}
catch (ClientException ce)
{
logger.warn(LocalizableMessage.raw("Client exception "+ce));
errPrintln();
errPrintln(ce.getMessageObject());
errPrintln();
sourceServerCI.resetConnectionArguments();
}
catch (ArgumentException ae)
{
logger.warn(LocalizableMessage.raw("Argument exception "+ae));
argParser.displayMessageAndUsageReference(getErrStream(), ae.getMessageObject());
cancelled = true;
}
}
if (!cancelled)
{
uData.getServer1().setHostName(host1);
uData.getServer1().setPort(port1);
uData.getServer1().setBindDn(bindDn1);
uData.getServer1().setPwd(pwd1);
}
int replicationPort1 = -1;
boolean secureReplication1 = argParser.server1.secureReplicationArg.isPresent();
boolean configureReplicationServer1 = argParser.server1.configureReplicationServer();
boolean configureReplicationDomain1 = argParser.server1.configureReplicationDomain();
if (ctx1 != null)
{
int repPort1 = getReplicationPort(ctx1);
boolean replicationServer1Configured = repPort1 > 0;
if (replicationServer1Configured && !configureReplicationServer1)
{
final LocalizableMessage msg =
INFO_REPLICATION_SERVER_CONFIGURED_WARNING_PROMPT.get(getHostPort(ctx1), repPort1);
if (!askConfirmation(msg, false))
{
cancelled = true;
}
}
// Try to get the replication port for server 1 only if it is required.
if (!cancelled
&& configureReplicationServer1
&& !replicationServer1Configured
&& argParser.advancedArg.isPresent()
&& configureReplicationDomain1)
{
// Only ask if the replication domain will be configured (if not
// the replication server MUST be configured).
try
{
configureReplicationServer1 = askConfirmation(
INFO_REPLICATION_ENABLE_REPLICATION_SERVER1_PROMPT.get(),
true, logger);
}
catch (ClientException ce)
{
errPrintln(ce.getMessageObject());
cancelled = true;
}
}
if (!cancelled
&& configureReplicationServer1
&& !replicationServer1Configured)
{
boolean tryWithDefault = argParser.getReplicationPort1() != -1;
while (replicationPort1 == -1)
{
if (tryWithDefault)
{
replicationPort1 = argParser.getReplicationPort1();
tryWithDefault = false;
}
else
{
replicationPort1 = askPort(
INFO_REPLICATION_ENABLE_REPLICATIONPORT1_PROMPT.get(),
getDefaultValue(argParser.server1.replicationPortArg), logger);
println();
}
if (!argParser.skipReplicationPortCheck() && isLocalHost(host1))
{
if (!SetupUtils.canUseAsPort(replicationPort1))
{
errPrintln();
errPrintln(getCannotBindToPortError(replicationPort1));
errPrintln();
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
errPrintln();
errPrintln(ERR_REPLICATION_PORT_AND_REPLICATION_PORT_EQUAL.get(host1, replicationPort1));
errPrintln();
replicationPort1 = -1;
}
}
if (!secureReplication1)
{
try
{
secureReplication1 =
askConfirmation(INFO_REPLICATION_ENABLE_SECURE1_PROMPT.get(replicationPort1),
false, logger);
}
catch (ClientException ce)
{
errPrintln(ce.getMessageObject());
cancelled = true;
}
println();
}
}
if (!cancelled &&
configureReplicationDomain1 &&
configureReplicationServer1 &&
argParser.advancedArg.isPresent())
{
// Only necessary to ask if the replication server will be configured
try
{
configureReplicationDomain1 = askConfirmation(
INFO_REPLICATION_ENABLE_REPLICATION_DOMAIN1_PROMPT.get(),
true, logger);
}
catch (ClientException ce)
{
errPrintln(ce.getMessageObject());
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)
{
AtomicReference<InitialLdapContext> aux = new AtomicReference<>(ctx1);
cancelled = !loadADSAndAcceptCertificates(sourceServerCI, aux, uData, true);
ctx1 = aux.get();
}
if (!cancelled)
{
administratorDefined |= hasAdministrator(ctx1);
if (uData.getAdminPwd() != null)
{
adminPwd = uData.getAdminPwd();
}
}
}
uData.getServer1().setReplicationPort(replicationPort1);
uData.getServer1().setSecureReplication(secureReplication1);
uData.getServer1().setConfigureReplicationServer(configureReplicationServer1);
uData.getServer1().setConfigureReplicationDomain(configureReplicationDomain1);
firstServerCommandBuilder = new CommandBuilder(null, null);
if (mustPrintCommandBuilder())
{
firstServerCommandBuilder.append(sourceServerCI.getCommandBuilder());
}
/* Prompt for information on the second server. */
String host2 = null;
int port2 = -1;
String bindDn2 = null;
String pwd2 = null;
LDAPConnectionConsoleInteraction destinationServerCI = new LDAPConnectionConsoleInteraction(this,
argParser.getSecureArgsList());
destinationServerCI.resetHeadingDisplayed();
boolean doNotDisplayFirstError = false;
if (!cancelled)
{
host2 = getValue(argParser.server2.hostNameArg);
port2 = getValue(argParser.server2.portArg);
bindDn2 = getValue(argParser.server2.bindDnArg);
pwd2 = argParser.server2.getBindPassword();
pwdFile = null;
pwd = null;
if (argParser.server2.bindPasswordArg.isPresent())
{
pwd = argParser.server2.bindPasswordArg.getValue();
}
else if (argParser.server2.bindPasswordFileArg.isPresent())
{
pwdFile = argParser.server2.bindPasswordFileArg.getNameToValueMap();
}
else if (bindDn2 == null)
{
doNotDisplayFirstError = true;
pwd = adminPwd;
if (argParser.getSecureArgsList().bindPasswordFileArg.isPresent())
{
pwdFile = argParser.getSecureArgsList().bindPasswordFileArg.
getNameToValueMap();
}
}
/*
* Use a copy of the argument properties since the map might be cleared
* in initializeGlobalArguments.
*/
destinationServerCI.initializeGlobalArguments(host2, port2, adminUid, bindDn2, pwd,
pwdFile == null ? null : new LinkedHashMap<String, String>(pwdFile));
destinationServerCI.setUseAdminOrBindDn(true);
}
InitialLdapContext ctx2 = null;
while (ctx2 == null && !cancelled)
{
try
{
destinationServerCI.setHeadingMessage(INFO_REPLICATION_ENABLE_HOST2_CONNECTION_PARAMETERS.get());
destinationServerCI.run();
host2 = destinationServerCI.getHostName();
port2 = destinationServerCI.getPortNumber();
if (destinationServerCI.getProvidedAdminUID() != null)
{
adminUid = destinationServerCI.getProvidedAdminUID();
if (destinationServerCI.getProvidedBindDN() == null)
{
// 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.
adminPwd = destinationServerCI.getBindPassword();
}
}
bindDn2 = destinationServerCI.getBindDN();
pwd2 = destinationServerCI.getBindPassword();
boolean error = false;
if (host1.equalsIgnoreCase(host2) && port1 == port2)
{
port2 = -1;
errPrintln();
errPrintln(ERR_REPLICATION_ENABLE_SAME_SERVER_PORT.get(host1, port1));
errPrintln();
error = true;
}
if (!error)
{
ctx2 = createInitialLdapContextInteracting(destinationServerCI, true);
if (ctx2 == null)
{
cancelled = true;
}
}
}
catch (ClientException ce)
{
logger.warn(LocalizableMessage.raw("Client exception "+ce));
if (!doNotDisplayFirstError)
{
errPrintln();
errPrintln(ce.getMessageObject());
errPrintln();
destinationServerCI.resetConnectionArguments();
}
else
{
// Reset only the credential parameters.
destinationServerCI.resetConnectionArguments();
destinationServerCI.initializeGlobalArguments(host2, port2, null, null, null, null);
}
}
catch (ArgumentException ae)
{
logger.warn(LocalizableMessage.raw("Argument exception "+ae));
argParser.displayMessageAndUsageReference(getErrStream(), ae.getMessageObject());
cancelled = true;
}
finally
{
doNotDisplayFirstError = false;
}
}
if (!cancelled)
{
uData.getServer2().setHostName(host2);
uData.getServer2().setPort(port2);
uData.getServer2().setBindDn(bindDn2);
uData.getServer2().setPwd(pwd2);
}
int replicationPort2 = -1;
boolean secureReplication2 = argParser.server2.secureReplicationArg.isPresent();
boolean configureReplicationServer2 = argParser.server2.configureReplicationServer();
boolean configureReplicationDomain2 = argParser.server2.configureReplicationDomain();
if (ctx2 != null)
{
int repPort2 = getReplicationPort(ctx2);
boolean replicationServer2Configured = repPort2 > 0;
if (replicationServer2Configured && !configureReplicationServer2)
{
final LocalizableMessage prompt =
INFO_REPLICATION_SERVER_CONFIGURED_WARNING_PROMPT.get(getHostPort(ctx2), repPort2);
if (!askConfirmation(prompt, false))
{
cancelled = true;
}
}
// Try to get the replication port for server 2 only if it is required.
if (!cancelled
&& configureReplicationServer2
&& !replicationServer2Configured)
{
// Only ask if the replication domain will be configured (if not the
// replication server MUST be configured).
if (argParser.advancedArg.isPresent() &&
configureReplicationDomain2)
{
try
{
configureReplicationServer2 = askConfirmation(
INFO_REPLICATION_ENABLE_REPLICATION_SERVER2_PROMPT.get(),
true, logger);
}
catch (ClientException ce)
{
errPrintln(ce.getMessageObject());
cancelled = true;
}
}
if (!cancelled
&& configureReplicationServer2
&& !replicationServer2Configured)
{
boolean tryWithDefault = argParser.getReplicationPort2() != -1;
while (replicationPort2 == -1)
{
if (tryWithDefault)
{
replicationPort2 = argParser.getReplicationPort2();
tryWithDefault = false;
}
else
{
replicationPort2 = askPort(
INFO_REPLICATION_ENABLE_REPLICATIONPORT2_PROMPT.get(),
getDefaultValue(argParser.server2.replicationPortArg), logger);
println();
}
if (!argParser.skipReplicationPortCheck() &&
isLocalHost(host2))
{
if (!SetupUtils.canUseAsPort(replicationPort2))
{
errPrintln();
errPrintln(getCannotBindToPortError(replicationPort2));
errPrintln();
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
errPrintln();
errPrintln(ERR_REPLICATION_PORT_AND_REPLICATION_PORT_EQUAL.get(host2, replicationPort2));
replicationPort2 = -1;
}
if (host1.equalsIgnoreCase(host2)
&& replicationPort1 > 0
&& replicationPort1 == replicationPort2)
{
errPrintln();
errPrintln(ERR_REPLICATION_SAME_REPLICATION_PORT.get(replicationPort2, host1));
errPrintln();
replicationPort2 = -1;
}
}
if (!secureReplication2)
{
try
{
secureReplication2 =
askConfirmation(INFO_REPLICATION_ENABLE_SECURE2_PROMPT.get(replicationPort2), false, logger);
}
catch (ClientException ce)
{
errPrintln(ce.getMessageObject());
cancelled = true;
}
println();
}
}
}
if (!cancelled &&
configureReplicationDomain2 &&
configureReplicationServer2 &&
argParser.advancedArg.isPresent())
{
// Only necessary to ask if the replication server will be configured
try
{
configureReplicationDomain2 = askConfirmation(
INFO_REPLICATION_ENABLE_REPLICATION_DOMAIN2_PROMPT.get(),
true, logger);
}
catch (ClientException ce)
{
errPrintln(ce.getMessageObject());
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)
{
AtomicReference<InitialLdapContext> aux = new AtomicReference<>(ctx2);
cancelled = !loadADSAndAcceptCertificates(destinationServerCI, aux, uData, false);
ctx2 = aux.get();
}
if (!cancelled)
{
administratorDefined |= hasAdministrator(ctx2);
}
}
uData.getServer2().setReplicationPort(replicationPort2);
uData.getServer2().setSecureReplication(secureReplication2);
uData.getServer2().setConfigureReplicationServer(configureReplicationServer2);
uData.getServer2().setConfigureReplicationDomain(configureReplicationDomain2);
// 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).
if (!cancelled && uData.getAdminUid() == null && !administratorDefined)
{
if (adminUid == null)
{
println(INFO_REPLICATION_ENABLE_ADMINISTRATOR_MUST_BE_CREATED.get());
promptedForAdmin = true;
adminUid= askForAdministratorUID(
getDefaultValue(argParser.getAdminUidArg()), logger);
println();
}
uData.setAdminUid(adminUid);
}
if (uData.getAdminPwd() == null)
{
uData.setAdminPwd(adminPwd);
}
if (!cancelled && uData.getAdminPwd() == null && !administratorDefined)
{
adminPwd = null;
int nPasswordPrompts = 0;
while (adminPwd == null)
{
if (nPasswordPrompts > CONFIRMATION_MAX_TRIES)
{
errPrintln(ERR_CONFIRMATION_TRIES_LIMIT_REACHED.get(
CONFIRMATION_MAX_TRIES));
cancelled = true;
break;
}
nPasswordPrompts ++;
if (!promptedForAdmin)
{
println();
println(INFO_REPLICATION_ENABLE_ADMINISTRATOR_MUST_BE_CREATED.get());
println();
}
while (adminPwd == null)
{
adminPwd = askForAdministratorPwd(logger);
println();
}
String adminPwdConfirm = null;
while (adminPwdConfirm == null)
{
try
{
adminPwdConfirm = String.valueOf(readPassword(INFO_ADMINISTRATOR_PWD_CONFIRM_PROMPT.get()));
}
catch (ClientException ex)
{
logger.warn(LocalizableMessage.raw("Error reading input: " + ex, ex));
}
println();
}
if (!adminPwd.equals(adminPwdConfirm))
{
println();
errPrintln(ERR_ADMINISTRATOR_PWD_DO_NOT_MATCH.get());
println();
adminPwd = null;
}
}
uData.setAdminPwd(adminPwd);
}
if (!cancelled)
{
LinkedList<String> suffixes = argParser.getBaseDNs();
checkSuffixesForEnableReplication(suffixes, ctx1, ctx2, true, uData);
cancelled = suffixes.isEmpty();
uData.setBaseDNs(suffixes);
}
close(ctx1, ctx2);
uData.setReplicateSchema(!argParser.noSchemaReplication());
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.
*/
private boolean promptIfRequired(DisableReplicationUserData uData)
throws ReplicationCliException
{
boolean cancelled = false;
String adminPwd = argParser.getBindPasswordAdmin();
String adminUid = argParser.getAdministratorUID();
String bindDn = argParser.getBindDNToDisable();
// This is done because we want to ask explicitly for this
String host = argParser.getHostNameToDisable();
int port = argParser.getPortToDisable();
/* Try to connect to the server. */
InitialLdapContext ctx = null;
while (ctx == null && !cancelled)
{
try
{
sourceServerCI.setUseAdminOrBindDn(true);
sourceServerCI.run();
host = sourceServerCI.getHostName();
port = sourceServerCI.getPortNumber();
bindDn = sourceServerCI.getProvidedBindDN();
adminUid = sourceServerCI.getProvidedAdminUID();
adminPwd = sourceServerCI.getBindPassword();
ctx = createInitialLdapContextInteracting(sourceServerCI);
if (ctx == null)
{
cancelled = true;
}
}
catch (ClientException ce)
{
logger.warn(LocalizableMessage.raw("Client exception "+ce));
errPrintln();
errPrintln(ce.getMessageObject());
errPrintln();
sourceServerCI.resetConnectionArguments();
}
catch (ArgumentException ae)
{
logger.warn(LocalizableMessage.raw("Argument exception "+ae));
argParser.displayMessageAndUsageReference(getErrStream(), ae.getMessageObject());
cancelled = true;
}
}
if (!cancelled)
{
uData.setHostName(host);
uData.setPort(port);
uData.setAdminUid(adminUid);
uData.setBindDn(bindDn);
uData.setAdminPwd(adminPwd);
}
if (ctx != null && adminUid != null)
{
// 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.
AtomicReference<InitialLdapContext> aux = new AtomicReference<>(ctx);
cancelled = !loadADSAndAcceptCertificates(sourceServerCI, aux, uData, false);
ctx = aux.get();
}
boolean disableAll = argParser.disableAllArg.isPresent();
boolean disableReplicationServer =
argParser.disableReplicationServerArg.isPresent();
if (disableAll ||
(argParser.advancedArg.isPresent() &&
argParser.getBaseDNs().isEmpty() &&
!disableReplicationServer))
{
try
{
disableAll = askConfirmation(INFO_REPLICATION_PROMPT_DISABLE_ALL.get(),
disableAll, logger);
}
catch (ClientException ce)
{
errPrintln(ce.getMessageObject());
cancelled = true;
}
}
int repPort = getReplicationPort(ctx);
if (!disableAll
&& (argParser.advancedArg.isPresent() || disableReplicationServer)
&& repPort > 0)
{
try
{
disableReplicationServer = askConfirmation(
INFO_REPLICATION_PROMPT_DISABLE_REPLICATION_SERVER.get(repPort),
disableReplicationServer,
logger);
}
catch (ClientException ce)
{
errPrintln(ce.getMessageObject());
cancelled = true;
}
}
if (disableReplicationServer && repPort < 0)
{
disableReplicationServer = false;
final LocalizableMessage msg = INFO_REPLICATION_PROMPT_NO_REPLICATION_SERVER_TO_DISABLE.get(getHostPort(ctx));
try
{
cancelled = askConfirmation(msg, false, logger);
}
catch (ClientException ce)
{
errPrintln(ce.getMessageObject());
cancelled = true;
}
}
if (repPort > 0 && disableAll)
{
disableReplicationServer = true;
}
uData.setDisableAll(disableAll);
uData.setDisableReplicationServer(disableReplicationServer);
if (!cancelled && !disableAll)
{
LinkedList<String> suffixes = argParser.getBaseDNs();
checkSuffixesForDisableReplication(suffixes, ctx, true, !disableReplicationServer);
cancelled = suffixes.isEmpty() && !disableReplicationServer;
uData.setBaseDNs(suffixes);
if (!uData.disableReplicationServer() && repPort > 0 &&
disableAllBaseDns(ctx, uData) && !argParser.advancedArg.isPresent())
{
try
{
uData.setDisableReplicationServer(askConfirmation(
INFO_REPLICATION_DISABLE_ALL_SUFFIXES_DISABLE_REPLICATION_SERVER.get(getHostPort(ctx), repPort), true,
logger));
}
catch (ClientException ce)
{
errPrintln(ce.getMessageObject());
cancelled = true;
}
}
}
if (!cancelled)
{
// Ask for confirmation to disable if not already done.
boolean disableADS = false;
boolean disableSchema = false;
for (String dn : uData.getBaseDNs())
{
if (areDnsEqual(ADSContext.getAdministrationSuffixDN(), dn))
{
disableADS = true;
}
else if (areDnsEqual(Constants.SCHEMA_DN, dn))
{
disableSchema = true;
}
}
if (disableADS)
{
println();
LocalizableMessage msg = INFO_REPLICATION_CONFIRM_DISABLE_ADS.get(ADSContext.getAdministrationSuffixDN());
cancelled = !askConfirmation(msg, true);
println();
}
if (disableSchema)
{
println();
LocalizableMessage msg = INFO_REPLICATION_CONFIRM_DISABLE_SCHEMA.get();
cancelled = !askConfirmation(msg, true);
println();
}
if (!disableSchema && !disableADS)
{
println();
if (!uData.disableAll() && !uData.getBaseDNs().isEmpty())
{
cancelled = !askConfirmation(INFO_REPLICATION_CONFIRM_DISABLE_GENERIC.get(), true);
}
println();
}
}
close(ctx);
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.
*/
private boolean promptIfRequired(InitializeAllReplicationUserData uData)
{
InitialLdapContext ctx = null;
try
{
ctx = getInitialLdapContext(uData);
if (ctx == null)
{
return false;
}
LinkedList<String> suffixes = argParser.getBaseDNs();
checkSuffixesForInitializeReplication(suffixes, ctx, true);
if (suffixes.isEmpty())
{
return false;
}
uData.setBaseDNs(suffixes);
// Ask for confirmation to initialize.
println();
if (!askConfirmation(getPrompt(uData, ctx), true))
{
return false;
}
println();
return true;
}
finally
{
close(ctx);
}
}
private LocalizableMessage getPrompt(InitializeAllReplicationUserData uData, InitialLdapContext ctx)
{
String hostPortSource = getHostPort(ctx);
if (initializeADS(uData.getBaseDNs()))
{
return INFO_REPLICATION_CONFIRM_INITIALIZE_ALL_ADS.get(ADSContext.getAdministrationSuffixDN(), hostPortSource);
}
return INFO_REPLICATION_CONFIRM_INITIALIZE_ALL_GENERIC.get(hostPortSource);
}
private boolean askConfirmation(final LocalizableMessage msg, final boolean defaultValue)
{
try
{
return askConfirmation(msg, defaultValue, logger);
}
catch (ClientException ce)
{
errPrintln(ce.getMessageObject());
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.
*/
private boolean promptIfRequiredForPreOrPost(MonoServerReplicationUserData uData)
{
InitialLdapContext ctx = null;
try
{
ctx = getInitialLdapContext(uData);
if (ctx == null)
{
return false;
}
LinkedList<String> suffixes = argParser.getBaseDNs();
checkSuffixesForInitializeReplication(suffixes, ctx, true);
uData.setBaseDNs(suffixes);
return !suffixes.isEmpty();
}
finally
{
close(ctx);
}
}
private InitialLdapContext getInitialLdapContext(MonoServerReplicationUserData uData)
{
// Try to connect to the server.
while (true)
{
try
{
if (uData instanceof InitializeAllReplicationUserData)
{
sourceServerCI.setHeadingMessage(INFO_INITIALIZE_SOURCE_CONNECTION_PARAMETERS.get());
}
sourceServerCI.run();
InitialLdapContext ctx = createInitialLdapContextInteracting(sourceServerCI);
if (ctx != null)
{
uData.setHostName(sourceServerCI.getHostName());
uData.setPort(sourceServerCI.getPortNumber());
uData.setAdminUid(sourceServerCI.getAdministratorUID());
uData.setAdminPwd(sourceServerCI.getBindPassword());
if (uData instanceof StatusReplicationUserData)
{
((StatusReplicationUserData) uData).setScriptFriendly(argParser.isScriptFriendly());
}
}
return ctx;
}
catch (ClientException ce)
{
logger.warn(LocalizableMessage.raw("Client exception " + ce));
errPrintln();
errPrintln(ce.getMessageObject());
errPrintln();
sourceServerCI.resetConnectionArguments();
}
catch (ArgumentException ae)
{
logger.warn(LocalizableMessage.raw("Argument exception " + ae));
argParser.displayMessageAndUsageReference(getErrStream(), ae.getMessageObject());
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.
*/
private boolean promptIfRequired(StatusReplicationUserData uData)
throws ReplicationCliException
{
InitialLdapContext ctx = null;
try
{
ctx = getInitialLdapContext(uData);
if (ctx == null)
{
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.
AtomicReference<InitialLdapContext> aux = new AtomicReference<>(ctx);
boolean cancelled = !loadADSAndAcceptCertificates(sourceServerCI, aux, uData, false);
ctx = aux.get();
if (cancelled)
{
return false;
}
if (!cancelled)
{
uData.setBaseDNs(argParser.getBaseDNs());
}
return !cancelled;
}
finally
{
close(ctx);
}
}
/**
* 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.
* @param serversOperations Additional processing for the command
* @return <CODE>true</CODE> if the object was successfully updated and
* <CODE>false</CODE> if the user cancelled the operation.
*/
private boolean promptIfRequired(SourceDestinationServerUserData uData,
OperationBetweenSourceAndDestinationServers serversOperations)
{
boolean cancelled = false;
String adminPwd = argParser.getBindPasswordAdmin();
String adminUid = argParser.getAdministratorUID();
String hostSource = argParser.getHostNameSource();
int portSource = argParser.getPortSource();
Map<String, String> pwdFile = null;
if (argParser.getSecureArgsList().bindPasswordFileArg.isPresent())
{
pwdFile = argParser.getSecureArgsList().bindPasswordFileArg.getNameToValueMap();
}
/*
* Use a copy of the argument properties since the map might be cleared
* in initializeGlobalArguments.
*/
sourceServerCI.initializeGlobalArguments(hostSource, portSource, adminUid, null, adminPwd,
pwdFile == null ? null : new LinkedHashMap<String, String>(pwdFile));
/* Try to connect to the source server. */
InitialLdapContext ctxSource = null;
while (ctxSource == null && !cancelled)
{
try
{
sourceServerCI.setHeadingMessage(INFO_INITIALIZE_SOURCE_CONNECTION_PARAMETERS.get());
sourceServerCI.run();
hostSource = sourceServerCI.getHostName();
portSource = sourceServerCI.getPortNumber();
adminUid = sourceServerCI.getAdministratorUID();
adminPwd = sourceServerCI.getBindPassword();
ctxSource = createInitialLdapContextInteracting(sourceServerCI);
if (ctxSource == null)
{
cancelled = true;
}
}
catch (ClientException ce)
{
logger.warn(LocalizableMessage.raw("Client exception "+ce));
errPrintln();
errPrintln(ce.getMessageObject());
errPrintln();
sourceServerCI.resetConnectionArguments();
}
catch (ArgumentException ae)
{
logger.warn(LocalizableMessage.raw("Argument exception "+ae));
argParser.displayMessageAndUsageReference(getErrStream(), ae.getMessageObject());
cancelled = true;
}
}
if (!cancelled)
{
uData.setHostNameSource(hostSource);
uData.setPortSource(portSource);
uData.setAdminUid(adminUid);
uData.setAdminPwd(adminPwd);
}
firstServerCommandBuilder = new CommandBuilder(null, null);
if (mustPrintCommandBuilder())
{
firstServerCommandBuilder.append(sourceServerCI.getCommandBuilder());
}
/* Prompt for destination server credentials */
String hostDestination = argParser.getHostNameDestination();
int portDestination = argParser.getPortDestination();
/*
* Use a copy of the argument properties since the map might be cleared
* in initializeGlobalArguments.
*/
LDAPConnectionConsoleInteraction destinationServerCI = new LDAPConnectionConsoleInteraction(this,
argParser.getSecureArgsList());
destinationServerCI.initializeGlobalArguments(hostDestination, portDestination, adminUid, null, adminPwd,
pwdFile == null ? null : new LinkedHashMap<String, String>(pwdFile));
/* Try to connect to the destination server. */
InitialLdapContext ctxDestination = null;
destinationServerCI.resetHeadingDisplayed();
while (ctxDestination == null && !cancelled)
{
try
{
destinationServerCI.setHeadingMessage(INFO_INITIALIZE_DESTINATION_CONNECTION_PARAMETERS.get());
destinationServerCI.run();
hostDestination = destinationServerCI.getHostName();
portDestination = destinationServerCI.getPortNumber();
boolean error = false;
if (hostSource.equalsIgnoreCase(hostDestination)
&& portSource == portDestination)
{
portDestination = -1;
errPrintln();
errPrintln(ERR_SOURCE_DESTINATION_INITIALIZE_SAME_SERVER_PORT.get(hostSource, portSource));
errPrintln();
error = true;
}
if (!error)
{
ctxDestination = createInitialLdapContextInteracting(destinationServerCI, true);
if (ctxDestination == null)
{
cancelled = true;
}
}
}
catch (ClientException ce)
{
logger.warn(LocalizableMessage.raw("Client exception "+ce));
errPrintln();
errPrintln(ce.getMessageObject());
errPrintln();
destinationServerCI.resetConnectionArguments();
}
catch (ArgumentException ae)
{
logger.warn(LocalizableMessage.raw("Argument exception "+ae));
argParser.displayMessageAndUsageReference(getErrStream(), ae.getMessageObject());
cancelled = true;
}
}
if (!cancelled)
{
uData.setHostNameDestination(hostDestination);
uData.setPortDestination(portDestination);
}
if (!cancelled)
{
LinkedList<String> suffixes = argParser.getBaseDNs();
cancelled = serversOperations.continueAfterUserInput(suffixes, ctxSource, ctxDestination, true);
uData.setBaseDNs(suffixes);
}
if (!cancelled)
{
println();
cancelled = serversOperations.confirmOperation(uData, ctxSource, ctxDestination, true);
println();
}
close(ctxSource, ctxDestination);
return !cancelled;
}
private LocalizableMessage getInitializeReplicationPrompt(SourceDestinationServerUserData uData,
InitialLdapContext ctxSource, InitialLdapContext ctxDestination)
{
String hostPortSource = getHostPort(ctxSource);
String hostPortDestination = getHostPort(ctxDestination);
if (initializeADS(uData.getBaseDNs()))
{
final String adminSuffixDN = ADSContext.getAdministrationSuffixDN();
return INFO_REPLICATION_CONFIRM_INITIALIZE_ADS.get(adminSuffixDN, hostPortDestination, hostPortSource);
}
return INFO_REPLICATION_CONFIRM_INITIALIZE_GENERIC.get(hostPortDestination, hostPortSource);
}
private boolean initializeADS(List<String> baseDNs)
{
for (String dn : baseDNs)
{
if (areDnsEqual(ADSContext.getAdministrationSuffixDN(), dn))
{
return true;
}
}
return false;
}
/**
* Returns the trust manager to be used by this application.
* @param ci the LDAP connection to the server
* @return the trust manager to be used by this application.
*/
private ApplicationTrustManager getTrustManager(LDAPConnectionConsoleInteraction ci)
{
return isInteractive() ? ci.getTrustManager() : argParser.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.
*/
private void initializeWithArgParser(EnableReplicationUserData uData)
{
initialize(uData);
final String adminDN = getAdministratorDN(uData.getAdminUid());
final String adminPwd = uData.getAdminPwd();
setConnectionDetails(uData.getServer1(), argParser.server1, adminDN, adminPwd);
setConnectionDetails(uData.getServer2(), argParser.server2, adminDN, adminPwd);
uData.setReplicateSchema(!argParser.noSchemaReplication());
setReplicationDetails(uData.getServer1(), argParser.server1);
setReplicationDetails(uData.getServer2(), argParser.server2);
}
private void setConnectionDetails(
EnableReplicationServerData server, ServerArgs args, String adminDN, String adminPwd)
{
server.setHostName(getValueOrDefault(args.hostNameArg));
server.setPort(getValueOrDefault(args.portArg));
String pwd = args.getBindPassword();
if (pwd == null)
{
server.setBindDn(adminDN);
server.setPwd(adminPwd);
}
else
{
// Best-effort: try to use admin, if it does not work, use bind DN.
try
{
InitialLdapContext ctx = createAdministrativeContext(server.getHostName(), server.getPort(),
useSSL, useStartTLS, adminDN, adminPwd, getConnectTimeout(), getTrustManager(sourceServerCI));
server.setBindDn(adminDN);
server.setPwd(adminPwd);
ctx.close();
}
catch (Throwable t)
{
server.setBindDn(getValueOrDefault(args.bindDnArg));
server.setPwd(pwd);
}
}
}
private void setReplicationDetails(EnableReplicationServerData server, ServerArgs args)
{
server.setSecureReplication(args.secureReplicationArg.isPresent());
server.setConfigureReplicationDomain(args.configureReplicationDomain());
server.setConfigureReplicationServer(args.configureReplicationServer());
if (server.configureReplicationServer())
{
server.setReplicationPort(getValueOrDefault(args.replicationPortArg));
}
}
/**
* 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.
*/
private void initializeWithArgParser(SourceDestinationServerUserData uData)
{
initialize(uData);
uData.setHostNameSource(argParser.getHostNameSourceOrDefault());
uData.setPortSource(argParser.getPortSourceOrDefault());
uData.setHostNameDestination(argParser.getHostNameDestinationOrDefault());
uData.setPortDestination(argParser.getPortDestinationOrDefault());
}
/**
* 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.
*/
private void initializeWithArgParser(DisableReplicationUserData uData)
{
uData.setBaseDNs(new LinkedList<String>(argParser.getBaseDNs()));
String adminUid = argParser.getAdministratorUID();
String bindDn = argParser.getBindDNToDisable();
if (bindDn == null && adminUid == null)
{
adminUid = argParser.getAdministratorUIDOrDefault();
bindDn = getAdministratorDN(adminUid);
}
uData.setAdminUid(adminUid);
uData.setBindDn(bindDn);
uData.setAdminPwd(argParser.getBindPasswordAdmin());
uData.setHostName(argParser.getHostNameToDisableOrDefault());
uData.setPort(argParser.getPortToDisableOrDefault());
uData.setDisableAll(argParser.disableAllArg.isPresent());
uData.setDisableReplicationServer(argParser.disableReplicationServerArg.isPresent());
}
/**
* 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.
*/
private void initializeWithArgParser(MonoServerReplicationUserData uData)
{
initialize(uData);
uData.setHostName(argParser.getHostNameToInitializeAllOrDefault());
uData.setPort(argParser.getPortToInitializeAllOrDefault());
}
/**
* 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.
*/
private void initializeWithArgParser(StatusReplicationUserData uData)
{
initialize(uData);
uData.setHostName(argParser.getHostNameToStatusOrDefault());
uData.setPort(argParser.getPortToStatusOrDefault());
uData.setScriptFriendly(argParser.isScriptFriendly());
}
private void initialize(ReplicationUserData uData)
{
uData.setBaseDNs(new LinkedList<String>(argParser.getBaseDNs()));
uData.setAdminUid(argParser.getAdministratorUIDOrDefault());
uData.setAdminPwd(argParser.getBindPasswordAdmin());
}
/**
* 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.
*/
private boolean hasReplicationPort(InitialLdapContext ctx)
{
return getReplicationPort(ctx) != -1;
}
/**
* 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.
*/
private int getReplicationPort(InitialLdapContext ctx)
{
int replicationPort = -1;
try
{
ManagementContext mCtx = LDAPManagementContext.createFromContext(
JNDIDirContextAdaptor.adapt(ctx));
RootCfgClient root = mCtx.getRootConfiguration();
ReplicationSynchronizationProviderCfgClient sync =
(ReplicationSynchronizationProviderCfgClient)
root.getSynchronizationProvider("Multimaster Synchronization");
if (sync.hasReplicationServer())
{
ReplicationServerCfgClient replicationServer =
sync.getReplicationServer();
replicationPort = replicationServer.getReplicationPort();
}
}
catch (Throwable t)
{
logger.warn(LocalizableMessage.raw("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 ci the LDAP connection to the server
* @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.
*/
private boolean loadADSAndAcceptCertificates(LDAPConnectionConsoleInteraction ci,
AtomicReference<InitialLdapContext> ctx, ReplicationUserData uData, boolean isFirstOrSourceServer)
throws ReplicationCliException
{
boolean cancelled = false;
boolean triedWithUserProvidedAdmin = false;
final InitialLdapContext ctx1 = ctx.get();
String host = getHostName(ctx1);
int port = getPort(ctx1);
boolean isSSL = isSSL(ctx1);
boolean isStartTLS = isStartTLS(ctx1);
if (getTrustManager(ci) == 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.
forceTrustManagerInitialization(ci);
}
try
{
ADSContext adsContext = new ADSContext(ctx1);
if (adsContext.hasAdminData())
{
boolean reloadTopology = true;
LinkedList<LocalizableMessage> exceptionMsgs = new LinkedList<>();
while (reloadTopology && !cancelled)
{
// We must recreate the cache because the trust manager in the
// LDAPConnectionConsoleInteraction object might have changed.
TopologyCache cache = new TopologyCache(adsContext,
getTrustManager(ci), getConnectTimeout());
cache.getFilter().setSearchMonitoringInformation(false);
cache.getFilter().setSearchBaseDNInformation(false);
cache.setPreferredConnections(getPreferredConnections(ctx1));
cache.reloadTopology();
reloadTopology = false;
exceptionMsgs.clear();
/* 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.
*/
Set<TopologyCacheException> exceptions = new HashSet<>();
Set<ServerDescriptor> servers = cache.getServers();
for (ServerDescriptor server : servers)
{
TopologyCacheException e = server.getLastException();
if (e != null)
{
exceptions.add(e);
}
}
/* Check the exceptions and see if we throw them or not. */
boolean notGlobalAdministratorError = false;
for (TopologyCacheException e : exceptions)
{
if (notGlobalAdministratorError)
{
break;
}
switch (e.getType())
{
case NOT_GLOBAL_ADMINISTRATOR:
notGlobalAdministratorError = true;
boolean connected = false;
String adminUid = uData.getAdminUid();
String adminPwd = uData.getAdminPwd();
boolean errorDisplayed = false;
while (!connected)
{
if (!triedWithUserProvidedAdmin && adminPwd == null)
{
adminUid = argParser.getAdministratorUIDOrDefault();
adminPwd = argParser.getBindPasswordAdmin();
triedWithUserProvidedAdmin = true;
}
if (adminPwd == null)
{
if (!errorDisplayed)
{
println();
println(
INFO_NOT_GLOBAL_ADMINISTRATOR_PROVIDED.get());
errorDisplayed = true;
}
adminUid = askForAdministratorUID(
getDefaultValue(argParser.getAdminUidArg()), logger);
println();
adminPwd = askForAdministratorPwd(logger);
println();
}
close(ctx1);
try
{
final InitialLdapContext ctx2 = createAdministrativeContext(host, port, isSSL,
isStartTLS, getAdministratorDN(adminUid),
adminPwd, getConnectTimeout(), getTrustManager(ci));
ctx.set(ctx2);
adsContext = new ADSContext(ctx2);
cache = new TopologyCache(adsContext, getTrustManager(ci),
getConnectTimeout());
cache.getFilter().setSearchMonitoringInformation(false);
cache.getFilter().setSearchBaseDNInformation(false);
cache.setPreferredConnections(getPreferredConnections(ctx2));
connected = true;
}
catch (Throwable t)
{
errPrintln();
errPrintln(
ERR_ERROR_CONNECTING_TO_SERVER_PROMPT_AGAIN.get(
getServerRepresentation(host, port), t.getMessage()));
logger.warn(LocalizableMessage.raw("Complete error stack:", t));
errPrintln();
}
}
uData.setAdminUid(adminUid);
uData.setAdminPwd(adminPwd);
if (uData instanceof EnableReplicationUserData)
{
EnableReplicationUserData enableData = (EnableReplicationUserData) uData;
EnableReplicationServerData server =
isFirstOrSourceServer ? enableData.getServer1() : enableData.getServer2();
server.setBindDn(getAdministratorDN(adminUid));
server.setPwd(adminPwd);
}
reloadTopology = true;
break;
case GENERIC_CREATING_CONNECTION:
if (isCertificateException(e.getCause()))
{
reloadTopology = true;
cancelled = !ci.promptForCertificateConfirmation(e.getCause(),
e.getTrustManager(), e.getLdapUrl(), logger);
}
else
{
exceptionMsgs.add(getMessage(e));
}
break;
default:
exceptionMsgs.add(getMessage(e));
}
}
}
if (!exceptionMsgs.isEmpty() && !cancelled)
{
if (uData instanceof StatusReplicationUserData)
{
errPrintln(
ERR_REPLICATION_STATUS_READING_REGISTERED_SERVERS.get(
getMessageFromCollection(exceptionMsgs,
Constants.LINE_SEPARATOR)));
errPrintln();
}
else
{
LocalizableMessage msg = ERR_REPLICATION_READING_REGISTERED_SERVERS_CONFIRM_UPDATE_REMOTE.get(
getMessageFromCollection(exceptionMsgs, Constants.LINE_SEPARATOR));
cancelled = !askConfirmation(msg, true);
}
}
}
}
catch (ADSContextException ace)
{
logger.error(LocalizableMessage.raw("Complete error stack:"), ace);
throw new ReplicationCliException(
ERR_REPLICATION_READING_ADS.get(ace.getMessage()),
ERROR_READING_ADS, ace);
}
catch (TopologyCacheException tce)
{
logger.error(LocalizableMessage.raw("Complete error stack:"), tce);
throw new ReplicationCliException(
ERR_REPLICATION_READING_ADS.get(tce.getMessage()),
ERROR_READING_TOPOLOGY_CACHE, tce);
}
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.
*/
private boolean hasAdministrator(InitialLdapContext ctx)
{
try
{
ADSContext adsContext = new ADSContext(ctx);
if (adsContext.hasAdminData())
{
Set<?> administrators = adsContext.readAdministratorRegistry();
return !administrators.isEmpty();
}
}
catch (Throwable t)
{
logger.warn(LocalizableMessage.raw(
"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.
*/
private boolean hasAdministrator(InitialLdapContext ctx,
ReplicationUserData uData)
{
String adminUid = uData.getAdminUid();
try
{
ADSContext adsContext = new ADSContext(ctx);
Set<Map<AdministratorProperty, Object>> administrators =
adsContext.readAdministratorRegistry();
for (Map<AdministratorProperty, Object> admin : administrators)
{
String uid = (String)admin.get(AdministratorProperty.UID);
// If the administrator UID is null it means that we are just
// checking for the existence of an administrator
if (uid != null && (uid.equalsIgnoreCase(adminUid) || adminUid == null))
{
return true;
}
}
}
catch (Throwable t)
{
logger.warn(LocalizableMessage.raw(
"Unexpected error retrieving the ADS data: "+t, t));
}
return false;
}
/** Helper type for the {@link #getCommonSuffixes(InitialLdapContext, InitialLdapContext, SuffixRelationType)}. */
private enum SuffixRelationType
{
NOT_REPLICATED, FULLY_REPLICATED, REPLICATED, NOT_FULLY_REPLICATED, ALL
}
/**
* 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.
*/
private List<String> getCommonSuffixes(InitialLdapContext ctx1, InitialLdapContext ctx2, SuffixRelationType type)
{
LinkedList<String> suffixes = new LinkedList<>();
try
{
TopologyCacheFilter filter = new TopologyCacheFilter();
filter.setSearchMonitoringInformation(false);
ServerDescriptor server1 = ServerDescriptor.createStandalone(ctx1, filter);
ServerDescriptor server2 = ServerDescriptor.createStandalone(ctx2, filter);
for (ReplicaDescriptor rep1 : server1.getReplicas())
{
for (ReplicaDescriptor rep2 : server2.getReplicas())
{
String rep1SuffixDN = rep1.getSuffix().getDN();
String rep2SuffixDN = rep2.getSuffix().getDN();
boolean areDnsEqual = areDnsEqual(rep1SuffixDN, rep2SuffixDN);
switch (type)
{
case NOT_REPLICATED:
if (!areReplicated(rep1, rep2) && areDnsEqual)
{
suffixes.add(rep1SuffixDN);
}
break;
case FULLY_REPLICATED:
if (areFullyReplicated(rep1, rep2))
{
suffixes.add(rep1SuffixDN);
}
break;
case REPLICATED:
if (areReplicated(rep1, rep2))
{
suffixes.add(rep1SuffixDN);
}
break;
case NOT_FULLY_REPLICATED:
if (!areFullyReplicated(rep1, rep2) && areDnsEqual)
{
suffixes.add(rep1SuffixDN);
}
break;
case ALL:
if (areDnsEqual)
{
suffixes.add(rep1SuffixDN);
}
break;
default:
throw new IllegalStateException("Unknown type: "+type);
}
}
}
}
catch (Throwable t)
{
logger.warn(LocalizableMessage.raw(
"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.
*/
private boolean areFullyReplicated(ReplicaDescriptor rep1,
ReplicaDescriptor rep2)
{
if (areDnsEqual(rep1.getSuffix().getDN(), rep2.getSuffix().getDN()) &&
rep1.isReplicated() && rep2.isReplicated() &&
rep1.getServer().isReplicationServer() &&
rep2.getServer().isReplicationServer())
{
Set<String> servers1 = rep1.getReplicationServers();
Set<String> servers2 = rep2.getReplicationServers();
String server1 = rep1.getServer().getReplicationServerHostPort();
String server2 = rep2.getServer().getReplicationServerHostPort();
return servers1.contains(server2) && servers2.contains(server1);
}
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.
*/
private boolean areReplicated(ReplicaDescriptor rep1, ReplicaDescriptor rep2)
{
if (areDnsEqual(rep1.getSuffix().getDN(), rep2.getSuffix().getDN()) &&
rep1.isReplicated() && rep2.isReplicated())
{
Set<String> servers1 = rep1.getReplicationServers();
Set<String> servers2 = rep2.getReplicationServers();
servers1.retainAll(servers2);
return !servers1.isEmpty();
}
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.
*/
private Collection<ReplicaDescriptor> getReplicas(InitialLdapContext ctx)
{
LinkedList<ReplicaDescriptor> suffixes = new LinkedList<>();
TopologyCacheFilter filter = new TopologyCacheFilter();
filter.setSearchMonitoringInformation(false);
try
{
ServerDescriptor server = ServerDescriptor.createStandalone(ctx, filter);
suffixes.addAll(server.getReplicas());
}
catch (Throwable t)
{
logger.warn(LocalizableMessage.raw(
"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.
*/
private ReplicationCliReturnCode enableReplication(EnableReplicationUserData uData)
{
InitialLdapContext ctx1 = null;
InitialLdapContext ctx2 = null;
try
{
println();
print(formatter.getFormattedWithPoints(INFO_REPLICATION_CONNECTING.get()));
LinkedList<LocalizableMessage> errorMessages = new LinkedList<>();
ctx1 = createAdministrativeContext(uData, true, errorMessages);
ctx2 = createAdministrativeContext(uData, false, errorMessages);
if (!errorMessages.isEmpty())
{
errPrintLn(errorMessages);
return ERROR_CONNECTING;
}
// This done is for the message informing that we are connecting.
print(formatter.getFormattedDone());
println();
if (!argParser.isInteractive())
{
checksForNonInteractiveMode(uData, ctx1, ctx2, errorMessages);
if (!errorMessages.isEmpty())
{
errPrintLn(errorMessages);
return ERROR_USER_DATA;
}
}
List<String> suffixes = uData.getBaseDNs();
checkSuffixesForEnableReplication(suffixes, ctx1, ctx2, false, uData);
if (suffixes.isEmpty())
{
// The error messages are already displayed in the method
// checkSuffixesForEnableReplication.
return REPLICATION_CANNOT_BE_ENABLED_ON_BASEDN;
}
uData.setBaseDNs(suffixes);
if (mustPrintCommandBuilder())
{
printNewCommandBuilder(ENABLE_REPLICATION_SUBCMD_NAME, uData);
}
if (!isInteractive())
{
checkReplicationServerAlreadyConfigured(ctx1, uData.getServer1());
checkReplicationServerAlreadyConfigured(ctx2, uData.getServer2());
}
try
{
updateConfiguration(ctx1, ctx2, uData);
printSuccessfullyEnabled(ctx1, ctx2);
return SUCCESSFUL;
}
catch (ReplicationCliException rce)
{
errPrintln();
errPrintln(getCriticalExceptionMessage(rce));
logger.error(LocalizableMessage.raw("Complete error stack:"), rce);
return rce.getErrorCode();
}
}
finally
{
close(ctx1, ctx2);
}
}
private void checkReplicationServerAlreadyConfigured(InitialLdapContext ctx, EnableReplicationServerData server)
{
int repPort = getReplicationPort(ctx);
if (!server.configureReplicationServer() && repPort > 0)
{
println(INFO_REPLICATION_SERVER_CONFIGURED_WARNING.get(getHostPort(ctx), repPort));
println();
}
}
private void checksForNonInteractiveMode(EnableReplicationUserData uData,
InitialLdapContext ctx1, InitialLdapContext ctx2, LinkedList<LocalizableMessage> errorMessages)
{
EnableReplicationServerData server1 = uData.getServer1();
EnableReplicationServerData server2 = uData.getServer2();
String host1 = server1.getHostName();
String host2 = server2.getHostName();
int replPort1 = checkReplicationPort(ctx1, server1, errorMessages);
int replPort2 = checkReplicationPort(ctx2, server2, errorMessages);
if (replPort1 > 0 && replPort1 == replPort2 && host1.equalsIgnoreCase(host2))
{
errorMessages.add(ERR_REPLICATION_SAME_REPLICATION_PORT.get(replPort1, host1));
}
if (argParser.skipReplicationPortCheck())
{
// This is something that we must do in any case... this test is
// already included when we call SetupUtils.canUseAsPort
checkAdminAndReplicationPortsAreDifferent(replPort1, server1, errorMessages);
checkAdminAndReplicationPortsAreDifferent(replPort2, server2, errorMessages);
}
}
private int checkReplicationPort(
InitialLdapContext ctx, EnableReplicationServerData server, LinkedList<LocalizableMessage> errorMessages)
{
int replPort = getReplicationPort(ctx);
boolean hasReplicationPort = replPort > 0;
if (replPort < 0 && server.configureReplicationServer())
{
replPort = server.getReplicationPort();
}
boolean checkReplicationPort = replPort > 0;
if (!hasReplicationPort
&& checkReplicationPort
&& !argParser.skipReplicationPortCheck()
&& server.configureReplicationServer()
&& isLocalHost(server.getHostName())
&& !SetupUtils.canUseAsPort(replPort))
{
errorMessages.add(getCannotBindToPortError(replPort));
}
return replPort;
}
private void checkAdminAndReplicationPortsAreDifferent(
int replPort, EnableReplicationServerData server, LinkedList<LocalizableMessage> errorMessages)
{
if (replPort > 0 && replPort == server.getPort())
{
errorMessages.add(ERR_REPLICATION_PORT_AND_REPLICATION_PORT_EQUAL.get(server.getHostName(), replPort));
}
}
private void printSuccessfullyEnabled(InitialLdapContext ctx1, InitialLdapContext ctx2)
{
long time1 = getServerClock(ctx1);
long time2 = getServerClock(ctx2);
if (time1 != -1
&& time2 != -1
&& Math.abs(time1 - time2) > Installer.THRESHOLD_CLOCK_DIFFERENCE_WARNING * 60 * 1000)
{
println(INFO_WARNING_SERVERS_CLOCK_DIFFERENCE.get(getHostPort(ctx1), getHostPort(ctx2),
Installer.THRESHOLD_CLOCK_DIFFERENCE_WARNING));
}
println();
println(INFO_REPLICATION_POST_ENABLE_INFO.get("dsreplication", INITIALIZE_REPLICATION_SUBCMD_NAME));
println();
}
private void errPrintLn(LinkedList<LocalizableMessage> errorMessages)
{
for (LocalizableMessage msg : errorMessages)
{
errPrintln();
errPrintln(msg);
}
}
private InitialLdapContext createAdministrativeContext(EnableReplicationUserData uData, boolean isFirstSetOfValues,
LinkedList<LocalizableMessage> errorMessages)
{
EnableReplicationServerData server = isFirstSetOfValues ? uData.getServer1() : uData.getServer2();
try
{
return createAdministrativeContext(
server.getHostName(), server.getPort(), useSSL, useStartTLS, server.getBindDn(), server.getPwd(),
getConnectTimeout(), getTrustManager(sourceServerCI));
}
catch (NamingException ne)
{
String hostPort = getServerRepresentation(server.getHostName(), server.getPort());
errorMessages.add(getMessageForException(ne, hostPort));
logger.error(LocalizableMessage.raw("Complete error stack:"), 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.
*/
private ReplicationCliReturnCode disableReplication(DisableReplicationUserData uData)
{
print(formatter.getFormattedWithPoints(INFO_REPLICATION_CONNECTING.get()));
String bindDn = uData.getAdminUid() != null
? getAdministratorDN(uData.getAdminUid())
: uData.getBindDn();
InitialLdapContext ctx = createAdministrativeContext(uData, bindDn);
if (ctx == null)
{
return ERROR_CONNECTING;
}
try
{
// This done is for the message informing that we are connecting.
print(formatter.getFormattedDone());
println();
List<String> suffixes = uData.getBaseDNs();
checkSuffixesForDisableReplication(suffixes, ctx, false, !uData.disableReplicationServer());
if (suffixes.isEmpty() && !uData.disableReplicationServer() && !uData.disableAll())
{
return REPLICATION_CANNOT_BE_DISABLED_ON_BASEDN;
}
uData.setBaseDNs(suffixes);
if (!isInteractive())
{
boolean hasReplicationPort = hasReplicationPort(ctx);
if (uData.disableAll() && hasReplicationPort)
{
uData.setDisableReplicationServer(true);
}
else if (uData.disableReplicationServer() && !hasReplicationPort && !uData.disableAll())
{
uData.setDisableReplicationServer(false);
println(INFO_REPLICATION_WARNING_NO_REPLICATION_SERVER_TO_DISABLE.get(getHostPort(ctx)));
println();
}
}
if (mustPrintCommandBuilder())
{
printNewCommandBuilder(DISABLE_REPLICATION_SUBCMD_NAME, uData);
}
if (!isInteractive() && !uData.disableReplicationServer() && !uData.disableAll() && disableAllBaseDns(ctx, uData)
&& hasReplicationPort(ctx))
{
// Inform the user that the replication server will not be disabled.
// Inform also of the user of the disableReplicationServerArg
println(INFO_REPLICATION_DISABLE_ALL_SUFFIXES_KEEP_REPLICATION_SERVER.get(getHostPort(ctx),
argParser.disableReplicationServerArg.getLongIdentifier(), argParser.disableAllArg.getLongIdentifier()));
}
try
{
updateConfiguration(ctx, uData);
return SUCCESSFUL;
}
catch (ReplicationCliException rce)
{
errPrintln();
errPrintln(getCriticalExceptionMessage(rce));
logger.error(LocalizableMessage.raw("Complete error stack:"), rce);
return rce.getErrorCode();
}
}
finally
{
close(ctx);
}
}
/**
* 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.
*/
private ReplicationCliReturnCode statusReplication(
StatusReplicationUserData uData)
{
final InitialLdapContext ctx = createAdministrativeContext(uData);
if (ctx == null)
{
return ERROR_CONNECTING;
}
try
{
try
{
displayStatus(ctx, uData);
return SUCCESSFUL;
}
catch (ReplicationCliException rce)
{
errPrintln();
errPrintln(getCriticalExceptionMessage(rce));
logger.error(LocalizableMessage.raw("Complete error stack:"), rce);
return rce.getErrorCode();
}
}
finally
{
close(ctx);
}
}
/**
* 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.
*/
private ReplicationCliReturnCode initializeReplication(SourceDestinationServerUserData uData)
{
InitialLdapContext ctxSource = createAdministrativeContext(uData, uData.getSource());
InitialLdapContext ctxDestination = createAdministrativeContext(uData, uData.getDestination());
try
{
if (ctxSource == null || ctxDestination == null)
{
return ERROR_CONNECTING;
}
List<String> baseDNs = uData.getBaseDNs();
checkSuffixesForInitializeReplication(baseDNs, ctxSource, ctxDestination, false);
if (baseDNs.isEmpty())
{
return REPLICATION_CANNOT_BE_INITIALIZED_ON_BASEDN;
}
if (mustPrintCommandBuilder())
{
uData.setBaseDNs(baseDNs);
printNewCommandBuilder(INITIALIZE_REPLICATION_SUBCMD_NAME, uData);
}
ReplicationCliReturnCode returnValue = SUCCESSFUL_NOP;
for (String baseDN : baseDNs)
{
try
{
println();
print(formatter.getFormattedProgress(INFO_PROGRESS_INITIALIZING_SUFFIX.get(baseDN, getHostPort(ctxSource))));
println();
initializeSuffix(baseDN, ctxSource, ctxDestination, true);
returnValue = SUCCESSFUL;
}
catch (ReplicationCliException rce)
{
errPrintln();
errPrintln(getCriticalExceptionMessage(rce));
returnValue = rce.getErrorCode();
logger.error(LocalizableMessage.raw("Complete error stack:"), rce);
}
}
return returnValue;
}
finally
{
close(ctxDestination, ctxSource);
}
}
private InitialLdapContext createAdministrativeContext(SourceDestinationServerUserData uData, HostPort server)
{
try
{
return createAdministrativeContext(
server.getHost(), server.getPort(), useSSL, useStartTLS,
getAdministratorDN(uData.getAdminUid()), uData.getAdminPwd(),
getConnectTimeout(), getTrustManager(sourceServerCI));
}
catch (NamingException ne)
{
errPrintln();
errPrintln(getMessageForException(ne, server.toString()));
logger.error(LocalizableMessage.raw("Complete error stack:"), ne);
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.
*/
private ReplicationCliReturnCode initializeAllReplication(
InitializeAllReplicationUserData uData)
{
final InitialLdapContext ctx = createAdministrativeContext(uData);
if (ctx == null)
{
return ERROR_CONNECTING;
}
try
{
List<String> baseDNs = uData.getBaseDNs();
checkSuffixesForInitializeReplication(baseDNs, ctx, false);
if (baseDNs.isEmpty())
{
return REPLICATION_CANNOT_BE_INITIALIZED_ON_BASEDN;
}
if (mustPrintCommandBuilder())
{
uData.setBaseDNs(baseDNs);
printNewCommandBuilder(INITIALIZE_ALL_REPLICATION_SUBCMD_NAME, uData);
}
ReplicationCliReturnCode returnValue = SUCCESSFUL_NOP;
for (String baseDN : baseDNs)
{
try
{
println();
print(formatter.getFormattedProgress(INFO_PROGRESS_INITIALIZING_SUFFIX.get(baseDN, getHostPort(ctx))));
println();
initializeAllSuffix(baseDN, ctx, true);
returnValue = SUCCESSFUL;
}
catch (ReplicationCliException rce)
{
errPrintln();
errPrintln(getCriticalExceptionMessage(rce));
returnValue = rce.getErrorCode();
logger.error(LocalizableMessage.raw("Complete error stack:"), rce);
}
}
return returnValue;
}
finally
{
close(ctx);
}
}
/**
* 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.
*/
private ReplicationCliReturnCode preExternalInitialization(
PreExternalInitializationUserData uData)
{
InitialLdapContext ctx = createAdministrativeContext(uData);
if (ctx == null)
{
return ERROR_CONNECTING;
}
try
{
List<String> baseDNs = uData.getBaseDNs();
checkSuffixesForInitializeReplication(baseDNs, ctx, false);
if (baseDNs.isEmpty())
{
return REPLICATION_CANNOT_BE_INITIALIZED_ON_BASEDN;
}
if (mustPrintCommandBuilder())
{
uData.setBaseDNs(baseDNs);
printNewCommandBuilder(PRE_EXTERNAL_INITIALIZATION_SUBCMD_NAME, uData);
}
ReplicationCliReturnCode returnValue = SUCCESSFUL;
for (String baseDN : baseDNs)
{
try
{
println();
print(formatter.getFormattedWithPoints(INFO_PROGRESS_PRE_EXTERNAL_INITIALIZATION.get(baseDN)));
preExternalInitialization(baseDN, ctx);
print(formatter.getFormattedDone());
println();
}
catch (ReplicationCliException rce)
{
errPrintln();
errPrintln(getCriticalExceptionMessage(rce));
returnValue = rce.getErrorCode();
logger.error(LocalizableMessage.raw("Complete error stack:"), rce);
}
}
println();
print(INFO_PROGRESS_PRE_INITIALIZATION_FINISHED_PROCEDURE.get(POST_EXTERNAL_INITIALIZATION_SUBCMD_NAME));
println();
return returnValue;
}
finally
{
close(ctx);
}
}
/**
* 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.
*/
private ReplicationCliReturnCode postExternalInitialization(
PostExternalInitializationUserData uData)
{
InitialLdapContext ctx = createAdministrativeContext(uData);
if (ctx == null)
{
return ERROR_CONNECTING;
}
try
{
List<String> baseDNs = uData.getBaseDNs();
checkSuffixesForInitializeReplication(baseDNs, ctx, false);
if (baseDNs.isEmpty())
{
return REPLICATION_CANNOT_BE_INITIALIZED_ON_BASEDN;
}
if (mustPrintCommandBuilder())
{
uData.setBaseDNs(baseDNs);
printNewCommandBuilder(POST_EXTERNAL_INITIALIZATION_SUBCMD_NAME, uData);
}
ReplicationCliReturnCode returnValue = SUCCESSFUL;
for (String baseDN : baseDNs)
{
try
{
println();
print(formatter.getFormattedWithPoints(INFO_PROGRESS_POST_EXTERNAL_INITIALIZATION.get(baseDN)));
postExternalInitialization(baseDN, ctx);
println(formatter.getFormattedDone());
println();
}
catch (ReplicationCliException rce)
{
errPrintln();
errPrintln(getCriticalExceptionMessage(rce));
returnValue = rce.getErrorCode();
logger.error(LocalizableMessage.raw("Complete error stack:"), rce);
}
}
println();
print(INFO_PROGRESS_POST_INITIALIZATION_FINISHED_PROCEDURE.get());
println();
return returnValue;
}
finally
{
close(ctx);
}
}
/**
* 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.
*/
private void checkSuffixesForEnableReplication(Collection<String> suffixes,
InitialLdapContext ctx1, InitialLdapContext ctx2,
boolean interactive, EnableReplicationUserData uData)
{
EnableReplicationServerData server1 = uData.getServer1();
EnableReplicationServerData server2 = uData.getServer2();
final TreeSet<String> availableSuffixes = new TreeSet<>();
final TreeSet<String> alreadyReplicatedSuffixes = new TreeSet<>();
if (server1.configureReplicationDomain() &&
server2.configureReplicationDomain())
{
availableSuffixes.addAll(getCommonSuffixes(ctx1, ctx2,
SuffixRelationType.NOT_FULLY_REPLICATED));
alreadyReplicatedSuffixes.addAll(getCommonSuffixes(ctx1, ctx2,
SuffixRelationType.FULLY_REPLICATED));
}
else if (server1.configureReplicationDomain())
{
updateAvailableAndReplicatedSuffixesForOneDomain(ctx1, ctx2,
availableSuffixes, alreadyReplicatedSuffixes);
}
else if (server2.configureReplicationDomain())
{
updateAvailableAndReplicatedSuffixesForOneDomain(ctx2, ctx1,
availableSuffixes, alreadyReplicatedSuffixes);
}
else
{
updateAvailableAndReplicatedSuffixesForNoDomain(ctx1, ctx2,
availableSuffixes, alreadyReplicatedSuffixes);
}
if (availableSuffixes.isEmpty())
{
println();
if (!server1.configureReplicationDomain() &&
!server1.configureReplicationDomain() &&
alreadyReplicatedSuffixes.isEmpty())
{
// Use a clarifying message: there is no replicated base DN.
errPrintln(ERR_NO_SUFFIXES_AVAILABLE_TO_ENABLE_REPLICATION_NO_DOMAIN.get());
}
else
{
errPrintln(ERR_NO_SUFFIXES_AVAILABLE_TO_ENABLE_REPLICATION.get());
}
LinkedList<String> userProvidedSuffixes = argParser.getBaseDNs();
TreeSet<String> userProvidedReplicatedSuffixes = new TreeSet<>();
for (String s1 : userProvidedSuffixes)
{
for (String s2 : alreadyReplicatedSuffixes)
{
if (areDnsEqual(s1, s2))
{
userProvidedReplicatedSuffixes.add(s1);
}
}
}
if (!userProvidedReplicatedSuffixes.isEmpty())
{
println();
println(INFO_ALREADY_REPLICATED_SUFFIXES.get(toSingleLine(userProvidedReplicatedSuffixes)));
}
suffixes.clear();
}
else
{
// Verify that the provided suffixes are configured in the servers.
TreeSet<String> notFound = new TreeSet<>();
TreeSet<String> alreadyReplicated = new TreeSet<>();
for (String dn : suffixes)
{
if (!containsDN(availableSuffixes, dn))
{
if (containsDN(alreadyReplicatedSuffixes, dn))
{
alreadyReplicated.add(dn);
}
else
{
notFound.add(dn);
}
}
}
suffixes.removeAll(notFound);
suffixes.removeAll(alreadyReplicated);
if (!notFound.isEmpty())
{
errPrintln();
errPrintln(ERR_REPLICATION_ENABLE_SUFFIXES_NOT_FOUND.get(toSingleLine(notFound)));
}
if (!alreadyReplicated.isEmpty())
{
println();
println(INFO_ALREADY_REPLICATED_SUFFIXES.get(toSingleLine(alreadyReplicated)));
}
if (interactive)
{
askConfirmations(suffixes, availableSuffixes,
ERR_NO_SUFFIXES_AVAILABLE_TO_ENABLE_REPLICATION,
ERR_NO_SUFFIXES_SELECTED_TO_REPLICATE,
INFO_REPLICATION_ENABLE_SUFFIX_PROMPT);
}
}
}
/**
* 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.
*/
private void checkSuffixesForDisableReplication(Collection<String> suffixes,
InitialLdapContext ctx, boolean interactive, boolean displayErrors)
{
// whether the user must provide base DNs or not
// (if it is <CODE>false</CODE> the user will be proposed the suffixes only once)
final boolean areSuffixRequired = displayErrors;
TreeSet<String> availableSuffixes = new TreeSet<>();
TreeSet<String> notReplicatedSuffixes = new TreeSet<>();
Collection<ReplicaDescriptor> replicas = getReplicas(ctx);
for (ReplicaDescriptor rep : replicas)
{
String dn = rep.getSuffix().getDN();
if (rep.isReplicated())
{
availableSuffixes.add(dn);
}
else
{
notReplicatedSuffixes.add(dn);
}
}
if (availableSuffixes.isEmpty())
{
if (displayErrors)
{
errPrintln();
errPrintln(ERR_NO_SUFFIXES_AVAILABLE_TO_DISABLE_REPLICATION.get());
}
LinkedList<String> userProvidedSuffixes = argParser.getBaseDNs();
TreeSet<String> userProvidedNotReplicatedSuffixes = new TreeSet<>();
for (String s1 : userProvidedSuffixes)
{
for (String s2 : notReplicatedSuffixes)
{
if (areDnsEqual(s1, s2))
{
userProvidedNotReplicatedSuffixes.add(s1);
}
}
}
if (!userProvidedNotReplicatedSuffixes.isEmpty() && displayErrors)
{
println();
println(INFO_ALREADY_NOT_REPLICATED_SUFFIXES.get(
toSingleLine(userProvidedNotReplicatedSuffixes)));
}
suffixes.clear();
}
else
{
// Verify that the provided suffixes are configured in the servers.
TreeSet<String> notFound = new TreeSet<>();
TreeSet<String> alreadyNotReplicated = new TreeSet<>();
for (String dn : suffixes)
{
if (!containsDN(availableSuffixes, dn))
{
if (containsDN(notReplicatedSuffixes, dn))
{
alreadyNotReplicated.add(dn);
}
else
{
notFound.add(dn);
}
}
}
suffixes.removeAll(notFound);
suffixes.removeAll(alreadyNotReplicated);
if (!notFound.isEmpty() && displayErrors)
{
errPrintln();
errPrintln(ERR_REPLICATION_DISABLE_SUFFIXES_NOT_FOUND.get(toSingleLine(notFound)));
}
if (!alreadyNotReplicated.isEmpty() && displayErrors)
{
println();
println(INFO_ALREADY_NOT_REPLICATED_SUFFIXES.get(toSingleLine(alreadyNotReplicated)));
}
if (interactive)
{
while (suffixes.isEmpty())
{
if (containsOnlySchemaOrAdminSuffix(availableSuffixes))
{
// In interactive mode we do not propose to manage the administration suffix.
if (displayErrors)
{
errPrintln();
errPrintln(ERR_NO_SUFFIXES_AVAILABLE_TO_DISABLE_REPLICATION.get());
}
break;
}
if (areSuffixRequired)
{
errPrintln();
errPrintln(ERR_NO_SUFFIXES_SELECTED_TO_DISABLE.get());
}
boolean confirmationLimitReached =
askConfirmations(INFO_REPLICATION_DISABLE_SUFFIX_PROMPT, availableSuffixes, suffixes);
if (confirmationLimitReached)
{
suffixes.clear();
break;
}
if (!areSuffixRequired)
{
break;
}
}
}
}
}
private boolean askConfirmations(Arg1<Object> confirmationMsg,
Collection<String> availableSuffixes, Collection<String> suffixes)
{
for (String dn : availableSuffixes)
{
if (!isSchemaOrInternalAdminSuffix(dn))
{
try
{
if (askConfirmation(confirmationMsg.get(dn), true, logger))
{
suffixes.add(dn);
}
}
catch (ClientException ce)
{
errPrintln(ce.getMessageObject());
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(
Collection<String> suffixes, InitialLdapContext ctx, boolean interactive)
{
TreeSet<String> availableSuffixes = new TreeSet<>();
TreeSet<String> notReplicatedSuffixes = new TreeSet<>();
Collection<ReplicaDescriptor> replicas = getReplicas(ctx);
for (ReplicaDescriptor rep : replicas)
{
String dn = rep.getSuffix().getDN();
if (rep.isReplicated())
{
availableSuffixes.add(dn);
}
else
{
notReplicatedSuffixes.add(dn);
}
}
if (availableSuffixes.isEmpty())
{
println();
if (argParser.isInitializeAllReplicationSubcommand())
{
errPrintln(ERR_NO_SUFFIXES_AVAILABLE_TO_INITIALIZE_ALL_REPLICATION.get());
}
else
{
errPrintln(
ERR_NO_SUFFIXES_AVAILABLE_TO_INITIALIZE_LOCAL_REPLICATION.get());
}
LinkedList<String> userProvidedSuffixes = argParser.getBaseDNs();
TreeSet<String> userProvidedNotReplicatedSuffixes = new TreeSet<>();
for (String s1 : userProvidedSuffixes)
{
for (String s2 : notReplicatedSuffixes)
{
if (areDnsEqual(s1, s2))
{
userProvidedNotReplicatedSuffixes.add(s1);
}
}
}
if (!userProvidedNotReplicatedSuffixes.isEmpty())
{
println();
println(INFO_ALREADY_NOT_REPLICATED_SUFFIXES.get(
toSingleLine(userProvidedNotReplicatedSuffixes)));
}
suffixes.clear();
}
else
{
// Verify that the provided suffixes are configured in the servers.
TreeSet<String> notFound = new TreeSet<>();
TreeSet<String> alreadyNotReplicated = new TreeSet<>();
for (String dn : suffixes)
{
if (!containsDN(availableSuffixes, dn))
{
if (containsDN(notReplicatedSuffixes, dn))
{
alreadyNotReplicated.add(dn);
}
else
{
notFound.add(dn);
}
}
}
suffixes.removeAll(notFound);
suffixes.removeAll(alreadyNotReplicated);
if (!notFound.isEmpty())
{
errPrintln();
errPrintln(ERR_REPLICATION_INITIALIZE_LOCAL_SUFFIXES_NOT_FOUND.get(toSingleLine(notFound)));
}
if (!alreadyNotReplicated.isEmpty())
{
println();
println(INFO_ALREADY_NOT_REPLICATED_SUFFIXES.get(toSingleLine(alreadyNotReplicated)));
}
if (interactive)
{
boolean confirmationLimitReached = false;
while (suffixes.isEmpty())
{
println();
if (containsOnlySchemaOrAdminSuffix(availableSuffixes))
{
// In interactive mode we do not propose to manage the administration suffix.
if (argParser.isInitializeAllReplicationSubcommand())
{
errPrintln(ERR_NO_SUFFIXES_AVAILABLE_TO_INITIALIZE_ALL_REPLICATION.get());
}
else
{
errPrintln(ERR_NO_SUFFIXES_AVAILABLE_TO_INITIALIZE_LOCAL_REPLICATION.get());
}
break;
}
else
{
if (argParser.isInitializeAllReplicationSubcommand())
{
errPrintln(ERR_NO_SUFFIXES_SELECTED_TO_INITIALIZE_ALL.get());
}
else if (argParser.isPreExternalInitializationSubcommand())
{
errPrintln(ERR_NO_SUFFIXES_SELECTED_TO_PRE_EXTERNAL_INITIALIZATION.get());
}
else if (argParser.isPostExternalInitializationSubcommand())
{
errPrintln(ERR_NO_SUFFIXES_SELECTED_TO_POST_EXTERNAL_INITIALIZATION.get());
}
for (String dn : availableSuffixes)
{
if (!isSchemaOrInternalAdminSuffix(dn))
{
boolean addSuffix;
try
{
if (argParser.isPreExternalInitializationSubcommand())
{
addSuffix = askConfirmation(
INFO_REPLICATION_PRE_EXTERNAL_INITIALIZATION_SUFFIX_PROMPT.
get(dn), true, logger);
}
else if (argParser.isPostExternalInitializationSubcommand())
{
addSuffix = askConfirmation(
INFO_REPLICATION_POST_EXTERNAL_INITIALIZATION_SUFFIX_PROMPT.
get(dn), true, logger);
}
else
{
addSuffix = askConfirmation(
INFO_REPLICATION_INITIALIZE_ALL_SUFFIX_PROMPT.get(dn),
true, logger);
}
}
catch (ClientException ce)
{
errPrintln(ce.getMessageObject());
confirmationLimitReached = true;
break;
}
if (addSuffix)
{
suffixes.add(dn);
}
}
}
}
if (confirmationLimitReached)
{
suffixes.clear();
break;
}
}
}
}
}
private String toSingleLine(Collection<String> notFound)
{
return joinAsString(Constants.LINE_SEPARATOR, notFound);
}
/**
* 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(
Collection<String> suffixes, InitialLdapContext ctxSource,
InitialLdapContext ctxDestination, boolean interactive)
{
TreeSet<String> availableSuffixes = new TreeSet<>(
getCommonSuffixes(ctxSource, ctxDestination, SuffixRelationType.REPLICATED));
if (availableSuffixes.isEmpty())
{
errPrintln();
errPrintln(ERR_NO_SUFFIXES_AVAILABLE_TO_INITIALIZE_REPLICATION.get());
suffixes.clear();
}
else
{
// Verify that the provided suffixes are configured in the servers.
LinkedList<String> notFound = new LinkedList<>();
for (String dn : suffixes)
{
if (!containsDN(availableSuffixes, dn))
{
notFound.add(dn);
}
}
suffixes.removeAll(notFound);
if (!notFound.isEmpty())
{
errPrintln();
errPrintln(ERR_SUFFIXES_CANNOT_BE_INITIALIZED.get(toSingleLine(notFound)));
}
if (interactive)
{
askConfirmations(suffixes, availableSuffixes,
ERR_NO_SUFFIXES_AVAILABLE_TO_INITIALIZE_REPLICATION,
ERR_NO_SUFFIXES_SELECTED_TO_INITIALIZE,
INFO_REPLICATION_INITIALIZE_SUFFIX_PROMPT);
}
}
}
/**
* 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.
*/
private void updateConfiguration(InitialLdapContext ctx1,
InitialLdapContext ctx2, EnableReplicationUserData uData)
throws ReplicationCliException
{
final Set<String> twoReplServers = new LinkedHashSet<>();
final Set<String> allRepServers = new LinkedHashSet<>();
final Map<String, Set<String>> hmRepServers = new HashMap<>();
final Set<Integer> usedReplicationServerIds = new HashSet<>();
final Map<String, Set<Integer>> hmUsedReplicationDomainIds = new HashMap<>();
TopologyCacheFilter filter = new TopologyCacheFilter();
filter.setSearchMonitoringInformation(false);
filter.addBaseDNToSearch(ADSContext.getAdministrationSuffixDN());
filter.addBaseDNToSearch(Constants.SCHEMA_DN);
addBaseDNs(filter, uData.getBaseDNs());
ServerDescriptor serverDesc1 = createStandalone(ctx1, filter);
ServerDescriptor serverDesc2 = createStandalone(ctx2, filter);
ADSContext adsCtx1 = new ADSContext(ctx1);
ADSContext adsCtx2 = new ADSContext(ctx2);
if (!argParser.isInteractive())
{
// Inform the user of the potential errors that we found in the already
// registered servers.
final Set<LocalizableMessage> messages = new LinkedHashSet<>();
try
{
final Set<PreferredConnection> cnx = new LinkedHashSet<>();
cnx.addAll(getPreferredConnections(ctx1));
cnx.addAll(getPreferredConnections(ctx2));
TopologyCache cache1 = createTopologyCache(adsCtx1, cnx, uData);
if (cache1 != null)
{
messages.addAll(cache1.getErrorMessages());
}
TopologyCache cache2 = createTopologyCache(adsCtx2, cnx, uData);
if (cache2 != null)
{
messages.addAll(cache2.getErrorMessages());
}
}
catch (TopologyCacheException tce)
{
throw new ReplicationCliException(
ERR_REPLICATION_READING_ADS.get(tce.getMessage()),
ERROR_READING_TOPOLOGY_CACHE, tce);
}
catch (ADSContextException adce)
{
throw new ReplicationCliException(
ERR_REPLICATION_READING_ADS.get(adce.getMessage()),
ERROR_READING_ADS, adce);
}
if (!messages.isEmpty())
{
errPrintln(ERR_REPLICATION_READING_REGISTERED_SERVERS_WARNING.get(
getMessageFromCollection(messages,
Constants.LINE_SEPARATOR)));
}
}
// Check whether there is more than one replication server in the topology.
Set<String> baseDNsWithOneReplicationServer = new TreeSet<>();
Set<String> baseDNsWithNoReplicationServer = new TreeSet<>();
updateBaseDnsWithNotEnoughReplicationServer(adsCtx1, adsCtx2, uData,
baseDNsWithNoReplicationServer, baseDNsWithOneReplicationServer);
if (!baseDNsWithNoReplicationServer.isEmpty())
{
LocalizableMessage errorMsg =
ERR_REPLICATION_NO_REPLICATION_SERVER.get(toSingleLine(baseDNsWithNoReplicationServer));
throw new ReplicationCliException(errorMsg, ERROR_USER_DATA, null);
}
else if (!baseDNsWithOneReplicationServer.isEmpty())
{
if (isInteractive())
{
LocalizableMessage confirmMsg = INFO_REPLICATION_ONLY_ONE_REPLICATION_SERVER_CONFIRM.get(
toSingleLine(baseDNsWithOneReplicationServer));
try
{
if (!confirmAction(confirmMsg, false))
{
throw new ReplicationCliException(
ERR_REPLICATION_USER_CANCELLED.get(), USER_CANCELLED, null);
}
}
catch (Throwable t)
{
throw new ReplicationCliException(
ERR_REPLICATION_USER_CANCELLED.get(), USER_CANCELLED, t);
}
}
else
{
errPrintln(INFO_REPLICATION_ONLY_ONE_REPLICATION_SERVER_WARNING.get(
toSingleLine(baseDNsWithOneReplicationServer)));
errPrintln();
}
}
// These are used to identify which server we use to initialize
// the contents of the other server (if any).
InitialLdapContext ctxSource = null;
InitialLdapContext ctxDestination = null;
ADSContext adsCtxSource = null;
boolean adsAlreadyReplicated = false;
boolean adsMergeDone = false;
print(formatter.getFormattedWithPoints(
INFO_REPLICATION_ENABLE_UPDATING_ADS_CONTENTS.get()));
try
{
if (adsCtx1.hasAdminData() && adsCtx2.hasAdminData())
{
Set<Map<ServerProperty, Object>> registry1 = adsCtx1.readServerRegistry();
Set<Map<ServerProperty, Object>> registry2 = adsCtx2.readServerRegistry();
if (registry2.size() <= 1)
{
if (!hasAdministrator(adsCtx1.getDirContext(), uData))
{
adsCtx1.createAdministrator(getAdministratorProperties(uData));
}
serverDesc2.updateAdsPropertiesWithServerProperties();
registerServer(adsCtx1, serverDesc2.getAdsProperties());
if (!ADSContext.isRegistered(serverDesc1, registry1))
{
serverDesc1.updateAdsPropertiesWithServerProperties();
registerServer(adsCtx1, serverDesc1.getAdsProperties());
}
ctxSource = ctx1;
ctxDestination = ctx2;
adsCtxSource = adsCtx1;
}
else if (registry1.size() <= 1)
{
if (!hasAdministrator(adsCtx2.getDirContext(), uData))
{
adsCtx2.createAdministrator(getAdministratorProperties(uData));
}
serverDesc1.updateAdsPropertiesWithServerProperties();
registerServer(adsCtx2, serverDesc1.getAdsProperties());
if (!ADSContext.isRegistered(serverDesc2, registry2))
{
serverDesc2.updateAdsPropertiesWithServerProperties();
registerServer(adsCtx2, serverDesc2.getAdsProperties());
}
ctxSource = ctx2;
ctxDestination = ctx1;
adsCtxSource = adsCtx2;
}
else if (!areEqual(registry1, registry2))
{
print(formatter.getFormattedDone());
println();
boolean isFirstSource = mergeRegistries(adsCtx1, adsCtx2);
ctxSource = isFirstSource ? ctx1 : ctx2;
adsMergeDone = true;
}
else
{
// They are already replicated: nothing to do in terms of ADS
// initialization or ADS update data
adsAlreadyReplicated = isBaseDNReplicated(serverDesc1, serverDesc2, ADSContext.getAdministrationSuffixDN());
if (!adsAlreadyReplicated)
{
// Try to merge if both are replicated
boolean isADS1Replicated = isBaseDNReplicated(serverDesc1, ADSContext.getAdministrationSuffixDN());
boolean isADS2Replicated = isBaseDNReplicated(serverDesc2, ADSContext.getAdministrationSuffixDN());
if (isADS1Replicated && isADS2Replicated)
{
// Merge
print(formatter.getFormattedDone());
println();
boolean isFirstSource = mergeRegistries(adsCtx1, adsCtx2);
ctxSource = isFirstSource ? ctx1 : ctx2;
adsMergeDone = true;
}
else if (isADS1Replicated || !isADS2Replicated)
{
// The case where only the first ADS is replicated or none
// is replicated.
if (!hasAdministrator(adsCtx1.getDirContext(), uData))
{
adsCtx1.createAdministrator(getAdministratorProperties(uData));
}
serverDesc2.updateAdsPropertiesWithServerProperties();
registerServer(adsCtx1, serverDesc2.getAdsProperties());
if (!ADSContext.isRegistered(serverDesc1, registry1))
{
serverDesc1.updateAdsPropertiesWithServerProperties();
registerServer(adsCtx1, serverDesc1.getAdsProperties());
}
ctxSource = ctx1;
ctxDestination = ctx2;
adsCtxSource = adsCtx1;
}
else if (isADS2Replicated)
{
if (!hasAdministrator(adsCtx2.getDirContext(), uData))
{
adsCtx2.createAdministrator(getAdministratorProperties(uData));
}
serverDesc1.updateAdsPropertiesWithServerProperties();
registerServer(adsCtx2, serverDesc1.getAdsProperties());
if (!ADSContext.isRegistered(serverDesc2, registry2))
{
serverDesc2.updateAdsPropertiesWithServerProperties();
registerServer(adsCtx2, serverDesc2.getAdsProperties());
}
ctxSource = ctx2;
ctxDestination = ctx1;
adsCtxSource = adsCtx2;
}
}
}
}
else if (!adsCtx1.hasAdminData() && adsCtx2.hasAdminData())
{
if (!hasAdministrator(adsCtx2.getDirContext(), uData))
{
adsCtx2.createAdministrator(getAdministratorProperties(uData));
}
serverDesc1.updateAdsPropertiesWithServerProperties();
registerServer(adsCtx2, serverDesc1.getAdsProperties());
Set<Map<ServerProperty, Object>> registry2 = adsCtx2.readServerRegistry();
if (!ADSContext.isRegistered(serverDesc2, registry2))
{
serverDesc2.updateAdsPropertiesWithServerProperties();
registerServer(adsCtx2, serverDesc2.getAdsProperties());
}
ctxSource = ctx2;
ctxDestination = ctx1;
adsCtxSource = adsCtx2;
}
else if (adsCtx1.hasAdminData() && !adsCtx2.hasAdminData())
{
if (!hasAdministrator(adsCtx1.getDirContext(), uData))
{
adsCtx1.createAdministrator(getAdministratorProperties(uData));
}
serverDesc2.updateAdsPropertiesWithServerProperties();
registerServer(adsCtx1, serverDesc2.getAdsProperties());
Set<Map<ServerProperty, Object>> registry1 = adsCtx1.readServerRegistry();
if (!ADSContext.isRegistered(serverDesc1, registry1))
{
serverDesc1.updateAdsPropertiesWithServerProperties();
registerServer(adsCtx1, serverDesc1.getAdsProperties());
}
ctxSource = ctx1;
ctxDestination = ctx2;
adsCtxSource = adsCtx1;
}
else
{
adsCtx1.createAdminData(null);
if (!hasAdministrator(ctx1, uData))
{
// This could occur if the user created an administrator without
// registering any server.
adsCtx1.createAdministrator(getAdministratorProperties(uData));
}
serverDesc1.updateAdsPropertiesWithServerProperties();
adsCtx1.registerServer(serverDesc1.getAdsProperties());
serverDesc2.updateAdsPropertiesWithServerProperties();
adsCtx1.registerServer(serverDesc2.getAdsProperties());
ctxSource = ctx1;
ctxDestination = ctx2;
adsCtxSource = adsCtx1;
}
}
catch (ADSContextException adce)
{
throw new ReplicationCliException(
ERR_REPLICATION_UPDATING_ADS.get(adce.getMessageObject()),
ERROR_UPDATING_ADS, adce);
}
if (!adsAlreadyReplicated && !adsMergeDone)
{
try
{
ServerDescriptor.seedAdsTrustStore(ctxDestination,
adsCtxSource.getTrustedCertificates());
}
catch (Throwable t)
{
logger.error(LocalizableMessage.raw("Error seeding truststores: "+t, t));
throw new ReplicationCliException(
ERR_REPLICATION_ENABLE_SEEDING_TRUSTSTORE.get(getHostPort(ctxDestination),
getHostPort(adsCtxSource.getDirContext()), toString(t)),
ERROR_SEEDING_TRUSTORE, t);
}
}
if (!adsMergeDone)
{
print(formatter.getFormattedDone());
println();
}
List<String> baseDNs = uData.getBaseDNs();
if (!adsAlreadyReplicated
&& !containsDN(baseDNs, ADSContext.getAdministrationSuffixDN()))
{
baseDNs.add(ADSContext.getAdministrationSuffixDN());
uData.setBaseDNs(baseDNs);
}
if (uData.replicateSchema())
{
baseDNs = uData.getBaseDNs();
baseDNs.add(Constants.SCHEMA_DN);
uData.setBaseDNs(baseDNs);
}
TopologyCache cache1 = null;
TopologyCache cache2 = null;
try
{
Set<PreferredConnection> cnx = new LinkedHashSet<>();
cnx.addAll(getPreferredConnections(ctx1));
cnx.addAll(getPreferredConnections(ctx2));
cache1 = createTopologyCache(adsCtx1, cnx, uData);
if (cache1 != null)
{
usedReplicationServerIds.addAll(getReplicationServerIds(cache1));
}
cache2 = createTopologyCache(adsCtx2, cnx, uData);
if (cache1 != null)
{
usedReplicationServerIds.addAll(getReplicationServerIds(cache1));
}
}
catch (ADSContextException adce)
{
throw new ReplicationCliException(
ERR_REPLICATION_READING_ADS.get(adce.getMessage()),
ERROR_READING_ADS, adce);
}
catch (TopologyCacheException tce)
{
throw new ReplicationCliException(
ERR_REPLICATION_READING_ADS.get(tce.getMessage()),
ERROR_READING_TOPOLOGY_CACHE, tce);
}
addToSets(serverDesc1, uData.getServer1(), ctx1, twoReplServers, usedReplicationServerIds);
addToSets(serverDesc2, uData.getServer2(), ctx2, twoReplServers, usedReplicationServerIds);
for (String baseDN : uData.getBaseDNs())
{
Set<String> repServersForBaseDN = new LinkedHashSet<>();
repServersForBaseDN.addAll(getReplicationServers(baseDN, cache1, serverDesc1));
repServersForBaseDN.addAll(getReplicationServers(baseDN, cache2, serverDesc2));
repServersForBaseDN.addAll(twoReplServers);
hmRepServers.put(baseDN, repServersForBaseDN);
Set<Integer> ids = new HashSet<>();
ids.addAll(getReplicationDomainIds(baseDN, serverDesc1));
ids.addAll(getReplicationDomainIds(baseDN, serverDesc2));
if (cache1 != null)
{
for (ServerDescriptor server : cache1.getServers())
{
ids.addAll(getReplicationDomainIds(baseDN, server));
}
}
if (cache2 != null)
{
for (ServerDescriptor server : cache2.getServers())
{
ids.addAll(getReplicationDomainIds(baseDN, server));
}
}
hmUsedReplicationDomainIds.put(baseDN, ids);
}
for (Set<String> v : hmRepServers.values())
{
allRepServers.addAll(v);
}
Set<String> alreadyConfiguredReplicationServers = new HashSet<>();
configureServer(ctx1, serverDesc1, uData.getServer1(), argParser.server1.replicationPortArg,
usedReplicationServerIds, allRepServers, alreadyConfiguredReplicationServers,
WARN_FIRST_REPLICATION_SERVER_ALREADY_CONFIGURED);
configureServer(ctx2, serverDesc2, uData.getServer2(), argParser.server2.replicationPortArg,
usedReplicationServerIds, allRepServers, alreadyConfiguredReplicationServers,
WARN_SECOND_REPLICATION_SERVER_ALREADY_CONFIGURED);
for (String baseDN : uData.getBaseDNs())
{
Set<String> repServers = hmRepServers.get(baseDN);
Set<Integer> usedIds = hmUsedReplicationDomainIds.get(baseDN);
Set<String> alreadyConfiguredServers = new HashSet<>();
configureToReplicateBaseDN(uData.getServer1(), ctx1, serverDesc1, cache1, baseDN,
usedIds, alreadyConfiguredServers, repServers, allRepServers, alreadyConfiguredReplicationServers);
configureToReplicateBaseDN(uData.getServer2(), ctx2, serverDesc2, cache2, baseDN,
usedIds, alreadyConfiguredServers, repServers, allRepServers, alreadyConfiguredReplicationServers);
}
// 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 pointAdder = new PointAdder(this);
print(INFO_ENABLE_REPLICATION_INITIALIZING_ADS_ALL.get(getHostPort(ctxSource)));
pointAdder.start();
try
{
initializeAllSuffix(ADSContext.getAdministrationSuffixDN(), ctxSource, false);
}
finally
{
pointAdder.stop();
}
print(formatter.getSpace());
print(formatter.getFormattedDone());
println();
}
else if (ctxSource != null && ctxDestination != null)
{
print(formatter.getFormattedWithPoints(
INFO_ENABLE_REPLICATION_INITIALIZING_ADS.get(
getHostPort(ctxDestination), getHostPort(ctxSource))));
initializeSuffix(ADSContext.getAdministrationSuffixDN(), ctxSource, ctxDestination, false);
print(formatter.getFormattedDone());
println();
}
// If we must initialize the schema do so.
if (mustInitializeSchema(serverDesc1, serverDesc2, uData))
{
if (argParser.useSecondServerAsSchemaSource())
{
ctxSource = ctx2;
ctxDestination = ctx1;
}
else
{
ctxSource = ctx1;
ctxDestination = ctx2;
}
if (adsMergeDone)
{
PointAdder pointAdder = new PointAdder(this);
println(INFO_ENABLE_REPLICATION_INITIALIZING_SCHEMA.get(
getHostPort(ctxDestination), getHostPort(ctxSource)));
pointAdder.start();
try
{
initializeAllSuffix(Constants.SCHEMA_DN, ctxSource, false);
}
finally
{
pointAdder.stop();
}
print(formatter.getSpace());
}
else
{
print(formatter.getFormattedWithPoints(INFO_ENABLE_REPLICATION_INITIALIZING_SCHEMA.get(
getHostPort(ctxDestination), getHostPort(ctxSource))));
initializeSuffix(Constants.SCHEMA_DN, ctxSource, ctxDestination, false);
}
print(formatter.getFormattedDone());
println();
}
}
private void addToSets(ServerDescriptor serverDesc, EnableReplicationServerData serverData, InitialLdapContext ctx,
final Set<String> twoReplServers, final Set<Integer> usedReplicationServerIds)
{
if (serverDesc.isReplicationServer())
{
twoReplServers.add(serverDesc.getReplicationServerHostPort());
usedReplicationServerIds.add(serverDesc.getReplicationServerId());
}
else if (serverData.configureReplicationServer())
{
twoReplServers.add(getReplicationServer(getHostName(ctx), serverData.getReplicationPort()));
}
}
private void configureToReplicateBaseDN(EnableReplicationServerData server, InitialLdapContext ctx,
ServerDescriptor serverDesc, TopologyCache cache, String baseDN, Set<Integer> usedIds,
Set<String> alreadyConfiguredServers, Set<String> repServers, final Set<String> allRepServers,
Set<String> alreadyConfiguredReplicationServers) throws ReplicationCliException
{
if (server.configureReplicationDomain()
|| areDnsEqual(baseDN, ADSContext.getAdministrationSuffixDN()))
{
try
{
configureToReplicateBaseDN(ctx, baseDN, repServers, usedIds);
}
catch (OpenDsException ode)
{
LocalizableMessage msg = getMessageForEnableException(getHostPort(ctx), baseDN);
throw new ReplicationCliException(msg, ERROR_ENABLING_REPLICATION_ON_BASEDN, ode);
}
}
alreadyConfiguredServers.add(serverDesc.getId());
if (cache != null)
{
configureToReplicateBaseDN(baseDN, repServers, usedIds, cache, serverDesc, alreadyConfiguredServers,
allRepServers, alreadyConfiguredReplicationServers);
}
}
private void configureServer(InitialLdapContext ctx, ServerDescriptor serverDesc,
EnableReplicationServerData enableServer, IntegerArgument replicationPortArg,
Set<Integer> usedReplicationServerIds, Set<String> allRepServers,
Set<String> alreadyConfiguredReplicationServers, Arg2<Number, Number> replicationServerAlreadyConfiguredMsg)
throws ReplicationCliException
{
if (!serverDesc.isReplicationServer() && enableServer.configureReplicationServer())
{
try
{
configureAsReplicationServer(ctx, enableServer.getReplicationPort(), enableServer.isSecureReplication(),
allRepServers, usedReplicationServerIds);
}
catch (OpenDsException ode)
{
throw errorConfiguringReplicationServer(ctx, ode);
}
}
else if (serverDesc.isReplicationServer())
{
try
{
updateReplicationServer(ctx, allRepServers);
}
catch (OpenDsException ode)
{
throw errorConfiguringReplicationServer(ctx, ode);
}
if (replicationPortArg.isPresent() && enableServer.getReplicationPort() != serverDesc.getReplicationServerPort())
{
LocalizableMessage msg = replicationServerAlreadyConfiguredMsg.get(
serverDesc.getReplicationServerPort(), enableServer.getReplicationPort());
logger.warn(msg);
errPrintln(msg);
}
}
alreadyConfiguredReplicationServers.add(serverDesc.getId());
}
private ReplicationCliException errorConfiguringReplicationServer(InitialLdapContext ctx, OpenDsException ode)
{
return new ReplicationCliException(
ERR_REPLICATION_CONFIGURING_REPLICATIONSERVER.get(getHostPort(ctx)),
ERROR_CONFIGURING_REPLICATIONSERVER, ode);
}
private TopologyCache createTopologyCache(ADSContext adsCtx, Set<PreferredConnection> cnx, ReplicationUserData uData)
throws ADSContextException, TopologyCacheException
{
if (adsCtx.hasAdminData())
{
TopologyCache cache = new TopologyCache(adsCtx, getTrustManager(sourceServerCI), getConnectTimeout());
cache.setPreferredConnections(cnx);
cache.getFilter().setSearchMonitoringInformation(false);
addBaseDNs(cache.getFilter(), uData.getBaseDNs());
cache.reloadTopology();
return cache;
}
return null;
}
private ServerDescriptor createStandalone(InitialLdapContext ctx, TopologyCacheFilter filter)
throws ReplicationCliException
{
try
{
return ServerDescriptor.createStandalone(ctx, filter);
}
catch (NamingException ne)
{
throw new ReplicationCliException(
getMessageForException(ne, getHostPort(ctx)),
ERROR_READING_CONFIGURATION, ne);
}
}
/**
* 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.
*/
private void updateConfiguration(InitialLdapContext ctx,
DisableReplicationUserData uData) throws ReplicationCliException
{
TopologyCacheFilter filter = new TopologyCacheFilter();
filter.setSearchMonitoringInformation(false);
if (!uData.disableAll())
{
filter.addBaseDNToSearch(ADSContext.getAdministrationSuffixDN());
addBaseDNs(filter, uData.getBaseDNs());
}
ServerDescriptor server = createStandalone(ctx, filter);
ADSContext adsCtx = new ADSContext(ctx);
TopologyCache cache = null;
// Only try to update remote server if the user provided a Global
// Administrator to authenticate.
boolean tryToUpdateRemote = uData.getAdminUid() != null;
try
{
if (adsCtx.hasAdminData() && tryToUpdateRemote)
{
cache = new TopologyCache(adsCtx, getTrustManager(sourceServerCI), getConnectTimeout());
cache.setPreferredConnections(getPreferredConnections(ctx));
cache.getFilter().setSearchMonitoringInformation(false);
if (!uData.disableAll())
{
addBaseDNs(cache.getFilter(), uData.getBaseDNs());
}
cache.reloadTopology();
}
}
catch (ADSContextException adce)
{
throw new ReplicationCliException(
ERR_REPLICATION_READING_ADS.get(adce.getMessage()),
ERROR_READING_ADS, adce);
}
catch (TopologyCacheException tce)
{
throw new ReplicationCliException(
ERR_REPLICATION_READING_ADS.get(tce.getMessage()),
ERROR_READING_TOPOLOGY_CACHE, tce);
}
if (!argParser.isInteractive())
{
// Inform the user of the potential errors that we found.
Set<LocalizableMessage> messages = new LinkedHashSet<>();
if (cache != null)
{
messages.addAll(cache.getErrorMessages());
}
if (!messages.isEmpty())
{
errPrintln(
ERR_REPLICATION_READING_REGISTERED_SERVERS_WARNING.get(
getMessageFromCollection(messages,
Constants.LINE_SEPARATOR)));
}
}
final boolean disableReplicationServer = server.isReplicationServer()
&& (uData.disableReplicationServer() || uData.disableAll());
if (cache != null && disableReplicationServer)
{
String replicationServer = server.getReplicationServerHostPort();
// 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).
Set<SuffixDescriptor> lastRepServer = new TreeSet<>(new SuffixComparator());
Set<SuffixDescriptor> beforeLastRepServer = new TreeSet<>(new SuffixComparator());
for (SuffixDescriptor suffix : cache.getSuffixes())
{
if (isSchemaOrInternalAdminSuffix(suffix.getDN()))
{
// Do not display these suffixes.
continue;
}
Set<String> repServers = suffix.getReplicationServers();
if (repServers.size() <= 2
&& containsIgnoreCase(repServers, replicationServer))
{
if (repServers.size() == 2)
{
beforeLastRepServer.add(suffix);
}
else
{
lastRepServer.add(suffix);
}
}
}
// Inform the user
if (!beforeLastRepServer.isEmpty())
{
Set<String> baseDNs = new LinkedHashSet<>();
for (SuffixDescriptor suffix : beforeLastRepServer)
{
if (!isSchemaOrInternalAdminSuffix(suffix.getDN()))
{
// Do not display these suffixes.
baseDNs.add(suffix.getDN());
}
}
if (!baseDNs.isEmpty())
{
String arg = toSingleLine(baseDNs);
if (!isInteractive())
{
println(INFO_DISABLE_REPLICATION_ONE_POINT_OF_FAILURE.get(arg));
}
else
{
LocalizableMessage msg = INFO_DISABLE_REPLICATION_ONE_POINT_OF_FAILURE_PROMPT.get(arg);
if (!askConfirmation(msg, false))
{
throw new ReplicationCliException(ERR_REPLICATION_USER_CANCELLED.get(), USER_CANCELLED, null);
}
}
}
}
if (!lastRepServer.isEmpty())
{
// Check that there are other replicas and that this message, really
// makes sense to be displayed.
Set<String> suffixArg = new LinkedHashSet<>();
for (SuffixDescriptor suffix : lastRepServer)
{
boolean baseDNSpecified = false;
for (String baseDN : uData.getBaseDNs())
{
if (!isSchemaOrInternalAdminSuffix(baseDN) && areDnsEqual(baseDN, suffix.getDN()))
{
baseDNSpecified = true;
break;
}
}
if (!baseDNSpecified)
{
Set<ServerDescriptor> servers = new TreeSet<>(new ServerComparator());
for (ReplicaDescriptor replica : suffix.getReplicas())
{
servers.add(replica.getServer());
}
suffixArg.add(getSuffixDisplay(suffix.getDN(), servers));
}
else if (suffix.getReplicas().size() > 1)
{
// If there is just one replica, it is the one in this server.
Set<ServerDescriptor> servers = new TreeSet<>(new ServerComparator());
for (ReplicaDescriptor replica : suffix.getReplicas())
{
if (!replica.getServer().isSameServer(server))
{
servers.add(replica.getServer());
}
}
if (!servers.isEmpty())
{
suffixArg.add(getSuffixDisplay(suffix.getDN(), servers));
}
}
}
if (!suffixArg.isEmpty())
{
String arg = toSingleLine(suffixArg);
if (!isInteractive())
{
println(INFO_DISABLE_REPLICATION_DISABLE_IN_REMOTE.get(arg));
}
else
{
LocalizableMessage msg = INFO_DISABLE_REPLICATION_DISABLE_IN_REMOTE_PROMPT.get(arg);
if (!askConfirmation(msg, false))
{
throw new ReplicationCliException(ERR_REPLICATION_USER_CANCELLED.get(), USER_CANCELLED, null);
}
}
}
}
}
/**
* 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;
boolean disableAllBaseDns = disableAllBaseDns(ctx, uData);
Collection<ReplicaDescriptor> replicas = getReplicas(ctx);
for (ReplicaDescriptor rep : replicas)
{
String dn = rep.getSuffix().getDN();
if (rep.isReplicated())
{
if (areDnsEqual(ADSContext.getAdministrationSuffixDN(), dn))
{
adsReplicated = true;
}
else if (areDnsEqual(Constants.SCHEMA_DN, dn))
{
schemaReplicated = true;
}
}
}
if (disableAllBaseDns &&
(disableReplicationServer || !server.isReplicationServer()))
{
// Unregister the server from the ADS if no other server has dependencies
// with it (no replicated base DNs and no replication server).
server.updateAdsPropertiesWithServerProperties();
try
{
adsCtx.unregisterServer(server.getAdsProperties());
// To be sure that the change gets propagated
sleepCatchInterrupt(2000);
}
catch (ADSContextException adce)
{
logger.error(LocalizableMessage.raw("Error unregistering server: "+
server.getAdsProperties(), adce));
if (adce.getError() != ADSContextException.ErrorType.NOT_YET_REGISTERED)
{
throw new ReplicationCliException(
ERR_REPLICATION_UPDATING_ADS.get(adce.getMessageObject()),
ERROR_READING_ADS, adce);
}
}
}
Set<String> suffixesToDisable = new HashSet<>();
if (uData.disableAll())
{
for (ReplicaDescriptor replica : server.getReplicas())
{
if (replica.isReplicated())
{
suffixesToDisable.add(replica.getSuffix().getDN());
}
}
}
else
{
suffixesToDisable.addAll(uData.getBaseDNs());
if (disableAllBaseDns &&
(disableReplicationServer || !server.isReplicationServer()))
{
forceDisableSchema = schemaReplicated;
forceDisableADS = adsReplicated;
}
for (String dn : uData.getBaseDNs())
{
if (areDnsEqual(ADSContext.getAdministrationSuffixDN(), dn))
{
// The user already asked this to be explicitly disabled
forceDisableADS = false;
}
else if (areDnsEqual(Constants.SCHEMA_DN, dn))
{
// The user already asked this to be explicitly disabled
forceDisableSchema = false;
}
}
if (forceDisableSchema)
{
suffixesToDisable.add(Constants.SCHEMA_DN);
}
if (forceDisableADS)
{
suffixesToDisable.add(ADSContext.getAdministrationSuffixDN());
}
}
String replicationServerHostPort =
server.isReplicationServer() ? server.getReplicationServerHostPort() : null;
for (String baseDN : suffixesToDisable)
{
try
{
deleteReplicationDomain(ctx, baseDN);
}
catch (OpenDsException ode)
{
LocalizableMessage msg = getMessageForDisableException(getHostPort(ctx), baseDN);
throw new ReplicationCliException(msg,
ERROR_DISABLING_REPLICATION_ON_BASEDN, ode);
}
}
boolean replicationServerDisabled = false;
if (replicationServerHostPort != null && cache != null)
{
Set<ServerDescriptor> serversToUpdate = new LinkedHashSet<>();
Set<String> baseDNsToUpdate = new HashSet<>(suffixesToDisable);
for (String baseDN : baseDNsToUpdate)
{
SuffixDescriptor suffix = getSuffix(baseDN, cache, server);
if (suffix != null)
{
for (ReplicaDescriptor replica : suffix.getReplicas())
{
serversToUpdate.add(replica.getServer());
}
}
}
if (disableReplicationServer)
{
// Find references in all servers.
for (SuffixDescriptor suffix : cache.getSuffixes())
{
if (containsIgnoreCase(suffix.getReplicationServers(), replicationServerHostPort))
{
baseDNsToUpdate.add(suffix.getDN());
for (ReplicaDescriptor replica : suffix.getReplicas())
{
serversToUpdate.add(replica.getServer());
}
}
}
}
String bindDn = getBindDN(ctx);
String pwd = getBindPassword(ctx);
for (ServerDescriptor s : serversToUpdate)
{
removeReferencesInServer(s, replicationServerHostPort, bindDn, pwd,
baseDNsToUpdate, disableReplicationServer,
getPreferredConnections(ctx));
}
if (disableReplicationServer)
{
// Disable replication server
disableReplicationServer(ctx);
replicationServerDisabled = true;
// Wait to be sure that changes are taken into account and reset the
// contents of the ADS.
sleepCatchInterrupt(5000);
}
}
if (disableReplicationServer && !replicationServerDisabled)
{
// This can happen if we could not retrieve the TopologyCache
disableReplicationServer(ctx);
replicationServerDisabled = true;
}
if (uData.disableAll())
{
try
{
// Delete all contents from ADSContext.
print(formatter.getFormattedWithPoints(
INFO_REPLICATION_REMOVE_ADS_CONTENTS.get()));
adsCtx.removeAdminData(false /* avoid self-disconnect */);
print(formatter.getFormattedDone());
println();
}
catch (ADSContextException adce)
{
logger.error(LocalizableMessage.raw("Error removing contents of cn=admin data: "+
adce, adce));
throw new ReplicationCliException(
ERR_REPLICATION_UPDATING_ADS.get(adce.getMessageObject()),
ERROR_UPDATING_ADS, adce);
}
}
else if (disableAllBaseDns &&
(disableReplicationServer || !server.isReplicationServer()))
{
// Unregister the servers from the ADS of the local server.
try
{
for (Map<ADSContext.ServerProperty, Object> s : adsCtx.readServerRegistry())
{
adsCtx.unregisterServer(s);
}
// To be sure that the change gets propagated
sleepCatchInterrupt(2000);
}
catch (ADSContextException adce)
{
// This is not critical, do not send an error
logger.warn(LocalizableMessage.raw("Error unregistering server: "+
server.getAdsProperties(), adce));
}
}
}
private void addBaseDNs(TopologyCacheFilter filter, List<String> baseDNs)
{
for (String dn : baseDNs)
{
filter.addBaseDNToSearch(dn);
}
}
/**
* 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.
*/
private void displayStatus(InitialLdapContext ctx,
StatusReplicationUserData uData) throws ReplicationCliException
{
ADSContext adsCtx = new ADSContext(ctx);
boolean somethingDisplayed = false;
TopologyCache cache;
try
{
cache = new TopologyCache(adsCtx, getTrustManager(sourceServerCI), getConnectTimeout());
cache.setPreferredConnections(getPreferredConnections(ctx));
addBaseDNs(cache.getFilter(), uData.getBaseDNs());
cache.reloadTopology();
}
catch (TopologyCacheException tce)
{
throw new ReplicationCliException(
ERR_REPLICATION_READING_ADS.get(tce.getMessage()),
ERROR_READING_TOPOLOGY_CACHE, tce);
}
if (mustPrintCommandBuilder())
{
printNewCommandBuilder(STATUS_REPLICATION_SUBCMD_NAME, uData);
}
if (!argParser.isInteractive())
{
// Inform the user of the potential errors that we found.
Set<LocalizableMessage> messages = new LinkedHashSet<>(cache.getErrorMessages());
if (!messages.isEmpty())
{
errPrintln(ERR_REPLICATION_STATUS_READING_REGISTERED_SERVERS.get(
getMessageFromCollection(messages, Constants.LINE_SEPARATOR)));
}
}
List<String> userBaseDNs = uData.getBaseDNs();
List<Set<ReplicaDescriptor>> replicaLists = new LinkedList<>();
boolean oneReplicated = false;
boolean displayAll = userBaseDNs.isEmpty();
for (SuffixDescriptor suffix : cache.getSuffixes())
{
String dn = suffix.getDN();
// If no base DNs where specified display all the base DNs but the schema
// and cn=admin data.
boolean found = containsDN(userBaseDNs, dn) || (displayAll && !isSchemaOrInternalAdminSuffix(dn));
if (found)
{
if (isAnyReplicated(suffix))
{
oneReplicated = true;
replicaLists.add(suffix.getReplicas());
}
else
{
// Check if there are already some non replicated base DNs.
found = false;
for (Set<ReplicaDescriptor> replicas : replicaLists)
{
ReplicaDescriptor replica = replicas.iterator().next();
if (!replica.isReplicated() &&
areDnsEqual(dn, replica.getSuffix().getDN()))
{
replicas.addAll(suffix.getReplicas());
found = true;
break;
}
}
if (!found)
{
replicaLists.add(suffix.getReplicas());
}
}
}
}
if (!oneReplicated && displayAll)
{
// Maybe there are some replication server configured...
SortedSet<ServerDescriptor> rServers = new TreeSet<>(new ReplicationServerComparator());
for (ServerDescriptor server : cache.getServers())
{
if (server.isReplicationServer())
{
rServers.add(server);
}
}
if (!rServers.isEmpty())
{
displayStatus(rServers, uData.isScriptFriendly(), getPreferredConnections(ctx));
somethingDisplayed = true;
}
}
if (!replicaLists.isEmpty())
{
List<Set<ReplicaDescriptor>> orderedReplicaLists = new LinkedList<>();
for (Set<ReplicaDescriptor> replicas : replicaLists)
{
String dn1 = replicas.iterator().next().getSuffix().getDN();
boolean inserted = false;
for (int i=0; i<orderedReplicaLists.size() && !inserted; i++)
{
String dn2 =
orderedReplicaLists.get(i).iterator().next().getSuffix().getDN();
if (dn1.compareTo(dn2) < 0)
{
orderedReplicaLists.add(i, replicas);
inserted = true;
}
}
if (!inserted)
{
orderedReplicaLists.add(replicas);
}
}
Set<ReplicaDescriptor> replicasWithNoReplicationServer = new HashSet<>();
Set<ServerDescriptor> serversWithNoReplica = new HashSet<>();
displayStatus(orderedReplicaLists, uData.isScriptFriendly(),
getPreferredConnections(ctx),
cache.getServers(),
replicasWithNoReplicationServer, serversWithNoReplica);
somethingDisplayed = true;
if (oneReplicated && !uData.isScriptFriendly())
{
println();
print(INFO_REPLICATION_STATUS_REPLICATED_LEGEND.get());
if (!replicasWithNoReplicationServer.isEmpty() ||
!serversWithNoReplica.isEmpty())
{
println();
print(
INFO_REPLICATION_STATUS_NOT_A_REPLICATION_SERVER_LEGEND.get());
println();
print(
INFO_REPLICATION_STATUS_NOT_A_REPLICATION_DOMAIN_LEGEND.get());
}
println();
somethingDisplayed = true;
}
}
if (!somethingDisplayed)
{
if (displayAll)
{
print(INFO_REPLICATION_STATUS_NO_REPLICATION_INFORMATION.get());
println();
}
else
{
print(INFO_REPLICATION_STATUS_NO_BASEDNS.get());
println();
}
}
}
private boolean isAnyReplicated(SuffixDescriptor suffix)
{
for (ReplicaDescriptor replica : suffix.getReplicas())
{
if (replica.isReplicated())
{
return true;
}
}
return false;
}
/**
* 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(
List<Set<ReplicaDescriptor>> orderedReplicaLists,
boolean scriptFriendly, Set<PreferredConnection> cnx,
Set<ServerDescriptor> servers,
Set<ReplicaDescriptor> replicasWithNoReplicationServer,
Set<ServerDescriptor> serversWithNoReplica)
{
Set<ReplicaDescriptor> orderedReplicas = new LinkedHashSet<>();
Set<String> hostPorts = new TreeSet<>();
Set<ServerDescriptor> notAddedReplicationServers = new TreeSet<>(new ReplicationServerComparator());
for (Set<ReplicaDescriptor> replicas : orderedReplicaLists)
{
for (ReplicaDescriptor replica : replicas)
{
hostPorts.add(getHostPort2(replica.getServer(), cnx));
}
for (String hostPort : hostPorts)
{
for (ReplicaDescriptor replica : replicas)
{
if (getHostPort2(replica.getServer(), cnx).equals(hostPort))
{
orderedReplicas.add(replica);
}
}
}
for (ServerDescriptor server : servers)
{
if (server.isReplicationServer() && isRepServerNotInDomain(replicas, server))
{
notAddedReplicationServers.add(server);
}
}
}
/*
* 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.
*/
TableBuilder tableBuilder = new TableBuilder();
/* Table headings. */
tableBuilder.appendHeading(
INFO_REPLICATION_STATUS_HEADER_SUFFIX_DN.get());
tableBuilder.appendHeading(
INFO_REPLICATION_STATUS_HEADER_SERVERPORT.get());
tableBuilder.appendHeading(
INFO_REPLICATION_STATUS_HEADER_NUMBER_ENTRIES.get());
tableBuilder.appendHeading(
INFO_REPLICATION_STATUS_HEADER_REPLICATION_ENABLED.get());
tableBuilder.appendHeading(INFO_REPLICATION_STATUS_HEADER_DS_ID.get());
tableBuilder.appendHeading(INFO_REPLICATION_STATUS_HEADER_RS_ID.get());
tableBuilder.appendHeading(
INFO_REPLICATION_STATUS_HEADER_REPLICATION_PORT.get());
tableBuilder.appendHeading(
INFO_REPLICATION_STATUS_HEADER_MISSING_CHANGES.get());
tableBuilder.appendHeading(
INFO_REPLICATION_STATUS_HEADER_AGE_OF_OLDEST_MISSING_CHANGE.get());
tableBuilder.appendHeading(
INFO_REPLICATION_STATUS_HEADER_SECURE.get());
/* Table data. */
for (ReplicaDescriptor replica : orderedReplicas)
{
tableBuilder.startRow();
// Suffix DN
tableBuilder.appendCell(LocalizableMessage.raw(replica.getSuffix().getDN()));
// Server port
tableBuilder.appendCell(
LocalizableMessage.raw(getHostPort2(replica.getServer(), cnx)));
// Number of entries
int nEntries = replica.getEntries();
if (nEntries >= 0)
{
tableBuilder.appendCell(LocalizableMessage.raw(String.valueOf(nEntries)));
}
else
{
tableBuilder.appendCell(EMPTY_MSG);
}
if (!replica.isReplicated())
{
tableBuilder.appendCell(EMPTY_MSG);
}
else
{
// Replication enabled
tableBuilder.appendCell(
LocalizableMessage.raw(Boolean.toString(replica.isReplicationEnabled())));
// DS instance ID
tableBuilder.appendCell(
LocalizableMessage.raw(Integer.toString(replica.getReplicationId())));
// RS ID and port.
if (replica.getServer().isReplicationServer())
{
tableBuilder.appendCell(Integer.toString(replica.getServer()
.getReplicationServerId()));
tableBuilder.appendCell(LocalizableMessage.raw(String.valueOf(replica
.getServer().getReplicationServerPort())));
}
else
{
if (scriptFriendly)
{
tableBuilder.appendCell(EMPTY_MSG);
}
else
{
tableBuilder.appendCell(
INFO_REPLICATION_STATUS_NOT_A_REPLICATION_SERVER_SHORT.get());
}
tableBuilder.appendCell(EMPTY_MSG);
replicasWithNoReplicationServer.add(replica);
}
// Missing changes
int missingChanges = replica.getMissingChanges();
if (missingChanges >= 0)
{
tableBuilder.appendCell(LocalizableMessage.raw(String.valueOf(missingChanges)));
}
else
{
tableBuilder.appendCell(EMPTY_MSG);
}
// Age of oldest missing change
long ageOfOldestMissingChange = replica.getAgeOfOldestMissingChange();
if (ageOfOldestMissingChange > 0)
{
Date date = new Date(ageOfOldestMissingChange);
tableBuilder.appendCell(LocalizableMessage.raw(date.toString()));
}
else
{
tableBuilder.appendCell(EMPTY_MSG);
}
// Secure
if (!replica.getServer().isReplicationServer())
{
tableBuilder.appendCell(EMPTY_MSG);
}
else
{
tableBuilder.appendCell(
LocalizableMessage.raw(Boolean.toString(
replica.getServer().isReplicationSecure())));
}
}
}
for (ServerDescriptor server : notAddedReplicationServers)
{
tableBuilder.startRow();
serversWithNoReplica.add(server);
// Suffix DN
tableBuilder.appendCell(EMPTY_MSG);
// Server port
tableBuilder.appendCell(LocalizableMessage.raw(getHostPort2(server, cnx)));
// Number of entries
if (scriptFriendly)
{
tableBuilder.appendCell(EMPTY_MSG);
}
else
{
tableBuilder.appendCell(
INFO_REPLICATION_STATUS_NOT_A_REPLICATION_DOMAIN_SHORT.get());
}
// Replication enabled
tableBuilder.appendCell(Boolean.toString(true));
// DS ID
tableBuilder.appendCell(EMPTY_MSG);
// RS ID
tableBuilder.appendCell(
LocalizableMessage.raw(Integer.toString(server.getReplicationServerId())));
// Replication port
int replicationPort = server.getReplicationServerPort();
if (replicationPort >= 0)
{
tableBuilder.appendCell(
LocalizableMessage.raw(String.valueOf(replicationPort)));
}
else
{
tableBuilder.appendCell(EMPTY_MSG);
}
// Missing changes
tableBuilder.appendCell(EMPTY_MSG);
// Age of oldest change
tableBuilder.appendCell(EMPTY_MSG);
// Secure
tableBuilder.appendCell(
LocalizableMessage.raw(Boolean.toString(server.isReplicationSecure())));
}
TablePrinter printer;
PrintStream out = getOutputStream();
if (scriptFriendly)
{
printer = new TabSeparatedTablePrinter(out);
}
else
{
final TextTablePrinter ttPrinter = new TextTablePrinter(out);
ttPrinter.setColumnSeparator(LIST_TABLE_SEPARATOR);
printer = ttPrinter;
}
tableBuilder.print(printer);
}
private boolean isRepServerNotInDomain(Set<ReplicaDescriptor> replicas, ServerDescriptor server)
{
boolean isDomain = false;
boolean isRepServer = false;
String replicationServer = server.getReplicationServerHostPort();
for (ReplicaDescriptor replica : replicas)
{
if (!isRepServer)
{
isRepServer = containsIgnoreCase(replica.getReplicationServers(), replicationServer);
}
if (replica.getServer() == server)
{
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.
*/
private void displayStatus(Set<ServerDescriptor> servers,
boolean scriptFriendly, Set<PreferredConnection> cnx)
{
TableBuilder tableBuilder = new TableBuilder();
tableBuilder.appendHeading(INFO_REPLICATION_STATUS_HEADER_SERVERPORT.get());
tableBuilder.appendHeading(
INFO_REPLICATION_STATUS_HEADER_REPLICATION_PORT.get());
tableBuilder.appendHeading(INFO_REPLICATION_STATUS_HEADER_SECURE.get());
for (ServerDescriptor server : servers)
{
tableBuilder.startRow();
// Server port
tableBuilder.appendCell(LocalizableMessage.raw(getHostPort2(server, cnx)));
// Replication port
int replicationPort = server.getReplicationServerPort();
if (replicationPort >= 0)
{
tableBuilder.appendCell(LocalizableMessage.raw(String.valueOf(replicationPort)));
}
else
{
tableBuilder.appendCell(EMPTY_MSG);
}
// Secure
tableBuilder.appendCell(LocalizableMessage.raw(Boolean.toString(server.isReplicationSecure())));
}
PrintStream out = getOutputStream();
TablePrinter printer;
if (scriptFriendly)
{
print(INFO_REPLICATION_STATUS_INDEPENDENT_REPLICATION_SERVERS.get());
println();
printer = new TabSeparatedTablePrinter(out);
}
else
{
LocalizableMessage msg = INFO_REPLICATION_STATUS_INDEPENDENT_REPLICATION_SERVERS.get();
print(msg);
println();
int length = msg.length();
StringBuilder buf = new StringBuilder();
for (int i=0; i<length; i++)
{
buf.append("=");
}
print(LocalizableMessage.raw(buf.toString()));
println();
printer = new TextTablePrinter(getOutputStream());
((TextTablePrinter)printer).setColumnSeparator(
LIST_TABLE_SEPARATOR);
}
tableBuilder.print(printer);
}
/**
* 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.
*/
private Set<String> getReplicationServers(String baseDN,
TopologyCache cache, ServerDescriptor server)
{
Set<String> servers = getAllReplicationServers(baseDN, server);
if (cache != null)
{
for (SuffixDescriptor suffix : cache.getSuffixes())
{
if (areDnsEqual(suffix.getDN(), baseDN))
{
Set<String> s = suffix.getReplicationServers();
// 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).
Set<String> copy = new HashSet<>(s);
copy.retainAll(servers);
if (!copy.isEmpty())
{
servers.addAll(s);
break;
}
else if (server.isReplicationServer()
&& containsIgnoreCase(s, server.getReplicationServerHostPort()))
{
// this server is acting as replication server with no domain.
servers.addAll(s);
break;
}
}
}
}
return servers;
}
private boolean containsIgnoreCase(Set<String> col, String toFind)
{
for (String s : col)
{
if (s.equalsIgnoreCase(toFind))
{
return true;
}
}
return false;
}
private String findIgnoreCase(Set<String> col, String toFind)
{
for (String s : col)
{
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.
*/
private SuffixDescriptor getSuffix(String baseDN, TopologyCache cache,
ServerDescriptor server)
{
String replicationServer = null;
if (server.isReplicationServer())
{
replicationServer = server.getReplicationServerHostPort();
}
SuffixDescriptor returnValue = null;
Set<String> servers = getAllReplicationServers(baseDN, server);
for (SuffixDescriptor suffix : cache.getSuffixes())
{
if (areDnsEqual(suffix.getDN(), baseDN))
{
Set<String> s = suffix.getReplicationServers();
// 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).
HashSet<String> copy = new HashSet<>(s);
copy.retainAll(servers);
if (!copy.isEmpty())
{
return suffix;
}
else if (replicationServer != null && containsIgnoreCase(s, replicationServer))
{
returnValue = suffix;
}
}
}
return returnValue;
}
private Set<String> getAllReplicationServers(String baseDN, ServerDescriptor server)
{
Set<String> servers = new LinkedHashSet<>();
for (ReplicaDescriptor replica : server.getReplicas())
{
if (areDnsEqual(replica.getSuffix().getDN(), baseDN))
{
servers.addAll(replica.getReplicationServers());
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.
*/
private Set<Integer> getReplicationDomainIds(String baseDN,
ServerDescriptor server)
{
Set<Integer> ids = new HashSet<>();
for (ReplicaDescriptor replica : server.getReplicas())
{
if (replica.isReplicated()
&& areDnsEqual(replica.getSuffix().getDN(), baseDN))
{
ids.add(replica.getReplicationId());
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.
*/
private void configureAsReplicationServer(InitialLdapContext ctx,
int replicationPort, boolean useSecureReplication,
Set<String> replicationServers,
Set<Integer> usedReplicationServerIds) throws OpenDsException
{
print(formatter.getFormattedWithPoints(
INFO_REPLICATION_ENABLE_CONFIGURING_REPLICATION_SERVER.get(getHostPort(ctx))));
ManagementContext mCtx = LDAPManagementContext.createFromContext(
JNDIDirContextAdaptor.adapt(ctx));
RootCfgClient root = mCtx.getRootConfiguration();
/* Configure Synchronization plugin. */
ReplicationSynchronizationProviderCfgClient sync = null;
try
{
sync = (ReplicationSynchronizationProviderCfgClient)
root.getSynchronizationProvider("Multimaster Synchronization");
}
catch (ManagedObjectNotFoundException monfe)
{
logger.info(LocalizableMessage.raw("Synchronization server does not exist in " + getHostPort(ctx)));
}
if (sync == null)
{
ReplicationSynchronizationProviderCfgDefn provider =
ReplicationSynchronizationProviderCfgDefn.getInstance();
sync = root.createSynchronizationProvider(provider,
"Multimaster Synchronization",
new ArrayList<PropertyException>());
sync.setJavaClass(
org.opends.server.replication.plugin.MultimasterReplication.class.
getName());
sync.setEnabled(Boolean.TRUE);
}
else if (!sync.isEnabled())
{
sync.setEnabled(Boolean.TRUE);
}
sync.commit();
/* Configure the replication server. */
ReplicationServerCfgClient replicationServer;
boolean mustCommit = false;
if (!sync.hasReplicationServer())
{
CryptoManagerCfgClient crypto = root.getCryptoManager();
if (useSecureReplication != crypto.isSSLEncryption())
{
crypto.setSSLEncryption(useSecureReplication);
crypto.commit();
}
int id = InstallerHelper.getReplicationId(usedReplicationServerIds);
usedReplicationServerIds.add(id);
replicationServer = sync.createReplicationServer(
ReplicationServerCfgDefn.getInstance(),
new ArrayList<PropertyException>());
replicationServer.setReplicationServerId(id);
replicationServer.setReplicationPort(replicationPort);
replicationServer.setReplicationServer(replicationServers);
mustCommit = true;
}
else
{
replicationServer = sync.getReplicationServer();
usedReplicationServerIds.add(
replicationServer.getReplicationServerId());
Set<String> servers = replicationServer.getReplicationServer();
if (servers == null)
{
replicationServer.setReplicationServer(replicationServers);
mustCommit = true;
}
else if (!areReplicationServersEqual(servers, replicationServers))
{
replicationServer.setReplicationServer(
mergeReplicationServers(replicationServers, servers));
mustCommit = true;
}
}
if (mustCommit)
{
replicationServer.commit();
}
print(formatter.getFormattedDone());
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.
*/
private void updateReplicationServer(InitialLdapContext ctx,
Set<String> replicationServers) throws OpenDsException
{
print(formatter.getFormattedWithPoints(
INFO_REPLICATION_ENABLE_UPDATING_REPLICATION_SERVER.get(getHostPort(ctx))));
ManagementContext mCtx = LDAPManagementContext.createFromContext(
JNDIDirContextAdaptor.adapt(ctx));
RootCfgClient root = mCtx.getRootConfiguration();
ReplicationSynchronizationProviderCfgClient sync =
(ReplicationSynchronizationProviderCfgClient)
root.getSynchronizationProvider("Multimaster Synchronization");
boolean mustCommit = false;
ReplicationServerCfgClient replicationServer = sync.getReplicationServer();
Set<String> servers = replicationServer.getReplicationServer();
if (servers == null)
{
replicationServer.setReplicationServer(replicationServers);
mustCommit = true;
}
else if (!areReplicationServersEqual(servers, replicationServers))
{
replicationServers.addAll(servers);
replicationServer.setReplicationServer(
mergeReplicationServers(replicationServers, servers));
mustCommit = true;
}
if (mustCommit)
{
replicationServer.commit();
}
print(formatter.getFormattedDone());
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.
*/
private Set<Integer> getReplicationServerIds(TopologyCache cache)
{
Set<Integer> ids = new HashSet<>();
for (ServerDescriptor server : cache.getServers())
{
if (server.isReplicationServer())
{
ids.add(server.getReplicationServerId());
}
}
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.
*/
private void configureToReplicateBaseDN(InitialLdapContext ctx,
String baseDN,
Set<String> replicationServers,
Set<Integer> usedReplicationDomainIds) throws OpenDsException
{
boolean userSpecifiedAdminBaseDN = false;
List<String> l = argParser.getBaseDNs();
if (l != null)
{
userSpecifiedAdminBaseDN = containsDN(l, ADSContext.getAdministrationSuffixDN());
}
if (!userSpecifiedAdminBaseDN
&& areDnsEqual(baseDN, ADSContext.getAdministrationSuffixDN()))
{
print(formatter.getFormattedWithPoints(
INFO_REPLICATION_ENABLE_CONFIGURING_ADS.get(getHostPort(ctx))));
}
else
{
print(formatter.getFormattedWithPoints(
INFO_REPLICATION_ENABLE_CONFIGURING_BASEDN.get(baseDN, getHostPort(ctx))));
}
ManagementContext mCtx = LDAPManagementContext.createFromContext(
JNDIDirContextAdaptor.adapt(ctx));
RootCfgClient root = mCtx.getRootConfiguration();
ReplicationSynchronizationProviderCfgClient sync =
(ReplicationSynchronizationProviderCfgClient)
root.getSynchronizationProvider("Multimaster Synchronization");
String[] domainNames = sync.listReplicationDomains();
if (domainNames == null)
{
domainNames = new String[]{};
}
ReplicationDomainCfgClient[] domains =
new ReplicationDomainCfgClient[domainNames.length];
for (int i=0; i<domains.length; i++)
{
domains[i] = sync.getReplicationDomain(domainNames[i]);
}
ReplicationDomainCfgClient domain = null;
for (ReplicationDomainCfgClient domain2 : domains)
{
if (areDnsEqual(baseDN, domain2.getBaseDN().toString()))
{
domain = domain2;
break;
}
}
boolean mustCommit = false;
if (domain == null)
{
int domainId = InstallerHelper.getReplicationId(usedReplicationDomainIds);
usedReplicationDomainIds.add(domainId);
String domainName =
InstallerHelper.getDomainName(domainNames, domainId, baseDN);
domain = sync.createReplicationDomain(
ReplicationDomainCfgDefn.getInstance(), domainName,
new ArrayList<PropertyException>());
domain.setServerId(domainId);
domain.setBaseDN(DN.valueOf(baseDN));
domain.setReplicationServer(replicationServers);
mustCommit = true;
}
else
{
Set<String> servers = domain.getReplicationServer();
if (servers == null)
{
domain.setReplicationServer(null);
mustCommit = true;
}
else if (!areReplicationServersEqual(servers, replicationServers))
{
domain.setReplicationServer(mergeReplicationServers(replicationServers,
servers));
mustCommit = true;
}
}
if (mustCommit)
{
domain.commit();
}
print(formatter.getFormattedDone());
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.
*/
private void configureToReplicateBaseDN(String baseDN,
Set<String> repServers, Set<Integer> usedIds,
TopologyCache cache, ServerDescriptor server,
Set<String> alreadyConfiguredServers, Set<String> allRepServers,
Set<String> alreadyConfiguredReplicationServers)
throws ReplicationCliException
{
logger.info(LocalizableMessage.raw("Configuring base DN '"+baseDN+
"' the replication servers are "+repServers));
Set<ServerDescriptor> serversToConfigureDomain = new HashSet<>();
Set<ServerDescriptor> replicationServersToConfigure = new HashSet<>();
SuffixDescriptor suffix = getSuffix(baseDN, cache, server);
if (suffix != null)
{
for (ReplicaDescriptor replica: suffix.getReplicas())
{
ServerDescriptor s = replica.getServer();
if (!alreadyConfiguredServers.contains(s.getId()))
{
serversToConfigureDomain.add(s);
}
}
}
// Now check the replication servers.
for (ServerDescriptor s : cache.getServers())
{
if (s.isReplicationServer()
&& !alreadyConfiguredReplicationServers.contains(s.getId())
// Check if it is part of the replication topology
&& containsIgnoreCase(repServers, s.getReplicationServerHostPort()))
{
replicationServersToConfigure.add(s);
}
}
Set<ServerDescriptor> allServers = new HashSet<>(serversToConfigureDomain);
allServers.addAll(replicationServersToConfigure);
for (ServerDescriptor s : allServers)
{
logger.info(LocalizableMessage.raw("Configuring server "+server.getHostPort(true)));
InitialLdapContext ctx = null;
try
{
ctx = getDirContextForServer(cache, s);
if (serversToConfigureDomain.contains(s))
{
configureToReplicateBaseDN(ctx, baseDN, repServers, usedIds);
}
if (replicationServersToConfigure.contains(s))
{
updateReplicationServer(ctx, allRepServers);
}
}
catch (NamingException ne)
{
String hostPort = getHostPort2(s, cache.getPreferredConnections());
LocalizableMessage msg = getMessageForException(ne, hostPort);
throw new ReplicationCliException(msg, ERROR_CONNECTING, ne);
}
catch (OpenDsException ode)
{
String hostPort = getHostPort2(s, cache.getPreferredConnections());
LocalizableMessage msg = getMessageForEnableException(hostPort, baseDN);
throw new ReplicationCliException(msg,
ERROR_ENABLING_REPLICATION_ON_BASEDN, ode);
}
finally
{
close(ctx);
}
alreadyConfiguredServers.add(s.getId());
alreadyConfiguredReplicationServers.add(s.getId());
}
}
/**
* 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
*/
private Map<ADSContext.AdministratorProperty, Object>
getAdministratorProperties(ReplicationUserData uData)
{
Map<ADSContext.AdministratorProperty, Object> adminProperties = new HashMap<>();
adminProperties.put(ADSContext.AdministratorProperty.UID, uData.getAdminUid());
adminProperties.put(ADSContext.AdministratorProperty.PASSWORD, uData.getAdminPwd());
adminProperties.put(ADSContext.AdministratorProperty.DESCRIPTION,
INFO_GLOBAL_ADMINISTRATOR_DESCRIPTION.get().toString());
return adminProperties;
}
private void initializeSuffix(String baseDN, InitialLdapContext ctxSource,
InitialLdapContext ctxDestination, boolean displayProgress)
throws ReplicationCliException
{
int replicationId = -1;
try
{
TopologyCacheFilter filter = new TopologyCacheFilter();
filter.setSearchMonitoringInformation(false);
filter.addBaseDNToSearch(baseDN);
ServerDescriptor source = ServerDescriptor.createStandalone(ctxSource, filter);
for (ReplicaDescriptor replica : source.getReplicas())
{
if (areDnsEqual(replica.getSuffix().getDN(), baseDN))
{
replicationId = replica.getReplicationId();
break;
}
}
}
catch (NamingException ne)
{
String hostPort = getHostPort(ctxSource);
LocalizableMessage msg = getMessageForException(ne, hostPort);
throw new ReplicationCliException(msg, ERROR_READING_CONFIGURATION, ne);
}
if (replicationId == -1)
{
throw new ReplicationCliException(
ERR_INITIALIZING_REPLICATIONID_NOT_FOUND.get(getHostPort(ctxSource), baseDN),
REPLICATIONID_NOT_FOUND, null);
}
OfflineInstaller installer = new OfflineInstaller();
installer.setProgressMessageFormatter(formatter);
installer.addProgressUpdateListener(new ProgressUpdateListener()
{
@Override
public void progressUpdate(ProgressUpdateEvent ev)
{
LocalizableMessage newLogDetails = ev.getNewLogs();
if (newLogDetails != null && !"".equals(newLogDetails.toString().trim()))
{
print(newLogDetails);
println();
}
}
});
int nTries = 5;
boolean initDone = false;
while (!initDone)
{
try
{
installer.initializeSuffix(ctxDestination, replicationId, baseDN, displayProgress, getHostPort(ctxSource));
initDone = true;
}
catch (PeerNotFoundException pnfe)
{
logger.info(LocalizableMessage.raw("Peer could not be found"));
if (nTries == 1)
{
throw new ReplicationCliException(
ERR_REPLICATION_INITIALIZING_TRIES_COMPLETED.get(
pnfe.getMessageObject()), INITIALIZING_TRIES_COMPLETED, pnfe);
}
sleepCatchInterrupt((5 - nTries) * 3000);
}
catch (ApplicationException ae)
{
throw new ReplicationCliException(ae.getMessageObject(),
ERROR_INITIALIZING_BASEDN_GENERIC, 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.
*/
public void initializeAllSuffix(String baseDN, InitialLdapContext ctx,
boolean displayProgress) throws ReplicationCliException
{
if (argParser == null)
{
try
{
createArgumenParser();
}
catch (ArgumentException ae)
{
throw new RuntimeException("Error creating argument parser: "+ae, ae);
}
}
int nTries = 5;
boolean initDone = false;
while (!initDone)
{
try
{
initializeAllSuffixTry(baseDN, ctx, displayProgress);
postPreExternalInitialization(baseDN, ctx, false);
initDone = true;
}
catch (PeerNotFoundException pnfe)
{
logger.info(LocalizableMessage.raw("Peer could not be found"));
if (nTries == 1)
{
throw new ReplicationCliException(
ERR_REPLICATION_INITIALIZING_TRIES_COMPLETED.get(
pnfe.getMessageObject()), INITIALIZING_TRIES_COMPLETED, pnfe);
}
sleepCatchInterrupt((5 - nTries) * 3000);
}
catch (ClientException ae)
{
throw new ReplicationCliException(ae.getMessageObject(),
ERROR_INITIALIZING_BASEDN_GENERIC, 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
{
postPreExternalInitialization(baseDN, ctx, true);
}
/**
* 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
{
postPreExternalInitialization(baseDN, ctx, false);
}
/**
* 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.
*/
private void postPreExternalInitialization(String baseDN,
InitialLdapContext ctx, boolean isPre) throws ReplicationCliException
{
boolean isOver = false;
String dn = null;
Map<String, String> attrMap = new TreeMap<>();
if (isPre)
{
attrMap.put("ds-task-reset-generation-id-new-value", "-1");
}
attrMap.put("ds-task-reset-generation-id-domain-base-dn", baseDN);
try {
dn = createServerTask(ctx, "ds-task-reset-generation-id", "org.opends.server.tasks.SetGenerationIdTask",
"dsreplication-reset-generation-id", attrMap);
}
catch (NamingException ne)
{
LocalizableMessage msg = isPre ?
ERR_LAUNCHING_PRE_EXTERNAL_INITIALIZATION.get():
ERR_LAUNCHING_POST_EXTERNAL_INITIALIZATION.get();
ReplicationCliReturnCode code = isPre?
ERROR_LAUNCHING_PRE_EXTERNAL_INITIALIZATION:
ERROR_LAUNCHING_POST_EXTERNAL_INITIALIZATION;
throw new ReplicationCliException(getThrowableMsg(msg, ne), code, ne);
}
String lastLogMsg = null;
while (!isOver)
{
sleepCatchInterrupt(500);
try
{
SearchResult sr = getLastSearchResult(ctx, dn, "ds-task-log-message", "ds-task-state");
String logMsg = getFirstValue(sr, "ds-task-log-message");
if (logMsg != null && !logMsg.equals(lastLogMsg))
{
logger.info(LocalizableMessage.raw(logMsg));
lastLogMsg = logMsg;
}
InstallerHelper helper = new InstallerHelper();
String state = getFirstValue(sr, "ds-task-state");
if (helper.isDone(state) || helper.isStoppedByError(state))
{
isOver = true;
LocalizableMessage errorMsg = getPrePostErrorMsg(lastLogMsg, state, ctx);
if (helper.isCompletedWithErrors(state))
{
logger.warn(LocalizableMessage.raw("Completed with error: "+errorMsg));
errPrintln(errorMsg);
}
else if (!helper.isSuccessful(state) ||
helper.isStoppedByError(state))
{
logger.warn(LocalizableMessage.raw("Error: "+errorMsg));
ReplicationCliReturnCode code = isPre?
ERROR_LAUNCHING_PRE_EXTERNAL_INITIALIZATION:
ERROR_LAUNCHING_POST_EXTERNAL_INITIALIZATION;
throw new ReplicationCliException(errorMsg, code, null);
}
}
}
catch (NameNotFoundException x)
{
isOver = true;
}
catch (NamingException ne)
{
throw new ReplicationCliException(getThrowableMsg(ERR_READING_SERVER_TASK_PROGRESS.get(), ne),
ERROR_CONNECTING, ne);
}
}
}
private LocalizableMessage getPrePostErrorMsg(String lastLogMsg, String state, InitialLdapContext ctx)
{
String server = getHostPort(ctx);
if (lastLogMsg != null)
{
return ERR_UNEXPECTED_DURING_TASK_WITH_LOG.get(lastLogMsg, state, server);
}
return ERR_UNEXPECTED_DURING_TASK_NO_LOG.get(state, server);
}
private void sleepCatchInterrupt(long millis)
{
try
{
Thread.sleep(millis);
}
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.
*/
public void initializeAllSuffixTry(String baseDN, InitialLdapContext ctx,
boolean displayProgress)
throws ClientException, PeerNotFoundException
{
boolean isOver = false;
String dn = null;
String serverDisplay = getHostPort(ctx);
Map<String, String> attrsMap = new TreeMap<>();
attrsMap.put("ds-task-initialize-domain-dn", baseDN);
attrsMap.put("ds-task-initialize-replica-server-id", "all");
try
{
dn = createServerTask(ctx, "ds-task-initialize-remote-replica", "org.opends.server.tasks.InitializeTargetTask",
"dsreplication-initialize", attrsMap);
}
catch (NamingException ne)
{
throw new ClientException(ReturnCode.APPLICATION_ERROR,
getThrowableMsg(INFO_ERROR_LAUNCHING_INITIALIZATION.get(serverDisplay), ne), ne);
}
LocalizableMessage lastDisplayedMsg = null;
String lastLogMsg = null;
long lastTimeMsgDisplayed = -1;
long lastTimeMsgLogged = -1;
long totalEntries = 0;
while (!isOver)
{
sleepCatchInterrupt(500);
try
{
SearchResult sr = getLastSearchResult(ctx, dn, "ds-task-unprocessed-entry-count",
"ds-task-processed-entry-count", "ds-task-log-message", "ds-task-state" );
// Get the number of entries that have been handled and a percentage...
String sProcessed = getFirstValue(sr, "ds-task-processed-entry-count");
String sUnprocessed = getFirstValue(sr, "ds-task-unprocessed-entry-count");
long processed = -1;
long unprocessed = -1;
if (sProcessed != null)
{
processed = Integer.parseInt(sProcessed);
}
if (sUnprocessed != null)
{
unprocessed = Integer.parseInt(sUnprocessed);
}
totalEntries = Math.max(totalEntries, processed+unprocessed);
LocalizableMessage msg = getMsg(lastDisplayedMsg, processed, unprocessed);
if (msg != null)
{
long currentTime = System.currentTimeMillis();
/* Refresh period: to avoid having too many lines in the log */
long minRefreshPeriod = getMinRefreshPeriod(totalEntries);
if (currentTime - minRefreshPeriod > lastTimeMsgLogged)
{
lastTimeMsgLogged = currentTime;
logger.info(LocalizableMessage.raw("Progress msg: "+msg));
}
if (displayProgress
&& currentTime - minRefreshPeriod > lastTimeMsgDisplayed
&& !msg.equals(lastDisplayedMsg))
{
print(msg);
lastDisplayedMsg = msg;
println();
lastTimeMsgDisplayed = currentTime;
}
}
String logMsg = getFirstValue(sr, "ds-task-log-message");
if (logMsg != null && !logMsg.equals(lastLogMsg))
{
logger.info(LocalizableMessage.raw(logMsg));
lastLogMsg = logMsg;
}
InstallerHelper helper = new InstallerHelper();
String state = getFirstValue(sr, "ds-task-state");
if (helper.isDone(state) || helper.isStoppedByError(state))
{
isOver = true;
logger.info(LocalizableMessage.raw("Last task entry: "+sr));
if (displayProgress && msg != null && !msg.equals(lastDisplayedMsg))
{
print(msg);
lastDisplayedMsg = msg;
println();
}
LocalizableMessage errorMsg = getInitializeAllErrorMsg(serverDisplay, lastLogMsg, state);
if (helper.isCompletedWithErrors(state))
{
logger.warn(LocalizableMessage.raw("Processed errorMsg: "+errorMsg));
if (displayProgress)
{
errPrintln(errorMsg);
}
}
else if (!helper.isSuccessful(state) ||
helper.isStoppedByError(state))
{
logger.warn(LocalizableMessage.raw("Processed errorMsg: "+errorMsg));
ClientException ce = new ClientException(
ReturnCode.APPLICATION_ERROR, errorMsg,
null);
if (lastLogMsg == null
|| helper.isPeersNotFoundError(lastLogMsg))
{
logger.warn(LocalizableMessage.raw("Throwing peer not found error. "+
"Last Log Msg: "+lastLogMsg));
// Assume that this is a peer not found error.
throw new PeerNotFoundException(errorMsg);
}
else
{
logger.error(LocalizableMessage.raw("Throwing ApplicationException."));
throw ce;
}
}
else
{
if (displayProgress)
{
print(INFO_SUFFIX_INITIALIZED_SUCCESSFULLY.get());
println();
}
logger.info(LocalizableMessage.raw("Processed msg: "+errorMsg));
logger.info(LocalizableMessage.raw("Initialization completed successfully."));
}
}
}
catch (NameNotFoundException x)
{
isOver = true;
logger.info(LocalizableMessage.raw("Initialization entry not found."));
if (displayProgress)
{
print(INFO_SUFFIX_INITIALIZED_SUCCESSFULLY.get());
println();
}
}
catch (NamingException ne)
{
throw new ClientException(
ReturnCode.APPLICATION_ERROR,
getThrowableMsg(INFO_ERROR_POOLING_INITIALIZATION.get(
serverDisplay), ne), ne);
}
}
}
private SearchResult getLastSearchResult(InitialLdapContext ctx, String dn, String... returnedAttributes)
throws NamingException
{
SearchControls searchControls = new SearchControls();
searchControls.setSearchScope(SearchControls.OBJECT_SCOPE);
searchControls.setReturningAttributes(returnedAttributes);
NamingEnumeration<SearchResult> res = ctx.search(dn, "objectclass=*", searchControls);
try
{
SearchResult sr = null;
while (res.hasMore())
{
sr = res.next();
}
return sr;
}
finally
{
res.close();
}
}
private String createServerTask(InitialLdapContext ctx, String taskObjectclass,
String taskJavaClass, String taskID, Map<String, String> taskAttrs) throws NamingException
{
int i = 1;
String dn = "";
BasicAttributes attrs = new BasicAttributes();
attrs.put("objectclass", taskObjectclass);
attrs.put("ds-task-class-name", taskJavaClass);
for (Map.Entry<String, String> attr : taskAttrs.entrySet())
{
attrs.put(attr.getKey(), attr.getValue());
}
while (true)
{
String id = taskID + "-" + i;
dn = "ds-task-id=" + id + ",cn=Scheduled Tasks,cn=Tasks";
try
{
DirContext dirCtx = ctx.createSubcontext(dn, attrs);
logger.info(LocalizableMessage.raw("created task entry: " + attrs));
dirCtx.close();
return dn;
}
catch (NameAlreadyBoundException x)
{
logger.warn(LocalizableMessage.raw("A task with dn: " + dn + " already existed."));
}
catch (NamingException ne)
{
logger.error(LocalizableMessage.raw("Error creating task " + attrs, ne));
throw ne;
}
i++;
}
}
private LocalizableMessage getInitializeAllErrorMsg(String serverDisplay, String lastLogMsg, String state)
{
if (lastLogMsg != null)
{
return INFO_ERROR_DURING_INITIALIZATION_LOG.get(serverDisplay, lastLogMsg, state, serverDisplay);
}
return INFO_ERROR_DURING_INITIALIZATION_NO_LOG.get(serverDisplay, state, serverDisplay);
}
private LocalizableMessage getMsg(LocalizableMessage lastDisplayedMsg, long processed, long unprocessed)
{
if (processed != -1 && unprocessed != -1)
{
if (processed + unprocessed > 0)
{
long perc = (100 * processed) / (processed + unprocessed);
return INFO_INITIALIZE_PROGRESS_WITH_PERCENTAGE.get(processed, perc);
}
else
{
// return INFO_NO_ENTRIES_TO_INITIALIZE.get();
return null;
}
}
else if (processed != -1)
{
return INFO_INITIALIZE_PROGRESS_WITH_PROCESSED.get(processed);
}
else if (unprocessed != -1)
{
return INFO_INITIALIZE_PROGRESS_WITH_UNPROCESSED.get(unprocessed);
}
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.
*/
private void removeReferencesInServer(ServerDescriptor server,
String replicationServer, String bindDn, String pwd,
Collection<String> baseDNs, boolean updateReplicationServers,
Set<PreferredConnection> cnx)
throws ReplicationCliException
{
TopologyCacheFilter filter = new TopologyCacheFilter();
filter.setSearchMonitoringInformation(false);
filter.setSearchBaseDNInformation(false);
ServerLoader loader = new ServerLoader(server.getAdsProperties(), bindDn,
pwd, getTrustManager(sourceServerCI), getConnectTimeout(), cnx, filter);
InitialLdapContext ctx = null;
String lastBaseDN = null;
String hostPort = null;
try
{
ctx = loader.createContext();
hostPort = getHostPort(ctx);
ManagementContext mCtx = LDAPManagementContext.createFromContext(
JNDIDirContextAdaptor.adapt(ctx));
RootCfgClient root = mCtx.getRootConfiguration();
ReplicationSynchronizationProviderCfgClient sync = null;
try
{
sync = (ReplicationSynchronizationProviderCfgClient)
root.getSynchronizationProvider("Multimaster Synchronization");
}
catch (ManagedObjectNotFoundException monfe)
{
// It does not exist.
logger.info(LocalizableMessage.raw("No synchronization found on "+ hostPort +".",
monfe));
}
if (sync != null)
{
String[] domainNames = sync.listReplicationDomains();
if (domainNames != null)
{
for (String domainName : domainNames)
{
ReplicationDomainCfgClient domain =
sync.getReplicationDomain(domainName);
for (String baseDN : baseDNs)
{
lastBaseDN = baseDN;
if (areDnsEqual(domain.getBaseDN().toString(), baseDN))
{
print(formatter.getFormattedWithPoints(
INFO_REPLICATION_REMOVING_REFERENCES_ON_REMOTE.get(baseDN, hostPort)));
Set<String> replServers = domain.getReplicationServer();
if (replServers != null)
{
String replServer = findIgnoreCase(replServers, replicationServer);
if (replServer != null)
{
logger.info(LocalizableMessage.raw("Updating references in domain " +
domain.getBaseDN()+" on " + hostPort + "."));
replServers.remove(replServer);
if (!replServers.isEmpty())
{
domain.setReplicationServer(replServers);
domain.commit();
}
else
{
sync.removeReplicationDomain(domainName);
sync.commit();
}
}
}
print(formatter.getFormattedDone());
println();
}
}
}
}
if (updateReplicationServers && sync.hasReplicationServer())
{
ReplicationServerCfgClient rServerObj = sync.getReplicationServer();
Set<String> replServers = rServerObj.getReplicationServer();
if (replServers != null)
{
String replServer = findIgnoreCase(replServers, replicationServer);
if (replServer != null)
{
replServers.remove(replServer);
if (!replServers.isEmpty())
{
rServerObj.setReplicationServer(replServers);
rServerObj.commit();
}
else
{
sync.removeReplicationServer();
sync.commit();
}
}
}
}
}
}
catch (NamingException ne)
{
hostPort = getHostPort2(server, cnx);
LocalizableMessage msg = getMessageForException(ne, hostPort);
throw new ReplicationCliException(msg, ERROR_CONNECTING, ne);
}
catch (OpenDsException ode)
{
if (lastBaseDN != null)
{
LocalizableMessage msg = getMessageForDisableException(hostPort, lastBaseDN);
throw new ReplicationCliException(msg,
ERROR_DISABLING_REPLICATION_REMOVE_REFERENCE_ON_BASEDN, ode);
}
else
{
LocalizableMessage msg = ERR_REPLICATION_ERROR_READING_CONFIGURATION.get(hostPort,
ode.getMessage());
throw new ReplicationCliException(msg, ERROR_CONNECTING, ode);
}
}
finally
{
close(ctx);
}
}
/**
* 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.
*/
private void deleteReplicationDomain(InitialLdapContext ctx,
String baseDN) throws ReplicationCliException
{
String hostPort = getHostPort(ctx);
try
{
ManagementContext mCtx = LDAPManagementContext.createFromContext(
JNDIDirContextAdaptor.adapt(ctx));
RootCfgClient root = mCtx.getRootConfiguration();
ReplicationSynchronizationProviderCfgClient sync = null;
try
{
sync = (ReplicationSynchronizationProviderCfgClient)
root.getSynchronizationProvider("Multimaster Synchronization");
}
catch (ManagedObjectNotFoundException monfe)
{
// It does not exist.
logger.info(LocalizableMessage.raw("No synchronization found on "+ hostPort +".",
monfe));
}
if (sync != null)
{
String[] domainNames = sync.listReplicationDomains();
if (domainNames != null)
{
for (String domainName : domainNames)
{
ReplicationDomainCfgClient domain =
sync.getReplicationDomain(domainName);
if (areDnsEqual(domain.getBaseDN().toString(), baseDN))
{
print(formatter.getFormattedWithPoints(
INFO_REPLICATION_DISABLING_BASEDN.get(baseDN, hostPort)));
sync.removeReplicationDomain(domainName);
sync.commit();
print(formatter.getFormattedDone());
println();
}
}
}
}
}
catch (OpenDsException ode)
{
LocalizableMessage msg = getMessageForDisableException(hostPort, baseDN);
throw new ReplicationCliException(msg,
ERROR_DISABLING_REPLICATION_REMOVE_REFERENCE_ON_BASEDN, ode);
}
}
/**
* 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.
*/
private void disableReplicationServer(InitialLdapContext ctx)
throws ReplicationCliException
{
String hostPort = getHostPort(ctx);
try
{
ManagementContext mCtx = LDAPManagementContext.createFromContext(
JNDIDirContextAdaptor.adapt(ctx));
RootCfgClient root = mCtx.getRootConfiguration();
ReplicationSynchronizationProviderCfgClient sync = null;
ReplicationServerCfgClient replicationServer = null;
try
{
sync = (ReplicationSynchronizationProviderCfgClient)
root.getSynchronizationProvider("Multimaster Synchronization");
if (sync.hasReplicationServer())
{
replicationServer = sync.getReplicationServer();
}
}
catch (ManagedObjectNotFoundException monfe)
{
// It does not exist.
logger.info(LocalizableMessage.raw("No synchronization found on "+ hostPort +".",
monfe));
}
if (replicationServer != null)
{
String s = String.valueOf(replicationServer.getReplicationPort());
print(formatter.getFormattedWithPoints(
INFO_REPLICATION_DISABLING_REPLICATION_SERVER.get(s,
hostPort)));
sync.removeReplicationServer();
sync.commit();
print(formatter.getFormattedDone());
println();
}
}
catch (OpenDsException ode)
{
throw new ReplicationCliException(
ERR_REPLICATION_DISABLING_REPLICATIONSERVER.get(hostPort),
ERROR_DISABLING_REPLICATION_SERVER,
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 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).
*/
private LocalizableMessage getMessageForEnableException(String hostPort, String baseDN)
{
return ERR_REPLICATION_CONFIGURING_BASEDN.get(baseDN, hostPort);
}
/**
* 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).
*/
private LocalizableMessage getMessageForDisableException(String hostPort, String baseDN)
{
return ERR_REPLICATION_CONFIGURING_BASEDN.get(baseDN, hostPort);
}
/**
* 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.
*/
private LocalizableMessage getCannotBindToPortError(int port)
{
if (SetupUtils.isPrivilegedPort(port))
{
return ERR_CANNOT_BIND_TO_PRIVILEGED_PORT.get(port);
}
return ERR_CANNOT_BIND_TO_PORT.get(port);
}
/**
* 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.
*/
private boolean areReplicationServersEqual(Set<String> s1, Set<String> s2)
{
Set<String> c1 = new HashSet<>();
for (String s : s1)
{
c1.add(s.toLowerCase());
}
Set<String> c2 = new HashSet<>();
for (String s : s2)
{
c2.add(s.toLowerCase());
}
return c1.equals(c2);
}
/**
* 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.
*/
private Set<String> mergeReplicationServers(Set<String> s1, Set<String> s2)
{
Set<String> c1 = new HashSet<>();
for (String s : s1)
{
c1.add(s.toLowerCase());
}
for (String s : s2)
{
c1.add(s.toLowerCase());
}
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.
*/
private LocalizableMessage getCriticalExceptionMessage(ReplicationCliException rce)
{
LocalizableMessageBuilder mb = new LocalizableMessageBuilder();
mb.append(rce.getMessageObject());
File logFile = ControlPanelLog.getLogFile();
if (logFile != null && rce.getErrorCode() != USER_CANCELLED)
{
mb.append(Constants.LINE_SEPARATOR);
mb.append(INFO_GENERAL_SEE_FOR_DETAILS.get(logFile.getPath()));
}
// Check if the cause has already been included in the message
Throwable c = rce.getCause();
if (c != null)
{
String s;
if (c instanceof NamingException)
{
s = ((NamingException)c).toString(true);
}
else if (c instanceof OpenDsException)
{
LocalizableMessage msg = ((OpenDsException)c).getMessageObject();
if (msg != null)
{
s = msg.toString();
}
else
{
s = c.toString();
}
}
else
{
s = c.toString();
}
if (!mb.toString().contains(s))
{
mb.append(Constants.LINE_SEPARATOR);
mb.append(INFO_REPLICATION_CRITICAL_ERROR_DETAILS.get(s));
}
}
return mb.toMessage();
}
private boolean mustInitializeSchema(ServerDescriptor server1,
ServerDescriptor server2, EnableReplicationUserData uData)
{
boolean mustInitializeSchema = false;
if (!argParser.noSchemaReplication())
{
String id1 = server1.getSchemaReplicationID();
String id2 = server2.getSchemaReplicationID();
mustInitializeSchema = id1 == null || !id1.equals(id2);
}
if (mustInitializeSchema)
{
// Check that both will contain replication data
mustInitializeSchema = uData.getServer1().configureReplicationDomain()
&& uData.getServer2().configureReplicationDomain();
}
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.
*/
private void registerServer(ADSContext adsContext, Map<ServerProperty, Object> serverProperties)
throws ADSContextException
{
try
{
adsContext.registerServer(serverProperties);
}
catch (ADSContextException ade)
{
if (ade.getError() ==
ADSContextException.ErrorType.ALREADY_REGISTERED)
{
logger.warn(LocalizableMessage.raw("The server was already registered: "+
serverProperties));
adsContext.unregisterServer(serverProperties);
adsContext.registerServer(serverProperties);
}
else
{
throw ade;
}
}
}
/** {@inheritDoc} */
@Override
public boolean isAdvancedMode() {
return false;
}
/** {@inheritDoc} */
@Override
public boolean isInteractive() {
return !forceNonInteractive && argParser.isInteractive();
}
/** {@inheritDoc} */
@Override
public boolean isMenuDrivenMode() {
return true;
}
/** {@inheritDoc} */
@Override
public boolean isQuiet()
{
return argParser.isQuiet();
}
/** {@inheritDoc} */
@Override
public boolean isScriptFriendly() {
return argParser.isScriptFriendly();
}
/** {@inheritDoc} */
@Override
public boolean isVerbose() {
return true;
}
/**
* Forces the initialization of the trust manager in the LDAPConnectionInteraction object.
* @param ci the LDAP connection to the server
*/
private void forceTrustManagerInitialization(LDAPConnectionConsoleInteraction ci)
{
forceNonInteractive = true;
try
{
ci.initializeTrustManagerIfRequired();
}
catch (ArgumentException ae)
{
logger.warn(LocalizableMessage.raw("Error initializing trust store: "+ae, 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 registry1.size() == registry2.size()
&& equals(registry1, registry2, getPropertiesToCompare());
}
private Set<ServerProperty> getPropertiesToCompare()
{
final Set<ServerProperty> propertiesToCompare = new HashSet<>();
for (ServerProperty property : ServerProperty.values())
{
if (property.getAttributeSyntax() != ADSPropertySyntax.CERTIFICATE_BINARY)
{
propertiesToCompare.add(property);
}
}
return propertiesToCompare;
}
private boolean equals(Set<Map<ServerProperty, Object>> registry1, Set<Map<ServerProperty, Object>> registry2,
Set<ServerProperty> propertiesToCompare)
{
for (Map<ServerProperty, Object> server1 : registry1)
{
if (!exists(registry2, server1, propertiesToCompare))
{
return false;
}
}
return true;
}
private boolean exists(Set<Map<ServerProperty, Object>> registry2, Map<ServerProperty, Object> server1,
Set<ServerProperty> propertiesToCompare)
{
for (Map<ServerProperty, Object> server2 : registry2)
{
if (equals(server1, server2, propertiesToCompare))
{
return true;
}
}
return false;
}
private boolean equals(Map<ServerProperty, Object> server1, Map<ServerProperty, Object> server2,
Set<ServerProperty> propertiesToCompare)
{
for (ServerProperty prop : propertiesToCompare)
{
if (!Objects.equals(server1.get(prop), server2.get(prop)))
{
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.
*/
private boolean disableAllBaseDns(InitialLdapContext ctx,
DisableReplicationUserData uData)
{
if (uData.disableAll())
{
return true;
}
Collection<ReplicaDescriptor> replicas = getReplicas(ctx);
Set<String> replicatedSuffixes = new HashSet<>();
for (ReplicaDescriptor rep : replicas)
{
String dn = rep.getSuffix().getDN();
if (rep.isReplicated())
{
replicatedSuffixes.add(dn);
}
}
for (String dn1 : replicatedSuffixes)
{
if (!areDnsEqual(ADSContext.getAdministrationSuffixDN(), dn1)
&& !areDnsEqual(Constants.SCHEMA_DN, dn1)
&& !containsDN(uData.getBaseDNs(), dn1))
{
return false;
}
}
return true;
}
private boolean containsDN(final Collection<String> dns, String dnToFind)
{
for (String dn : dns)
{
if (areDnsEqual(dn, dnToFind))
{
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.
*/
private String getHostPort2(ServerDescriptor server,
Collection<PreferredConnection> cnx)
{
String hostPort = null;
for (PreferredConnection connection : cnx)
{
String url = connection.getLDAPURL();
if (url.equals(server.getLDAPURL()))
{
hostPort = server.getHostPort(false);
}
else if (url.equals(server.getLDAPsURL()))
{
hostPort = server.getHostPort(true);
}
}
if (hostPort != null)
{
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()
{
MenuBuilder<SubcommandChoice> builder = new MenuBuilder<>(this);
builder.setPrompt(INFO_REPLICATION_SUBCOMMAND_PROMPT.get());
builder.addCancelOption(false);
for (SubcommandChoice choice : SubcommandChoice.values())
{
if (choice != SubcommandChoice.CANCEL)
{
builder.addNumberedOption(choice.getPrompt(),
MenuResult.success(choice));
}
}
try
{
MenuResult<SubcommandChoice> m = builder.toMenu().run();
if (m.isSuccess())
{
return m.getValue();
}
// The user cancelled
return SubcommandChoice.CANCEL;
}
catch (ClientException ce)
{
logger.warn(LocalizableMessage.raw("Error reading input: "+ce, ce));
return SubcommandChoice.CANCEL;
}
}
private boolean mustPrintCommandBuilder()
{
return argParser.isInteractive() &&
(argParser.displayEquivalentArgument.isPresent() ||
argParser.equivalentCommandFileArgument.isPresent());
}
/**
* 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 subCommandName the command builder to be printed.
* @param uData input parameters from cli
*/
private void printNewCommandBuilder(String subCommandName, ReplicationUserData uData)
{
try
{
final CommandBuilder commandBuilder = createCommandBuilder(sourceServerCI, subCommandName, uData);
if (argParser.displayEquivalentArgument.isPresent())
{
println();
// We assume that the app we are running is this one.
println(INFO_REPLICATION_NON_INTERACTIVE.get(commandBuilder));
}
if (argParser.equivalentCommandFileArgument.isPresent())
{
// Write to the file.
String file = argParser.equivalentCommandFileArgument.getValue();
try
{
BufferedWriter writer = new BufferedWriter(new FileWriter(file, true));
writer.write(SHELL_COMMENT_SEPARATOR+getCurrentOperationDateMessage());
writer.newLine();
writer.write(commandBuilder.toString());
writer.newLine();
writer.newLine();
writer.flush();
writer.close();
}
catch (IOException ioe)
{
errPrintln(ERR_REPLICATION_ERROR_WRITING_EQUIVALENT_COMMAND_LINE.get(file, ioe));
}
}
}
catch (Throwable t)
{
logger.error(LocalizableMessage.raw("Error printing equivalent command-line: " + t), 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 ci the LDAP connection to the server
* @param subcommandName the subcommand name.
* @param uData the user data.
* @return the command builder that has been created with the specified
* subcommandName.
*/
private CommandBuilder createCommandBuilder(LDAPConnectionConsoleInteraction ci, String subcommandName,
ReplicationUserData uData) throws ArgumentException
{
String commandName = getCommandName();
CommandBuilder commandBuilder = new CommandBuilder(commandName, subcommandName);
if (ENABLE_REPLICATION_SUBCMD_NAME.equals(subcommandName))
{
// All the arguments for enable replication are update here.
updateCommandBuilder(commandBuilder, (EnableReplicationUserData)uData);
}
else if (INITIALIZE_REPLICATION_SUBCMD_NAME.equals(subcommandName) ||
RESET_CHANGE_NUMBER_SUBCMD_NAME.equals(subcommandName))
{
// All the arguments for initialize replication are update here.
updateCommandBuilder(commandBuilder, (SourceDestinationServerUserData)uData);
}
else if (PURGE_HISTORICAL_SUBCMD_NAME.equals(subcommandName))
{
// All the arguments for initialize replication are update here.
updateCommandBuilder(ci, commandBuilder, (PurgeHistoricalUserData)uData);
}
else
{
// Update the arguments used in the console interaction with the
// actual arguments of dsreplication.
updateCommandBuilderWithConsoleInteraction(commandBuilder, ci);
}
if (DISABLE_REPLICATION_SUBCMD_NAME.equals(subcommandName))
{
DisableReplicationUserData disableData =
(DisableReplicationUserData)uData;
if (disableData.disableAll())
{
commandBuilder.addArgument(newBooleanArgument(
argParser.disableAllArg, INFO_DESCRIPTION_DISABLE_ALL));
}
else if (disableData.disableReplicationServer())
{
commandBuilder.addArgument(newBooleanArgument(
argParser.disableReplicationServerArg, INFO_DESCRIPTION_DISABLE_REPLICATION_SERVER));
}
}
addGlobalArguments(commandBuilder, uData);
return commandBuilder;
}
private String getCommandName()
{
String commandName = System.getProperty(ServerConstants.PROPERTY_SCRIPT_NAME);
if (commandName != null)
{
return commandName;
}
return "dsreplication";
}
private void updateCommandBuilderWithConsoleInteraction(CommandBuilder commandBuilder,
LDAPConnectionConsoleInteraction ci) throws ArgumentException
{
if (ci != null && ci.getCommandBuilder() != null)
{
CommandBuilder interactionBuilder = ci.getCommandBuilder();
for (Argument arg : interactionBuilder.getArguments())
{
if (OPTION_LONG_BINDPWD.equals(arg.getLongIdentifier()))
{
commandBuilder.addObfuscatedArgument(getAdminPasswordArg(arg));
}
else if (OPTION_LONG_BINDPWD_FILE.equals(arg.getLongIdentifier()))
{
commandBuilder.addArgument(getAdminPasswordFileArg(arg));
}
else
{
addArgument(commandBuilder, arg, interactionBuilder.isObfuscated(arg));
}
}
}
}
private void updateCommandBuilder(LDAPConnectionConsoleInteraction ci, CommandBuilder commandBuilder,
PurgeHistoricalUserData uData) throws ArgumentException
{
if (uData.isOnline())
{
updateCommandBuilderWithConsoleInteraction(commandBuilder, ci);
if (uData.getTaskSchedule() != null)
{
updateCommandBuilderWithTaskSchedule(commandBuilder,
uData.getTaskSchedule());
}
}
IntegerArgument maximumDurationArg = new IntegerArgument(
argParser.maximumDurationArg.getName(),
argParser.maximumDurationArg.getShortIdentifier(),
argParser.maximumDurationArg.getLongIdentifier(),
argParser.maximumDurationArg.isRequired(),
argParser.maximumDurationArg.isMultiValued(),
argParser.maximumDurationArg.needsValue(),
argParser.maximumDurationArg.getValuePlaceholder(),
PurgeConflictsHistoricalTask.DEFAULT_MAX_DURATION,
argParser.maximumDurationArg.getPropertyName(),
argParser.maximumDurationArg.getDescription());
maximumDurationArg.addValue(String.valueOf(uData.getMaximumDuration()));
commandBuilder.addArgument(maximumDurationArg);
}
private void updateCommandBuilderWithTaskSchedule(
CommandBuilder commandBuilder,
TaskScheduleUserData taskSchedule)
{
TaskScheduleUserData.updateCommandBuilderWithTaskSchedule(
commandBuilder, taskSchedule);
}
private void addGlobalArguments(CommandBuilder commandBuilder, ReplicationUserData uData)
throws ArgumentException
{
List<String> baseDNs = uData.getBaseDNs();
StringArgument baseDNsArg = new StringArgument("baseDNs",
OPTION_SHORT_BASEDN,
OPTION_LONG_BASEDN, false, true, true, INFO_BASEDN_PLACEHOLDER.get(),
null,
null, INFO_DESCRIPTION_REPLICATION_BASEDNS.get());
for (String baseDN : baseDNs)
{
baseDNsArg.addValue(baseDN);
}
commandBuilder.addArgument(baseDNsArg);
if (argParser.resetChangeNumber.isPresent())
{
commandBuilder.addArgument(argParser.resetChangeNumber);
}
// Try to find some arguments and put them at the end.
String[] identifiersToMove ={
OPTION_LONG_ADMIN_UID,
"adminPassword",
"adminPasswordFile",
OPTION_LONG_SASLOPTION,
OPTION_LONG_TRUSTALL,
OPTION_LONG_TRUSTSTOREPATH,
OPTION_LONG_TRUSTSTORE_PWD,
OPTION_LONG_TRUSTSTORE_PWD_FILE,
OPTION_LONG_KEYSTOREPATH,
OPTION_LONG_KEYSTORE_PWD,
OPTION_LONG_KEYSTORE_PWD_FILE,
OPTION_LONG_CERT_NICKNAME
};
ArrayList<Argument> toMoveArgs = new ArrayList<>();
for (String longID : identifiersToMove)
{
final Argument arg = findArg(commandBuilder, longID);
if (arg != null)
{
toMoveArgs.add(arg);
}
}
for (Argument argToMove : toMoveArgs)
{
boolean toObfuscate = commandBuilder.isObfuscated(argToMove);
commandBuilder.removeArgument(argToMove);
if (toObfuscate)
{
commandBuilder.addObfuscatedArgument(argToMove);
}
else
{
commandBuilder.addArgument(argToMove);
}
}
if (argParser.isVerbose())
{
commandBuilder.addArgument(new BooleanArgument("verbose",
OPTION_SHORT_VERBOSE,
OPTION_LONG_VERBOSE, INFO_DESCRIPTION_VERBOSE.get()));
}
if (argParser.isScriptFriendly())
{
commandBuilder.addArgument(argParser.scriptFriendlyArg);
}
commandBuilder.addArgument(argParser.noPromptArg);
if (argParser.propertiesFileArgument.isPresent())
{
commandBuilder.addArgument(argParser.propertiesFileArgument);
}
if (argParser.noPropertiesFileArgument.isPresent())
{
commandBuilder.addArgument(argParser.noPropertiesFileArgument);
}
}
private Argument findArg(CommandBuilder commandBuilder, String longIdentifier)
{
for (Argument arg : commandBuilder.getArguments())
{
if (longIdentifier.equals(arg.getLongIdentifier()))
{
return arg;
}
}
return null;
}
private boolean existsArg(CommandBuilder commandBuilder, String longIdentifier)
{
return findArg(commandBuilder, longIdentifier) != null;
}
private void addArgument(CommandBuilder commandBuilder, Argument arg, boolean isObfuscated)
{
if (isObfuscated)
{
commandBuilder.addObfuscatedArgument(arg);
}
else
{
commandBuilder.addArgument(arg);
}
}
private void updateCommandBuilder(CommandBuilder commandBuilder, EnableReplicationUserData uData)
throws ArgumentException
{
// Update the arguments used in the console interaction with the
// actual arguments of dsreplication.
boolean adminInformationAdded = false;
EnableReplicationServerData server1 = uData.getServer1();
if (firstServerCommandBuilder != null)
{
boolean useAdminUID = existsArg(firstServerCommandBuilder, OPTION_LONG_ADMIN_UID);
// 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)
{
String bindDN1 = server1.getBindDn();
String adminUID = uData.getAdminUid();
if (bindDN1 != null
&& adminUID != null
&& !areDnsEqual(getAdministratorDN(adminUID), bindDN1))
{
forceAddBindDN1 = true;
forceAddBindPwdFile1 = existsArg(firstServerCommandBuilder, OPTION_LONG_BINDPWD_FILE);
}
}
for (Argument arg : firstServerCommandBuilder.getArguments())
{
if (OPTION_LONG_HOST.equals(arg.getLongIdentifier()))
{
commandBuilder.addArgument(getHostArg("host1", OPTION_SHORT_HOST, server1.getHostName(),
INFO_DESCRIPTION_ENABLE_REPLICATION_HOST1));
}
else if (OPTION_LONG_PORT.equals(arg.getLongIdentifier()))
{
commandBuilder.addArgument(getPortArg("port1", OPTION_SHORT_PORT, server1.getPort(),
INFO_DESCRIPTION_ENABLE_REPLICATION_SERVER_PORT1));
if (forceAddBindDN1)
{
commandBuilder.addArgument(getBindDN1Arg(uData));
if (forceAddBindPwdFile1)
{
FileBasedArgument bindPasswordFileArg = getBindPasswordFile1Arg();
bindPasswordFileArg.getNameToValueMap().put("{password file}",
"{password file}");
commandBuilder.addArgument(bindPasswordFileArg);
}
else
{
commandBuilder.addObfuscatedArgument(getBindPassword1Arg(arg));
}
}
}
else if (OPTION_LONG_BINDDN.equals(arg.getLongIdentifier()))
{
commandBuilder.addArgument(getBindDN1Arg(uData));
}
else if (OPTION_LONG_BINDPWD.equals(arg.getLongIdentifier()))
{
if (useAdminUID)
{
adminInformationAdded = true;
commandBuilder.addObfuscatedArgument(getAdminPasswordArg(arg));
}
else
{
commandBuilder.addObfuscatedArgument(getBindPassword1Arg(arg));
}
}
else if (OPTION_LONG_BINDPWD_FILE.equals(arg.getLongIdentifier()))
{
if (useAdminUID)
{
commandBuilder.addArgument(getAdminPasswordFileArg(arg));
}
else
{
FileBasedArgument bindPasswordFileArg = getBindPasswordFile1Arg();
bindPasswordFileArg.getNameToValueMap().putAll(
((FileBasedArgument)arg).getNameToValueMap());
commandBuilder.addArgument(bindPasswordFileArg);
}
}
else
{
if (OPTION_LONG_ADMIN_UID.equals(arg.getLongIdentifier()))
{
adminInformationAdded = true;
}
addArgument(commandBuilder, arg, firstServerCommandBuilder.isObfuscated(arg));
}
}
}
EnableReplicationServerData server2 = uData.getServer2();
if (sourceServerCI != null && sourceServerCI.getCommandBuilder() != null)
{
CommandBuilder interactionBuilder = sourceServerCI.getCommandBuilder();
boolean useAdminUID = existsArg(interactionBuilder, OPTION_LONG_ADMIN_UID);
boolean hasBindDN = existsArg(interactionBuilder, OPTION_LONG_BINDDN);
// 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)
{
String bindDN2 = server2.getBindDn();
String adminUID = uData.getAdminUid();
if (bindDN2 != null
&& adminUID != null
&& !areDnsEqual(getAdministratorDN(adminUID), bindDN2))
{
forceAddBindDN2 = true;
forceAddBindPwdFile2 = existsArg(interactionBuilder, OPTION_LONG_BINDPWD_FILE);
}
}
ArrayList<Argument> argsToAnalyze = new ArrayList<>();
for (Argument arg : interactionBuilder.getArguments())
{
if (OPTION_LONG_HOST.equals(arg.getLongIdentifier()))
{
commandBuilder.addArgument(
getHostArg("host2", 'O', server2.getHostName(), INFO_DESCRIPTION_ENABLE_REPLICATION_HOST2));
}
else if (OPTION_LONG_PORT.equals(arg.getLongIdentifier()))
{
commandBuilder.addArgument(getPortArg("port2", null, server2.getPort(),
INFO_DESCRIPTION_ENABLE_REPLICATION_SERVER_PORT2));
if (forceAddBindDN2)
{
commandBuilder.addArgument(getBindDN2Arg(uData, OPTION_SHORT_BINDDN));
if (forceAddBindPwdFile2)
{
FileBasedArgument bindPasswordFileArg = getBindPasswordFile2Arg();
bindPasswordFileArg.getNameToValueMap().put("{password file}",
"{password file}");
commandBuilder.addArgument(bindPasswordFileArg);
}
else
{
commandBuilder.addObfuscatedArgument(getBindPassword2Arg(arg));
}
}
}
else if (OPTION_LONG_BINDDN.equals(arg.getLongIdentifier()))
{
commandBuilder.addArgument(getBindDN2Arg(uData, null));
}
else if (OPTION_LONG_BINDPWD.equals(arg.getLongIdentifier()))
{
if (useAdminUID && !adminInformationAdded)
{
adminInformationAdded = true;
commandBuilder.addObfuscatedArgument(getAdminPasswordArg(arg));
}
else if (hasBindDN)
{
commandBuilder.addObfuscatedArgument(getBindPassword2Arg(arg));
}
}
else if (OPTION_LONG_BINDPWD_FILE.equals(arg.getLongIdentifier()))
{
if (useAdminUID && !adminInformationAdded)
{
adminInformationAdded = true;
commandBuilder.addArgument(getAdminPasswordFileArg(arg));
}
else if (hasBindDN)
{
FileBasedArgument bindPasswordFileArg = getBindPasswordFile2Arg();
bindPasswordFileArg.getNameToValueMap().putAll(
((FileBasedArgument)arg).getNameToValueMap());
commandBuilder.addArgument(bindPasswordFileArg);
}
}
else
{
argsToAnalyze.add(arg);
}
}
for (Argument arg : argsToAnalyze)
{
// Just check that the arguments have not already been added.
if (!existsArg(commandBuilder, arg.getLongIdentifier()))
{
addArgument(commandBuilder, arg, interactionBuilder.isObfuscated(arg));
}
}
}
// Try to add the new administration information.
if (!adminInformationAdded)
{
if (uData.getAdminUid() != null)
{
StringArgument adminUID = new StringArgument(OPTION_LONG_ADMIN_UID, 'I',
OPTION_LONG_ADMIN_UID, false, false, true,
INFO_ADMINUID_PLACEHOLDER.get(),
Constants.GLOBAL_ADMIN_UID, null,
INFO_DESCRIPTION_REPLICATION_ADMIN_UID.get(ENABLE_REPLICATION_SUBCMD_NAME));
adminUID.addValue(uData.getAdminUid());
commandBuilder.addArgument(adminUID);
}
if (userProvidedAdminPwdFile != null)
{
commandBuilder.addArgument(userProvidedAdminPwdFile);
}
else if (uData.getAdminPwd() != null)
{
Argument bindPasswordArg = getAdminPasswordArg();
bindPasswordArg.addValue(uData.getAdminPwd());
commandBuilder.addObfuscatedArgument(bindPasswordArg);
}
}
if (server1.configureReplicationServer() &&
!server1.configureReplicationDomain())
{
commandBuilder.addArgument(newBooleanArgument(
argParser.server1.onlyReplicationServerArg, INFO_DESCRIPTION_ENABLE_REPLICATION_ONLY_REPLICATION_SERVER1));
}
if (!server1.configureReplicationServer() &&
server1.configureReplicationDomain())
{
commandBuilder.addArgument(newBooleanArgument(
argParser.server1.noReplicationServerArg, INFO_DESCRIPTION_ENABLE_REPLICATION_NO_REPLICATION_SERVER1));
}
if (server1.configureReplicationServer() &&
server1.getReplicationPort() > 0)
{
commandBuilder.addArgument(getReplicationPortArg(
"replicationPort1", server1, 8989, INFO_DESCRIPTION_ENABLE_REPLICATION_PORT1));
}
if (server1.isSecureReplication())
{
commandBuilder.addArgument(
newBooleanArgument("secureReplication1", INFO_DESCRIPTION_ENABLE_SECURE_REPLICATION1));
}
if (server2.configureReplicationServer() &&
!server2.configureReplicationDomain())
{
commandBuilder.addArgument(newBooleanArgument(
argParser.server2.onlyReplicationServerArg, INFO_DESCRIPTION_ENABLE_REPLICATION_ONLY_REPLICATION_SERVER2));
}
if (!server2.configureReplicationServer() &&
server2.configureReplicationDomain())
{
commandBuilder.addArgument(newBooleanArgument(
argParser.server2.noReplicationServerArg, INFO_DESCRIPTION_ENABLE_REPLICATION_NO_REPLICATION_SERVER2));
}
if (server2.configureReplicationServer() &&
server2.getReplicationPort() > 0)
{
commandBuilder.addArgument(getReplicationPortArg(
"replicationPort2", server2, server2.getReplicationPort(), INFO_DESCRIPTION_ENABLE_REPLICATION_PORT2));
}
if (server2.isSecureReplication())
{
commandBuilder.addArgument(
newBooleanArgument("secureReplication2", INFO_DESCRIPTION_ENABLE_SECURE_REPLICATION2));
}
if (!uData.replicateSchema())
{
commandBuilder.addArgument(new BooleanArgument(
"noschemareplication", null, "noSchemaReplication",
INFO_DESCRIPTION_ENABLE_REPLICATION_NO_SCHEMA_REPLICATION.get()));
}
if (argParser.skipReplicationPortCheck())
{
commandBuilder.addArgument(new BooleanArgument(
"skipportcheck", 'S', "skipPortCheck",
INFO_DESCRIPTION_ENABLE_REPLICATION_SKIPPORT.get()));
}
if (argParser.useSecondServerAsSchemaSource())
{
commandBuilder.addArgument(new BooleanArgument(
"usesecondserverasschemasource", null,
"useSecondServerAsSchemaSource",
INFO_DESCRIPTION_ENABLE_REPLICATION_USE_SECOND_AS_SCHEMA_SOURCE.get(
"--" + argParser.noSchemaReplicationArg.getLongIdentifier())));
}
}
private IntegerArgument getReplicationPortArg(
String name, EnableReplicationServerData server, int defaultValue, Arg0 description) throws ArgumentException
{
IntegerArgument replicationPort = new IntegerArgument(
name, 'r', name, false, false, true,
INFO_PORT_PLACEHOLDER.get(), defaultValue, null, description.get());
int value = server.getReplicationPort();
replicationPort.addValue(String.valueOf(value));
return replicationPort;
}
private BooleanArgument newBooleanArgument(String name, Arg0 msg) throws ArgumentException
{
return new BooleanArgument(name, null, name, msg.get());
}
private BooleanArgument newBooleanArgument(BooleanArgument arg, Arg0 msg) throws ArgumentException
{
return new BooleanArgument(arg.getName(), arg.getShortIdentifier(), arg.getLongIdentifier(), msg.get());
}
private StringArgument getBindPassword1Arg(Argument arg) throws ArgumentException
{
return getBindPasswordArg("bindPassword1", arg, INFO_DESCRIPTION_ENABLE_REPLICATION_BINDPASSWORD1);
}
private StringArgument getBindPassword2Arg(Argument arg) throws ArgumentException
{
return getBindPasswordArg("bindPassword2", arg, INFO_DESCRIPTION_ENABLE_REPLICATION_BINDPASSWORD2);
}
private StringArgument getBindPasswordArg(String name, Argument arg, Arg0 bindPwdMsg) throws ArgumentException
{
StringArgument bindPasswordArg = new StringArgument(
name, null, name, false, false, true,
INFO_BINDPWD_PLACEHOLDER.get(), null, null, bindPwdMsg.get());
bindPasswordArg.addValue(arg.getValue());
return bindPasswordArg;
}
private FileBasedArgument getBindPasswordFile1Arg() throws ArgumentException
{
return new FileBasedArgument(
"bindPasswordFile1", null, "bindPasswordFile1", false, false,
INFO_BINDPWD_FILE_PLACEHOLDER.get(), null, null,
INFO_DESCRIPTION_ENABLE_REPLICATION_BINDPASSWORDFILE1.get());
}
private FileBasedArgument getBindPasswordFile2Arg() throws ArgumentException
{
return new FileBasedArgument(
"bindPasswordFile2", null, "bindPasswordFile2", false, false,
INFO_BINDPWD_FILE_PLACEHOLDER.get(), null, null,
INFO_DESCRIPTION_ENABLE_REPLICATION_BINDPASSWORDFILE2.get());
}
private StringArgument getBindDN1Arg(EnableReplicationUserData uData) throws ArgumentException
{
StringArgument bindDN = new StringArgument("bindDN1", OPTION_SHORT_BINDDN, "bindDN1", false, false, true,
INFO_BINDDN_PLACEHOLDER.get(), "cn=Directory Manager", null,
INFO_DESCRIPTION_ENABLE_REPLICATION_BINDDN1.get());
bindDN.addValue(uData.getServer1().getBindDn());
return bindDN;
}
private StringArgument getBindDN2Arg(EnableReplicationUserData uData, Character shortIdentifier)
throws ArgumentException
{
StringArgument bindDN = new StringArgument("bindDN2", shortIdentifier, "bindDN2", false, false, true,
INFO_BINDDN_PLACEHOLDER.get(), "cn=Directory Manager", null,
INFO_DESCRIPTION_ENABLE_REPLICATION_BINDDN2.get());
bindDN.addValue(uData.getServer2().getBindDn());
return bindDN;
}
private void updateCommandBuilder(CommandBuilder commandBuilder,
SourceDestinationServerUserData uData)
throws ArgumentException
{
// Update the arguments used in the console interaction with the
// actual arguments of dsreplication.
if (firstServerCommandBuilder != null)
{
for (Argument arg : firstServerCommandBuilder.getArguments())
{
if (OPTION_LONG_HOST.equals(arg.getLongIdentifier()))
{
commandBuilder.addArgument(getHostArg("hostSource", 'O', uData.getHostNameSource(),
INFO_DESCRIPTION_INITIALIZE_REPLICATION_HOST_SOURCE));
}
else if (OPTION_LONG_PORT.equals(arg.getLongIdentifier()))
{
commandBuilder.addArgument(getPortArg("portSource", null, uData.getPortSource(),
INFO_DESCRIPTION_INITIALIZE_REPLICATION_SERVER_PORT_SOURCE));
}
else if (OPTION_LONG_BINDPWD.equals(arg.getLongIdentifier()))
{
commandBuilder.addObfuscatedArgument(getAdminPasswordArg(arg));
}
else if (OPTION_LONG_BINDPWD_FILE.equals(arg.getLongIdentifier()))
{
commandBuilder.addArgument(getAdminPasswordFileArg(arg));
}
else
{
addArgument(commandBuilder, arg, firstServerCommandBuilder.isObfuscated(arg));
}
}
}
if (sourceServerCI != null && sourceServerCI.getCommandBuilder() != null)
{
CommandBuilder interactionBuilder = sourceServerCI.getCommandBuilder();
for (Argument arg : interactionBuilder.getArguments())
{
if (OPTION_LONG_HOST.equals(arg.getLongIdentifier()))
{
commandBuilder.addArgument(getHostArg("hostDestination", 'O', uData.getHostNameDestination(),
INFO_DESCRIPTION_INITIALIZE_REPLICATION_HOST_DESTINATION));
}
else if (OPTION_LONG_PORT.equals(arg.getLongIdentifier()))
{
commandBuilder.addArgument(getPortArg("portDestination", null, uData.getPortDestination(),
INFO_DESCRIPTION_INITIALIZE_REPLICATION_SERVER_PORT_DESTINATION));
}
}
}
}
private StringArgument getAdminPasswordArg(Argument arg) throws ArgumentException
{
StringArgument sArg = getAdminPasswordArg();
sArg.addValue(arg.getValue());
return sArg;
}
private StringArgument getAdminPasswordArg() throws ArgumentException
{
return new StringArgument("adminPassword",
OPTION_SHORT_BINDPWD, "adminPassword", false, false, true,
INFO_BINDPWD_PLACEHOLDER.get(), null, null,
INFO_DESCRIPTION_REPLICATION_ADMIN_BINDPASSWORD.get());
}
private FileBasedArgument getAdminPasswordFileArg(Argument arg) throws ArgumentException
{
FileBasedArgument fbArg = new FileBasedArgument(
"adminPasswordFile", OPTION_SHORT_BINDPWD_FILE, "adminPasswordFile", false, false,
INFO_BINDPWD_FILE_PLACEHOLDER.get(), null, null,
INFO_DESCRIPTION_REPLICATION_ADMIN_BINDPASSWORDFILE.get());
fbArg.getNameToValueMap().putAll(((FileBasedArgument) arg).getNameToValueMap());
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,
INFO_PORT_PLACEHOLDER.get(), 4444, null, arg.get());
iArg.addValue(String.valueOf(value));
return iArg;
}
private StringArgument getHostArg(String longIdentifier, char shortIdentifier, String value,
Arg0 description) throws ArgumentException
{
StringArgument sArg = new StringArgument(longIdentifier, shortIdentifier, longIdentifier, false, false, true,
INFO_HOST_PLACEHOLDER.get(), null, null, description.get());
sArg.addValue(value);
return sArg;
}
private void updateAvailableAndReplicatedSuffixesForOneDomain(
InitialLdapContext ctxDomain, InitialLdapContext ctxOther,
Set<String> availableSuffixes, Set<String> alreadyReplicatedSuffixes)
{
Collection<ReplicaDescriptor> replicas = getReplicas(ctxDomain);
int replicationPort = getReplicationPort(ctxOther);
boolean isReplicationServerConfigured = replicationPort != -1;
String replicationServer = getReplicationServer(getHostName(ctxOther), replicationPort);
for (ReplicaDescriptor replica : replicas)
{
if (!isReplicationServerConfigured)
{
if (replica.isReplicated())
{
alreadyReplicatedSuffixes.add(replica.getSuffix().getDN());
}
availableSuffixes.add(replica.getSuffix().getDN());
}
if (!isReplicationServerConfigured)
{
availableSuffixes.add(replica.getSuffix().getDN());
}
else if (!replica.isReplicated())
{
availableSuffixes.add(replica.getSuffix().getDN());
}
else if (containsIgnoreCase(replica.getReplicationServers(), replicationServer))
{
alreadyReplicatedSuffixes.add(replica.getSuffix().getDN());
}
else
{
availableSuffixes.add(replica.getSuffix().getDN());
}
}
}
private void updateAvailableAndReplicatedSuffixesForNoDomain(
InitialLdapContext ctx1, InitialLdapContext ctx2,
Set<String> availableSuffixes, Set<String> alreadyReplicatedSuffixes)
{
int replicationPort1 = getReplicationPort(ctx1);
boolean isReplicationServer1Configured = replicationPort1 != -1;
String replicationServer1 = getReplicationServer(getHostName(ctx1), replicationPort1);
int replicationPort2 = getReplicationPort(ctx2);
boolean isReplicationServer2Configured = replicationPort2 != -1;
String replicationServer2 = getReplicationServer(getHostName(ctx2), replicationPort2);
TopologyCache cache1 = isReplicationServer1Configured ? createTopologyCache(ctx1) : null;
TopologyCache cache2 = isReplicationServer2Configured ? createTopologyCache(ctx2) : null;
if (cache1 != null && cache2 != null)
{
updateAvailableAndReplicatedSuffixesForNoDomainOneSense(cache1, cache2,
replicationServer1, replicationServer2, availableSuffixes,
alreadyReplicatedSuffixes);
updateAvailableAndReplicatedSuffixesForNoDomainOneSense(cache2, cache1,
replicationServer2, replicationServer1, availableSuffixes,
alreadyReplicatedSuffixes);
}
else if (cache1 != null)
{
addAllAvailableSuffixes(availableSuffixes, cache1.getSuffixes(), replicationServer1);
}
else if (cache2 != null)
{
addAllAvailableSuffixes(availableSuffixes, cache2.getSuffixes(), replicationServer2);
}
}
private TopologyCache createTopologyCache(InitialLdapContext ctx)
{
try
{
ADSContext adsContext = new ADSContext(ctx);
if (adsContext.hasAdminData())
{
TopologyCache cache = new TopologyCache(adsContext, getTrustManager(sourceServerCI), getConnectTimeout());
cache.getFilter().setSearchMonitoringInformation(false);
cache.setPreferredConnections(getPreferredConnections(ctx));
cache.reloadTopology();
return cache;
}
}
catch (Throwable t)
{
logger.warn(LocalizableMessage.raw("Error loading topology cache in " + getLdapUrl(ctx) + ": " + t, t));
}
return null;
}
private void addAllAvailableSuffixes(Collection<String> availableSuffixes,
Set<SuffixDescriptor> suffixes, String rsToFind)
{
for (SuffixDescriptor suffix : suffixes)
{
for (String rs : suffix.getReplicationServers())
{
if (rs.equalsIgnoreCase(rsToFind))
{
availableSuffixes.add(suffix.getDN());
}
}
}
}
private void updateAvailableAndReplicatedSuffixesForNoDomainOneSense(
TopologyCache cache1, TopologyCache cache2, String replicationServer1,
String replicationServer2,
Set<String> availableSuffixes, Set<String> alreadyReplicatedSuffixes)
{
for (SuffixDescriptor suffix : cache1.getSuffixes())
{
for (String rServer : suffix.getReplicationServers())
{
if (rServer.equalsIgnoreCase(replicationServer1))
{
boolean isSecondReplicatedInSameTopology = false;
boolean isSecondReplicated = false;
boolean isFirstReplicated = false;
for (SuffixDescriptor suffix2 : cache2.getSuffixes())
{
if (areDnsEqual(suffix.getDN(), suffix2.getDN()))
{
for (String rServer2 : suffix2.getReplicationServers())
{
if (rServer2.equalsIgnoreCase(replicationServer2))
{
isSecondReplicated = true;
}
if (rServer.equalsIgnoreCase(replicationServer2))
{
isFirstReplicated = true;
}
if (isFirstReplicated && isSecondReplicated)
{
isSecondReplicatedInSameTopology = true;
break;
}
}
break;
}
}
if (!isSecondReplicatedInSameTopology)
{
availableSuffixes.add(suffix.getDN());
}
else
{
alreadyReplicatedSuffixes.add(suffix.getDN());
}
break;
}
}
}
}
private void updateBaseDnsWithNotEnoughReplicationServer(ADSContext adsCtx1,
ADSContext adsCtx2, EnableReplicationUserData uData,
Set<String> baseDNsWithNoReplicationServer,
Set<String> baseDNsWithOneReplicationServer)
{
EnableReplicationServerData server1 = uData.getServer1();
EnableReplicationServerData server2 = uData.getServer2();
if (server1.configureReplicationServer() &&
server2.configureReplicationServer())
{
return;
}
Set<SuffixDescriptor> suffixes = new HashSet<>();
createTopologyCache(adsCtx1, uData, suffixes);
createTopologyCache(adsCtx2, uData, suffixes);
int repPort1 = getReplicationPort(adsCtx1.getDirContext());
String repServer1 = getReplicationServer(server1.getHostName(), repPort1);
int repPort2 = getReplicationPort(adsCtx2.getDirContext());
String repServer2 = getReplicationServer(server2.getHostName(), repPort2);
for (String baseDN : uData.getBaseDNs())
{
int nReplicationServers = 0;
for (SuffixDescriptor suffix : suffixes)
{
if (areDnsEqual(suffix.getDN(), baseDN))
{
Set<String> replicationServers = suffix.getReplicationServers();
nReplicationServers += replicationServers.size();
for (String repServer : replicationServers)
{
if (server1.configureReplicationServer() &&
repServer.equalsIgnoreCase(repServer1))
{
nReplicationServers --;
}
if (server2.configureReplicationServer() &&
repServer.equalsIgnoreCase(repServer2))
{
nReplicationServers --;
}
}
}
}
if (server1.configureReplicationServer())
{
nReplicationServers ++;
}
if (server2.configureReplicationServer())
{
nReplicationServers ++;
}
if (nReplicationServers == 1)
{
baseDNsWithOneReplicationServer.add(baseDN);
}
else if (nReplicationServers == 0)
{
baseDNsWithNoReplicationServer.add(baseDN);
}
}
}
private void createTopologyCache(ADSContext adsCtx, ReplicationUserData uData, Set<SuffixDescriptor> suffixes)
{
try
{
if (adsCtx.hasAdminData())
{
TopologyCache cache = new TopologyCache(adsCtx, getTrustManager(sourceServerCI), getConnectTimeout());
cache.getFilter().setSearchMonitoringInformation(false);
addBaseDNs(cache.getFilter(), uData.getBaseDNs());
cache.reloadTopology();
suffixes.addAll(cache.getSuffixes());
}
}
catch (Throwable t)
{
String msg = "Error loading topology cache from " + getHostPort(adsCtx.getDirContext()) + ": " + t;
logger.warn(LocalizableMessage.raw(msg, 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.
*/
private boolean mergeRegistries(ADSContext adsCtx1, ADSContext adsCtx2)
throws ReplicationCliException
{
PointAdder pointAdder = new PointAdder(this);
try
{
Set<PreferredConnection> cnx = new LinkedHashSet<>(getPreferredConnections(adsCtx1.getDirContext()));
cnx.addAll(getPreferredConnections(adsCtx2.getDirContext()));
TopologyCache cache1 = createTopologyCache(adsCtx1, cnx);
TopologyCache cache2 = createTopologyCache(adsCtx2, cnx);
// Look for the cache with biggest number of replication servers:
// that one is going to be source.
int nRepServers1 = countReplicationServers(cache1);
int nRepServers2 = countReplicationServers(cache2);
InitialLdapContext ctxSource;
InitialLdapContext ctxDestination;
if (nRepServers1 >= nRepServers2)
{
ctxSource = adsCtx1.getDirContext();
ctxDestination = adsCtx2.getDirContext();
}
else
{
ctxSource = adsCtx2.getDirContext();
ctxDestination = adsCtx1.getDirContext();
}
String hostPortSource = getHostPort(ctxSource);
String hostPortDestination = getHostPort(ctxDestination);
if (isInteractive())
{
LocalizableMessage msg = INFO_REPLICATION_MERGING_REGISTRIES_CONFIRMATION.get(hostPortSource,
hostPortDestination, hostPortSource, hostPortDestination);
if (!askConfirmation(msg, true))
{
throw new ReplicationCliException(ERR_REPLICATION_USER_CANCELLED.get(), USER_CANCELLED, null);
}
}
else
{
LocalizableMessage msg = INFO_REPLICATION_MERGING_REGISTRIES_DESCRIPTION.get(hostPortSource,
hostPortDestination, hostPortSource, hostPortDestination);
println(msg);
println();
}
print(INFO_REPLICATION_MERGING_REGISTRIES_PROGRESS.get());
pointAdder.start();
checkCanMergeReplicationTopologies(adsCtx1, cache1);
checkCanMergeReplicationTopologies(adsCtx2, cache2);
Set<LocalizableMessage> commonRepServerIDErrors = new HashSet<>();
for (ServerDescriptor server1 : cache1.getServers())
{
if (findSameReplicationServer(server1, cache2.getServers(), commonRepServerIDErrors))
{
break;
}
}
Set<LocalizableMessage> commonDomainIDErrors = new HashSet<>();
for (SuffixDescriptor suffix1 : cache1.getSuffixes())
{
for (ReplicaDescriptor replica1 : suffix1.getReplicas())
{
if (replica1.isReplicated())
{
for (SuffixDescriptor suffix2 : cache2.getSuffixes())
{
if (findReplicaInSuffix2(replica1, suffix2, suffix1.getDN(), commonDomainIDErrors))
{
break;
}
}
}
}
}
if (!commonRepServerIDErrors.isEmpty() || !commonDomainIDErrors.isEmpty())
{
LocalizableMessageBuilder mb = new LocalizableMessageBuilder();
if (!commonRepServerIDErrors.isEmpty())
{
mb.append(ERR_REPLICATION_ENABLE_COMMON_REPLICATION_SERVER_ID.get(
getMessageFromCollection(commonRepServerIDErrors, Constants.LINE_SEPARATOR)));
}
if (!commonDomainIDErrors.isEmpty())
{
if (mb.length() > 0)
{
mb.append(Constants.LINE_SEPARATOR);
}
mb.append(ERR_REPLICATION_ENABLE_COMMON_DOMAIN_ID.get(
getMessageFromCollection(commonDomainIDErrors, Constants.LINE_SEPARATOR)));
}
throw new ReplicationCliException(mb.toMessage(),
REPLICATION_ADS_MERGE_NOT_SUPPORTED, null);
}
ADSContext adsCtxSource;
ADSContext adsCtxDestination;
TopologyCache cacheDestination;
if (nRepServers1 >= nRepServers2)
{
adsCtxSource = adsCtx1;
adsCtxDestination = adsCtx2;
cacheDestination = cache2;
}
else
{
adsCtxSource = adsCtx2;
adsCtxDestination = adsCtx1;
cacheDestination = cache1;
}
try
{
adsCtxSource.mergeWithRegistry(adsCtxDestination);
}
catch (ADSContextException adce)
{
logger.error(LocalizableMessage.raw("Error merging registry of "+
getHostPort(adsCtxSource.getDirContext())+
" with registry of "+
getHostPort(adsCtxDestination.getDirContext())+" "+
adce, adce));
if (adce.getError() == ADSContextException.ErrorType.ERROR_MERGING)
{
throw new ReplicationCliException(adce.getMessageObject(),
REPLICATION_ADS_MERGE_NOT_SUPPORTED, adce);
}
else
{
throw new ReplicationCliException(
ERR_REPLICATION_UPDATING_ADS.get(adce.getMessageObject()),
ERROR_UPDATING_ADS, adce);
}
}
try
{
for (ServerDescriptor server : cacheDestination.getServers())
{
if (server.isReplicationServer())
{
logger.info(LocalizableMessage.raw("Seeding to replication server on "+
server.getHostPort(true)+" with certificates of "+
getHostPort(adsCtxSource.getDirContext())));
InitialLdapContext ctx = null;
try
{
ctx = getDirContextForServer(cacheDestination, server);
ServerDescriptor.seedAdsTrustStore(ctx,
adsCtxSource.getTrustedCertificates());
}
finally
{
close(ctx);
}
}
}
}
catch (Throwable t)
{
logger.error(LocalizableMessage.raw("Error seeding truststore: "+t, t));
LocalizableMessage msg = ERR_REPLICATION_ENABLE_SEEDING_TRUSTSTORE.get(getHostPort(adsCtx2.getDirContext()),
getHostPort(adsCtx1.getDirContext()), toString(t));
throw new ReplicationCliException(msg, ERROR_SEEDING_TRUSTORE, t);
}
pointAdder.stop();
print(formatter.getSpace());
print(formatter.getFormattedDone());
println();
return adsCtxSource == adsCtx1;
}
finally
{
pointAdder.stop();
}
}
private int countReplicationServers(TopologyCache cache)
{
int nbRepServers = 0;
for (ServerDescriptor server : cache.getServers())
{
if (server.isReplicationServer())
{
nbRepServers++;
}
}
return nbRepServers;
}
private void checkCanMergeReplicationTopologies(ADSContext adsCtx, TopologyCache cache)
throws ReplicationCliException
{
Set<LocalizableMessage> cacheErrors = cache.getErrorMessages();
if (!cacheErrors.isEmpty())
{
LocalizableMessage msg = getMessageFromCollection(cacheErrors, Constants.LINE_SEPARATOR);
throw new ReplicationCliException(
ERR_REPLICATION_CANNOT_MERGE_WITH_ERRORS.get(getHostPort(adsCtx.getDirContext()), msg),
ERROR_READING_ADS, null);
}
}
private boolean findSameReplicationServer(ServerDescriptor serverToFind, Set<ServerDescriptor> servers,
Set<LocalizableMessage> commonRepServerIDErrors)
{
if (!serverToFind.isReplicationServer())
{
return false;
}
int replicationID1 = serverToFind.getReplicationServerId();
String replServerHostPort1 = serverToFind.getReplicationServerHostPort();
for (ServerDescriptor server2 : servers)
{
if (server2.isReplicationServer() && server2.getReplicationServerId() == replicationID1
&& !server2.getReplicationServerHostPort().equalsIgnoreCase(replServerHostPort1))
{
commonRepServerIDErrors.add(ERR_REPLICATION_ENABLE_COMMON_REPLICATION_SERVER_ID_ARG.get(
serverToFind.getHostPort(true), server2.getHostPort(true), replicationID1));
return true;
}
}
return false;
}
private boolean findReplicaInSuffix2(ReplicaDescriptor replica1, SuffixDescriptor suffix2, String suffix1DN,
Set<LocalizableMessage> commonDomainIDErrors)
{
if (!areDnsEqual(suffix2.getDN(), replica1.getSuffix().getDN()))
{
// Conflicting domain names must apply to same suffix.
return false;
}
int domain1Id = replica1.getReplicationId();
for (ReplicaDescriptor replica2 : suffix2.getReplicas())
{
if (replica2.isReplicated()
&& domain1Id == replica2.getReplicationId())
{
commonDomainIDErrors.add(
ERR_REPLICATION_ENABLE_COMMON_DOMAIN_ID_ARG.get(replica1.getServer().getHostPort(true), suffix1DN,
replica2.getServer().getHostPort(true), suffix2.getDN(), domain1Id));
return true;
}
}
return false;
}
private String toString(Throwable t)
{
return (t instanceof OpenDsException) ?
((OpenDsException) t).getMessageObject().toString() : t.toString();
}
private TopologyCache createTopologyCache(ADSContext adsCtx, Set<PreferredConnection> cnx)
throws ReplicationCliException
{
TopologyCache cache = new TopologyCache(adsCtx, getTrustManager(sourceServerCI), getConnectTimeout());
cache.setPreferredConnections(cnx);
cache.getFilter().setSearchBaseDNInformation(false);
try
{
cache.reloadTopology();
return cache;
}
catch (TopologyCacheException te)
{
logger.error(LocalizableMessage.raw(
"Error reading topology cache of " + getHostPort(adsCtx.getDirContext()) + " " + te, te));
throw new ReplicationCliException(ERR_REPLICATION_READING_ADS.get(te.getMessageObject()), ERROR_UPDATING_ADS, te);
}
}
private InitialLdapContext getDirContextForServer(TopologyCache cache, ServerDescriptor server)
throws NamingException
{
String dn = getBindDN(cache.getAdsContext().getDirContext());
String pwd = getBindPassword(cache.getAdsContext().getDirContext());
TopologyCacheFilter filter = new TopologyCacheFilter();
filter.setSearchMonitoringInformation(false);
filter.setSearchBaseDNInformation(false);
ServerLoader loader = new ServerLoader(server.getAdsProperties(),
dn, pwd, getTrustManager(sourceServerCI), getConnectTimeout(),
cache.getPreferredConnections(), filter);
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.
*/
private boolean isBaseDNReplicated(ServerDescriptor server, String baseDN)
{
return findReplicated(server.getReplicas(), baseDN) != null;
}
/**
* 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.
*/
private boolean isBaseDNReplicated(ServerDescriptor server1,
ServerDescriptor server2, String baseDN)
{
final ReplicaDescriptor replica1 = findReplicated(server1.getReplicas(), baseDN);
final ReplicaDescriptor replica2 = findReplicated(server2.getReplicas(), baseDN);
if (replica1 != null && replica2 != null)
{
Set<String> replServers1 = replica1.getSuffix().getReplicationServers();
Set<String> replServers2 = replica1.getSuffix().getReplicationServers();
for (String replServer1 : replServers1)
{
if (containsIgnoreCase(replServers2, replServer1))
{
// it is replicated in both
return true;
}
}
}
return false;
}
private ReplicaDescriptor findReplicated(Set<ReplicaDescriptor> replicas, String baseDN)
{
for (ReplicaDescriptor replica : replicas)
{
if (areDnsEqual(replica.getSuffix().getDN(), baseDN))
{
return replica;
}
}
return null;
}
private boolean displayLogFileAtEnd(String subCommand)
{
final List<String> subCommands = Arrays.asList(ENABLE_REPLICATION_SUBCMD_NAME, DISABLE_REPLICATION_SUBCMD_NAME,
INITIALIZE_ALL_REPLICATION_SUBCMD_NAME, INITIALIZE_REPLICATION_SUBCMD_NAME, RESET_CHANGE_NUMBER_SUBCMD_NAME);
return subCommands.contains(subCommand);
}
/**
* 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 binDir;
/**
* Returns the binary/script directory.
* @return the binary/script directory.
*/
private String getBinaryDir()
{
if (binDir == null)
{
File f = Installation.getLocal().getBinariesDirectory();
try
{
binDir = f.getCanonicalPath();
}
catch (Throwable t)
{
binDir = f.getAbsolutePath();
}
if (binDir.lastIndexOf(File.separatorChar) != binDir.length() - 1)
{
binDir += File.separatorChar;
}
}
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.
*/
private String getCommandLinePath(String scriptBasicName)
{
if (isWindows())
{
return getBinaryDir() + scriptBasicName + ".bat";
}
return getBinaryDir() + scriptBasicName;
}
}
/** Class used to compare replication servers. */
class ReplicationServerComparator implements Comparator<ServerDescriptor>
{
/** {@inheritDoc} */
@Override
public int compare(ServerDescriptor s1, ServerDescriptor s2)
{
int compare = s1.getHostName().compareTo(s2.getHostName());
if (compare == 0)
{
if (s1.getReplicationServerPort() > s2.getReplicationServerPort())
{
return 1;
}
else if (s1.getReplicationServerPort() < s2.getReplicationServerPort())
{
return -1;
}
}
return compare;
}
}
/** Class used to compare suffixes. */
class SuffixComparator implements Comparator<SuffixDescriptor>
{
@Override
public int compare(SuffixDescriptor s1, SuffixDescriptor s2)
{
return s1.getId().compareTo(s2.getId());
}
}
/** Class used to compare servers. */
class ServerComparator implements Comparator<ServerDescriptor>
{
@Override
public int compare(ServerDescriptor s1, ServerDescriptor s2)
{
return s1.getId().compareTo(s2.getId());
}
}