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