001/*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License").  You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2007-2010 Sun Microsystems, Inc.
025 *      Portions Copyright 2011-2015 ForgeRock AS
026 *      Portions Copyright 2012 profiq s.r.o.
027 */
028package org.opends.server.tools.dsreplication;
029
030import java.io.BufferedWriter;
031import java.io.File;
032import java.io.FileWriter;
033import java.io.IOException;
034import java.io.OutputStream;
035import java.io.PrintStream;
036import java.util.ArrayList;
037import java.util.Arrays;
038import java.util.Collection;
039import java.util.Comparator;
040import java.util.Date;
041import java.util.HashMap;
042import java.util.HashSet;
043import java.util.LinkedHashMap;
044import java.util.LinkedHashSet;
045import java.util.LinkedList;
046import java.util.List;
047import java.util.Map;
048import java.util.Objects;
049import java.util.Set;
050import java.util.SortedSet;
051import java.util.TreeMap;
052import java.util.TreeSet;
053import java.util.concurrent.atomic.AtomicReference;
054
055import javax.naming.NameAlreadyBoundException;
056import javax.naming.NameNotFoundException;
057import javax.naming.NamingEnumeration;
058import javax.naming.NamingException;
059import javax.naming.NoPermissionException;
060import javax.naming.directory.BasicAttributes;
061import javax.naming.directory.DirContext;
062import javax.naming.directory.SearchControls;
063import javax.naming.directory.SearchResult;
064import javax.naming.ldap.InitialLdapContext;
065import javax.naming.ldap.LdapName;
066import javax.net.ssl.KeyManager;
067import javax.net.ssl.SSLException;
068import javax.net.ssl.SSLHandshakeException;
069import javax.net.ssl.TrustManager;
070
071import org.forgerock.i18n.LocalizableMessage;
072import org.forgerock.i18n.LocalizableMessageBuilder;
073import org.forgerock.i18n.LocalizableMessageDescriptor.Arg0;
074import org.forgerock.i18n.LocalizableMessageDescriptor.Arg1;
075import org.forgerock.i18n.LocalizableMessageDescriptor.Arg2;
076import org.forgerock.i18n.slf4j.LocalizedLogger;
077import org.forgerock.opendj.config.server.ConfigException;
078import org.opends.admin.ads.*;
079import org.opends.admin.ads.ADSContext.ADSPropertySyntax;
080import org.opends.admin.ads.ADSContext.AdministratorProperty;
081import org.opends.admin.ads.ADSContext.ServerProperty;
082import org.opends.admin.ads.util.ApplicationTrustManager;
083import org.opends.admin.ads.util.OpendsCertificateException;
084import org.opends.admin.ads.util.PreferredConnection;
085import org.opends.admin.ads.util.ServerLoader;
086import org.opends.guitools.controlpanel.datamodel.BackendDescriptor;
087import org.opends.guitools.controlpanel.datamodel.BaseDNDescriptor;
088import org.opends.guitools.controlpanel.util.*;
089import org.opends.quicksetup.ApplicationException;
090import org.opends.quicksetup.Constants;
091import org.opends.quicksetup.Installation;
092import org.opends.quicksetup.event.ProgressUpdateEvent;
093import org.opends.quicksetup.event.ProgressUpdateListener;
094import org.opends.quicksetup.installer.Installer;
095import org.opends.quicksetup.installer.InstallerHelper;
096import org.opends.quicksetup.installer.PeerNotFoundException;
097import org.opends.quicksetup.installer.offline.OfflineInstaller;
098import org.opends.quicksetup.util.PlainTextProgressMessageFormatter;
099import org.opends.server.admin.*;
100import org.opends.server.admin.client.ManagementContext;
101import org.opends.server.admin.client.ldap.JNDIDirContextAdaptor;
102import org.opends.server.admin.client.ldap.LDAPManagementContext;
103import org.opends.server.admin.std.client.*;
104import org.opends.server.admin.std.meta.ReplicationDomainCfgDefn;
105import org.opends.server.admin.std.meta.ReplicationServerCfgDefn;
106import org.opends.server.admin.std.meta.ReplicationSynchronizationProviderCfgDefn;
107import org.opends.server.core.DirectoryServer;
108import org.opends.server.tasks.PurgeConflictsHistoricalTask;
109import org.opends.server.tools.dsreplication.EnableReplicationUserData.EnableReplicationServerData;
110import org.opends.server.tools.tasks.TaskEntry;
111import org.opends.server.tools.tasks.TaskScheduleInteraction;
112import org.opends.server.tools.tasks.TaskScheduleUserData;
113import org.opends.server.types.DN;
114import org.opends.server.types.DirectoryException;
115import org.opends.server.types.HostPort;
116import org.opends.server.types.InitializationException;
117import org.opends.server.types.NullOutputStream;
118import org.opends.server.types.OpenDsException;
119import org.opends.server.util.BuildVersion;
120import org.opends.server.util.ServerConstants;
121import org.opends.server.util.SetupUtils;
122import org.opends.server.util.StaticUtils;
123import org.opends.server.util.cli.LDAPConnectionConsoleInteraction;
124import org.opends.server.util.cli.PointAdder;
125
126import com.forgerock.opendj.cli.Argument;
127import com.forgerock.opendj.cli.ArgumentException;
128import com.forgerock.opendj.cli.BooleanArgument;
129import com.forgerock.opendj.cli.CliConstants;
130import com.forgerock.opendj.cli.ClientException;
131import com.forgerock.opendj.cli.CommandBuilder;
132import com.forgerock.opendj.cli.ConsoleApplication;
133import com.forgerock.opendj.cli.FileBasedArgument;
134import com.forgerock.opendj.cli.IntegerArgument;
135import com.forgerock.opendj.cli.MenuBuilder;
136import com.forgerock.opendj.cli.MenuResult;
137import com.forgerock.opendj.cli.ReturnCode;
138import com.forgerock.opendj.cli.StringArgument;
139import com.forgerock.opendj.cli.SubCommand;
140import com.forgerock.opendj.cli.TabSeparatedTablePrinter;
141import com.forgerock.opendj.cli.TableBuilder;
142import com.forgerock.opendj.cli.TablePrinter;
143import com.forgerock.opendj.cli.TextTablePrinter;
144import com.forgerock.opendj.cli.ValidationCallback;
145
146import static com.forgerock.opendj.cli.ArgumentConstants.*;
147import static com.forgerock.opendj.cli.CliMessages.*;
148import static com.forgerock.opendj.cli.Utils.*;
149import static com.forgerock.opendj.util.OperatingSystem.*;
150
151import static java.util.Collections.*;
152import static org.forgerock.util.Utils.*;
153import static org.opends.admin.ads.util.ConnectionUtils.*;
154import static org.opends.admin.ads.util.PreferredConnection.*;
155import static org.opends.admin.ads.ServerDescriptor.getReplicationServer;
156import static org.opends.admin.ads.ServerDescriptor.getServerRepresentation;
157import static org.opends.admin.ads.ServerDescriptor.getSuffixDisplay;
158import static org.opends.messages.AdminToolMessages.*;
159import static org.opends.messages.QuickSetupMessages.*;
160import static org.opends.messages.ToolMessages.INFO_TASK_TOOL_TASK_SUCESSFULL;
161import static org.opends.messages.ToolMessages.INFO_TASK_TOOL_TASK_SCHEDULED_FUTURE;
162import static org.opends.messages.ToolMessages.INFO_TASK_TOOL_RECURRING_TASK_SCHEDULED;
163import static org.opends.quicksetup.util.Utils.*;
164import static org.opends.server.tools.dsreplication.ReplicationCliArgumentParser.*;
165import static org.opends.server.tools.dsreplication.ReplicationCliReturnCode.*;
166import static org.opends.server.util.StaticUtils.*;
167
168/**
169 * This class provides a tool that can be used to enable and disable replication
170 * and also to initialize the contents of a replicated suffix with the contents
171 * of another suffix.  It also allows to display the replicated status of the
172 * different base DNs of the servers that are registered in the ADS.
173 */
174public class ReplicationCliMain extends ConsoleApplication
175{
176  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
177  /** The fully-qualified name of this class. */
178  private static final String CLASS_NAME = ReplicationCliMain.class.getName();
179  /** Prefix for log files. */
180  public static final String LOG_FILE_PREFIX = "opendj-replication-";
181  /** Suffix for log files. */
182  public static final String LOG_FILE_SUFFIX = ".log";
183
184  /**
185   * Property used to call the dsreplication script and ReplicationCliMain to
186   * know which are the java properties to be used (those of dsreplication or
187   * those of dsreplication.offline).
188   */
189  private static final String SCRIPT_CALL_STATUS = "org.opends.server.dsreplicationcallstatus";
190
191  /** The value set by the dsreplication script if it is called the first time. */
192  private static final String FIRST_SCRIPT_CALL = "firstcall";
193  private static final LocalizableMessage EMPTY_MSG = LocalizableMessage.raw("");
194
195  private boolean forceNonInteractive;
196
197  /** Always use SSL with the administration connector. */
198  private final boolean useSSL = true;
199  private final boolean useStartTLS = false;
200
201  /**
202   * The enumeration containing the different options we display when we ask
203   * the user to provide the subcommand interactively.
204   */
205  private enum SubcommandChoice
206  {
207    /** Enable replication. */
208    ENABLE(ENABLE_REPLICATION_SUBCMD_NAME, INFO_REPLICATION_ENABLE_MENU_PROMPT.get()),
209    /** Disable replication. */
210    DISABLE(DISABLE_REPLICATION_SUBCMD_NAME, INFO_REPLICATION_DISABLE_MENU_PROMPT.get()),
211    /** Initialize replication. */
212    INITIALIZE(INITIALIZE_REPLICATION_SUBCMD_NAME, INFO_REPLICATION_INITIALIZE_MENU_PROMPT.get()),
213    /** Initialize All. */
214    INITIALIZE_ALL(INITIALIZE_ALL_REPLICATION_SUBCMD_NAME, INFO_REPLICATION_INITIALIZE_ALL_MENU_PROMPT.get()),
215    /** Pre external initialization. */
216    PRE_EXTERNAL_INITIALIZATION(PRE_EXTERNAL_INITIALIZATION_SUBCMD_NAME,
217        INFO_REPLICATION_PRE_EXTERNAL_INITIALIZATION_MENU_PROMPT.get()),
218    /** Post external initialization. */
219    POST_EXTERNAL_INITIALIZATION(POST_EXTERNAL_INITIALIZATION_SUBCMD_NAME,
220        INFO_REPLICATION_POST_EXTERNAL_INITIALIZATION_MENU_PROMPT.get()),
221    /** Replication status. */
222    STATUS(STATUS_REPLICATION_SUBCMD_NAME, INFO_REPLICATION_STATUS_MENU_PROMPT.get()),
223    /** Replication purge historical. */
224    PURGE_HISTORICAL(PURGE_HISTORICAL_SUBCMD_NAME, INFO_REPLICATION_PURGE_HISTORICAL_MENU_PROMPT.get()),
225    /** Set changelog change number from another server. */
226    RESET_CHANGE_NUMBER(ReplicationCliArgumentParser.RESET_CHANGE_NUMBER_SUBCMD_NAME,
227        INFO_DESCRIPTION_RESET_CHANGE_NUMBER.get()),
228    /** Cancel operation. */
229    CANCEL(null, null);
230
231    private final String name;
232    private LocalizableMessage prompt;
233
234    private SubcommandChoice(String name, LocalizableMessage prompt)
235    {
236      this.name = name;
237      this.prompt = prompt;
238    }
239
240    private LocalizableMessage getPrompt()
241    {
242      return prompt;
243    }
244
245    private String getName()
246    {
247      return name;
248    }
249
250    private static SubcommandChoice fromName(String subCommandName)
251    {
252      SubcommandChoice[] f = values();
253      for (SubcommandChoice subCommand : f)
254      {
255        if (subCommand.name.equals(subCommandName))
256        {
257          return subCommand;
258        }
259      }
260      return null;
261    }
262  }
263
264  /** Abstract some of the operations when two servers must be queried for information. */
265  private interface OperationBetweenSourceAndDestinationServers
266  {
267    /**
268     * Returns whether we should stop processing after asking the user for additional information.
269     * Might connect to servers to run configuration checks.
270     * @param baseDNs user specified baseDNs
271     * @param source the source server
272     * @param dest the destination server
273     * @param interactive if user has to input information
274     * @return whether we should stop
275     */
276    boolean continueAfterUserInput(Collection<String> baseDNs, InitialLdapContext source, InitialLdapContext dest,
277        boolean interactive);
278
279    /**
280     * Confirm with the user whether the current task should continue.
281     *
282     * @param uData servers address and authentication parameters
283     * @param ctxSource LDAP Context for the destination server
284     * @param ctxDestination LDAP Context for the source server
285     * @param defaultValue default yes or no
286     * @return whether the current task should be interrupted
287     */
288    boolean confirmOperation(SourceDestinationServerUserData uData, InitialLdapContext ctxSource,
289        InitialLdapContext ctxDestination, final boolean defaultValue);
290  }
291
292  /** The argument parser to be used. */
293  private ReplicationCliArgumentParser argParser;
294  private FileBasedArgument userProvidedAdminPwdFile;
295  private LDAPConnectionConsoleInteraction sourceServerCI;
296  private CommandBuilder firstServerCommandBuilder;
297  /** The message formatter. */
298  private PlainTextProgressMessageFormatter formatter = new PlainTextProgressMessageFormatter();
299
300  /**
301   * Constructor for the ReplicationCliMain object.
302   *
303   * @param out the print stream to use for standard output.
304   * @param err the print stream to use for standard error.
305   */
306  public ReplicationCliMain(PrintStream out, PrintStream err)
307  {
308    super(out, err);
309  }
310
311  /**
312   * The main method for the replication tool.
313   *
314   * @param args the command-line arguments provided to this program.
315   */
316
317  public static void main(String[] args)
318  {
319    int retCode = mainCLI(args, true, System.out, System.err);
320    System.exit(retCode);
321  }
322
323  /**
324   * Parses the provided command-line arguments and uses that information to
325   * run the replication tool.
326   *
327   * @param args the command-line arguments provided to this program.
328   *
329   * @return The error code.
330   */
331
332  public static int mainCLI(String[] args)
333  {
334    return mainCLI(args, true, System.out, System.err);
335  }
336
337  /**
338   * Parses the provided command-line arguments and uses that information to
339   * run the replication tool.
340   *
341   * @param  args              The command-line arguments provided to this
342   *                           program.
343   * @param initializeServer   Indicates whether to initialize the server.
344   * @param  outStream         The output stream to use for standard output, or
345   *                           <CODE>null</CODE> if standard output is not
346   *                           needed.
347   * @param  errStream         The output stream to use for standard error, or
348   *                           <CODE>null</CODE> if standard error is not
349   *                           needed.
350   * @return The error code.
351   */
352  public static int mainCLI(String[] args, boolean initializeServer,
353      OutputStream outStream, OutputStream errStream)
354  {
355    PrintStream out = NullOutputStream.wrapOrNullStream(outStream);
356    PrintStream err = NullOutputStream.wrapOrNullStream(errStream);
357
358    try
359    {
360      ControlPanelLog.initLogFileHandler(
361          File.createTempFile(LOG_FILE_PREFIX, LOG_FILE_SUFFIX));
362    } catch (Throwable t) {
363      System.err.println("Unable to initialize log");
364      t.printStackTrace();
365    }
366    ReplicationCliMain replicationCli = new ReplicationCliMain(out, err);
367    ReplicationCliReturnCode result = replicationCli.execute(args, initializeServer);
368    if (result.getReturnCode() == 0)
369    {
370      // Delete the temp log file, in case of success.
371      ControlPanelLog.closeAndDeleteLogFile();
372    }
373    return result.getReturnCode();
374  }
375
376  /**
377   * Parses the provided command-line arguments and uses that information to
378   * run the replication tool.
379   *
380   * @param args the command-line arguments provided to this program.
381   * @param  initializeServer  Indicates whether to initialize the server.
382   *
383   * @return The error code.
384   */
385  public ReplicationCliReturnCode execute(String[] args, boolean initializeServer)
386  {
387    // Create the command-line argument parser for use with this program.
388    try
389    {
390      createArgumenParser();
391    }
392    catch (ArgumentException ae)
393    {
394      errPrintln(ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage()));
395      logger.error(LocalizableMessage.raw("Complete error stack:"), ae);
396      return CANNOT_INITIALIZE_ARGS;
397    }
398
399    try
400    {
401      argParser.getSecureArgsList().initArgumentsWithConfiguration();
402    }
403    catch (ConfigException ce)
404    {
405      // Ignore.
406    }
407
408    // Parse the command-line arguments provided to this program.
409    try
410    {
411      argParser.parseArguments(args);
412    }
413    catch (ArgumentException ae)
414    {
415      argParser.displayMessageAndUsageReference(getErrStream(), ERR_ERROR_PARSING_ARGS.get(ae.getMessage()));
416      logger.error(LocalizableMessage.raw("Complete error stack:"), ae);
417      return ERROR_USER_DATA;
418    }
419
420    // If we should just display usage or version information, then print it and exit.
421    if (argParser.usageOrVersionDisplayed())
422    {
423      return SUCCESSFUL_NOP;
424    }
425
426    // Checks the version - if upgrade required, the tool is unusable
427    try
428    {
429      BuildVersion.checkVersionMismatch();
430    }
431    catch (InitializationException e)
432    {
433      errPrintln(e.getMessageObject());
434      return CANNOT_INITIALIZE_ARGS;
435    }
436
437    // Check that the provided parameters are compatible.
438    LocalizableMessageBuilder buf = new LocalizableMessageBuilder();
439    argParser.validateOptions(buf);
440    if (buf.length() > 0)
441    {
442      errPrintln(buf.toMessage());
443      errPrintln(LocalizableMessage.raw(argParser.getUsage()));
444      return ERROR_USER_DATA;
445    }
446
447    if (initializeServer)
448    {
449      DirectoryServer.bootstrapClient();
450
451      // Bootstrap definition classes.
452      try
453      {
454        if (!ClassLoaderProvider.getInstance().isEnabled())
455        {
456          ClassLoaderProvider.getInstance().enable();
457        }
458        // Switch off class name validation in client.
459        ClassPropertyDefinition.setAllowClassValidation(false);
460
461        // Switch off attribute type name validation in client.
462        AttributeTypePropertyDefinition.setCheckSchema(false);
463      }
464      catch (InitializationException ie)
465      {
466        errPrintln(ie.getMessageObject());
467        return ERROR_INITIALIZING_ADMINISTRATION_FRAMEWORK;
468      }
469    }
470
471    if (argParser.getSecureArgsList().bindPasswordFileArg.isPresent())
472    {
473      try
474      {
475        userProvidedAdminPwdFile = new FileBasedArgument(
476            "adminPasswordFile", OPTION_SHORT_BINDPWD_FILE, "adminPasswordFile", false, false,
477            INFO_BINDPWD_FILE_PLACEHOLDER.get(), null, null,
478            INFO_DESCRIPTION_REPLICATION_ADMIN_BINDPASSWORDFILE.get());
479        userProvidedAdminPwdFile.getNameToValueMap().putAll(
480            argParser.getSecureArgsList().bindPasswordFileArg.getNameToValueMap());
481      }
482      catch (Throwable t)
483      {
484        throw new IllegalStateException("Unexpected error: " + t, t);
485      }
486    }
487    sourceServerCI = new LDAPConnectionConsoleInteraction(this, argParser.getSecureArgsList());
488    sourceServerCI.setDisplayLdapIfSecureParameters(false);
489
490    ReplicationCliReturnCode returnValue = SUCCESSFUL_NOP;
491    String subCommand = null;
492    final SubcommandChoice subcommandChoice = getSubcommandChoice(argParser.getSubCommand());
493    if (subcommandChoice != null)
494    {
495      subCommand = subcommandChoice.getName();
496      returnValue = execute(subcommandChoice);
497    }
498    else if (argParser.isInteractive())
499    {
500      final SubcommandChoice subCommandChoice = promptForSubcommand();
501      if (subCommandChoice == null || SubcommandChoice.CANCEL.equals(subCommandChoice))
502      {
503        return USER_CANCELLED;
504      }
505
506      subCommand = subCommandChoice.getName();
507      if (subCommand != null)
508      {
509        String[] newArgs = new String[args.length + 1];
510        newArgs[0] = subCommand;
511        System.arraycopy(args, 0, newArgs, 1, args.length);
512        // The server (if requested) has already been initialized.
513        return execute(newArgs, false);
514      }
515    }
516    else
517    {
518      errPrintln(ERR_REPLICATION_VALID_SUBCOMMAND_NOT_FOUND.get("--" + OPTION_LONG_NO_PROMPT));
519      errPrintln(LocalizableMessage.raw(argParser.getUsage()));
520      return ERROR_USER_DATA;
521    }
522
523    // Display the log file only if the operation is successful (when there
524    // is a critical error this is already displayed).
525    if (returnValue == SUCCESSFUL && displayLogFileAtEnd(subCommand))
526    {
527      File logFile = ControlPanelLog.getLogFile();
528      if (logFile != null)
529      {
530        println();
531        println(INFO_GENERAL_SEE_FOR_DETAILS.get(logFile.getPath()));
532        println();
533      }
534    }
535
536    return returnValue;
537  }
538
539  private SubcommandChoice getSubcommandChoice(SubCommand subCommand)
540  {
541    if (subCommand != null)
542    {
543      return SubcommandChoice.fromName(subCommand.getName());
544    }
545    return null;
546  }
547
548  private ReplicationCliReturnCode execute(SubcommandChoice subcommandChoice)
549  {
550    switch (subcommandChoice)
551    {
552    case ENABLE:
553      return enableReplication();
554    case DISABLE:
555      return disableReplication();
556    case INITIALIZE:
557      return initializeReplication();
558    case INITIALIZE_ALL:
559      return initializeAllReplication();
560    case PRE_EXTERNAL_INITIALIZATION:
561      return preExternalInitialization();
562    case POST_EXTERNAL_INITIALIZATION:
563      return postExternalInitialization();
564    case STATUS:
565      return statusReplication();
566    case PURGE_HISTORICAL:
567      return purgeHistorical();
568    case RESET_CHANGE_NUMBER:
569      return resetChangeNumber();
570    default:
571      return SUCCESSFUL_NOP;
572    }
573  }
574
575  /**
576   * Prompts the user to give the Global Administrator UID.
577   *
578   * @param defaultValue
579   *          the default value that will be proposed in the prompt message.
580   * @param logger
581   *          the Logger to be used to log the error message.
582   * @return the Global Administrator UID as provided by the user.
583   */
584  private String askForAdministratorUID(String defaultValue, LocalizedLogger logger)
585  {
586    return ask(logger, INFO_ADMINISTRATOR_UID_PROMPT.get(), defaultValue);
587  }
588
589  /**
590   * Prompts the user to give the Global Administrator password.
591   *
592   * @param logger
593   *          the Logger to be used to log the error message.
594   * @return the Global Administrator password as provided by the user.
595   */
596  private String askForAdministratorPwd(LocalizedLogger logger)
597  {
598    try
599    {
600      return new String(readPassword(INFO_ADMINISTRATOR_PWD_PROMPT.get()));
601    }
602    catch (ClientException ex)
603    {
604      logger.warn(LocalizableMessage.raw("Error reading input: " + ex, ex));
605      return null;
606    }
607  }
608
609  private String ask(LocalizedLogger logger, LocalizableMessage msgPrompt, String defaultValue)
610  {
611    try
612    {
613      return readInput(msgPrompt, defaultValue);
614    }
615    catch (ClientException ce)
616    {
617      logger.warn(LocalizableMessage.raw("Error reading input: " + ce, ce));
618      return defaultValue;
619    }
620  }
621
622  /**
623   * Commodity method used to repeatidly ask the user to provide an integer
624   * value.
625   *
626   * @param prompt
627   *          the prompt message.
628   * @param defaultValue
629   *          the default value to be proposed to the user.
630   * @param logger
631   *          the logger where the errors will be written.
632   * @return the value provided by the user.
633   */
634  private int askInteger(LocalizableMessage prompt, int defaultValue, LocalizedLogger logger)
635  {
636    int newInt = -1;
637    while (newInt == -1)
638    {
639      try
640      {
641        newInt = readInteger(prompt, defaultValue);
642      }
643      catch (ClientException ce)
644      {
645        newInt = -1;
646        logger.warn(LocalizableMessage.raw("Error reading input: " + ce, ce));
647      }
648    }
649    return newInt;
650  }
651
652  /**
653   * Interactively retrieves an integer value from the console.
654   *
655   * @param prompt
656   *          The message prompt.
657   * @param defaultValue
658   *          The default value.
659   * @return Returns the value.
660   * @throws ClientException
661   *           If the value could not be retrieved for some reason.
662   */
663  public final int readInteger(
664      LocalizableMessage prompt, final int defaultValue) throws ClientException
665  {
666    ValidationCallback<Integer> callback = new ValidationCallback<Integer>()
667    {
668      @Override
669      public Integer validate(ConsoleApplication app, String input)
670          throws ClientException
671      {
672        String ninput = input.trim();
673        if (ninput.length() == 0)
674        {
675          return defaultValue;
676        }
677
678        try
679        {
680          int i = Integer.parseInt(ninput);
681          if (i < 1)
682          {
683            throw new NumberFormatException();
684          }
685          return i;
686        }
687        catch (NumberFormatException e)
688        {
689          // Try again...
690          app.errPrintln();
691          app.errPrintln(ERR_BAD_INTEGER.get(ninput));
692          app.errPrintln();
693          return null;
694        }
695      }
696
697    };
698
699    if (defaultValue != -1)
700    {
701      prompt = INFO_PROMPT_SINGLE_DEFAULT.get(prompt, defaultValue);
702    }
703
704    return readValidatedInput(prompt, callback, CONFIRMATION_MAX_TRIES);
705  }
706
707  private boolean isFirstCallFromScript()
708  {
709    return FIRST_SCRIPT_CALL.equals(System.getProperty(SCRIPT_CALL_STATUS));
710  }
711
712  private void createArgumenParser() throws ArgumentException
713  {
714    argParser = new ReplicationCliArgumentParser(CLASS_NAME);
715    argParser.initializeParser(getOutputStream());
716  }
717
718  /**
719   * Based on the data provided in the command-line it enables replication
720   * between two servers.
721   * @return the error code if the operation failed and 0 if it was successful.
722   */
723  private ReplicationCliReturnCode enableReplication()
724  {
725    EnableReplicationUserData uData = new EnableReplicationUserData();
726    if (argParser.isInteractive())
727    {
728      try
729      {
730        if (promptIfRequired(uData))
731        {
732          return enableReplication(uData);
733        }
734        else
735        {
736          return USER_CANCELLED;
737        }
738      }
739      catch (ReplicationCliException rce)
740      {
741        errPrintln();
742        errPrintln(getCriticalExceptionMessage(rce));
743        return rce.getErrorCode();
744      }
745    }
746    else
747    {
748      initializeWithArgParser(uData);
749      return enableReplication(uData);
750    }
751  }
752
753  /**
754   * Based on the data provided in the command-line it disables replication
755   * in the server.
756   * @return the error code if the operation failed and SUCCESSFUL if it was
757   * successful.
758   */
759  private ReplicationCliReturnCode disableReplication()
760  {
761    DisableReplicationUserData uData = new DisableReplicationUserData();
762    if (argParser.isInteractive())
763    {
764      try
765      {
766        if (promptIfRequired(uData))
767        {
768          return disableReplication(uData);
769        }
770        else
771        {
772          return USER_CANCELLED;
773        }
774      }
775      catch (ReplicationCliException rce)
776      {
777        errPrintln();
778        errPrintln(getCriticalExceptionMessage(rce));
779        return rce.getErrorCode();
780      }
781    }
782    else
783    {
784      initializeWithArgParser(uData);
785      return disableReplication(uData);
786    }
787  }
788
789  /**
790   * Based on the data provided in the command-line initialize the contents
791   * of the whole replication topology.
792   * @return the error code if the operation failed and SUCCESSFUL if it was
793   * successful.
794   */
795  private ReplicationCliReturnCode initializeAllReplication()
796  {
797    InitializeAllReplicationUserData uData =
798      new InitializeAllReplicationUserData();
799    if (argParser.isInteractive())
800    {
801      if (promptIfRequired(uData))
802      {
803        return initializeAllReplication(uData);
804      }
805      else
806      {
807        return USER_CANCELLED;
808      }
809    }
810    else
811    {
812      initializeWithArgParser(uData);
813      return initializeAllReplication(uData);
814    }
815  }
816
817  /**
818   * Based on the data provided in the command-line execute the pre external
819   * initialization operation.
820   * @return the error code if the operation failed and SUCCESSFUL if it was
821   * successful.
822   */
823  private ReplicationCliReturnCode preExternalInitialization()
824  {
825    PreExternalInitializationUserData uData =
826      new PreExternalInitializationUserData();
827    if (argParser.isInteractive())
828    {
829      if (promptIfRequiredForPreOrPost(uData))
830      {
831        return preExternalInitialization(uData);
832      }
833      else
834      {
835        return USER_CANCELLED;
836      }
837    }
838    else
839    {
840      initializeWithArgParser(uData);
841      return preExternalInitialization(uData);
842    }
843  }
844
845  /**
846   * Based on the data provided in the command-line execute the post external
847   * initialization operation.
848   * @return the error code if the operation failed and SUCCESSFUL if it was
849   * successful.
850   */
851  private ReplicationCliReturnCode postExternalInitialization()
852  {
853    PostExternalInitializationUserData uData =
854      new PostExternalInitializationUserData();
855    if (argParser.isInteractive())
856    {
857      if (promptIfRequiredForPreOrPost(uData))
858      {
859        return postExternalInitialization(uData);
860      }
861      else
862      {
863        return USER_CANCELLED;
864      }
865    }
866    else
867    {
868      initializeWithArgParser(uData);
869      return postExternalInitialization(uData);
870    }
871  }
872
873  /**
874   * Based on the data provided in the command-line it displays replication
875   * status.
876   * @return the error code if the operation failed and SUCCESSFUL if it was
877   * successful.
878   */
879  private ReplicationCliReturnCode statusReplication()
880  {
881    StatusReplicationUserData uData = new StatusReplicationUserData();
882    if (argParser.isInteractive())
883    {
884      try
885      {
886        if (promptIfRequired(uData))
887        {
888          return statusReplication(uData);
889        }
890        else
891        {
892          return USER_CANCELLED;
893        }
894      }
895      catch (ReplicationCliException rce)
896      {
897        errPrintln();
898        errPrintln(getCriticalExceptionMessage(rce));
899        return rce.getErrorCode();
900      }
901    }
902    else
903    {
904      initializeWithArgParser(uData);
905      return statusReplication(uData);
906    }
907  }
908
909  /**
910   * Based on the data provided in the command-line it displays replication
911   * status.
912   * @return the error code if the operation failed and SUCCESSFUL if it was
913   * successful.
914   */
915  private ReplicationCliReturnCode purgeHistorical()
916  {
917    final PurgeHistoricalUserData uData = new PurgeHistoricalUserData();
918    if (argParser.isInteractive())
919    {
920      if (promptIfRequired(uData))
921      {
922        return purgeHistorical(uData);
923      }
924      else
925      {
926        return USER_CANCELLED;
927      }
928    }
929    else
930    {
931      initializeWithArgParser(uData);
932      return purgeHistorical(uData);
933    }
934  }
935
936  /**
937   * Initializes the contents of the provided purge historical replication user
938   * data object with what was provided in the command-line without prompting to
939   * the user.
940   * @param uData the purge historical replication user data object to be
941   * initialized.
942   */
943  private void initializeWithArgParser(PurgeHistoricalUserData uData)
944  {
945    PurgeHistoricalUserData.initializeWithArgParser(uData, argParser);
946  }
947
948  private ReplicationCliReturnCode purgeHistorical(PurgeHistoricalUserData uData)
949  {
950      return uData.isOnline()
951          ? purgeHistoricalRemotely(uData)
952          : purgeHistoricalLocally(uData);
953  }
954
955  private ReplicationCliReturnCode purgeHistoricalLocally(
956      PurgeHistoricalUserData uData)
957  {
958    List<String> baseDNs = uData.getBaseDNs();
959    checkSuffixesForLocalPurgeHistorical(baseDNs, false);
960    if (!baseDNs.isEmpty())
961    {
962      uData.setBaseDNs(baseDNs);
963      if (mustPrintCommandBuilder())
964      {
965        printNewCommandBuilder(PURGE_HISTORICAL_SUBCMD_NAME, uData);
966      }
967
968      try
969      {
970        return purgeHistoricalLocallyTask(uData);
971      }
972      catch (ReplicationCliException rce)
973      {
974        errPrintln();
975        errPrintln(getCriticalExceptionMessage(rce));
976        logger.error(LocalizableMessage.raw("Complete error stack:"), rce);
977        return rce.getErrorCode();
978      }
979    }
980    else
981    {
982      return HISTORICAL_CANNOT_BE_PURGED_ON_BASEDN;
983    }
984  }
985
986  private void printPurgeProgressMessage(PurgeHistoricalUserData uData)
987  {
988    String separator = formatter.getLineBreak().toString() + formatter.getTab();
989    println();
990    LocalizableMessage msg = formatter.getFormattedProgress(
991        INFO_PROGRESS_PURGE_HISTORICAL.get(separator, joinAsString(separator, uData.getBaseDNs())));
992    print(msg);
993    println();
994  }
995
996  private ReplicationCliReturnCode purgeHistoricalLocallyTask(PurgeHistoricalUserData uData)
997      throws ReplicationCliException
998  {
999    ReplicationCliReturnCode returnCode = SUCCESSFUL;
1000    if (isFirstCallFromScript())
1001    {
1002      // Launch the process: launch dsreplication in non-interactive mode with
1003      // the recursive property set.
1004      ArrayList<String> args = new ArrayList<>();
1005      args.add(getCommandLinePath(getCommandName()));
1006      args.add(PURGE_HISTORICAL_SUBCMD_NAME);
1007      args.add("--"+argParser.noPromptArg.getLongIdentifier());
1008      args.add("--"+argParser.maximumDurationArg.getLongIdentifier());
1009      args.add(String.valueOf(uData.getMaximumDuration()));
1010      for (String baseDN : uData.getBaseDNs())
1011      {
1012        args.add("--"+argParser.baseDNsArg.getLongIdentifier());
1013        args.add(baseDN);
1014      }
1015      ProcessBuilder pb = new ProcessBuilder(args);
1016      // Use the java args in the script.
1017      Map<String, String> env = pb.environment();
1018      env.put("RECURSIVE_LOCAL_CALL", "true");
1019      try
1020      {
1021        Process process = pb.start();
1022        ProcessReader outReader =
1023            new ProcessReader(process, getOutputStream(), false);
1024        ProcessReader errReader =
1025            new ProcessReader(process, getErrorStream(), true);
1026
1027        outReader.startReading();
1028        errReader.startReading();
1029
1030        int code = process.waitFor();
1031        for (ReplicationCliReturnCode c : ReplicationCliReturnCode.values())
1032        {
1033          if (c.getReturnCode() == code)
1034          {
1035            returnCode = c;
1036            break;
1037          }
1038        }
1039      }
1040      catch (Exception e)
1041      {
1042        LocalizableMessage msg = ERR_LAUNCHING_PURGE_HISTORICAL.get();
1043        ReplicationCliReturnCode code = ERROR_LAUNCHING_PURGE_HISTORICAL;
1044        throw new ReplicationCliException(
1045            getThrowableMsg(msg, e), code, e);
1046      }
1047    }
1048    else
1049    {
1050      printPurgeProgressMessage(uData);
1051      LocalPurgeHistorical localPurgeHistorical =
1052        new LocalPurgeHistorical(uData, this, formatter,
1053            argParser.getConfigFile(),
1054            argParser.getConfigClass());
1055      returnCode = localPurgeHistorical.execute();
1056
1057      if (returnCode == SUCCESSFUL)
1058      {
1059        printSuccessMessage(uData, null);
1060      }
1061    }
1062    return returnCode;
1063  }
1064
1065  /**
1066   * Returns an InitialLdapContext using the provided parameters. We try to
1067   * guarantee that the connection is able to read the configuration.
1068   *
1069   * @param host
1070   *          the host name.
1071   * @param port
1072   *          the port to connect.
1073   * @param useSSL
1074   *          whether to use SSL or not.
1075   * @param useStartTLS
1076   *          whether to use StartTLS or not.
1077   * @param bindDn
1078   *          the bind dn to be used.
1079   * @param pwd
1080   *          the password.
1081   * @param connectTimeout
1082   *          the timeout in milliseconds to connect to the server.
1083   * @param trustManager
1084   *          the trust manager.
1085   * @return an InitialLdapContext connected.
1086   * @throws NamingException
1087   *           if there was an error establishing the connection.
1088   */
1089  private InitialLdapContext createAdministrativeContext(String host,
1090      int port, boolean useSSL, boolean useStartTLS, String bindDn, String pwd,
1091      int connectTimeout, ApplicationTrustManager trustManager)
1092      throws NamingException
1093  {
1094    InitialLdapContext ctx;
1095    String ldapUrl = getLDAPUrl(host, port, useSSL);
1096    if (useSSL)
1097    {
1098      ctx = createLdapsContext(ldapUrl, bindDn, pwd, connectTimeout, null, trustManager, null);
1099    }
1100    else if (useStartTLS)
1101    {
1102      ctx = createStartTLSContext(ldapUrl, bindDn, pwd, connectTimeout, null, trustManager, null);
1103    }
1104    else
1105    {
1106      ctx = createLdapContext(ldapUrl, bindDn, pwd, connectTimeout, null);
1107    }
1108    if (!connectedAsAdministrativeUser(ctx))
1109    {
1110      throw new NoPermissionException(ERR_NOT_ADMINISTRATIVE_USER.get().toString());
1111    }
1112    return ctx;
1113  }
1114
1115  /**
1116   * Creates an Initial LDAP Context interacting with the user if the
1117   * application is interactive.
1118   *
1119   * @param ci
1120   *          the LDAPConnectionConsoleInteraction object that is assumed to
1121   *          have been already run.
1122   * @return the initial LDAP context or <CODE>null</CODE> if the user did not
1123   *         accept to trust the certificates.
1124   * @throws ClientException
1125   *           if there was an error establishing the connection.
1126   */
1127  private InitialLdapContext createInitialLdapContextInteracting(LDAPConnectionConsoleInteraction ci)
1128      throws ClientException
1129  {
1130    return createInitialLdapContextInteracting(ci, isInteractive() && ci.isTrustStoreInMemory());
1131  }
1132
1133  private OpendsCertificateException getCertificateRootException(Throwable t)
1134  {
1135    while (t != null)
1136    {
1137      t = t.getCause();
1138      if (t instanceof OpendsCertificateException)
1139      {
1140        return (OpendsCertificateException) t;
1141      }
1142    }
1143    return null;
1144  }
1145
1146  /**
1147   * Creates an Initial LDAP Context interacting with the user if the
1148   * application is interactive.
1149   *
1150   * @param ci
1151   *          the LDAPConnectionConsoleInteraction object that is assumed to
1152   *          have been already run.
1153   * @param promptForCertificate
1154   *          whether we should prompt for the certificate or not.
1155   * @return the initial LDAP context or <CODE>null</CODE> if the user did not
1156   *         accept to trust the certificates.
1157   * @throws ClientException
1158   *           if there was an error establishing the connection.
1159   */
1160  private InitialLdapContext createInitialLdapContextInteracting(LDAPConnectionConsoleInteraction ci,
1161      boolean promptForCertificate) throws ClientException
1162  {
1163    // Interact with the user though the console to get
1164    // LDAP connection information
1165    String hostName = getHostNameForLdapUrl(ci.getHostName());
1166    Integer portNumber = ci.getPortNumber();
1167    String bindDN = ci.getBindDN();
1168    String bindPassword = ci.getBindPassword();
1169    TrustManager trustManager = ci.getTrustManager();
1170    KeyManager keyManager = ci.getKeyManager();
1171
1172    InitialLdapContext ctx;
1173
1174    if (ci.useSSL())
1175    {
1176      String ldapsUrl = "ldaps://" + hostName + ":" + portNumber;
1177      while (true)
1178      {
1179        try
1180        {
1181          ctx = createLdapsContext(ldapsUrl, bindDN, bindPassword, ci.getConnectTimeout(),
1182              null, trustManager, keyManager);
1183          ctx.reconnect(null);
1184          break;
1185        }
1186        catch (NamingException e)
1187        {
1188          if (promptForCertificate)
1189          {
1190            OpendsCertificateException oce = getCertificateRootException(e);
1191            if (oce != null)
1192            {
1193              String authType = null;
1194              if (trustManager instanceof ApplicationTrustManager)
1195              {
1196                ApplicationTrustManager appTrustManager =
1197                    (ApplicationTrustManager) trustManager;
1198                authType = appTrustManager.getLastRefusedAuthType();
1199              }
1200              if (ci.checkServerCertificate(oce.getChain(), authType, hostName))
1201              {
1202                // If the certificate is trusted, update the trust manager.
1203                trustManager = ci.getTrustManager();
1204
1205                // Try to connect again.
1206                continue;
1207              }
1208              else
1209              {
1210                // Assume user canceled.
1211                return null;
1212              }
1213            }
1214          }
1215          if (e.getCause() != null)
1216          {
1217            if (!isInteractive()
1218                && !ci.isTrustAll()
1219                && (getCertificateRootException(e) != null
1220                  || e.getCause() instanceof SSLHandshakeException))
1221            {
1222              LocalizableMessage message =
1223                  ERR_FAILED_TO_CONNECT_NOT_TRUSTED.get(hostName, portNumber);
1224              throw new ClientException(ReturnCode.CLIENT_SIDE_CONNECT_ERROR, message);
1225            }
1226            if (e.getCause() instanceof SSLException)
1227            {
1228              LocalizableMessage message =
1229                  ERR_FAILED_TO_CONNECT_WRONG_PORT.get(hostName, portNumber);
1230              throw new ClientException(
1231                  ReturnCode.CLIENT_SIDE_CONNECT_ERROR, message);
1232            }
1233          }
1234          String hostPort =
1235              ServerDescriptor.getServerRepresentation(hostName, portNumber);
1236          LocalizableMessage message = getMessageForException(e, hostPort);
1237          throw new ClientException(ReturnCode.CLIENT_SIDE_CONNECT_ERROR, message);
1238        }
1239      }
1240    }
1241    else if (ci.useStartTLS())
1242    {
1243      String ldapUrl = "ldap://" + hostName + ":" + portNumber;
1244      while (true)
1245      {
1246        try
1247        {
1248          ctx = createStartTLSContext(ldapUrl, bindDN,
1249                  bindPassword, CliConstants.DEFAULT_LDAP_CONNECT_TIMEOUT, null,
1250                  trustManager, keyManager, null);
1251          ctx.reconnect(null);
1252          break;
1253        }
1254        catch (NamingException e)
1255        {
1256          if (promptForCertificate)
1257          {
1258            OpendsCertificateException oce = getCertificateRootException(e);
1259            if (oce != null)
1260            {
1261              String authType = null;
1262              if (trustManager instanceof ApplicationTrustManager)
1263              {
1264                ApplicationTrustManager appTrustManager =
1265                    (ApplicationTrustManager) trustManager;
1266                authType = appTrustManager.getLastRefusedAuthType();
1267              }
1268
1269              if (ci.checkServerCertificate(oce.getChain(), authType, hostName))
1270              {
1271                // If the certificate is trusted, update the trust manager.
1272                trustManager = ci.getTrustManager();
1273
1274                // Try to connect again.
1275                continue;
1276              }
1277              else
1278              {
1279                // Assume user cancelled.
1280                return null;
1281              }
1282            }
1283            else
1284            {
1285              LocalizableMessage message =
1286                  ERR_FAILED_TO_CONNECT.get(hostName, portNumber);
1287              throw new ClientException(
1288                  ReturnCode.CLIENT_SIDE_CONNECT_ERROR, message);
1289            }
1290          }
1291          LocalizableMessage message =
1292              ERR_FAILED_TO_CONNECT.get(hostName, portNumber);
1293          throw new ClientException(ReturnCode.CLIENT_SIDE_CONNECT_ERROR,
1294              message);
1295        }
1296      }
1297    }
1298    else
1299    {
1300      String ldapUrl = "ldap://" + hostName + ":" + portNumber;
1301      while (true)
1302      {
1303        try
1304        {
1305          ctx = createLdapContext(ldapUrl, bindDN, bindPassword,
1306                  CliConstants.DEFAULT_LDAP_CONNECT_TIMEOUT, null);
1307          ctx.reconnect(null);
1308          break;
1309        }
1310        catch (NamingException e)
1311        {
1312          LocalizableMessage message =
1313              ERR_FAILED_TO_CONNECT.get(hostName, portNumber);
1314          throw new ClientException(ReturnCode.CLIENT_SIDE_CONNECT_ERROR,
1315              message);
1316        }
1317      }
1318    }
1319    return ctx;
1320  }
1321
1322  private ReplicationCliReturnCode purgeHistoricalRemotely(
1323      PurgeHistoricalUserData uData)
1324  {
1325    // Connect to the provided server
1326    InitialLdapContext ctx = createAdministrativeContext(uData);
1327    if (ctx == null)
1328    {
1329      return ERROR_CONNECTING;
1330    }
1331
1332    try
1333    {
1334      List<String> baseDNs = uData.getBaseDNs();
1335      checkSuffixesForPurgeHistorical(baseDNs, ctx, false);
1336      if (baseDNs.isEmpty())
1337      {
1338        return HISTORICAL_CANNOT_BE_PURGED_ON_BASEDN;
1339      }
1340      uData.setBaseDNs(baseDNs);
1341      if (mustPrintCommandBuilder())
1342      {
1343        printNewCommandBuilder(PURGE_HISTORICAL_SUBCMD_NAME, uData);
1344      }
1345
1346      try
1347      {
1348        return purgeHistoricalRemoteTask(ctx, uData);
1349      }
1350      catch (ReplicationCliException rce)
1351      {
1352        errPrintln();
1353        errPrintln(getCriticalExceptionMessage(rce));
1354        logger.error(LocalizableMessage.raw("Complete error stack:"), rce);
1355        return rce.getErrorCode();
1356      }
1357    }
1358    finally
1359    {
1360      close(ctx);
1361    }
1362  }
1363
1364  private ReplicationCliReturnCode resetChangeNumber()
1365  {
1366    final String changeNumber;
1367    final SourceDestinationServerUserData uData = new SourceDestinationServerUserData();
1368
1369    if (!argParser.isInteractive())
1370    {
1371      initializeWithArgParser(uData);
1372      return resetChangeNumber(uData);
1373    }
1374    OperationBetweenSourceAndDestinationServers
1375        resetChangeNumberOperations = new OperationBetweenSourceAndDestinationServers()
1376    {
1377      @Override
1378      public boolean continueAfterUserInput(Collection<String> baseDNs, InitialLdapContext source,
1379          InitialLdapContext dest, boolean interactive)
1380      {
1381        TopologyCacheFilter filter = new TopologyCacheFilter();
1382        filter.setSearchMonitoringInformation(false);
1383
1384        if (!argParser.resetChangeNumber.isPresent())
1385        {
1386          String cn = getNewestChangeNumber(source);
1387          if (cn.isEmpty())
1388          {
1389            return true;
1390          }
1391          argParser.setResetChangeNumber(
1392              ask(logger, INFO_RESET_CHANGE_NUMBER_TO.get(uData.getSource(), uData.getDestination()), cn));
1393        }
1394        return false;
1395      }
1396
1397      @Override
1398      public boolean confirmOperation(SourceDestinationServerUserData uData, InitialLdapContext ctxSource,
1399          InitialLdapContext ctxDestination, boolean defaultValue)
1400      {
1401        return !askConfirmation(INFO_RESET_CHANGE_NUMBER_CONFIRM_RESET.get(uData.getDestinationHostPort()),
1402            defaultValue);
1403      }
1404    };
1405
1406    return promptIfRequired(uData, resetChangeNumberOperations) ? resetChangeNumber(uData) : USER_CANCELLED;
1407  }
1408
1409  private ReplicationCliReturnCode resetChangeNumber(SourceDestinationServerUserData uData)
1410  {
1411
1412    InitialLdapContext ctxSource = createAdministrativeContext(uData, uData.getSource());
1413    InitialLdapContext ctxDest = createAdministrativeContext(uData, uData.getDestination());
1414    if (!getCommonSuffixes(ctxSource, ctxDest, SuffixRelationType.NOT_FULLY_REPLICATED).isEmpty())
1415    {
1416      errPrintln(ERROR_RESET_CHANGE_NUMBER_SERVERS_BASEDNS_DIFFER.get(uData.getSourceHostPort(),
1417          uData.getDestinationHostPort()));
1418      return ERROR_RESET_CHANGE_NUMBER_BASEDNS_SHOULD_EQUAL;
1419    }
1420    if (mustPrintCommandBuilder())
1421    {
1422      printNewCommandBuilder(RESET_CHANGE_NUMBER_SUBCMD_NAME, uData);
1423    }
1424    try
1425    {
1426      String newStartCN;
1427      if (argParser.resetChangeNumber.isPresent())
1428      {
1429        newStartCN = String.valueOf(argParser.getResetChangeNumber());
1430      }
1431      else
1432      {
1433        newStartCN = getNewestChangeNumber(ctxSource);
1434        if (newStartCN.isEmpty())
1435        {
1436          return ERROR_UNKNOWN_CHANGE_NUMBER;
1437        }
1438        argParser.setResetChangeNumber(newStartCN);
1439      }
1440      SearchControls ctls = new SearchControls();
1441      ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
1442      ctls.setReturningAttributes(
1443          new String[] {
1444              "changeNumber",
1445              "replicationCSN",
1446              "targetDN"
1447          });
1448      NamingEnumeration<SearchResult> listeners = ctxSource.search(new LdapName("cn=changelog"),
1449          "(changeNumber=" + newStartCN + ")", ctls);
1450      if (!listeners.hasMore())
1451      {
1452        errPrintln(ERROR_RESET_CHANGE_NUMBER_UNKNOWN_NUMBER.get(newStartCN, uData.getSourceHostPort()));
1453        return ERROR_UNKNOWN_CHANGE_NUMBER;
1454      }
1455      SearchResult sr = listeners.next();
1456      String newStartCSN = getFirstValue(sr, "replicationCSN");
1457      if (newStartCSN == null)
1458      {
1459        errPrintln(ERROR_RESET_CHANGE_NUMBER_NO_CSN_FOUND.get(newStartCN, uData.getSourceHostPort()));
1460        return ERROR_RESET_CHANGE_NUMBER_NO_CSN;
1461      }
1462      String targetDN = getFirstValue(sr, "targetDN");
1463      DN targetBaseDN = DN.rootDN();
1464      try
1465      {
1466        for (String adn : getCommonSuffixes(ctxSource, ctxDest, SuffixRelationType.REPLICATED))
1467        {
1468          DN dn = DN.valueOf(adn);
1469          if (DN.valueOf(targetDN).isDescendantOf(dn) && dn.isDescendantOf(targetBaseDN))
1470          {
1471            targetBaseDN = dn;
1472          }
1473        }
1474      }
1475      catch (DirectoryException de)
1476      {
1477        errPrintln(ERROR_RESET_CHANGE_NUMBER_EXCEPTION.get(de.getLocalizedMessage()));
1478        return ERROR_RESET_CHANGE_NUMBER_PROBLEM;
1479      }
1480      if (targetBaseDN.isRootDN())
1481      {
1482        errPrintln(ERROR_RESET_CHANGE_NUMBER_NO_BASEDN.get(newStartCN, targetDN, newStartCSN));
1483        return ERROR_RESET_CHANGE_NUMBER_UNKNOWN_BASEDN;
1484      }
1485      logger.info(INFO_RESET_CHANGE_NUMBER_INFO.get(uData.getDestinationHostPort(),
1486          newStartCN, newStartCSN, targetBaseDN.toString()));
1487      Map<String, String> taskAttrs = new TreeMap<>();
1488      taskAttrs.put("ds-task-reset-change-number-to", newStartCN);
1489      taskAttrs.put("ds-task-reset-change-number-csn", newStartCSN);
1490      taskAttrs.put("ds-task-reset-change-number-base-dn", targetBaseDN.toString());
1491      String taskDN = createServerTask(ctxDest,
1492          "ds-task-reset-change-number", "org.opends.server.tasks.ResetChangeNumberTask", "dsreplication-reset-cn",
1493          taskAttrs);
1494      waitUntilResetChangeNumberTaskEnds(ctxDest, taskDN);
1495      return SUCCESSFUL;
1496    }
1497    catch (ReplicationCliException | NamingException | NullPointerException e)
1498    {
1499      errPrintln(ERROR_RESET_CHANGE_NUMBER_EXCEPTION.get(e.getLocalizedMessage()));
1500      return ERROR_RESET_CHANGE_NUMBER_PROBLEM;
1501    }
1502  }
1503
1504  private String getNewestChangeNumber(InitialLdapContext source)
1505  {
1506    try
1507    {
1508      SearchControls ctls = new SearchControls();
1509      ctls.setSearchScope(SearchControls.OBJECT_SCOPE);
1510      ctls.setReturningAttributes(new String[] {"lastChangeNumber"});
1511      NamingEnumeration<SearchResult> results = source.search(new LdapName(""), "objectclass=*", ctls);
1512      if (results.hasMore()) {
1513        return getFirstValue(results.next(), "lastChangeNumber");
1514      }
1515    }
1516    catch (NamingException e)
1517    {
1518      errPrintln(ERROR_RESET_CHANGE_NUMBER_EXCEPTION.get(e.getLocalizedMessage()));
1519    }
1520
1521    return "";
1522  }
1523
1524  private void waitUntilResetChangeNumberTaskEnds(InitialLdapContext server, String taskDN)
1525      throws ReplicationCliException
1526  {
1527    String lastLogMsg = null;
1528    while (true)
1529    {
1530      sleepCatchInterrupt(500);
1531      try
1532      {
1533        SearchResult sr = getLastSearchResult(server, taskDN, "ds-task-log-message", "ds-task-state" );
1534        String logMsg = getFirstValue(sr, "ds-task-log-message");
1535        if (logMsg != null && !logMsg.equals(lastLogMsg))
1536        {
1537          logger.info(LocalizableMessage.raw(logMsg));
1538          lastLogMsg = logMsg;
1539        }
1540        InstallerHelper helper = new InstallerHelper();
1541        String state = getFirstValue(sr, "ds-task-state");
1542
1543        if (helper.isDone(state) || helper.isStoppedByError(state))
1544        {
1545          LocalizableMessage errorMsg = ERR_UNEXPECTED_DURING_TASK_WITH_LOG.get(lastLogMsg, state, server);
1546
1547          if (helper.isCompletedWithErrors(state))
1548          {
1549            logger.warn(LocalizableMessage.raw("Completed with error: " + errorMsg));
1550            errPrintln(errorMsg);
1551          }
1552          else if (!helper.isSuccessful(state) || helper.isStoppedByError(state))
1553          {
1554            logger.warn(LocalizableMessage.raw("Error: " + errorMsg));
1555            throw new ReplicationCliException(errorMsg, ERROR_LAUNCHING_RESET_CHANGE_NUMBER, null);
1556          }
1557          else
1558          {
1559            print(INFO_RESET_CHANGE_NUMBER_TASK_FINISHED.get());
1560            println();
1561          }
1562          return;
1563        }
1564      }
1565      catch (NameNotFoundException x)
1566      {
1567        return;
1568      }
1569      catch (NamingException ne)
1570      {
1571        throw new ReplicationCliException(getThrowableMsg(ERR_READING_SERVER_TASK_PROGRESS.get(), ne),
1572            ERROR_CONNECTING, ne);
1573      }
1574    }
1575  }
1576
1577  private InitialLdapContext createAdministrativeContext(MonoServerReplicationUserData uData)
1578  {
1579    final String bindDn = getAdministratorDN(uData.getAdminUid());
1580    return createAdministrativeContext(uData, bindDn);
1581  }
1582
1583  private InitialLdapContext createAdministrativeContext(MonoServerReplicationUserData uData, final String bindDn)
1584  {
1585    try
1586    {
1587      return createAdministrativeContext(uData.getHostName(), uData.getPort(),
1588          useSSL, useStartTLS, bindDn,
1589          uData.getAdminPwd(), getConnectTimeout(), getTrustManager(sourceServerCI));
1590    }
1591    catch (NamingException ne)
1592    {
1593      String hostPort = getServerRepresentation(uData.getHostName(), uData.getPort());
1594      errPrintln();
1595      errPrintln(getMessageForException(ne, hostPort));
1596      logger.error(LocalizableMessage.raw("Complete error stack:"), ne);
1597      return null;
1598    }
1599  }
1600
1601  private void printSuccessMessage(PurgeHistoricalUserData uData, String taskID)
1602  {
1603    println();
1604    if (!uData.isOnline())
1605    {
1606      print(
1607          INFO_PROGRESS_PURGE_HISTORICAL_FINISHED_PROCEDURE.get());
1608    }
1609    else if (uData.getTaskSchedule().isStartNow())
1610    {
1611      print(INFO_TASK_TOOL_TASK_SUCESSFULL.get(
1612          INFO_PURGE_HISTORICAL_TASK_NAME.get(),
1613          taskID));
1614    }
1615    else if (uData.getTaskSchedule().getStartDate() != null)
1616    {
1617      print(INFO_TASK_TOOL_TASK_SCHEDULED_FUTURE.get(
1618          INFO_PURGE_HISTORICAL_TASK_NAME.get(),
1619          taskID,
1620          StaticUtils.formatDateTimeString(
1621              uData.getTaskSchedule().getStartDate())));
1622    }
1623    else
1624    {
1625      print(INFO_TASK_TOOL_RECURRING_TASK_SCHEDULED.get(
1626          INFO_PURGE_HISTORICAL_TASK_NAME.get(),
1627          taskID));
1628    }
1629
1630    println();
1631  }
1632
1633  /**
1634   * Launches the purge historical operation using the
1635   * provided connection.
1636   * @param ctx the connection to the server.
1637   * @throws ReplicationCliException if there is an error performing the
1638   * operation.
1639   */
1640  private ReplicationCliReturnCode purgeHistoricalRemoteTask(
1641      InitialLdapContext ctx,
1642      PurgeHistoricalUserData uData)
1643  throws ReplicationCliException
1644  {
1645    printPurgeProgressMessage(uData);
1646    ReplicationCliReturnCode returnCode = SUCCESSFUL;
1647    boolean taskCreated = false;
1648    boolean isOver = false;
1649    String dn = null;
1650    String taskID = null;
1651    while (!taskCreated)
1652    {
1653      BasicAttributes attrs = PurgeHistoricalUserData.getTaskAttributes(uData);
1654      dn = PurgeHistoricalUserData.getTaskDN(attrs);
1655      taskID = PurgeHistoricalUserData.getTaskID(attrs);
1656      try
1657      {
1658        DirContext dirCtx = ctx.createSubcontext(dn, attrs);
1659        taskCreated = true;
1660        logger.info(LocalizableMessage.raw("created task entry: "+attrs));
1661        dirCtx.close();
1662      }
1663      catch (NamingException ne)
1664      {
1665        logger.error(LocalizableMessage.raw("Error creating task "+attrs, ne));
1666        LocalizableMessage msg = ERR_LAUNCHING_PURGE_HISTORICAL.get();
1667        ReplicationCliReturnCode code = ERROR_LAUNCHING_PURGE_HISTORICAL;
1668        throw new ReplicationCliException(
1669            getThrowableMsg(msg, ne), code, ne);
1670      }
1671    }
1672
1673    // Polling only makes sense when we are recurrently scheduling a task
1674    // or the task is being executed now.
1675    String lastLogMsg = null;
1676    while (!isOver && uData.getTaskSchedule().getStartDate() == null)
1677    {
1678      sleepCatchInterrupt(500);
1679      try
1680      {
1681        SearchResult sr = getFirstSearchResult(ctx, dn,
1682            "ds-task-log-message",
1683            "ds-task-state",
1684            "ds-task-purge-conflicts-historical-purged-values-count",
1685            "ds-task-purge-conflicts-historical-purge-completed-in-time",
1686            "ds-task-purge-conflicts-historical-purge-completed-in-time",
1687            "ds-task-purge-conflicts-historical-last-purged-changenumber");
1688        String logMsg = getFirstValue(sr, "ds-task-log-message");
1689        if (logMsg != null && !logMsg.equals(lastLogMsg))
1690        {
1691          logger.info(LocalizableMessage.raw(logMsg));
1692          lastLogMsg = logMsg;
1693        }
1694        InstallerHelper helper = new InstallerHelper();
1695        String state = getFirstValue(sr, "ds-task-state");
1696
1697        if (helper.isDone(state) || helper.isStoppedByError(state))
1698        {
1699          isOver = true;
1700          LocalizableMessage errorMsg = getPurgeErrorMsg(lastLogMsg, state, ctx);
1701
1702          if (helper.isCompletedWithErrors(state))
1703          {
1704            logger.warn(LocalizableMessage.raw("Completed with error: "+errorMsg));
1705            errPrintln(errorMsg);
1706          }
1707          else if (!helper.isSuccessful(state) ||
1708              helper.isStoppedByError(state))
1709          {
1710            logger.warn(LocalizableMessage.raw("Error: "+errorMsg));
1711            ReplicationCliReturnCode code = ERROR_LAUNCHING_PURGE_HISTORICAL;
1712            throw new ReplicationCliException(errorMsg, code, null);
1713          }
1714        }
1715      }
1716      catch (NameNotFoundException x)
1717      {
1718        isOver = true;
1719      }
1720      catch (NamingException ne)
1721      {
1722        LocalizableMessage msg = ERR_READING_SERVER_TASK_PROGRESS.get();
1723        throw new ReplicationCliException(
1724          getThrowableMsg(msg, ne), ERROR_CONNECTING, ne);
1725      }
1726    }
1727
1728    if (returnCode == SUCCESSFUL)
1729    {
1730      printSuccessMessage(uData, taskID);
1731    }
1732    return returnCode;
1733  }
1734
1735  private SearchResult getFirstSearchResult(InitialLdapContext ctx, String dn, String... returnedAttributes)
1736      throws NamingException
1737  {
1738    SearchControls searchControls = new SearchControls();
1739    searchControls.setCountLimit(1);
1740    searchControls.setSearchScope(SearchControls.OBJECT_SCOPE);
1741    searchControls.setReturningAttributes(returnedAttributes);
1742    NamingEnumeration<SearchResult> res = ctx.search(dn, "objectclass=*", searchControls);
1743    try
1744    {
1745      SearchResult sr = null;
1746      sr = res.next();
1747      return sr;
1748    }
1749    finally
1750    {
1751      res.close();
1752    }
1753  }
1754
1755  private LocalizableMessage getPurgeErrorMsg(String lastLogMsg, String state, InitialLdapContext ctx)
1756  {
1757    String server = getHostPort(ctx);
1758    if (lastLogMsg != null)
1759    {
1760      return ERR_UNEXPECTED_DURING_TASK_WITH_LOG.get(lastLogMsg, state, server);
1761    }
1762    return ERR_UNEXPECTED_DURING_TASK_NO_LOG.get(state, server);
1763  }
1764
1765  /**
1766   * Checks that historical can actually be purged in the provided baseDNs
1767   * for the server.
1768   * @param suffixes the suffixes provided by the user.  This Collection is
1769   * updated with the base DNs that the user provided interactively.
1770   * @param ctx connection to the server.
1771   * @param interactive whether to ask the user to provide interactively
1772   * base DNs if none of the provided base DNs can be purged.
1773   */
1774  private void checkSuffixesForPurgeHistorical(Collection<String> suffixes,
1775      InitialLdapContext ctx, boolean interactive)
1776  {
1777    checkSuffixesForPurgeHistorical(suffixes, getReplicas(ctx), interactive);
1778  }
1779
1780  /**
1781   * Checks that historical can actually be purged in the provided baseDNs
1782   * for the local server.
1783   * @param suffixes the suffixes provided by the user.  This Collection is
1784   * updated with the base DNs that the user provided interactively.
1785   * @param interactive whether to ask the user to provide interactively
1786   * base DNs if none of the provided base DNs can be purged.
1787   */
1788  private void checkSuffixesForLocalPurgeHistorical(Collection<String> suffixes,
1789      boolean interactive)
1790  {
1791    checkSuffixesForPurgeHistorical(suffixes, getLocalReplicas(), interactive);
1792  }
1793
1794  private Collection<ReplicaDescriptor> getLocalReplicas()
1795  {
1796    Collection<ReplicaDescriptor> replicas = new ArrayList<>();
1797    ConfigFromFile configFromFile = new ConfigFromFile();
1798    configFromFile.readConfiguration();
1799    Collection<BackendDescriptor> backends = configFromFile.getBackends();
1800    for (BackendDescriptor backend : backends)
1801    {
1802      for (BaseDNDescriptor baseDN : backend.getBaseDns())
1803      {
1804        SuffixDescriptor suffix = new SuffixDescriptor();
1805        suffix.setDN(baseDN.getDn().toString());
1806
1807        ReplicaDescriptor replica = new ReplicaDescriptor();
1808
1809        if (baseDN.getType() == BaseDNDescriptor.Type.REPLICATED)
1810        {
1811          replica.setReplicationId(baseDN.getReplicaID());
1812        }
1813        else
1814        {
1815          replica.setReplicationId(-1);
1816        }
1817        replica.setBackendName(backend.getBackendID());
1818        replica.setSuffix(suffix);
1819        suffix.setReplicas(singleton(replica));
1820
1821        replicas.add(replica);
1822      }
1823    }
1824    return replicas;
1825  }
1826
1827  private void checkSuffixesForPurgeHistorical(Collection<String> suffixes, Collection<ReplicaDescriptor> replicas,
1828      boolean interactive)
1829  {
1830    TreeSet<String> availableSuffixes = new TreeSet<>();
1831    TreeSet<String> notReplicatedSuffixes = new TreeSet<>();
1832
1833    for (ReplicaDescriptor rep : replicas)
1834    {
1835      String dn = rep.getSuffix().getDN();
1836      if (rep.isReplicated())
1837      {
1838        availableSuffixes.add(dn);
1839      }
1840      else
1841      {
1842        notReplicatedSuffixes.add(dn);
1843      }
1844    }
1845
1846    checkSuffixesForPurgeHistorical(suffixes, availableSuffixes, notReplicatedSuffixes, interactive);
1847  }
1848
1849  private void checkSuffixesForPurgeHistorical(Collection<String> suffixes,
1850      Collection<String> availableSuffixes,
1851      Collection<String> notReplicatedSuffixes,
1852      boolean interactive)
1853  {
1854    if (availableSuffixes.isEmpty())
1855    {
1856      errPrintln();
1857      errPrintln(ERR_NO_SUFFIXES_AVAILABLE_TO_PURGE_HISTORICAL.get());
1858      suffixes.clear();
1859    }
1860    else
1861    {
1862      // Verify that the provided suffixes are configured in the servers.
1863      TreeSet<String> notFound = new TreeSet<>();
1864      TreeSet<String> alreadyNotReplicated = new TreeSet<>();
1865      for (String dn : suffixes)
1866      {
1867        if (!containsDN(availableSuffixes, dn))
1868        {
1869          if (containsDN(notReplicatedSuffixes, dn))
1870          {
1871            alreadyNotReplicated.add(dn);
1872          }
1873          else
1874          {
1875            notFound.add(dn);
1876          }
1877        }
1878      }
1879      suffixes.removeAll(notFound);
1880      suffixes.removeAll(alreadyNotReplicated);
1881      if (!notFound.isEmpty())
1882      {
1883        errPrintln();
1884        errPrintln(ERR_REPLICATION_PURGE_SUFFIXES_NOT_FOUND.get(toSingleLine(notFound)));
1885      }
1886      if (interactive)
1887      {
1888        askConfirmations(suffixes, availableSuffixes,
1889            ERR_NO_SUFFIXES_AVAILABLE_TO_PURGE_HISTORICAL,
1890            ERR_NO_SUFFIXES_SELECTED_TO_PURGE_HISTORICAL,
1891            INFO_REPLICATION_PURGE_HISTORICAL_PROMPT);
1892      }
1893    }
1894  }
1895
1896  private void askConfirmations(Collection<String> suffixes,
1897      Collection<String> availableSuffixes, Arg0 noSuffixAvailableMsg,
1898      Arg0 noSuffixSelectedMsg, Arg1<Object> confirmationMsgPromt)
1899  {
1900    if (containsOnlySchemaOrAdminSuffix(availableSuffixes))
1901    {
1902      // In interactive mode we do not propose to manage the administration suffix.
1903      errPrintln();
1904      errPrintln(noSuffixAvailableMsg.get());
1905      return;
1906    }
1907
1908    while (suffixes.isEmpty())
1909    {
1910      errPrintln();
1911      errPrintln(noSuffixSelectedMsg.get());
1912      boolean confirmationLimitReached = askConfirmations(confirmationMsgPromt, availableSuffixes, suffixes);
1913      if (confirmationLimitReached)
1914      {
1915        suffixes.clear();
1916        break;
1917      }
1918    }
1919  }
1920
1921  private boolean containsOnlySchemaOrAdminSuffix(Collection<String> suffixes)
1922  {
1923    for (String suffix : suffixes)
1924    {
1925      if (!isSchemaOrInternalAdminSuffix(suffix))
1926      {
1927        return false;
1928      }
1929    }
1930    return true;
1931  }
1932
1933  private boolean isSchemaOrInternalAdminSuffix(String suffix)
1934  {
1935    return areDnsEqual(suffix, ADSContext.getAdministrationSuffixDN())
1936        || areDnsEqual(suffix, Constants.SCHEMA_DN)
1937        || areDnsEqual(suffix,  Constants.REPLICATION_CHANGES_DN);
1938  }
1939
1940  /**
1941   * Based on the data provided in the command-line it initializes replication
1942   * between two servers.
1943   * @return the error code if the operation failed and SUCCESSFUL if it was
1944   * successful.
1945   */
1946  private ReplicationCliReturnCode initializeReplication()
1947  {
1948    SourceDestinationServerUserData uData = new SourceDestinationServerUserData();
1949    if (!argParser.isInteractive())
1950    {
1951      initializeWithArgParser(uData);
1952      return initializeReplication(uData);
1953    }
1954
1955    OperationBetweenSourceAndDestinationServers
1956        initializeReplicationOperations = new OperationBetweenSourceAndDestinationServers()
1957    {
1958      @Override
1959      public boolean continueAfterUserInput(Collection<String> baseDNs, InitialLdapContext source,
1960          InitialLdapContext dest, boolean interactive)
1961      {
1962        checkSuffixesForInitializeReplication(baseDNs, source, dest, interactive);
1963        return baseDNs.isEmpty();
1964      }
1965
1966      @Override
1967      public boolean confirmOperation(SourceDestinationServerUserData uData, InitialLdapContext ctxSource,
1968          InitialLdapContext ctxDestination, boolean defaultValue)
1969      {
1970        return !askConfirmation(getInitializeReplicationPrompt(uData, ctxSource, ctxDestination), defaultValue);
1971      }
1972    };
1973    return promptIfRequired(uData, initializeReplicationOperations) ? initializeReplication(uData) : USER_CANCELLED;
1974  }
1975
1976  /**
1977   * Updates the contents of the provided PurgeHistoricalUserData
1978   * object with the information provided in the command-line.  If some
1979   * information is missing, ask the user to provide valid data.
1980   * We assume that if this method is called we are in interactive mode.
1981   * @param uData the object to be updated.
1982   * @return <CODE>true</CODE> if the object was successfully updated and
1983   * <CODE>false</CODE> if the user canceled the operation.
1984   */
1985  private boolean promptIfRequired(PurgeHistoricalUserData uData)
1986  {
1987    InitialLdapContext ctx = null;
1988    try
1989    {
1990      ctx = getInitialLdapContext(uData);
1991      if (ctx == null)
1992      {
1993        return false;
1994      }
1995
1996      /* Prompt for maximum duration */
1997      int maximumDuration = argParser.getMaximumDuration();
1998      if (!argParser.maximumDurationArg.isPresent())
1999      {
2000        println();
2001        maximumDuration = askInteger(INFO_REPLICATION_PURGE_HISTORICAL_MAXIMUM_DURATION_PROMPT.get(),
2002            getDefaultValue(argParser.maximumDurationArg), logger);
2003      }
2004      uData.setMaximumDuration(maximumDuration);
2005
2006      LinkedList<String> suffixes = argParser.getBaseDNs();
2007      if (uData.isOnline())
2008      {
2009        checkSuffixesForPurgeHistorical(suffixes, ctx, true);
2010      }
2011      else
2012      {
2013        checkSuffixesForLocalPurgeHistorical(suffixes, true);
2014      }
2015      if (suffixes.isEmpty())
2016      {
2017        return false;
2018      }
2019      uData.setBaseDNs(suffixes);
2020
2021      if (uData.isOnline())
2022      {
2023        List<? extends TaskEntry> taskEntries = getAvailableTaskEntries(ctx);
2024
2025        TaskScheduleInteraction interaction =
2026            new TaskScheduleInteraction(uData.getTaskSchedule(), argParser.taskArgs, this,
2027                INFO_PURGE_HISTORICAL_TASK_NAME.get());
2028        interaction.setFormatter(formatter);
2029        interaction.setTaskEntries(taskEntries);
2030        try
2031        {
2032          interaction.run();
2033        }
2034        catch (ClientException ce)
2035        {
2036          errPrintln(ce.getMessageObject());
2037          return false;
2038        }
2039      }
2040      return true;
2041    }
2042    finally
2043    {
2044      close(ctx);
2045    }
2046  }
2047
2048  private InitialLdapContext getInitialLdapContext(PurgeHistoricalUserData uData)
2049  {
2050    boolean firstTry = true;
2051    Boolean serverRunning = null;
2052
2053    while (true)
2054    {
2055      boolean promptForConnection = firstTry && argParser.connectionArgumentsPresent();
2056      if (!promptForConnection)
2057      {
2058        if (serverRunning == null)
2059        {
2060          serverRunning = Utilities.isServerRunning(Installation.getLocal().getInstanceDirectory());
2061        }
2062
2063        if (!serverRunning)
2064        {
2065          try
2066          {
2067            println();
2068            promptForConnection = !askConfirmation(
2069                INFO_REPLICATION_PURGE_HISTORICAL_LOCAL_PROMPT.get(), true, logger);
2070          }
2071          catch (ClientException ce)
2072          {
2073            errPrintln(ce.getMessageObject());
2074          }
2075
2076          if (!promptForConnection)
2077          {
2078            uData.setOnline(false);
2079            return null;
2080          }
2081        }
2082      }
2083
2084      try
2085      {
2086        sourceServerCI.run();
2087
2088        InitialLdapContext ctx = createInitialLdapContextInteracting(sourceServerCI);
2089        if (ctx != null)
2090        {
2091          uData.setOnline(true);
2092          uData.setHostName(sourceServerCI.getHostName());
2093          uData.setPort(sourceServerCI.getPortNumber());
2094          uData.setAdminUid(sourceServerCI.getAdministratorUID());
2095          uData.setAdminPwd(sourceServerCI.getBindPassword());
2096        }
2097        return ctx;
2098      }
2099      catch (ClientException ce)
2100      {
2101        logger.warn(LocalizableMessage.raw("Client exception " + ce));
2102        errPrintln();
2103        errPrintln(ce.getMessageObject());
2104        errPrintln();
2105        sourceServerCI.resetConnectionArguments();
2106      }
2107      catch (ArgumentException ae)
2108      {
2109        logger.warn(LocalizableMessage.raw("Argument exception " + ae));
2110        argParser.displayMessageAndUsageReference(getErrStream(), ae.getMessageObject());
2111        return null;
2112      }
2113      firstTry = false;
2114    }
2115  }
2116
2117  private List<? extends TaskEntry> getAvailableTaskEntries(
2118      InitialLdapContext ctx)
2119  {
2120    List<TaskEntry> taskEntries = new ArrayList<>();
2121    List<OpenDsException> exceptions = new ArrayList<>();
2122    ConfigFromDirContext cfg = new ConfigFromDirContext();
2123    cfg.updateTaskInformation(ctx, exceptions, taskEntries);
2124    for (OpenDsException ode : exceptions)
2125    {
2126      logger.warn(LocalizableMessage.raw("Error retrieving task entries: "+ode, ode));
2127    }
2128    return taskEntries;
2129  }
2130
2131  /**
2132   * Updates the contents of the provided EnableReplicationUserData object
2133   * with the information provided in the command-line.  If some information
2134   * is missing, ask the user to provide valid data.
2135   * We assume that if this method is called we are in interactive mode.
2136   * @param uData the object to be updated.
2137   * @return <CODE>true</CODE> if the object was successfully updated and
2138   * <CODE>false</CODE> if the user cancelled the operation.
2139   * @throws ReplicationCliException if a critical error occurs reading the
2140   * ADS.
2141   */
2142  private boolean promptIfRequired(EnableReplicationUserData uData)
2143  throws ReplicationCliException
2144  {
2145    boolean cancelled = false;
2146
2147    boolean administratorDefined = false;
2148
2149    sourceServerCI.setUseAdminOrBindDn(true);
2150
2151    String adminPwd = argParser.getBindPasswordAdmin();
2152    String adminUid = argParser.getAdministratorUID();
2153
2154    /* Try to connect to the first server. */
2155    String host1 = getValue(argParser.server1.hostNameArg);
2156    int port1 = getValue(argParser.server1.portArg);
2157    String bindDn1 = getValue(argParser.server1.bindDnArg);
2158    String pwd1 = argParser.server1.getBindPassword();
2159    String pwd = null;
2160    Map<String, String> pwdFile = null;
2161    if (argParser.server1.bindPasswordArg.isPresent())
2162    {
2163      pwd = argParser.server1.bindPasswordArg.getValue();
2164    }
2165    else if (argParser.server1.bindPasswordFileArg.isPresent())
2166    {
2167      pwdFile = argParser.server1.bindPasswordFileArg.getNameToValueMap();
2168    }
2169    else if (bindDn1 == null)
2170    {
2171      pwd = adminPwd;
2172      if (argParser.getSecureArgsList().bindPasswordFileArg.isPresent())
2173      {
2174        pwdFile = argParser.getSecureArgsList().bindPasswordFileArg.
2175          getNameToValueMap();
2176      }
2177    }
2178
2179    /*
2180     * Use a copy of the argument properties since the map might be cleared
2181     * in initializeGlobalArguments.
2182     */
2183    sourceServerCI.initializeGlobalArguments(host1, port1, adminUid, bindDn1, pwd,
2184        pwdFile == null ? null : new LinkedHashMap<String, String>(pwdFile));
2185    InitialLdapContext ctx1 = null;
2186
2187    while (ctx1 == null && !cancelled)
2188    {
2189      try
2190      {
2191        sourceServerCI.setHeadingMessage(INFO_REPLICATION_ENABLE_HOST1_CONNECTION_PARAMETERS.get());
2192        sourceServerCI.run();
2193        host1 = sourceServerCI.getHostName();
2194        port1 = sourceServerCI.getPortNumber();
2195        if (sourceServerCI.getProvidedAdminUID() != null)
2196        {
2197          adminUid = sourceServerCI.getProvidedAdminUID();
2198          if (sourceServerCI.getProvidedBindDN() == null)
2199          {
2200            // If the explicit bind DN is not null, the password corresponds
2201            // to that bind DN.  We are in the case where the user provides
2202            // bind DN on first server and admin UID globally.
2203            adminPwd = sourceServerCI.getBindPassword();
2204          }
2205        }
2206        bindDn1 = sourceServerCI.getBindDN();
2207        pwd1 = sourceServerCI.getBindPassword();
2208
2209        ctx1 = createInitialLdapContextInteracting(sourceServerCI);
2210        if (ctx1 == null)
2211        {
2212          cancelled = true;
2213        }
2214      }
2215      catch (ClientException ce)
2216      {
2217        logger.warn(LocalizableMessage.raw("Client exception "+ce));
2218        errPrintln();
2219        errPrintln(ce.getMessageObject());
2220        errPrintln();
2221        sourceServerCI.resetConnectionArguments();
2222      }
2223      catch (ArgumentException ae)
2224      {
2225        logger.warn(LocalizableMessage.raw("Argument exception "+ae));
2226        argParser.displayMessageAndUsageReference(getErrStream(), ae.getMessageObject());
2227        cancelled = true;
2228      }
2229    }
2230
2231    if (!cancelled)
2232    {
2233      uData.getServer1().setHostName(host1);
2234      uData.getServer1().setPort(port1);
2235      uData.getServer1().setBindDn(bindDn1);
2236      uData.getServer1().setPwd(pwd1);
2237    }
2238    int replicationPort1 = -1;
2239    boolean secureReplication1 = argParser.server1.secureReplicationArg.isPresent();
2240    boolean configureReplicationServer1 = argParser.server1.configureReplicationServer();
2241    boolean configureReplicationDomain1 = argParser.server1.configureReplicationDomain();
2242    if (ctx1 != null)
2243    {
2244      int repPort1 = getReplicationPort(ctx1);
2245      boolean replicationServer1Configured = repPort1 > 0;
2246      if (replicationServer1Configured && !configureReplicationServer1)
2247      {
2248        final LocalizableMessage msg =
2249            INFO_REPLICATION_SERVER_CONFIGURED_WARNING_PROMPT.get(getHostPort(ctx1), repPort1);
2250        if (!askConfirmation(msg, false))
2251        {
2252          cancelled = true;
2253        }
2254      }
2255
2256      // Try to get the replication port for server 1 only if it is required.
2257      if (!cancelled
2258          && configureReplicationServer1
2259          && !replicationServer1Configured
2260          && argParser.advancedArg.isPresent()
2261          && configureReplicationDomain1)
2262      {
2263        // Only ask if the replication domain will be configured (if not
2264        // the replication server MUST be configured).
2265        try
2266        {
2267          configureReplicationServer1 = askConfirmation(
2268              INFO_REPLICATION_ENABLE_REPLICATION_SERVER1_PROMPT.get(),
2269              true, logger);
2270        }
2271        catch (ClientException ce)
2272        {
2273          errPrintln(ce.getMessageObject());
2274          cancelled = true;
2275        }
2276      }
2277      if (!cancelled
2278          && configureReplicationServer1
2279          && !replicationServer1Configured)
2280      {
2281        boolean tryWithDefault = argParser.getReplicationPort1() != -1;
2282        while (replicationPort1 == -1)
2283        {
2284          if (tryWithDefault)
2285          {
2286            replicationPort1 = argParser.getReplicationPort1();
2287            tryWithDefault = false;
2288          }
2289          else
2290          {
2291            replicationPort1 = askPort(
2292                INFO_REPLICATION_ENABLE_REPLICATIONPORT1_PROMPT.get(),
2293                getDefaultValue(argParser.server1.replicationPortArg), logger);
2294            println();
2295          }
2296          if (!argParser.skipReplicationPortCheck() && isLocalHost(host1))
2297          {
2298            if (!SetupUtils.canUseAsPort(replicationPort1))
2299            {
2300              errPrintln();
2301              errPrintln(getCannotBindToPortError(replicationPort1));
2302              errPrintln();
2303              replicationPort1 = -1;
2304            }
2305          }
2306          else if (replicationPort1 == port1)
2307          {
2308            // This is something that we must do in any case... this test is
2309            // already included when we call SetupUtils.canUseAsPort
2310            errPrintln();
2311            errPrintln(ERR_REPLICATION_PORT_AND_REPLICATION_PORT_EQUAL.get(host1, replicationPort1));
2312            errPrintln();
2313            replicationPort1 = -1;
2314          }
2315        }
2316        if (!secureReplication1)
2317        {
2318          try
2319          {
2320            secureReplication1 =
2321              askConfirmation(INFO_REPLICATION_ENABLE_SECURE1_PROMPT.get(replicationPort1),
2322                  false, logger);
2323          }
2324          catch (ClientException ce)
2325          {
2326            errPrintln(ce.getMessageObject());
2327            cancelled = true;
2328          }
2329          println();
2330        }
2331      }
2332      if (!cancelled &&
2333          configureReplicationDomain1 &&
2334          configureReplicationServer1 &&
2335          argParser.advancedArg.isPresent())
2336      {
2337        // Only necessary to ask if the replication server will be configured
2338        try
2339        {
2340          configureReplicationDomain1 = askConfirmation(
2341              INFO_REPLICATION_ENABLE_REPLICATION_DOMAIN1_PROMPT.get(),
2342              true, logger);
2343        }
2344        catch (ClientException ce)
2345        {
2346          errPrintln(ce.getMessageObject());
2347          cancelled = true;
2348        }
2349      }
2350      // If the server contains an ADS. Try to load it and only load it: if
2351      // there are issues with the ADS they will be encountered in the
2352      // enableReplication(EnableReplicationUserData) method.  Here we have
2353      // to load the ADS to ask the user to accept the certificates and
2354      // eventually admin authentication data.
2355      if (!cancelled)
2356      {
2357        AtomicReference<InitialLdapContext> aux = new AtomicReference<>(ctx1);
2358        cancelled = !loadADSAndAcceptCertificates(sourceServerCI, aux, uData, true);
2359        ctx1 = aux.get();
2360      }
2361      if (!cancelled)
2362      {
2363        administratorDefined |= hasAdministrator(ctx1);
2364        if (uData.getAdminPwd() != null)
2365        {
2366          adminPwd = uData.getAdminPwd();
2367        }
2368      }
2369    }
2370    uData.getServer1().setReplicationPort(replicationPort1);
2371    uData.getServer1().setSecureReplication(secureReplication1);
2372    uData.getServer1().setConfigureReplicationServer(configureReplicationServer1);
2373    uData.getServer1().setConfigureReplicationDomain(configureReplicationDomain1);
2374    firstServerCommandBuilder = new CommandBuilder(null, null);
2375    if (mustPrintCommandBuilder())
2376    {
2377      firstServerCommandBuilder.append(sourceServerCI.getCommandBuilder());
2378    }
2379
2380    /* Prompt for information on the second server. */
2381    String host2 = null;
2382    int port2 = -1;
2383    String bindDn2 = null;
2384    String pwd2 = null;
2385    LDAPConnectionConsoleInteraction destinationServerCI = new LDAPConnectionConsoleInteraction(this,
2386        argParser.getSecureArgsList());
2387    destinationServerCI.resetHeadingDisplayed();
2388
2389    boolean doNotDisplayFirstError = false;
2390
2391    if (!cancelled)
2392    {
2393      host2 = getValue(argParser.server2.hostNameArg);
2394      port2 = getValue(argParser.server2.portArg);
2395      bindDn2 = getValue(argParser.server2.bindDnArg);
2396      pwd2 = argParser.server2.getBindPassword();
2397
2398      pwdFile = null;
2399      pwd = null;
2400      if (argParser.server2.bindPasswordArg.isPresent())
2401      {
2402        pwd = argParser.server2.bindPasswordArg.getValue();
2403      }
2404      else if (argParser.server2.bindPasswordFileArg.isPresent())
2405      {
2406        pwdFile = argParser.server2.bindPasswordFileArg.getNameToValueMap();
2407      }
2408      else if (bindDn2 == null)
2409      {
2410        doNotDisplayFirstError = true;
2411        pwd = adminPwd;
2412        if (argParser.getSecureArgsList().bindPasswordFileArg.isPresent())
2413        {
2414          pwdFile = argParser.getSecureArgsList().bindPasswordFileArg.
2415            getNameToValueMap();
2416        }
2417      }
2418
2419      /*
2420       * Use a copy of the argument properties since the map might be cleared
2421       * in initializeGlobalArguments.
2422       */
2423      destinationServerCI.initializeGlobalArguments(host2, port2, adminUid, bindDn2, pwd,
2424          pwdFile == null ? null : new LinkedHashMap<String, String>(pwdFile));
2425      destinationServerCI.setUseAdminOrBindDn(true);
2426    }
2427    InitialLdapContext ctx2 = null;
2428
2429    while (ctx2 == null && !cancelled)
2430    {
2431      try
2432      {
2433        destinationServerCI.setHeadingMessage(INFO_REPLICATION_ENABLE_HOST2_CONNECTION_PARAMETERS.get());
2434        destinationServerCI.run();
2435        host2 = destinationServerCI.getHostName();
2436        port2 = destinationServerCI.getPortNumber();
2437        if (destinationServerCI.getProvidedAdminUID() != null)
2438        {
2439          adminUid = destinationServerCI.getProvidedAdminUID();
2440          if (destinationServerCI.getProvidedBindDN() == null)
2441          {
2442            // If the explicit bind DN is not null, the password corresponds
2443            // to that bind DN.  We are in the case where the user provides
2444            // bind DN on first server and admin UID globally.
2445            adminPwd = destinationServerCI.getBindPassword();
2446          }
2447        }
2448        bindDn2 = destinationServerCI.getBindDN();
2449        pwd2 = destinationServerCI.getBindPassword();
2450
2451        boolean error = false;
2452        if (host1.equalsIgnoreCase(host2) && port1 == port2)
2453        {
2454          port2 = -1;
2455          errPrintln();
2456          errPrintln(ERR_REPLICATION_ENABLE_SAME_SERVER_PORT.get(host1, port1));
2457          errPrintln();
2458          error = true;
2459        }
2460
2461        if (!error)
2462        {
2463          ctx2 = createInitialLdapContextInteracting(destinationServerCI, true);
2464          if (ctx2 == null)
2465          {
2466            cancelled = true;
2467          }
2468        }
2469      }
2470      catch (ClientException ce)
2471      {
2472        logger.warn(LocalizableMessage.raw("Client exception "+ce));
2473        if (!doNotDisplayFirstError)
2474        {
2475          errPrintln();
2476          errPrintln(ce.getMessageObject());
2477          errPrintln();
2478          destinationServerCI.resetConnectionArguments();
2479        }
2480        else
2481        {
2482          // Reset only the credential parameters.
2483          destinationServerCI.resetConnectionArguments();
2484          destinationServerCI.initializeGlobalArguments(host2, port2, null, null, null, null);
2485        }
2486      }
2487      catch (ArgumentException ae)
2488      {
2489        logger.warn(LocalizableMessage.raw("Argument exception "+ae));
2490        argParser.displayMessageAndUsageReference(getErrStream(), ae.getMessageObject());
2491        cancelled = true;
2492      }
2493      finally
2494      {
2495        doNotDisplayFirstError = false;
2496      }
2497    }
2498
2499    if (!cancelled)
2500    {
2501      uData.getServer2().setHostName(host2);
2502      uData.getServer2().setPort(port2);
2503      uData.getServer2().setBindDn(bindDn2);
2504      uData.getServer2().setPwd(pwd2);
2505    }
2506
2507    int replicationPort2 = -1;
2508    boolean secureReplication2 = argParser.server2.secureReplicationArg.isPresent();
2509    boolean configureReplicationServer2 = argParser.server2.configureReplicationServer();
2510    boolean configureReplicationDomain2 = argParser.server2.configureReplicationDomain();
2511    if (ctx2 != null)
2512    {
2513      int repPort2 = getReplicationPort(ctx2);
2514      boolean replicationServer2Configured = repPort2 > 0;
2515      if (replicationServer2Configured && !configureReplicationServer2)
2516      {
2517        final LocalizableMessage prompt =
2518            INFO_REPLICATION_SERVER_CONFIGURED_WARNING_PROMPT.get(getHostPort(ctx2), repPort2);
2519        if (!askConfirmation(prompt, false))
2520        {
2521          cancelled = true;
2522        }
2523      }
2524
2525      // Try to get the replication port for server 2 only if it is required.
2526      if (!cancelled
2527          && configureReplicationServer2
2528          && !replicationServer2Configured)
2529      {
2530        // Only ask if the replication domain will be configured (if not the
2531        // replication server MUST be configured).
2532        if (argParser.advancedArg.isPresent() &&
2533            configureReplicationDomain2)
2534        {
2535          try
2536          {
2537            configureReplicationServer2 = askConfirmation(
2538                INFO_REPLICATION_ENABLE_REPLICATION_SERVER2_PROMPT.get(),
2539                true, logger);
2540          }
2541          catch (ClientException ce)
2542          {
2543            errPrintln(ce.getMessageObject());
2544            cancelled = true;
2545          }
2546        }
2547        if (!cancelled
2548            && configureReplicationServer2
2549            && !replicationServer2Configured)
2550        {
2551          boolean tryWithDefault = argParser.getReplicationPort2() != -1;
2552          while (replicationPort2 == -1)
2553          {
2554            if (tryWithDefault)
2555            {
2556              replicationPort2 = argParser.getReplicationPort2();
2557              tryWithDefault = false;
2558            }
2559            else
2560            {
2561              replicationPort2 = askPort(
2562                  INFO_REPLICATION_ENABLE_REPLICATIONPORT2_PROMPT.get(),
2563                  getDefaultValue(argParser.server2.replicationPortArg), logger);
2564              println();
2565            }
2566            if (!argParser.skipReplicationPortCheck() &&
2567                isLocalHost(host2))
2568            {
2569              if (!SetupUtils.canUseAsPort(replicationPort2))
2570              {
2571                errPrintln();
2572                errPrintln(getCannotBindToPortError(replicationPort2));
2573                errPrintln();
2574                replicationPort2 = -1;
2575              }
2576            }
2577            else if (replicationPort2 == port2)
2578            {
2579              // This is something that we must do in any case... this test is
2580              // already included when we call SetupUtils.canUseAsPort
2581              errPrintln();
2582              errPrintln(ERR_REPLICATION_PORT_AND_REPLICATION_PORT_EQUAL.get(host2, replicationPort2));
2583              replicationPort2 = -1;
2584            }
2585            if (host1.equalsIgnoreCase(host2)
2586                && replicationPort1 > 0
2587                && replicationPort1 == replicationPort2)
2588            {
2589              errPrintln();
2590              errPrintln(ERR_REPLICATION_SAME_REPLICATION_PORT.get(replicationPort2, host1));
2591              errPrintln();
2592              replicationPort2 = -1;
2593            }
2594          }
2595          if (!secureReplication2)
2596          {
2597            try
2598            {
2599              secureReplication2 =
2600                askConfirmation(INFO_REPLICATION_ENABLE_SECURE2_PROMPT.get(replicationPort2), false, logger);
2601            }
2602            catch (ClientException ce)
2603            {
2604              errPrintln(ce.getMessageObject());
2605              cancelled = true;
2606            }
2607            println();
2608          }
2609        }
2610      }
2611      if (!cancelled &&
2612          configureReplicationDomain2 &&
2613          configureReplicationServer2 &&
2614          argParser.advancedArg.isPresent())
2615      {
2616        // Only necessary to ask if the replication server will be configured
2617        try
2618        {
2619          configureReplicationDomain2 = askConfirmation(
2620              INFO_REPLICATION_ENABLE_REPLICATION_DOMAIN2_PROMPT.get(),
2621              true, logger);
2622        }
2623        catch (ClientException ce)
2624        {
2625          errPrintln(ce.getMessageObject());
2626          cancelled = true;
2627        }
2628      }
2629      // If the server contains an ADS. Try to load it and only load it: if
2630      // there are issues with the ADS they will be encountered in the
2631      // enableReplication(EnableReplicationUserData) method.  Here we have
2632      // to load the ADS to ask the user to accept the certificates.
2633      if (!cancelled)
2634      {
2635        AtomicReference<InitialLdapContext> aux = new AtomicReference<>(ctx2);
2636        cancelled = !loadADSAndAcceptCertificates(destinationServerCI, aux, uData, false);
2637        ctx2 = aux.get();
2638      }
2639      if (!cancelled)
2640      {
2641        administratorDefined |= hasAdministrator(ctx2);
2642      }
2643    }
2644    uData.getServer2().setReplicationPort(replicationPort2);
2645    uData.getServer2().setSecureReplication(secureReplication2);
2646    uData.getServer2().setConfigureReplicationServer(configureReplicationServer2);
2647    uData.getServer2().setConfigureReplicationDomain(configureReplicationDomain2);
2648
2649    // If the adminUid and adminPwd are not set in the EnableReplicationUserData
2650    // object, that means that there are no administrators and that they
2651    // must be created. The adminUId and adminPwd are updated inside
2652    // loadADSAndAcceptCertificates.
2653    boolean promptedForAdmin = false;
2654
2655    // There is a case where we haven't had need for the administrator
2656    // credentials even if the administrators are defined: where all the servers
2657    // can be accessed with another user (for instance if all the server have
2658    // defined cn=directory manager and all the entries have the same password).
2659    if (!cancelled && uData.getAdminUid() == null && !administratorDefined)
2660    {
2661      if (adminUid == null)
2662      {
2663        println(INFO_REPLICATION_ENABLE_ADMINISTRATOR_MUST_BE_CREATED.get());
2664        promptedForAdmin = true;
2665        adminUid= askForAdministratorUID(
2666            getDefaultValue(argParser.getAdminUidArg()), logger);
2667        println();
2668      }
2669      uData.setAdminUid(adminUid);
2670    }
2671
2672    if (uData.getAdminPwd() == null)
2673    {
2674      uData.setAdminPwd(adminPwd);
2675    }
2676    if (!cancelled && uData.getAdminPwd() == null && !administratorDefined)
2677    {
2678      adminPwd = null;
2679      int nPasswordPrompts = 0;
2680      while (adminPwd == null)
2681      {
2682        if (nPasswordPrompts > CONFIRMATION_MAX_TRIES)
2683        {
2684          errPrintln(ERR_CONFIRMATION_TRIES_LIMIT_REACHED.get(
2685              CONFIRMATION_MAX_TRIES));
2686          cancelled = true;
2687          break;
2688        }
2689        nPasswordPrompts ++;
2690        if (!promptedForAdmin)
2691        {
2692          println();
2693          println(INFO_REPLICATION_ENABLE_ADMINISTRATOR_MUST_BE_CREATED.get());
2694          println();
2695        }
2696        while (adminPwd == null)
2697        {
2698          adminPwd = askForAdministratorPwd(logger);
2699          println();
2700        }
2701        String adminPwdConfirm = null;
2702        while (adminPwdConfirm == null)
2703        {
2704          try
2705          {
2706            adminPwdConfirm = String.valueOf(readPassword(INFO_ADMINISTRATOR_PWD_CONFIRM_PROMPT.get()));
2707          }
2708          catch (ClientException ex)
2709          {
2710            logger.warn(LocalizableMessage.raw("Error reading input: " + ex, ex));
2711          }
2712          println();
2713        }
2714        if (!adminPwd.equals(adminPwdConfirm))
2715        {
2716          println();
2717          errPrintln(ERR_ADMINISTRATOR_PWD_DO_NOT_MATCH.get());
2718          println();
2719          adminPwd = null;
2720        }
2721      }
2722      uData.setAdminPwd(adminPwd);
2723    }
2724
2725    if (!cancelled)
2726    {
2727      LinkedList<String> suffixes = argParser.getBaseDNs();
2728      checkSuffixesForEnableReplication(suffixes, ctx1, ctx2, true, uData);
2729      cancelled = suffixes.isEmpty();
2730
2731      uData.setBaseDNs(suffixes);
2732    }
2733
2734    close(ctx1, ctx2);
2735    uData.setReplicateSchema(!argParser.noSchemaReplication());
2736
2737    return !cancelled;
2738  }
2739
2740  /**
2741   * Updates the contents of the provided DisableReplicationUserData object
2742   * with the information provided in the command-line.  If some information
2743   * is missing, ask the user to provide valid data.
2744   * We assume that if this method is called we are in interactive mode.
2745   * @param uData the object to be updated.
2746   * @return <CODE>true</CODE> if the object was successfully updated and
2747   * <CODE>false</CODE> if the user cancelled the operation.
2748   * @throws ReplicationCliException if there is a critical error reading the
2749   * ADS.
2750   */
2751  private boolean promptIfRequired(DisableReplicationUserData uData)
2752  throws ReplicationCliException
2753  {
2754    boolean cancelled = false;
2755
2756    String adminPwd = argParser.getBindPasswordAdmin();
2757    String adminUid = argParser.getAdministratorUID();
2758    String bindDn = argParser.getBindDNToDisable();
2759
2760    // This is done because we want to ask explicitly for this
2761
2762    String host = argParser.getHostNameToDisable();
2763    int port = argParser.getPortToDisable();
2764
2765    /* Try to connect to the server. */
2766    InitialLdapContext ctx = null;
2767
2768    while (ctx == null && !cancelled)
2769    {
2770      try
2771      {
2772        sourceServerCI.setUseAdminOrBindDn(true);
2773        sourceServerCI.run();
2774        host = sourceServerCI.getHostName();
2775        port = sourceServerCI.getPortNumber();
2776        bindDn = sourceServerCI.getProvidedBindDN();
2777        adminUid = sourceServerCI.getProvidedAdminUID();
2778        adminPwd = sourceServerCI.getBindPassword();
2779
2780        ctx = createInitialLdapContextInteracting(sourceServerCI);
2781        if (ctx == null)
2782        {
2783          cancelled = true;
2784        }
2785      }
2786      catch (ClientException ce)
2787      {
2788        logger.warn(LocalizableMessage.raw("Client exception "+ce));
2789        errPrintln();
2790        errPrintln(ce.getMessageObject());
2791        errPrintln();
2792        sourceServerCI.resetConnectionArguments();
2793      }
2794      catch (ArgumentException ae)
2795      {
2796        logger.warn(LocalizableMessage.raw("Argument exception "+ae));
2797        argParser.displayMessageAndUsageReference(getErrStream(), ae.getMessageObject());
2798        cancelled = true;
2799      }
2800    }
2801
2802    if (!cancelled)
2803    {
2804      uData.setHostName(host);
2805      uData.setPort(port);
2806      uData.setAdminUid(adminUid);
2807      uData.setBindDn(bindDn);
2808      uData.setAdminPwd(adminPwd);
2809    }
2810    if (ctx != null && adminUid != null)
2811    {
2812      // If the server contains an ADS, try to load it and only load it: if
2813      // there are issues with the ADS they will be encountered in the
2814      // disableReplication(DisableReplicationUserData) method.  Here we have
2815      // to load the ADS to ask the user to accept the certificates and
2816      // eventually admin authentication data.
2817      AtomicReference<InitialLdapContext> aux = new AtomicReference<>(ctx);
2818      cancelled = !loadADSAndAcceptCertificates(sourceServerCI, aux, uData, false);
2819      ctx = aux.get();
2820    }
2821
2822    boolean disableAll = argParser.disableAllArg.isPresent();
2823    boolean disableReplicationServer =
2824      argParser.disableReplicationServerArg.isPresent();
2825    if (disableAll ||
2826        (argParser.advancedArg.isPresent() &&
2827        argParser.getBaseDNs().isEmpty() &&
2828        !disableReplicationServer))
2829    {
2830      try
2831      {
2832        disableAll = askConfirmation(INFO_REPLICATION_PROMPT_DISABLE_ALL.get(),
2833          disableAll, logger);
2834      }
2835      catch (ClientException ce)
2836      {
2837        errPrintln(ce.getMessageObject());
2838        cancelled = true;
2839      }
2840    }
2841    int repPort = getReplicationPort(ctx);
2842    if (!disableAll
2843        && (argParser.advancedArg.isPresent() || disableReplicationServer)
2844        && repPort > 0)
2845    {
2846      try
2847      {
2848        disableReplicationServer = askConfirmation(
2849            INFO_REPLICATION_PROMPT_DISABLE_REPLICATION_SERVER.get(repPort),
2850            disableReplicationServer,
2851            logger);
2852      }
2853      catch (ClientException ce)
2854      {
2855        errPrintln(ce.getMessageObject());
2856        cancelled = true;
2857      }
2858    }
2859    if (disableReplicationServer && repPort < 0)
2860    {
2861      disableReplicationServer = false;
2862      final LocalizableMessage msg = INFO_REPLICATION_PROMPT_NO_REPLICATION_SERVER_TO_DISABLE.get(getHostPort(ctx));
2863      try
2864      {
2865        cancelled = askConfirmation(msg, false, logger);
2866      }
2867      catch (ClientException ce)
2868      {
2869        errPrintln(ce.getMessageObject());
2870        cancelled = true;
2871      }
2872    }
2873    if (repPort > 0 && disableAll)
2874    {
2875      disableReplicationServer = true;
2876    }
2877    uData.setDisableAll(disableAll);
2878    uData.setDisableReplicationServer(disableReplicationServer);
2879    if (!cancelled && !disableAll)
2880    {
2881      LinkedList<String> suffixes = argParser.getBaseDNs();
2882      checkSuffixesForDisableReplication(suffixes, ctx, true, !disableReplicationServer);
2883      cancelled = suffixes.isEmpty() && !disableReplicationServer;
2884
2885      uData.setBaseDNs(suffixes);
2886
2887      if (!uData.disableReplicationServer() && repPort > 0 &&
2888          disableAllBaseDns(ctx, uData) && !argParser.advancedArg.isPresent())
2889      {
2890        try
2891        {
2892          uData.setDisableReplicationServer(askConfirmation(
2893              INFO_REPLICATION_DISABLE_ALL_SUFFIXES_DISABLE_REPLICATION_SERVER.get(getHostPort(ctx), repPort), true,
2894              logger));
2895        }
2896        catch (ClientException ce)
2897        {
2898          errPrintln(ce.getMessageObject());
2899          cancelled = true;
2900        }
2901      }
2902    }
2903
2904    if (!cancelled)
2905    {
2906      // Ask for confirmation to disable if not already done.
2907      boolean disableADS = false;
2908      boolean disableSchema = false;
2909      for (String dn : uData.getBaseDNs())
2910      {
2911        if (areDnsEqual(ADSContext.getAdministrationSuffixDN(), dn))
2912        {
2913          disableADS = true;
2914        }
2915        else if (areDnsEqual(Constants.SCHEMA_DN, dn))
2916        {
2917          disableSchema = true;
2918        }
2919      }
2920      if (disableADS)
2921      {
2922        println();
2923        LocalizableMessage msg = INFO_REPLICATION_CONFIRM_DISABLE_ADS.get(ADSContext.getAdministrationSuffixDN());
2924        cancelled = !askConfirmation(msg, true);
2925        println();
2926      }
2927      if (disableSchema)
2928      {
2929        println();
2930        LocalizableMessage msg = INFO_REPLICATION_CONFIRM_DISABLE_SCHEMA.get();
2931        cancelled = !askConfirmation(msg, true);
2932        println();
2933      }
2934      if (!disableSchema && !disableADS)
2935      {
2936        println();
2937        if (!uData.disableAll() && !uData.getBaseDNs().isEmpty())
2938        {
2939          cancelled = !askConfirmation(INFO_REPLICATION_CONFIRM_DISABLE_GENERIC.get(), true);
2940        }
2941        println();
2942      }
2943    }
2944
2945    close(ctx);
2946
2947    return !cancelled;
2948  }
2949
2950  /**
2951   * Updates the contents of the provided InitializeAllReplicationUserData
2952   * object with the information provided in the command-line.  If some
2953   * information is missing, ask the user to provide valid data.
2954   * We assume that if this method is called we are in interactive mode.
2955   * @param uData the object to be updated.
2956   * @return <CODE>true</CODE> if the object was successfully updated and
2957   * <CODE>false</CODE> if the user cancelled the operation.
2958   */
2959  private boolean promptIfRequired(InitializeAllReplicationUserData uData)
2960  {
2961    InitialLdapContext ctx = null;
2962    try
2963    {
2964      ctx = getInitialLdapContext(uData);
2965      if (ctx == null)
2966      {
2967        return false;
2968      }
2969
2970      LinkedList<String> suffixes = argParser.getBaseDNs();
2971      checkSuffixesForInitializeReplication(suffixes, ctx, true);
2972      if (suffixes.isEmpty())
2973      {
2974        return false;
2975      }
2976      uData.setBaseDNs(suffixes);
2977
2978      // Ask for confirmation to initialize.
2979      println();
2980      if (!askConfirmation(getPrompt(uData, ctx), true))
2981      {
2982        return false;
2983      }
2984      println();
2985      return true;
2986    }
2987    finally
2988    {
2989      close(ctx);
2990    }
2991  }
2992
2993  private LocalizableMessage getPrompt(InitializeAllReplicationUserData uData, InitialLdapContext ctx)
2994  {
2995    String hostPortSource = getHostPort(ctx);
2996    if (initializeADS(uData.getBaseDNs()))
2997    {
2998      return INFO_REPLICATION_CONFIRM_INITIALIZE_ALL_ADS.get(ADSContext.getAdministrationSuffixDN(), hostPortSource);
2999    }
3000    return INFO_REPLICATION_CONFIRM_INITIALIZE_ALL_GENERIC.get(hostPortSource);
3001  }
3002
3003  private boolean askConfirmation(final LocalizableMessage msg, final boolean defaultValue)
3004  {
3005    try
3006    {
3007      return askConfirmation(msg, defaultValue, logger);
3008    }
3009    catch (ClientException ce)
3010    {
3011      errPrintln(ce.getMessageObject());
3012      return false;
3013    }
3014  }
3015
3016  /**
3017   * Updates the contents of the provided user data
3018   * object with the information provided in the command-line.
3019   * If some information is missing, ask the user to provide valid data.
3020   * We assume that if this method is called we are in interactive mode.
3021   * @param uData the object to be updated.
3022   * @return <CODE>true</CODE> if the object was successfully updated and
3023   * <CODE>false</CODE> if the user cancelled the operation.
3024   */
3025  private boolean promptIfRequiredForPreOrPost(MonoServerReplicationUserData uData)
3026  {
3027    InitialLdapContext ctx = null;
3028    try
3029    {
3030      ctx = getInitialLdapContext(uData);
3031      if (ctx == null)
3032      {
3033        return false;
3034      }
3035      LinkedList<String> suffixes = argParser.getBaseDNs();
3036      checkSuffixesForInitializeReplication(suffixes, ctx, true);
3037      uData.setBaseDNs(suffixes);
3038      return !suffixes.isEmpty();
3039    }
3040    finally
3041    {
3042      close(ctx);
3043    }
3044  }
3045
3046  private InitialLdapContext getInitialLdapContext(MonoServerReplicationUserData uData)
3047  {
3048    // Try to connect to the server.
3049    while (true)
3050    {
3051      try
3052      {
3053        if (uData instanceof InitializeAllReplicationUserData)
3054        {
3055          sourceServerCI.setHeadingMessage(INFO_INITIALIZE_SOURCE_CONNECTION_PARAMETERS.get());
3056        }
3057        sourceServerCI.run();
3058
3059        InitialLdapContext ctx = createInitialLdapContextInteracting(sourceServerCI);
3060        if (ctx != null)
3061        {
3062          uData.setHostName(sourceServerCI.getHostName());
3063          uData.setPort(sourceServerCI.getPortNumber());
3064          uData.setAdminUid(sourceServerCI.getAdministratorUID());
3065          uData.setAdminPwd(sourceServerCI.getBindPassword());
3066          if (uData instanceof StatusReplicationUserData)
3067          {
3068            ((StatusReplicationUserData) uData).setScriptFriendly(argParser.isScriptFriendly());
3069          }
3070        }
3071        return ctx;
3072      }
3073      catch (ClientException ce)
3074      {
3075        logger.warn(LocalizableMessage.raw("Client exception " + ce));
3076        errPrintln();
3077        errPrintln(ce.getMessageObject());
3078        errPrintln();
3079        sourceServerCI.resetConnectionArguments();
3080      }
3081      catch (ArgumentException ae)
3082      {
3083        logger.warn(LocalizableMessage.raw("Argument exception " + ae));
3084        argParser.displayMessageAndUsageReference(getErrStream(), ae.getMessageObject());
3085        return null;
3086      }
3087    }
3088  }
3089
3090  /**
3091   * Updates the contents of the provided StatusReplicationUserData object
3092   * with the information provided in the command-line.  If some information
3093   * is missing, ask the user to provide valid data.
3094   * We assume that if this method is called we are in interactive mode.
3095   * @param uData the object to be updated.
3096   * @return <CODE>true</CODE> if the object was successfully updated and
3097   * <CODE>false</CODE> if the user cancelled the operation.
3098   * @throws ReplicationCliException if a critical error occurs reading the
3099   * ADS.
3100   */
3101  private boolean promptIfRequired(StatusReplicationUserData uData)
3102  throws ReplicationCliException
3103  {
3104    InitialLdapContext ctx = null;
3105    try
3106    {
3107      ctx = getInitialLdapContext(uData);
3108      if (ctx == null)
3109      {
3110        return false;
3111      }
3112
3113      // If the server contains an ADS, try to load it and only load it: if
3114      // there are issues with the ADS they will be encountered in the
3115      // statusReplication(StatusReplicationUserData) method. Here we have
3116      // to load the ADS to ask the user to accept the certificates and
3117      // eventually admin authentication data.
3118      AtomicReference<InitialLdapContext> aux = new AtomicReference<>(ctx);
3119      boolean cancelled = !loadADSAndAcceptCertificates(sourceServerCI, aux, uData, false);
3120      ctx = aux.get();
3121      if (cancelled)
3122      {
3123        return false;
3124      }
3125
3126      if (!cancelled)
3127      {
3128        uData.setBaseDNs(argParser.getBaseDNs());
3129      }
3130      return !cancelled;
3131    }
3132    finally
3133    {
3134      close(ctx);
3135    }
3136  }
3137
3138  /**
3139   * Updates the contents of the provided InitializeReplicationUserData object
3140   * with the information provided in the command-line.  If some information
3141   * is missing, ask the user to provide valid data.
3142   * We assume that if this method is called we are in interactive mode.
3143   * @param uData the object to be updated.
3144   * @param serversOperations Additional processing for the command
3145   * @return <CODE>true</CODE> if the object was successfully updated and
3146   * <CODE>false</CODE> if the user cancelled the operation.
3147   */
3148  private boolean promptIfRequired(SourceDestinationServerUserData uData,
3149      OperationBetweenSourceAndDestinationServers serversOperations)
3150  {
3151    boolean cancelled = false;
3152
3153    String adminPwd = argParser.getBindPasswordAdmin();
3154    String adminUid = argParser.getAdministratorUID();
3155
3156    String hostSource = argParser.getHostNameSource();
3157    int portSource = argParser.getPortSource();
3158
3159    Map<String, String> pwdFile = null;
3160    if (argParser.getSecureArgsList().bindPasswordFileArg.isPresent())
3161    {
3162      pwdFile = argParser.getSecureArgsList().bindPasswordFileArg.getNameToValueMap();
3163    }
3164
3165    /*
3166     * Use a copy of the argument properties since the map might be cleared
3167     * in initializeGlobalArguments.
3168     */
3169    sourceServerCI.initializeGlobalArguments(hostSource, portSource, adminUid, null, adminPwd,
3170        pwdFile == null ? null : new LinkedHashMap<String, String>(pwdFile));
3171    /* Try to connect to the source server. */
3172    InitialLdapContext ctxSource = null;
3173
3174    while (ctxSource == null && !cancelled)
3175    {
3176      try
3177      {
3178        sourceServerCI.setHeadingMessage(INFO_INITIALIZE_SOURCE_CONNECTION_PARAMETERS.get());
3179        sourceServerCI.run();
3180        hostSource = sourceServerCI.getHostName();
3181        portSource = sourceServerCI.getPortNumber();
3182        adminUid = sourceServerCI.getAdministratorUID();
3183        adminPwd = sourceServerCI.getBindPassword();
3184
3185        ctxSource = createInitialLdapContextInteracting(sourceServerCI);
3186
3187        if (ctxSource == null)
3188        {
3189          cancelled = true;
3190        }
3191      }
3192      catch (ClientException ce)
3193      {
3194        logger.warn(LocalizableMessage.raw("Client exception "+ce));
3195        errPrintln();
3196        errPrintln(ce.getMessageObject());
3197        errPrintln();
3198        sourceServerCI.resetConnectionArguments();
3199      }
3200      catch (ArgumentException ae)
3201      {
3202        logger.warn(LocalizableMessage.raw("Argument exception "+ae));
3203        argParser.displayMessageAndUsageReference(getErrStream(), ae.getMessageObject());
3204        cancelled = true;
3205      }
3206    }
3207    if (!cancelled)
3208    {
3209      uData.setHostNameSource(hostSource);
3210      uData.setPortSource(portSource);
3211      uData.setAdminUid(adminUid);
3212      uData.setAdminPwd(adminPwd);
3213    }
3214
3215    firstServerCommandBuilder = new CommandBuilder(null, null);
3216    if (mustPrintCommandBuilder())
3217    {
3218      firstServerCommandBuilder.append(sourceServerCI.getCommandBuilder());
3219    }
3220
3221    /* Prompt for destination server credentials */
3222    String hostDestination = argParser.getHostNameDestination();
3223    int portDestination = argParser.getPortDestination();
3224
3225    /*
3226     * Use a copy of the argument properties since the map might be cleared
3227     * in initializeGlobalArguments.
3228     */
3229    LDAPConnectionConsoleInteraction destinationServerCI = new LDAPConnectionConsoleInteraction(this,
3230        argParser.getSecureArgsList());
3231    destinationServerCI.initializeGlobalArguments(hostDestination, portDestination, adminUid, null, adminPwd,
3232        pwdFile == null ? null : new LinkedHashMap<String, String>(pwdFile));
3233    /* Try to connect to the destination server. */
3234    InitialLdapContext ctxDestination = null;
3235
3236    destinationServerCI.resetHeadingDisplayed();
3237    while (ctxDestination == null && !cancelled)
3238    {
3239      try
3240      {
3241        destinationServerCI.setHeadingMessage(INFO_INITIALIZE_DESTINATION_CONNECTION_PARAMETERS.get());
3242        destinationServerCI.run();
3243        hostDestination = destinationServerCI.getHostName();
3244        portDestination = destinationServerCI.getPortNumber();
3245
3246        boolean error = false;
3247        if (hostSource.equalsIgnoreCase(hostDestination)
3248            && portSource == portDestination)
3249        {
3250          portDestination = -1;
3251          errPrintln();
3252          errPrintln(ERR_SOURCE_DESTINATION_INITIALIZE_SAME_SERVER_PORT.get(hostSource, portSource));
3253          errPrintln();
3254          error = true;
3255        }
3256
3257        if (!error)
3258        {
3259          ctxDestination = createInitialLdapContextInteracting(destinationServerCI, true);
3260
3261          if (ctxDestination == null)
3262          {
3263            cancelled = true;
3264          }
3265        }
3266      }
3267      catch (ClientException ce)
3268      {
3269        logger.warn(LocalizableMessage.raw("Client exception "+ce));
3270        errPrintln();
3271        errPrintln(ce.getMessageObject());
3272        errPrintln();
3273        destinationServerCI.resetConnectionArguments();
3274      }
3275      catch (ArgumentException ae)
3276      {
3277        logger.warn(LocalizableMessage.raw("Argument exception "+ae));
3278        argParser.displayMessageAndUsageReference(getErrStream(), ae.getMessageObject());
3279        cancelled = true;
3280      }
3281    }
3282    if (!cancelled)
3283    {
3284      uData.setHostNameDestination(hostDestination);
3285      uData.setPortDestination(portDestination);
3286    }
3287
3288    if (!cancelled)
3289    {
3290      LinkedList<String> suffixes = argParser.getBaseDNs();
3291      cancelled = serversOperations.continueAfterUserInput(suffixes, ctxSource, ctxDestination, true);
3292      uData.setBaseDNs(suffixes);
3293    }
3294
3295    if (!cancelled)
3296    {
3297      println();
3298      cancelled = serversOperations.confirmOperation(uData, ctxSource, ctxDestination, true);
3299      println();
3300    }
3301
3302    close(ctxSource, ctxDestination);
3303    return !cancelled;
3304  }
3305
3306  private LocalizableMessage getInitializeReplicationPrompt(SourceDestinationServerUserData uData,
3307      InitialLdapContext ctxSource, InitialLdapContext ctxDestination)
3308  {
3309    String hostPortSource = getHostPort(ctxSource);
3310    String hostPortDestination = getHostPort(ctxDestination);
3311    if (initializeADS(uData.getBaseDNs()))
3312    {
3313      final String adminSuffixDN = ADSContext.getAdministrationSuffixDN();
3314      return INFO_REPLICATION_CONFIRM_INITIALIZE_ADS.get(adminSuffixDN, hostPortDestination, hostPortSource);
3315    }
3316    return INFO_REPLICATION_CONFIRM_INITIALIZE_GENERIC.get(hostPortDestination, hostPortSource);
3317  }
3318
3319  private boolean initializeADS(List<String> baseDNs)
3320  {
3321    for (String dn : baseDNs)
3322    {
3323      if (areDnsEqual(ADSContext.getAdministrationSuffixDN(), dn))
3324      {
3325        return true;
3326      }
3327    }
3328    return false;
3329  }
3330
3331  /**
3332   * Returns the trust manager to be used by this application.
3333   * @param ci the LDAP connection to the server
3334   * @return the trust manager to be used by this application.
3335   */
3336  private ApplicationTrustManager getTrustManager(LDAPConnectionConsoleInteraction ci)
3337  {
3338    return isInteractive() ? ci.getTrustManager() : argParser.getTrustManager();
3339  }
3340
3341  /**
3342   * Initializes the contents of the provided enable replication user data
3343   * object with what was provided in the command-line without prompting to the
3344   * user.
3345   * @param uData the enable replication user data object to be initialized.
3346   */
3347  private void initializeWithArgParser(EnableReplicationUserData uData)
3348  {
3349    initialize(uData);
3350
3351    final String adminDN = getAdministratorDN(uData.getAdminUid());
3352    final String adminPwd = uData.getAdminPwd();
3353    setConnectionDetails(uData.getServer1(), argParser.server1, adminDN, adminPwd);
3354    setConnectionDetails(uData.getServer2(), argParser.server2, adminDN, adminPwd);
3355
3356    uData.setReplicateSchema(!argParser.noSchemaReplication());
3357
3358    setReplicationDetails(uData.getServer1(), argParser.server1);
3359    setReplicationDetails(uData.getServer2(), argParser.server2);
3360  }
3361
3362  private void setConnectionDetails(
3363      EnableReplicationServerData server, ServerArgs args, String adminDN, String adminPwd)
3364  {
3365    server.setHostName(getValueOrDefault(args.hostNameArg));
3366    server.setPort(getValueOrDefault(args.portArg));
3367
3368    String pwd = args.getBindPassword();
3369    if (pwd == null)
3370    {
3371      server.setBindDn(adminDN);
3372      server.setPwd(adminPwd);
3373    }
3374    else
3375    {
3376      // Best-effort: try to use admin, if it does not work, use bind DN.
3377      try
3378      {
3379        InitialLdapContext ctx = createAdministrativeContext(server.getHostName(), server.getPort(),
3380            useSSL, useStartTLS, adminDN, adminPwd, getConnectTimeout(), getTrustManager(sourceServerCI));
3381        server.setBindDn(adminDN);
3382        server.setPwd(adminPwd);
3383        ctx.close();
3384      }
3385      catch (Throwable t)
3386      {
3387        server.setBindDn(getValueOrDefault(args.bindDnArg));
3388        server.setPwd(pwd);
3389      }
3390    }
3391  }
3392
3393  private void setReplicationDetails(EnableReplicationServerData server, ServerArgs args)
3394  {
3395    server.setSecureReplication(args.secureReplicationArg.isPresent());
3396    server.setConfigureReplicationDomain(args.configureReplicationDomain());
3397    server.setConfigureReplicationServer(args.configureReplicationServer());
3398    if (server.configureReplicationServer())
3399    {
3400      server.setReplicationPort(getValueOrDefault(args.replicationPortArg));
3401    }
3402  }
3403
3404  /**
3405   * Initializes the contents of the provided initialize replication user data
3406   * object with what was provided in the command-line without prompting to the
3407   * user.
3408   * @param uData the initialize replication user data object to be initialized.
3409   */
3410  private void initializeWithArgParser(SourceDestinationServerUserData uData)
3411  {
3412    initialize(uData);
3413
3414    uData.setHostNameSource(argParser.getHostNameSourceOrDefault());
3415    uData.setPortSource(argParser.getPortSourceOrDefault());
3416    uData.setHostNameDestination(argParser.getHostNameDestinationOrDefault());
3417    uData.setPortDestination(argParser.getPortDestinationOrDefault());
3418  }
3419
3420  /**
3421   * Initializes the contents of the provided disable replication user data
3422   * object with what was provided in the command-line without prompting to the
3423   * user.
3424   * @param uData the disable replication user data object to be initialized.
3425   */
3426  private void initializeWithArgParser(DisableReplicationUserData uData)
3427  {
3428    uData.setBaseDNs(new LinkedList<String>(argParser.getBaseDNs()));
3429    String adminUid = argParser.getAdministratorUID();
3430    String bindDn = argParser.getBindDNToDisable();
3431    if (bindDn == null && adminUid == null)
3432    {
3433      adminUid = argParser.getAdministratorUIDOrDefault();
3434      bindDn = getAdministratorDN(adminUid);
3435    }
3436    uData.setAdminUid(adminUid);
3437    uData.setBindDn(bindDn);
3438    uData.setAdminPwd(argParser.getBindPasswordAdmin());
3439
3440    uData.setHostName(argParser.getHostNameToDisableOrDefault());
3441    uData.setPort(argParser.getPortToDisableOrDefault());
3442
3443    uData.setDisableAll(argParser.disableAllArg.isPresent());
3444    uData.setDisableReplicationServer(argParser.disableReplicationServerArg.isPresent());
3445  }
3446
3447  /**
3448   * Initializes the contents of the provided user data object with what was
3449   * provided in the command-line without prompting to the user.
3450   * @param uData the user data object to be initialized.
3451   */
3452  private void initializeWithArgParser(MonoServerReplicationUserData uData)
3453  {
3454    initialize(uData);
3455
3456    uData.setHostName(argParser.getHostNameToInitializeAllOrDefault());
3457    uData.setPort(argParser.getPortToInitializeAllOrDefault());
3458  }
3459
3460  /**
3461   * Initializes the contents of the provided status replication user data
3462   * object with what was provided in the command-line without prompting to the
3463   * user.
3464   * @param uData the status replication user data object to be initialized.
3465   */
3466  private void initializeWithArgParser(StatusReplicationUserData uData)
3467  {
3468    initialize(uData);
3469
3470    uData.setHostName(argParser.getHostNameToStatusOrDefault());
3471    uData.setPort(argParser.getPortToStatusOrDefault());
3472    uData.setScriptFriendly(argParser.isScriptFriendly());
3473  }
3474
3475  private void initialize(ReplicationUserData uData)
3476  {
3477    uData.setBaseDNs(new LinkedList<String>(argParser.getBaseDNs()));
3478    uData.setAdminUid(argParser.getAdministratorUIDOrDefault());
3479    uData.setAdminPwd(argParser.getBindPasswordAdmin());
3480  }
3481
3482  /**
3483   * Tells whether the server to which the LdapContext is connected has a
3484   * replication port or not.
3485   * @param ctx the InitialLdapContext to be used.
3486   * @return <CODE>true</CODE> if the replication port for the server could
3487   * be found and <CODE>false</CODE> otherwise.
3488   */
3489  private boolean hasReplicationPort(InitialLdapContext ctx)
3490  {
3491    return getReplicationPort(ctx) != -1;
3492  }
3493
3494  /**
3495   * Returns the replication port of server to which the LdapContext is
3496   * connected and -1 if the replication port could not be found.
3497   * @param ctx the InitialLdapContext to be used.
3498   * @return the replication port of server to which the LdapContext is
3499   * connected and -1 if the replication port could not be found.
3500   */
3501  private int getReplicationPort(InitialLdapContext ctx)
3502  {
3503    int replicationPort = -1;
3504    try
3505    {
3506      ManagementContext mCtx = LDAPManagementContext.createFromContext(
3507          JNDIDirContextAdaptor.adapt(ctx));
3508      RootCfgClient root = mCtx.getRootConfiguration();
3509
3510      ReplicationSynchronizationProviderCfgClient sync =
3511          (ReplicationSynchronizationProviderCfgClient)
3512          root.getSynchronizationProvider("Multimaster Synchronization");
3513      if (sync.hasReplicationServer())
3514      {
3515        ReplicationServerCfgClient replicationServer =
3516          sync.getReplicationServer();
3517        replicationPort = replicationServer.getReplicationPort();
3518      }
3519    }
3520    catch (Throwable t)
3521    {
3522      logger.warn(LocalizableMessage.raw("Unexpected error retrieving the replication port: " + t, t));
3523    }
3524    return replicationPort;
3525  }
3526
3527  /**
3528   * Loads the ADS with the provided context.  If there are certificates to
3529   * be accepted we prompt them to the user.  If there are errors loading the
3530   * servers we display them to the user and we ask for confirmation.  If the
3531   * provided ctx is not using Global Administrator credentials, we prompt the
3532   * user to provide them and update the provide ReplicationUserData
3533   * accordingly.
3534   *
3535   * @param ci the LDAP connection to the server
3536   * @param ctx the Ldap context to be used in an array: note the context
3537   * may be modified with the new credentials provided by the user.
3538   * @param uData the ReplicationUserData to be updated.
3539   * @param isFirstOrSourceServer whether this is the first server in the
3540   * enable replication subcommand or the source server in the initialize server
3541   * subcommand.
3542   * @throws ReplicationCliException if a critical error occurred.
3543   * @return <CODE>true</CODE> if everything went fine and the user accepted
3544   * all the certificates and confirmed everything.  Returns <CODE>false</CODE>
3545   * if the user did not accept a certificate or any of the confirmation
3546   * messages.
3547   */
3548  private boolean loadADSAndAcceptCertificates(LDAPConnectionConsoleInteraction ci,
3549      AtomicReference<InitialLdapContext> ctx, ReplicationUserData uData, boolean isFirstOrSourceServer)
3550  throws ReplicationCliException
3551  {
3552    boolean cancelled = false;
3553    boolean triedWithUserProvidedAdmin = false;
3554    final InitialLdapContext ctx1 = ctx.get();
3555    String host = getHostName(ctx1);
3556    int port = getPort(ctx1);
3557    boolean isSSL = isSSL(ctx1);
3558    boolean isStartTLS = isStartTLS(ctx1);
3559    if (getTrustManager(ci) == null)
3560    {
3561      // This is required when the user did  connect to the server using SSL or
3562      // Start TLS.  In this case LDAPConnectionConsoleInteraction.run does not
3563      // initialize the keystore and the trust manager is null.
3564      forceTrustManagerInitialization(ci);
3565    }
3566    try
3567    {
3568      ADSContext adsContext = new ADSContext(ctx1);
3569      if (adsContext.hasAdminData())
3570      {
3571        boolean reloadTopology = true;
3572        LinkedList<LocalizableMessage> exceptionMsgs = new LinkedList<>();
3573        while (reloadTopology && !cancelled)
3574        {
3575          // We must recreate the cache because the trust manager in the
3576          // LDAPConnectionConsoleInteraction object might have changed.
3577
3578          TopologyCache cache = new TopologyCache(adsContext,
3579              getTrustManager(ci), getConnectTimeout());
3580          cache.getFilter().setSearchMonitoringInformation(false);
3581          cache.getFilter().setSearchBaseDNInformation(false);
3582          cache.setPreferredConnections(getPreferredConnections(ctx1));
3583          cache.reloadTopology();
3584
3585          reloadTopology = false;
3586          exceptionMsgs.clear();
3587
3588          /* Analyze if we had any exception while loading servers.  For the
3589           * moment only throw the exception found if the user did not provide
3590           * the Administrator DN and this caused a problem authenticating in
3591           * one server or if there is a certificate problem.
3592           */
3593          Set<TopologyCacheException> exceptions = new HashSet<>();
3594          Set<ServerDescriptor> servers = cache.getServers();
3595          for (ServerDescriptor server : servers)
3596          {
3597            TopologyCacheException e = server.getLastException();
3598            if (e != null)
3599            {
3600              exceptions.add(e);
3601            }
3602          }
3603          /* Check the exceptions and see if we throw them or not. */
3604          boolean notGlobalAdministratorError = false;
3605          for (TopologyCacheException e : exceptions)
3606          {
3607            if (notGlobalAdministratorError)
3608            {
3609              break;
3610            }
3611
3612            switch (e.getType())
3613            {
3614              case NOT_GLOBAL_ADMINISTRATOR:
3615                notGlobalAdministratorError = true;
3616                boolean connected = false;
3617
3618                String adminUid = uData.getAdminUid();
3619                String adminPwd = uData.getAdminPwd();
3620
3621                boolean errorDisplayed = false;
3622                while (!connected)
3623                {
3624                  if (!triedWithUserProvidedAdmin && adminPwd == null)
3625                  {
3626                    adminUid = argParser.getAdministratorUIDOrDefault();
3627                    adminPwd = argParser.getBindPasswordAdmin();
3628                    triedWithUserProvidedAdmin = true;
3629                  }
3630                  if (adminPwd == null)
3631                  {
3632                    if (!errorDisplayed)
3633                    {
3634                      println();
3635                      println(
3636                          INFO_NOT_GLOBAL_ADMINISTRATOR_PROVIDED.get());
3637                      errorDisplayed = true;
3638                    }
3639                    adminUid = askForAdministratorUID(
3640                        getDefaultValue(argParser.getAdminUidArg()), logger);
3641                    println();
3642                    adminPwd = askForAdministratorPwd(logger);
3643                    println();
3644                  }
3645                  close(ctx1);
3646                  try
3647                  {
3648                    final InitialLdapContext ctx2 = createAdministrativeContext(host, port, isSSL,
3649                        isStartTLS, getAdministratorDN(adminUid),
3650                        adminPwd, getConnectTimeout(), getTrustManager(ci));
3651                    ctx.set(ctx2);
3652                    adsContext = new ADSContext(ctx2);
3653                    cache = new TopologyCache(adsContext, getTrustManager(ci),
3654                        getConnectTimeout());
3655                    cache.getFilter().setSearchMonitoringInformation(false);
3656                    cache.getFilter().setSearchBaseDNInformation(false);
3657                    cache.setPreferredConnections(getPreferredConnections(ctx2));
3658                    connected = true;
3659                  }
3660                  catch (Throwable t)
3661                  {
3662                    errPrintln();
3663                    errPrintln(
3664                        ERR_ERROR_CONNECTING_TO_SERVER_PROMPT_AGAIN.get(
3665                          getServerRepresentation(host, port), t.getMessage()));
3666                    logger.warn(LocalizableMessage.raw("Complete error stack:", t));
3667                    errPrintln();
3668                  }
3669                }
3670                uData.setAdminUid(adminUid);
3671                uData.setAdminPwd(adminPwd);
3672                if (uData instanceof EnableReplicationUserData)
3673                {
3674                  EnableReplicationUserData enableData = (EnableReplicationUserData) uData;
3675                  EnableReplicationServerData server =
3676                      isFirstOrSourceServer ? enableData.getServer1() : enableData.getServer2();
3677                  server.setBindDn(getAdministratorDN(adminUid));
3678                  server.setPwd(adminPwd);
3679                }
3680                reloadTopology = true;
3681              break;
3682            case GENERIC_CREATING_CONNECTION:
3683              if (isCertificateException(e.getCause()))
3684              {
3685                reloadTopology = true;
3686                cancelled = !ci.promptForCertificateConfirmation(e.getCause(),
3687                    e.getTrustManager(), e.getLdapUrl(), logger);
3688              }
3689              else
3690              {
3691                exceptionMsgs.add(getMessage(e));
3692              }
3693              break;
3694            default:
3695              exceptionMsgs.add(getMessage(e));
3696            }
3697          }
3698        }
3699        if (!exceptionMsgs.isEmpty() && !cancelled)
3700        {
3701          if (uData instanceof StatusReplicationUserData)
3702          {
3703            errPrintln(
3704                ERR_REPLICATION_STATUS_READING_REGISTERED_SERVERS.get(
3705                    getMessageFromCollection(exceptionMsgs,
3706                        Constants.LINE_SEPARATOR)));
3707            errPrintln();
3708          }
3709          else
3710          {
3711            LocalizableMessage msg = ERR_REPLICATION_READING_REGISTERED_SERVERS_CONFIRM_UPDATE_REMOTE.get(
3712                getMessageFromCollection(exceptionMsgs, Constants.LINE_SEPARATOR));
3713            cancelled = !askConfirmation(msg, true);
3714          }
3715        }
3716      }
3717    }
3718    catch (ADSContextException ace)
3719    {
3720      logger.error(LocalizableMessage.raw("Complete error stack:"), ace);
3721      throw new ReplicationCliException(
3722          ERR_REPLICATION_READING_ADS.get(ace.getMessage()),
3723          ERROR_READING_ADS, ace);
3724    }
3725    catch (TopologyCacheException tce)
3726    {
3727      logger.error(LocalizableMessage.raw("Complete error stack:"), tce);
3728      throw new ReplicationCliException(
3729          ERR_REPLICATION_READING_ADS.get(tce.getMessage()),
3730          ERROR_READING_TOPOLOGY_CACHE, tce);
3731    }
3732    return !cancelled;
3733  }
3734
3735  /**
3736   * Tells whether there is a Global Administrator defined in the server
3737   * to which the InitialLdapContext is connected.
3738   * @param ctx the InitialLdapContext.
3739   * @return <CODE>true</CODE> if we could find an administrator and
3740   * <CODE>false</CODE> otherwise.
3741   */
3742  private boolean hasAdministrator(InitialLdapContext ctx)
3743  {
3744    try
3745    {
3746      ADSContext adsContext = new ADSContext(ctx);
3747      if (adsContext.hasAdminData())
3748      {
3749        Set<?> administrators = adsContext.readAdministratorRegistry();
3750        return !administrators.isEmpty();
3751      }
3752    }
3753    catch (Throwable t)
3754    {
3755      logger.warn(LocalizableMessage.raw(
3756          "Unexpected error retrieving the ADS data: "+t, t));
3757    }
3758    return false;
3759  }
3760
3761  /**
3762   * Tells whether there is a Global Administrator corresponding to the provided
3763   * ReplicationUserData defined in the server to which the InitialLdapContext
3764   * is connected.
3765   * @param ctx the InitialLdapContext.
3766   * @param uData the user data
3767   * @return <CODE>true</CODE> if we could find an administrator and
3768   * <CODE>false</CODE> otherwise.
3769   */
3770  private boolean hasAdministrator(InitialLdapContext ctx,
3771      ReplicationUserData uData)
3772  {
3773    String adminUid = uData.getAdminUid();
3774    try
3775    {
3776      ADSContext adsContext = new ADSContext(ctx);
3777      Set<Map<AdministratorProperty, Object>> administrators =
3778        adsContext.readAdministratorRegistry();
3779      for (Map<AdministratorProperty, Object> admin : administrators)
3780      {
3781        String uid = (String)admin.get(AdministratorProperty.UID);
3782        // If the administrator UID is null it means that we are just
3783        // checking for the existence of an administrator
3784        if (uid != null && (uid.equalsIgnoreCase(adminUid) || adminUid == null))
3785        {
3786          return true;
3787        }
3788      }
3789    }
3790    catch (Throwable t)
3791    {
3792      logger.warn(LocalizableMessage.raw(
3793          "Unexpected error retrieving the ADS data: "+t, t));
3794    }
3795    return false;
3796  }
3797
3798  /** Helper type for the {@link #getCommonSuffixes(InitialLdapContext, InitialLdapContext, SuffixRelationType)}. */
3799  private enum SuffixRelationType
3800  {
3801    NOT_REPLICATED, FULLY_REPLICATED, REPLICATED, NOT_FULLY_REPLICATED, ALL
3802  }
3803
3804  /**
3805   * Returns a Collection containing a list of suffixes that are defined in
3806   * two servers at the same time (depending on the value of the argument
3807   * replicated this list contains only the suffixes that are replicated
3808   * between the servers or the list of suffixes that are not replicated
3809   * between the servers).
3810   * @param ctx1 the connection to the first server.
3811   * @param ctx2 the connection to the second server.
3812   * @param type whether to return a list with the suffixes that are
3813   * replicated, fully replicated (replicas have exactly the same list of
3814   * replication servers), not replicated or all the common suffixes.
3815   * @return a Collection containing a list of suffixes that are replicated
3816   * (or those that can be replicated) in two servers.
3817   */
3818  private List<String> getCommonSuffixes(InitialLdapContext ctx1, InitialLdapContext ctx2, SuffixRelationType type)
3819  {
3820    LinkedList<String> suffixes = new LinkedList<>();
3821    try
3822    {
3823      TopologyCacheFilter filter = new TopologyCacheFilter();
3824      filter.setSearchMonitoringInformation(false);
3825      ServerDescriptor server1 = ServerDescriptor.createStandalone(ctx1, filter);
3826      ServerDescriptor server2 = ServerDescriptor.createStandalone(ctx2, filter);
3827
3828      for (ReplicaDescriptor rep1 : server1.getReplicas())
3829      {
3830        for (ReplicaDescriptor rep2 : server2.getReplicas())
3831        {
3832          String rep1SuffixDN = rep1.getSuffix().getDN();
3833          String rep2SuffixDN = rep2.getSuffix().getDN();
3834          boolean areDnsEqual = areDnsEqual(rep1SuffixDN, rep2SuffixDN);
3835          switch (type)
3836          {
3837          case NOT_REPLICATED:
3838            if (!areReplicated(rep1, rep2) && areDnsEqual)
3839            {
3840              suffixes.add(rep1SuffixDN);
3841            }
3842            break;
3843          case FULLY_REPLICATED:
3844            if (areFullyReplicated(rep1, rep2))
3845            {
3846              suffixes.add(rep1SuffixDN);
3847            }
3848            break;
3849          case REPLICATED:
3850            if (areReplicated(rep1, rep2))
3851            {
3852              suffixes.add(rep1SuffixDN);
3853            }
3854            break;
3855          case NOT_FULLY_REPLICATED:
3856            if (!areFullyReplicated(rep1, rep2) && areDnsEqual)
3857            {
3858              suffixes.add(rep1SuffixDN);
3859            }
3860            break;
3861          case ALL:
3862            if (areDnsEqual)
3863            {
3864              suffixes.add(rep1SuffixDN);
3865            }
3866            break;
3867            default:
3868              throw new IllegalStateException("Unknown type: "+type);
3869          }
3870        }
3871      }
3872    }
3873    catch (Throwable t)
3874    {
3875      logger.warn(LocalizableMessage.raw(
3876          "Unexpected error retrieving the server configuration: "+t, t));
3877    }
3878    return suffixes;
3879  }
3880
3881  /**
3882   * Tells whether the two provided replicas are fully replicated or not.  The
3883   * code in fact checks that both replicas have the same DN that they are
3884   * replicated if both servers are replication servers and that both replicas
3885   * make reference to the other replication server.
3886   * @param rep1 the first replica.
3887   * @param rep2 the second replica.
3888   * @return <CODE>true</CODE> if we can assure that the two replicas are
3889   * replicated using the replication server and replication port information
3890   * and <CODE>false</CODE> otherwise.
3891   */
3892  private boolean areFullyReplicated(ReplicaDescriptor rep1,
3893      ReplicaDescriptor rep2)
3894  {
3895    if (areDnsEqual(rep1.getSuffix().getDN(), rep2.getSuffix().getDN()) &&
3896        rep1.isReplicated() && rep2.isReplicated() &&
3897        rep1.getServer().isReplicationServer() &&
3898        rep2.getServer().isReplicationServer())
3899    {
3900     Set<String> servers1 = rep1.getReplicationServers();
3901     Set<String> servers2 = rep2.getReplicationServers();
3902     String server1 = rep1.getServer().getReplicationServerHostPort();
3903     String server2 = rep2.getServer().getReplicationServerHostPort();
3904      return servers1.contains(server2) && servers2.contains(server1);
3905    }
3906    return false;
3907  }
3908
3909  /**
3910   * Tells whether the two provided replicas are replicated or not.  The
3911   * code in fact checks that both replicas have the same DN and that they
3912   * have at least one common replication server referenced.
3913   * @param rep1 the first replica.
3914   * @param rep2 the second replica.
3915   * @return <CODE>true</CODE> if we can assure that the two replicas are
3916   * replicated and <CODE>false</CODE> otherwise.
3917   */
3918  private boolean areReplicated(ReplicaDescriptor rep1, ReplicaDescriptor rep2)
3919  {
3920    if (areDnsEqual(rep1.getSuffix().getDN(), rep2.getSuffix().getDN()) &&
3921        rep1.isReplicated() && rep2.isReplicated())
3922    {
3923      Set<String> servers1 = rep1.getReplicationServers();
3924      Set<String> servers2 = rep2.getReplicationServers();
3925      servers1.retainAll(servers2);
3926      return !servers1.isEmpty();
3927    }
3928    return false;
3929  }
3930
3931  /**
3932   * Returns a Collection containing a list of replicas in a server.
3933   * @param ctx the connection to the server.
3934   * @return a Collection containing a list of replicas in a server.
3935   */
3936  private Collection<ReplicaDescriptor> getReplicas(InitialLdapContext ctx)
3937  {
3938    LinkedList<ReplicaDescriptor> suffixes = new LinkedList<>();
3939    TopologyCacheFilter filter = new TopologyCacheFilter();
3940    filter.setSearchMonitoringInformation(false);
3941    try
3942    {
3943      ServerDescriptor server = ServerDescriptor.createStandalone(ctx, filter);
3944      suffixes.addAll(server.getReplicas());
3945    }
3946    catch (Throwable t)
3947    {
3948      logger.warn(LocalizableMessage.raw(
3949          "Unexpected error retrieving the server configuration: "+t, t));
3950    }
3951    return suffixes;
3952  }
3953
3954  /**
3955   * Enables the replication between two servers using the parameters in the
3956   * provided EnableReplicationUserData.  This method does not prompt to the
3957   * user for information if something is missing.
3958   * @param uData the EnableReplicationUserData object.
3959   * @return ReplicationCliReturnCode.SUCCESSFUL if the operation was
3960   * successful and the replication could be enabled and an error code
3961   * otherwise.
3962   */
3963  private ReplicationCliReturnCode enableReplication(EnableReplicationUserData uData)
3964  {
3965    InitialLdapContext ctx1 = null;
3966    InitialLdapContext ctx2 = null;
3967    try
3968    {
3969      println();
3970      print(formatter.getFormattedWithPoints(INFO_REPLICATION_CONNECTING.get()));
3971
3972      LinkedList<LocalizableMessage> errorMessages = new LinkedList<>();
3973      ctx1 = createAdministrativeContext(uData, true, errorMessages);
3974      ctx2 = createAdministrativeContext(uData, false, errorMessages);
3975
3976      if (!errorMessages.isEmpty())
3977      {
3978        errPrintLn(errorMessages);
3979        return ERROR_CONNECTING;
3980      }
3981
3982      // This done is for the message informing that we are connecting.
3983      print(formatter.getFormattedDone());
3984      println();
3985
3986      if (!argParser.isInteractive())
3987      {
3988        checksForNonInteractiveMode(uData, ctx1, ctx2, errorMessages);
3989        if (!errorMessages.isEmpty())
3990        {
3991          errPrintLn(errorMessages);
3992          return ERROR_USER_DATA;
3993        }
3994      }
3995
3996      List<String> suffixes = uData.getBaseDNs();
3997      checkSuffixesForEnableReplication(suffixes, ctx1, ctx2, false, uData);
3998      if (suffixes.isEmpty())
3999      {
4000        // The error messages are already displayed in the method
4001        // checkSuffixesForEnableReplication.
4002        return REPLICATION_CANNOT_BE_ENABLED_ON_BASEDN;
4003      }
4004
4005      uData.setBaseDNs(suffixes);
4006      if (mustPrintCommandBuilder())
4007      {
4008        printNewCommandBuilder(ENABLE_REPLICATION_SUBCMD_NAME, uData);
4009      }
4010
4011      if (!isInteractive())
4012      {
4013        checkReplicationServerAlreadyConfigured(ctx1, uData.getServer1());
4014        checkReplicationServerAlreadyConfigured(ctx2, uData.getServer2());
4015      }
4016
4017      try
4018      {
4019        updateConfiguration(ctx1, ctx2, uData);
4020        printSuccessfullyEnabled(ctx1, ctx2);
4021        return SUCCESSFUL;
4022      }
4023      catch (ReplicationCliException rce)
4024      {
4025        errPrintln();
4026        errPrintln(getCriticalExceptionMessage(rce));
4027        logger.error(LocalizableMessage.raw("Complete error stack:"), rce);
4028        return rce.getErrorCode();
4029      }
4030    }
4031    finally
4032    {
4033      close(ctx1, ctx2);
4034    }
4035  }
4036
4037  private void checkReplicationServerAlreadyConfigured(InitialLdapContext ctx, EnableReplicationServerData server)
4038  {
4039    int repPort = getReplicationPort(ctx);
4040    if (!server.configureReplicationServer() && repPort > 0)
4041    {
4042      println(INFO_REPLICATION_SERVER_CONFIGURED_WARNING.get(getHostPort(ctx), repPort));
4043      println();
4044    }
4045  }
4046
4047  private void checksForNonInteractiveMode(EnableReplicationUserData uData,
4048      InitialLdapContext ctx1, InitialLdapContext ctx2, LinkedList<LocalizableMessage> errorMessages)
4049  {
4050    EnableReplicationServerData server1 = uData.getServer1();
4051    EnableReplicationServerData server2 = uData.getServer2();
4052    String host1 = server1.getHostName();
4053    String host2 = server2.getHostName();
4054
4055    int replPort1 = checkReplicationPort(ctx1, server1, errorMessages);
4056    int replPort2 = checkReplicationPort(ctx2, server2, errorMessages);
4057    if (replPort1 > 0 && replPort1 == replPort2 && host1.equalsIgnoreCase(host2))
4058    {
4059      errorMessages.add(ERR_REPLICATION_SAME_REPLICATION_PORT.get(replPort1, host1));
4060    }
4061
4062    if (argParser.skipReplicationPortCheck())
4063    {
4064      // This is something that we must do in any case... this test is
4065      // already included when we call SetupUtils.canUseAsPort
4066      checkAdminAndReplicationPortsAreDifferent(replPort1, server1, errorMessages);
4067      checkAdminAndReplicationPortsAreDifferent(replPort2, server2, errorMessages);
4068    }
4069  }
4070
4071  private int checkReplicationPort(
4072      InitialLdapContext ctx, EnableReplicationServerData server, LinkedList<LocalizableMessage> errorMessages)
4073  {
4074    int replPort = getReplicationPort(ctx);
4075    boolean hasReplicationPort = replPort > 0;
4076    if (replPort < 0 && server.configureReplicationServer())
4077    {
4078      replPort = server.getReplicationPort();
4079    }
4080    boolean checkReplicationPort = replPort > 0;
4081    if (!hasReplicationPort
4082        && checkReplicationPort
4083        && !argParser.skipReplicationPortCheck()
4084        && server.configureReplicationServer()
4085        && isLocalHost(server.getHostName())
4086        && !SetupUtils.canUseAsPort(replPort))
4087    {
4088      errorMessages.add(getCannotBindToPortError(replPort));
4089    }
4090    return replPort;
4091  }
4092
4093  private void checkAdminAndReplicationPortsAreDifferent(
4094      int replPort, EnableReplicationServerData server, LinkedList<LocalizableMessage> errorMessages)
4095  {
4096    if (replPort > 0 && replPort == server.getPort())
4097    {
4098      errorMessages.add(ERR_REPLICATION_PORT_AND_REPLICATION_PORT_EQUAL.get(server.getHostName(), replPort));
4099    }
4100  }
4101
4102  private void printSuccessfullyEnabled(InitialLdapContext ctx1, InitialLdapContext ctx2)
4103  {
4104    long time1 = getServerClock(ctx1);
4105    long time2 = getServerClock(ctx2);
4106    if (time1 != -1
4107        && time2 != -1
4108        && Math.abs(time1 - time2) > Installer.THRESHOLD_CLOCK_DIFFERENCE_WARNING * 60 * 1000)
4109    {
4110        println(INFO_WARNING_SERVERS_CLOCK_DIFFERENCE.get(getHostPort(ctx1), getHostPort(ctx2),
4111            Installer.THRESHOLD_CLOCK_DIFFERENCE_WARNING));
4112    }
4113    println();
4114    println(INFO_REPLICATION_POST_ENABLE_INFO.get("dsreplication", INITIALIZE_REPLICATION_SUBCMD_NAME));
4115    println();
4116  }
4117
4118  private void errPrintLn(LinkedList<LocalizableMessage> errorMessages)
4119  {
4120    for (LocalizableMessage msg : errorMessages)
4121    {
4122      errPrintln();
4123      errPrintln(msg);
4124    }
4125  }
4126
4127  private InitialLdapContext createAdministrativeContext(EnableReplicationUserData uData, boolean isFirstSetOfValues,
4128      LinkedList<LocalizableMessage> errorMessages)
4129  {
4130    EnableReplicationServerData server = isFirstSetOfValues ? uData.getServer1() : uData.getServer2();
4131    try
4132    {
4133      return createAdministrativeContext(
4134          server.getHostName(), server.getPort(), useSSL, useStartTLS, server.getBindDn(), server.getPwd(),
4135          getConnectTimeout(), getTrustManager(sourceServerCI));
4136    }
4137    catch (NamingException ne)
4138    {
4139      String hostPort = getServerRepresentation(server.getHostName(), server.getPort());
4140      errorMessages.add(getMessageForException(ne, hostPort));
4141      logger.error(LocalizableMessage.raw("Complete error stack:"), ne);
4142      return null;
4143    }
4144  }
4145
4146  /**
4147   * Disables the replication in the server for the provided suffixes using the
4148   * data in the DisableReplicationUserData object.  This method does not prompt
4149   * to the user for information if something is missing.
4150   * @param uData the DisableReplicationUserData object.
4151   * @return ReplicationCliReturnCode.SUCCESSFUL if the operation was
4152   * successful and an error code otherwise.
4153   */
4154  private ReplicationCliReturnCode disableReplication(DisableReplicationUserData uData)
4155  {
4156    print(formatter.getFormattedWithPoints(INFO_REPLICATION_CONNECTING.get()));
4157    String bindDn = uData.getAdminUid() != null
4158        ? getAdministratorDN(uData.getAdminUid())
4159        : uData.getBindDn();
4160
4161    InitialLdapContext ctx = createAdministrativeContext(uData, bindDn);
4162    if (ctx == null)
4163    {
4164      return ERROR_CONNECTING;
4165    }
4166
4167    try
4168    {
4169      // This done is for the message informing that we are connecting.
4170      print(formatter.getFormattedDone());
4171      println();
4172
4173      List<String> suffixes = uData.getBaseDNs();
4174      checkSuffixesForDisableReplication(suffixes, ctx, false, !uData.disableReplicationServer());
4175      if (suffixes.isEmpty() && !uData.disableReplicationServer() && !uData.disableAll())
4176      {
4177        return REPLICATION_CANNOT_BE_DISABLED_ON_BASEDN;
4178      }
4179      uData.setBaseDNs(suffixes);
4180
4181      if (!isInteractive())
4182      {
4183        boolean hasReplicationPort = hasReplicationPort(ctx);
4184        if (uData.disableAll() && hasReplicationPort)
4185        {
4186          uData.setDisableReplicationServer(true);
4187        }
4188        else if (uData.disableReplicationServer() && !hasReplicationPort && !uData.disableAll())
4189        {
4190          uData.setDisableReplicationServer(false);
4191          println(INFO_REPLICATION_WARNING_NO_REPLICATION_SERVER_TO_DISABLE.get(getHostPort(ctx)));
4192          println();
4193        }
4194      }
4195
4196      if (mustPrintCommandBuilder())
4197      {
4198        printNewCommandBuilder(DISABLE_REPLICATION_SUBCMD_NAME, uData);
4199      }
4200
4201      if (!isInteractive() && !uData.disableReplicationServer() && !uData.disableAll() && disableAllBaseDns(ctx, uData)
4202          && hasReplicationPort(ctx))
4203      {
4204        // Inform the user that the replication server will not be disabled.
4205        // Inform also of the user of the disableReplicationServerArg
4206        println(INFO_REPLICATION_DISABLE_ALL_SUFFIXES_KEEP_REPLICATION_SERVER.get(getHostPort(ctx),
4207            argParser.disableReplicationServerArg.getLongIdentifier(), argParser.disableAllArg.getLongIdentifier()));
4208      }
4209      try
4210      {
4211        updateConfiguration(ctx, uData);
4212        return SUCCESSFUL;
4213      }
4214      catch (ReplicationCliException rce)
4215      {
4216        errPrintln();
4217        errPrintln(getCriticalExceptionMessage(rce));
4218        logger.error(LocalizableMessage.raw("Complete error stack:"), rce);
4219        return rce.getErrorCode();
4220      }
4221    }
4222    finally
4223    {
4224      close(ctx);
4225    }
4226  }
4227
4228  /**
4229   * Displays the replication status of the baseDNs specified in the
4230   * StatusReplicationUserData object.  This method does not prompt
4231   * to the user for information if something is missing.
4232   * @param uData the StatusReplicationUserData object.
4233   * @return ReplicationCliReturnCode.SUCCESSFUL if the operation was
4234   * successful and an error code otherwise.
4235   */
4236  private ReplicationCliReturnCode statusReplication(
4237      StatusReplicationUserData uData)
4238  {
4239    final InitialLdapContext ctx = createAdministrativeContext(uData);
4240    if (ctx == null)
4241    {
4242      return ERROR_CONNECTING;
4243    }
4244
4245    try
4246    {
4247      try
4248      {
4249        displayStatus(ctx, uData);
4250        return SUCCESSFUL;
4251      }
4252      catch (ReplicationCliException rce)
4253      {
4254        errPrintln();
4255        errPrintln(getCriticalExceptionMessage(rce));
4256        logger.error(LocalizableMessage.raw("Complete error stack:"), rce);
4257        return rce.getErrorCode();
4258      }
4259    }
4260    finally
4261    {
4262      close(ctx);
4263    }
4264  }
4265
4266  /**
4267   * Initializes the contents of one server with the contents of the other
4268   * using the parameters in the provided InitializeReplicationUserData.
4269   * This method does not prompt to the user for information if something is
4270   * missing.
4271   * @param uData the InitializeReplicationUserData object.
4272   * @return ReplicationCliReturnCode.SUCCESSFUL if the operation was
4273   * successful and an error code otherwise.
4274   */
4275  private ReplicationCliReturnCode initializeReplication(SourceDestinationServerUserData uData)
4276  {
4277    InitialLdapContext ctxSource = createAdministrativeContext(uData, uData.getSource());
4278    InitialLdapContext ctxDestination = createAdministrativeContext(uData, uData.getDestination());
4279    try
4280    {
4281      if (ctxSource == null || ctxDestination == null)
4282      {
4283        return ERROR_CONNECTING;
4284      }
4285
4286      List<String> baseDNs = uData.getBaseDNs();
4287      checkSuffixesForInitializeReplication(baseDNs, ctxSource, ctxDestination, false);
4288      if (baseDNs.isEmpty())
4289      {
4290        return REPLICATION_CANNOT_BE_INITIALIZED_ON_BASEDN;
4291      }
4292      if (mustPrintCommandBuilder())
4293      {
4294        uData.setBaseDNs(baseDNs);
4295        printNewCommandBuilder(INITIALIZE_REPLICATION_SUBCMD_NAME, uData);
4296      }
4297
4298      ReplicationCliReturnCode returnValue = SUCCESSFUL_NOP;
4299      for (String baseDN : baseDNs)
4300      {
4301        try
4302        {
4303          println();
4304          print(formatter.getFormattedProgress(INFO_PROGRESS_INITIALIZING_SUFFIX.get(baseDN, getHostPort(ctxSource))));
4305          println();
4306          initializeSuffix(baseDN, ctxSource, ctxDestination, true);
4307          returnValue = SUCCESSFUL;
4308        }
4309        catch (ReplicationCliException rce)
4310        {
4311          errPrintln();
4312          errPrintln(getCriticalExceptionMessage(rce));
4313          returnValue = rce.getErrorCode();
4314          logger.error(LocalizableMessage.raw("Complete error stack:"), rce);
4315        }
4316      }
4317      return returnValue;
4318    }
4319    finally
4320    {
4321      close(ctxDestination, ctxSource);
4322    }
4323  }
4324
4325  private InitialLdapContext createAdministrativeContext(SourceDestinationServerUserData uData, HostPort server)
4326  {
4327    try
4328    {
4329      return createAdministrativeContext(
4330          server.getHost(), server.getPort(), useSSL, useStartTLS,
4331          getAdministratorDN(uData.getAdminUid()), uData.getAdminPwd(),
4332          getConnectTimeout(), getTrustManager(sourceServerCI));
4333    }
4334    catch (NamingException ne)
4335    {
4336      errPrintln();
4337      errPrintln(getMessageForException(ne, server.toString()));
4338      logger.error(LocalizableMessage.raw("Complete error stack:"), ne);
4339      return null;
4340    }
4341  }
4342
4343  /**
4344   * Initializes the contents of a whole topology with the contents of the other
4345   * using the parameters in the provided InitializeAllReplicationUserData.
4346   * This method does not prompt to the user for information if something is
4347   * missing.
4348   * @param uData the InitializeAllReplicationUserData object.
4349   * @return ReplicationCliReturnCode.SUCCESSFUL if the operation was
4350   * successful and an error code otherwise.
4351   */
4352  private ReplicationCliReturnCode initializeAllReplication(
4353      InitializeAllReplicationUserData uData)
4354  {
4355    final InitialLdapContext ctx = createAdministrativeContext(uData);
4356    if (ctx == null)
4357    {
4358      return ERROR_CONNECTING;
4359    }
4360
4361    try
4362    {
4363      List<String> baseDNs = uData.getBaseDNs();
4364      checkSuffixesForInitializeReplication(baseDNs, ctx, false);
4365      if (baseDNs.isEmpty())
4366      {
4367        return REPLICATION_CANNOT_BE_INITIALIZED_ON_BASEDN;
4368      }
4369      if (mustPrintCommandBuilder())
4370      {
4371        uData.setBaseDNs(baseDNs);
4372        printNewCommandBuilder(INITIALIZE_ALL_REPLICATION_SUBCMD_NAME, uData);
4373      }
4374
4375      ReplicationCliReturnCode returnValue = SUCCESSFUL_NOP;
4376      for (String baseDN : baseDNs)
4377      {
4378        try
4379        {
4380          println();
4381          print(formatter.getFormattedProgress(INFO_PROGRESS_INITIALIZING_SUFFIX.get(baseDN, getHostPort(ctx))));
4382          println();
4383          initializeAllSuffix(baseDN, ctx, true);
4384          returnValue = SUCCESSFUL;
4385        }
4386        catch (ReplicationCliException rce)
4387        {
4388          errPrintln();
4389          errPrintln(getCriticalExceptionMessage(rce));
4390          returnValue = rce.getErrorCode();
4391          logger.error(LocalizableMessage.raw("Complete error stack:"), rce);
4392        }
4393      }
4394      return returnValue;
4395    }
4396    finally
4397    {
4398      close(ctx);
4399    }
4400  }
4401
4402  /**
4403   * Performs the operation that must be made before initializing the topology
4404   * using the import-ldif command or the binary copy.  The operation uses
4405   * the parameters in the provided InitializeAllReplicationUserData.
4406   * This method does not prompt to the user for information if something is
4407   * missing.
4408   * @param uData the PreExternalInitializationUserData object.
4409   * @return ReplicationCliReturnCode.SUCCESSFUL if the operation was
4410   * successful and an error code otherwise.
4411   */
4412  private ReplicationCliReturnCode preExternalInitialization(
4413      PreExternalInitializationUserData uData)
4414  {
4415    InitialLdapContext ctx = createAdministrativeContext(uData);
4416    if (ctx == null)
4417    {
4418      return ERROR_CONNECTING;
4419    }
4420
4421    try
4422    {
4423      List<String> baseDNs = uData.getBaseDNs();
4424      checkSuffixesForInitializeReplication(baseDNs, ctx, false);
4425      if (baseDNs.isEmpty())
4426      {
4427        return REPLICATION_CANNOT_BE_INITIALIZED_ON_BASEDN;
4428      }
4429      if (mustPrintCommandBuilder())
4430      {
4431        uData.setBaseDNs(baseDNs);
4432        printNewCommandBuilder(PRE_EXTERNAL_INITIALIZATION_SUBCMD_NAME, uData);
4433      }
4434
4435      ReplicationCliReturnCode returnValue = SUCCESSFUL;
4436      for (String baseDN : baseDNs)
4437      {
4438        try
4439        {
4440          println();
4441          print(formatter.getFormattedWithPoints(INFO_PROGRESS_PRE_EXTERNAL_INITIALIZATION.get(baseDN)));
4442          preExternalInitialization(baseDN, ctx);
4443          print(formatter.getFormattedDone());
4444          println();
4445        }
4446        catch (ReplicationCliException rce)
4447        {
4448          errPrintln();
4449          errPrintln(getCriticalExceptionMessage(rce));
4450          returnValue = rce.getErrorCode();
4451          logger.error(LocalizableMessage.raw("Complete error stack:"), rce);
4452        }
4453      }
4454      println();
4455      print(INFO_PROGRESS_PRE_INITIALIZATION_FINISHED_PROCEDURE.get(POST_EXTERNAL_INITIALIZATION_SUBCMD_NAME));
4456      println();
4457      return returnValue;
4458    }
4459    finally
4460    {
4461      close(ctx);
4462    }
4463  }
4464
4465  /**
4466   * Performs the operation that must be made after initializing the topology
4467   * using the import-ldif command or the binary copy.  The operation uses
4468   * the parameters in the provided InitializeAllReplicationUserData.
4469   * This method does not prompt to the user for information if something is
4470   * missing.
4471   * @param uData the PostExternalInitializationUserData object.
4472   * @return ReplicationCliReturnCode.SUCCESSFUL if the operation was
4473   * successful and an error code otherwise.
4474   */
4475  private ReplicationCliReturnCode postExternalInitialization(
4476      PostExternalInitializationUserData uData)
4477  {
4478    InitialLdapContext ctx = createAdministrativeContext(uData);
4479    if (ctx == null)
4480    {
4481      return ERROR_CONNECTING;
4482    }
4483
4484    try
4485    {
4486      List<String> baseDNs = uData.getBaseDNs();
4487      checkSuffixesForInitializeReplication(baseDNs, ctx, false);
4488      if (baseDNs.isEmpty())
4489      {
4490        return REPLICATION_CANNOT_BE_INITIALIZED_ON_BASEDN;
4491      }
4492      if (mustPrintCommandBuilder())
4493      {
4494        uData.setBaseDNs(baseDNs);
4495        printNewCommandBuilder(POST_EXTERNAL_INITIALIZATION_SUBCMD_NAME, uData);
4496      }
4497
4498      ReplicationCliReturnCode returnValue = SUCCESSFUL;
4499      for (String baseDN : baseDNs)
4500      {
4501        try
4502        {
4503          println();
4504          print(formatter.getFormattedWithPoints(INFO_PROGRESS_POST_EXTERNAL_INITIALIZATION.get(baseDN)));
4505          postExternalInitialization(baseDN, ctx);
4506          println(formatter.getFormattedDone());
4507          println();
4508        }
4509        catch (ReplicationCliException rce)
4510        {
4511          errPrintln();
4512          errPrintln(getCriticalExceptionMessage(rce));
4513          returnValue = rce.getErrorCode();
4514          logger.error(LocalizableMessage.raw("Complete error stack:"), rce);
4515        }
4516      }
4517      println();
4518      print(INFO_PROGRESS_POST_INITIALIZATION_FINISHED_PROCEDURE.get());
4519      println();
4520      return returnValue;
4521    }
4522    finally
4523    {
4524      close(ctx);
4525    }
4526  }
4527
4528  /**
4529   * Checks that replication can actually be enabled in the provided baseDNs
4530   * for the two servers.
4531   * @param suffixes the suffixes provided by the user.  This Collection is
4532   * updated by removing the base DNs that cannot be enabled and with the
4533   * base DNs that the user provided interactively.
4534   * @param ctx1 connection to the first server.
4535   * @param ctx2 connection to the second server.
4536   * @param interactive whether to ask the user to provide interactively
4537   * base DNs if none of the provided base DNs can be enabled.
4538   * @param uData the user data.  This object will not be updated by this method
4539   * but it is assumed that it contains information about whether the
4540   * replication domains must be configured or not.
4541   */
4542  private void checkSuffixesForEnableReplication(Collection<String> suffixes,
4543      InitialLdapContext ctx1, InitialLdapContext ctx2,
4544      boolean interactive, EnableReplicationUserData uData)
4545  {
4546    EnableReplicationServerData server1 = uData.getServer1();
4547    EnableReplicationServerData server2 = uData.getServer2();
4548    final TreeSet<String> availableSuffixes = new TreeSet<>();
4549    final TreeSet<String> alreadyReplicatedSuffixes = new TreeSet<>();
4550    if (server1.configureReplicationDomain() &&
4551        server2.configureReplicationDomain())
4552    {
4553      availableSuffixes.addAll(getCommonSuffixes(ctx1, ctx2,
4554            SuffixRelationType.NOT_FULLY_REPLICATED));
4555      alreadyReplicatedSuffixes.addAll(getCommonSuffixes(ctx1, ctx2,
4556            SuffixRelationType.FULLY_REPLICATED));
4557    }
4558    else if (server1.configureReplicationDomain())
4559    {
4560      updateAvailableAndReplicatedSuffixesForOneDomain(ctx1, ctx2,
4561          availableSuffixes, alreadyReplicatedSuffixes);
4562    }
4563    else if (server2.configureReplicationDomain())
4564    {
4565      updateAvailableAndReplicatedSuffixesForOneDomain(ctx2, ctx1,
4566          availableSuffixes, alreadyReplicatedSuffixes);
4567    }
4568    else
4569    {
4570      updateAvailableAndReplicatedSuffixesForNoDomain(ctx1, ctx2,
4571          availableSuffixes, alreadyReplicatedSuffixes);
4572    }
4573
4574    if (availableSuffixes.isEmpty())
4575    {
4576      println();
4577      if (!server1.configureReplicationDomain() &&
4578          !server1.configureReplicationDomain() &&
4579          alreadyReplicatedSuffixes.isEmpty())
4580      {
4581        // Use a clarifying message: there is no replicated base DN.
4582        errPrintln(ERR_NO_SUFFIXES_AVAILABLE_TO_ENABLE_REPLICATION_NO_DOMAIN.get());
4583      }
4584      else
4585      {
4586        errPrintln(ERR_NO_SUFFIXES_AVAILABLE_TO_ENABLE_REPLICATION.get());
4587      }
4588
4589      LinkedList<String> userProvidedSuffixes = argParser.getBaseDNs();
4590      TreeSet<String> userProvidedReplicatedSuffixes = new TreeSet<>();
4591
4592      for (String s1 : userProvidedSuffixes)
4593      {
4594        for (String s2 : alreadyReplicatedSuffixes)
4595        {
4596          if (areDnsEqual(s1, s2))
4597          {
4598            userProvidedReplicatedSuffixes.add(s1);
4599          }
4600        }
4601      }
4602      if (!userProvidedReplicatedSuffixes.isEmpty())
4603      {
4604        println();
4605        println(INFO_ALREADY_REPLICATED_SUFFIXES.get(toSingleLine(userProvidedReplicatedSuffixes)));
4606      }
4607      suffixes.clear();
4608    }
4609    else
4610    {
4611      //  Verify that the provided suffixes are configured in the servers.
4612      TreeSet<String> notFound = new TreeSet<>();
4613      TreeSet<String> alreadyReplicated = new TreeSet<>();
4614      for (String dn : suffixes)
4615      {
4616        if (!containsDN(availableSuffixes, dn))
4617        {
4618          if (containsDN(alreadyReplicatedSuffixes, dn))
4619          {
4620            alreadyReplicated.add(dn);
4621          }
4622          else
4623          {
4624            notFound.add(dn);
4625          }
4626        }
4627      }
4628      suffixes.removeAll(notFound);
4629      suffixes.removeAll(alreadyReplicated);
4630      if (!notFound.isEmpty())
4631      {
4632        errPrintln();
4633        errPrintln(ERR_REPLICATION_ENABLE_SUFFIXES_NOT_FOUND.get(toSingleLine(notFound)));
4634      }
4635      if (!alreadyReplicated.isEmpty())
4636      {
4637        println();
4638        println(INFO_ALREADY_REPLICATED_SUFFIXES.get(toSingleLine(alreadyReplicated)));
4639      }
4640      if (interactive)
4641      {
4642        askConfirmations(suffixes, availableSuffixes,
4643            ERR_NO_SUFFIXES_AVAILABLE_TO_ENABLE_REPLICATION,
4644            ERR_NO_SUFFIXES_SELECTED_TO_REPLICATE,
4645            INFO_REPLICATION_ENABLE_SUFFIX_PROMPT);
4646      }
4647    }
4648  }
4649
4650  /**
4651   * Checks that replication can actually be disabled in the provided baseDNs
4652   * for the server.
4653   * @param suffixes the suffixes provided by the user.  This Collection is
4654   * updated by removing the base DNs that cannot be disabled and with the
4655   * base DNs that the user provided interactively.
4656   * @param ctx connection to the server.
4657   * @param interactive whether to ask the user to provide interactively
4658   * base DNs if none of the provided base DNs can be disabled.
4659   * @param displayErrors whether to display errors or not.
4660   */
4661  private void checkSuffixesForDisableReplication(Collection<String> suffixes,
4662      InitialLdapContext ctx, boolean interactive, boolean displayErrors)
4663  {
4664    // whether the user must provide base DNs or not
4665    // (if it is <CODE>false</CODE> the user will be proposed the suffixes only once)
4666    final boolean areSuffixRequired = displayErrors;
4667
4668    TreeSet<String> availableSuffixes = new TreeSet<>();
4669    TreeSet<String> notReplicatedSuffixes = new TreeSet<>();
4670
4671    Collection<ReplicaDescriptor> replicas = getReplicas(ctx);
4672    for (ReplicaDescriptor rep : replicas)
4673    {
4674      String dn = rep.getSuffix().getDN();
4675      if (rep.isReplicated())
4676      {
4677        availableSuffixes.add(dn);
4678      }
4679      else
4680      {
4681        notReplicatedSuffixes.add(dn);
4682      }
4683    }
4684    if (availableSuffixes.isEmpty())
4685    {
4686      if (displayErrors)
4687      {
4688        errPrintln();
4689        errPrintln(ERR_NO_SUFFIXES_AVAILABLE_TO_DISABLE_REPLICATION.get());
4690      }
4691      LinkedList<String> userProvidedSuffixes = argParser.getBaseDNs();
4692      TreeSet<String> userProvidedNotReplicatedSuffixes = new TreeSet<>();
4693      for (String s1 : userProvidedSuffixes)
4694      {
4695        for (String s2 : notReplicatedSuffixes)
4696        {
4697          if (areDnsEqual(s1, s2))
4698          {
4699            userProvidedNotReplicatedSuffixes.add(s1);
4700          }
4701        }
4702      }
4703      if (!userProvidedNotReplicatedSuffixes.isEmpty() && displayErrors)
4704      {
4705        println();
4706        println(INFO_ALREADY_NOT_REPLICATED_SUFFIXES.get(
4707            toSingleLine(userProvidedNotReplicatedSuffixes)));
4708      }
4709      suffixes.clear();
4710    }
4711    else
4712    {
4713      // Verify that the provided suffixes are configured in the servers.
4714      TreeSet<String> notFound = new TreeSet<>();
4715      TreeSet<String> alreadyNotReplicated = new TreeSet<>();
4716      for (String dn : suffixes)
4717      {
4718        if (!containsDN(availableSuffixes, dn))
4719        {
4720          if (containsDN(notReplicatedSuffixes, dn))
4721          {
4722            alreadyNotReplicated.add(dn);
4723          }
4724          else
4725          {
4726            notFound.add(dn);
4727          }
4728        }
4729      }
4730      suffixes.removeAll(notFound);
4731      suffixes.removeAll(alreadyNotReplicated);
4732      if (!notFound.isEmpty() && displayErrors)
4733      {
4734        errPrintln();
4735        errPrintln(ERR_REPLICATION_DISABLE_SUFFIXES_NOT_FOUND.get(toSingleLine(notFound)));
4736      }
4737      if (!alreadyNotReplicated.isEmpty() && displayErrors)
4738      {
4739        println();
4740        println(INFO_ALREADY_NOT_REPLICATED_SUFFIXES.get(toSingleLine(alreadyNotReplicated)));
4741      }
4742      if (interactive)
4743      {
4744        while (suffixes.isEmpty())
4745        {
4746          if (containsOnlySchemaOrAdminSuffix(availableSuffixes))
4747          {
4748            // In interactive mode we do not propose to manage the administration suffix.
4749            if (displayErrors)
4750            {
4751              errPrintln();
4752              errPrintln(ERR_NO_SUFFIXES_AVAILABLE_TO_DISABLE_REPLICATION.get());
4753            }
4754            break;
4755          }
4756
4757          if (areSuffixRequired)
4758          {
4759            errPrintln();
4760            errPrintln(ERR_NO_SUFFIXES_SELECTED_TO_DISABLE.get());
4761          }
4762          boolean confirmationLimitReached =
4763              askConfirmations(INFO_REPLICATION_DISABLE_SUFFIX_PROMPT, availableSuffixes, suffixes);
4764          if (confirmationLimitReached)
4765          {
4766            suffixes.clear();
4767            break;
4768          }
4769          if (!areSuffixRequired)
4770          {
4771            break;
4772          }
4773        }
4774      }
4775    }
4776  }
4777
4778  private boolean askConfirmations(Arg1<Object> confirmationMsg,
4779      Collection<String> availableSuffixes, Collection<String> suffixes)
4780  {
4781    for (String dn : availableSuffixes)
4782    {
4783      if (!isSchemaOrInternalAdminSuffix(dn))
4784      {
4785        try
4786        {
4787          if (askConfirmation(confirmationMsg.get(dn), true, logger))
4788          {
4789            suffixes.add(dn);
4790          }
4791        }
4792        catch (ClientException ce)
4793        {
4794          errPrintln(ce.getMessageObject());
4795          return true;
4796        }
4797      }
4798    }
4799    return false;
4800  }
4801
4802  /**
4803   * Checks that replication can actually be initialized in the provided baseDNs
4804   * for the server.
4805   * @param suffixes the suffixes provided by the user.  This Collection is
4806   * updated by removing the base DNs that cannot be initialized and with the
4807   * base DNs that the user provided interactively.
4808   * @param ctx connection to the server.
4809   * @param interactive whether to ask the user to provide interactively
4810   * base DNs if none of the provided base DNs can be initialized.
4811   */
4812  private void checkSuffixesForInitializeReplication(
4813      Collection<String> suffixes, InitialLdapContext ctx, boolean interactive)
4814  {
4815    TreeSet<String> availableSuffixes = new TreeSet<>();
4816    TreeSet<String> notReplicatedSuffixes = new TreeSet<>();
4817
4818    Collection<ReplicaDescriptor> replicas = getReplicas(ctx);
4819    for (ReplicaDescriptor rep : replicas)
4820    {
4821      String dn = rep.getSuffix().getDN();
4822      if (rep.isReplicated())
4823      {
4824        availableSuffixes.add(dn);
4825      }
4826      else
4827      {
4828        notReplicatedSuffixes.add(dn);
4829      }
4830    }
4831    if (availableSuffixes.isEmpty())
4832    {
4833      println();
4834      if (argParser.isInitializeAllReplicationSubcommand())
4835      {
4836        errPrintln(ERR_NO_SUFFIXES_AVAILABLE_TO_INITIALIZE_ALL_REPLICATION.get());
4837      }
4838      else
4839      {
4840        errPrintln(
4841            ERR_NO_SUFFIXES_AVAILABLE_TO_INITIALIZE_LOCAL_REPLICATION.get());
4842      }
4843      LinkedList<String> userProvidedSuffixes = argParser.getBaseDNs();
4844      TreeSet<String> userProvidedNotReplicatedSuffixes = new TreeSet<>();
4845      for (String s1 : userProvidedSuffixes)
4846      {
4847        for (String s2 : notReplicatedSuffixes)
4848        {
4849          if (areDnsEqual(s1, s2))
4850          {
4851            userProvidedNotReplicatedSuffixes.add(s1);
4852          }
4853        }
4854      }
4855      if (!userProvidedNotReplicatedSuffixes.isEmpty())
4856      {
4857        println();
4858        println(INFO_ALREADY_NOT_REPLICATED_SUFFIXES.get(
4859            toSingleLine(userProvidedNotReplicatedSuffixes)));
4860      }
4861      suffixes.clear();
4862    }
4863    else
4864    {
4865      // Verify that the provided suffixes are configured in the servers.
4866      TreeSet<String> notFound = new TreeSet<>();
4867      TreeSet<String> alreadyNotReplicated = new TreeSet<>();
4868      for (String dn : suffixes)
4869      {
4870        if (!containsDN(availableSuffixes, dn))
4871        {
4872          if (containsDN(notReplicatedSuffixes, dn))
4873          {
4874            alreadyNotReplicated.add(dn);
4875          }
4876          else
4877          {
4878            notFound.add(dn);
4879          }
4880        }
4881      }
4882      suffixes.removeAll(notFound);
4883      suffixes.removeAll(alreadyNotReplicated);
4884      if (!notFound.isEmpty())
4885      {
4886        errPrintln();
4887        errPrintln(ERR_REPLICATION_INITIALIZE_LOCAL_SUFFIXES_NOT_FOUND.get(toSingleLine(notFound)));
4888      }
4889      if (!alreadyNotReplicated.isEmpty())
4890      {
4891        println();
4892        println(INFO_ALREADY_NOT_REPLICATED_SUFFIXES.get(toSingleLine(alreadyNotReplicated)));
4893      }
4894      if (interactive)
4895      {
4896        boolean confirmationLimitReached = false;
4897        while (suffixes.isEmpty())
4898        {
4899          println();
4900          if (containsOnlySchemaOrAdminSuffix(availableSuffixes))
4901          {
4902            // In interactive mode we do not propose to manage the administration suffix.
4903            if (argParser.isInitializeAllReplicationSubcommand())
4904            {
4905              errPrintln(ERR_NO_SUFFIXES_AVAILABLE_TO_INITIALIZE_ALL_REPLICATION.get());
4906            }
4907            else
4908            {
4909              errPrintln(ERR_NO_SUFFIXES_AVAILABLE_TO_INITIALIZE_LOCAL_REPLICATION.get());
4910            }
4911            break;
4912          }
4913          else
4914          {
4915            if (argParser.isInitializeAllReplicationSubcommand())
4916            {
4917              errPrintln(ERR_NO_SUFFIXES_SELECTED_TO_INITIALIZE_ALL.get());
4918            }
4919            else if (argParser.isPreExternalInitializationSubcommand())
4920            {
4921              errPrintln(ERR_NO_SUFFIXES_SELECTED_TO_PRE_EXTERNAL_INITIALIZATION.get());
4922            }
4923            else if (argParser.isPostExternalInitializationSubcommand())
4924            {
4925              errPrintln(ERR_NO_SUFFIXES_SELECTED_TO_POST_EXTERNAL_INITIALIZATION.get());
4926            }
4927
4928            for (String dn : availableSuffixes)
4929            {
4930              if (!isSchemaOrInternalAdminSuffix(dn))
4931              {
4932                boolean addSuffix;
4933                try
4934                {
4935                  if (argParser.isPreExternalInitializationSubcommand())
4936                  {
4937                    addSuffix = askConfirmation(
4938                    INFO_REPLICATION_PRE_EXTERNAL_INITIALIZATION_SUFFIX_PROMPT.
4939                        get(dn), true, logger);
4940                  }
4941                  else if (argParser.isPostExternalInitializationSubcommand())
4942                  {
4943                    addSuffix = askConfirmation(
4944                    INFO_REPLICATION_POST_EXTERNAL_INITIALIZATION_SUFFIX_PROMPT.
4945                        get(dn), true, logger);
4946                  }
4947                  else
4948                  {
4949                    addSuffix = askConfirmation(
4950                        INFO_REPLICATION_INITIALIZE_ALL_SUFFIX_PROMPT.get(dn),
4951                        true, logger);
4952                  }
4953                }
4954                catch (ClientException ce)
4955                {
4956                  errPrintln(ce.getMessageObject());
4957                  confirmationLimitReached = true;
4958                  break;
4959                }
4960                if (addSuffix)
4961                {
4962                  suffixes.add(dn);
4963                }
4964              }
4965            }
4966          }
4967          if (confirmationLimitReached)
4968          {
4969            suffixes.clear();
4970            break;
4971          }
4972        }
4973      }
4974    }
4975  }
4976
4977  private String toSingleLine(Collection<String> notFound)
4978  {
4979    return joinAsString(Constants.LINE_SEPARATOR, notFound);
4980  }
4981
4982  /**
4983   * Checks that we can initialize the provided baseDNs between the two servers.
4984   * @param suffixes the suffixes provided by the user.  This Collection is
4985   * updated by removing the base DNs that cannot be enabled and with the
4986   * base DNs that the user provided interactively.
4987   * @param ctxSource connection to the source server.
4988   * @param ctxDestination connection to the destination server.
4989   * @param interactive whether to ask the user to provide interactively
4990   * base DNs if none of the provided base DNs can be initialized.
4991   */
4992  private void checkSuffixesForInitializeReplication(
4993      Collection<String> suffixes, InitialLdapContext ctxSource,
4994      InitialLdapContext ctxDestination, boolean interactive)
4995  {
4996    TreeSet<String> availableSuffixes = new TreeSet<>(
4997        getCommonSuffixes(ctxSource, ctxDestination, SuffixRelationType.REPLICATED));
4998    if (availableSuffixes.isEmpty())
4999    {
5000      errPrintln();
5001      errPrintln(ERR_NO_SUFFIXES_AVAILABLE_TO_INITIALIZE_REPLICATION.get());
5002      suffixes.clear();
5003    }
5004    else
5005    {
5006      // Verify that the provided suffixes are configured in the servers.
5007      LinkedList<String> notFound = new LinkedList<>();
5008      for (String dn : suffixes)
5009      {
5010        if (!containsDN(availableSuffixes, dn))
5011        {
5012          notFound.add(dn);
5013        }
5014      }
5015      suffixes.removeAll(notFound);
5016      if (!notFound.isEmpty())
5017      {
5018        errPrintln();
5019        errPrintln(ERR_SUFFIXES_CANNOT_BE_INITIALIZED.get(toSingleLine(notFound)));
5020      }
5021      if (interactive)
5022      {
5023        askConfirmations(suffixes, availableSuffixes,
5024            ERR_NO_SUFFIXES_AVAILABLE_TO_INITIALIZE_REPLICATION,
5025            ERR_NO_SUFFIXES_SELECTED_TO_INITIALIZE,
5026            INFO_REPLICATION_INITIALIZE_SUFFIX_PROMPT);
5027      }
5028    }
5029  }
5030
5031  /**
5032   * Updates the configuration in the two servers (and in other servers if
5033   * they are referenced) to enable replication.
5034   * @param ctx1 the connection to the first server.
5035   * @param ctx2 the connection to the second server.
5036   * @param uData the EnableReplicationUserData object containing the required
5037   * parameters to update the configuration.
5038   * @throws ReplicationCliException if there is an error.
5039   */
5040  private void updateConfiguration(InitialLdapContext ctx1,
5041      InitialLdapContext ctx2, EnableReplicationUserData uData)
5042  throws ReplicationCliException
5043  {
5044    final Set<String> twoReplServers = new LinkedHashSet<>();
5045    final Set<String> allRepServers = new LinkedHashSet<>();
5046    final Map<String, Set<String>> hmRepServers = new HashMap<>();
5047    final Set<Integer> usedReplicationServerIds = new HashSet<>();
5048    final Map<String, Set<Integer>> hmUsedReplicationDomainIds = new HashMap<>();
5049
5050    TopologyCacheFilter filter = new TopologyCacheFilter();
5051    filter.setSearchMonitoringInformation(false);
5052    filter.addBaseDNToSearch(ADSContext.getAdministrationSuffixDN());
5053    filter.addBaseDNToSearch(Constants.SCHEMA_DN);
5054    addBaseDNs(filter, uData.getBaseDNs());
5055    ServerDescriptor serverDesc1 = createStandalone(ctx1, filter);
5056    ServerDescriptor serverDesc2 = createStandalone(ctx2, filter);
5057
5058    ADSContext adsCtx1 = new ADSContext(ctx1);
5059    ADSContext adsCtx2 = new ADSContext(ctx2);
5060
5061    if (!argParser.isInteractive())
5062    {
5063      // Inform the user of the potential errors that we found in the already
5064      // registered servers.
5065      final Set<LocalizableMessage> messages = new LinkedHashSet<>();
5066      try
5067      {
5068        final Set<PreferredConnection> cnx = new LinkedHashSet<>();
5069        cnx.addAll(getPreferredConnections(ctx1));
5070        cnx.addAll(getPreferredConnections(ctx2));
5071        TopologyCache cache1 = createTopologyCache(adsCtx1, cnx, uData);
5072        if (cache1 != null)
5073        {
5074          messages.addAll(cache1.getErrorMessages());
5075        }
5076        TopologyCache cache2 = createTopologyCache(adsCtx2, cnx, uData);
5077        if (cache2 != null)
5078        {
5079          messages.addAll(cache2.getErrorMessages());
5080        }
5081      }
5082      catch (TopologyCacheException tce)
5083      {
5084        throw new ReplicationCliException(
5085            ERR_REPLICATION_READING_ADS.get(tce.getMessage()),
5086            ERROR_READING_TOPOLOGY_CACHE, tce);
5087      }
5088      catch (ADSContextException adce)
5089      {
5090        throw new ReplicationCliException(
5091            ERR_REPLICATION_READING_ADS.get(adce.getMessage()),
5092            ERROR_READING_ADS, adce);
5093      }
5094      if (!messages.isEmpty())
5095      {
5096        errPrintln(ERR_REPLICATION_READING_REGISTERED_SERVERS_WARNING.get(
5097                getMessageFromCollection(messages,
5098                    Constants.LINE_SEPARATOR)));
5099      }
5100    }
5101    // Check whether there is more than one replication server in the topology.
5102    Set<String> baseDNsWithOneReplicationServer = new TreeSet<>();
5103    Set<String> baseDNsWithNoReplicationServer = new TreeSet<>();
5104    updateBaseDnsWithNotEnoughReplicationServer(adsCtx1, adsCtx2, uData,
5105       baseDNsWithNoReplicationServer, baseDNsWithOneReplicationServer);
5106
5107    if (!baseDNsWithNoReplicationServer.isEmpty())
5108    {
5109      LocalizableMessage errorMsg =
5110        ERR_REPLICATION_NO_REPLICATION_SERVER.get(toSingleLine(baseDNsWithNoReplicationServer));
5111      throw new ReplicationCliException(errorMsg, ERROR_USER_DATA, null);
5112    }
5113    else if (!baseDNsWithOneReplicationServer.isEmpty())
5114    {
5115      if (isInteractive())
5116      {
5117        LocalizableMessage confirmMsg = INFO_REPLICATION_ONLY_ONE_REPLICATION_SERVER_CONFIRM.get(
5118            toSingleLine(baseDNsWithOneReplicationServer));
5119        try
5120        {
5121          if (!confirmAction(confirmMsg, false))
5122          {
5123            throw new ReplicationCliException(
5124                ERR_REPLICATION_USER_CANCELLED.get(), USER_CANCELLED, null);
5125          }
5126        }
5127        catch (Throwable t)
5128        {
5129          throw new ReplicationCliException(
5130              ERR_REPLICATION_USER_CANCELLED.get(), USER_CANCELLED, t);
5131        }
5132      }
5133      else
5134      {
5135        errPrintln(INFO_REPLICATION_ONLY_ONE_REPLICATION_SERVER_WARNING.get(
5136            toSingleLine(baseDNsWithOneReplicationServer)));
5137        errPrintln();
5138      }
5139    }
5140
5141    // These are used to identify which server we use to initialize
5142    // the contents of the other server (if any).
5143    InitialLdapContext ctxSource = null;
5144    InitialLdapContext ctxDestination = null;
5145    ADSContext adsCtxSource = null;
5146
5147    boolean adsAlreadyReplicated = false;
5148    boolean adsMergeDone = false;
5149
5150    print(formatter.getFormattedWithPoints(
5151        INFO_REPLICATION_ENABLE_UPDATING_ADS_CONTENTS.get()));
5152    try
5153    {
5154      if (adsCtx1.hasAdminData() && adsCtx2.hasAdminData())
5155      {
5156        Set<Map<ServerProperty, Object>> registry1 = adsCtx1.readServerRegistry();
5157        Set<Map<ServerProperty, Object>> registry2 = adsCtx2.readServerRegistry();
5158        if (registry2.size() <= 1)
5159        {
5160          if (!hasAdministrator(adsCtx1.getDirContext(), uData))
5161          {
5162            adsCtx1.createAdministrator(getAdministratorProperties(uData));
5163          }
5164          serverDesc2.updateAdsPropertiesWithServerProperties();
5165          registerServer(adsCtx1, serverDesc2.getAdsProperties());
5166          if (!ADSContext.isRegistered(serverDesc1, registry1))
5167          {
5168            serverDesc1.updateAdsPropertiesWithServerProperties();
5169            registerServer(adsCtx1, serverDesc1.getAdsProperties());
5170          }
5171
5172          ctxSource = ctx1;
5173          ctxDestination = ctx2;
5174          adsCtxSource = adsCtx1;
5175        }
5176        else if (registry1.size() <= 1)
5177        {
5178          if (!hasAdministrator(adsCtx2.getDirContext(), uData))
5179          {
5180            adsCtx2.createAdministrator(getAdministratorProperties(uData));
5181          }
5182          serverDesc1.updateAdsPropertiesWithServerProperties();
5183          registerServer(adsCtx2, serverDesc1.getAdsProperties());
5184
5185          if (!ADSContext.isRegistered(serverDesc2, registry2))
5186          {
5187            serverDesc2.updateAdsPropertiesWithServerProperties();
5188            registerServer(adsCtx2, serverDesc2.getAdsProperties());
5189          }
5190
5191          ctxSource = ctx2;
5192          ctxDestination = ctx1;
5193          adsCtxSource = adsCtx2;
5194        }
5195        else if (!areEqual(registry1, registry2))
5196        {
5197          print(formatter.getFormattedDone());
5198          println();
5199
5200          boolean isFirstSource = mergeRegistries(adsCtx1, adsCtx2);
5201          ctxSource = isFirstSource ? ctx1 : ctx2;
5202          adsMergeDone = true;
5203        }
5204        else
5205        {
5206          // They are already replicated: nothing to do in terms of ADS
5207          // initialization or ADS update data
5208          adsAlreadyReplicated = isBaseDNReplicated(serverDesc1, serverDesc2, ADSContext.getAdministrationSuffixDN());
5209
5210          if (!adsAlreadyReplicated)
5211          {
5212            // Try to merge if both are replicated
5213            boolean isADS1Replicated = isBaseDNReplicated(serverDesc1, ADSContext.getAdministrationSuffixDN());
5214            boolean isADS2Replicated = isBaseDNReplicated(serverDesc2, ADSContext.getAdministrationSuffixDN());
5215            if (isADS1Replicated && isADS2Replicated)
5216            {
5217              // Merge
5218              print(formatter.getFormattedDone());
5219              println();
5220
5221              boolean isFirstSource = mergeRegistries(adsCtx1, adsCtx2);
5222              ctxSource = isFirstSource ? ctx1 : ctx2;
5223              adsMergeDone = true;
5224            }
5225            else if (isADS1Replicated || !isADS2Replicated)
5226            {
5227              // The case where only the first ADS is replicated or none
5228              // is replicated.
5229              if (!hasAdministrator(adsCtx1.getDirContext(), uData))
5230              {
5231                adsCtx1.createAdministrator(getAdministratorProperties(uData));
5232              }
5233              serverDesc2.updateAdsPropertiesWithServerProperties();
5234              registerServer(adsCtx1, serverDesc2.getAdsProperties());
5235              if (!ADSContext.isRegistered(serverDesc1, registry1))
5236              {
5237                serverDesc1.updateAdsPropertiesWithServerProperties();
5238                registerServer(adsCtx1, serverDesc1.getAdsProperties());
5239              }
5240
5241              ctxSource = ctx1;
5242              ctxDestination = ctx2;
5243              adsCtxSource = adsCtx1;
5244            }
5245            else if (isADS2Replicated)
5246            {
5247              if (!hasAdministrator(adsCtx2.getDirContext(), uData))
5248              {
5249                adsCtx2.createAdministrator(getAdministratorProperties(uData));
5250              }
5251              serverDesc1.updateAdsPropertiesWithServerProperties();
5252              registerServer(adsCtx2, serverDesc1.getAdsProperties());
5253              if (!ADSContext.isRegistered(serverDesc2, registry2))
5254              {
5255                serverDesc2.updateAdsPropertiesWithServerProperties();
5256                registerServer(adsCtx2, serverDesc2.getAdsProperties());
5257              }
5258
5259              ctxSource = ctx2;
5260              ctxDestination = ctx1;
5261              adsCtxSource = adsCtx2;
5262            }
5263          }
5264        }
5265      }
5266      else if (!adsCtx1.hasAdminData() && adsCtx2.hasAdminData())
5267      {
5268        if (!hasAdministrator(adsCtx2.getDirContext(), uData))
5269        {
5270          adsCtx2.createAdministrator(getAdministratorProperties(uData));
5271        }
5272        serverDesc1.updateAdsPropertiesWithServerProperties();
5273        registerServer(adsCtx2, serverDesc1.getAdsProperties());
5274        Set<Map<ServerProperty, Object>> registry2 = adsCtx2.readServerRegistry();
5275        if (!ADSContext.isRegistered(serverDesc2, registry2))
5276        {
5277          serverDesc2.updateAdsPropertiesWithServerProperties();
5278          registerServer(adsCtx2, serverDesc2.getAdsProperties());
5279        }
5280
5281        ctxSource = ctx2;
5282        ctxDestination = ctx1;
5283        adsCtxSource = adsCtx2;
5284      }
5285      else if (adsCtx1.hasAdminData() && !adsCtx2.hasAdminData())
5286      {
5287        if (!hasAdministrator(adsCtx1.getDirContext(), uData))
5288        {
5289          adsCtx1.createAdministrator(getAdministratorProperties(uData));
5290        }
5291        serverDesc2.updateAdsPropertiesWithServerProperties();
5292        registerServer(adsCtx1, serverDesc2.getAdsProperties());
5293        Set<Map<ServerProperty, Object>> registry1 = adsCtx1.readServerRegistry();
5294        if (!ADSContext.isRegistered(serverDesc1, registry1))
5295        {
5296          serverDesc1.updateAdsPropertiesWithServerProperties();
5297          registerServer(adsCtx1, serverDesc1.getAdsProperties());
5298        }
5299
5300        ctxSource = ctx1;
5301        ctxDestination = ctx2;
5302        adsCtxSource = adsCtx1;
5303      }
5304      else
5305      {
5306        adsCtx1.createAdminData(null);
5307        if (!hasAdministrator(ctx1, uData))
5308        {
5309          // This could occur if the user created an administrator without
5310          // registering any server.
5311          adsCtx1.createAdministrator(getAdministratorProperties(uData));
5312        }
5313        serverDesc1.updateAdsPropertiesWithServerProperties();
5314        adsCtx1.registerServer(serverDesc1.getAdsProperties());
5315        serverDesc2.updateAdsPropertiesWithServerProperties();
5316        adsCtx1.registerServer(serverDesc2.getAdsProperties());
5317
5318        ctxSource = ctx1;
5319        ctxDestination = ctx2;
5320        adsCtxSource = adsCtx1;
5321      }
5322    }
5323    catch (ADSContextException adce)
5324    {
5325      throw new ReplicationCliException(
5326          ERR_REPLICATION_UPDATING_ADS.get(adce.getMessageObject()),
5327          ERROR_UPDATING_ADS, adce);
5328    }
5329    if (!adsAlreadyReplicated && !adsMergeDone)
5330    {
5331      try
5332      {
5333        ServerDescriptor.seedAdsTrustStore(ctxDestination,
5334            adsCtxSource.getTrustedCertificates());
5335      }
5336      catch (Throwable t)
5337      {
5338        logger.error(LocalizableMessage.raw("Error seeding truststores: "+t, t));
5339        throw new ReplicationCliException(
5340            ERR_REPLICATION_ENABLE_SEEDING_TRUSTSTORE.get(getHostPort(ctxDestination),
5341            getHostPort(adsCtxSource.getDirContext()), toString(t)),
5342            ERROR_SEEDING_TRUSTORE, t);
5343      }
5344    }
5345    if (!adsMergeDone)
5346    {
5347      print(formatter.getFormattedDone());
5348      println();
5349    }
5350    List<String> baseDNs = uData.getBaseDNs();
5351    if (!adsAlreadyReplicated
5352        && !containsDN(baseDNs, ADSContext.getAdministrationSuffixDN()))
5353    {
5354      baseDNs.add(ADSContext.getAdministrationSuffixDN());
5355      uData.setBaseDNs(baseDNs);
5356    }
5357
5358    if (uData.replicateSchema())
5359    {
5360      baseDNs = uData.getBaseDNs();
5361      baseDNs.add(Constants.SCHEMA_DN);
5362      uData.setBaseDNs(baseDNs);
5363    }
5364
5365    TopologyCache cache1 = null;
5366    TopologyCache cache2 = null;
5367    try
5368    {
5369      Set<PreferredConnection> cnx = new LinkedHashSet<>();
5370      cnx.addAll(getPreferredConnections(ctx1));
5371      cnx.addAll(getPreferredConnections(ctx2));
5372      cache1 = createTopologyCache(adsCtx1, cnx, uData);
5373      if (cache1 != null)
5374      {
5375        usedReplicationServerIds.addAll(getReplicationServerIds(cache1));
5376      }
5377      cache2 = createTopologyCache(adsCtx2, cnx, uData);
5378      if (cache1 != null)
5379      {
5380        usedReplicationServerIds.addAll(getReplicationServerIds(cache1));
5381      }
5382    }
5383    catch (ADSContextException adce)
5384    {
5385      throw new ReplicationCliException(
5386          ERR_REPLICATION_READING_ADS.get(adce.getMessage()),
5387          ERROR_READING_ADS, adce);
5388    }
5389    catch (TopologyCacheException tce)
5390    {
5391      throw new ReplicationCliException(
5392          ERR_REPLICATION_READING_ADS.get(tce.getMessage()),
5393          ERROR_READING_TOPOLOGY_CACHE, tce);
5394    }
5395
5396    addToSets(serverDesc1, uData.getServer1(), ctx1, twoReplServers, usedReplicationServerIds);
5397    addToSets(serverDesc2, uData.getServer2(), ctx2, twoReplServers, usedReplicationServerIds);
5398
5399    for (String baseDN : uData.getBaseDNs())
5400    {
5401      Set<String> repServersForBaseDN = new LinkedHashSet<>();
5402      repServersForBaseDN.addAll(getReplicationServers(baseDN, cache1, serverDesc1));
5403      repServersForBaseDN.addAll(getReplicationServers(baseDN, cache2, serverDesc2));
5404      repServersForBaseDN.addAll(twoReplServers);
5405      hmRepServers.put(baseDN, repServersForBaseDN);
5406
5407      Set<Integer> ids = new HashSet<>();
5408      ids.addAll(getReplicationDomainIds(baseDN, serverDesc1));
5409      ids.addAll(getReplicationDomainIds(baseDN, serverDesc2));
5410      if (cache1 != null)
5411      {
5412        for (ServerDescriptor server : cache1.getServers())
5413        {
5414          ids.addAll(getReplicationDomainIds(baseDN, server));
5415        }
5416      }
5417      if (cache2 != null)
5418      {
5419        for (ServerDescriptor server : cache2.getServers())
5420        {
5421          ids.addAll(getReplicationDomainIds(baseDN, server));
5422        }
5423      }
5424      hmUsedReplicationDomainIds.put(baseDN, ids);
5425    }
5426    for (Set<String> v : hmRepServers.values())
5427    {
5428      allRepServers.addAll(v);
5429    }
5430
5431    Set<String> alreadyConfiguredReplicationServers = new HashSet<>();
5432    configureServer(ctx1, serverDesc1, uData.getServer1(), argParser.server1.replicationPortArg,
5433        usedReplicationServerIds, allRepServers, alreadyConfiguredReplicationServers,
5434        WARN_FIRST_REPLICATION_SERVER_ALREADY_CONFIGURED);
5435    configureServer(ctx2, serverDesc2, uData.getServer2(), argParser.server2.replicationPortArg,
5436        usedReplicationServerIds, allRepServers, alreadyConfiguredReplicationServers,
5437        WARN_SECOND_REPLICATION_SERVER_ALREADY_CONFIGURED);
5438
5439    for (String baseDN : uData.getBaseDNs())
5440    {
5441      Set<String> repServers = hmRepServers.get(baseDN);
5442      Set<Integer> usedIds = hmUsedReplicationDomainIds.get(baseDN);
5443      Set<String> alreadyConfiguredServers = new HashSet<>();
5444
5445      configureToReplicateBaseDN(uData.getServer1(), ctx1, serverDesc1, cache1, baseDN,
5446          usedIds, alreadyConfiguredServers, repServers, allRepServers, alreadyConfiguredReplicationServers);
5447
5448      configureToReplicateBaseDN(uData.getServer2(), ctx2, serverDesc2, cache2, baseDN,
5449          usedIds, alreadyConfiguredServers, repServers, allRepServers, alreadyConfiguredReplicationServers);
5450    }
5451
5452    // Now that replication is configured in all servers, simply try to
5453    // initialize the contents of one ADS with the other (in the case where
5454    // already both servers were replicating the same ADS there is nothing to be done).
5455    if (adsMergeDone)
5456    {
5457      PointAdder pointAdder = new PointAdder(this);
5458      print(INFO_ENABLE_REPLICATION_INITIALIZING_ADS_ALL.get(getHostPort(ctxSource)));
5459      pointAdder.start();
5460      try
5461      {
5462        initializeAllSuffix(ADSContext.getAdministrationSuffixDN(), ctxSource, false);
5463      }
5464      finally
5465      {
5466        pointAdder.stop();
5467      }
5468      print(formatter.getSpace());
5469      print(formatter.getFormattedDone());
5470      println();
5471    }
5472    else if (ctxSource != null && ctxDestination != null)
5473    {
5474      print(formatter.getFormattedWithPoints(
5475          INFO_ENABLE_REPLICATION_INITIALIZING_ADS.get(
5476              getHostPort(ctxDestination), getHostPort(ctxSource))));
5477
5478      initializeSuffix(ADSContext.getAdministrationSuffixDN(), ctxSource, ctxDestination, false);
5479      print(formatter.getFormattedDone());
5480      println();
5481    }
5482
5483    // If we must initialize the schema do so.
5484    if (mustInitializeSchema(serverDesc1, serverDesc2, uData))
5485    {
5486      if (argParser.useSecondServerAsSchemaSource())
5487      {
5488        ctxSource = ctx2;
5489        ctxDestination = ctx1;
5490      }
5491      else
5492      {
5493        ctxSource = ctx1;
5494        ctxDestination = ctx2;
5495      }
5496      if (adsMergeDone)
5497      {
5498        PointAdder pointAdder = new PointAdder(this);
5499        println(INFO_ENABLE_REPLICATION_INITIALIZING_SCHEMA.get(
5500            getHostPort(ctxDestination), getHostPort(ctxSource)));
5501        pointAdder.start();
5502        try
5503        {
5504          initializeAllSuffix(Constants.SCHEMA_DN, ctxSource, false);
5505        }
5506        finally
5507        {
5508          pointAdder.stop();
5509        }
5510        print(formatter.getSpace());
5511      }
5512      else
5513      {
5514        print(formatter.getFormattedWithPoints(INFO_ENABLE_REPLICATION_INITIALIZING_SCHEMA.get(
5515            getHostPort(ctxDestination), getHostPort(ctxSource))));
5516        initializeSuffix(Constants.SCHEMA_DN, ctxSource, ctxDestination, false);
5517      }
5518      print(formatter.getFormattedDone());
5519      println();
5520    }
5521  }
5522
5523  private void addToSets(ServerDescriptor serverDesc, EnableReplicationServerData serverData, InitialLdapContext ctx,
5524      final Set<String> twoReplServers, final Set<Integer> usedReplicationServerIds)
5525  {
5526    if (serverDesc.isReplicationServer())
5527    {
5528      twoReplServers.add(serverDesc.getReplicationServerHostPort());
5529      usedReplicationServerIds.add(serverDesc.getReplicationServerId());
5530    }
5531    else if (serverData.configureReplicationServer())
5532    {
5533      twoReplServers.add(getReplicationServer(getHostName(ctx), serverData.getReplicationPort()));
5534    }
5535  }
5536
5537  private void configureToReplicateBaseDN(EnableReplicationServerData server, InitialLdapContext ctx,
5538      ServerDescriptor serverDesc, TopologyCache cache, String baseDN, Set<Integer> usedIds,
5539      Set<String> alreadyConfiguredServers, Set<String> repServers, final Set<String> allRepServers,
5540      Set<String> alreadyConfiguredReplicationServers) throws ReplicationCliException
5541  {
5542    if (server.configureReplicationDomain()
5543        || areDnsEqual(baseDN, ADSContext.getAdministrationSuffixDN()))
5544    {
5545      try
5546      {
5547        configureToReplicateBaseDN(ctx, baseDN, repServers, usedIds);
5548      }
5549      catch (OpenDsException ode)
5550      {
5551        LocalizableMessage msg = getMessageForEnableException(getHostPort(ctx), baseDN);
5552        throw new ReplicationCliException(msg, ERROR_ENABLING_REPLICATION_ON_BASEDN, ode);
5553      }
5554    }
5555    alreadyConfiguredServers.add(serverDesc.getId());
5556
5557    if (cache != null)
5558    {
5559      configureToReplicateBaseDN(baseDN, repServers, usedIds, cache, serverDesc, alreadyConfiguredServers,
5560          allRepServers, alreadyConfiguredReplicationServers);
5561    }
5562  }
5563
5564  private void configureServer(InitialLdapContext ctx, ServerDescriptor serverDesc,
5565      EnableReplicationServerData enableServer, IntegerArgument replicationPortArg,
5566      Set<Integer> usedReplicationServerIds, Set<String> allRepServers,
5567      Set<String> alreadyConfiguredReplicationServers, Arg2<Number, Number> replicationServerAlreadyConfiguredMsg)
5568      throws ReplicationCliException
5569  {
5570    if (!serverDesc.isReplicationServer() && enableServer.configureReplicationServer())
5571    {
5572      try
5573      {
5574        configureAsReplicationServer(ctx, enableServer.getReplicationPort(), enableServer.isSecureReplication(),
5575            allRepServers, usedReplicationServerIds);
5576      }
5577      catch (OpenDsException ode)
5578      {
5579        throw errorConfiguringReplicationServer(ctx, ode);
5580      }
5581    }
5582    else if (serverDesc.isReplicationServer())
5583    {
5584      try
5585      {
5586        updateReplicationServer(ctx, allRepServers);
5587      }
5588      catch (OpenDsException ode)
5589      {
5590        throw errorConfiguringReplicationServer(ctx, ode);
5591      }
5592      if (replicationPortArg.isPresent() && enableServer.getReplicationPort() != serverDesc.getReplicationServerPort())
5593      {
5594        LocalizableMessage msg = replicationServerAlreadyConfiguredMsg.get(
5595            serverDesc.getReplicationServerPort(), enableServer.getReplicationPort());
5596        logger.warn(msg);
5597        errPrintln(msg);
5598      }
5599    }
5600    alreadyConfiguredReplicationServers.add(serverDesc.getId());
5601  }
5602
5603  private ReplicationCliException errorConfiguringReplicationServer(InitialLdapContext ctx, OpenDsException ode)
5604  {
5605    return new ReplicationCliException(
5606        ERR_REPLICATION_CONFIGURING_REPLICATIONSERVER.get(getHostPort(ctx)),
5607        ERROR_CONFIGURING_REPLICATIONSERVER, ode);
5608  }
5609
5610  private TopologyCache createTopologyCache(ADSContext adsCtx, Set<PreferredConnection> cnx, ReplicationUserData uData)
5611      throws ADSContextException, TopologyCacheException
5612  {
5613    if (adsCtx.hasAdminData())
5614    {
5615      TopologyCache cache = new TopologyCache(adsCtx, getTrustManager(sourceServerCI), getConnectTimeout());
5616      cache.setPreferredConnections(cnx);
5617      cache.getFilter().setSearchMonitoringInformation(false);
5618      addBaseDNs(cache.getFilter(), uData.getBaseDNs());
5619      cache.reloadTopology();
5620      return cache;
5621    }
5622    return null;
5623  }
5624
5625  private ServerDescriptor createStandalone(InitialLdapContext ctx, TopologyCacheFilter filter)
5626      throws ReplicationCliException
5627  {
5628    try
5629    {
5630      return ServerDescriptor.createStandalone(ctx, filter);
5631    }
5632    catch (NamingException ne)
5633    {
5634      throw new ReplicationCliException(
5635          getMessageForException(ne, getHostPort(ctx)),
5636          ERROR_READING_CONFIGURATION, ne);
5637    }
5638  }
5639
5640  /**
5641   * Updates the configuration in the server (and in other servers if
5642   * they are referenced) to disable replication.
5643   * @param ctx the connection to the server.
5644   * @param uData the DisableReplicationUserData object containing the required
5645   * parameters to update the configuration.
5646   * @throws ReplicationCliException if there is an error.
5647   */
5648  private void updateConfiguration(InitialLdapContext ctx,
5649      DisableReplicationUserData uData) throws ReplicationCliException
5650  {
5651    TopologyCacheFilter filter = new TopologyCacheFilter();
5652    filter.setSearchMonitoringInformation(false);
5653    if (!uData.disableAll())
5654    {
5655      filter.addBaseDNToSearch(ADSContext.getAdministrationSuffixDN());
5656      addBaseDNs(filter, uData.getBaseDNs());
5657    }
5658    ServerDescriptor server = createStandalone(ctx, filter);
5659
5660    ADSContext adsCtx = new ADSContext(ctx);
5661
5662    TopologyCache cache = null;
5663    // Only try to update remote server if the user provided a Global
5664    // Administrator to authenticate.
5665    boolean tryToUpdateRemote = uData.getAdminUid() != null;
5666    try
5667    {
5668      if (adsCtx.hasAdminData() && tryToUpdateRemote)
5669      {
5670        cache = new TopologyCache(adsCtx, getTrustManager(sourceServerCI), getConnectTimeout());
5671        cache.setPreferredConnections(getPreferredConnections(ctx));
5672        cache.getFilter().setSearchMonitoringInformation(false);
5673        if (!uData.disableAll())
5674        {
5675          addBaseDNs(cache.getFilter(), uData.getBaseDNs());
5676        }
5677        cache.reloadTopology();
5678      }
5679    }
5680    catch (ADSContextException adce)
5681    {
5682      throw new ReplicationCliException(
5683          ERR_REPLICATION_READING_ADS.get(adce.getMessage()),
5684          ERROR_READING_ADS, adce);
5685    }
5686    catch (TopologyCacheException tce)
5687    {
5688      throw new ReplicationCliException(
5689          ERR_REPLICATION_READING_ADS.get(tce.getMessage()),
5690          ERROR_READING_TOPOLOGY_CACHE, tce);
5691    }
5692    if (!argParser.isInteractive())
5693    {
5694      // Inform the user of the potential errors that we found.
5695      Set<LocalizableMessage> messages = new LinkedHashSet<>();
5696      if (cache != null)
5697      {
5698        messages.addAll(cache.getErrorMessages());
5699      }
5700      if (!messages.isEmpty())
5701      {
5702        errPrintln(
5703            ERR_REPLICATION_READING_REGISTERED_SERVERS_WARNING.get(
5704                getMessageFromCollection(messages,
5705                    Constants.LINE_SEPARATOR)));
5706      }
5707    }
5708
5709    final boolean disableReplicationServer = server.isReplicationServer()
5710        && (uData.disableReplicationServer() || uData.disableAll());
5711    if (cache != null && disableReplicationServer)
5712    {
5713      String replicationServer = server.getReplicationServerHostPort();
5714      // Figure out if this is the last replication server for a given
5715      // topology (containing a different replica) or there will be only
5716      // another replication server left (single point of failure).
5717      Set<SuffixDescriptor> lastRepServer = new TreeSet<>(new SuffixComparator());
5718      Set<SuffixDescriptor> beforeLastRepServer = new TreeSet<>(new SuffixComparator());
5719
5720      for (SuffixDescriptor suffix : cache.getSuffixes())
5721      {
5722        if (isSchemaOrInternalAdminSuffix(suffix.getDN()))
5723        {
5724          // Do not display these suffixes.
5725          continue;
5726        }
5727
5728        Set<String> repServers = suffix.getReplicationServers();
5729        if (repServers.size() <= 2
5730            && containsIgnoreCase(repServers, replicationServer))
5731        {
5732          if (repServers.size() == 2)
5733          {
5734            beforeLastRepServer.add(suffix);
5735          }
5736          else
5737          {
5738            lastRepServer.add(suffix);
5739          }
5740        }
5741      }
5742
5743      // Inform the user
5744      if (!beforeLastRepServer.isEmpty())
5745      {
5746        Set<String> baseDNs = new LinkedHashSet<>();
5747        for (SuffixDescriptor suffix : beforeLastRepServer)
5748        {
5749          if (!isSchemaOrInternalAdminSuffix(suffix.getDN()))
5750          {
5751            // Do not display these suffixes.
5752            baseDNs.add(suffix.getDN());
5753          }
5754        }
5755        if (!baseDNs.isEmpty())
5756        {
5757          String arg = toSingleLine(baseDNs);
5758          if (!isInteractive())
5759          {
5760            println(INFO_DISABLE_REPLICATION_ONE_POINT_OF_FAILURE.get(arg));
5761          }
5762          else
5763          {
5764            LocalizableMessage msg = INFO_DISABLE_REPLICATION_ONE_POINT_OF_FAILURE_PROMPT.get(arg);
5765            if (!askConfirmation(msg, false))
5766            {
5767              throw new ReplicationCliException(ERR_REPLICATION_USER_CANCELLED.get(), USER_CANCELLED, null);
5768            }
5769          }
5770        }
5771      }
5772      if (!lastRepServer.isEmpty())
5773      {
5774        // Check that there are other replicas and that this message, really
5775        // makes sense to be displayed.
5776        Set<String> suffixArg = new LinkedHashSet<>();
5777        for (SuffixDescriptor suffix : lastRepServer)
5778        {
5779          boolean baseDNSpecified = false;
5780          for (String baseDN : uData.getBaseDNs())
5781          {
5782            if (!isSchemaOrInternalAdminSuffix(baseDN) && areDnsEqual(baseDN, suffix.getDN()))
5783            {
5784              baseDNSpecified = true;
5785              break;
5786            }
5787          }
5788          if (!baseDNSpecified)
5789          {
5790            Set<ServerDescriptor> servers = new TreeSet<>(new ServerComparator());
5791            for (ReplicaDescriptor replica : suffix.getReplicas())
5792            {
5793              servers.add(replica.getServer());
5794            }
5795            suffixArg.add(getSuffixDisplay(suffix.getDN(), servers));
5796          }
5797          else if (suffix.getReplicas().size() > 1)
5798          {
5799            // If there is just one replica, it is the one in this server.
5800            Set<ServerDescriptor> servers = new TreeSet<>(new ServerComparator());
5801            for (ReplicaDescriptor replica : suffix.getReplicas())
5802            {
5803              if (!replica.getServer().isSameServer(server))
5804              {
5805                servers.add(replica.getServer());
5806              }
5807            }
5808            if (!servers.isEmpty())
5809            {
5810              suffixArg.add(getSuffixDisplay(suffix.getDN(), servers));
5811            }
5812          }
5813        }
5814
5815        if (!suffixArg.isEmpty())
5816        {
5817          String arg = toSingleLine(suffixArg);
5818          if (!isInteractive())
5819          {
5820            println(INFO_DISABLE_REPLICATION_DISABLE_IN_REMOTE.get(arg));
5821          }
5822          else
5823          {
5824            LocalizableMessage msg = INFO_DISABLE_REPLICATION_DISABLE_IN_REMOTE_PROMPT.get(arg);
5825            if (!askConfirmation(msg, false))
5826            {
5827              throw new ReplicationCliException(ERR_REPLICATION_USER_CANCELLED.get(), USER_CANCELLED, null);
5828            }
5829          }
5830        }
5831      }
5832    }
5833
5834    /**
5835     * Try to figure out if we must explicitly disable replication on
5836     * cn=admin data and cn=schema.
5837     */
5838    boolean forceDisableSchema = false;
5839    boolean forceDisableADS = false;
5840    boolean schemaReplicated = false;
5841    boolean adsReplicated = false;
5842    boolean disableAllBaseDns = disableAllBaseDns(ctx, uData);
5843
5844    Collection<ReplicaDescriptor> replicas = getReplicas(ctx);
5845    for (ReplicaDescriptor rep : replicas)
5846    {
5847      String dn = rep.getSuffix().getDN();
5848      if (rep.isReplicated())
5849      {
5850        if (areDnsEqual(ADSContext.getAdministrationSuffixDN(), dn))
5851        {
5852          adsReplicated = true;
5853        }
5854        else if (areDnsEqual(Constants.SCHEMA_DN, dn))
5855        {
5856          schemaReplicated = true;
5857        }
5858      }
5859    }
5860
5861    if (disableAllBaseDns &&
5862        (disableReplicationServer || !server.isReplicationServer()))
5863    {
5864      // Unregister the server from the ADS if no other server has dependencies
5865      // with it (no replicated base DNs and no replication server).
5866      server.updateAdsPropertiesWithServerProperties();
5867      try
5868      {
5869        adsCtx.unregisterServer(server.getAdsProperties());
5870        // To be sure that the change gets propagated
5871        sleepCatchInterrupt(2000);
5872      }
5873      catch (ADSContextException adce)
5874      {
5875        logger.error(LocalizableMessage.raw("Error unregistering server: "+
5876            server.getAdsProperties(), adce));
5877        if (adce.getError() != ADSContextException.ErrorType.NOT_YET_REGISTERED)
5878        {
5879          throw new ReplicationCliException(
5880              ERR_REPLICATION_UPDATING_ADS.get(adce.getMessageObject()),
5881              ERROR_READING_ADS, adce);
5882        }
5883      }
5884    }
5885
5886    Set<String> suffixesToDisable = new HashSet<>();
5887    if (uData.disableAll())
5888    {
5889      for (ReplicaDescriptor replica : server.getReplicas())
5890      {
5891        if (replica.isReplicated())
5892        {
5893          suffixesToDisable.add(replica.getSuffix().getDN());
5894        }
5895      }
5896    }
5897    else
5898    {
5899      suffixesToDisable.addAll(uData.getBaseDNs());
5900
5901      if (disableAllBaseDns &&
5902          (disableReplicationServer || !server.isReplicationServer()))
5903      {
5904        forceDisableSchema = schemaReplicated;
5905        forceDisableADS = adsReplicated;
5906      }
5907      for (String dn : uData.getBaseDNs())
5908      {
5909        if (areDnsEqual(ADSContext.getAdministrationSuffixDN(), dn))
5910        {
5911          // The user already asked this to be explicitly disabled
5912          forceDisableADS = false;
5913        }
5914        else if (areDnsEqual(Constants.SCHEMA_DN, dn))
5915        {
5916          // The user already asked this to be explicitly disabled
5917          forceDisableSchema = false;
5918        }
5919      }
5920
5921      if (forceDisableSchema)
5922      {
5923        suffixesToDisable.add(Constants.SCHEMA_DN);
5924      }
5925      if (forceDisableADS)
5926      {
5927        suffixesToDisable.add(ADSContext.getAdministrationSuffixDN());
5928      }
5929    }
5930
5931    String replicationServerHostPort =
5932        server.isReplicationServer() ? server.getReplicationServerHostPort() : null;
5933
5934    for (String baseDN : suffixesToDisable)
5935    {
5936      try
5937      {
5938        deleteReplicationDomain(ctx, baseDN);
5939      }
5940      catch (OpenDsException ode)
5941      {
5942        LocalizableMessage msg = getMessageForDisableException(getHostPort(ctx), baseDN);
5943        throw new ReplicationCliException(msg,
5944            ERROR_DISABLING_REPLICATION_ON_BASEDN, ode);
5945      }
5946    }
5947
5948    boolean replicationServerDisabled = false;
5949    if (replicationServerHostPort != null && cache != null)
5950    {
5951      Set<ServerDescriptor> serversToUpdate = new LinkedHashSet<>();
5952      Set<String> baseDNsToUpdate = new HashSet<>(suffixesToDisable);
5953      for (String baseDN : baseDNsToUpdate)
5954      {
5955        SuffixDescriptor suffix = getSuffix(baseDN, cache, server);
5956        if (suffix != null)
5957        {
5958          for (ReplicaDescriptor replica : suffix.getReplicas())
5959          {
5960            serversToUpdate.add(replica.getServer());
5961          }
5962        }
5963      }
5964      if (disableReplicationServer)
5965      {
5966        // Find references in all servers.
5967        for (SuffixDescriptor suffix : cache.getSuffixes())
5968        {
5969          if (containsIgnoreCase(suffix.getReplicationServers(), replicationServerHostPort))
5970          {
5971            baseDNsToUpdate.add(suffix.getDN());
5972            for (ReplicaDescriptor replica : suffix.getReplicas())
5973            {
5974              serversToUpdate.add(replica.getServer());
5975            }
5976          }
5977        }
5978      }
5979      String bindDn = getBindDN(ctx);
5980      String pwd = getBindPassword(ctx);
5981      for (ServerDescriptor s : serversToUpdate)
5982      {
5983        removeReferencesInServer(s, replicationServerHostPort, bindDn, pwd,
5984            baseDNsToUpdate, disableReplicationServer,
5985            getPreferredConnections(ctx));
5986      }
5987
5988      if (disableReplicationServer)
5989      {
5990        // Disable replication server
5991        disableReplicationServer(ctx);
5992        replicationServerDisabled = true;
5993        // Wait to be sure that changes are taken into account and reset the
5994        // contents of the ADS.
5995        sleepCatchInterrupt(5000);
5996      }
5997    }
5998    if (disableReplicationServer && !replicationServerDisabled)
5999    {
6000      // This can happen if we could not retrieve the TopologyCache
6001      disableReplicationServer(ctx);
6002      replicationServerDisabled = true;
6003    }
6004
6005    if (uData.disableAll())
6006    {
6007      try
6008      {
6009        // Delete all contents from ADSContext.
6010        print(formatter.getFormattedWithPoints(
6011            INFO_REPLICATION_REMOVE_ADS_CONTENTS.get()));
6012        adsCtx.removeAdminData(false /* avoid self-disconnect */);
6013        print(formatter.getFormattedDone());
6014        println();
6015      }
6016      catch (ADSContextException adce)
6017      {
6018        logger.error(LocalizableMessage.raw("Error removing contents of cn=admin data: "+
6019            adce, adce));
6020        throw new ReplicationCliException(
6021            ERR_REPLICATION_UPDATING_ADS.get(adce.getMessageObject()),
6022            ERROR_UPDATING_ADS, adce);
6023      }
6024    }
6025    else if (disableAllBaseDns &&
6026        (disableReplicationServer || !server.isReplicationServer()))
6027    {
6028      // Unregister the servers from the ADS of the local server.
6029      try
6030      {
6031        for (Map<ADSContext.ServerProperty, Object> s : adsCtx.readServerRegistry())
6032        {
6033          adsCtx.unregisterServer(s);
6034        }
6035        // To be sure that the change gets propagated
6036        sleepCatchInterrupt(2000);
6037      }
6038      catch (ADSContextException adce)
6039      {
6040        // This is not critical, do not send an error
6041        logger.warn(LocalizableMessage.raw("Error unregistering server: "+
6042            server.getAdsProperties(), adce));
6043      }
6044    }
6045  }
6046
6047  private void addBaseDNs(TopologyCacheFilter filter, List<String> baseDNs)
6048  {
6049    for (String dn : baseDNs)
6050    {
6051      filter.addBaseDNToSearch(dn);
6052    }
6053  }
6054
6055  /**
6056   * Displays the replication status of the different base DNs in the servers
6057   * registered in the ADS.
6058   * @param ctx the connection to the server.
6059   * @param uData the StatusReplicationUserData object containing the required
6060   * parameters to update the configuration.
6061   * @throws ReplicationCliException if there is an error.
6062   */
6063  private void displayStatus(InitialLdapContext ctx,
6064      StatusReplicationUserData uData) throws ReplicationCliException
6065  {
6066    ADSContext adsCtx = new ADSContext(ctx);
6067
6068    boolean somethingDisplayed = false;
6069    TopologyCache cache;
6070    try
6071    {
6072      cache = new TopologyCache(adsCtx, getTrustManager(sourceServerCI), getConnectTimeout());
6073      cache.setPreferredConnections(getPreferredConnections(ctx));
6074      addBaseDNs(cache.getFilter(), uData.getBaseDNs());
6075      cache.reloadTopology();
6076    }
6077    catch (TopologyCacheException tce)
6078    {
6079      throw new ReplicationCliException(
6080          ERR_REPLICATION_READING_ADS.get(tce.getMessage()),
6081          ERROR_READING_TOPOLOGY_CACHE, tce);
6082    }
6083    if (mustPrintCommandBuilder())
6084    {
6085      printNewCommandBuilder(STATUS_REPLICATION_SUBCMD_NAME, uData);
6086    }
6087    if (!argParser.isInteractive())
6088    {
6089      // Inform the user of the potential errors that we found.
6090      Set<LocalizableMessage> messages = new LinkedHashSet<>(cache.getErrorMessages());
6091      if (!messages.isEmpty())
6092      {
6093        errPrintln(ERR_REPLICATION_STATUS_READING_REGISTERED_SERVERS.get(
6094            getMessageFromCollection(messages, Constants.LINE_SEPARATOR)));
6095      }
6096    }
6097
6098    List<String> userBaseDNs = uData.getBaseDNs();
6099    List<Set<ReplicaDescriptor>> replicaLists = new LinkedList<>();
6100
6101    boolean oneReplicated = false;
6102
6103    boolean displayAll = userBaseDNs.isEmpty();
6104    for (SuffixDescriptor suffix : cache.getSuffixes())
6105    {
6106      String dn = suffix.getDN();
6107
6108      // If no base DNs where specified display all the base DNs but the schema
6109      // and cn=admin data.
6110      boolean found = containsDN(userBaseDNs, dn) || (displayAll && !isSchemaOrInternalAdminSuffix(dn));
6111      if (found)
6112      {
6113        if (isAnyReplicated(suffix))
6114        {
6115          oneReplicated = true;
6116          replicaLists.add(suffix.getReplicas());
6117        }
6118        else
6119        {
6120          // Check if there are already some non replicated base DNs.
6121          found = false;
6122          for (Set<ReplicaDescriptor> replicas : replicaLists)
6123          {
6124            ReplicaDescriptor replica = replicas.iterator().next();
6125            if (!replica.isReplicated() &&
6126                areDnsEqual(dn, replica.getSuffix().getDN()))
6127            {
6128              replicas.addAll(suffix.getReplicas());
6129              found = true;
6130              break;
6131            }
6132          }
6133          if (!found)
6134          {
6135            replicaLists.add(suffix.getReplicas());
6136          }
6137        }
6138      }
6139    }
6140
6141    if (!oneReplicated && displayAll)
6142    {
6143      // Maybe there are some replication server configured...
6144      SortedSet<ServerDescriptor> rServers = new TreeSet<>(new ReplicationServerComparator());
6145      for (ServerDescriptor server : cache.getServers())
6146      {
6147        if (server.isReplicationServer())
6148        {
6149          rServers.add(server);
6150        }
6151      }
6152      if (!rServers.isEmpty())
6153      {
6154        displayStatus(rServers, uData.isScriptFriendly(), getPreferredConnections(ctx));
6155        somethingDisplayed = true;
6156      }
6157    }
6158
6159    if (!replicaLists.isEmpty())
6160    {
6161      List<Set<ReplicaDescriptor>> orderedReplicaLists = new LinkedList<>();
6162      for (Set<ReplicaDescriptor> replicas : replicaLists)
6163      {
6164        String dn1 = replicas.iterator().next().getSuffix().getDN();
6165        boolean inserted = false;
6166        for (int i=0; i<orderedReplicaLists.size() && !inserted; i++)
6167        {
6168          String dn2 =
6169            orderedReplicaLists.get(i).iterator().next().getSuffix().getDN();
6170          if (dn1.compareTo(dn2) < 0)
6171          {
6172            orderedReplicaLists.add(i, replicas);
6173            inserted = true;
6174          }
6175        }
6176        if (!inserted)
6177        {
6178          orderedReplicaLists.add(replicas);
6179        }
6180      }
6181      Set<ReplicaDescriptor> replicasWithNoReplicationServer = new HashSet<>();
6182      Set<ServerDescriptor> serversWithNoReplica = new HashSet<>();
6183      displayStatus(orderedReplicaLists, uData.isScriptFriendly(),
6184            getPreferredConnections(ctx),
6185            cache.getServers(),
6186            replicasWithNoReplicationServer, serversWithNoReplica);
6187      somethingDisplayed = true;
6188
6189      if (oneReplicated && !uData.isScriptFriendly())
6190      {
6191        println();
6192        print(INFO_REPLICATION_STATUS_REPLICATED_LEGEND.get());
6193
6194        if (!replicasWithNoReplicationServer.isEmpty() ||
6195            !serversWithNoReplica.isEmpty())
6196        {
6197          println();
6198          print(
6199              INFO_REPLICATION_STATUS_NOT_A_REPLICATION_SERVER_LEGEND.get());
6200
6201          println();
6202          print(
6203              INFO_REPLICATION_STATUS_NOT_A_REPLICATION_DOMAIN_LEGEND.get());
6204        }
6205        println();
6206        somethingDisplayed = true;
6207      }
6208    }
6209    if (!somethingDisplayed)
6210    {
6211      if (displayAll)
6212      {
6213        print(INFO_REPLICATION_STATUS_NO_REPLICATION_INFORMATION.get());
6214        println();
6215      }
6216      else
6217      {
6218        print(INFO_REPLICATION_STATUS_NO_BASEDNS.get());
6219        println();
6220      }
6221    }
6222  }
6223
6224  private boolean isAnyReplicated(SuffixDescriptor suffix)
6225  {
6226    for (ReplicaDescriptor replica : suffix.getReplicas())
6227    {
6228      if (replica.isReplicated())
6229      {
6230        return true;
6231      }
6232    }
6233    return false;
6234  }
6235
6236  /**
6237   * Displays the replication status of the replicas provided.  The code assumes
6238   * that all the replicas have the same baseDN and that if they are replicated
6239   * all the replicas are replicated with each other.
6240   * Note: the code assumes that all the objects come from the same read of the
6241   * topology cache.  So comparisons in terms of pointers can be made.
6242   * @param orderedReplicaLists the list of replicas that we are trying to
6243   * display.
6244   * @param scriptFriendly whether to display it on script-friendly mode or not.
6245   * @param cnx the preferred connections used to connect to the server.
6246   * @param servers all the servers configured in the topology.
6247   * @param replicasWithNoReplicationServer the set of replicas that will be
6248   * updated with all the replicas that have no replication server.
6249   * @param serversWithNoReplica the set of servers that will be updated with
6250   * all the servers that act as replication server in the topology but have
6251   * no replica.
6252   */
6253  private void displayStatus(
6254      List<Set<ReplicaDescriptor>> orderedReplicaLists,
6255      boolean scriptFriendly, Set<PreferredConnection> cnx,
6256      Set<ServerDescriptor> servers,
6257      Set<ReplicaDescriptor> replicasWithNoReplicationServer,
6258      Set<ServerDescriptor> serversWithNoReplica)
6259  {
6260    Set<ReplicaDescriptor> orderedReplicas = new LinkedHashSet<>();
6261    Set<String> hostPorts = new TreeSet<>();
6262    Set<ServerDescriptor> notAddedReplicationServers = new TreeSet<>(new ReplicationServerComparator());
6263    for (Set<ReplicaDescriptor> replicas : orderedReplicaLists)
6264    {
6265      for (ReplicaDescriptor replica : replicas)
6266      {
6267        hostPorts.add(getHostPort2(replica.getServer(), cnx));
6268      }
6269      for (String hostPort : hostPorts)
6270      {
6271        for (ReplicaDescriptor replica : replicas)
6272        {
6273          if (getHostPort2(replica.getServer(), cnx).equals(hostPort))
6274          {
6275            orderedReplicas.add(replica);
6276          }
6277        }
6278      }
6279      for (ServerDescriptor server : servers)
6280      {
6281        if (server.isReplicationServer() && isRepServerNotInDomain(replicas, server))
6282        {
6283          notAddedReplicationServers.add(server);
6284        }
6285      }
6286    }
6287
6288    /*
6289     * The table has the following columns:
6290     * - suffix DN;
6291     * - server;
6292     * - number of entries;
6293     * - replication enabled indicator;
6294     * - directory server instance ID;
6295     * - replication server;
6296     * - replication server ID;
6297     * - missing changes;
6298     * - age of the oldest change, and
6299     * - security enabled indicator.
6300     */
6301    TableBuilder tableBuilder = new TableBuilder();
6302
6303    /* Table headings. */
6304    tableBuilder.appendHeading(
6305      INFO_REPLICATION_STATUS_HEADER_SUFFIX_DN.get());
6306    tableBuilder.appendHeading(
6307      INFO_REPLICATION_STATUS_HEADER_SERVERPORT.get());
6308    tableBuilder.appendHeading(
6309      INFO_REPLICATION_STATUS_HEADER_NUMBER_ENTRIES.get());
6310    tableBuilder.appendHeading(
6311      INFO_REPLICATION_STATUS_HEADER_REPLICATION_ENABLED.get());
6312    tableBuilder.appendHeading(INFO_REPLICATION_STATUS_HEADER_DS_ID.get());
6313    tableBuilder.appendHeading(INFO_REPLICATION_STATUS_HEADER_RS_ID.get());
6314    tableBuilder.appendHeading(
6315        INFO_REPLICATION_STATUS_HEADER_REPLICATION_PORT.get());
6316    tableBuilder.appendHeading(
6317      INFO_REPLICATION_STATUS_HEADER_MISSING_CHANGES.get());
6318    tableBuilder.appendHeading(
6319      INFO_REPLICATION_STATUS_HEADER_AGE_OF_OLDEST_MISSING_CHANGE.get());
6320    tableBuilder.appendHeading(
6321      INFO_REPLICATION_STATUS_HEADER_SECURE.get());
6322
6323    /* Table data. */
6324    for (ReplicaDescriptor replica : orderedReplicas)
6325    {
6326      tableBuilder.startRow();
6327      // Suffix DN
6328      tableBuilder.appendCell(LocalizableMessage.raw(replica.getSuffix().getDN()));
6329      // Server port
6330      tableBuilder.appendCell(
6331          LocalizableMessage.raw(getHostPort2(replica.getServer(), cnx)));
6332      // Number of entries
6333      int nEntries = replica.getEntries();
6334      if (nEntries >= 0)
6335      {
6336        tableBuilder.appendCell(LocalizableMessage.raw(String.valueOf(nEntries)));
6337      }
6338      else
6339      {
6340        tableBuilder.appendCell(EMPTY_MSG);
6341      }
6342
6343      if (!replica.isReplicated())
6344      {
6345        tableBuilder.appendCell(EMPTY_MSG);
6346      }
6347      else
6348      {
6349        // Replication enabled
6350        tableBuilder.appendCell(
6351          LocalizableMessage.raw(Boolean.toString(replica.isReplicationEnabled())));
6352
6353        // DS instance ID
6354        tableBuilder.appendCell(
6355            LocalizableMessage.raw(Integer.toString(replica.getReplicationId())));
6356
6357        // RS ID and port.
6358        if (replica.getServer().isReplicationServer())
6359        {
6360          tableBuilder.appendCell(Integer.toString(replica.getServer()
6361              .getReplicationServerId()));
6362          tableBuilder.appendCell(LocalizableMessage.raw(String.valueOf(replica
6363              .getServer().getReplicationServerPort())));
6364        }
6365        else
6366        {
6367          if (scriptFriendly)
6368          {
6369            tableBuilder.appendCell(EMPTY_MSG);
6370          }
6371          else
6372          {
6373            tableBuilder.appendCell(
6374              INFO_REPLICATION_STATUS_NOT_A_REPLICATION_SERVER_SHORT.get());
6375          }
6376          tableBuilder.appendCell(EMPTY_MSG);
6377          replicasWithNoReplicationServer.add(replica);
6378        }
6379
6380        // Missing changes
6381        int missingChanges = replica.getMissingChanges();
6382        if (missingChanges >= 0)
6383        {
6384          tableBuilder.appendCell(LocalizableMessage.raw(String.valueOf(missingChanges)));
6385        }
6386        else
6387        {
6388          tableBuilder.appendCell(EMPTY_MSG);
6389        }
6390
6391        // Age of oldest missing change
6392        long ageOfOldestMissingChange = replica.getAgeOfOldestMissingChange();
6393        if (ageOfOldestMissingChange > 0)
6394        {
6395          Date date = new Date(ageOfOldestMissingChange);
6396          tableBuilder.appendCell(LocalizableMessage.raw(date.toString()));
6397        }
6398        else
6399        {
6400          tableBuilder.appendCell(EMPTY_MSG);
6401        }
6402
6403        // Secure
6404        if (!replica.getServer().isReplicationServer())
6405        {
6406          tableBuilder.appendCell(EMPTY_MSG);
6407        }
6408        else
6409        {
6410          tableBuilder.appendCell(
6411            LocalizableMessage.raw(Boolean.toString(
6412              replica.getServer().isReplicationSecure())));
6413        }
6414      }
6415    }
6416
6417    for (ServerDescriptor server : notAddedReplicationServers)
6418    {
6419      tableBuilder.startRow();
6420      serversWithNoReplica.add(server);
6421
6422      // Suffix DN
6423      tableBuilder.appendCell(EMPTY_MSG);
6424      // Server port
6425      tableBuilder.appendCell(LocalizableMessage.raw(getHostPort2(server, cnx)));
6426      // Number of entries
6427      if (scriptFriendly)
6428      {
6429        tableBuilder.appendCell(EMPTY_MSG);
6430      }
6431      else
6432      {
6433        tableBuilder.appendCell(
6434          INFO_REPLICATION_STATUS_NOT_A_REPLICATION_DOMAIN_SHORT.get());
6435      }
6436
6437      // Replication enabled
6438      tableBuilder.appendCell(Boolean.toString(true));
6439
6440      // DS ID
6441      tableBuilder.appendCell(EMPTY_MSG);
6442
6443      // RS ID
6444      tableBuilder.appendCell(
6445        LocalizableMessage.raw(Integer.toString(server.getReplicationServerId())));
6446
6447      // Replication port
6448      int replicationPort = server.getReplicationServerPort();
6449      if (replicationPort >= 0)
6450      {
6451        tableBuilder.appendCell(
6452          LocalizableMessage.raw(String.valueOf(replicationPort)));
6453      }
6454      else
6455      {
6456        tableBuilder.appendCell(EMPTY_MSG);
6457      }
6458
6459      // Missing changes
6460      tableBuilder.appendCell(EMPTY_MSG);
6461
6462      // Age of oldest change
6463      tableBuilder.appendCell(EMPTY_MSG);
6464
6465      // Secure
6466      tableBuilder.appendCell(
6467        LocalizableMessage.raw(Boolean.toString(server.isReplicationSecure())));
6468    }
6469
6470    TablePrinter printer;
6471    PrintStream out = getOutputStream();
6472    if (scriptFriendly)
6473    {
6474      printer = new TabSeparatedTablePrinter(out);
6475    }
6476    else
6477    {
6478      final TextTablePrinter ttPrinter = new TextTablePrinter(out);
6479      ttPrinter.setColumnSeparator(LIST_TABLE_SEPARATOR);
6480      printer = ttPrinter;
6481    }
6482    tableBuilder.print(printer);
6483  }
6484
6485  private boolean isRepServerNotInDomain(Set<ReplicaDescriptor> replicas, ServerDescriptor server)
6486  {
6487    boolean isDomain = false;
6488    boolean isRepServer = false;
6489    String replicationServer = server.getReplicationServerHostPort();
6490    for (ReplicaDescriptor replica : replicas)
6491    {
6492      if (!isRepServer)
6493      {
6494        isRepServer = containsIgnoreCase(replica.getReplicationServers(), replicationServer);
6495      }
6496      if (replica.getServer() == server)
6497      {
6498        isDomain = true;
6499      }
6500      if (isDomain && isRepServer)
6501      {
6502        break;
6503      }
6504    }
6505    return !isDomain && isRepServer;
6506  }
6507
6508  /**
6509   * Displays the replication status of the replication servers provided.  The
6510   * code assumes that all the servers have a replication server and that there
6511   * are associated with no replication domain.
6512   * @param servers the servers
6513   * @param cnx the preferred connections used to connect to the server.
6514   * @param scriptFriendly wheter to display it on script-friendly mode or not.
6515   */
6516  private void displayStatus(Set<ServerDescriptor> servers,
6517      boolean scriptFriendly, Set<PreferredConnection> cnx)
6518  {
6519    TableBuilder tableBuilder = new TableBuilder();
6520    tableBuilder.appendHeading(INFO_REPLICATION_STATUS_HEADER_SERVERPORT.get());
6521    tableBuilder.appendHeading(
6522      INFO_REPLICATION_STATUS_HEADER_REPLICATION_PORT.get());
6523    tableBuilder.appendHeading(INFO_REPLICATION_STATUS_HEADER_SECURE.get());
6524
6525    for (ServerDescriptor server : servers)
6526    {
6527      tableBuilder.startRow();
6528      // Server port
6529      tableBuilder.appendCell(LocalizableMessage.raw(getHostPort2(server, cnx)));
6530      // Replication port
6531      int replicationPort = server.getReplicationServerPort();
6532      if (replicationPort >= 0)
6533      {
6534        tableBuilder.appendCell(LocalizableMessage.raw(String.valueOf(replicationPort)));
6535      }
6536      else
6537      {
6538        tableBuilder.appendCell(EMPTY_MSG);
6539      }
6540      // Secure
6541      tableBuilder.appendCell(LocalizableMessage.raw(Boolean.toString(server.isReplicationSecure())));
6542    }
6543
6544    PrintStream out = getOutputStream();
6545    TablePrinter printer;
6546
6547    if (scriptFriendly)
6548    {
6549      print(INFO_REPLICATION_STATUS_INDEPENDENT_REPLICATION_SERVERS.get());
6550      println();
6551      printer = new TabSeparatedTablePrinter(out);
6552    }
6553    else
6554    {
6555      LocalizableMessage msg = INFO_REPLICATION_STATUS_INDEPENDENT_REPLICATION_SERVERS.get();
6556      print(msg);
6557      println();
6558      int length = msg.length();
6559      StringBuilder buf = new StringBuilder();
6560      for (int i=0; i<length; i++)
6561      {
6562        buf.append("=");
6563      }
6564      print(LocalizableMessage.raw(buf.toString()));
6565      println();
6566
6567      printer = new TextTablePrinter(getOutputStream());
6568      ((TextTablePrinter)printer).setColumnSeparator(
6569        LIST_TABLE_SEPARATOR);
6570    }
6571    tableBuilder.print(printer);
6572  }
6573
6574  /**
6575   * Retrieves all the replication servers for a given baseDN.  The
6576   * ServerDescriptor is used to identify the server where the suffix is
6577   * defined and it cannot be null.  The TopologyCache is used to retrieve
6578   * replication servers defined in other replicas but not in the one we
6579   * get in the ServerDescriptor.
6580   * @param baseDN the base DN.
6581   * @param cache the TopologyCache (might be null).
6582   * @param server the ServerDescriptor.
6583   * @return a Set containing the replication servers currently being used
6584   * to replicate the baseDN defined in the server described by the
6585   * ServerDescriptor.
6586   */
6587  private Set<String> getReplicationServers(String baseDN,
6588      TopologyCache cache, ServerDescriptor server)
6589  {
6590    Set<String> servers = getAllReplicationServers(baseDN, server);
6591    if (cache != null)
6592    {
6593      for (SuffixDescriptor suffix : cache.getSuffixes())
6594      {
6595        if (areDnsEqual(suffix.getDN(), baseDN))
6596        {
6597          Set<String> s = suffix.getReplicationServers();
6598          // Test that at least we share one of the replication servers.
6599          // If we do: we are dealing with the same replication topology
6600          // (we must consider the case of disjoint replication topologies
6601          // replicating the same base DN).
6602          Set<String> copy = new HashSet<>(s);
6603          copy.retainAll(servers);
6604          if (!copy.isEmpty())
6605          {
6606            servers.addAll(s);
6607            break;
6608          }
6609          else if (server.isReplicationServer()
6610              && containsIgnoreCase(s, server.getReplicationServerHostPort()))
6611          {
6612            // this server is acting as replication server with no domain.
6613            servers.addAll(s);
6614            break;
6615          }
6616        }
6617      }
6618    }
6619    return servers;
6620  }
6621
6622  private boolean containsIgnoreCase(Set<String> col, String toFind)
6623  {
6624    for (String s : col)
6625    {
6626      if (s.equalsIgnoreCase(toFind))
6627      {
6628        return true;
6629      }
6630    }
6631    return false;
6632  }
6633
6634  private String findIgnoreCase(Set<String> col, String toFind)
6635  {
6636    for (String s : col)
6637    {
6638      if (toFind.equalsIgnoreCase(s))
6639      {
6640        return s;
6641      }
6642    }
6643    return null;
6644  }
6645
6646  /**
6647   * Retrieves the suffix in the TopologyCache for a given baseDN.  The
6648   * ServerDescriptor is used to identify the server where the suffix is
6649   * defined.
6650   * @param baseDN the base DN.
6651   * @param cache the TopologyCache.
6652   * @param server the ServerDescriptor.
6653   * @return the suffix in the TopologyCache for a given baseDN.
6654   */
6655  private SuffixDescriptor getSuffix(String baseDN, TopologyCache cache,
6656      ServerDescriptor server)
6657  {
6658    String replicationServer = null;
6659    if (server.isReplicationServer())
6660    {
6661      replicationServer = server.getReplicationServerHostPort();
6662    }
6663
6664    SuffixDescriptor returnValue = null;
6665    Set<String> servers = getAllReplicationServers(baseDN, server);
6666    for (SuffixDescriptor suffix : cache.getSuffixes())
6667    {
6668      if (areDnsEqual(suffix.getDN(), baseDN))
6669      {
6670        Set<String> s = suffix.getReplicationServers();
6671        // Test that at least we share one of the replication servers.
6672        // If we do: we are dealing with the same replication topology
6673        // (we must consider the case of disjoint replication topologies
6674        // replicating the same base DN).
6675        HashSet<String> copy = new HashSet<>(s);
6676        copy.retainAll(servers);
6677        if (!copy.isEmpty())
6678        {
6679          return suffix;
6680        }
6681        else if (replicationServer != null && containsIgnoreCase(s, replicationServer))
6682        {
6683          returnValue = suffix;
6684        }
6685      }
6686    }
6687    return returnValue;
6688  }
6689
6690  private Set<String> getAllReplicationServers(String baseDN, ServerDescriptor server)
6691  {
6692    Set<String> servers = new LinkedHashSet<>();
6693    for (ReplicaDescriptor replica : server.getReplicas())
6694    {
6695      if (areDnsEqual(replica.getSuffix().getDN(), baseDN))
6696      {
6697        servers.addAll(replica.getReplicationServers());
6698        break;
6699      }
6700    }
6701    return servers;
6702  }
6703
6704  /**
6705   * Retrieves all the replication domain IDs for a given baseDN in the
6706   * ServerDescriptor.
6707   * @param baseDN the base DN.
6708   * @param server the ServerDescriptor.
6709   * @return a Set containing the replication domain IDs for a given baseDN in
6710   * the ServerDescriptor.
6711   */
6712  private Set<Integer> getReplicationDomainIds(String baseDN,
6713      ServerDescriptor server)
6714  {
6715    Set<Integer> ids = new HashSet<>();
6716    for (ReplicaDescriptor replica : server.getReplicas())
6717    {
6718      if (replica.isReplicated()
6719          && areDnsEqual(replica.getSuffix().getDN(), baseDN))
6720      {
6721        ids.add(replica.getReplicationId());
6722        break;
6723      }
6724    }
6725    return ids;
6726  }
6727
6728  /**
6729   * Configures the server to which the provided InitialLdapContext is connected
6730   * as a replication server.  The replication server listens in the provided
6731   * port.
6732   * @param ctx the context connected to the server that we want to configure.
6733   * @param replicationPort the replication port of the replication server.
6734   * @param useSecureReplication whether to have encrypted communication with
6735   * the replication port or not.
6736   * @param replicationServers the list of replication servers to which the
6737   * replication server will communicate with.
6738   * @param usedReplicationServerIds the set of replication server IDs that
6739   * are already in use.  The set will be updated with the replication ID
6740   * that will be used by the newly configured replication server.
6741   * @throws OpenDsException if there is an error updating the configuration.
6742   */
6743  private void configureAsReplicationServer(InitialLdapContext ctx,
6744      int replicationPort, boolean useSecureReplication,
6745      Set<String> replicationServers,
6746      Set<Integer> usedReplicationServerIds) throws OpenDsException
6747  {
6748    print(formatter.getFormattedWithPoints(
6749        INFO_REPLICATION_ENABLE_CONFIGURING_REPLICATION_SERVER.get(getHostPort(ctx))));
6750
6751    ManagementContext mCtx = LDAPManagementContext.createFromContext(
6752        JNDIDirContextAdaptor.adapt(ctx));
6753    RootCfgClient root = mCtx.getRootConfiguration();
6754
6755    /* Configure Synchronization plugin. */
6756    ReplicationSynchronizationProviderCfgClient sync = null;
6757    try
6758    {
6759      sync = (ReplicationSynchronizationProviderCfgClient)
6760      root.getSynchronizationProvider("Multimaster Synchronization");
6761    }
6762    catch (ManagedObjectNotFoundException monfe)
6763    {
6764      logger.info(LocalizableMessage.raw("Synchronization server does not exist in " + getHostPort(ctx)));
6765    }
6766    if (sync == null)
6767    {
6768      ReplicationSynchronizationProviderCfgDefn provider =
6769        ReplicationSynchronizationProviderCfgDefn.getInstance();
6770      sync = root.createSynchronizationProvider(provider,
6771          "Multimaster Synchronization",
6772          new ArrayList<PropertyException>());
6773      sync.setJavaClass(
6774          org.opends.server.replication.plugin.MultimasterReplication.class.
6775          getName());
6776      sync.setEnabled(Boolean.TRUE);
6777    }
6778    else if (!sync.isEnabled())
6779    {
6780      sync.setEnabled(Boolean.TRUE);
6781    }
6782    sync.commit();
6783
6784    /* Configure the replication server. */
6785    ReplicationServerCfgClient replicationServer;
6786
6787    boolean mustCommit = false;
6788
6789    if (!sync.hasReplicationServer())
6790    {
6791      CryptoManagerCfgClient crypto = root.getCryptoManager();
6792      if (useSecureReplication != crypto.isSSLEncryption())
6793      {
6794        crypto.setSSLEncryption(useSecureReplication);
6795        crypto.commit();
6796      }
6797      int id = InstallerHelper.getReplicationId(usedReplicationServerIds);
6798      usedReplicationServerIds.add(id);
6799      replicationServer = sync.createReplicationServer(
6800          ReplicationServerCfgDefn.getInstance(),
6801          new ArrayList<PropertyException>());
6802      replicationServer.setReplicationServerId(id);
6803      replicationServer.setReplicationPort(replicationPort);
6804      replicationServer.setReplicationServer(replicationServers);
6805      mustCommit = true;
6806    }
6807    else
6808    {
6809      replicationServer = sync.getReplicationServer();
6810      usedReplicationServerIds.add(
6811          replicationServer.getReplicationServerId());
6812      Set<String> servers = replicationServer.getReplicationServer();
6813      if (servers == null)
6814      {
6815        replicationServer.setReplicationServer(replicationServers);
6816        mustCommit = true;
6817      }
6818      else if (!areReplicationServersEqual(servers, replicationServers))
6819      {
6820        replicationServer.setReplicationServer(
6821            mergeReplicationServers(replicationServers, servers));
6822        mustCommit = true;
6823      }
6824    }
6825    if (mustCommit)
6826    {
6827      replicationServer.commit();
6828    }
6829
6830    print(formatter.getFormattedDone());
6831    println();
6832  }
6833
6834  /**
6835   * Updates the configuration of the replication server with the list of
6836   * replication servers provided.
6837   * @param ctx the context connected to the server that we want to update.
6838   * @param replicationServers the list of replication servers to which the
6839   * replication server will communicate with.
6840   * @throws OpenDsException if there is an error updating the configuration.
6841   */
6842  private void updateReplicationServer(InitialLdapContext ctx,
6843      Set<String> replicationServers) throws OpenDsException
6844  {
6845    print(formatter.getFormattedWithPoints(
6846        INFO_REPLICATION_ENABLE_UPDATING_REPLICATION_SERVER.get(getHostPort(ctx))));
6847
6848    ManagementContext mCtx = LDAPManagementContext.createFromContext(
6849        JNDIDirContextAdaptor.adapt(ctx));
6850    RootCfgClient root = mCtx.getRootConfiguration();
6851
6852    ReplicationSynchronizationProviderCfgClient sync =
6853      (ReplicationSynchronizationProviderCfgClient)
6854    root.getSynchronizationProvider("Multimaster Synchronization");
6855    boolean mustCommit = false;
6856    ReplicationServerCfgClient replicationServer = sync.getReplicationServer();
6857    Set<String> servers = replicationServer.getReplicationServer();
6858    if (servers == null)
6859    {
6860      replicationServer.setReplicationServer(replicationServers);
6861      mustCommit = true;
6862    }
6863    else if (!areReplicationServersEqual(servers, replicationServers))
6864    {
6865      replicationServers.addAll(servers);
6866      replicationServer.setReplicationServer(
6867          mergeReplicationServers(replicationServers, servers));
6868      mustCommit = true;
6869    }
6870    if (mustCommit)
6871    {
6872      replicationServer.commit();
6873    }
6874
6875    print(formatter.getFormattedDone());
6876    println();
6877  }
6878
6879  /**
6880   * Returns a Set containing all the replication server ids found in the
6881   * servers of a given TopologyCache object.
6882   * @param cache the TopologyCache object to use.
6883   * @return a Set containing all the replication server ids found in a given
6884   * TopologyCache object.
6885   */
6886  private Set<Integer> getReplicationServerIds(TopologyCache cache)
6887  {
6888    Set<Integer> ids = new HashSet<>();
6889    for (ServerDescriptor server : cache.getServers())
6890    {
6891      if (server.isReplicationServer())
6892      {
6893        ids.add(server.getReplicationServerId());
6894      }
6895    }
6896    return ids;
6897  }
6898
6899  /**
6900   * Configures a replication domain for a given base DN in the server to which
6901   * the provided InitialLdapContext is connected.
6902   * @param ctx the context connected to the server that we want to configure.
6903   * @param baseDN the base DN of the replication domain to configure.
6904   * @param replicationServers the list of replication servers to which the
6905   * replication domain will communicate with.
6906   * @param usedReplicationDomainIds the set of replication domain IDs that
6907   * are already in use.  The set will be updated with the replication ID
6908   * that will be used by the newly configured replication server.
6909   * @throws OpenDsException if there is an error updating the configuration.
6910   */
6911  private void configureToReplicateBaseDN(InitialLdapContext ctx,
6912      String baseDN,
6913      Set<String> replicationServers,
6914      Set<Integer> usedReplicationDomainIds) throws OpenDsException
6915  {
6916    boolean userSpecifiedAdminBaseDN = false;
6917    List<String> l = argParser.getBaseDNs();
6918    if (l != null)
6919    {
6920      userSpecifiedAdminBaseDN = containsDN(l, ADSContext.getAdministrationSuffixDN());
6921    }
6922    if (!userSpecifiedAdminBaseDN
6923        && areDnsEqual(baseDN, ADSContext.getAdministrationSuffixDN()))
6924    {
6925      print(formatter.getFormattedWithPoints(
6926          INFO_REPLICATION_ENABLE_CONFIGURING_ADS.get(getHostPort(ctx))));
6927    }
6928    else
6929    {
6930      print(formatter.getFormattedWithPoints(
6931          INFO_REPLICATION_ENABLE_CONFIGURING_BASEDN.get(baseDN, getHostPort(ctx))));
6932    }
6933    ManagementContext mCtx = LDAPManagementContext.createFromContext(
6934        JNDIDirContextAdaptor.adapt(ctx));
6935    RootCfgClient root = mCtx.getRootConfiguration();
6936
6937    ReplicationSynchronizationProviderCfgClient sync =
6938      (ReplicationSynchronizationProviderCfgClient)
6939      root.getSynchronizationProvider("Multimaster Synchronization");
6940
6941    String[] domainNames = sync.listReplicationDomains();
6942    if (domainNames == null)
6943    {
6944      domainNames = new String[]{};
6945    }
6946    ReplicationDomainCfgClient[] domains =
6947      new ReplicationDomainCfgClient[domainNames.length];
6948    for (int i=0; i<domains.length; i++)
6949    {
6950      domains[i] = sync.getReplicationDomain(domainNames[i]);
6951    }
6952    ReplicationDomainCfgClient domain = null;
6953    for (ReplicationDomainCfgClient domain2 : domains)
6954    {
6955      if (areDnsEqual(baseDN, domain2.getBaseDN().toString()))
6956      {
6957        domain = domain2;
6958        break;
6959      }
6960    }
6961    boolean mustCommit = false;
6962    if (domain == null)
6963    {
6964      int domainId = InstallerHelper.getReplicationId(usedReplicationDomainIds);
6965      usedReplicationDomainIds.add(domainId);
6966      String domainName =
6967          InstallerHelper.getDomainName(domainNames, domainId, baseDN);
6968      domain = sync.createReplicationDomain(
6969          ReplicationDomainCfgDefn.getInstance(), domainName,
6970          new ArrayList<PropertyException>());
6971      domain.setServerId(domainId);
6972      domain.setBaseDN(DN.valueOf(baseDN));
6973      domain.setReplicationServer(replicationServers);
6974      mustCommit = true;
6975    }
6976    else
6977    {
6978      Set<String> servers = domain.getReplicationServer();
6979      if (servers == null)
6980      {
6981        domain.setReplicationServer(null);
6982        mustCommit = true;
6983      }
6984      else if (!areReplicationServersEqual(servers, replicationServers))
6985      {
6986        domain.setReplicationServer(mergeReplicationServers(replicationServers,
6987            servers));
6988        mustCommit = true;
6989      }
6990    }
6991
6992    if (mustCommit)
6993    {
6994      domain.commit();
6995    }
6996
6997    print(formatter.getFormattedDone());
6998    println();
6999  }
7000
7001  /**
7002   * Configures the baseDN to replicate in all the Replicas found in a Topology
7003   * Cache that are replicated with the Replica of the same base DN in the
7004   * provided ServerDescriptor object.
7005   * @param baseDN the base DN to replicate.
7006   * @param repServers the replication servers to be defined in the domain.
7007   * @param usedIds the replication domain Ids already used.  This Set is
7008   * updated with the new domains that are used.
7009   * @param cache the TopologyCache used to retrieve the different defined
7010   * replicas.
7011   * @param server the ServerDescriptor that is used to identify the
7012   * replication topology that we are interested at (we only update the replicas
7013   * that are already replicated with this server).
7014   * @param alreadyConfiguredServers the list of already configured servers.  If
7015   * a server is in this list no updates are performed to the domain.
7016   * @param alreadyConfiguredReplicationServers the list of already configured
7017   * servers.  If a server is in this list no updates are performed to the
7018   * replication server.
7019   * @throws ReplicationCliException if something goes wrong.
7020   */
7021  private void configureToReplicateBaseDN(String baseDN,
7022      Set<String> repServers, Set<Integer> usedIds,
7023      TopologyCache cache, ServerDescriptor server,
7024      Set<String> alreadyConfiguredServers, Set<String> allRepServers,
7025      Set<String> alreadyConfiguredReplicationServers)
7026  throws ReplicationCliException
7027  {
7028    logger.info(LocalizableMessage.raw("Configuring base DN '"+baseDN+
7029        "' the replication servers are "+repServers));
7030    Set<ServerDescriptor> serversToConfigureDomain = new HashSet<>();
7031    Set<ServerDescriptor> replicationServersToConfigure = new HashSet<>();
7032    SuffixDescriptor suffix = getSuffix(baseDN, cache, server);
7033    if (suffix != null)
7034    {
7035      for (ReplicaDescriptor replica: suffix.getReplicas())
7036      {
7037        ServerDescriptor s = replica.getServer();
7038        if (!alreadyConfiguredServers.contains(s.getId()))
7039        {
7040          serversToConfigureDomain.add(s);
7041        }
7042      }
7043    }
7044    // Now check the replication servers.
7045    for (ServerDescriptor s : cache.getServers())
7046    {
7047      if (s.isReplicationServer()
7048          && !alreadyConfiguredReplicationServers.contains(s.getId())
7049          // Check if it is part of the replication topology
7050          && containsIgnoreCase(repServers, s.getReplicationServerHostPort()))
7051      {
7052        replicationServersToConfigure.add(s);
7053      }
7054    }
7055
7056    Set<ServerDescriptor> allServers = new HashSet<>(serversToConfigureDomain);
7057    allServers.addAll(replicationServersToConfigure);
7058
7059    for (ServerDescriptor s : allServers)
7060    {
7061      logger.info(LocalizableMessage.raw("Configuring server "+server.getHostPort(true)));
7062      InitialLdapContext ctx = null;
7063      try
7064      {
7065        ctx = getDirContextForServer(cache, s);
7066        if (serversToConfigureDomain.contains(s))
7067        {
7068          configureToReplicateBaseDN(ctx, baseDN, repServers, usedIds);
7069        }
7070        if (replicationServersToConfigure.contains(s))
7071        {
7072          updateReplicationServer(ctx, allRepServers);
7073        }
7074      }
7075      catch (NamingException ne)
7076      {
7077        String hostPort = getHostPort2(s, cache.getPreferredConnections());
7078        LocalizableMessage msg = getMessageForException(ne, hostPort);
7079        throw new ReplicationCliException(msg, ERROR_CONNECTING, ne);
7080      }
7081      catch (OpenDsException ode)
7082      {
7083        String hostPort = getHostPort2(s, cache.getPreferredConnections());
7084        LocalizableMessage msg = getMessageForEnableException(hostPort, baseDN);
7085        throw new ReplicationCliException(msg,
7086            ERROR_ENABLING_REPLICATION_ON_BASEDN, ode);
7087      }
7088      finally
7089      {
7090        close(ctx);
7091      }
7092      alreadyConfiguredServers.add(s.getId());
7093      alreadyConfiguredReplicationServers.add(s.getId());
7094    }
7095  }
7096
7097  /**
7098   * Returns the Map of properties to be used to update the ADS.
7099   * This map uses the data provided by the user.
7100   * @return the Map of properties to be used to update the ADS.
7101   * This map uses the data provided by the user
7102   */
7103  private Map<ADSContext.AdministratorProperty, Object>
7104  getAdministratorProperties(ReplicationUserData uData)
7105  {
7106    Map<ADSContext.AdministratorProperty, Object> adminProperties = new HashMap<>();
7107    adminProperties.put(ADSContext.AdministratorProperty.UID, uData.getAdminUid());
7108    adminProperties.put(ADSContext.AdministratorProperty.PASSWORD, uData.getAdminPwd());
7109    adminProperties.put(ADSContext.AdministratorProperty.DESCRIPTION,
7110        INFO_GLOBAL_ADMINISTRATOR_DESCRIPTION.get().toString());
7111    return adminProperties;
7112  }
7113
7114  private void initializeSuffix(String baseDN, InitialLdapContext ctxSource,
7115      InitialLdapContext ctxDestination, boolean displayProgress)
7116  throws ReplicationCliException
7117  {
7118    int replicationId = -1;
7119    try
7120    {
7121      TopologyCacheFilter filter = new TopologyCacheFilter();
7122      filter.setSearchMonitoringInformation(false);
7123      filter.addBaseDNToSearch(baseDN);
7124      ServerDescriptor source = ServerDescriptor.createStandalone(ctxSource, filter);
7125      for (ReplicaDescriptor replica : source.getReplicas())
7126      {
7127        if (areDnsEqual(replica.getSuffix().getDN(), baseDN))
7128        {
7129          replicationId = replica.getReplicationId();
7130          break;
7131        }
7132      }
7133    }
7134    catch (NamingException ne)
7135    {
7136      String hostPort = getHostPort(ctxSource);
7137      LocalizableMessage msg = getMessageForException(ne, hostPort);
7138      throw new ReplicationCliException(msg, ERROR_READING_CONFIGURATION, ne);
7139    }
7140
7141    if (replicationId == -1)
7142    {
7143      throw new ReplicationCliException(
7144          ERR_INITIALIZING_REPLICATIONID_NOT_FOUND.get(getHostPort(ctxSource), baseDN),
7145          REPLICATIONID_NOT_FOUND, null);
7146    }
7147
7148    OfflineInstaller installer = new OfflineInstaller();
7149    installer.setProgressMessageFormatter(formatter);
7150    installer.addProgressUpdateListener(new ProgressUpdateListener()
7151    {
7152      @Override
7153      public void progressUpdate(ProgressUpdateEvent ev)
7154      {
7155        LocalizableMessage newLogDetails = ev.getNewLogs();
7156        if (newLogDetails != null && !"".equals(newLogDetails.toString().trim()))
7157        {
7158          print(newLogDetails);
7159          println();
7160        }
7161      }
7162    });
7163    int nTries = 5;
7164    boolean initDone = false;
7165    while (!initDone)
7166    {
7167      try
7168      {
7169        installer.initializeSuffix(ctxDestination, replicationId, baseDN, displayProgress, getHostPort(ctxSource));
7170        initDone = true;
7171      }
7172      catch (PeerNotFoundException pnfe)
7173      {
7174        logger.info(LocalizableMessage.raw("Peer could not be found"));
7175        if (nTries == 1)
7176        {
7177          throw new ReplicationCliException(
7178              ERR_REPLICATION_INITIALIZING_TRIES_COMPLETED.get(
7179                  pnfe.getMessageObject()), INITIALIZING_TRIES_COMPLETED, pnfe);
7180        }
7181        sleepCatchInterrupt((5 - nTries) * 3000);
7182      }
7183      catch (ApplicationException ae)
7184      {
7185        throw new ReplicationCliException(ae.getMessageObject(),
7186            ERROR_INITIALIZING_BASEDN_GENERIC, ae);
7187      }
7188      nTries--;
7189    }
7190  }
7191
7192  /**
7193   * Initializes all the replicas in the topology with the contents of a
7194   * given replica.
7195   * @param ctx the connection to the server where the source replica of the
7196   * initialization is.
7197   * @param baseDN the dn of the suffix.
7198   * @param displayProgress whether we want to display progress or not.
7199   * @throws ReplicationCliException if an unexpected error occurs.
7200   */
7201  public void initializeAllSuffix(String baseDN, InitialLdapContext ctx,
7202  boolean displayProgress) throws ReplicationCliException
7203  {
7204    if (argParser == null)
7205    {
7206      try
7207      {
7208        createArgumenParser();
7209      }
7210      catch (ArgumentException ae)
7211      {
7212        throw new RuntimeException("Error creating argument parser: "+ae, ae);
7213      }
7214    }
7215    int nTries = 5;
7216    boolean initDone = false;
7217    while (!initDone)
7218    {
7219      try
7220      {
7221        initializeAllSuffixTry(baseDN, ctx, displayProgress);
7222        postPreExternalInitialization(baseDN, ctx, false);
7223        initDone = true;
7224      }
7225      catch (PeerNotFoundException pnfe)
7226      {
7227        logger.info(LocalizableMessage.raw("Peer could not be found"));
7228        if (nTries == 1)
7229        {
7230          throw new ReplicationCliException(
7231              ERR_REPLICATION_INITIALIZING_TRIES_COMPLETED.get(
7232                  pnfe.getMessageObject()), INITIALIZING_TRIES_COMPLETED, pnfe);
7233        }
7234        sleepCatchInterrupt((5 - nTries) * 3000);
7235      }
7236      catch (ClientException ae)
7237      {
7238        throw new ReplicationCliException(ae.getMessageObject(),
7239            ERROR_INITIALIZING_BASEDN_GENERIC, ae);
7240      }
7241      nTries--;
7242    }
7243  }
7244
7245  /**
7246   * Launches the pre external initialization operation using the provided
7247   * connection on a given base DN.
7248   * @param baseDN the base DN that we want to reset.
7249   * @param ctx the connection to the server.
7250   * @throws ReplicationCliException if there is an error performing the
7251   * operation.
7252   */
7253  private void preExternalInitialization(String baseDN, InitialLdapContext ctx) throws ReplicationCliException
7254  {
7255    postPreExternalInitialization(baseDN, ctx, true);
7256  }
7257
7258  /**
7259   * Launches the post external initialization operation using the provided
7260   * connection on a given base DN required for replication to work.
7261   * @param baseDN the base DN that we want to reset.
7262   * @param ctx the connection to the server.
7263   * @throws ReplicationCliException if there is an error performing the
7264   * operation.
7265   */
7266  private void postExternalInitialization(String baseDN, InitialLdapContext ctx) throws ReplicationCliException
7267  {
7268    postPreExternalInitialization(baseDN, ctx, false);
7269  }
7270
7271  /**
7272   * Launches the pre or post external initialization operation using the
7273   * provided connection on a given base DN.
7274   * @param baseDN the base DN that we want to reset.
7275   * @param ctx the connection to the server.
7276   * @param isPre whether this is the pre operation or the post operation.
7277   * @throws ReplicationCliException if there is an error performing the
7278   * operation.
7279   */
7280  private void postPreExternalInitialization(String baseDN,
7281      InitialLdapContext ctx, boolean isPre) throws ReplicationCliException
7282  {
7283    boolean isOver = false;
7284    String dn = null;
7285    Map<String, String> attrMap = new TreeMap<>();
7286    if (isPre)
7287    {
7288      attrMap.put("ds-task-reset-generation-id-new-value", "-1");
7289    }
7290    attrMap.put("ds-task-reset-generation-id-domain-base-dn", baseDN);
7291
7292    try {
7293      dn = createServerTask(ctx, "ds-task-reset-generation-id", "org.opends.server.tasks.SetGenerationIdTask",
7294          "dsreplication-reset-generation-id", attrMap);
7295    }
7296    catch (NamingException ne)
7297    {
7298      LocalizableMessage msg = isPre ?
7299          ERR_LAUNCHING_PRE_EXTERNAL_INITIALIZATION.get():
7300          ERR_LAUNCHING_POST_EXTERNAL_INITIALIZATION.get();
7301      ReplicationCliReturnCode code = isPre?
7302          ERROR_LAUNCHING_PRE_EXTERNAL_INITIALIZATION:
7303          ERROR_LAUNCHING_POST_EXTERNAL_INITIALIZATION;
7304      throw new ReplicationCliException(getThrowableMsg(msg, ne), code, ne);
7305    }
7306
7307    String lastLogMsg = null;
7308    while (!isOver)
7309    {
7310      sleepCatchInterrupt(500);
7311      try
7312      {
7313        SearchResult sr = getLastSearchResult(ctx, dn, "ds-task-log-message", "ds-task-state");
7314        String logMsg = getFirstValue(sr, "ds-task-log-message");
7315        if (logMsg != null && !logMsg.equals(lastLogMsg))
7316        {
7317          logger.info(LocalizableMessage.raw(logMsg));
7318          lastLogMsg = logMsg;
7319        }
7320        InstallerHelper helper = new InstallerHelper();
7321        String state = getFirstValue(sr, "ds-task-state");
7322
7323        if (helper.isDone(state) || helper.isStoppedByError(state))
7324        {
7325          isOver = true;
7326          LocalizableMessage errorMsg = getPrePostErrorMsg(lastLogMsg, state, ctx);
7327
7328          if (helper.isCompletedWithErrors(state))
7329          {
7330            logger.warn(LocalizableMessage.raw("Completed with error: "+errorMsg));
7331            errPrintln(errorMsg);
7332          }
7333          else if (!helper.isSuccessful(state) ||
7334              helper.isStoppedByError(state))
7335          {
7336            logger.warn(LocalizableMessage.raw("Error: "+errorMsg));
7337            ReplicationCliReturnCode code = isPre?
7338                ERROR_LAUNCHING_PRE_EXTERNAL_INITIALIZATION:
7339                  ERROR_LAUNCHING_POST_EXTERNAL_INITIALIZATION;
7340            throw new ReplicationCliException(errorMsg, code, null);
7341          }
7342        }
7343      }
7344      catch (NameNotFoundException x)
7345      {
7346        isOver = true;
7347      }
7348      catch (NamingException ne)
7349      {
7350        throw new ReplicationCliException(getThrowableMsg(ERR_READING_SERVER_TASK_PROGRESS.get(), ne),
7351            ERROR_CONNECTING, ne);
7352      }
7353    }
7354  }
7355
7356  private LocalizableMessage getPrePostErrorMsg(String lastLogMsg, String state, InitialLdapContext ctx)
7357  {
7358    String server = getHostPort(ctx);
7359    if (lastLogMsg != null)
7360    {
7361      return ERR_UNEXPECTED_DURING_TASK_WITH_LOG.get(lastLogMsg, state, server);
7362    }
7363    return ERR_UNEXPECTED_DURING_TASK_NO_LOG.get(state, server);
7364  }
7365
7366  private void sleepCatchInterrupt(long millis)
7367  {
7368    try
7369    {
7370      Thread.sleep(millis);
7371    }
7372    catch (InterruptedException e)
7373    {
7374    }
7375  }
7376
7377  /**
7378   * Initializes all the replicas in the topology with the contents of a
7379   * given replica.  This method will try to create the task only once.
7380   * @param ctx the connection to the server where the source replica of the
7381   * initialization is.
7382   * @param baseDN the dn of the suffix.
7383   * @param displayProgress whether we want to display progress or not.
7384   * @throws ClientException if an unexpected error occurs.
7385   * @throws PeerNotFoundException if the replication mechanism cannot find
7386   * a peer.
7387   */
7388  public void initializeAllSuffixTry(String baseDN, InitialLdapContext ctx,
7389      boolean displayProgress)
7390  throws ClientException, PeerNotFoundException
7391  {
7392    boolean isOver = false;
7393    String dn = null;
7394    String serverDisplay = getHostPort(ctx);
7395    Map<String, String> attrsMap = new TreeMap<>();
7396    attrsMap.put("ds-task-initialize-domain-dn", baseDN);
7397    attrsMap.put("ds-task-initialize-replica-server-id", "all");
7398    try
7399    {
7400      dn = createServerTask(ctx, "ds-task-initialize-remote-replica", "org.opends.server.tasks.InitializeTargetTask",
7401          "dsreplication-initialize", attrsMap);
7402    }
7403    catch (NamingException ne)
7404    {
7405      throw new ClientException(ReturnCode.APPLICATION_ERROR,
7406              getThrowableMsg(INFO_ERROR_LAUNCHING_INITIALIZATION.get(serverDisplay), ne), ne);
7407    }
7408
7409    LocalizableMessage lastDisplayedMsg = null;
7410    String lastLogMsg = null;
7411    long lastTimeMsgDisplayed = -1;
7412    long lastTimeMsgLogged = -1;
7413    long totalEntries = 0;
7414    while (!isOver)
7415    {
7416      sleepCatchInterrupt(500);
7417      try
7418      {
7419        SearchResult sr = getLastSearchResult(ctx, dn, "ds-task-unprocessed-entry-count",
7420            "ds-task-processed-entry-count", "ds-task-log-message", "ds-task-state" );
7421
7422        // Get the number of entries that have been handled and a percentage...
7423        String sProcessed = getFirstValue(sr, "ds-task-processed-entry-count");
7424        String sUnprocessed = getFirstValue(sr, "ds-task-unprocessed-entry-count");
7425        long processed = -1;
7426        long unprocessed = -1;
7427        if (sProcessed != null)
7428        {
7429          processed = Integer.parseInt(sProcessed);
7430        }
7431        if (sUnprocessed != null)
7432        {
7433          unprocessed = Integer.parseInt(sUnprocessed);
7434        }
7435        totalEntries = Math.max(totalEntries, processed+unprocessed);
7436
7437        LocalizableMessage msg = getMsg(lastDisplayedMsg, processed, unprocessed);
7438        if (msg != null)
7439        {
7440          long currentTime = System.currentTimeMillis();
7441          /* Refresh period: to avoid having too many lines in the log */
7442          long minRefreshPeriod = getMinRefreshPeriod(totalEntries);
7443          if (currentTime - minRefreshPeriod > lastTimeMsgLogged)
7444          {
7445            lastTimeMsgLogged = currentTime;
7446            logger.info(LocalizableMessage.raw("Progress msg: "+msg));
7447          }
7448          if (displayProgress
7449              && currentTime - minRefreshPeriod > lastTimeMsgDisplayed
7450              && !msg.equals(lastDisplayedMsg))
7451          {
7452            print(msg);
7453            lastDisplayedMsg = msg;
7454            println();
7455            lastTimeMsgDisplayed = currentTime;
7456          }
7457        }
7458
7459        String logMsg = getFirstValue(sr, "ds-task-log-message");
7460        if (logMsg != null && !logMsg.equals(lastLogMsg))
7461        {
7462          logger.info(LocalizableMessage.raw(logMsg));
7463          lastLogMsg = logMsg;
7464        }
7465        InstallerHelper helper = new InstallerHelper();
7466        String state = getFirstValue(sr, "ds-task-state");
7467
7468        if (helper.isDone(state) || helper.isStoppedByError(state))
7469        {
7470          isOver = true;
7471          logger.info(LocalizableMessage.raw("Last task entry: "+sr));
7472          if (displayProgress && msg != null && !msg.equals(lastDisplayedMsg))
7473          {
7474            print(msg);
7475            lastDisplayedMsg = msg;
7476            println();
7477          }
7478
7479          LocalizableMessage errorMsg = getInitializeAllErrorMsg(serverDisplay, lastLogMsg, state);
7480          if (helper.isCompletedWithErrors(state))
7481          {
7482            logger.warn(LocalizableMessage.raw("Processed errorMsg: "+errorMsg));
7483            if (displayProgress)
7484            {
7485              errPrintln(errorMsg);
7486            }
7487          }
7488          else if (!helper.isSuccessful(state) ||
7489              helper.isStoppedByError(state))
7490          {
7491            logger.warn(LocalizableMessage.raw("Processed errorMsg: "+errorMsg));
7492            ClientException ce = new ClientException(
7493                ReturnCode.APPLICATION_ERROR, errorMsg,
7494                null);
7495            if (lastLogMsg == null
7496                || helper.isPeersNotFoundError(lastLogMsg))
7497            {
7498              logger.warn(LocalizableMessage.raw("Throwing peer not found error.  "+
7499                  "Last Log Msg: "+lastLogMsg));
7500              // Assume that this is a peer not found error.
7501              throw new PeerNotFoundException(errorMsg);
7502            }
7503            else
7504            {
7505              logger.error(LocalizableMessage.raw("Throwing ApplicationException."));
7506              throw ce;
7507            }
7508          }
7509          else
7510          {
7511            if (displayProgress)
7512            {
7513              print(INFO_SUFFIX_INITIALIZED_SUCCESSFULLY.get());
7514              println();
7515            }
7516            logger.info(LocalizableMessage.raw("Processed msg: "+errorMsg));
7517            logger.info(LocalizableMessage.raw("Initialization completed successfully."));
7518          }
7519        }
7520      }
7521      catch (NameNotFoundException x)
7522      {
7523        isOver = true;
7524        logger.info(LocalizableMessage.raw("Initialization entry not found."));
7525        if (displayProgress)
7526        {
7527          print(INFO_SUFFIX_INITIALIZED_SUCCESSFULLY.get());
7528          println();
7529        }
7530      }
7531      catch (NamingException ne)
7532      {
7533        throw new ClientException(
7534            ReturnCode.APPLICATION_ERROR,
7535                getThrowableMsg(INFO_ERROR_POOLING_INITIALIZATION.get(
7536                    serverDisplay), ne), ne);
7537      }
7538    }
7539  }
7540
7541  private SearchResult getLastSearchResult(InitialLdapContext ctx, String dn, String... returnedAttributes)
7542      throws NamingException
7543  {
7544    SearchControls searchControls = new SearchControls();
7545    searchControls.setSearchScope(SearchControls.OBJECT_SCOPE);
7546    searchControls.setReturningAttributes(returnedAttributes);
7547    NamingEnumeration<SearchResult> res = ctx.search(dn, "objectclass=*", searchControls);
7548    try
7549    {
7550      SearchResult sr = null;
7551      while (res.hasMore())
7552      {
7553        sr = res.next();
7554      }
7555      return sr;
7556    }
7557    finally
7558    {
7559      res.close();
7560    }
7561  }
7562
7563  private String createServerTask(InitialLdapContext ctx, String taskObjectclass,
7564      String taskJavaClass, String taskID, Map<String, String> taskAttrs) throws NamingException
7565  {
7566    int i = 1;
7567    String dn = "";
7568    BasicAttributes attrs = new BasicAttributes();
7569    attrs.put("objectclass", taskObjectclass);
7570    attrs.put("ds-task-class-name", taskJavaClass);
7571    for (Map.Entry<String, String> attr : taskAttrs.entrySet())
7572    {
7573      attrs.put(attr.getKey(), attr.getValue());
7574    }
7575
7576    while (true)
7577    {
7578      String id = taskID + "-" + i;
7579      dn = "ds-task-id=" + id + ",cn=Scheduled Tasks,cn=Tasks";
7580      try
7581      {
7582        DirContext dirCtx = ctx.createSubcontext(dn, attrs);
7583        logger.info(LocalizableMessage.raw("created task entry: " + attrs));
7584        dirCtx.close();
7585        return dn;
7586      }
7587      catch (NameAlreadyBoundException x)
7588      {
7589        logger.warn(LocalizableMessage.raw("A task with dn: " + dn + " already existed."));
7590      }
7591      catch (NamingException ne)
7592      {
7593        logger.error(LocalizableMessage.raw("Error creating task " + attrs, ne));
7594        throw ne;
7595      }
7596      i++;
7597    }
7598  }
7599
7600  private LocalizableMessage getInitializeAllErrorMsg(String serverDisplay, String lastLogMsg, String state)
7601  {
7602    if (lastLogMsg != null)
7603    {
7604      return INFO_ERROR_DURING_INITIALIZATION_LOG.get(serverDisplay, lastLogMsg, state, serverDisplay);
7605    }
7606    return INFO_ERROR_DURING_INITIALIZATION_NO_LOG.get(serverDisplay, state, serverDisplay);
7607  }
7608
7609  private LocalizableMessage getMsg(LocalizableMessage lastDisplayedMsg, long processed, long unprocessed)
7610  {
7611    if (processed != -1 && unprocessed != -1)
7612    {
7613      if (processed + unprocessed > 0)
7614      {
7615        long perc = (100 * processed) / (processed + unprocessed);
7616        return INFO_INITIALIZE_PROGRESS_WITH_PERCENTAGE.get(processed, perc);
7617      }
7618      else
7619      {
7620        // return INFO_NO_ENTRIES_TO_INITIALIZE.get();
7621        return null;
7622      }
7623    }
7624    else if (processed != -1)
7625    {
7626      return INFO_INITIALIZE_PROGRESS_WITH_PROCESSED.get(processed);
7627    }
7628    else if (unprocessed != -1)
7629    {
7630      return INFO_INITIALIZE_PROGRESS_WITH_UNPROCESSED.get(unprocessed);
7631    }
7632    else
7633    {
7634      return lastDisplayedMsg;
7635    }
7636  }
7637
7638  private long getMinRefreshPeriod(long totalEntries)
7639  {
7640    if (totalEntries < 100)
7641    {
7642      return 0;
7643    }
7644    else if (totalEntries < 1000)
7645    {
7646      return 1000;
7647    }
7648    else if (totalEntries < 10000)
7649    {
7650      return 5000;
7651    }
7652    return 10000;
7653  }
7654
7655  /**
7656   * Removes the references to a replication server in the base DNs of a
7657   * given server.
7658   * @param server the server that we want to update.
7659   * @param replicationServer the replication server whose references we want
7660   * to remove.
7661   * @param bindDn the bindDn that must be used to log to the server.
7662   * @param pwd the password that must be used to log to the server.
7663   * @param baseDNs the list of base DNs where we want to remove the references
7664   * to the provided replication server.
7665   * @param updateReplicationServers if references in the replication servers
7666   * must be updated.
7667   * @param cnx the preferred LDAP URLs to be used to connect to the
7668   * server.
7669   * @throws ReplicationCliException if there is an error updating the
7670   * configuration.
7671   */
7672  private void removeReferencesInServer(ServerDescriptor server,
7673      String replicationServer, String bindDn, String pwd,
7674      Collection<String> baseDNs, boolean updateReplicationServers,
7675      Set<PreferredConnection> cnx)
7676  throws ReplicationCliException
7677  {
7678    TopologyCacheFilter filter = new TopologyCacheFilter();
7679    filter.setSearchMonitoringInformation(false);
7680    filter.setSearchBaseDNInformation(false);
7681    ServerLoader loader = new ServerLoader(server.getAdsProperties(), bindDn,
7682        pwd, getTrustManager(sourceServerCI), getConnectTimeout(), cnx, filter);
7683    InitialLdapContext ctx = null;
7684    String lastBaseDN = null;
7685    String hostPort = null;
7686
7687    try
7688    {
7689      ctx = loader.createContext();
7690      hostPort = getHostPort(ctx);
7691      ManagementContext mCtx = LDAPManagementContext.createFromContext(
7692          JNDIDirContextAdaptor.adapt(ctx));
7693      RootCfgClient root = mCtx.getRootConfiguration();
7694      ReplicationSynchronizationProviderCfgClient sync = null;
7695      try
7696      {
7697        sync = (ReplicationSynchronizationProviderCfgClient)
7698        root.getSynchronizationProvider("Multimaster Synchronization");
7699      }
7700      catch (ManagedObjectNotFoundException monfe)
7701      {
7702        // It does not exist.
7703        logger.info(LocalizableMessage.raw("No synchronization found on "+ hostPort +".",
7704            monfe));
7705      }
7706      if (sync != null)
7707      {
7708        String[] domainNames = sync.listReplicationDomains();
7709        if (domainNames != null)
7710        {
7711          for (String domainName : domainNames)
7712          {
7713            ReplicationDomainCfgClient domain =
7714              sync.getReplicationDomain(domainName);
7715            for (String baseDN : baseDNs)
7716            {
7717              lastBaseDN = baseDN;
7718              if (areDnsEqual(domain.getBaseDN().toString(), baseDN))
7719              {
7720                print(formatter.getFormattedWithPoints(
7721                    INFO_REPLICATION_REMOVING_REFERENCES_ON_REMOTE.get(baseDN, hostPort)));
7722                Set<String> replServers = domain.getReplicationServer();
7723                if (replServers != null)
7724                {
7725                  String replServer = findIgnoreCase(replServers, replicationServer);
7726                  if (replServer != null)
7727                  {
7728                    logger.info(LocalizableMessage.raw("Updating references in domain " +
7729                        domain.getBaseDN()+" on " + hostPort + "."));
7730                    replServers.remove(replServer);
7731                    if (!replServers.isEmpty())
7732                    {
7733                      domain.setReplicationServer(replServers);
7734                      domain.commit();
7735                    }
7736                    else
7737                    {
7738                      sync.removeReplicationDomain(domainName);
7739                      sync.commit();
7740                    }
7741                  }
7742                }
7743                print(formatter.getFormattedDone());
7744                println();
7745              }
7746            }
7747          }
7748        }
7749        if (updateReplicationServers && sync.hasReplicationServer())
7750        {
7751          ReplicationServerCfgClient rServerObj = sync.getReplicationServer();
7752          Set<String> replServers = rServerObj.getReplicationServer();
7753          if (replServers != null)
7754          {
7755            String replServer = findIgnoreCase(replServers, replicationServer);
7756            if (replServer != null)
7757            {
7758              replServers.remove(replServer);
7759              if (!replServers.isEmpty())
7760              {
7761                rServerObj.setReplicationServer(replServers);
7762                rServerObj.commit();
7763              }
7764              else
7765              {
7766                sync.removeReplicationServer();
7767                sync.commit();
7768              }
7769            }
7770          }
7771        }
7772      }
7773    }
7774    catch (NamingException ne)
7775    {
7776      hostPort = getHostPort2(server, cnx);
7777      LocalizableMessage msg = getMessageForException(ne, hostPort);
7778      throw new ReplicationCliException(msg, ERROR_CONNECTING, ne);
7779    }
7780    catch (OpenDsException ode)
7781    {
7782      if (lastBaseDN != null)
7783      {
7784        LocalizableMessage msg = getMessageForDisableException(hostPort, lastBaseDN);
7785        throw new ReplicationCliException(msg,
7786          ERROR_DISABLING_REPLICATION_REMOVE_REFERENCE_ON_BASEDN, ode);
7787      }
7788      else
7789      {
7790        LocalizableMessage msg = ERR_REPLICATION_ERROR_READING_CONFIGURATION.get(hostPort,
7791            ode.getMessage());
7792        throw new ReplicationCliException(msg, ERROR_CONNECTING, ode);
7793      }
7794    }
7795    finally
7796    {
7797      close(ctx);
7798    }
7799  }
7800
7801  /**
7802   * Deletes a replication domain in a server for a given base DN (disable
7803   * replication of the base DN).
7804   * @param ctx the connection to the server.
7805   * @param baseDN the base DN of the replication domain that we want to
7806   * delete.
7807   * @throws ReplicationCliException if there is an error updating the
7808   * configuration of the server.
7809   */
7810  private void deleteReplicationDomain(InitialLdapContext ctx,
7811      String baseDN) throws ReplicationCliException
7812  {
7813    String hostPort = getHostPort(ctx);
7814    try
7815    {
7816      ManagementContext mCtx = LDAPManagementContext.createFromContext(
7817          JNDIDirContextAdaptor.adapt(ctx));
7818      RootCfgClient root = mCtx.getRootConfiguration();
7819      ReplicationSynchronizationProviderCfgClient sync = null;
7820      try
7821      {
7822        sync = (ReplicationSynchronizationProviderCfgClient)
7823        root.getSynchronizationProvider("Multimaster Synchronization");
7824      }
7825      catch (ManagedObjectNotFoundException monfe)
7826      {
7827        // It does not exist.
7828        logger.info(LocalizableMessage.raw("No synchronization found on "+ hostPort +".",
7829            monfe));
7830      }
7831      if (sync != null)
7832      {
7833        String[] domainNames = sync.listReplicationDomains();
7834        if (domainNames != null)
7835        {
7836          for (String domainName : domainNames)
7837          {
7838            ReplicationDomainCfgClient domain =
7839              sync.getReplicationDomain(domainName);
7840            if (areDnsEqual(domain.getBaseDN().toString(), baseDN))
7841            {
7842              print(formatter.getFormattedWithPoints(
7843                  INFO_REPLICATION_DISABLING_BASEDN.get(baseDN, hostPort)));
7844              sync.removeReplicationDomain(domainName);
7845              sync.commit();
7846
7847              print(formatter.getFormattedDone());
7848              println();
7849            }
7850          }
7851        }
7852      }
7853    }
7854    catch (OpenDsException ode)
7855    {
7856      LocalizableMessage msg = getMessageForDisableException(hostPort, baseDN);
7857        throw new ReplicationCliException(msg,
7858          ERROR_DISABLING_REPLICATION_REMOVE_REFERENCE_ON_BASEDN, ode);
7859    }
7860  }
7861
7862  /**
7863   * Disables the replication server for a given server.
7864   * @param ctx the connection to the server.
7865   * @throws ReplicationCliException if there is an error updating the
7866   * configuration of the server.
7867   */
7868  private void disableReplicationServer(InitialLdapContext ctx)
7869  throws ReplicationCliException
7870  {
7871    String hostPort = getHostPort(ctx);
7872    try
7873    {
7874      ManagementContext mCtx = LDAPManagementContext.createFromContext(
7875          JNDIDirContextAdaptor.adapt(ctx));
7876      RootCfgClient root = mCtx.getRootConfiguration();
7877      ReplicationSynchronizationProviderCfgClient sync = null;
7878      ReplicationServerCfgClient replicationServer = null;
7879      try
7880      {
7881        sync = (ReplicationSynchronizationProviderCfgClient)
7882        root.getSynchronizationProvider("Multimaster Synchronization");
7883        if (sync.hasReplicationServer())
7884        {
7885          replicationServer = sync.getReplicationServer();
7886        }
7887      }
7888      catch (ManagedObjectNotFoundException monfe)
7889      {
7890        // It does not exist.
7891        logger.info(LocalizableMessage.raw("No synchronization found on "+ hostPort +".",
7892            monfe));
7893      }
7894      if (replicationServer != null)
7895      {
7896        String s = String.valueOf(replicationServer.getReplicationPort());
7897        print(formatter.getFormattedWithPoints(
7898            INFO_REPLICATION_DISABLING_REPLICATION_SERVER.get(s,
7899                hostPort)));
7900
7901        sync.removeReplicationServer();
7902        sync.commit();
7903        print(formatter.getFormattedDone());
7904        println();
7905      }
7906    }
7907    catch (OpenDsException ode)
7908    {
7909      throw new ReplicationCliException(
7910          ERR_REPLICATION_DISABLING_REPLICATIONSERVER.get(hostPort),
7911          ERROR_DISABLING_REPLICATION_SERVER,
7912          ode);
7913    }
7914  }
7915
7916  /**
7917   * Returns a message for a given OpenDsException (we assume that was an
7918   * exception generated updating the configuration of the server) that
7919   * occurred when we were configuring some replication domain (creating
7920   * the replication domain or updating the list of replication servers of
7921   * the replication domain).
7922   * @param hostPort the hostPort representation of the server we were
7923   * contacting when the OpenDsException occurred.
7924   * @return a message for a given OpenDsException (we assume that was an
7925   * exception generated updating the configuration of the server) that
7926   * occurred when we were configuring some replication domain (creating
7927   * the replication domain or updating the list of replication servers of
7928   * the replication domain).
7929   */
7930  private LocalizableMessage getMessageForEnableException(String hostPort, String baseDN)
7931  {
7932    return ERR_REPLICATION_CONFIGURING_BASEDN.get(baseDN, hostPort);
7933  }
7934
7935  /**
7936   * Returns a message for a given OpenDsException (we assume that was an
7937   * exception generated updating the configuration of the server) that
7938   * occurred when we were configuring some replication domain (deleting
7939   * the replication domain or updating the list of replication servers of
7940   * the replication domain).
7941   * @param hostPort the hostPort representation of the server we were
7942   * contacting when the OpenDsException occurred.
7943   * @return a message for a given OpenDsException (we assume that was an
7944   * exception generated updating the configuration of the server) that
7945   * occurred when we were configuring some replication domain (deleting
7946   * the replication domain or updating the list of replication servers of
7947   * the replication domain).
7948   */
7949  private LocalizableMessage getMessageForDisableException(String hostPort, String baseDN)
7950  {
7951    return ERR_REPLICATION_CONFIGURING_BASEDN.get(baseDN, hostPort);
7952  }
7953
7954  /**
7955   * Returns a message informing the user that the provided port cannot be used.
7956   * @param port the port that cannot be used.
7957   * @return a message informing the user that the provided port cannot be used.
7958   */
7959  private LocalizableMessage getCannotBindToPortError(int port)
7960  {
7961    if (SetupUtils.isPrivilegedPort(port))
7962    {
7963      return ERR_CANNOT_BIND_TO_PRIVILEGED_PORT.get(port);
7964    }
7965    return ERR_CANNOT_BIND_TO_PORT.get(port);
7966  }
7967
7968  /**
7969   * Convenience method used to know if one Set of replication servers equals
7970   * another set of replication servers.
7971   * @param s1 the first set of replication servers.
7972   * @param s2 the second set of replication servers.
7973   * @return <CODE>true</CODE> if the two sets represent the same replication
7974   * servers and <CODE>false</CODE> otherwise.
7975   */
7976  private boolean areReplicationServersEqual(Set<String> s1, Set<String> s2)
7977  {
7978    Set<String> c1 = new HashSet<>();
7979    for (String s : s1)
7980    {
7981      c1.add(s.toLowerCase());
7982    }
7983    Set<String> c2 = new HashSet<>();
7984    for (String s : s2)
7985    {
7986      c2.add(s.toLowerCase());
7987    }
7988    return c1.equals(c2);
7989  }
7990
7991  /**
7992   * Convenience method used to merge two Sets of replication servers.
7993   * @param s1 the first set of replication servers.
7994   * @param s2 the second set of replication servers.
7995   * @return a Set of replication servers containing all the replication servers
7996   * specified in the provided Sets.
7997   */
7998  private Set<String> mergeReplicationServers(Set<String> s1, Set<String> s2)
7999  {
8000    Set<String> c1 = new HashSet<>();
8001    for (String s : s1)
8002    {
8003      c1.add(s.toLowerCase());
8004    }
8005    for (String s : s2)
8006    {
8007      c1.add(s.toLowerCase());
8008    }
8009    return c1;
8010  }
8011
8012  /**
8013   * Returns the message that must be displayed to the user for a given
8014   * exception.  This is assumed to be a critical exception that stops all
8015   * the processing.
8016   * @param rce the ReplicationCliException.
8017   * @return a message to be displayed to the user.
8018   */
8019  private LocalizableMessage getCriticalExceptionMessage(ReplicationCliException rce)
8020  {
8021    LocalizableMessageBuilder mb = new LocalizableMessageBuilder();
8022    mb.append(rce.getMessageObject());
8023    File logFile = ControlPanelLog.getLogFile();
8024    if (logFile != null && rce.getErrorCode() != USER_CANCELLED)
8025    {
8026      mb.append(Constants.LINE_SEPARATOR);
8027      mb.append(INFO_GENERAL_SEE_FOR_DETAILS.get(logFile.getPath()));
8028    }
8029    // Check if the cause has already been included in the message
8030    Throwable c = rce.getCause();
8031    if (c != null)
8032    {
8033      String s;
8034      if (c instanceof NamingException)
8035      {
8036        s = ((NamingException)c).toString(true);
8037      }
8038      else if (c instanceof OpenDsException)
8039      {
8040        LocalizableMessage msg = ((OpenDsException)c).getMessageObject();
8041        if (msg != null)
8042        {
8043          s = msg.toString();
8044        }
8045        else
8046        {
8047          s = c.toString();
8048        }
8049      }
8050      else
8051      {
8052        s = c.toString();
8053      }
8054      if (!mb.toString().contains(s))
8055      {
8056        mb.append(Constants.LINE_SEPARATOR);
8057        mb.append(INFO_REPLICATION_CRITICAL_ERROR_DETAILS.get(s));
8058      }
8059    }
8060    return mb.toMessage();
8061  }
8062
8063  private boolean mustInitializeSchema(ServerDescriptor server1,
8064      ServerDescriptor server2, EnableReplicationUserData uData)
8065  {
8066    boolean mustInitializeSchema = false;
8067    if (!argParser.noSchemaReplication())
8068    {
8069      String id1 = server1.getSchemaReplicationID();
8070      String id2 = server2.getSchemaReplicationID();
8071      mustInitializeSchema = id1 == null || !id1.equals(id2);
8072    }
8073    if (mustInitializeSchema)
8074    {
8075      // Check that both will contain replication data
8076      mustInitializeSchema = uData.getServer1().configureReplicationDomain()
8077          && uData.getServer2().configureReplicationDomain();
8078    }
8079    return mustInitializeSchema;
8080  }
8081
8082  /**
8083   * This method registers a server in a given ADSContext.  If the server was
8084   * already registered it unregisters it and registers again (some properties
8085   * might have changed).
8086   * @param adsContext the ADS Context to be used.
8087   * @param serverProperties the properties of the server to be registered.
8088   * @throws ADSContextException if an error occurs during the registration or
8089   * unregistration of the server.
8090   */
8091  private void registerServer(ADSContext adsContext, Map<ServerProperty, Object> serverProperties)
8092      throws ADSContextException
8093  {
8094    try
8095    {
8096      adsContext.registerServer(serverProperties);
8097    }
8098    catch (ADSContextException ade)
8099    {
8100      if (ade.getError() ==
8101        ADSContextException.ErrorType.ALREADY_REGISTERED)
8102      {
8103        logger.warn(LocalizableMessage.raw("The server was already registered: "+
8104            serverProperties));
8105        adsContext.unregisterServer(serverProperties);
8106        adsContext.registerServer(serverProperties);
8107      }
8108      else
8109      {
8110        throw ade;
8111      }
8112    }
8113  }
8114
8115  /** {@inheritDoc} */
8116  @Override
8117  public boolean isAdvancedMode() {
8118    return false;
8119  }
8120
8121  /** {@inheritDoc} */
8122  @Override
8123  public boolean isInteractive() {
8124    return !forceNonInteractive && argParser.isInteractive();
8125  }
8126
8127  /** {@inheritDoc} */
8128  @Override
8129  public boolean isMenuDrivenMode() {
8130    return true;
8131  }
8132
8133  /** {@inheritDoc} */
8134  @Override
8135  public boolean isQuiet()
8136  {
8137    return argParser.isQuiet();
8138  }
8139
8140  /** {@inheritDoc} */
8141  @Override
8142  public boolean isScriptFriendly() {
8143    return argParser.isScriptFriendly();
8144  }
8145
8146  /** {@inheritDoc} */
8147  @Override
8148  public boolean isVerbose() {
8149    return true;
8150  }
8151
8152  /**
8153   * Forces the initialization of the trust manager in the LDAPConnectionInteraction object.
8154   * @param ci the LDAP connection to the server
8155   */
8156  private void forceTrustManagerInitialization(LDAPConnectionConsoleInteraction ci)
8157  {
8158    forceNonInteractive = true;
8159    try
8160    {
8161      ci.initializeTrustManagerIfRequired();
8162    }
8163    catch (ArgumentException ae)
8164    {
8165      logger.warn(LocalizableMessage.raw("Error initializing trust store: "+ae, ae));
8166    }
8167    forceNonInteractive = false;
8168  }
8169
8170  /**
8171   * Method used to compare two server registries.
8172   * @param registry1 the first registry to compare.
8173   * @param registry2 the second registry to compare.
8174   * @return <CODE>true</CODE> if the registries are equal and
8175   * <CODE>false</CODE> otherwise.
8176   */
8177  private boolean areEqual(Set<Map<ServerProperty, Object>> registry1, Set<Map<ServerProperty, Object>> registry2)
8178  {
8179    return registry1.size() == registry2.size()
8180        && equals(registry1, registry2, getPropertiesToCompare());
8181  }
8182
8183  private Set<ServerProperty> getPropertiesToCompare()
8184  {
8185    final Set<ServerProperty> propertiesToCompare = new HashSet<>();
8186    for (ServerProperty property : ServerProperty.values())
8187    {
8188      if (property.getAttributeSyntax() != ADSPropertySyntax.CERTIFICATE_BINARY)
8189      {
8190        propertiesToCompare.add(property);
8191      }
8192    }
8193    return propertiesToCompare;
8194  }
8195
8196  private boolean equals(Set<Map<ServerProperty, Object>> registry1, Set<Map<ServerProperty, Object>> registry2,
8197      Set<ServerProperty> propertiesToCompare)
8198  {
8199    for (Map<ServerProperty, Object> server1 : registry1)
8200    {
8201      if (!exists(registry2, server1, propertiesToCompare))
8202      {
8203        return false;
8204      }
8205    }
8206    return true;
8207  }
8208
8209  private boolean exists(Set<Map<ServerProperty, Object>> registry2, Map<ServerProperty, Object> server1,
8210      Set<ServerProperty> propertiesToCompare)
8211  {
8212    for (Map<ServerProperty, Object> server2 : registry2)
8213    {
8214      if (equals(server1, server2, propertiesToCompare))
8215      {
8216        return true;
8217      }
8218    }
8219    return false;
8220  }
8221
8222  private boolean equals(Map<ServerProperty, Object> server1, Map<ServerProperty, Object> server2,
8223      Set<ServerProperty> propertiesToCompare)
8224  {
8225    for (ServerProperty prop : propertiesToCompare)
8226    {
8227      if (!Objects.equals(server1.get(prop), server2.get(prop)))
8228      {
8229        return false;
8230      }
8231    }
8232    return true;
8233  }
8234
8235  /**
8236   * Tells whether we are trying to disable all the replicated suffixes.
8237   * @param uData the disable replication data provided by the user.
8238   * @return <CODE>true</CODE> if we want to disable all the replicated suffixes
8239   * and <CODE>false</CODE> otherwise.
8240   */
8241  private boolean disableAllBaseDns(InitialLdapContext ctx,
8242      DisableReplicationUserData uData)
8243  {
8244    if (uData.disableAll())
8245    {
8246      return true;
8247    }
8248
8249    Collection<ReplicaDescriptor> replicas = getReplicas(ctx);
8250    Set<String> replicatedSuffixes = new HashSet<>();
8251    for (ReplicaDescriptor rep : replicas)
8252    {
8253      String dn = rep.getSuffix().getDN();
8254      if (rep.isReplicated())
8255      {
8256        replicatedSuffixes.add(dn);
8257      }
8258    }
8259
8260    for (String dn1 : replicatedSuffixes)
8261    {
8262      if (!areDnsEqual(ADSContext.getAdministrationSuffixDN(), dn1)
8263          && !areDnsEqual(Constants.SCHEMA_DN, dn1)
8264          && !containsDN(uData.getBaseDNs(), dn1))
8265      {
8266        return false;
8267      }
8268    }
8269    return true;
8270  }
8271
8272  private boolean containsDN(final Collection<String> dns, String dnToFind)
8273  {
8274    for (String dn : dns)
8275    {
8276      if (areDnsEqual(dn, dnToFind))
8277      {
8278        return true;
8279      }
8280    }
8281    return false;
8282  }
8283
8284  /**
8285   * Returns the host port representation of the server to be used in progress,
8286   * status and error messages.  It takes into account the fact the host and
8287   * port provided by the user.
8288   * @param server the ServerDescriptor.
8289   * @param cnx the preferred connections list.
8290   * @return the host port string representation of the provided server.
8291   */
8292  private String getHostPort2(ServerDescriptor server,
8293      Collection<PreferredConnection> cnx)
8294  {
8295    String hostPort = null;
8296    for (PreferredConnection connection : cnx)
8297    {
8298      String url = connection.getLDAPURL();
8299      if (url.equals(server.getLDAPURL()))
8300      {
8301        hostPort = server.getHostPort(false);
8302      }
8303      else if (url.equals(server.getLDAPsURL()))
8304      {
8305        hostPort = server.getHostPort(true);
8306      }
8307    }
8308    if (hostPort != null)
8309    {
8310      return hostPort;
8311    }
8312    return server.getHostPort(true);
8313  }
8314
8315  /**
8316   * Prompts the user for the subcommand that should be executed.
8317   * @return the subcommand choice of the user.
8318   */
8319  private SubcommandChoice promptForSubcommand()
8320  {
8321    MenuBuilder<SubcommandChoice> builder = new MenuBuilder<>(this);
8322    builder.setPrompt(INFO_REPLICATION_SUBCOMMAND_PROMPT.get());
8323    builder.addCancelOption(false);
8324    for (SubcommandChoice choice : SubcommandChoice.values())
8325    {
8326      if (choice != SubcommandChoice.CANCEL)
8327      {
8328        builder.addNumberedOption(choice.getPrompt(),
8329            MenuResult.success(choice));
8330      }
8331    }
8332    try
8333    {
8334      MenuResult<SubcommandChoice> m = builder.toMenu().run();
8335      if (m.isSuccess())
8336      {
8337        return m.getValue();
8338      }
8339      // The user cancelled
8340      return SubcommandChoice.CANCEL;
8341    }
8342    catch (ClientException ce)
8343    {
8344      logger.warn(LocalizableMessage.raw("Error reading input: "+ce, ce));
8345      return SubcommandChoice.CANCEL;
8346    }
8347  }
8348
8349  private boolean mustPrintCommandBuilder()
8350  {
8351    return argParser.isInteractive() &&
8352        (argParser.displayEquivalentArgument.isPresent() ||
8353        argParser.equivalentCommandFileArgument.isPresent());
8354  }
8355
8356  /**
8357   * Prints the contents of a command builder.  This method has been created
8358   * since SetPropSubCommandHandler calls it.  All the logic of DSConfig is on
8359   * this method.  Currently it simply writes the content of the CommandBuilder
8360   * to the standard output, but if we provide an option to write the content
8361   * to a file only the implementation of this method must be changed.
8362   * @param subCommandName the command builder to be printed.
8363   * @param uData input parameters from cli
8364   */
8365  private void printNewCommandBuilder(String subCommandName, ReplicationUserData uData)
8366  {
8367    try
8368    {
8369      final CommandBuilder commandBuilder = createCommandBuilder(sourceServerCI, subCommandName, uData);
8370      if (argParser.displayEquivalentArgument.isPresent())
8371      {
8372        println();
8373        // We assume that the app we are running is this one.
8374        println(INFO_REPLICATION_NON_INTERACTIVE.get(commandBuilder));
8375      }
8376      if (argParser.equivalentCommandFileArgument.isPresent())
8377      {
8378        // Write to the file.
8379        String file = argParser.equivalentCommandFileArgument.getValue();
8380        try
8381        {
8382          BufferedWriter writer = new BufferedWriter(new FileWriter(file, true));
8383
8384          writer.write(SHELL_COMMENT_SEPARATOR+getCurrentOperationDateMessage());
8385          writer.newLine();
8386
8387          writer.write(commandBuilder.toString());
8388          writer.newLine();
8389          writer.newLine();
8390
8391          writer.flush();
8392          writer.close();
8393        }
8394        catch (IOException ioe)
8395        {
8396          errPrintln(ERR_REPLICATION_ERROR_WRITING_EQUIVALENT_COMMAND_LINE.get(file, ioe));
8397        }
8398      }
8399    }
8400    catch (Throwable t)
8401    {
8402      logger.error(LocalizableMessage.raw("Error printing equivalent command-line: " + t), t);
8403    }
8404  }
8405
8406  /**
8407   * Creates a command builder with the global options: script friendly,
8408   * verbose, etc. for a given subcommand name.  It also adds systematically the
8409   * no-prompt option.
8410   *
8411   * @param ci the LDAP connection to the server
8412   * @param subcommandName the subcommand name.
8413   * @param uData the user data.
8414   * @return the command builder that has been created with the specified
8415   * subcommandName.
8416   */
8417  private CommandBuilder createCommandBuilder(LDAPConnectionConsoleInteraction ci, String subcommandName,
8418      ReplicationUserData uData) throws ArgumentException
8419  {
8420    String commandName = getCommandName();
8421
8422    CommandBuilder commandBuilder = new CommandBuilder(commandName, subcommandName);
8423
8424    if (ENABLE_REPLICATION_SUBCMD_NAME.equals(subcommandName))
8425    {
8426      // All the arguments for enable replication are update here.
8427      updateCommandBuilder(commandBuilder, (EnableReplicationUserData)uData);
8428    }
8429    else if (INITIALIZE_REPLICATION_SUBCMD_NAME.equals(subcommandName) ||
8430        RESET_CHANGE_NUMBER_SUBCMD_NAME.equals(subcommandName))
8431    {
8432      // All the arguments for initialize replication are update here.
8433      updateCommandBuilder(commandBuilder, (SourceDestinationServerUserData)uData);
8434    }
8435    else if (PURGE_HISTORICAL_SUBCMD_NAME.equals(subcommandName))
8436    {
8437      // All the arguments for initialize replication are update here.
8438      updateCommandBuilder(ci, commandBuilder, (PurgeHistoricalUserData)uData);
8439    }
8440    else
8441    {
8442      // Update the arguments used in the console interaction with the
8443      // actual arguments of dsreplication.
8444      updateCommandBuilderWithConsoleInteraction(commandBuilder, ci);
8445    }
8446
8447    if (DISABLE_REPLICATION_SUBCMD_NAME.equals(subcommandName))
8448    {
8449      DisableReplicationUserData disableData =
8450        (DisableReplicationUserData)uData;
8451      if (disableData.disableAll())
8452      {
8453        commandBuilder.addArgument(newBooleanArgument(
8454            argParser.disableAllArg, INFO_DESCRIPTION_DISABLE_ALL));
8455      }
8456      else if (disableData.disableReplicationServer())
8457      {
8458        commandBuilder.addArgument(newBooleanArgument(
8459            argParser.disableReplicationServerArg, INFO_DESCRIPTION_DISABLE_REPLICATION_SERVER));
8460      }
8461    }
8462
8463    addGlobalArguments(commandBuilder, uData);
8464    return commandBuilder;
8465  }
8466
8467  private String getCommandName()
8468  {
8469    String commandName = System.getProperty(ServerConstants.PROPERTY_SCRIPT_NAME);
8470    if (commandName != null)
8471    {
8472      return commandName;
8473    }
8474    return "dsreplication";
8475  }
8476
8477  private void updateCommandBuilderWithConsoleInteraction(CommandBuilder commandBuilder,
8478      LDAPConnectionConsoleInteraction ci) throws ArgumentException
8479  {
8480    if (ci != null && ci.getCommandBuilder() != null)
8481    {
8482      CommandBuilder interactionBuilder = ci.getCommandBuilder();
8483      for (Argument arg : interactionBuilder.getArguments())
8484      {
8485        if (OPTION_LONG_BINDPWD.equals(arg.getLongIdentifier()))
8486        {
8487          commandBuilder.addObfuscatedArgument(getAdminPasswordArg(arg));
8488        }
8489        else if (OPTION_LONG_BINDPWD_FILE.equals(arg.getLongIdentifier()))
8490        {
8491          commandBuilder.addArgument(getAdminPasswordFileArg(arg));
8492        }
8493        else
8494        {
8495          addArgument(commandBuilder, arg, interactionBuilder.isObfuscated(arg));
8496        }
8497      }
8498    }
8499  }
8500
8501  private void updateCommandBuilder(LDAPConnectionConsoleInteraction ci, CommandBuilder commandBuilder,
8502      PurgeHistoricalUserData uData) throws ArgumentException
8503  {
8504    if (uData.isOnline())
8505    {
8506      updateCommandBuilderWithConsoleInteraction(commandBuilder, ci);
8507      if (uData.getTaskSchedule() != null)
8508      {
8509        updateCommandBuilderWithTaskSchedule(commandBuilder,
8510            uData.getTaskSchedule());
8511      }
8512    }
8513
8514    IntegerArgument maximumDurationArg = new IntegerArgument(
8515        argParser.maximumDurationArg.getName(),
8516        argParser.maximumDurationArg.getShortIdentifier(),
8517        argParser.maximumDurationArg.getLongIdentifier(),
8518        argParser.maximumDurationArg.isRequired(),
8519        argParser.maximumDurationArg.isMultiValued(),
8520        argParser.maximumDurationArg.needsValue(),
8521        argParser.maximumDurationArg.getValuePlaceholder(),
8522        PurgeConflictsHistoricalTask.DEFAULT_MAX_DURATION,
8523        argParser.maximumDurationArg.getPropertyName(),
8524        argParser.maximumDurationArg.getDescription());
8525    maximumDurationArg.addValue(String.valueOf(uData.getMaximumDuration()));
8526    commandBuilder.addArgument(maximumDurationArg);
8527  }
8528
8529  private void updateCommandBuilderWithTaskSchedule(
8530      CommandBuilder commandBuilder,
8531      TaskScheduleUserData taskSchedule)
8532  {
8533    TaskScheduleUserData.updateCommandBuilderWithTaskSchedule(
8534        commandBuilder, taskSchedule);
8535  }
8536
8537  private void addGlobalArguments(CommandBuilder commandBuilder, ReplicationUserData uData)
8538  throws ArgumentException
8539  {
8540    List<String> baseDNs = uData.getBaseDNs();
8541    StringArgument baseDNsArg = new StringArgument("baseDNs",
8542        OPTION_SHORT_BASEDN,
8543        OPTION_LONG_BASEDN, false, true, true, INFO_BASEDN_PLACEHOLDER.get(),
8544        null,
8545        null, INFO_DESCRIPTION_REPLICATION_BASEDNS.get());
8546    for (String baseDN : baseDNs)
8547    {
8548      baseDNsArg.addValue(baseDN);
8549    }
8550    commandBuilder.addArgument(baseDNsArg);
8551
8552    if (argParser.resetChangeNumber.isPresent())
8553    {
8554      commandBuilder.addArgument(argParser.resetChangeNumber);
8555    }
8556
8557    // Try to find some arguments and put them at the end.
8558    String[] identifiersToMove ={
8559        OPTION_LONG_ADMIN_UID,
8560        "adminPassword",
8561        "adminPasswordFile",
8562        OPTION_LONG_SASLOPTION,
8563        OPTION_LONG_TRUSTALL,
8564        OPTION_LONG_TRUSTSTOREPATH,
8565        OPTION_LONG_TRUSTSTORE_PWD,
8566        OPTION_LONG_TRUSTSTORE_PWD_FILE,
8567        OPTION_LONG_KEYSTOREPATH,
8568        OPTION_LONG_KEYSTORE_PWD,
8569        OPTION_LONG_KEYSTORE_PWD_FILE,
8570        OPTION_LONG_CERT_NICKNAME
8571    };
8572
8573    ArrayList<Argument> toMoveArgs = new ArrayList<>();
8574    for (String longID : identifiersToMove)
8575    {
8576      final Argument arg = findArg(commandBuilder, longID);
8577      if (arg != null)
8578      {
8579        toMoveArgs.add(arg);
8580      }
8581    }
8582    for (Argument argToMove : toMoveArgs)
8583    {
8584      boolean toObfuscate = commandBuilder.isObfuscated(argToMove);
8585      commandBuilder.removeArgument(argToMove);
8586      if (toObfuscate)
8587      {
8588        commandBuilder.addObfuscatedArgument(argToMove);
8589      }
8590      else
8591      {
8592        commandBuilder.addArgument(argToMove);
8593      }
8594    }
8595
8596    if (argParser.isVerbose())
8597    {
8598      commandBuilder.addArgument(new BooleanArgument("verbose",
8599          OPTION_SHORT_VERBOSE,
8600          OPTION_LONG_VERBOSE, INFO_DESCRIPTION_VERBOSE.get()));
8601    }
8602
8603    if (argParser.isScriptFriendly())
8604    {
8605      commandBuilder.addArgument(argParser.scriptFriendlyArg);
8606    }
8607
8608    commandBuilder.addArgument(argParser.noPromptArg);
8609
8610    if (argParser.propertiesFileArgument.isPresent())
8611    {
8612      commandBuilder.addArgument(argParser.propertiesFileArgument);
8613    }
8614
8615    if (argParser.noPropertiesFileArgument.isPresent())
8616    {
8617      commandBuilder.addArgument(argParser.noPropertiesFileArgument);
8618    }
8619  }
8620
8621  private Argument findArg(CommandBuilder commandBuilder, String longIdentifier)
8622  {
8623    for (Argument arg : commandBuilder.getArguments())
8624    {
8625      if (longIdentifier.equals(arg.getLongIdentifier()))
8626      {
8627        return arg;
8628      }
8629    }
8630    return null;
8631  }
8632
8633  private boolean existsArg(CommandBuilder commandBuilder, String longIdentifier)
8634  {
8635    return findArg(commandBuilder, longIdentifier) != null;
8636  }
8637
8638  private void addArgument(CommandBuilder commandBuilder, Argument arg, boolean isObfuscated)
8639  {
8640    if (isObfuscated)
8641    {
8642      commandBuilder.addObfuscatedArgument(arg);
8643    }
8644    else
8645    {
8646      commandBuilder.addArgument(arg);
8647    }
8648  }
8649
8650  private void updateCommandBuilder(CommandBuilder commandBuilder, EnableReplicationUserData uData)
8651      throws ArgumentException
8652  {
8653    // Update the arguments used in the console interaction with the
8654    // actual arguments of dsreplication.
8655    boolean adminInformationAdded = false;
8656
8657    EnableReplicationServerData server1 = uData.getServer1();
8658    if (firstServerCommandBuilder != null)
8659    {
8660      boolean useAdminUID = existsArg(firstServerCommandBuilder, OPTION_LONG_ADMIN_UID);
8661      // This is required when both the bindDN and the admin UID are provided
8662      // in the command-line.
8663      boolean forceAddBindDN1 = false;
8664      boolean forceAddBindPwdFile1 = false;
8665      if (useAdminUID)
8666      {
8667        String bindDN1 = server1.getBindDn();
8668        String adminUID = uData.getAdminUid();
8669        if (bindDN1 != null
8670            && adminUID != null
8671            && !areDnsEqual(getAdministratorDN(adminUID), bindDN1))
8672        {
8673          forceAddBindDN1 = true;
8674          forceAddBindPwdFile1 = existsArg(firstServerCommandBuilder, OPTION_LONG_BINDPWD_FILE);
8675        }
8676      }
8677      for (Argument arg : firstServerCommandBuilder.getArguments())
8678      {
8679        if (OPTION_LONG_HOST.equals(arg.getLongIdentifier()))
8680        {
8681          commandBuilder.addArgument(getHostArg("host1", OPTION_SHORT_HOST, server1.getHostName(),
8682              INFO_DESCRIPTION_ENABLE_REPLICATION_HOST1));
8683        }
8684        else if (OPTION_LONG_PORT.equals(arg.getLongIdentifier()))
8685        {
8686          commandBuilder.addArgument(getPortArg("port1", OPTION_SHORT_PORT, server1.getPort(),
8687              INFO_DESCRIPTION_ENABLE_REPLICATION_SERVER_PORT1));
8688
8689          if (forceAddBindDN1)
8690          {
8691            commandBuilder.addArgument(getBindDN1Arg(uData));
8692            if (forceAddBindPwdFile1)
8693            {
8694              FileBasedArgument bindPasswordFileArg = getBindPasswordFile1Arg();
8695              bindPasswordFileArg.getNameToValueMap().put("{password file}",
8696                  "{password file}");
8697              commandBuilder.addArgument(bindPasswordFileArg);
8698            }
8699            else
8700            {
8701              commandBuilder.addObfuscatedArgument(getBindPassword1Arg(arg));
8702            }
8703          }
8704        }
8705        else if (OPTION_LONG_BINDDN.equals(arg.getLongIdentifier()))
8706        {
8707          commandBuilder.addArgument(getBindDN1Arg(uData));
8708        }
8709        else if (OPTION_LONG_BINDPWD.equals(arg.getLongIdentifier()))
8710        {
8711          if (useAdminUID)
8712          {
8713            adminInformationAdded = true;
8714            commandBuilder.addObfuscatedArgument(getAdminPasswordArg(arg));
8715          }
8716          else
8717          {
8718            commandBuilder.addObfuscatedArgument(getBindPassword1Arg(arg));
8719          }
8720        }
8721        else if (OPTION_LONG_BINDPWD_FILE.equals(arg.getLongIdentifier()))
8722        {
8723          if (useAdminUID)
8724          {
8725            commandBuilder.addArgument(getAdminPasswordFileArg(arg));
8726          }
8727          else
8728          {
8729            FileBasedArgument bindPasswordFileArg = getBindPasswordFile1Arg();
8730            bindPasswordFileArg.getNameToValueMap().putAll(
8731                ((FileBasedArgument)arg).getNameToValueMap());
8732            commandBuilder.addArgument(bindPasswordFileArg);
8733          }
8734        }
8735        else
8736        {
8737          if (OPTION_LONG_ADMIN_UID.equals(arg.getLongIdentifier()))
8738          {
8739            adminInformationAdded = true;
8740          }
8741
8742          addArgument(commandBuilder, arg, firstServerCommandBuilder.isObfuscated(arg));
8743        }
8744      }
8745    }
8746
8747    EnableReplicationServerData server2 = uData.getServer2();
8748    if (sourceServerCI != null && sourceServerCI.getCommandBuilder() != null)
8749    {
8750      CommandBuilder interactionBuilder = sourceServerCI.getCommandBuilder();
8751      boolean useAdminUID = existsArg(interactionBuilder, OPTION_LONG_ADMIN_UID);
8752      boolean hasBindDN = existsArg(interactionBuilder, OPTION_LONG_BINDDN);
8753//    This is required when both the bindDN and the admin UID are provided
8754      // in the command-line.
8755      boolean forceAddBindDN2 = false;
8756      boolean forceAddBindPwdFile2 = false;
8757      if (useAdminUID)
8758      {
8759        String bindDN2 = server2.getBindDn();
8760        String adminUID = uData.getAdminUid();
8761        if (bindDN2 != null
8762            && adminUID != null
8763            && !areDnsEqual(getAdministratorDN(adminUID), bindDN2))
8764        {
8765          forceAddBindDN2 = true;
8766          forceAddBindPwdFile2 = existsArg(interactionBuilder, OPTION_LONG_BINDPWD_FILE);
8767        }
8768      }
8769      ArrayList<Argument> argsToAnalyze = new ArrayList<>();
8770      for (Argument arg : interactionBuilder.getArguments())
8771      {
8772        if (OPTION_LONG_HOST.equals(arg.getLongIdentifier()))
8773        {
8774          commandBuilder.addArgument(
8775              getHostArg("host2", 'O', server2.getHostName(), INFO_DESCRIPTION_ENABLE_REPLICATION_HOST2));
8776        }
8777        else if (OPTION_LONG_PORT.equals(arg.getLongIdentifier()))
8778        {
8779          commandBuilder.addArgument(getPortArg("port2", null, server2.getPort(),
8780              INFO_DESCRIPTION_ENABLE_REPLICATION_SERVER_PORT2));
8781
8782          if (forceAddBindDN2)
8783          {
8784            commandBuilder.addArgument(getBindDN2Arg(uData, OPTION_SHORT_BINDDN));
8785            if (forceAddBindPwdFile2)
8786            {
8787              FileBasedArgument bindPasswordFileArg = getBindPasswordFile2Arg();
8788              bindPasswordFileArg.getNameToValueMap().put("{password file}",
8789                  "{password file}");
8790              commandBuilder.addArgument(bindPasswordFileArg);
8791            }
8792            else
8793            {
8794              commandBuilder.addObfuscatedArgument(getBindPassword2Arg(arg));
8795            }
8796          }
8797        }
8798        else if (OPTION_LONG_BINDDN.equals(arg.getLongIdentifier()))
8799        {
8800          commandBuilder.addArgument(getBindDN2Arg(uData, null));
8801        }
8802        else if (OPTION_LONG_BINDPWD.equals(arg.getLongIdentifier()))
8803        {
8804          if (useAdminUID && !adminInformationAdded)
8805          {
8806            adminInformationAdded = true;
8807            commandBuilder.addObfuscatedArgument(getAdminPasswordArg(arg));
8808          }
8809          else if (hasBindDN)
8810          {
8811            commandBuilder.addObfuscatedArgument(getBindPassword2Arg(arg));
8812          }
8813        }
8814        else if (OPTION_LONG_BINDPWD_FILE.equals(arg.getLongIdentifier()))
8815        {
8816          if (useAdminUID && !adminInformationAdded)
8817          {
8818            adminInformationAdded = true;
8819            commandBuilder.addArgument(getAdminPasswordFileArg(arg));
8820          }
8821          else if (hasBindDN)
8822          {
8823            FileBasedArgument bindPasswordFileArg = getBindPasswordFile2Arg();
8824            bindPasswordFileArg.getNameToValueMap().putAll(
8825                ((FileBasedArgument)arg).getNameToValueMap());
8826            commandBuilder.addArgument(bindPasswordFileArg);
8827          }
8828        }
8829        else
8830        {
8831          argsToAnalyze.add(arg);
8832        }
8833      }
8834
8835      for (Argument arg : argsToAnalyze)
8836      {
8837        // Just check that the arguments have not already been added.
8838        if (!existsArg(commandBuilder, arg.getLongIdentifier()))
8839        {
8840          addArgument(commandBuilder, arg, interactionBuilder.isObfuscated(arg));
8841        }
8842      }
8843    }
8844
8845    // Try to add the new administration information.
8846    if (!adminInformationAdded)
8847    {
8848      if (uData.getAdminUid() != null)
8849      {
8850        StringArgument adminUID = new StringArgument(OPTION_LONG_ADMIN_UID, 'I',
8851            OPTION_LONG_ADMIN_UID, false, false, true,
8852            INFO_ADMINUID_PLACEHOLDER.get(),
8853            Constants.GLOBAL_ADMIN_UID, null,
8854            INFO_DESCRIPTION_REPLICATION_ADMIN_UID.get(ENABLE_REPLICATION_SUBCMD_NAME));
8855        adminUID.addValue(uData.getAdminUid());
8856        commandBuilder.addArgument(adminUID);
8857      }
8858
8859      if (userProvidedAdminPwdFile != null)
8860      {
8861        commandBuilder.addArgument(userProvidedAdminPwdFile);
8862      }
8863      else if (uData.getAdminPwd() != null)
8864      {
8865        Argument bindPasswordArg = getAdminPasswordArg();
8866        bindPasswordArg.addValue(uData.getAdminPwd());
8867        commandBuilder.addObfuscatedArgument(bindPasswordArg);
8868      }
8869    }
8870
8871    if (server1.configureReplicationServer() &&
8872        !server1.configureReplicationDomain())
8873    {
8874      commandBuilder.addArgument(newBooleanArgument(
8875          argParser.server1.onlyReplicationServerArg, INFO_DESCRIPTION_ENABLE_REPLICATION_ONLY_REPLICATION_SERVER1));
8876    }
8877
8878    if (!server1.configureReplicationServer() &&
8879        server1.configureReplicationDomain())
8880    {
8881      commandBuilder.addArgument(newBooleanArgument(
8882          argParser.server1.noReplicationServerArg, INFO_DESCRIPTION_ENABLE_REPLICATION_NO_REPLICATION_SERVER1));
8883    }
8884
8885    if (server1.configureReplicationServer() &&
8886        server1.getReplicationPort() > 0)
8887    {
8888      commandBuilder.addArgument(getReplicationPortArg(
8889          "replicationPort1", server1, 8989, INFO_DESCRIPTION_ENABLE_REPLICATION_PORT1));
8890    }
8891    if (server1.isSecureReplication())
8892    {
8893      commandBuilder.addArgument(
8894          newBooleanArgument("secureReplication1", INFO_DESCRIPTION_ENABLE_SECURE_REPLICATION1));
8895    }
8896
8897    if (server2.configureReplicationServer() &&
8898        !server2.configureReplicationDomain())
8899    {
8900      commandBuilder.addArgument(newBooleanArgument(
8901          argParser.server2.onlyReplicationServerArg, INFO_DESCRIPTION_ENABLE_REPLICATION_ONLY_REPLICATION_SERVER2));
8902    }
8903
8904    if (!server2.configureReplicationServer() &&
8905        server2.configureReplicationDomain())
8906    {
8907      commandBuilder.addArgument(newBooleanArgument(
8908          argParser.server2.noReplicationServerArg, INFO_DESCRIPTION_ENABLE_REPLICATION_NO_REPLICATION_SERVER2));
8909    }
8910    if (server2.configureReplicationServer() &&
8911        server2.getReplicationPort() > 0)
8912    {
8913      commandBuilder.addArgument(getReplicationPortArg(
8914          "replicationPort2", server2, server2.getReplicationPort(), INFO_DESCRIPTION_ENABLE_REPLICATION_PORT2));
8915    }
8916    if (server2.isSecureReplication())
8917    {
8918      commandBuilder.addArgument(
8919          newBooleanArgument("secureReplication2", INFO_DESCRIPTION_ENABLE_SECURE_REPLICATION2));
8920    }
8921
8922    if (!uData.replicateSchema())
8923    {
8924      commandBuilder.addArgument(new BooleanArgument(
8925          "noschemareplication", null, "noSchemaReplication",
8926          INFO_DESCRIPTION_ENABLE_REPLICATION_NO_SCHEMA_REPLICATION.get()));
8927    }
8928    if (argParser.skipReplicationPortCheck())
8929    {
8930      commandBuilder.addArgument(new BooleanArgument(
8931          "skipportcheck", 'S', "skipPortCheck",
8932          INFO_DESCRIPTION_ENABLE_REPLICATION_SKIPPORT.get()));
8933    }
8934    if (argParser.useSecondServerAsSchemaSource())
8935    {
8936      commandBuilder.addArgument(new BooleanArgument(
8937          "usesecondserverasschemasource", null,
8938          "useSecondServerAsSchemaSource",
8939          INFO_DESCRIPTION_ENABLE_REPLICATION_USE_SECOND_AS_SCHEMA_SOURCE.get(
8940              "--" + argParser.noSchemaReplicationArg.getLongIdentifier())));
8941    }
8942  }
8943
8944  private IntegerArgument getReplicationPortArg(
8945      String name, EnableReplicationServerData server, int defaultValue, Arg0 description) throws ArgumentException
8946  {
8947    IntegerArgument replicationPort = new IntegerArgument(
8948        name, 'r', name, false, false, true,
8949        INFO_PORT_PLACEHOLDER.get(), defaultValue, null, description.get());
8950    int value = server.getReplicationPort();
8951    replicationPort.addValue(String.valueOf(value));
8952    return replicationPort;
8953  }
8954
8955  private BooleanArgument newBooleanArgument(String name, Arg0 msg) throws ArgumentException
8956  {
8957    return new BooleanArgument(name, null, name, msg.get());
8958  }
8959
8960  private BooleanArgument newBooleanArgument(BooleanArgument arg, Arg0 msg) throws ArgumentException
8961  {
8962    return new BooleanArgument(arg.getName(), arg.getShortIdentifier(), arg.getLongIdentifier(), msg.get());
8963  }
8964
8965  private StringArgument getBindPassword1Arg(Argument arg) throws ArgumentException
8966  {
8967    return getBindPasswordArg("bindPassword1", arg, INFO_DESCRIPTION_ENABLE_REPLICATION_BINDPASSWORD1);
8968  }
8969
8970  private StringArgument getBindPassword2Arg(Argument arg) throws ArgumentException
8971  {
8972    return getBindPasswordArg("bindPassword2", arg, INFO_DESCRIPTION_ENABLE_REPLICATION_BINDPASSWORD2);
8973  }
8974
8975  private StringArgument getBindPasswordArg(String name, Argument arg, Arg0 bindPwdMsg) throws ArgumentException
8976  {
8977    StringArgument bindPasswordArg = new StringArgument(
8978        name, null, name, false, false, true,
8979        INFO_BINDPWD_PLACEHOLDER.get(), null, null, bindPwdMsg.get());
8980    bindPasswordArg.addValue(arg.getValue());
8981    return bindPasswordArg;
8982  }
8983
8984  private FileBasedArgument getBindPasswordFile1Arg() throws ArgumentException
8985  {
8986    return new FileBasedArgument(
8987        "bindPasswordFile1", null, "bindPasswordFile1", false, false,
8988        INFO_BINDPWD_FILE_PLACEHOLDER.get(), null, null,
8989        INFO_DESCRIPTION_ENABLE_REPLICATION_BINDPASSWORDFILE1.get());
8990  }
8991
8992  private FileBasedArgument getBindPasswordFile2Arg() throws ArgumentException
8993  {
8994    return new FileBasedArgument(
8995        "bindPasswordFile2", null, "bindPasswordFile2", false, false,
8996        INFO_BINDPWD_FILE_PLACEHOLDER.get(), null, null,
8997        INFO_DESCRIPTION_ENABLE_REPLICATION_BINDPASSWORDFILE2.get());
8998  }
8999
9000  private StringArgument getBindDN1Arg(EnableReplicationUserData uData) throws ArgumentException
9001  {
9002    StringArgument bindDN = new StringArgument("bindDN1", OPTION_SHORT_BINDDN, "bindDN1", false, false, true,
9003        INFO_BINDDN_PLACEHOLDER.get(), "cn=Directory Manager", null,
9004        INFO_DESCRIPTION_ENABLE_REPLICATION_BINDDN1.get());
9005    bindDN.addValue(uData.getServer1().getBindDn());
9006    return bindDN;
9007  }
9008
9009  private StringArgument getBindDN2Arg(EnableReplicationUserData uData, Character shortIdentifier)
9010      throws ArgumentException
9011  {
9012    StringArgument bindDN = new StringArgument("bindDN2", shortIdentifier, "bindDN2", false, false, true,
9013        INFO_BINDDN_PLACEHOLDER.get(), "cn=Directory Manager", null,
9014        INFO_DESCRIPTION_ENABLE_REPLICATION_BINDDN2.get());
9015    bindDN.addValue(uData.getServer2().getBindDn());
9016    return bindDN;
9017  }
9018
9019  private void updateCommandBuilder(CommandBuilder commandBuilder,
9020      SourceDestinationServerUserData uData)
9021  throws ArgumentException
9022  {
9023    // Update the arguments used in the console interaction with the
9024    // actual arguments of dsreplication.
9025
9026    if (firstServerCommandBuilder != null)
9027    {
9028      for (Argument arg : firstServerCommandBuilder.getArguments())
9029      {
9030        if (OPTION_LONG_HOST.equals(arg.getLongIdentifier()))
9031        {
9032          commandBuilder.addArgument(getHostArg("hostSource", 'O', uData.getHostNameSource(),
9033              INFO_DESCRIPTION_INITIALIZE_REPLICATION_HOST_SOURCE));
9034        }
9035        else if (OPTION_LONG_PORT.equals(arg.getLongIdentifier()))
9036        {
9037          commandBuilder.addArgument(getPortArg("portSource", null, uData.getPortSource(),
9038              INFO_DESCRIPTION_INITIALIZE_REPLICATION_SERVER_PORT_SOURCE));
9039        }
9040        else if (OPTION_LONG_BINDPWD.equals(arg.getLongIdentifier()))
9041        {
9042          commandBuilder.addObfuscatedArgument(getAdminPasswordArg(arg));
9043        }
9044        else if (OPTION_LONG_BINDPWD_FILE.equals(arg.getLongIdentifier()))
9045        {
9046          commandBuilder.addArgument(getAdminPasswordFileArg(arg));
9047        }
9048        else
9049        {
9050          addArgument(commandBuilder, arg, firstServerCommandBuilder.isObfuscated(arg));
9051        }
9052      }
9053    }
9054
9055    if (sourceServerCI != null && sourceServerCI.getCommandBuilder() != null)
9056    {
9057      CommandBuilder interactionBuilder = sourceServerCI.getCommandBuilder();
9058      for (Argument arg : interactionBuilder.getArguments())
9059      {
9060        if (OPTION_LONG_HOST.equals(arg.getLongIdentifier()))
9061        {
9062          commandBuilder.addArgument(getHostArg("hostDestination", 'O', uData.getHostNameDestination(),
9063              INFO_DESCRIPTION_INITIALIZE_REPLICATION_HOST_DESTINATION));
9064        }
9065        else if (OPTION_LONG_PORT.equals(arg.getLongIdentifier()))
9066        {
9067          commandBuilder.addArgument(getPortArg("portDestination", null, uData.getPortDestination(),
9068              INFO_DESCRIPTION_INITIALIZE_REPLICATION_SERVER_PORT_DESTINATION));
9069        }
9070      }
9071    }
9072  }
9073
9074  private StringArgument getAdminPasswordArg(Argument arg) throws ArgumentException
9075  {
9076    StringArgument sArg = getAdminPasswordArg();
9077    sArg.addValue(arg.getValue());
9078    return sArg;
9079  }
9080
9081  private StringArgument getAdminPasswordArg() throws ArgumentException
9082  {
9083    return new StringArgument("adminPassword",
9084        OPTION_SHORT_BINDPWD, "adminPassword", false, false, true,
9085        INFO_BINDPWD_PLACEHOLDER.get(), null, null,
9086        INFO_DESCRIPTION_REPLICATION_ADMIN_BINDPASSWORD.get());
9087  }
9088
9089  private FileBasedArgument getAdminPasswordFileArg(Argument arg) throws ArgumentException
9090  {
9091    FileBasedArgument fbArg = new FileBasedArgument(
9092        "adminPasswordFile", OPTION_SHORT_BINDPWD_FILE, "adminPasswordFile", false, false,
9093        INFO_BINDPWD_FILE_PLACEHOLDER.get(), null, null,
9094        INFO_DESCRIPTION_REPLICATION_ADMIN_BINDPASSWORDFILE.get());
9095    fbArg.getNameToValueMap().putAll(((FileBasedArgument) arg).getNameToValueMap());
9096    return fbArg;
9097  }
9098
9099  private IntegerArgument getPortArg(String longIdentifier, Character shortIdentifier, int value, Arg0 arg)
9100      throws ArgumentException
9101  {
9102    IntegerArgument iArg = new IntegerArgument(longIdentifier, shortIdentifier, longIdentifier, false, false, true,
9103        INFO_PORT_PLACEHOLDER.get(), 4444, null, arg.get());
9104    iArg.addValue(String.valueOf(value));
9105    return iArg;
9106  }
9107
9108  private StringArgument getHostArg(String longIdentifier, char shortIdentifier, String value,
9109      Arg0 description) throws ArgumentException
9110  {
9111    StringArgument sArg = new StringArgument(longIdentifier, shortIdentifier, longIdentifier, false, false, true,
9112        INFO_HOST_PLACEHOLDER.get(), null, null, description.get());
9113    sArg.addValue(value);
9114    return sArg;
9115  }
9116
9117  private void updateAvailableAndReplicatedSuffixesForOneDomain(
9118      InitialLdapContext ctxDomain, InitialLdapContext ctxOther,
9119      Set<String> availableSuffixes, Set<String> alreadyReplicatedSuffixes)
9120  {
9121    Collection<ReplicaDescriptor> replicas = getReplicas(ctxDomain);
9122    int replicationPort = getReplicationPort(ctxOther);
9123    boolean isReplicationServerConfigured = replicationPort != -1;
9124    String replicationServer = getReplicationServer(getHostName(ctxOther), replicationPort);
9125    for (ReplicaDescriptor replica : replicas)
9126    {
9127      if (!isReplicationServerConfigured)
9128      {
9129        if (replica.isReplicated())
9130        {
9131          alreadyReplicatedSuffixes.add(replica.getSuffix().getDN());
9132        }
9133        availableSuffixes.add(replica.getSuffix().getDN());
9134      }
9135
9136      if (!isReplicationServerConfigured)
9137      {
9138        availableSuffixes.add(replica.getSuffix().getDN());
9139      }
9140      else if (!replica.isReplicated())
9141      {
9142        availableSuffixes.add(replica.getSuffix().getDN());
9143      }
9144      else if (containsIgnoreCase(replica.getReplicationServers(), replicationServer))
9145      {
9146        alreadyReplicatedSuffixes.add(replica.getSuffix().getDN());
9147      }
9148      else
9149      {
9150        availableSuffixes.add(replica.getSuffix().getDN());
9151      }
9152    }
9153  }
9154
9155  private void updateAvailableAndReplicatedSuffixesForNoDomain(
9156      InitialLdapContext ctx1, InitialLdapContext ctx2,
9157      Set<String> availableSuffixes, Set<String> alreadyReplicatedSuffixes)
9158  {
9159    int replicationPort1 = getReplicationPort(ctx1);
9160    boolean isReplicationServer1Configured = replicationPort1 != -1;
9161    String replicationServer1 = getReplicationServer(getHostName(ctx1), replicationPort1);
9162
9163    int replicationPort2 = getReplicationPort(ctx2);
9164    boolean isReplicationServer2Configured = replicationPort2 != -1;
9165    String replicationServer2 = getReplicationServer(getHostName(ctx2), replicationPort2);
9166
9167    TopologyCache cache1 = isReplicationServer1Configured ? createTopologyCache(ctx1) : null;
9168    TopologyCache cache2 = isReplicationServer2Configured ? createTopologyCache(ctx2) : null;
9169    if (cache1 != null && cache2 != null)
9170    {
9171      updateAvailableAndReplicatedSuffixesForNoDomainOneSense(cache1, cache2,
9172          replicationServer1, replicationServer2, availableSuffixes,
9173          alreadyReplicatedSuffixes);
9174      updateAvailableAndReplicatedSuffixesForNoDomainOneSense(cache2, cache1,
9175          replicationServer2, replicationServer1, availableSuffixes,
9176          alreadyReplicatedSuffixes);
9177    }
9178    else if (cache1 != null)
9179    {
9180      addAllAvailableSuffixes(availableSuffixes, cache1.getSuffixes(), replicationServer1);
9181    }
9182    else if (cache2 != null)
9183    {
9184      addAllAvailableSuffixes(availableSuffixes, cache2.getSuffixes(), replicationServer2);
9185    }
9186  }
9187
9188  private TopologyCache createTopologyCache(InitialLdapContext ctx)
9189  {
9190    try
9191    {
9192      ADSContext adsContext = new ADSContext(ctx);
9193      if (adsContext.hasAdminData())
9194      {
9195        TopologyCache cache = new TopologyCache(adsContext, getTrustManager(sourceServerCI), getConnectTimeout());
9196        cache.getFilter().setSearchMonitoringInformation(false);
9197        cache.setPreferredConnections(getPreferredConnections(ctx));
9198        cache.reloadTopology();
9199        return cache;
9200      }
9201    }
9202    catch (Throwable t)
9203    {
9204      logger.warn(LocalizableMessage.raw("Error loading topology cache in " + getLdapUrl(ctx) + ": " + t, t));
9205    }
9206    return null;
9207  }
9208
9209  private void addAllAvailableSuffixes(Collection<String> availableSuffixes,
9210      Set<SuffixDescriptor> suffixes, String rsToFind)
9211  {
9212    for (SuffixDescriptor suffix : suffixes)
9213    {
9214      for (String rs : suffix.getReplicationServers())
9215      {
9216        if (rs.equalsIgnoreCase(rsToFind))
9217        {
9218          availableSuffixes.add(suffix.getDN());
9219        }
9220      }
9221    }
9222  }
9223
9224  private void updateAvailableAndReplicatedSuffixesForNoDomainOneSense(
9225      TopologyCache cache1, TopologyCache cache2, String replicationServer1,
9226      String replicationServer2,
9227      Set<String> availableSuffixes, Set<String> alreadyReplicatedSuffixes)
9228  {
9229    for (SuffixDescriptor suffix : cache1.getSuffixes())
9230    {
9231      for (String rServer : suffix.getReplicationServers())
9232      {
9233        if (rServer.equalsIgnoreCase(replicationServer1))
9234        {
9235          boolean isSecondReplicatedInSameTopology = false;
9236          boolean isSecondReplicated = false;
9237          boolean isFirstReplicated = false;
9238          for (SuffixDescriptor suffix2 : cache2.getSuffixes())
9239          {
9240            if (areDnsEqual(suffix.getDN(), suffix2.getDN()))
9241            {
9242              for (String rServer2 : suffix2.getReplicationServers())
9243              {
9244                if (rServer2.equalsIgnoreCase(replicationServer2))
9245                {
9246                  isSecondReplicated = true;
9247                }
9248                if (rServer.equalsIgnoreCase(replicationServer2))
9249                {
9250                  isFirstReplicated = true;
9251                }
9252                if (isFirstReplicated && isSecondReplicated)
9253                {
9254                  isSecondReplicatedInSameTopology = true;
9255                  break;
9256                }
9257              }
9258              break;
9259            }
9260          }
9261          if (!isSecondReplicatedInSameTopology)
9262          {
9263            availableSuffixes.add(suffix.getDN());
9264          }
9265          else
9266          {
9267            alreadyReplicatedSuffixes.add(suffix.getDN());
9268          }
9269          break;
9270        }
9271      }
9272    }
9273  }
9274
9275  private void updateBaseDnsWithNotEnoughReplicationServer(ADSContext adsCtx1,
9276      ADSContext adsCtx2, EnableReplicationUserData uData,
9277      Set<String> baseDNsWithNoReplicationServer,
9278      Set<String> baseDNsWithOneReplicationServer)
9279  {
9280    EnableReplicationServerData server1 = uData.getServer1();
9281    EnableReplicationServerData server2 = uData.getServer2();
9282    if (server1.configureReplicationServer() &&
9283        server2.configureReplicationServer())
9284    {
9285      return;
9286    }
9287
9288    Set<SuffixDescriptor> suffixes = new HashSet<>();
9289    createTopologyCache(adsCtx1, uData, suffixes);
9290    createTopologyCache(adsCtx2, uData, suffixes);
9291
9292    int repPort1 = getReplicationPort(adsCtx1.getDirContext());
9293    String repServer1 =  getReplicationServer(server1.getHostName(), repPort1);
9294    int repPort2 = getReplicationPort(adsCtx2.getDirContext());
9295    String repServer2 =  getReplicationServer(server2.getHostName(), repPort2);
9296    for (String baseDN : uData.getBaseDNs())
9297    {
9298      int nReplicationServers = 0;
9299      for (SuffixDescriptor suffix : suffixes)
9300      {
9301        if (areDnsEqual(suffix.getDN(), baseDN))
9302        {
9303          Set<String> replicationServers = suffix.getReplicationServers();
9304          nReplicationServers += replicationServers.size();
9305          for (String repServer : replicationServers)
9306          {
9307            if (server1.configureReplicationServer() &&
9308                repServer.equalsIgnoreCase(repServer1))
9309            {
9310              nReplicationServers --;
9311            }
9312            if (server2.configureReplicationServer() &&
9313                repServer.equalsIgnoreCase(repServer2))
9314            {
9315              nReplicationServers --;
9316            }
9317          }
9318        }
9319      }
9320      if (server1.configureReplicationServer())
9321      {
9322        nReplicationServers ++;
9323      }
9324      if (server2.configureReplicationServer())
9325      {
9326        nReplicationServers ++;
9327      }
9328      if (nReplicationServers == 1)
9329      {
9330        baseDNsWithOneReplicationServer.add(baseDN);
9331      }
9332      else if (nReplicationServers == 0)
9333      {
9334        baseDNsWithNoReplicationServer.add(baseDN);
9335      }
9336    }
9337  }
9338
9339  private void createTopologyCache(ADSContext adsCtx, ReplicationUserData uData, Set<SuffixDescriptor> suffixes)
9340  {
9341    try
9342    {
9343      if (adsCtx.hasAdminData())
9344      {
9345        TopologyCache cache = new TopologyCache(adsCtx, getTrustManager(sourceServerCI), getConnectTimeout());
9346        cache.getFilter().setSearchMonitoringInformation(false);
9347        addBaseDNs(cache.getFilter(), uData.getBaseDNs());
9348        cache.reloadTopology();
9349        suffixes.addAll(cache.getSuffixes());
9350      }
9351    }
9352    catch (Throwable t)
9353    {
9354      String msg = "Error loading topology cache from " + getHostPort(adsCtx.getDirContext()) + ": " + t;
9355      logger.warn(LocalizableMessage.raw(msg, t));
9356    }
9357  }
9358
9359  /**
9360   * Merge the contents of the two registries but only does it partially.
9361   * Only one of the two ADSContext will be updated (in terms of data in
9362   * cn=admin data), while the other registry's replication servers will have
9363   * their truststore updated to be able to initialize all the contents.
9364   *
9365   * This method does NOT configure replication between topologies or initialize
9366   * replication.
9367   *
9368   * @param adsCtx1 the ADSContext of the first registry.
9369   * @param adsCtx2 the ADSContext of the second registry.
9370   * @return <CODE>true</CODE> if the registry containing all the data is
9371   * the first registry and <CODE>false</CODE> otherwise.
9372   * @throws ReplicationCliException if there is a problem reading or updating
9373   * the registries.
9374   */
9375  private boolean mergeRegistries(ADSContext adsCtx1, ADSContext adsCtx2)
9376  throws ReplicationCliException
9377  {
9378    PointAdder pointAdder = new PointAdder(this);
9379    try
9380    {
9381      Set<PreferredConnection> cnx = new LinkedHashSet<>(getPreferredConnections(adsCtx1.getDirContext()));
9382      cnx.addAll(getPreferredConnections(adsCtx2.getDirContext()));
9383      TopologyCache cache1 = createTopologyCache(adsCtx1, cnx);
9384      TopologyCache cache2 = createTopologyCache(adsCtx2, cnx);
9385
9386      // Look for the cache with biggest number of replication servers:
9387      // that one is going to be source.
9388      int nRepServers1 = countReplicationServers(cache1);
9389      int nRepServers2 = countReplicationServers(cache2);
9390
9391      InitialLdapContext ctxSource;
9392      InitialLdapContext ctxDestination;
9393      if (nRepServers1 >= nRepServers2)
9394      {
9395        ctxSource = adsCtx1.getDirContext();
9396        ctxDestination = adsCtx2.getDirContext();
9397      }
9398      else
9399      {
9400        ctxSource = adsCtx2.getDirContext();
9401        ctxDestination = adsCtx1.getDirContext();
9402      }
9403
9404      String hostPortSource = getHostPort(ctxSource);
9405      String hostPortDestination = getHostPort(ctxDestination);
9406      if (isInteractive())
9407      {
9408        LocalizableMessage msg = INFO_REPLICATION_MERGING_REGISTRIES_CONFIRMATION.get(hostPortSource,
9409            hostPortDestination, hostPortSource, hostPortDestination);
9410        if (!askConfirmation(msg, true))
9411        {
9412          throw new ReplicationCliException(ERR_REPLICATION_USER_CANCELLED.get(), USER_CANCELLED, null);
9413        }
9414      }
9415      else
9416      {
9417        LocalizableMessage msg = INFO_REPLICATION_MERGING_REGISTRIES_DESCRIPTION.get(hostPortSource,
9418            hostPortDestination, hostPortSource, hostPortDestination);
9419        println(msg);
9420        println();
9421      }
9422
9423      print(INFO_REPLICATION_MERGING_REGISTRIES_PROGRESS.get());
9424      pointAdder.start();
9425
9426      checkCanMergeReplicationTopologies(adsCtx1, cache1);
9427      checkCanMergeReplicationTopologies(adsCtx2, cache2);
9428
9429      Set<LocalizableMessage> commonRepServerIDErrors = new HashSet<>();
9430      for (ServerDescriptor server1 : cache1.getServers())
9431      {
9432        if (findSameReplicationServer(server1, cache2.getServers(), commonRepServerIDErrors))
9433        {
9434          break;
9435        }
9436      }
9437      Set<LocalizableMessage> commonDomainIDErrors = new HashSet<>();
9438      for (SuffixDescriptor suffix1 : cache1.getSuffixes())
9439      {
9440        for (ReplicaDescriptor replica1 : suffix1.getReplicas())
9441        {
9442          if (replica1.isReplicated())
9443          {
9444            for (SuffixDescriptor suffix2 : cache2.getSuffixes())
9445            {
9446              if (findReplicaInSuffix2(replica1, suffix2, suffix1.getDN(), commonDomainIDErrors))
9447              {
9448                break;
9449              }
9450            }
9451          }
9452        }
9453      }
9454      if (!commonRepServerIDErrors.isEmpty() || !commonDomainIDErrors.isEmpty())
9455      {
9456        LocalizableMessageBuilder mb = new LocalizableMessageBuilder();
9457        if (!commonRepServerIDErrors.isEmpty())
9458        {
9459          mb.append(ERR_REPLICATION_ENABLE_COMMON_REPLICATION_SERVER_ID.get(
9460              getMessageFromCollection(commonRepServerIDErrors, Constants.LINE_SEPARATOR)));
9461        }
9462        if (!commonDomainIDErrors.isEmpty())
9463        {
9464          if (mb.length() > 0)
9465          {
9466            mb.append(Constants.LINE_SEPARATOR);
9467          }
9468          mb.append(ERR_REPLICATION_ENABLE_COMMON_DOMAIN_ID.get(
9469              getMessageFromCollection(commonDomainIDErrors, Constants.LINE_SEPARATOR)));
9470        }
9471        throw new ReplicationCliException(mb.toMessage(),
9472            REPLICATION_ADS_MERGE_NOT_SUPPORTED, null);
9473      }
9474
9475      ADSContext adsCtxSource;
9476      ADSContext adsCtxDestination;
9477      TopologyCache cacheDestination;
9478      if (nRepServers1 >= nRepServers2)
9479      {
9480        adsCtxSource = adsCtx1;
9481        adsCtxDestination = adsCtx2;
9482        cacheDestination = cache2;
9483      }
9484      else
9485      {
9486        adsCtxSource = adsCtx2;
9487        adsCtxDestination = adsCtx1;
9488        cacheDestination = cache1;
9489      }
9490
9491      try
9492      {
9493        adsCtxSource.mergeWithRegistry(adsCtxDestination);
9494      }
9495      catch (ADSContextException adce)
9496      {
9497        logger.error(LocalizableMessage.raw("Error merging registry of "+
9498            getHostPort(adsCtxSource.getDirContext())+
9499            " with registry of "+
9500            getHostPort(adsCtxDestination.getDirContext())+" "+
9501            adce, adce));
9502        if (adce.getError() == ADSContextException.ErrorType.ERROR_MERGING)
9503        {
9504          throw new ReplicationCliException(adce.getMessageObject(),
9505          REPLICATION_ADS_MERGE_NOT_SUPPORTED, adce);
9506        }
9507        else
9508        {
9509          throw new ReplicationCliException(
9510              ERR_REPLICATION_UPDATING_ADS.get(adce.getMessageObject()),
9511              ERROR_UPDATING_ADS, adce);
9512        }
9513      }
9514
9515      try
9516      {
9517        for (ServerDescriptor server : cacheDestination.getServers())
9518        {
9519          if (server.isReplicationServer())
9520          {
9521            logger.info(LocalizableMessage.raw("Seeding to replication server on "+
9522                server.getHostPort(true)+" with certificates of "+
9523                getHostPort(adsCtxSource.getDirContext())));
9524            InitialLdapContext ctx = null;
9525            try
9526            {
9527              ctx = getDirContextForServer(cacheDestination, server);
9528              ServerDescriptor.seedAdsTrustStore(ctx,
9529                  adsCtxSource.getTrustedCertificates());
9530            }
9531            finally
9532            {
9533              close(ctx);
9534            }
9535          }
9536        }
9537      }
9538      catch (Throwable t)
9539      {
9540        logger.error(LocalizableMessage.raw("Error seeding truststore: "+t, t));
9541        LocalizableMessage msg = ERR_REPLICATION_ENABLE_SEEDING_TRUSTSTORE.get(getHostPort(adsCtx2.getDirContext()),
9542            getHostPort(adsCtx1.getDirContext()), toString(t));
9543        throw new ReplicationCliException(msg, ERROR_SEEDING_TRUSTORE, t);
9544      }
9545      pointAdder.stop();
9546      print(formatter.getSpace());
9547      print(formatter.getFormattedDone());
9548      println();
9549
9550      return adsCtxSource == adsCtx1;
9551    }
9552    finally
9553    {
9554      pointAdder.stop();
9555    }
9556  }
9557
9558  private int countReplicationServers(TopologyCache cache)
9559  {
9560    int nbRepServers = 0;
9561    for (ServerDescriptor server : cache.getServers())
9562    {
9563      if (server.isReplicationServer())
9564      {
9565        nbRepServers++;
9566      }
9567    }
9568    return nbRepServers;
9569  }
9570
9571  private void checkCanMergeReplicationTopologies(ADSContext adsCtx, TopologyCache cache)
9572      throws ReplicationCliException
9573  {
9574    Set<LocalizableMessage> cacheErrors = cache.getErrorMessages();
9575    if (!cacheErrors.isEmpty())
9576    {
9577      LocalizableMessage msg = getMessageFromCollection(cacheErrors, Constants.LINE_SEPARATOR);
9578      throw new ReplicationCliException(
9579          ERR_REPLICATION_CANNOT_MERGE_WITH_ERRORS.get(getHostPort(adsCtx.getDirContext()), msg),
9580          ERROR_READING_ADS, null);
9581    }
9582  }
9583
9584  private boolean findSameReplicationServer(ServerDescriptor serverToFind, Set<ServerDescriptor> servers,
9585      Set<LocalizableMessage> commonRepServerIDErrors)
9586  {
9587    if (!serverToFind.isReplicationServer())
9588    {
9589      return false;
9590    }
9591
9592    int replicationID1 = serverToFind.getReplicationServerId();
9593    String replServerHostPort1 = serverToFind.getReplicationServerHostPort();
9594    for (ServerDescriptor server2 : servers)
9595    {
9596      if (server2.isReplicationServer() && server2.getReplicationServerId() == replicationID1
9597          && !server2.getReplicationServerHostPort().equalsIgnoreCase(replServerHostPort1))
9598      {
9599        commonRepServerIDErrors.add(ERR_REPLICATION_ENABLE_COMMON_REPLICATION_SERVER_ID_ARG.get(
9600            serverToFind.getHostPort(true), server2.getHostPort(true), replicationID1));
9601        return true;
9602      }
9603    }
9604    return false;
9605  }
9606
9607  private boolean findReplicaInSuffix2(ReplicaDescriptor replica1, SuffixDescriptor suffix2, String suffix1DN,
9608      Set<LocalizableMessage> commonDomainIDErrors)
9609  {
9610    if (!areDnsEqual(suffix2.getDN(), replica1.getSuffix().getDN()))
9611    {
9612      // Conflicting domain names must apply to same suffix.
9613      return false;
9614    }
9615
9616    int domain1Id = replica1.getReplicationId();
9617    for (ReplicaDescriptor replica2 : suffix2.getReplicas())
9618    {
9619      if (replica2.isReplicated()
9620          && domain1Id == replica2.getReplicationId())
9621      {
9622        commonDomainIDErrors.add(
9623            ERR_REPLICATION_ENABLE_COMMON_DOMAIN_ID_ARG.get(replica1.getServer().getHostPort(true), suffix1DN,
9624                replica2.getServer().getHostPort(true), suffix2.getDN(), domain1Id));
9625        return true;
9626      }
9627    }
9628    return false;
9629  }
9630
9631  private String toString(Throwable t)
9632  {
9633    return (t instanceof OpenDsException) ?
9634        ((OpenDsException) t).getMessageObject().toString() : t.toString();
9635  }
9636
9637  private TopologyCache createTopologyCache(ADSContext adsCtx, Set<PreferredConnection> cnx)
9638      throws ReplicationCliException
9639  {
9640    TopologyCache cache = new TopologyCache(adsCtx, getTrustManager(sourceServerCI), getConnectTimeout());
9641    cache.setPreferredConnections(cnx);
9642    cache.getFilter().setSearchBaseDNInformation(false);
9643    try
9644    {
9645      cache.reloadTopology();
9646      return cache;
9647    }
9648    catch (TopologyCacheException te)
9649    {
9650      logger.error(LocalizableMessage.raw(
9651          "Error reading topology cache of " + getHostPort(adsCtx.getDirContext()) + " " + te, te));
9652      throw new ReplicationCliException(ERR_REPLICATION_READING_ADS.get(te.getMessageObject()), ERROR_UPDATING_ADS, te);
9653    }
9654  }
9655
9656  private InitialLdapContext getDirContextForServer(TopologyCache cache, ServerDescriptor server)
9657      throws NamingException
9658  {
9659    String dn = getBindDN(cache.getAdsContext().getDirContext());
9660    String pwd = getBindPassword(cache.getAdsContext().getDirContext());
9661    TopologyCacheFilter filter = new TopologyCacheFilter();
9662    filter.setSearchMonitoringInformation(false);
9663    filter.setSearchBaseDNInformation(false);
9664    ServerLoader loader = new ServerLoader(server.getAdsProperties(),
9665        dn, pwd, getTrustManager(sourceServerCI), getConnectTimeout(),
9666        cache.getPreferredConnections(), filter);
9667    return loader.createContext();
9668  }
9669
9670  /**
9671   * Returns <CODE>true</CODE> if the provided baseDN is replicated in the
9672   * provided server, <CODE>false</CODE> otherwise.
9673   * @param server the server.
9674   * @param baseDN the base DN.
9675   * @return <CODE>true</CODE> if the provided baseDN is replicated in the
9676   * provided server, <CODE>false</CODE> otherwise.
9677   */
9678  private boolean isBaseDNReplicated(ServerDescriptor server, String baseDN)
9679  {
9680    return findReplicated(server.getReplicas(), baseDN) != null;
9681  }
9682
9683  /**
9684   * Returns <CODE>true</CODE> if the provided baseDN is replicated between
9685   * both servers, <CODE>false</CODE> otherwise.
9686   * @param server1 the first server.
9687   * @param server2 the second server.
9688   * @param baseDN the base DN.
9689   * @return <CODE>true</CODE> if the provided baseDN is replicated between
9690   * both servers, <CODE>false</CODE> otherwise.
9691   */
9692  private boolean isBaseDNReplicated(ServerDescriptor server1,
9693      ServerDescriptor server2, String baseDN)
9694  {
9695    final ReplicaDescriptor replica1 = findReplicated(server1.getReplicas(), baseDN);
9696    final ReplicaDescriptor replica2 = findReplicated(server2.getReplicas(), baseDN);
9697    if (replica1 != null && replica2 != null)
9698    {
9699      Set<String> replServers1 = replica1.getSuffix().getReplicationServers();
9700      Set<String> replServers2 = replica1.getSuffix().getReplicationServers();
9701      for (String replServer1 : replServers1)
9702      {
9703        if (containsIgnoreCase(replServers2, replServer1))
9704        {
9705          // it is replicated in both
9706          return true;
9707        }
9708      }
9709    }
9710    return false;
9711  }
9712
9713  private ReplicaDescriptor findReplicated(Set<ReplicaDescriptor> replicas, String baseDN)
9714  {
9715    for (ReplicaDescriptor replica : replicas)
9716    {
9717      if (areDnsEqual(replica.getSuffix().getDN(), baseDN))
9718      {
9719        return replica;
9720      }
9721    }
9722    return null;
9723  }
9724
9725  private boolean displayLogFileAtEnd(String subCommand)
9726  {
9727    final List<String> subCommands = Arrays.asList(ENABLE_REPLICATION_SUBCMD_NAME, DISABLE_REPLICATION_SUBCMD_NAME,
9728        INITIALIZE_ALL_REPLICATION_SUBCMD_NAME, INITIALIZE_REPLICATION_SUBCMD_NAME, RESET_CHANGE_NUMBER_SUBCMD_NAME);
9729    return subCommands.contains(subCommand);
9730  }
9731
9732  /**
9733   * Returns the timeout to be used to connect in milliseconds.  The method
9734   * must be called after parsing the arguments.
9735   * @return the timeout to be used to connect in milliseconds.  Returns
9736   * {@code 0} if there is no timeout.
9737   */
9738  private int getConnectTimeout()
9739  {
9740    return argParser.getConnectTimeout();
9741  }
9742
9743  private String binDir;
9744
9745  /**
9746   * Returns the binary/script directory.
9747   * @return the binary/script directory.
9748   */
9749  private String getBinaryDir()
9750  {
9751    if (binDir == null)
9752    {
9753      File f = Installation.getLocal().getBinariesDirectory();
9754      try
9755      {
9756        binDir = f.getCanonicalPath();
9757      }
9758      catch (Throwable t)
9759      {
9760        binDir = f.getAbsolutePath();
9761      }
9762      if (binDir.lastIndexOf(File.separatorChar) != binDir.length() - 1)
9763      {
9764        binDir += File.separatorChar;
9765      }
9766    }
9767    return binDir;
9768  }
9769
9770  /**
9771   * Returns the full path of the command-line for a given script name.
9772   * @param scriptBasicName the script basic name (with no extension).
9773   * @return the full path of the command-line for a given script name.
9774   */
9775  private String getCommandLinePath(String scriptBasicName)
9776  {
9777    if (isWindows())
9778    {
9779      return getBinaryDir() + scriptBasicName + ".bat";
9780    }
9781    return getBinaryDir() + scriptBasicName;
9782  }
9783}
9784
9785/** Class used to compare replication servers. */
9786class ReplicationServerComparator implements Comparator<ServerDescriptor>
9787{
9788  /** {@inheritDoc} */
9789  @Override
9790  public int compare(ServerDescriptor s1, ServerDescriptor s2)
9791  {
9792    int compare = s1.getHostName().compareTo(s2.getHostName());
9793    if (compare == 0)
9794    {
9795      if (s1.getReplicationServerPort() > s2.getReplicationServerPort())
9796      {
9797        return 1;
9798      }
9799      else if (s1.getReplicationServerPort() < s2.getReplicationServerPort())
9800      {
9801        return -1;
9802      }
9803    }
9804    return compare;
9805  }
9806}
9807
9808/** Class used to compare suffixes. */
9809class SuffixComparator implements Comparator<SuffixDescriptor>
9810{
9811  @Override
9812  public int compare(SuffixDescriptor s1, SuffixDescriptor s2)
9813  {
9814    return s1.getId().compareTo(s2.getId());
9815  }
9816}
9817
9818/** Class used to compare servers. */
9819class ServerComparator implements Comparator<ServerDescriptor>
9820{
9821  @Override
9822  public int compare(ServerDescriptor s1, ServerDescriptor s2)
9823  {
9824    return s1.getId().compareTo(s2.getId());
9825  }
9826}