001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions Copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2007-2010 Sun Microsystems, Inc.
015 * Portions Copyright 2012-2016 ForgeRock AS.
016 */
017package org.opends.server.tools.dsreplication;
018
019import static com.forgerock.opendj.cli.ArgumentConstants.*;
020import static com.forgerock.opendj.cli.CliMessages.INFO_BINDPWD_FILE_PLACEHOLDER;
021import static com.forgerock.opendj.cli.CliMessages.INFO_PORT_PLACEHOLDER;
022import static com.forgerock.opendj.cli.Utils.*;
023import static com.forgerock.opendj.cli.CommonArguments.*;
024
025import static org.opends.messages.AdminToolMessages.*;
026import static org.opends.messages.ToolMessages.*;
027
028import java.io.File;
029import java.io.OutputStream;
030import java.util.ArrayList;
031import java.util.Collection;
032import java.util.List;
033
034import org.forgerock.i18n.LocalizableMessage;
035import org.forgerock.i18n.LocalizableMessageBuilder;
036import org.opends.quicksetup.Constants;
037import org.opends.server.admin.AdministrationConnector;
038import org.opends.server.admin.client.cli.SecureConnectionCliArgs;
039import org.opends.server.admin.client.cli.SecureConnectionCliParser;
040import org.opends.server.admin.client.cli.TaskScheduleArgs;
041import org.opends.server.core.DirectoryServer.DirectoryServerVersionHandler;
042import org.opends.server.extensions.ConfigFileHandler;
043import org.opends.server.tasks.PurgeConflictsHistoricalTask;
044
045import com.forgerock.opendj.cli.Argument;
046import com.forgerock.opendj.cli.ArgumentException;
047import com.forgerock.opendj.cli.ArgumentGroup;
048import com.forgerock.opendj.cli.BooleanArgument;
049import com.forgerock.opendj.cli.ClientException;
050import com.forgerock.opendj.cli.CommonArguments;
051import com.forgerock.opendj.cli.FileBasedArgument;
052import com.forgerock.opendj.cli.IntegerArgument;
053import com.forgerock.opendj.cli.StringArgument;
054import com.forgerock.opendj.cli.SubCommand;
055
056/**
057 * This class is used to parse the arguments passed to the replication CLI.
058 * It also checks the compatibility between the values and that all the
059 * required information has been provided.  However it does not do any
060 * verification that require connection to any server.
061 */
062public class ReplicationCliArgumentParser extends SecureConnectionCliParser
063{
064  /** Arguments used when enabling replication for a server. */
065  static class ServerArgs
066  {
067    /** The 'hostName' argument for the first server. */
068    StringArgument hostNameArg;
069    /** The 'port' argument for the first server. */
070    IntegerArgument portArg;
071    /** The 'bindDN' argument for the first server. */
072    StringArgument bindDnArg;
073    /** The 'bindPasswordFile' argument for the first server. */
074    FileBasedArgument bindPasswordFileArg;
075    /** The 'bindPassword' argument for the first server. */
076    StringArgument bindPasswordArg;
077    /** The 'replicationPort' argument for the first server. */
078    IntegerArgument replicationPortArg;
079    /** The 'noReplicationServer' argument for the first server. */
080    BooleanArgument noReplicationServerArg;
081    /** The 'onlyReplicationServer' argument for the first server. */
082    BooleanArgument onlyReplicationServerArg;
083    /** The 'secureReplication' argument for the first server. */
084    BooleanArgument secureReplicationArg;
085
086
087    /**
088     * Get the password which has to be used for the command to connect to this server without
089     * prompting the user in the enable replication subcommand. If no password was specified return
090     * null.
091     *
092     * @return the password which has to be used for the command to connect to this server without
093     *         prompting the user in the enable replication subcommand. If no password was specified
094     *         return null.
095     */
096    String getBindPassword()
097    {
098      return ReplicationCliArgumentParser.getBindPassword(bindPasswordArg, bindPasswordFileArg);
099    }
100
101    boolean configureReplicationDomain()
102    {
103      return !onlyReplicationServerArg.isPresent();
104    }
105
106    boolean configureReplicationServer()
107    {
108      return !noReplicationServerArg.isPresent();
109    }
110  }
111
112  private SubCommand enableReplicationSubCmd;
113  private SubCommand disableReplicationSubCmd;
114  private SubCommand initializeReplicationSubCmd;
115  private SubCommand initializeAllReplicationSubCmd;
116  private SubCommand postExternalInitializationSubCmd;
117  private SubCommand preExternalInitializationSubCmd;
118  private SubCommand resetChangelogNumber;
119  private SubCommand statusReplicationSubCmd;
120  private SubCommand purgeHistoricalSubCmd;
121
122  private int defaultAdminPort =
123    AdministrationConnector.DEFAULT_ADMINISTRATION_CONNECTOR_PORT;
124
125  /** No-prompt argument. */
126  BooleanArgument noPromptArg;
127  private String defaultLocalHostValue;
128
129  /** Arguments for the first server. */
130  ServerArgs server1 = new ServerArgs();
131  /** Arguments for the second server. */
132  ServerArgs server2 = new ServerArgs();
133
134  /** The 'skipPortCheckArg' argument to not check replication ports. */
135  private BooleanArgument skipPortCheckArg;
136  /** The 'noSchemaReplication' argument to not replicate schema. */
137  BooleanArgument noSchemaReplicationArg;
138  /** The 'useSecondServerAsSchemaSource' argument to not replicate schema. */
139  private BooleanArgument useSecondServerAsSchemaSourceArg;
140  /** The 'disableAll' argument to disable all the replication configuration of server. */
141  BooleanArgument disableAllArg;
142  /** The 'disableReplicationServer' argument to disable the replication server. */
143  BooleanArgument disableReplicationServerArg;
144  /** The 'hostName' argument for the source server. */
145  private StringArgument hostNameSourceArg;
146  /** The 'port' argument for the source server. */
147  private IntegerArgument portSourceArg;
148  /** The 'hostName' argument for the destination server. */
149  private StringArgument hostNameDestinationArg;
150  /** The 'port' argument for the destination server. */
151  private IntegerArgument portDestinationArg;
152  /** The 'suffixes' global argument. */
153  StringArgument baseDNsArg;
154  /**The 'quiet' argument.   */
155  private BooleanArgument quietArg;
156  /**The 'scriptFriendly' argument.   */
157  BooleanArgument scriptFriendlyArg;
158  /**Properties file argument.   */
159  StringArgument propertiesFileArgument;
160  /**No-properties file argument.   */
161  BooleanArgument noPropertiesFileArgument;
162  /**
163   * The argument that the user must set to display the equivalent
164   * non-interactive mode argument.
165   */
166  BooleanArgument displayEquivalentArgument;
167  /**
168   * The argument that allows the user to dump the equivalent non-interactive
169   * command to a file.
170   */
171  StringArgument equivalentCommandFileArgument;
172  /** The argument that the user must set to have advanced options in interactive mode. */
173  BooleanArgument advancedArg;
174
175  /**
176   * The argument set by the user to specify the configuration class
177   * (useful when dsreplication purge-historical runs locally).
178   */
179  private StringArgument  configClassArg;
180
181  /**
182   * The argument set by the user to specify the configuration file
183   * (useful when dsreplication purge-historical runs locally).
184   */
185  private StringArgument  configFileArg;
186
187  TaskScheduleArgs taskArgs;
188
189  /** The 'maximumDuration' argument for the purge of historical. */
190  IntegerArgument maximumDurationArg;
191
192  /** the 'change-number' argument for task reset-changenumber. */
193  IntegerArgument resetChangeNumber;
194
195  /** The text of the enable replication subcommand. */
196  static final String ENABLE_REPLICATION_SUBCMD_NAME = "enable";
197  /** The text of the disable replication subcommand. */
198  static final String DISABLE_REPLICATION_SUBCMD_NAME = "disable";
199  /** The text of the initialize replication subcommand. */
200  static final String INITIALIZE_REPLICATION_SUBCMD_NAME = "initialize";
201  /** The text of the initialize all replication subcommand. */
202  public static final String INITIALIZE_ALL_REPLICATION_SUBCMD_NAME = "initialize-all";
203  /** The text of the pre external initialization subcommand. */
204  static final String PRE_EXTERNAL_INITIALIZATION_SUBCMD_NAME = "pre-external-initialization";
205  /** The text of the initialize all replication subcommand. */
206  static final String POST_EXTERNAL_INITIALIZATION_SUBCMD_NAME = "post-external-initialization";
207  /** The text of the reset changenumber subcommand. */
208  static final String RESET_CHANGE_NUMBER_SUBCMD_NAME = "reset-change-number";
209
210  /** The text of the status replication subcommand. */
211  static final String STATUS_REPLICATION_SUBCMD_NAME = "status";
212  /** The text of the purge historical subcommand. */
213  static final String PURGE_HISTORICAL_SUBCMD_NAME = "purge-historical";
214  /** This CLI is always using the administration connector with SSL. */
215  private static final boolean alwaysSSL = true;
216
217  /**
218   * Creates a new instance of this argument parser with no arguments.
219   *
220   * @param mainClassName
221   *          The fully-qualified name of the Java class that should
222   *          be invoked to launch the program with which this
223   *          argument parser is associated.
224   */
225  ReplicationCliArgumentParser(String mainClassName)
226  {
227    super(mainClassName,
228        INFO_REPLICATION_TOOL_DESCRIPTION.get(ENABLE_REPLICATION_SUBCMD_NAME, INITIALIZE_REPLICATION_SUBCMD_NAME),
229        false);
230    setShortToolDescription(REF_SHORT_DESC_DSREPLICATION.get());
231    setVersionHandler(new DirectoryServerVersionHandler());
232  }
233
234  /**
235   * Initialize the parser with the Global options and subcommands.
236   *
237   * @param outStream
238   *          The output stream to use for standard output, or {@code null}
239   *          if standard output is not needed.
240   * @throws ArgumentException
241   *           If there is a problem with any of the parameters used to create this argument.
242   */
243  void initializeParser(OutputStream outStream)
244      throws ArgumentException
245  {
246    taskArgs = new TaskScheduleArgs();
247    initializeGlobalArguments(outStream);
248    try
249    {
250      defaultAdminPort = secureArgsList.getAdminPortFromConfig();
251    }
252    catch (Throwable t)
253    {
254      // Ignore
255    }
256    createEnableReplicationSubCommand();
257    createDisableReplicationSubCommand();
258    createRelatedServersOptions();
259    createInitializeReplicationSubCommand();
260    createInitializeAllReplicationSubCommand();
261    createPreExternalInitializationSubCommand();
262    createPostExternalInitializationSubCommand();
263    createResetChangeNumberSubCommand();
264    createStatusReplicationSubCommand();
265    createPurgeHistoricalSubCommand();
266  }
267
268  /**
269   * Checks all the options parameters and updates the provided LocalizableMessageBuilder
270   * with the errors that where encountered.
271   *
272   * This method assumes that the method parseArguments for the parser has
273   * already been called.
274   * @param buf the LocalizableMessageBuilder object where we add the error messages
275   * describing the errors encountered.
276   */
277  void validateOptions(LocalizableMessageBuilder buf)
278  {
279    validateGlobalOptions(buf);
280    validateSubcommandOptions(buf);
281  }
282
283  /** {@inheritDoc} */
284  @Override
285  public int validateGlobalOptions(LocalizableMessageBuilder buf)
286  {
287    int returnValue;
288    super.validateGlobalOptions(buf);
289
290    final List<LocalizableMessage> errors = new ArrayList<>();
291    // Check that we can write on the provided path where we write the
292    // equivalent non-interactive commands.
293    if (equivalentCommandFileArgument.isPresent())
294    {
295      String file = equivalentCommandFileArgument.getValue();
296      if (!canWrite(file))
297      {
298        errors.add(ERR_REPLICATION_CANNOT_WRITE_EQUIVALENT_COMMAND_LINE_FILE.get(file));
299      }
300      else
301      {
302        File f = new File(file);
303        if (f.isDirectory())
304        {
305          errors.add(
306              ERR_REPLICATION_EQUIVALENT_COMMAND_LINE_FILE_DIRECTORY.get(file));
307        }
308      }
309    }
310
311    addErrorMessageIfArgumentsConflict(errors, noPromptArg, advancedArg);
312
313    if (!isInteractive())
314    {
315      // Check that we have the required data
316      if (!baseDNsArg.isPresent() &&
317          !isStatusReplicationSubcommand() &&
318          !isResetChangeNumber() &&
319          !disableAllArg.isPresent() &&
320          !disableReplicationServerArg.isPresent())
321      {
322        errors.add(ERR_REPLICATION_NO_BASE_DN_PROVIDED.get());
323      }
324      if (getBindPasswordAdmin() == null &&
325          !isPurgeHistoricalSubcommand())
326      {
327        errors.add(ERR_REPLICATION_NO_ADMINISTRATOR_PASSWORD_PROVIDED.get(
328            "--"+ secureArgsList.getBindPasswordArg().getLongIdentifier(),
329            "--"+ secureArgsList.getBindPasswordFileArg().getLongIdentifier()));
330      }
331    }
332
333    if (baseDNsArg.isPresent())
334    {
335      List<String> baseDNs = baseDNsArg.getValues();
336      for (String dn : baseDNs)
337      {
338        if (!isDN(dn))
339        {
340          errors.add(ERR_REPLICATION_NOT_A_VALID_BASEDN.get(dn));
341        }
342        if (dn.equalsIgnoreCase(Constants.REPLICATION_CHANGES_DN))
343        {
344          errors.add(ERR_REPLICATION_NOT_A_USER_SUFFIX.get(Constants.REPLICATION_CHANGES_DN));
345        }
346      }
347    }
348    if (!errors.isEmpty())
349    {
350      for (LocalizableMessage error : errors)
351      {
352        addMessage(buf, error);
353      }
354    }
355
356    if (buf.length() > 0)
357    {
358      returnValue = ReplicationCliReturnCode.CONFLICTING_ARGS.getReturnCode();
359    }
360    else
361    {
362      returnValue = ReplicationCliReturnCode.SUCCESSFUL_NOP.getReturnCode();
363    }
364    return returnValue;
365  }
366
367  /**
368   * Initialize Global option.
369   *
370   * @param outStream
371   *          The output stream used for the usage.
372   * @throws ArgumentException
373   *           If there is a problem with any of the parameters used
374   *           to create this argument.
375   */
376  private void initializeGlobalArguments(OutputStream outStream)
377  throws ArgumentException
378  {
379    ArrayList<Argument> defaultArgs = new ArrayList<>(createGlobalArguments(outStream, alwaysSSL));
380
381    Argument[] argsToRemove = {
382            secureArgsList.getHostNameArg(),
383            secureArgsList.getPortArg(),
384            secureArgsList.getBindDnArg(),
385            secureArgsList.getBindPasswordFileArg(),
386            secureArgsList.getBindPasswordArg()
387    };
388
389    for (Argument arg : argsToRemove)
390    {
391      defaultArgs.remove(arg);
392    }
393    defaultArgs.remove(super.noPropertiesFileArg);
394    defaultArgs.remove(super.propertiesFileArg);
395    // Remove it from the default location and redefine it.
396    defaultArgs.remove(getAdminUidArg());
397
398    int index = 0;
399
400    baseDNsArg =
401            StringArgument.builder(OPTION_LONG_BASEDN)
402                    .shortIdentifier(OPTION_SHORT_BASEDN)
403                    .description(INFO_DESCRIPTION_REPLICATION_BASEDNS.get())
404                    .multiValued()
405                    .valuePlaceholder(INFO_BASEDN_PLACEHOLDER.get())
406                    .buildArgument();
407    defaultArgs.add(index++, baseDNsArg);
408
409    secureArgsList.createVisibleAdminUidArgument(
410        INFO_DESCRIPTION_REPLICATION_ADMIN_UID.get(ENABLE_REPLICATION_SUBCMD_NAME));
411    defaultArgs.add(index++, secureArgsList.getAdminUidArg());
412
413    secureArgsList.setBindPasswordArgument(
414            StringArgument.builder(OPTION_LONG_ADMIN_PWD)
415                    .shortIdentifier(OPTION_SHORT_BINDPWD)
416                    .description(INFO_DESCRIPTION_REPLICATION_ADMIN_BINDPASSWORD.get())
417                    .valuePlaceholder(INFO_BINDPWD_PLACEHOLDER.get())
418                    .buildArgument());
419    defaultArgs.add(index++, secureArgsList.getBindPasswordArg());
420
421    secureArgsList.setBindPasswordFileArgument(
422            FileBasedArgument.builder(OPTION_LONG_ADMIN_PWD_FILE)
423                    .shortIdentifier(OPTION_SHORT_BINDPWD_FILE)
424                    .description(INFO_DESCRIPTION_REPLICATION_ADMIN_BINDPASSWORDFILE.get())
425                    .valuePlaceholder(INFO_BINDPWD_FILE_PLACEHOLDER.get())
426                    .buildArgument());
427    defaultArgs.add(index++, secureArgsList.getBindPasswordFileArg());
428
429    defaultArgs.remove(verboseArg);
430
431    quietArg = quietArgument();
432    defaultArgs.add(index++, quietArg);
433
434    noPromptArg = noPromptArgument();
435    defaultArgs.add(index++, noPromptArg);
436
437    displayEquivalentArgument = displayEquivalentCommandArgument();
438
439    defaultArgs.add(index++, displayEquivalentArgument);
440
441    equivalentCommandFileArgument =
442        CommonArguments
443            .equivalentCommandFileArgument(
444                INFO_REPLICATION_DESCRIPTION_EQUIVALENT_COMMAND_FILE_PATH.get());
445    defaultArgs.add(index++, equivalentCommandFileArgument);
446
447    advancedArg = advancedModeArgument();
448    defaultArgs.add(index++, advancedArg);
449
450    configClassArg =
451        configClassArgument(ConfigFileHandler.class.getName());
452    defaultArgs.add(index++, configClassArg);
453
454    configFileArg = configFileArgument();
455    defaultArgs.add(index++, configFileArg);
456
457    this.propertiesFileArgument = propertiesFileArgument();
458    defaultArgs.add(this.propertiesFileArgument);
459    setFilePropertiesArgument(this.propertiesFileArgument);
460
461    this.noPropertiesFileArgument = noPropertiesFileArgument();
462    defaultArgs.add(this.noPropertiesFileArgument);
463    setNoPropertiesFileArgument(this.noPropertiesFileArgument);
464
465    initializeGlobalArguments(defaultArgs, null);
466  }
467
468  /**
469   * Initialize the global options with the provided set of arguments.
470   * @param args the arguments to use to initialize the global options.
471   * @param argGroup the group to which args will be added.
472   * @throws ArgumentException if there is a conflict with the provided
473   * arguments.
474   */
475  @Override
476  protected void initializeGlobalArguments(
477          Collection<Argument> args,
478          ArgumentGroup argGroup)
479  throws ArgumentException
480  {
481
482    for (Argument arg : args)
483    {
484      if (arg == advancedArg)
485      {
486        ArgumentGroup toolOptionsGroup = new ArgumentGroup(
487            INFO_DESCRIPTION_CONFIG_OPTIONS_ARGS.get(), 2);
488        addGlobalArgument(advancedArg, toolOptionsGroup);
489      }
490      else
491      {
492        addGlobalArgument(arg, argGroup);
493      }
494    }
495
496    // Set the propertiesFile argument
497    setFilePropertiesArgument(propertiesFileArg);
498  }
499
500  /**
501   * Creates the enable replication subcommand and all the specific options
502   * for the subcommand.
503   */
504  private void createEnableReplicationSubCommand() throws ArgumentException
505  {
506    createServerArgs1();
507    createServerArgs2();
508
509    skipPortCheckArg =
510            BooleanArgument.builder("skipPortCheck")
511                    .shortIdentifier('S')
512                    .description(INFO_DESCRIPTION_ENABLE_REPLICATION_SKIPPORT.get())
513                    .buildArgument();
514    noSchemaReplicationArg =
515            BooleanArgument.builder("noSchemaReplication")
516                    .description(INFO_DESCRIPTION_ENABLE_REPLICATION_NO_SCHEMA_REPLICATION.get())
517                    .buildArgument();
518    useSecondServerAsSchemaSourceArg =
519            BooleanArgument.builder("useSecondServerAsSchemaSource")
520                    .description(INFO_DESCRIPTION_ENABLE_REPLICATION_USE_SECOND_AS_SCHEMA_SOURCE.get(
521                            "--" + noSchemaReplicationArg.getLongIdentifier()))
522                    .buildArgument();
523
524    enableReplicationSubCmd = new SubCommand(this,
525        ENABLE_REPLICATION_SUBCMD_NAME,
526        INFO_DESCRIPTION_SUBCMD_ENABLE_REPLICATION.get());
527    addArgumentsToSubCommand(enableReplicationSubCmd,
528            server1.hostNameArg, server1.portArg, server1.bindDnArg, server1.bindPasswordArg,
529            server1.bindPasswordFileArg, server1.replicationPortArg, server1.secureReplicationArg,
530            server1.noReplicationServerArg, server1.onlyReplicationServerArg,
531            server2.hostNameArg, server2.portArg, server2.bindDnArg, server2.bindPasswordArg,
532            server2.bindPasswordFileArg, server2.replicationPortArg, server2.secureReplicationArg,
533            server2.noReplicationServerArg, server2.onlyReplicationServerArg,
534            skipPortCheckArg, noSchemaReplicationArg, useSecondServerAsSchemaSourceArg);
535  }
536
537  private void createServerArgs1() throws ArgumentException
538  {
539    ServerArgs server = server1;
540    server.hostNameArg =
541            StringArgument.builder("host1")
542                    .shortIdentifier(OPTION_SHORT_HOST)
543                    .description(INFO_DESCRIPTION_ENABLE_REPLICATION_HOST1.get())
544                    .defaultValue(secureArgsList.getDefaultHostName())
545                    .valuePlaceholder(INFO_HOST_PLACEHOLDER.get())
546                    .buildArgument();
547    server.portArg =
548            IntegerArgument.builder("port1")
549                    .shortIdentifier(OPTION_SHORT_PORT)
550                    .description(INFO_DESCRIPTION_ENABLE_REPLICATION_SERVER_PORT1.get())
551                    .range(1, 65336)
552                    .defaultValue(defaultAdminPort)
553                    .valuePlaceholder(INFO_PORT_PLACEHOLDER.get())
554                    .buildArgument();
555    server.bindDnArg =
556            StringArgument.builder("bindDN1")
557                    .shortIdentifier(OPTION_SHORT_BINDDN)
558                    .description(INFO_DESCRIPTION_ENABLE_REPLICATION_BINDDN1.get())
559                    .defaultValue("cn=Directory Manager")
560                    .valuePlaceholder(INFO_BINDDN_PLACEHOLDER.get())
561                    .buildArgument();
562    server.bindPasswordArg =
563            StringArgument.builder("bindPassword1")
564                    .description(INFO_DESCRIPTION_ENABLE_REPLICATION_BINDPASSWORD1.get())
565                    .valuePlaceholder(INFO_BINDPWD_PLACEHOLDER.get())
566                    .buildArgument();
567    server.bindPasswordFileArg =
568            FileBasedArgument.builder("bindPasswordFile1")
569                    .description(INFO_DESCRIPTION_ENABLE_REPLICATION_BINDPASSWORDFILE1.get())
570                    .valuePlaceholder(INFO_BINDPWD_FILE_PLACEHOLDER.get())
571                    .buildArgument();
572    server.replicationPortArg =
573            IntegerArgument.builder("replicationPort1")
574                    .shortIdentifier('r')
575                    .description(INFO_DESCRIPTION_ENABLE_REPLICATION_PORT1.get())
576                    .range(1, 65336)
577                    .defaultValue(8989)
578                    .valuePlaceholder(INFO_PORT_PLACEHOLDER.get())
579                    .buildArgument();
580    server.secureReplicationArg =
581            BooleanArgument.builder("secureReplication1")
582                    .description(INFO_DESCRIPTION_ENABLE_SECURE_REPLICATION1.get())
583                    .buildArgument();
584    server.noReplicationServerArg =
585            BooleanArgument.builder("noReplicationServer1")
586                    .description(INFO_DESCRIPTION_ENABLE_REPLICATION_NO_REPLICATION_SERVER1.get())
587                    .buildArgument();
588    server.onlyReplicationServerArg =
589            BooleanArgument.builder("onlyReplicationServer1")
590                    .description(INFO_DESCRIPTION_ENABLE_REPLICATION_ONLY_REPLICATION_SERVER1.get())
591                    .buildArgument();
592  }
593
594  private void createServerArgs2() throws ArgumentException
595  {
596    ServerArgs server = server2;
597    server.hostNameArg =
598            StringArgument.builder("host2")
599                    .shortIdentifier('O')
600                    .description(INFO_DESCRIPTION_ENABLE_REPLICATION_HOST2.get())
601                    .defaultValue(secureArgsList.getDefaultHostName())
602                    .valuePlaceholder(INFO_HOST_PLACEHOLDER.get())
603                    .buildArgument();
604    server.portArg =
605            IntegerArgument.builder("port2")
606                    .description(INFO_DESCRIPTION_ENABLE_REPLICATION_SERVER_PORT2.get())
607                    .range(1, 65336)
608                    .defaultValue(defaultAdminPort)
609                    .valuePlaceholder(INFO_PORT_PLACEHOLDER.get())
610                    .buildArgument();
611    server.bindDnArg =
612            StringArgument.builder("bindDN2")
613                    .description(INFO_DESCRIPTION_ENABLE_REPLICATION_BINDDN2.get())
614                    .defaultValue("cn=Directory Manager")
615                    .valuePlaceholder(INFO_BINDDN_PLACEHOLDER.get())
616                    .buildArgument();
617    server.bindPasswordArg =
618            StringArgument.builder("bindPassword2")
619                    .description(INFO_DESCRIPTION_ENABLE_REPLICATION_BINDPASSWORD2.get())
620                    .valuePlaceholder(INFO_BINDPWD_PLACEHOLDER.get())
621                    .buildArgument();
622    server.bindPasswordFileArg =
623            FileBasedArgument.builder("bindPasswordFile2")
624                    .shortIdentifier('F')
625                    .description(INFO_DESCRIPTION_ENABLE_REPLICATION_BINDPASSWORDFILE2.get())
626                    .valuePlaceholder(INFO_BINDPWD_FILE_PLACEHOLDER.get())
627                    .buildArgument();
628    server.replicationPortArg =
629            IntegerArgument.builder("replicationPort2")
630                    .shortIdentifier('R')
631                    .description(INFO_DESCRIPTION_ENABLE_REPLICATION_PORT2.get())
632                    .range(1, 65336)
633                    .defaultValue(8989)
634                    .valuePlaceholder(INFO_PORT_PLACEHOLDER.get())
635                    .buildArgument();
636    server.secureReplicationArg =
637            BooleanArgument.builder("secureReplication2")
638                    .description(INFO_DESCRIPTION_ENABLE_SECURE_REPLICATION2.get())
639                    .buildArgument();
640    server.noReplicationServerArg =
641            BooleanArgument.builder("noReplicationServer2")
642                    .description(INFO_DESCRIPTION_ENABLE_REPLICATION_NO_REPLICATION_SERVER2.get())
643                    .buildArgument();
644    server.onlyReplicationServerArg =
645            BooleanArgument.builder("onlyReplicationServer2")
646                    .description(INFO_DESCRIPTION_ENABLE_REPLICATION_ONLY_REPLICATION_SERVER2.get())
647                    .buildArgument();
648  }
649
650  /**
651   * Creates the disable replication subcommand and all the specific options
652   * for the subcommand.  Note: this method assumes that
653   * initializeGlobalArguments has already been called and that hostNameArg and
654   * portArg have been created.
655   */
656  private void createDisableReplicationSubCommand()
657  throws ArgumentException
658  {
659    disableReplicationSubCmd = new SubCommand(this,
660        DISABLE_REPLICATION_SUBCMD_NAME,
661        INFO_DESCRIPTION_SUBCMD_DISABLE_REPLICATION.get());
662    secureArgsList.setBindDnArgDescription(INFO_DESCRIPTION_DISABLE_REPLICATION_BINDDN.get());
663    disableReplicationServerArg =
664            BooleanArgument.builder("disableReplicationServer")
665                    .shortIdentifier('a')
666                    .description(INFO_DESCRIPTION_DISABLE_REPLICATION_SERVER.get())
667                    .buildArgument();
668    disableAllArg =
669            BooleanArgument.builder("disableAll")
670                    .description(INFO_DESCRIPTION_DISABLE_ALL.get())
671                    .buildArgument();
672
673    Argument[] argsToAdd = { secureArgsList.getHostNameArg(),
674            secureArgsList.getPortArg(), secureArgsList.getBindDnArg(),
675        disableReplicationServerArg, disableAllArg};
676    for (Argument arg : argsToAdd)
677    {
678      disableReplicationSubCmd.addArgument(arg);
679    }
680  }
681
682  /**
683   * Creates the initialize replication subcommand and all the specific options
684   * for the subcommand.
685   */
686  private void createInitializeReplicationSubCommand() throws ArgumentException
687  {
688    initializeReplicationSubCmd = new SubCommand(this, INITIALIZE_REPLICATION_SUBCMD_NAME,
689        INFO_DESCRIPTION_SUBCMD_INITIALIZE_REPLICATION.get(INITIALIZE_ALL_REPLICATION_SUBCMD_NAME));
690    addArgumentsToSubCommand(initializeReplicationSubCmd,
691        hostNameSourceArg, portSourceArg, hostNameDestinationArg, portDestinationArg);
692  }
693
694  private void createRelatedServersOptions() throws ArgumentException
695  {
696    final String defaultHostName = secureArgsList.getDefaultHostName();
697    hostNameSourceArg =
698            StringArgument.builder("hostSource")
699                    .shortIdentifier(OPTION_SHORT_HOST)
700                    .description(INFO_DESCRIPTION_INITIALIZE_REPLICATION_HOST_SOURCE.get())
701                    .defaultValue(defaultHostName)
702                    .valuePlaceholder(INFO_HOST_PLACEHOLDER.get())
703                    .buildArgument();
704    portSourceArg =
705            IntegerArgument.builder("portSource")
706                    .shortIdentifier(OPTION_SHORT_PORT)
707                    .description(INFO_DESCRIPTION_INITIALIZE_REPLICATION_SERVER_PORT_SOURCE.get())
708                    .range(1, 65336)
709                    .defaultValue(defaultAdminPort)
710                    .valuePlaceholder(INFO_PORT_PLACEHOLDER.get())
711                    .buildArgument();
712    hostNameDestinationArg =
713            StringArgument.builder("hostDestination")
714                    .shortIdentifier('O')
715                    .description(INFO_DESCRIPTION_INITIALIZE_REPLICATION_HOST_DESTINATION.get())
716                    .defaultValue(defaultHostName)
717                    .valuePlaceholder(INFO_HOST_PLACEHOLDER.get())
718                    .buildArgument();
719    portDestinationArg =
720            IntegerArgument.builder("portDestination")
721                    .description(INFO_DESCRIPTION_INITIALIZE_REPLICATION_SERVER_PORT_DESTINATION.get())
722                    .range(1, 65336)
723                    .defaultValue(defaultAdminPort)
724                    .valuePlaceholder(INFO_PORT_PLACEHOLDER.get())
725                    .buildArgument();
726  }
727
728  /**
729   * Creates the initialize all replication subcommand and all the specific
730   * options for the subcommand.  Note: this method assumes that
731   * initializeGlobalArguments has already been called and that hostNameArg and
732   * portArg have been created.
733   */
734  private void createInitializeAllReplicationSubCommand()
735  throws ArgumentException
736  {
737    initializeAllReplicationSubCmd = new SubCommand(this,
738        INITIALIZE_ALL_REPLICATION_SUBCMD_NAME,
739        INFO_DESCRIPTION_SUBCMD_INITIALIZE_ALL_REPLICATION.get(
740            INITIALIZE_REPLICATION_SUBCMD_NAME));
741    Argument[] argsToAdd = { secureArgsList.getHostNameArg(),
742            secureArgsList.getPortArg() };
743    for (Argument arg : argsToAdd)
744    {
745      initializeAllReplicationSubCmd.addArgument(arg);
746    }
747  }
748
749  /**
750   * Creates the subcommand that the user must launch before doing an external
751   * initialization of the topology ( and all the specific
752   * options for the subcommand.  Note: this method assumes that
753   * initializeGlobalArguments has already been called and that hostNameArg and
754   * portArg have been created.
755   */
756  private void createPreExternalInitializationSubCommand()
757  throws ArgumentException
758  {
759    preExternalInitializationSubCmd = new SubCommand(this,
760        PRE_EXTERNAL_INITIALIZATION_SUBCMD_NAME,
761        INFO_DESCRIPTION_SUBCMD_PRE_EXTERNAL_INITIALIZATION.get(
762            POST_EXTERNAL_INITIALIZATION_SUBCMD_NAME));
763    BooleanArgument externalInitializationLocalOnlyArg =
764            BooleanArgument.builder("local-only")
765                    .shortIdentifier('l')
766                    .description(LocalizableMessage.EMPTY)
767                    .hidden()
768                    .buildArgument();
769
770    Argument[] argsToAdd = { secureArgsList.getHostNameArg(),
771            secureArgsList.getPortArg(),
772        externalInitializationLocalOnlyArg};
773
774    for (Argument arg : argsToAdd)
775    {
776      preExternalInitializationSubCmd.addArgument(arg);
777    }
778  }
779
780  /**
781   * Creates the subcommand that the user must launch after doing an external
782   * initialization of the topology ( and all the specific
783   * options for the subcommand.  Note: this method assumes that
784   * initializeGlobalArguments has already been called and that hostNameArg and
785   * portArg have been created.
786   */
787  private void createPostExternalInitializationSubCommand()
788  throws ArgumentException
789  {
790    postExternalInitializationSubCmd = new SubCommand(this,
791        POST_EXTERNAL_INITIALIZATION_SUBCMD_NAME,
792        INFO_DESCRIPTION_SUBCMD_POST_EXTERNAL_INITIALIZATION.get(
793            PRE_EXTERNAL_INITIALIZATION_SUBCMD_NAME));
794    Argument[] argsToAdd = { secureArgsList.getHostNameArg(),
795            secureArgsList.getPortArg() };
796    for (Argument arg : argsToAdd)
797    {
798      postExternalInitializationSubCmd.addArgument(arg);
799    }
800  }
801
802  private void createResetChangeNumberSubCommand() throws ArgumentException
803  {
804    resetChangelogNumber = new SubCommand(this, RESET_CHANGE_NUMBER_SUBCMD_NAME,
805        INFO_DESCRIPTION_RESET_CHANGE_NUMBER.get());
806    resetChangeNumber = newChangeNumberArgument();
807    addArgumentsToSubCommand(resetChangelogNumber,
808            hostNameSourceArg, portSourceArg, hostNameDestinationArg, portDestinationArg, resetChangeNumber);
809  }
810
811  IntegerArgument newChangeNumberArgument() throws ArgumentException
812  {
813    return IntegerArgument.builder("change-number")
814            .description(INFO_DESCRIPTION_START_CHANGE_NUMBER.get())
815            .valuePlaceholder(INFO_CHANGE_NUMBER_PLACEHOLDER.get())
816            .buildArgument();
817  }
818
819  /**
820   * Creates the status replication subcommand and all the specific options
821   * for the subcommand.  Note: this method assumes that
822   * initializeGlobalArguments has already been called and that hostNameArg and
823   * portArg have been created.
824   */
825  private void createStatusReplicationSubCommand() throws ArgumentException
826  {
827    statusReplicationSubCmd = new SubCommand(this,
828        STATUS_REPLICATION_SUBCMD_NAME,
829        INFO_DESCRIPTION_SUBCMD_STATUS_REPLICATION.get());
830    scriptFriendlyArg =
831            BooleanArgument.builder("script-friendly")
832                    .shortIdentifier('s')
833                    .description(INFO_DESCRIPTION_SCRIPT_FRIENDLY.get())
834                    .buildArgument();
835    addArgumentsToSubCommand(
836            statusReplicationSubCmd, secureArgsList.getHostNameArg(), secureArgsList.getPortArg(), scriptFriendlyArg);
837  }
838
839  /**
840   * Creates the purge historical subcommand and all the specific options
841   * for the subcommand.  Note: this method assumes that
842   * initializeGlobalArguments has already been called and that hostNameArg and
843   * portArg have been created.
844   */
845  private void createPurgeHistoricalSubCommand() throws ArgumentException
846  {
847    maximumDurationArg =
848            IntegerArgument.builder("maximumDuration")
849                    .description(INFO_DESCRIPTION_PURGE_HISTORICAL_MAXIMUM_DURATION.get())
850                    .required()
851                    .lowerBound(0)
852                    .defaultValue(PurgeConflictsHistoricalTask.DEFAULT_MAX_DURATION)
853                    .valuePlaceholder(INFO_MAXIMUM_DURATION_PLACEHOLDER.get())
854                    .buildArgument();
855
856    purgeHistoricalSubCmd = new SubCommand(
857        this,
858        PURGE_HISTORICAL_SUBCMD_NAME,
859        INFO_DESCRIPTION_SUBCMD_PURGE_HISTORICAL.get());
860
861    addArgumentsToSubCommand(purgeHistoricalSubCmd,
862            secureArgsList.getHostNameArg(), secureArgsList.getPortArg(), maximumDurationArg);
863    addArgumentsToSubCommand(purgeHistoricalSubCmd, taskArgs.getArguments());
864  }
865
866  private void addArgumentsToSubCommand(final SubCommand subCommand, final Argument... args) throws ArgumentException
867  {
868    for (final Argument arg : args)
869    {
870      subCommand.addArgument(arg);
871    }
872  }
873
874  /**
875   * Tells whether the user specified to have an interactive operation or not.
876   * This method must be called after calling parseArguments.
877   * @return {@code true} if the user specified to have an interactive
878   * operation and {@code false} otherwise.
879   */
880  public boolean isInteractive()
881  {
882    return !noPromptArg.isPresent();
883  }
884
885  /**
886   * Tells whether the user specified to have a quite operation or not.
887   * This method must be called after calling parseArguments.
888   * @return {@code true} if the user specified to have a quite operation
889   * and {@code false} otherwise.
890   */
891  public boolean isQuiet()
892  {
893    return quietArg.isPresent();
894  }
895
896  /**
897   * Tells whether the user specified to have a script-friendly output or not.
898   * This method must be called after calling parseArguments.
899   * @return {@code true} if the user specified to have a script-friendly
900   * output and {@code false} otherwise.
901   */
902  public boolean isScriptFriendly()
903  {
904    return scriptFriendlyArg.isPresent();
905  }
906
907  /**
908   * Get the global administrator password which has to be used for the command
909   * to connect to the server(s) without prompting the user.  If no password was
910   * specified, return null.
911   *
912   * @return the global administrator password which has to be used for the
913   * command to connect to the server(s) without prompting the user.  If no
914   * password was specified, return null.
915   */
916  public String getBindPasswordAdmin()
917  {
918    return getBindPassword(secureArgsList.getBindPasswordArg(), secureArgsList.getBindPasswordFileArg());
919  }
920
921  /**
922   * Returns the Administrator UID explicitly provided in the command-line.
923   * @return the Administrator UID explicitly provided in the command-line.
924   */
925  @Override
926  public String getAdministratorUID()
927  {
928    return getValue(getAdminUidArg());
929  }
930
931  /**
932   * Returns the default Administrator UID value.
933   * @return the default Administrator UID value.
934   */
935  public String getAdministratorUIDOrDefault()
936  {
937    return getValueOrDefault(getAdminUidArg());
938  }
939
940  /**
941   * Returns the Administrator UID argument.
942   * @return the Administrator UID argument.
943   */
944  StringArgument getAdminUidArg()
945  {
946    return secureArgsList.getAdminUidArg();
947  }
948
949  /**
950   * Returns the first server replication port explicitly provided in the enable
951   * replication subcommand.
952   * @return the first server replication port explicitly provided in the enable
953   * replication subcommand.  Returns -1 if no port was explicitly provided.
954   */
955  public int getReplicationPort1()
956  {
957    return getValue(server1.replicationPortArg);
958  }
959
960  /**
961   * Returns the second server replication port explicitly provided in the
962   * enable replication subcommand.
963   * @return the second server replication port explicitly provided in the
964   * enable replication subcommand.  Returns -1 if no port was explicitly
965   * provided.
966   */
967  public int getReplicationPort2()
968  {
969    return getValue(server2.replicationPortArg);
970  }
971
972  /**
973   * Returns whether the user asked to skip the replication port checks (if the
974   * ports are free) or not.
975   * @return {@code true} the user asked to skip the replication port
976   * checks (if the ports are free) and {@code false} otherwise.
977   */
978  boolean skipReplicationPortCheck()
979  {
980    return skipPortCheckArg.isPresent();
981  }
982
983  /**
984   * Returns whether the user asked to not replicate the schema between servers.
985   * @return {@code true} if the user asked to not replicate schema and
986   * {@code false} otherwise.
987   */
988  boolean noSchemaReplication()
989  {
990    return noSchemaReplicationArg.isPresent();
991  }
992
993  /**
994   * Returns whether the user asked to use the second server to initialize the
995   * schema of the first server.
996   * @return {@code true} if the user asked to use the second server to
997   * initialize the schema of the first server and {@code false} otherwise.
998   */
999  boolean useSecondServerAsSchemaSource()
1000  {
1001    return useSecondServerAsSchemaSourceArg.isPresent();
1002  }
1003
1004  /**
1005   * Returns the host name explicitly provided in the disable replication
1006   * subcommand.
1007   * @return the host name explicitly provided in the disable replication
1008   * subcommand.
1009   */
1010  public String getHostNameToDisable()
1011  {
1012    return getValue(secureArgsList.getHostNameArg());
1013  }
1014
1015  /**
1016   * Returns the host name default value in the disable replication
1017   * subcommand.
1018   * @return the host name default value in the disable replication
1019   * subcommand.
1020   */
1021  public String getHostNameToDisableOrDefault()
1022  {
1023    return getValueOrDefault(secureArgsList.getHostNameArg());
1024  }
1025
1026  /**
1027   * Returns the server bind dn explicitly provided in the disable replication
1028   * subcommand.
1029   * @return the server bind dn explicitly provided in the disable replication
1030   * subcommand.
1031   */
1032  public String getBindDNToDisable()
1033  {
1034    return getValue(secureArgsList.getBindDnArg());
1035  }
1036
1037  /**
1038   * Returns the host name default value in the status replication subcommand.
1039   * @return the host name default value in the status replication subcommand.
1040   */
1041  public String getHostNameToStatusOrDefault()
1042  {
1043    return getValueOrDefault(secureArgsList.getHostNameArg());
1044  }
1045
1046  /**
1047   * Returns the host name default value in the initialize all replication
1048   * subcommand.
1049   * @return the host name default value in the initialize all replication
1050   * subcommand.
1051   */
1052  public String getHostNameToInitializeAllOrDefault()
1053  {
1054    return getValueOrDefault(secureArgsList.getHostNameArg());
1055  }
1056
1057  /**
1058   * Returns the source host name explicitly provided in the initialize
1059   * replication subcommand.
1060   * @return the source host name explicitly provided in the initialize
1061   * replication subcommand.
1062   */
1063  public String getHostNameSource()
1064  {
1065    return getValue(hostNameSourceArg);
1066  }
1067
1068  /**
1069   * Returns the first host name default value in the initialize replication
1070   * subcommand.
1071   * @return the first host name default value in the initialize replication
1072   * subcommand.
1073   */
1074  public String getHostNameSourceOrDefault()
1075  {
1076    return getValueOrDefault(hostNameSourceArg);
1077  }
1078
1079  /**
1080   * Returns the destination host name explicitly provided in the initialize
1081   * replication subcommand.
1082   * @return the destination host name explicitly provided in the initialize
1083   * replication subcommand.
1084   */
1085  public String getHostNameDestination()
1086  {
1087    return getValue(hostNameDestinationArg);
1088  }
1089
1090  /**
1091   * Returns the destination host name default value in the initialize
1092   * replication subcommand.
1093   * @return the destination host name default value in the initialize
1094   * replication subcommand.
1095   */
1096  public String getHostNameDestinationOrDefault()
1097  {
1098    return getValueOrDefault(hostNameDestinationArg);
1099  }
1100
1101  /**
1102   * Returns the source server port explicitly provided in the initialize
1103   * replication subcommand.
1104   * @return the source server port explicitly provided in the initialize
1105   * replication subcommand.  Returns -1 if no port was explicitly provided.
1106   */
1107  public int getPortSource()
1108  {
1109    return getValue(portSourceArg);
1110  }
1111
1112  /**
1113   * Returns the source server port default value in the initialize replication
1114   * subcommand.
1115   * @return the source server port default value in the initialize replication
1116   * subcommand.
1117   */
1118  public int getPortSourceOrDefault()
1119  {
1120    return getValueOrDefault(portSourceArg);
1121  }
1122
1123  /**
1124   * Returns the destination server port explicitly provided in the initialize
1125   * replication subcommand.
1126   * @return the destination server port explicitly provided in the initialize
1127   * replication subcommand.  Returns -1 if no port was explicitly provided.
1128   */
1129  public int getPortDestination()
1130  {
1131    return getValue(portDestinationArg);
1132  }
1133
1134  /**
1135   * Returns the destination server port default value in the initialize
1136   * replication subcommand.
1137   * @return the destination server port default value in the initialize
1138   * replication subcommand.
1139   */
1140  public int getPortDestinationOrDefault()
1141  {
1142    return getValueOrDefault(portDestinationArg);
1143  }
1144
1145  /**
1146   * Returns the server port explicitly provided in the disable replication
1147   * subcommand.
1148   * @return the server port explicitly provided in the disable replication
1149   * subcommand.  Returns -1 if no port was explicitly provided.
1150   */
1151  public int getPortToDisable()
1152  {
1153    return getValue(secureArgsList.getPortArg());
1154  }
1155
1156  /**
1157   * Returns the server port default value in the disable replication
1158   * subcommand.
1159   * @return the server port default value in the disable replication
1160   * subcommand.
1161   */
1162  public int getPortToDisableOrDefault()
1163  {
1164    return getValueOrDefault(secureArgsList.getPortArg());
1165  }
1166
1167  /**
1168   * Returns the server port default value in the initialize all replication
1169   * subcommand.
1170   * @return the server port default value in the initialize all replication
1171   * subcommand.
1172   */
1173  public int getPortToInitializeAllOrDefault()
1174  {
1175    return getValueOrDefault(secureArgsList.getPortArg());
1176  }
1177
1178  /**
1179   * Returns the server port default value in the status replication subcommand.
1180   * @return the server port default value in the status replication subcommand.
1181   */
1182  public int getPortToStatusOrDefault()
1183  {
1184    return getValueOrDefault(secureArgsList.getPortArg());
1185  }
1186
1187  /**
1188   * Returns the list of base DNs provided by the user.
1189   * @return the list of base DNs provided by the user.
1190   */
1191  public List<String> getBaseDNs()
1192  {
1193    return baseDNsArg.getValues();
1194  }
1195
1196  /**
1197   * Returns the config class value provided in the hidden argument of the
1198   * command-line.
1199   * @return the config class value provided in the hidden argument of the
1200   * command-line.
1201   */
1202  public String getConfigClass()
1203  {
1204    return getValue(configClassArg);
1205  }
1206
1207  /**
1208   * Returns the config file value provided in the hidden argument of the
1209   * command-line.
1210   * @return the config file value provided in the hidden argument of the
1211   * command-line.
1212   */
1213  public String getConfigFile()
1214  {
1215    return getValue(configFileArg);
1216  }
1217
1218  /**
1219   * Returns the argument's value if present or else return the argument's default value.
1220   *
1221   * @param arg the argument
1222   * @return the argument's value if present, the argument's default value if not present
1223   */
1224  static String getValueOrDefault(StringArgument arg)
1225  {
1226    String v = getValue(arg);
1227    String defaultValue = getDefaultValue(arg);
1228    return v != null ? v : defaultValue;
1229  }
1230
1231  /**
1232   * Returns the argument's value if present or else return the argument's default value.
1233   *
1234   * @param arg the argument
1235   * @return the argument's value if present, the argument's default value if not present
1236   */
1237  static int getValueOrDefault(IntegerArgument arg)
1238  {
1239    int v = getValue(arg);
1240    int defaultValue = getDefaultValue(arg);
1241    return v != -1 ? v : defaultValue;
1242  }
1243
1244  /**
1245   * Returns the value of the provided argument only if the user provided it
1246   * explicitly.
1247   * @param arg the StringArgument to be handled.
1248   * @return the value of the provided argument only if the user provided it
1249   * explicitly.
1250   */
1251  static String getValue(StringArgument arg)
1252  {
1253    return arg.isPresent() ? arg.getValue() : null;
1254  }
1255
1256  /**
1257   * Returns the default value of the provided argument.
1258   * @param arg the StringArgument to be handled.
1259   * @return the default value of the provided argument.
1260   */
1261  static String getDefaultValue(StringArgument arg)
1262  {
1263    return arg.getDefaultValue();
1264  }
1265
1266  /**
1267   * Returns the value of the provided argument only if the user provided it
1268   * explicitly.
1269   * @param arg the StringArgument to be handled.
1270   * @return the value of the provided argument only if the user provided it
1271   * explicitly.
1272   */
1273  static int getValue(IntegerArgument arg)
1274  {
1275    if (arg.isPresent())
1276    {
1277      try
1278      {
1279        return arg.getIntValue();
1280      }
1281      catch (ArgumentException ae)
1282      {
1283        // This is a bug
1284        throw new IllegalStateException(
1285            "There was an argument exception calling "+
1286            "ReplicationCliParser.getValue().  This appears to be a bug "+
1287            "because this method should be called after calling "+
1288            "parseArguments which should result in an error.", ae);
1289      }
1290    }
1291    return -1;
1292  }
1293
1294  /**
1295   * Returns the default value of the provided argument.
1296   * @param arg the StringArgument to be handled.
1297   * @return the default value of the provided argument.
1298   */
1299  static int getDefaultValue(IntegerArgument arg)
1300  {
1301    String v = arg.getDefaultValue();
1302    return v != null ? Integer.parseInt(v) : -1;
1303  }
1304
1305  /**
1306   * Checks the subcommand options and updates the provided LocalizableMessageBuilder
1307   * with the errors that were encountered with the subcommand options.
1308   *
1309   * This method assumes that the method parseArguments for the parser has
1310   * already been called.
1311   * @param buf the LocalizableMessageBuilder object where we add the error messages
1312   * describing the errors encountered.
1313   */
1314  private void validateSubcommandOptions(LocalizableMessageBuilder buf)
1315  {
1316    if (isEnableReplicationSubcommand())
1317    {
1318      validateEnableReplicationOptions(buf);
1319    }
1320    else if (isDisableReplicationSubcommand())
1321    {
1322      validateDisableReplicationOptions(buf);
1323    }
1324    else if (isStatusReplicationSubcommand())
1325    {
1326      validateStatusReplicationOptions(buf);
1327    }
1328    else  if (isInitializeReplicationSubcommand())
1329    {
1330      validateSourceAndDestinationServersOptions(buf);
1331    }
1332    else if (isPurgeHistoricalSubcommand())
1333    {
1334      validatePurgeHistoricalOptions(buf);
1335    }
1336    else if (isResetChangeNumber())
1337    {
1338      validateSourceAndDestinationServersOptions(buf);
1339    }
1340  }
1341
1342  /**
1343   * Checks the purge historical subcommand options and updates the
1344   * provided LocalizableMessageBuilder with the errors that were encountered with the
1345   * subcommand options.
1346   *
1347   * This method assumes that the method parseArguments for the parser has
1348   * already been called.
1349   * @param buf the LocalizableMessageBuilder object where we add the error messages
1350   * describing the errors encountered.
1351   */
1352  private void validatePurgeHistoricalOptions(LocalizableMessageBuilder buf)
1353  {
1354    try
1355    {
1356      if (!isInteractive() && !connectionArgumentsPresent())
1357      {
1358        taskArgs.validateArgsIfOffline();
1359      }
1360      else
1361      {
1362        taskArgs.validateArgs();
1363      }
1364    }
1365    catch (ClientException | ArgumentException e)
1366    {
1367      addMessage(buf, e.getMessageObject());
1368    }
1369  }
1370
1371  /**
1372   * Returns whether the user provided subcommand is the enable replication
1373   * or not.
1374   * @return {@code true} if the user provided subcommand is the
1375   * enable replication and {@code false} otherwise.
1376   */
1377  public boolean isEnableReplicationSubcommand()
1378  {
1379    return isSubcommand(ENABLE_REPLICATION_SUBCMD_NAME);
1380  }
1381
1382  /**
1383   * Returns whether the user provided subcommand is the disable replication
1384   * or not.
1385   * @return {@code true} if the user provided subcommand is the
1386   * disable replication and {@code false} otherwise.
1387   */
1388  public boolean isDisableReplicationSubcommand()
1389  {
1390    return isSubcommand(DISABLE_REPLICATION_SUBCMD_NAME);
1391  }
1392
1393  /**
1394   * Returns whether the user specified the reset changelog numbering subcommand.
1395   * @return {@code true} if the user wanted to reset change number
1396   */
1397  public boolean isResetChangeNumber()
1398  {
1399    return isSubcommand(RESET_CHANGE_NUMBER_SUBCMD_NAME);
1400  }
1401
1402  /**
1403   * Returns whether the user provided subcommand is the status replication
1404   * or not.
1405   * @return {@code true} if the user provided subcommand is the
1406   * status replication and {@code false} otherwise.
1407   */
1408  public boolean isStatusReplicationSubcommand()
1409  {
1410    return isSubcommand(STATUS_REPLICATION_SUBCMD_NAME);
1411  }
1412
1413  /**
1414   * Returns whether the user provided subcommand is the purge historical
1415   * or not.
1416   * @return {@code true} if the user provided subcommand is the
1417   * purge historical and {@code false} otherwise.
1418   */
1419  public boolean isPurgeHistoricalSubcommand()
1420  {
1421    return isSubcommand(PURGE_HISTORICAL_SUBCMD_NAME);
1422  }
1423
1424  /**
1425   * Returns whether the user provided subcommand is the initialize all
1426   * replication or not.
1427   * @return {@code true} if the user provided subcommand is the
1428   * initialize all replication and {@code false} otherwise.
1429   */
1430  public boolean isInitializeAllReplicationSubcommand()
1431  {
1432    return isSubcommand(INITIALIZE_ALL_REPLICATION_SUBCMD_NAME);
1433  }
1434
1435  /**
1436   * Returns whether the user provided subcommand is the pre external
1437   * initialization or not.
1438   * @return {@code true} if the user provided subcommand is the
1439   * pre external initialization and {@code false} otherwise.
1440   */
1441  public boolean isPreExternalInitializationSubcommand()
1442  {
1443    return isSubcommand(PRE_EXTERNAL_INITIALIZATION_SUBCMD_NAME);
1444  }
1445
1446  /**
1447   * Returns whether the user provided subcommand is the post external
1448   * initialization or not.
1449   * @return {@code true} if the user provided subcommand is the
1450   * post external initialization and {@code false} otherwise.
1451   */
1452  public boolean isPostExternalInitializationSubcommand()
1453  {
1454    return isSubcommand(POST_EXTERNAL_INITIALIZATION_SUBCMD_NAME);
1455  }
1456
1457  /**
1458   * Returns whether the user provided subcommand is the initialize replication
1459   * or not.
1460   * @return {@code true} if the user provided subcommand is the
1461   * initialize replication and {@code false} otherwise.
1462   */
1463  public boolean isInitializeReplicationSubcommand()
1464  {
1465    return isSubcommand(INITIALIZE_REPLICATION_SUBCMD_NAME);
1466  }
1467
1468  /**
1469   * Returns whether the command-line subcommand has the name provided
1470   * or not.
1471   * @param name the name of the subcommand.
1472   * @return {@code true} if command-line subcommand has the name provided
1473   * and {@code false} otherwise.
1474   */
1475  private boolean isSubcommand(String name)
1476  {
1477    SubCommand subCommand = getSubCommand();
1478    return subCommand != null && subCommand.getName().equalsIgnoreCase(name);
1479  }
1480
1481  /**
1482   * Checks the enable replication subcommand options and updates the provided
1483   * LocalizableMessageBuilder with the errors that were encountered with the subcommand
1484   * options.
1485   *
1486   * This method assumes that the method parseArguments for the parser has
1487   * already been called.
1488   * @param buf the LocalizableMessageBuilder object where we add the error messages
1489   * describing the errors encountered.
1490   */
1491  private void validateEnableReplicationOptions(LocalizableMessageBuilder buf)
1492  {
1493    appendErrorMessageIfArgumentsConflict(buf, server1.bindPasswordArg, server1.bindPasswordFileArg );
1494    appendErrorMessageIfArgumentsConflict(buf, server2.bindPasswordArg, server2.bindPasswordFileArg );
1495    appendErrorMessageIfArgumentsConflict(buf, server1.replicationPortArg, server1.noReplicationServerArg );
1496    appendErrorMessageIfArgumentsConflict(buf, server1.noReplicationServerArg, server1.onlyReplicationServerArg );
1497    appendErrorMessageIfArgumentsConflict(buf, server2.replicationPortArg, server2.noReplicationServerArg );
1498    appendErrorMessageIfArgumentsConflict(buf, server2.noReplicationServerArg, server2.onlyReplicationServerArg );
1499    appendErrorMessageIfArgumentsConflict(buf,noSchemaReplicationArg, useSecondServerAsSchemaSourceArg);
1500
1501    if (server1.hostNameArg.getValue().equalsIgnoreCase(server2.hostNameArg.getValue())
1502        && !isInteractive()
1503        && server1.portArg.getValue().equals(server2.portArg.getValue()))
1504    {
1505      LocalizableMessage message = ERR_REPLICATION_ENABLE_SAME_SERVER_PORT.get(
1506          server1.hostNameArg.getValue(), server1.portArg.getValue());
1507      addMessage(buf, message);
1508    }
1509  }
1510
1511  /**
1512   * Checks the disable replication subcommand options and updates the provided
1513   * LocalizableMessageBuilder with the errors that were encountered with the subcommand
1514   * options.
1515   *
1516   * This method assumes that the method parseArguments for the parser has
1517   * already been called.
1518   * @param buf the LocalizableMessageBuilder object where we add the error messages
1519   * describing the errors encountered.
1520   */
1521  private void validateDisableReplicationOptions(LocalizableMessageBuilder buf)
1522  {
1523    appendErrorMessageIfArgumentsConflict(buf, getAdminUidArg(), secureArgsList.getBindDnArg());
1524    appendErrorMessageIfArgumentsConflict(buf, disableAllArg, disableReplicationServerArg);
1525    appendErrorMessageIfArgumentsConflict(buf, disableAllArg, baseDNsArg);
1526  }
1527
1528  /**
1529   * Checks the status replication subcommand options and updates the provided
1530   * LocalizableMessageBuilder with the errors that were encountered with the subcommand
1531   * options.
1532   *
1533   * This method assumes that the method parseArguments for the parser has
1534   * already been called.
1535   * @param buf the LocalizableMessageBuilder object where we add the error messages
1536   * describing the errors encountered.
1537   */
1538  private void validateStatusReplicationOptions(LocalizableMessageBuilder buf)
1539  {
1540    if (quietArg.isPresent())
1541    {
1542      LocalizableMessage message = ERR_REPLICATION_STATUS_QUIET.get(
1543          STATUS_REPLICATION_SUBCMD_NAME, "--"+quietArg.getLongIdentifier());
1544      addMessage(buf, message);
1545    }
1546  }
1547
1548  /**
1549   * Checks the initialize replication subcommand options and updates the
1550   * provided LocalizableMessageBuilder with the errors that were encountered with the
1551   * subcommand options.
1552   *
1553   * This method assumes that the method parseArguments for the parser has
1554   * already been called.
1555   * @param buf the LocalizableMessageBuilder object where we add the error messages
1556   * describing the errors encountered.
1557   */
1558  private void validateSourceAndDestinationServersOptions(LocalizableMessageBuilder buf)
1559  {
1560    if (hostNameSourceArg.getValue().equalsIgnoreCase(hostNameDestinationArg.getValue())
1561        && !isInteractive()
1562        && portSourceArg.getValue().equals(portDestinationArg.getValue()))
1563    {
1564      LocalizableMessage message = ERR_SOURCE_DESTINATION_INITIALIZE_SAME_SERVER_PORT.get(
1565          hostNameSourceArg.getValue(), portSourceArg.getValue());
1566      addMessage(buf, message);
1567    }
1568  }
1569
1570  /**
1571   * Adds a message to the provided LocalizableMessageBuilder.
1572   * @param buf the LocalizableMessageBuilder.
1573   * @param message the message to be added.
1574   */
1575  private void addMessage(LocalizableMessageBuilder buf, LocalizableMessage message)
1576  {
1577    if (buf.length() > 0)
1578    {
1579      buf.append(LINE_SEPARATOR);
1580    }
1581    buf.append(message);
1582  }
1583
1584  /**
1585   * Returns the SecureConnectionCliArgs object containing the arguments
1586   * of this parser.
1587   * @return the SecureConnectionCliArgs object containing the arguments
1588   * of this parser.
1589   */
1590  public SecureConnectionCliArgs getSecureArgsList()
1591  {
1592    return secureArgsList;
1593  }
1594
1595  /**
1596   * Returns the TaskScheduleArgs object containing the arguments
1597   * of this parser.
1598   * @return the TaskScheduleArgs object containing the arguments
1599   * of this parser.
1600   */
1601  public TaskScheduleArgs getTaskArgsList()
1602  {
1603    return taskArgs;
1604  }
1605
1606  /**
1607   * Returns whether the user specified connection arguments or not.
1608   * @return {@code true} if the user specified connection arguments and
1609   * {@code false} otherwise.
1610   */
1611  boolean connectionArgumentsPresent()
1612  {
1613    if (isPurgeHistoricalSubcommand()) {
1614      boolean secureArgsPresent = getSecureArgsList() != null &&
1615      getSecureArgsList().argumentsPresent();
1616      // This have to be explicitly specified because their original definition
1617      // has been replaced.
1618      boolean adminArgsPresent = getAdminUidArg().isPresent() ||
1619      secureArgsList.getBindPasswordArg().isPresent() ||
1620      secureArgsList.getBindPasswordFileArg().isPresent();
1621      return secureArgsPresent || adminArgsPresent;
1622    }
1623    return true;
1624  }
1625
1626  /**
1627    * Returns the maximum duration explicitly provided in the purge historical
1628    * replication subcommand.
1629    * @return the maximum duration explicitly provided in the purge historical
1630    * replication subcommand.  Returns -1 if no port was explicitly provided.
1631    */
1632  public int getMaximumDuration()
1633  {
1634     return getValue(maximumDurationArg);
1635  }
1636
1637  /**
1638   * Returns the maximum duration default value in the purge historical
1639   * replication subcommand.
1640   * @return the maximum duration default value in the purge historical
1641   * replication subcommand.
1642   */
1643  public int getMaximumDurationOrDefault()
1644  {
1645    return getValueOrDefault(maximumDurationArg);
1646  }
1647
1648  /**
1649   * Returns the changenumber specified as argument.
1650   * @return the changenumber specified as argument
1651   */
1652  public int getResetChangeNumber()
1653  {
1654    return getValue(resetChangeNumber);
1655  }
1656
1657  /**
1658   * Sets the start change number value.
1659   * @param changeNumber the new value of the option
1660   */
1661  public void setResetChangeNumber(String changeNumber)
1662  {
1663    resetChangeNumber.setPresent(true);
1664    resetChangeNumber.addValue(changeNumber);
1665  }
1666}