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