001/*
002 * Copyright 2016-2019 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2016-2019 Ping Identity Corporation
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021package com.unboundid.ldap.sdk.unboundidds.tools;
022
023
024
025import java.io.BufferedReader;
026import java.io.File;
027import java.io.FileOutputStream;
028import java.io.FileReader;
029import java.io.OutputStream;
030import java.util.ArrayList;
031import java.util.Arrays;
032import java.util.LinkedHashMap;
033import java.util.concurrent.atomic.AtomicBoolean;
034
035import com.unboundid.ldap.sdk.DN;
036import com.unboundid.ldap.sdk.ExtendedResult;
037import com.unboundid.ldap.sdk.Filter;
038import com.unboundid.ldap.sdk.LDAPConnection;
039import com.unboundid.ldap.sdk.LDAPConnectionOptions;
040import com.unboundid.ldap.sdk.LDAPConnectionPool;
041import com.unboundid.ldap.sdk.LDAPException;
042import com.unboundid.ldap.sdk.ResultCode;
043import com.unboundid.ldap.sdk.UnsolicitedNotificationHandler;
044import com.unboundid.ldap.sdk.Version;
045import com.unboundid.ldif.LDIFWriter;
046import com.unboundid.util.Debug;
047import com.unboundid.util.DNFileReader;
048import com.unboundid.util.LDAPCommandLineTool;
049import com.unboundid.util.FilterFileReader;
050import com.unboundid.util.FixedRateBarrier;
051import com.unboundid.util.RateAdjustor;
052import com.unboundid.util.StaticUtils;
053import com.unboundid.util.ThreadSafety;
054import com.unboundid.util.ThreadSafetyLevel;
055import com.unboundid.util.args.ArgumentException;
056import com.unboundid.util.args.ArgumentParser;
057import com.unboundid.util.args.BooleanArgument;
058import com.unboundid.util.args.BooleanValueArgument;
059import com.unboundid.util.args.DNArgument;
060import com.unboundid.util.args.FileArgument;
061import com.unboundid.util.args.FilterArgument;
062import com.unboundid.util.args.IPAddressArgumentValueValidator;
063import com.unboundid.util.args.IntegerArgument;
064import com.unboundid.util.args.StringArgument;
065import com.unboundid.util.args.TimestampArgument;
066import com.unboundid.util.args.SubCommand;
067
068import static com.unboundid.ldap.sdk.unboundidds.tools.ToolMessages.*;
069
070
071
072/**
073 * This class provides a tool that can be used to perform a variety of account
074 * management functions against user entries in the Ping Identity, UnboundID,
075 * or Nokia/Alcatel-Lucent 8661 Directory Server.  It primarily uses the
076 * password policy state extended operation for its processing.
077 * <BR>
078 * <BLOCKQUOTE>
079 *   <B>NOTE:</B>  This class, and other classes within the
080 *   {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only
081 *   supported for use against Ping Identity, UnboundID, and
082 *   Nokia/Alcatel-Lucent 8661 server products.  These classes provide support
083 *   for proprietary functionality or for external specifications that are not
084 *   considered stable or mature enough to be guaranteed to work in an
085 *   interoperable way with other types of LDAP servers.
086 * </BLOCKQUOTE>
087 */
088@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
089public final class ManageAccount
090       extends LDAPCommandLineTool
091       implements UnsolicitedNotificationHandler
092{
093  /**
094   * The column at which to wrap long lines.
095   */
096  private static final int WRAP_COLUMN = StaticUtils.TERMINAL_WIDTH_COLUMNS - 1;
097
098
099
100  /**
101   * The primary name of the argument used to indicate that the tool should
102   * append to the reject file rather than overwrite it.
103   */
104  private static final String ARG_APPEND_TO_REJECT_FILE = "appendToRejectFile";
105
106
107
108  /**
109   * The primary name of the argument used to specify a base DN to use for
110   * searches.
111   */
112  static final String ARG_BASE_DN = "baseDN";
113
114
115
116  /**
117   * The primary name of the argument used to specify the path to a file to a
118   * sample variable rate data file to create.
119   */
120  private static final String ARG_GENERATE_SAMPLE_RATE_FILE =
121       "generateSampleRateFile";
122
123
124
125  /**
126   * The primary name of the argument used to specify the path to a file
127   * containing the DNs of the users on which to operate.
128   */
129  private static final String ARG_DN_INPUT_FILE = "dnInputFile";
130
131
132
133  /**
134   * The primary name of the argument used to specify the path to a file
135   * containing search filters to use to identify users.
136   */
137  private static final String ARG_FILTER_INPUT_FILE = "filterInputFile";
138
139
140
141  /**
142   * The primary name of the argument used to specify the number of threads to
143   * use to process search operations to identify which users to target.
144   */
145  static final String ARG_NUM_SEARCH_THREADS = "numSearchThreads";
146
147
148
149  /**
150   * The primary name of the argument used to specify the number of threads to
151   * use to perform manage-account processing.
152   */
153  static final String ARG_NUM_THREADS = "numThreads";
154
155
156
157  /**
158   * The primary name of the argument used to specify the target rate of
159   * operations per second.
160   */
161  private static final String ARG_RATE_PER_SECOND = "ratePerSecond";
162
163
164
165  /**
166   * The primary name of the argument used to specify the path to a reject file
167   * to create.
168   */
169  private static final String ARG_REJECT_FILE = "rejectFile";
170
171
172
173  /**
174   * The primary name of the argument used to specify the simple page size to
175   * use when performing searches.
176   */
177  static final String ARG_SIMPLE_PAGE_SIZE = "simplePageSize";
178
179
180
181  /**
182   * The primary name of the argument used to suppress result operation types
183   * without values.
184   */
185  static final String ARG_SUPPRESS_EMPTY_RESULT_OPERATIONS =
186       "suppressEmptyResultOperations";
187
188
189
190  /**
191   * The primary name of the argument used to specify the DN of the user on
192   * which to operate.
193   */
194  private static final String ARG_TARGET_DN = "targetDN";
195
196
197
198  /**
199   * The primary name of the argument used to specify a search filter to use to
200   * identify users.
201   */
202  private static final String ARG_TARGET_FILTER = "targetFilter";
203
204
205
206  /**
207   * The primary name of the argument used to specify the user IDs of target
208   * users.
209   */
210  private static final String ARG_TARGET_USER_ID = "targetUserID";
211
212
213
214  /**
215   * The primary name of the argument used to specify the name of the attribute
216   * to identify which user has a given user ID.
217   */
218  static final String ARG_USER_ID_ATTRIBUTE = "userIDAttribute";
219
220
221
222  /**
223   * The primary name of the argument used to specify the path to a file
224   * containing the user IDs of the target users.
225   */
226  private static final String ARG_USER_ID_INPUT_FILE = "userIDInputFile";
227
228
229
230  /**
231   * The primary name of the argument used to specify the path to a variable
232   * rate data file.
233   */
234  private static final String ARG_VARIABLE_RATE_DATA = "variableRateData";
235
236
237
238  /**
239   * The default search base DN.
240   */
241  private static final DN DEFAULT_BASE_DN = DN.NULL_DN;
242
243
244
245  /**
246   * The default user ID attribute.
247   */
248  private static final String DEFAULT_USER_ID_ATTRIBUTE = "uid";
249
250
251
252  /**
253   * A target user DN to use in examples.
254   */
255  private static final String EXAMPLE_TARGET_USER_DN =
256       "uid=jdoe,ou=People,dc=example,dc=com";
257
258
259
260  // The argument parser for this tool.
261  private volatile ArgumentParser parser;
262
263  // Indicates whether all DNs have been provided to the manage-account
264  // processor.
265  private final AtomicBoolean allDNsProvided;
266
267  // Indicates whether all filters have been provided to the manage-account
268  // search processor.
269  private final AtomicBoolean allFiltersProvided;
270
271  // Indicates whether a request has been made to cancel processing.
272  private final AtomicBoolean cancelRequested;
273
274  // The rate limiter to use for this tool.
275  private volatile FixedRateBarrier rateLimiter;
276
277  // The LDAP connection options to use for connections created by this tool.
278  private final LDAPConnectionOptions connectionOptions;
279
280  // The LDIF writer to use to write information about successful and failed
281  // operations.
282  private volatile LDIFWriter outputWriter;
283
284  // The LDIF writer to use to write information about failed operations.
285  private volatile LDIFWriter rejectWriter;
286
287  // The search processor for this tool.
288  private volatile ManageAccountSearchProcessor searchProcessor;
289
290  // The rate adjustor to use to vary the load over time.
291  private volatile RateAdjustor rateAdjustor;
292
293
294
295  /**
296   * Invokes the tool with the provided set of arguments.
297   *
298   * @param  args  The command-line arguments provided to this tool.
299   */
300  public static void main(final String... args)
301  {
302    final ResultCode resultCode = main(System.out, System.err, args);
303    if (resultCode != ResultCode.SUCCESS)
304    {
305      System.exit(resultCode.intValue());
306    }
307  }
308
309
310
311  /**
312   * Invokes the tool with the provided set of arguments.
313   *
314   * @param  out   The output stream to use for standard out.  It may be
315   *               {@code null} if standard out should be suppressed.
316   * @param  err   The output stream to use for standard error.  It may be
317   *               {@code null} if standard error should be suppressed.
318   * @param  args  The command-line arguments provided to this tool.
319   *
320   * @return  A result code with the status of the tool processing.  Any result
321   *          code other than {@link ResultCode#SUCCESS} should be considered a
322   *          failure.
323   */
324  public static ResultCode main(final OutputStream out, final OutputStream err,
325                                final String... args)
326  {
327    final ManageAccount tool = new ManageAccount(out, err);
328
329    final boolean origCommentAboutBase64EncodedValues =
330         LDIFWriter.commentAboutBase64EncodedValues();
331    LDIFWriter.setCommentAboutBase64EncodedValues(true);
332    try
333    {
334      return tool.runTool(args);
335    }
336    finally
337    {
338      LDIFWriter.setCommentAboutBase64EncodedValues(
339           origCommentAboutBase64EncodedValues);
340    }
341  }
342
343
344
345  /**
346   * Creates a new instance of this tool with the provided arguments.
347   *
348   * @param  out  The output stream to use for standard out.  It may be
349   *              {@code null} if standard out should be suppressed.
350   * @param  err  The output stream to use for standard error.  It may be
351   *              {@code null} if standard error should be suppressed.
352   */
353  public ManageAccount(final OutputStream out, final OutputStream err)
354  {
355    super(out, err);
356
357    connectionOptions = new LDAPConnectionOptions();
358    connectionOptions.setUnsolicitedNotificationHandler(this);
359
360    allDNsProvided = new AtomicBoolean(false);
361    allFiltersProvided = new AtomicBoolean(false);
362    cancelRequested = new AtomicBoolean(false);
363
364    parser = null;
365    rateLimiter = null;
366    rateAdjustor = null;
367    outputWriter = null;
368    rejectWriter = null;
369    searchProcessor = null;
370  }
371
372
373
374  /**
375   * {@inheritDoc}
376   */
377  @Override()
378  public String getToolName()
379  {
380    return "manage-account";
381  }
382
383
384
385  /**
386   * {@inheritDoc}
387   */
388  @Override()
389  public String getToolDescription()
390  {
391    return INFO_MANAGE_ACCT_TOOL_DESC.get();
392  }
393
394
395
396  /**
397   * {@inheritDoc}
398   */
399  @Override()
400  public String getToolVersion()
401  {
402    return Version.NUMERIC_VERSION_STRING;
403  }
404
405
406
407  /**
408   * {@inheritDoc}
409   */
410  @Override()
411  public boolean supportsInteractiveMode()
412  {
413    return true;
414  }
415
416
417
418  /**
419   * {@inheritDoc}
420   */
421  @Override()
422  public boolean defaultsToInteractiveMode()
423  {
424    return true;
425  }
426
427
428
429  /**
430   * {@inheritDoc}
431   */
432  @Override()
433  public boolean supportsPropertiesFile()
434  {
435    return true;
436  }
437
438
439
440  /**
441   * {@inheritDoc}
442   */
443  @Override()
444  protected boolean supportsOutputFile()
445  {
446    return true;
447  }
448
449
450
451  /**
452   * {@inheritDoc}
453   */
454  @Override()
455  protected boolean supportsAuthentication()
456  {
457    return true;
458  }
459
460
461
462  /**
463   * {@inheritDoc}
464   */
465  @Override()
466  protected boolean defaultToPromptForBindPassword()
467  {
468    return true;
469  }
470
471
472
473  /**
474   * {@inheritDoc}
475   */
476  @Override()
477  protected boolean supportsSASLHelp()
478  {
479    return true;
480  }
481
482
483
484  /**
485   * {@inheritDoc}
486   */
487  @Override()
488  protected boolean includeAlternateLongIdentifiers()
489  {
490    return true;
491  }
492
493
494
495  /**
496   * {@inheritDoc}
497   */
498  @Override()
499  protected boolean supportsSSLDebugging()
500  {
501    return true;
502  }
503
504
505
506  /**
507   * {@inheritDoc}
508   */
509  @Override()
510  protected boolean supportsMultipleServers()
511  {
512    return true;
513  }
514
515
516
517  /**
518   * {@inheritDoc}
519   */
520  @Override()
521  protected boolean logToolInvocationByDefault()
522  {
523    return true;
524  }
525
526
527
528  /**
529   * {@inheritDoc}
530   */
531  @Override()
532  public void addNonLDAPArguments(final ArgumentParser parser)
533       throws ArgumentException
534  {
535    // Get a copy of the argument parser for later use.
536    this.parser = parser;
537
538
539    // Get the current time formatted as a generalized time.
540    final String currentGeneralizedTime =
541         StaticUtils.encodeGeneralizedTime(System.currentTimeMillis());
542    final String olderGeneralizedTime =
543         StaticUtils.encodeGeneralizedTime(
544              System.currentTimeMillis() - 12_345L);
545
546
547    // Define the global arguments used to indicate which users to target.
548    final DNArgument targetDN = new DNArgument('b', ARG_TARGET_DN, false, 0,
549         null, INFO_MANAGE_ACCT_ARG_DESC_TARGET_DN.get());
550    targetDN.addLongIdentifier("userDN", true);
551    targetDN.addLongIdentifier("target-dn", true);
552    targetDN.addLongIdentifier("user-dn", true);
553    targetDN.setArgumentGroupName(
554         INFO_MANAGE_ACCT_ARG_GROUP_TARGET_USER_ARGS.get());
555    parser.addArgument(targetDN);
556
557    final FileArgument dnInputFile = new FileArgument(null, ARG_DN_INPUT_FILE,
558         false, 0, null, INFO_MANAGE_ACCT_ARG_DESC_DN_FILE.get(), true,
559         true, true, false);
560    dnInputFile.addLongIdentifier("targetDNFile", true);
561    dnInputFile.addLongIdentifier("userDNFile", true);
562    dnInputFile.addLongIdentifier("dn-input-file", true);
563    dnInputFile.addLongIdentifier("target-dn-file", true);
564    dnInputFile.addLongIdentifier("user-dn-file", true);
565    dnInputFile.setArgumentGroupName(
566         INFO_MANAGE_ACCT_ARG_GROUP_TARGET_USER_ARGS.get());
567    parser.addArgument(dnInputFile);
568
569    final FilterArgument targetFilter = new FilterArgument(null,
570         ARG_TARGET_FILTER, false, 0, null,
571         INFO_MANAGE_ACCT_ARG_DESC_TARGET_FILTER.get(ARG_BASE_DN));
572    targetFilter.addLongIdentifier("target-filter", true);
573    targetFilter.setArgumentGroupName(
574         INFO_MANAGE_ACCT_ARG_GROUP_TARGET_USER_ARGS.get());
575    parser.addArgument(targetFilter);
576
577    final FileArgument filterInputFile = new FileArgument(null,
578         ARG_FILTER_INPUT_FILE, false, 0, null,
579         INFO_MANAGE_ACCT_ARG_DESC_FILTER_INPUT_FILE.get(ARG_BASE_DN),
580         true, true, true, false);
581    filterInputFile.addLongIdentifier("targetFilterFile", true);
582    filterInputFile.addLongIdentifier("filter-input-file", true);
583    filterInputFile.addLongIdentifier("target-filter-file", true);
584    filterInputFile.setArgumentGroupName(
585         INFO_MANAGE_ACCT_ARG_GROUP_TARGET_USER_ARGS.get());
586    parser.addArgument(filterInputFile);
587
588    final StringArgument targetUserID = new StringArgument(null,
589         ARG_TARGET_USER_ID, false, 0, null,
590         INFO_MANAGE_ACCT_ARG_DESC_TARGET_USER_ID.get(ARG_BASE_DN,
591              ARG_USER_ID_ATTRIBUTE));
592    targetUserID.addLongIdentifier("userID", true);
593    targetUserID.addLongIdentifier("target-user-id", true);
594    targetUserID.addLongIdentifier("user-id", true);
595    targetUserID.setArgumentGroupName(
596         INFO_MANAGE_ACCT_ARG_GROUP_TARGET_USER_ARGS.get());
597    parser.addArgument(targetUserID);
598
599    final FileArgument userIDInputFile = new FileArgument(null,
600         ARG_USER_ID_INPUT_FILE, false, 0, null,
601         INFO_MANAGE_ACCT_ARG_DESC_USER_ID_INPUT_FILE.get(ARG_BASE_DN,
602              ARG_USER_ID_ATTRIBUTE),
603         true, true, true, false);
604    userIDInputFile.addLongIdentifier("targetUserIDFile", true);
605    userIDInputFile.addLongIdentifier("user-id-input-file", true);
606    userIDInputFile.addLongIdentifier("target-user-id-file", true);
607    userIDInputFile.setArgumentGroupName(
608         INFO_MANAGE_ACCT_ARG_GROUP_TARGET_USER_ARGS.get());
609    parser.addArgument(userIDInputFile);
610
611    final StringArgument userIDAttribute = new StringArgument(null,
612         ARG_USER_ID_ATTRIBUTE, false, 1, null,
613         INFO_MANAGE_ACCT_ARG_DESC_USER_ID_ATTR.get(
614              ARG_TARGET_USER_ID, ARG_USER_ID_INPUT_FILE,
615              DEFAULT_USER_ID_ATTRIBUTE),
616         DEFAULT_USER_ID_ATTRIBUTE);
617    userIDAttribute.addLongIdentifier("user-id-attribute", true);
618    userIDAttribute.setArgumentGroupName(
619         INFO_MANAGE_ACCT_ARG_GROUP_TARGET_USER_ARGS.get());
620    parser.addArgument(userIDAttribute);
621
622    final DNArgument baseDN = new DNArgument(null, ARG_BASE_DN, false, 1, null,
623         INFO_MANAGE_ACCT_ARG_DESC_BASE_DN.get(ARG_TARGET_FILTER,
624              ARG_FILTER_INPUT_FILE, ARG_TARGET_USER_ID,
625              ARG_USER_ID_INPUT_FILE),
626         DEFAULT_BASE_DN);
627    baseDN.addLongIdentifier("base-dn", true);
628    baseDN.setArgumentGroupName(
629         INFO_MANAGE_ACCT_ARG_GROUP_TARGET_USER_ARGS.get());
630    parser.addArgument(baseDN);
631
632    final IntegerArgument simplePageSize = new IntegerArgument('z',
633         ARG_SIMPLE_PAGE_SIZE, false, 1, null,
634         INFO_MANAGE_ACCT_ARG_DESC_SIMPLE_PAGE_SIZE.get(getToolName()), 1,
635         Integer.MAX_VALUE);
636    simplePageSize.addLongIdentifier("simple-page-size", true);
637    simplePageSize.setArgumentGroupName(
638         INFO_MANAGE_ACCT_ARG_GROUP_TARGET_USER_ARGS.get(getToolName()));
639    parser.addArgument(simplePageSize);
640
641
642    // Ensure that the user will be required ot provide at least one of the
643    // arguments to specify which users to target.
644    parser.addRequiredArgumentSet(targetDN, dnInputFile, targetFilter,
645         filterInputFile, targetUserID, userIDInputFile);
646
647
648    // Define the global arguments used to control the amount of load the tool
649    // should be permitted to generate.
650    final IntegerArgument numThreads = new IntegerArgument('t', ARG_NUM_THREADS,
651         false, 1, null,
652         INFO_MANAGE_ACCT_ARG_DESC_NUM_THREADS.get(getToolName()), 1,
653         Integer.MAX_VALUE, 1);
654    numThreads.addLongIdentifier("num-threads", true);
655    numThreads.setArgumentGroupName(
656         INFO_MANAGE_ACCT_ARG_GROUP_PERFORMANCE.get());
657    parser.addArgument(numThreads);
658
659    final IntegerArgument numSearchThreads = new IntegerArgument(null,
660         ARG_NUM_SEARCH_THREADS, false, 1, null,
661         INFO_MANAGE_ACCT_ARG_DESC_NUM_SEARCH_THREADS.get(getToolName()), 1,
662         Integer.MAX_VALUE, 1);
663    numSearchThreads.addLongIdentifier("num-search-threads", true);
664    numSearchThreads.setArgumentGroupName(
665         INFO_MANAGE_ACCT_ARG_GROUP_PERFORMANCE.get());
666    parser.addArgument(numSearchThreads);
667
668    final IntegerArgument ratePerSecond = new IntegerArgument('r',
669         ARG_RATE_PER_SECOND, false, 1, null,
670         INFO_MANAGE_ACCT_ARG_DESC_RATE_PER_SECOND.get(
671              ARG_VARIABLE_RATE_DATA),
672         1, Integer.MAX_VALUE);
673    ratePerSecond.addLongIdentifier("rate-per-second", true);
674    ratePerSecond.setArgumentGroupName(
675         INFO_MANAGE_ACCT_ARG_GROUP_PERFORMANCE.get());
676    parser.addArgument(ratePerSecond);
677
678    final FileArgument variableRateData = new FileArgument(null,
679         ARG_VARIABLE_RATE_DATA, false, 1, null,
680         INFO_MANAGE_ACCT_ARG_DESC_VARIABLE_RATE_DATA.get(
681              ARG_RATE_PER_SECOND),
682         true, true, true, false);
683    variableRateData.addLongIdentifier("variable-rate-data", true);
684    variableRateData.setArgumentGroupName(
685         INFO_MANAGE_ACCT_ARG_GROUP_PERFORMANCE.get());
686    parser.addArgument(variableRateData);
687
688    final FileArgument generateSampleRateFile = new FileArgument(null,
689         ARG_GENERATE_SAMPLE_RATE_FILE, false, 1, null,
690         INFO_MANAGE_ACCT_ARG_DESC_GENERATE_SAMPLE_RATE_FILE.get(
691              ARG_VARIABLE_RATE_DATA),
692         false, true, true, false);
693    generateSampleRateFile.addLongIdentifier("generate-sample-rate-file", true);
694    generateSampleRateFile.setArgumentGroupName(
695         INFO_MANAGE_ACCT_ARG_GROUP_PERFORMANCE.get());
696    generateSampleRateFile.setUsageArgument(true);
697    parser.addArgument(generateSampleRateFile);
698
699
700    // Define the global arguments tht pertain to the reject file.
701    final FileArgument rejectFile = new FileArgument('R', ARG_REJECT_FILE,
702         false, 1, null, INFO_MANAGE_ACCT_ARG_DESC_REJECT_FILE.get(),
703         false, true, true, false);
704    rejectFile.addLongIdentifier("reject-file", true);
705    parser.addArgument(rejectFile);
706
707    final BooleanArgument appendToRejectFile = new BooleanArgument(null,
708         ARG_APPEND_TO_REJECT_FILE, 1,
709         INFO_MANAGE_ACCT_ARG_DESC_APPEND_TO_REJECT_FILE.get(
710              rejectFile.getIdentifierString()));
711    appendToRejectFile.addLongIdentifier("append-to-reject-file", true);
712    parser.addArgument(appendToRejectFile);
713
714    parser.addDependentArgumentSet(appendToRejectFile, rejectFile);
715
716
717    // Define the argument used to suppress result operations without values.
718    final BooleanArgument suppressEmptyResultOperations =
719         new BooleanArgument(null, ARG_SUPPRESS_EMPTY_RESULT_OPERATIONS,
720              1,
721              INFO_MANAGE_ACCT_ARG_DESC_SUPPRESS_EMPTY_RESULT_OPERATIONS.get(
722                   getToolName()));
723    parser.addArgument(suppressEmptyResultOperations);
724
725
726    // Define the subcommand used to retrieve all state information for a user.
727    createSubCommand(ManageAccountSubCommandType.GET_ALL,
728         INFO_MANAGE_ACCT_SC_GET_ALL_EXAMPLE.get(EXAMPLE_TARGET_USER_DN));
729
730
731    // Define the subcommand used to retrieve the password policy DN for a user.
732    createSubCommand(ManageAccountSubCommandType.GET_PASSWORD_POLICY_DN,
733         INFO_MANAGE_ACCT_SC_GET_POLICY_DN_EXAMPLE.get(EXAMPLE_TARGET_USER_DN));
734
735
736    // Define the subcommand to determine whether the account is usable.
737    createSubCommand(ManageAccountSubCommandType.GET_ACCOUNT_IS_USABLE,
738         INFO_MANAGE_ACCT_SC_GET_IS_USABLE_EXAMPLE.get(EXAMPLE_TARGET_USER_DN));
739
740
741    // Define the subcommand to retrieve the set of password policy state
742    // account usability notice messages.
743    createSubCommand(ManageAccountSubCommandType.GET_ACCOUNT_USABILITY_NOTICES,
744         INFO_MANAGE_ACCT_SC_GET_USABILITY_NOTICES_EXAMPLE.get(
745              EXAMPLE_TARGET_USER_DN));
746
747
748    // Define the subcommand to retrieve the set of password policy state
749    // account usability warning messages.
750    createSubCommand(ManageAccountSubCommandType.GET_ACCOUNT_USABILITY_WARNINGS,
751         INFO_MANAGE_ACCT_SC_GET_USABILITY_WARNINGS_EXAMPLE.get(
752              EXAMPLE_TARGET_USER_DN));
753
754
755    // Define the subcommand to retrieve the set of password policy state
756    // account usability error messages.
757    createSubCommand(ManageAccountSubCommandType.GET_ACCOUNT_USABILITY_ERRORS,
758         INFO_MANAGE_ACCT_SC_GET_USABILITY_ERRORS_EXAMPLE.get(
759              EXAMPLE_TARGET_USER_DN));
760
761
762    // Define the subcommand to retrieve the password changed time for a user.
763    createSubCommand(ManageAccountSubCommandType.GET_PASSWORD_CHANGED_TIME,
764         INFO_MANAGE_ACCT_SC_GET_PW_CHANGED_TIME_EXAMPLE.get(
765              EXAMPLE_TARGET_USER_DN));
766
767
768    // Define the subcommand to set the password changed time for a user.
769    final ArgumentParser setPWChangedTimeParser =
770         createSubCommandParser(
771              ManageAccountSubCommandType.SET_PASSWORD_CHANGED_TIME);
772
773    final TimestampArgument setPWChangedTimeValueArg = new TimestampArgument(
774         'O', "passwordChangedTime", false, 1, null,
775         INFO_MANAGE_ACCT_SC_SET_PW_CHANGED_TIME_ARG_VALUE.get());
776    setPWChangedTimeValueArg.addLongIdentifier("operationValue", true);
777    setPWChangedTimeValueArg.addLongIdentifier("password-changed-time", true);
778    setPWChangedTimeValueArg.addLongIdentifier("operation-value", true);
779    setPWChangedTimeParser.addArgument(setPWChangedTimeValueArg);
780
781    createSubCommand(ManageAccountSubCommandType.SET_PASSWORD_CHANGED_TIME,
782         setPWChangedTimeParser,
783         createSubCommandExample(
784              ManageAccountSubCommandType.SET_PASSWORD_CHANGED_TIME,
785              INFO_MANAGE_ACCT_SC_SET_PW_CHANGED_TIME_EXAMPLE.get(
786                   EXAMPLE_TARGET_USER_DN, currentGeneralizedTime),
787              "--passwordChangedTime", currentGeneralizedTime));
788
789
790    // Define the subcommand to clear the password changed time for a user.
791    createSubCommand(ManageAccountSubCommandType.CLEAR_PASSWORD_CHANGED_TIME,
792         INFO_MANAGE_ACCT_SC_CLEAR_PW_CHANGED_TIME_EXAMPLE.get(
793              EXAMPLE_TARGET_USER_DN));
794
795
796    // Define the subcommand to determine whether a user account is disabled.
797    createSubCommand(ManageAccountSubCommandType.GET_ACCOUNT_IS_DISABLED,
798         INFO_MANAGE_ACCT_SC_GET_IS_DISABLED_EXAMPLE.get(
799              EXAMPLE_TARGET_USER_DN));
800
801
802    // Define the subcommand to specify whether a user's account is disabled.
803    final ArgumentParser setAcctDisabledParser =
804         createSubCommandParser(
805              ManageAccountSubCommandType.SET_ACCOUNT_IS_DISABLED);
806
807    final BooleanValueArgument setAcctDisabledValueArg =
808         new BooleanValueArgument('O', "accountIsDisabled", true, null,
809              INFO_MANAGE_ACCT_SC_SET_IS_DISABLED_ARG_VALUE.get());
810    setAcctDisabledValueArg.addLongIdentifier("operationValue", true);
811    setAcctDisabledValueArg.addLongIdentifier("account-is-disabled", true);
812    setAcctDisabledValueArg.addLongIdentifier("operation-value", true);
813    setAcctDisabledParser.addArgument(setAcctDisabledValueArg);
814
815    createSubCommand(ManageAccountSubCommandType.SET_ACCOUNT_IS_DISABLED,
816         setAcctDisabledParser,
817         createSubCommandExample(
818              ManageAccountSubCommandType.SET_ACCOUNT_IS_DISABLED,
819              INFO_MANAGE_ACCT_SC_SET_IS_DISABLED_EXAMPLE.get(
820                   EXAMPLE_TARGET_USER_DN),
821              "--accountIsDisabled", "true"));
822
823
824    // Define the subcommand to clear the account disabled state.
825    createSubCommand(ManageAccountSubCommandType.CLEAR_ACCOUNT_IS_DISABLED,
826         INFO_MANAGE_ACCT_SC_CLEAR_IS_DISABLED_EXAMPLE.get(
827              EXAMPLE_TARGET_USER_DN));
828
829
830    // Define the subcommand to retrieve the account activation time for a user.
831    createSubCommand(ManageAccountSubCommandType.GET_ACCOUNT_ACTIVATION_TIME,
832         INFO_MANAGE_ACCT_SC_GET_ACCT_ACT_TIME_EXAMPLE.get(
833              EXAMPLE_TARGET_USER_DN));
834
835
836    // Define the subcommand to set the account activation time for a user.
837    final ArgumentParser setAcctActivationTimeParser =
838         createSubCommandParser(
839              ManageAccountSubCommandType.SET_ACCOUNT_ACTIVATION_TIME);
840
841    final TimestampArgument setAcctActivationTimeValueArg =
842         new TimestampArgument('O', "accountActivationTime", false, 1, null,
843              INFO_MANAGE_ACCT_SC_SET_ACCT_ACT_TIME_ARG_VALUE.get());
844    setAcctActivationTimeValueArg.addLongIdentifier("operationValue", true);
845    setAcctActivationTimeValueArg.addLongIdentifier("account-activation-time",
846         true);
847    setAcctActivationTimeValueArg.addLongIdentifier("operation-value", true);
848    setAcctActivationTimeParser.addArgument(setAcctActivationTimeValueArg);
849
850    createSubCommand(ManageAccountSubCommandType.SET_ACCOUNT_ACTIVATION_TIME,
851         setAcctActivationTimeParser,
852         createSubCommandExample(
853              ManageAccountSubCommandType.SET_ACCOUNT_ACTIVATION_TIME,
854              INFO_MANAGE_ACCT_SC_SET_ACCT_ACT_TIME_EXAMPLE.get(
855                   EXAMPLE_TARGET_USER_DN, currentGeneralizedTime),
856              "--accountActivationTime", currentGeneralizedTime));
857
858
859    // Define the subcommand to clear the account activation time for a user.
860    createSubCommand(ManageAccountSubCommandType.CLEAR_ACCOUNT_ACTIVATION_TIME,
861         INFO_MANAGE_ACCT_SC_CLEAR_ACCT_ACT_TIME_EXAMPLE.get(
862              EXAMPLE_TARGET_USER_DN));
863
864
865    // Define the subcommand to retrieve the length of time until a user's
866    // account is activated.
867    createSubCommand(
868         ManageAccountSubCommandType.GET_SECONDS_UNTIL_ACCOUNT_ACTIVATION,
869         INFO_MANAGE_ACCT_SC_GET_SECONDS_UNTIL_ACCT_ACT_EXAMPLE.get(
870              EXAMPLE_TARGET_USER_DN));
871
872
873    // Define the subcommand to determine whether a user's account is not yet
874    // active.
875    createSubCommand(ManageAccountSubCommandType.GET_ACCOUNT_IS_NOT_YET_ACTIVE,
876         INFO_MANAGE_ACCT_SC_GET_ACCT_NOT_YET_ACTIVE_EXAMPLE.get(
877              EXAMPLE_TARGET_USER_DN));
878
879
880    // Define the subcommand to retrieve the account expiration time for a user.
881    createSubCommand(ManageAccountSubCommandType.GET_ACCOUNT_EXPIRATION_TIME,
882         INFO_MANAGE_ACCT_SC_GET_ACCT_EXP_TIME_EXAMPLE.get(
883              EXAMPLE_TARGET_USER_DN));
884
885
886    // Define the subcommand to set the account expiration time for a user.
887    final ArgumentParser setAcctExpirationTimeParser =
888         createSubCommandParser(
889              ManageAccountSubCommandType.SET_ACCOUNT_EXPIRATION_TIME);
890
891    final TimestampArgument setAcctExpirationTimeValueArg =
892         new TimestampArgument('O', "accountExpirationTime", false, 1, null,
893              INFO_MANAGE_ACCT_SC_SET_ACCT_EXP_TIME_ARG_VALUE.get());
894    setAcctExpirationTimeValueArg.addLongIdentifier("operationValue", true);
895    setAcctExpirationTimeValueArg.addLongIdentifier("account-expiration-time",
896         true);
897    setAcctExpirationTimeValueArg.addLongIdentifier("operation-value", true);
898    setAcctExpirationTimeParser.addArgument(setAcctExpirationTimeValueArg);
899
900    createSubCommand(ManageAccountSubCommandType.SET_ACCOUNT_EXPIRATION_TIME,
901         setAcctExpirationTimeParser,
902         createSubCommandExample(
903              ManageAccountSubCommandType.SET_ACCOUNT_EXPIRATION_TIME,
904              INFO_MANAGE_ACCT_SC_SET_ACCT_EXP_TIME_EXAMPLE.get(
905                   EXAMPLE_TARGET_USER_DN, currentGeneralizedTime),
906              "--accountExpirationTime", currentGeneralizedTime));
907
908
909    // Define the subcommand to clear the account expiration time for a user.
910    createSubCommand(ManageAccountSubCommandType.CLEAR_ACCOUNT_EXPIRATION_TIME,
911         INFO_MANAGE_ACCT_SC_CLEAR_ACCT_EXP_TIME_EXAMPLE.get(
912              EXAMPLE_TARGET_USER_DN));
913
914
915    // Define the subcommand to retrieve the length of time until a user's
916    // account is expired.
917    createSubCommand(
918         ManageAccountSubCommandType.GET_SECONDS_UNTIL_ACCOUNT_EXPIRATION,
919         INFO_MANAGE_ACCT_SC_GET_SECONDS_UNTIL_ACCT_EXP_EXAMPLE.get(
920              EXAMPLE_TARGET_USER_DN));
921
922
923    // Define the subcommand to determine whether a user's account is expired.
924    createSubCommand(ManageAccountSubCommandType.GET_ACCOUNT_IS_EXPIRED,
925         INFO_MANAGE_ACCT_SC_GET_ACCT_IS_EXPIRED_EXAMPLE.get(
926              EXAMPLE_TARGET_USER_DN));
927
928
929    // Define the subcommand to retrieve a user's password expiration warned
930    // time.
931    createSubCommand(
932         ManageAccountSubCommandType.GET_PASSWORD_EXPIRATION_WARNED_TIME,
933         INFO_MANAGE_ACCT_SC_GET_PW_EXP_WARNED_TIME_EXAMPLE.get(
934              EXAMPLE_TARGET_USER_DN));
935
936
937    // Define the subcommand to set a user's password expiration warned time.
938    final ArgumentParser setPWExpWarnedTimeParser =
939         createSubCommandParser(
940              ManageAccountSubCommandType.SET_PASSWORD_EXPIRATION_WARNED_TIME);
941
942    final TimestampArgument setPWExpWarnedTimeValueArg =
943         new TimestampArgument('O', "passwordExpirationWarnedTime", false, 1,
944              null, INFO_MANAGE_ACCT_SC_SET_PW_EXP_WARNED_TIME_ARG_VALUE.get());
945    setPWExpWarnedTimeValueArg.addLongIdentifier("operationValue", true);
946    setPWExpWarnedTimeValueArg.addLongIdentifier(
947         "password-expiration-warned-time", true);
948    setPWExpWarnedTimeValueArg.addLongIdentifier("operation-value", true);
949    setPWExpWarnedTimeParser.addArgument(setPWExpWarnedTimeValueArg);
950
951    createSubCommand(
952         ManageAccountSubCommandType.SET_PASSWORD_EXPIRATION_WARNED_TIME,
953         setPWExpWarnedTimeParser,
954         createSubCommandExample(
955              ManageAccountSubCommandType.SET_PASSWORD_EXPIRATION_WARNED_TIME,
956              INFO_MANAGE_ACCT_SC_SET_PW_EXP_WARNED_TIME_EXAMPLE.get(
957                   EXAMPLE_TARGET_USER_DN, currentGeneralizedTime),
958              "--passwordExpirationWarnedTime", currentGeneralizedTime));
959
960
961    // Define the subcommand to clear a user's password expiration warned time.
962    createSubCommand(
963         ManageAccountSubCommandType.CLEAR_PASSWORD_EXPIRATION_WARNED_TIME,
964         INFO_MANAGE_ACCT_SC_CLEAR_PW_EXP_WARNED_TIME_EXAMPLE.get(
965              EXAMPLE_TARGET_USER_DN));
966
967
968    // Define the subcommand to get the number of seconds until a user is
969    // eligible to receive a password expiration warning.
970    createSubCommand(
971         ManageAccountSubCommandType.
972              GET_SECONDS_UNTIL_PASSWORD_EXPIRATION_WARNING,
973         INFO_MANAGE_ACCT_SC_GET_SECONDS_UNTIL_PW_EXP_WARNING_EXAMPLE.get(
974              EXAMPLE_TARGET_USER_DN));
975
976
977    // Define the subcommand to retrieve a user's password expiration time.
978    createSubCommand(ManageAccountSubCommandType.GET_PASSWORD_EXPIRATION_TIME,
979         INFO_MANAGE_ACCT_SC_GET_PW_EXP_TIME_EXAMPLE.get(
980              EXAMPLE_TARGET_USER_DN));
981
982
983    // Define the subcommand to get the number of seconds until a user's
984    // password expires.
985    createSubCommand(
986         ManageAccountSubCommandType.GET_SECONDS_UNTIL_PASSWORD_EXPIRATION,
987         INFO_MANAGE_ACCT_SC_GET_SECONDS_UNTIL_PW_EXP_EXAMPLE.get(
988              EXAMPLE_TARGET_USER_DN));
989
990
991    // Define the subcommand to determine whether a user's password is expired.
992    createSubCommand(ManageAccountSubCommandType.GET_PASSWORD_IS_EXPIRED,
993         INFO_MANAGE_ACCT_SC_GET_PW_IS_EXPIRED_EXAMPLE.get(
994              EXAMPLE_TARGET_USER_DN));
995
996
997    // Define the subcommand to determine whether an account is failure locked.
998    createSubCommand(ManageAccountSubCommandType.GET_ACCOUNT_IS_FAILURE_LOCKED,
999         INFO_MANAGE_ACCT_SC_GET_ACCT_FAILURE_LOCKED_EXAMPLE.get(
1000              EXAMPLE_TARGET_USER_DN));
1001
1002
1003    // Define the subcommand to specify whether an account is failure locked.
1004    final ArgumentParser setIsFailureLockedParser =
1005         createSubCommandParser(
1006              ManageAccountSubCommandType.SET_ACCOUNT_IS_FAILURE_LOCKED);
1007
1008    final BooleanValueArgument setIsFailureLockedValueArg =
1009         new BooleanValueArgument('O', "accountIsFailureLocked", true, null,
1010              INFO_MANAGE_ACCT_SC_SET_ACCT_FAILURE_LOCKED_ARG_VALUE.get());
1011    setIsFailureLockedValueArg.addLongIdentifier("operationValue", true);
1012    setIsFailureLockedValueArg.addLongIdentifier("account-is-failure-locked",
1013         true);
1014    setIsFailureLockedValueArg.addLongIdentifier("operation-value", true);
1015    setIsFailureLockedParser.addArgument(setIsFailureLockedValueArg);
1016
1017    createSubCommand(ManageAccountSubCommandType.SET_ACCOUNT_IS_FAILURE_LOCKED,
1018         setIsFailureLockedParser,
1019         createSubCommandExample(
1020              ManageAccountSubCommandType.SET_ACCOUNT_IS_FAILURE_LOCKED,
1021              INFO_MANAGE_ACCT_SC_SET_ACCT_FAILURE_LOCKED_EXAMPLE.get(
1022                   EXAMPLE_TARGET_USER_DN),
1023              "--accountIsFailureLocked", "true"));
1024
1025
1026    // Define the subcommand to get the time an account was failure locked.
1027    createSubCommand(ManageAccountSubCommandType.GET_FAILURE_LOCKOUT_TIME,
1028         INFO_MANAGE_ACCT_SC_GET_FAILURE_LOCKED_TIME_EXAMPLE.get(
1029              EXAMPLE_TARGET_USER_DN));
1030
1031
1032    // Define the subcommand to get the length of time until a failure-locked
1033    // account will be automatically unlocked.
1034    createSubCommand(
1035         ManageAccountSubCommandType.
1036              GET_SECONDS_UNTIL_AUTHENTICATION_FAILURE_UNLOCK,
1037         INFO_MANAGE_ACCT_SC_GET_SECONDS_UNTIL_FAILURE_UNLOCK_EXAMPLE.get(
1038              EXAMPLE_TARGET_USER_DN));
1039
1040
1041    // Define the subcommand to determine the authentication failure times.
1042    createSubCommand(
1043         ManageAccountSubCommandType.GET_AUTHENTICATION_FAILURE_TIMES,
1044         INFO_MANAGE_ACCT_SC_GET_AUTH_FAILURE_TIMES_EXAMPLE.get(
1045              EXAMPLE_TARGET_USER_DN));
1046
1047
1048    // Define the subcommand to add values to the set of authentication failure
1049    // times.
1050    final ArgumentParser addAuthFailureTimeParser =
1051         createSubCommandParser(
1052              ManageAccountSubCommandType.ADD_AUTHENTICATION_FAILURE_TIME);
1053
1054    final TimestampArgument addAuthFailureTimeValueArg =
1055         new TimestampArgument('O', "authenticationFailureTime", false, 0, null,
1056              INFO_MANAGE_ACCT_SC_ADD_AUTH_FAILURE_TIME_ARG_VALUE.get());
1057    addAuthFailureTimeValueArg.addLongIdentifier("operationValue", true);
1058    addAuthFailureTimeValueArg.addLongIdentifier(
1059         "authentication-failure-time", true);
1060    addAuthFailureTimeValueArg.addLongIdentifier("operation-value", true);
1061    addAuthFailureTimeParser.addArgument(addAuthFailureTimeValueArg);
1062
1063    createSubCommand(
1064         ManageAccountSubCommandType.ADD_AUTHENTICATION_FAILURE_TIME,
1065         addAuthFailureTimeParser,
1066         createSubCommandExample(
1067              ManageAccountSubCommandType.ADD_AUTHENTICATION_FAILURE_TIME,
1068              INFO_MANAGE_ACCT_SC_ADD_AUTH_FAILURE_TIME_EXAMPLE.get(
1069                   EXAMPLE_TARGET_USER_DN)));
1070
1071
1072    // Define the subcommand to replace the authentication failure times.
1073    final ArgumentParser setAuthFailureTimesParser =
1074         createSubCommandParser(
1075              ManageAccountSubCommandType.SET_AUTHENTICATION_FAILURE_TIMES);
1076
1077    final TimestampArgument setAuthFailureTimesValueArg =
1078         new TimestampArgument('O', "authenticationFailureTime", false, 0, null,
1079              INFO_MANAGE_ACCT_SC_SET_AUTH_FAILURE_TIMES_ARG_VALUE.get());
1080    setAuthFailureTimesValueArg.addLongIdentifier("operationValue", true);
1081    setAuthFailureTimesValueArg.addLongIdentifier(
1082         "authentication-failure-time", true);
1083    setAuthFailureTimesValueArg.addLongIdentifier("operation-value", true);
1084    setAuthFailureTimesParser.addArgument(setAuthFailureTimesValueArg);
1085
1086    createSubCommand(
1087         ManageAccountSubCommandType.SET_AUTHENTICATION_FAILURE_TIMES,
1088         setAuthFailureTimesParser,
1089         createSubCommandExample(
1090              ManageAccountSubCommandType.SET_AUTHENTICATION_FAILURE_TIMES,
1091              INFO_MANAGE_ACCT_SC_SET_AUTH_FAILURE_TIMES_EXAMPLE.get(
1092                   EXAMPLE_TARGET_USER_DN, olderGeneralizedTime,
1093                   currentGeneralizedTime),
1094              "--authenticationFailureTime", olderGeneralizedTime,
1095              "--authenticationFailureTime", currentGeneralizedTime));
1096
1097
1098    // Define the subcommand to clear the authentication failure times.
1099    createSubCommand(
1100         ManageAccountSubCommandType.CLEAR_AUTHENTICATION_FAILURE_TIMES,
1101         INFO_MANAGE_ACCT_SC_CLEAR_AUTH_FAILURE_TIMES_EXAMPLE.get(
1102              EXAMPLE_TARGET_USER_DN));
1103
1104
1105    // Define the subcommand to get the remaining authentication failure count.
1106    createSubCommand(
1107         ManageAccountSubCommandType.GET_REMAINING_AUTHENTICATION_FAILURE_COUNT,
1108         INFO_MANAGE_ACCT_SC_GET_REMAINING_FAILURE_COUNT_EXAMPLE.get(
1109              EXAMPLE_TARGET_USER_DN));
1110
1111
1112    // Define the subcommand to determine whether the account is idle locked.
1113    createSubCommand(ManageAccountSubCommandType.GET_ACCOUNT_IS_IDLE_LOCKED,
1114         INFO_MANAGE_ACCT_SC_GET_ACCT_IDLE_LOCKED_EXAMPLE.get(
1115              EXAMPLE_TARGET_USER_DN));
1116
1117
1118    // Define the subcommand to get the length of time until the account is
1119    // idle locked.
1120    createSubCommand(ManageAccountSubCommandType.GET_SECONDS_UNTIL_IDLE_LOCKOUT,
1121         INFO_MANAGE_ACCT_SC_GET_SECONDS_UNTIL_IDLE_LOCKOUT_EXAMPLE.get(
1122              EXAMPLE_TARGET_USER_DN));
1123
1124
1125    // Define the subcommand to get the idle lockout time for an account.
1126    createSubCommand(ManageAccountSubCommandType.GET_IDLE_LOCKOUT_TIME,
1127         INFO_MANAGE_ACCT_SC_GET_IDLE_LOCKOUT_TIME_EXAMPLE.get(
1128              EXAMPLE_TARGET_USER_DN));
1129
1130
1131    // Define the subcommand to determine whether a user's password has been
1132    // reset.
1133    createSubCommand(ManageAccountSubCommandType.GET_MUST_CHANGE_PASSWORD,
1134         INFO_MANAGE_ACCT_SC_GET_MUST_CHANGE_PW_EXAMPLE.get(
1135              EXAMPLE_TARGET_USER_DN));
1136
1137
1138    // Define the subcommand to specify whether a user's password has been
1139    // reset.
1140    final ArgumentParser setPWIsResetParser =
1141         createSubCommandParser(
1142              ManageAccountSubCommandType.SET_MUST_CHANGE_PASSWORD);
1143
1144    final BooleanValueArgument setPWIsResetValueArg =
1145         new BooleanValueArgument('O', "mustChangePassword", true, null,
1146              INFO_MANAGE_ACCT_SC_SET_MUST_CHANGE_PW_ARG_VALUE.get());
1147    setPWIsResetValueArg.addLongIdentifier("passwordIsReset", true);
1148    setPWIsResetValueArg.addLongIdentifier("operationValue", true);
1149    setPWIsResetValueArg.addLongIdentifier("must-change-password", true);
1150    setPWIsResetValueArg.addLongIdentifier("password-is-reset", true);
1151    setPWIsResetValueArg.addLongIdentifier("operation-value", true);
1152    setPWIsResetParser.addArgument(setPWIsResetValueArg);
1153
1154    createSubCommand(ManageAccountSubCommandType.SET_MUST_CHANGE_PASSWORD,
1155         setPWIsResetParser,
1156         createSubCommandExample(
1157              ManageAccountSubCommandType.SET_MUST_CHANGE_PASSWORD,
1158              INFO_MANAGE_ACCT_SC_SET_MUST_CHANGE_PW_EXAMPLE.get(
1159                   EXAMPLE_TARGET_USER_DN),
1160              "--mustChangePassword", "true"));
1161
1162
1163    // Define the subcommand to clear the password reset state information.
1164    createSubCommand(ManageAccountSubCommandType.CLEAR_MUST_CHANGE_PASSWORD,
1165         INFO_MANAGE_ACCT_SC_CLEAR_MUST_CHANGE_PW_EXAMPLE.get(
1166              EXAMPLE_TARGET_USER_DN));
1167
1168
1169    // Define the subcommand to determine whether the account is reset locked.
1170    createSubCommand(
1171         ManageAccountSubCommandType.GET_ACCOUNT_IS_PASSWORD_RESET_LOCKED,
1172         INFO_MANAGE_ACCT_SC_GET_ACCT_IS_RESET_LOCKED_EXAMPLE.get(
1173              EXAMPLE_TARGET_USER_DN));
1174
1175
1176    // Define the subcommand to get the length of time until the password is
1177    // reset locked.
1178    createSubCommand(
1179         ManageAccountSubCommandType.GET_SECONDS_UNTIL_PASSWORD_RESET_LOCKOUT,
1180         INFO_MANAGE_ACCT_SC_GET_SECONDS_UNTIL_RESET_LOCKOUT_EXAMPLE.get(
1181              EXAMPLE_TARGET_USER_DN));
1182
1183
1184    // Define the subcommand to get the password reset lockout time.
1185    createSubCommand(
1186         ManageAccountSubCommandType.GET_PASSWORD_RESET_LOCKOUT_TIME,
1187         INFO_MANAGE_ACCT_SC_GET_RESET_LOCKOUT_TIME_EXAMPLE.get(
1188              EXAMPLE_TARGET_USER_DN));
1189
1190
1191    // Define the subcommand to get the last login time.
1192    createSubCommand(ManageAccountSubCommandType.GET_LAST_LOGIN_TIME,
1193         INFO_MANAGE_ACCT_SC_GET_LAST_LOGIN_TIME_EXAMPLE.get(
1194              EXAMPLE_TARGET_USER_DN));
1195
1196
1197    // Define the subcommand to set the last login time.
1198    final ArgumentParser setLastLoginTimeParser =
1199         createSubCommandParser(
1200              ManageAccountSubCommandType.SET_LAST_LOGIN_TIME);
1201
1202    final TimestampArgument setLastLoginTimeValueArg = new TimestampArgument(
1203         'O', "lastLoginTime", false, 1, null,
1204         INFO_MANAGE_ACCT_SC_SET_LAST_LOGIN_TIME_ARG_VALUE.get());
1205    setLastLoginTimeValueArg.addLongIdentifier("operationValue", true);
1206    setLastLoginTimeValueArg.addLongIdentifier("last-login-time", true);
1207    setLastLoginTimeValueArg.addLongIdentifier("operation-value", true);
1208    setLastLoginTimeParser.addArgument(setLastLoginTimeValueArg);
1209
1210    createSubCommand(ManageAccountSubCommandType.SET_LAST_LOGIN_TIME,
1211         setLastLoginTimeParser,
1212         createSubCommandExample(
1213              ManageAccountSubCommandType.SET_LAST_LOGIN_TIME,
1214              INFO_MANAGE_ACCT_SC_SET_LAST_LOGIN_TIME_EXAMPLE.get(
1215                   EXAMPLE_TARGET_USER_DN, currentGeneralizedTime),
1216              "--lastLoginTime", currentGeneralizedTime));
1217
1218
1219    // Define the subcommand to clear the last login time.
1220    createSubCommand(ManageAccountSubCommandType.CLEAR_LAST_LOGIN_TIME,
1221         INFO_MANAGE_ACCT_SC_CLEAR_LAST_LOGIN_TIME_EXAMPLE.get(
1222              EXAMPLE_TARGET_USER_DN));
1223
1224
1225    // Define the subcommand to get the last login IP address.
1226    createSubCommand(ManageAccountSubCommandType.GET_LAST_LOGIN_IP_ADDRESS,
1227         INFO_MANAGE_ACCT_SC_GET_LAST_LOGIN_IP_EXAMPLE.get(
1228              EXAMPLE_TARGET_USER_DN));
1229
1230
1231    // Define the subcommand to set the last login IP address.
1232    final ArgumentParser setLastLoginIPParser =
1233         createSubCommandParser(
1234              ManageAccountSubCommandType.SET_LAST_LOGIN_IP_ADDRESS);
1235
1236    final StringArgument setLastLoginIPValueArg = new StringArgument('O',
1237         "lastLoginIPAddress", true, 1, null,
1238         INFO_MANAGE_ACCT_SC_SET_LAST_LOGIN_IP_ARG_VALUE.get());
1239    setLastLoginIPValueArg.addLongIdentifier("operationValue", true);
1240    setLastLoginIPValueArg.addLongIdentifier("last-login-ip-address", true);
1241    setLastLoginIPValueArg.addLongIdentifier("operation-value", true);
1242    setLastLoginIPValueArg.addValueValidator(
1243         new IPAddressArgumentValueValidator());
1244    setLastLoginIPParser.addArgument(setLastLoginIPValueArg);
1245
1246
1247    createSubCommand(ManageAccountSubCommandType.SET_LAST_LOGIN_IP_ADDRESS,
1248         setLastLoginIPParser,
1249         createSubCommandExample(
1250              ManageAccountSubCommandType.SET_LAST_LOGIN_IP_ADDRESS,
1251              INFO_MANAGE_ACCT_SC_SET_LAST_LOGIN_IP_EXAMPLE.get(
1252                   EXAMPLE_TARGET_USER_DN, "1.2.3.4"),
1253              "--lastLoginIPAddress", "1.2.3.4"));
1254
1255
1256    // Define the subcommand to clear the last login IP address.
1257    createSubCommand(ManageAccountSubCommandType.CLEAR_LAST_LOGIN_IP_ADDRESS,
1258         INFO_MANAGE_ACCT_SC_CLEAR_LAST_LOGIN_IP_EXAMPLE.get(
1259              EXAMPLE_TARGET_USER_DN));
1260
1261
1262    // Define the subcommand to get the grace login use times.
1263    createSubCommand(ManageAccountSubCommandType.GET_GRACE_LOGIN_USE_TIMES,
1264         INFO_MANAGE_ACCT_SC_GET_GRACE_LOGIN_TIMES_EXAMPLE.get(
1265              EXAMPLE_TARGET_USER_DN));
1266
1267
1268    // Define the subcommand to add values to the set of grace login use times.
1269    final ArgumentParser addGraceLoginTimeParser =
1270         createSubCommandParser(
1271              ManageAccountSubCommandType.ADD_GRACE_LOGIN_USE_TIME);
1272
1273    final TimestampArgument addGraceLoginTimeValueArg =
1274         new TimestampArgument('O', "graceLoginUseTime", false, 0, null,
1275              INFO_MANAGE_ACCT_SC_ADD_GRACE_LOGIN_TIME_ARG_VALUE.get());
1276    addGraceLoginTimeValueArg.addLongIdentifier("operationValue", true);
1277    addGraceLoginTimeValueArg.addLongIdentifier("grace-login-use-time", true);
1278    addGraceLoginTimeValueArg.addLongIdentifier("operation-value", true);
1279    addGraceLoginTimeParser.addArgument(addGraceLoginTimeValueArg);
1280
1281    createSubCommand(ManageAccountSubCommandType.ADD_GRACE_LOGIN_USE_TIME,
1282         addGraceLoginTimeParser,
1283         createSubCommandExample(
1284              ManageAccountSubCommandType.ADD_GRACE_LOGIN_USE_TIME,
1285              INFO_MANAGE_ACCT_SC_ADD_GRACE_LOGIN_TIME_EXAMPLE.get(
1286                   EXAMPLE_TARGET_USER_DN)));
1287
1288
1289    // Define the subcommand to replace the set of grace login use times.
1290    final ArgumentParser setGraceLoginTimesParser =
1291         createSubCommandParser(
1292              ManageAccountSubCommandType.SET_GRACE_LOGIN_USE_TIMES);
1293
1294    final TimestampArgument setGraceLoginTimesValueArg =
1295         new TimestampArgument('O', "graceLoginUseTime", false, 0, null,
1296              INFO_MANAGE_ACCT_SC_SET_GRACE_LOGIN_TIMES_ARG_VALUE.get());
1297    setGraceLoginTimesValueArg.addLongIdentifier("operationValue", true);
1298    setGraceLoginTimesValueArg.addLongIdentifier("grace-login-use-time", true);
1299    setGraceLoginTimesValueArg.addLongIdentifier("operation-value", true);
1300    setGraceLoginTimesParser.addArgument(setGraceLoginTimesValueArg);
1301
1302    createSubCommand(ManageAccountSubCommandType.SET_GRACE_LOGIN_USE_TIMES,
1303         setGraceLoginTimesParser,
1304         createSubCommandExample(
1305              ManageAccountSubCommandType.SET_GRACE_LOGIN_USE_TIMES,
1306              INFO_MANAGE_ACCT_SC_SET_GRACE_LOGIN_TIMES_EXAMPLE.get(
1307                   EXAMPLE_TARGET_USER_DN, olderGeneralizedTime,
1308                   currentGeneralizedTime),
1309              "--graceLoginUseTime", olderGeneralizedTime,
1310              "--graceLoginUseTime", currentGeneralizedTime));
1311
1312
1313    // Define the subcommand to clear the grace login use times.
1314    createSubCommand(ManageAccountSubCommandType.CLEAR_GRACE_LOGIN_USE_TIMES,
1315         INFO_MANAGE_ACCT_SC_CLEAR_GRACE_LOGIN_TIMES_EXAMPLE.get(
1316              EXAMPLE_TARGET_USER_DN));
1317
1318
1319    // Define the subcommand to get the remaining grace login count.
1320    createSubCommand(
1321         ManageAccountSubCommandType.GET_REMAINING_GRACE_LOGIN_COUNT,
1322         INFO_MANAGE_ACCT_SC_GET_REMAINING_GRACE_LOGIN_COUNT_EXAMPLE.get(
1323              EXAMPLE_TARGET_USER_DN));
1324
1325
1326    // Define the subcommand to get the password changed by required time value.
1327    createSubCommand(
1328         ManageAccountSubCommandType.GET_PASSWORD_CHANGED_BY_REQUIRED_TIME,
1329         INFO_MANAGE_ACCT_SC_GET_PW_CHANGED_BY_REQ_TIME_EXAMPLE.get(
1330              EXAMPLE_TARGET_USER_DN));
1331
1332
1333    // Define the subcommand to set the password changed by required time value.
1334    final ArgumentParser setPWChangedByReqTimeParser =
1335         createSubCommandParser(ManageAccountSubCommandType.
1336              SET_PASSWORD_CHANGED_BY_REQUIRED_TIME);
1337
1338    final TimestampArgument setPWChangedByReqTimeValueArg =
1339         new TimestampArgument('O', "passwordChangedByRequiredTime", false, 1,
1340              null,
1341              INFO_MANAGE_ACCT_SC_SET_PW_CHANGED_BY_REQ_TIME_ARG_VALUE.get());
1342    setPWChangedByReqTimeValueArg.addLongIdentifier("operationValue", true);
1343    setPWChangedByReqTimeValueArg.addLongIdentifier(
1344         "password-changed-by-required-time", true);
1345    setPWChangedByReqTimeValueArg.addLongIdentifier("operation-value", true);
1346    setPWChangedByReqTimeParser.addArgument(
1347         setPWChangedByReqTimeValueArg);
1348
1349    createSubCommand(
1350         ManageAccountSubCommandType.SET_PASSWORD_CHANGED_BY_REQUIRED_TIME,
1351         setPWChangedByReqTimeParser,
1352         createSubCommandExample(
1353              ManageAccountSubCommandType.SET_PASSWORD_CHANGED_BY_REQUIRED_TIME,
1354              INFO_MANAGE_ACCT_SC_SET_PW_CHANGED_BY_REQ_TIME_EXAMPLE.get(
1355                   EXAMPLE_TARGET_USER_DN)));
1356
1357
1358    // Define the subcommand to clear the password changed by required time
1359    // value.
1360    createSubCommand(
1361         ManageAccountSubCommandType.CLEAR_PASSWORD_CHANGED_BY_REQUIRED_TIME,
1362         INFO_MANAGE_ACCT_SC_CLEAR_PW_CHANGED_BY_REQ_TIME_EXAMPLE.get(
1363              EXAMPLE_TARGET_USER_DN));
1364
1365
1366    // Define the subcommand to get the length of time until the required change
1367    // time.
1368    createSubCommand(
1369         ManageAccountSubCommandType.
1370              GET_SECONDS_UNTIL_REQUIRED_PASSWORD_CHANGE_TIME,
1371         INFO_MANAGE_ACCT_SC_GET_SECS_UNTIL_REQ_CHANGE_TIME_EXAMPLE.get(
1372              EXAMPLE_TARGET_USER_DN));
1373
1374
1375    // Define the subcommand to get the password history count.
1376    createSubCommand(ManageAccountSubCommandType.GET_PASSWORD_HISTORY_COUNT,
1377         INFO_MANAGE_ACCT_SC_GET_PW_HISTORY_COUNT_EXAMPLE.get(
1378              EXAMPLE_TARGET_USER_DN));
1379
1380
1381    // Define the subcommand to clear a user's password history.
1382    createSubCommand(ManageAccountSubCommandType.CLEAR_PASSWORD_HISTORY,
1383         INFO_MANAGE_ACCT_SC_CLEAR_PW_HISTORY_EXAMPLE.get(
1384              EXAMPLE_TARGET_USER_DN));
1385
1386
1387    // Define the subcommand to determine whether a user has a retired password.
1388    createSubCommand(ManageAccountSubCommandType.GET_HAS_RETIRED_PASSWORD,
1389         INFO_MANAGE_ACCT_SC_GET_HAS_RETIRED_PW_EXAMPLE.get(
1390              EXAMPLE_TARGET_USER_DN));
1391
1392
1393    // Define the subcommand to retrieve the time that a user's former password
1394    // was retired.
1395    createSubCommand(ManageAccountSubCommandType.GET_PASSWORD_RETIRED_TIME,
1396         INFO_MANAGE_ACCT_SC_GET_PW_RETIRED_TIME_EXAMPLE.get(
1397              EXAMPLE_TARGET_USER_DN));
1398
1399
1400    // Define the subcommand to retrieve the retired password expiration time.
1401    createSubCommand(
1402         ManageAccountSubCommandType.GET_RETIRED_PASSWORD_EXPIRATION_TIME,
1403         INFO_MANAGE_ACCT_SC_GET_RETIRED_PW_EXP_TIME_EXAMPLE.get(
1404              EXAMPLE_TARGET_USER_DN));
1405
1406
1407    // Define the subcommand to purge a retired password.
1408    createSubCommand(ManageAccountSubCommandType.CLEAR_RETIRED_PASSWORD,
1409         INFO_MANAGE_ACCT_SC_PURGE_RETIRED_PW_EXAMPLE.get(
1410              EXAMPLE_TARGET_USER_DN));
1411
1412
1413    // Define the subcommand to get the available SASL mechanisms for a user.
1414    createSubCommand(ManageAccountSubCommandType.GET_AVAILABLE_SASL_MECHANISMS,
1415         INFO_MANAGE_ACCT_SC_GET_AVAILABLE_SASL_MECHS_EXAMPLE.get(
1416              EXAMPLE_TARGET_USER_DN));
1417
1418
1419    // Define the subcommand to get the available OTP delivery mechanisms for a
1420    // user.
1421    createSubCommand(
1422         ManageAccountSubCommandType.GET_AVAILABLE_OTP_DELIVERY_MECHANISMS,
1423         INFO_MANAGE_ACCT_SC_GET_AVAILABLE_OTP_MECHS_EXAMPLE.get(
1424              EXAMPLE_TARGET_USER_DN));
1425
1426
1427    // Define the subcommand to determine whether a user has at least one TOTP
1428    // shared secret.
1429    createSubCommand(ManageAccountSubCommandType.GET_HAS_TOTP_SHARED_SECRET,
1430         INFO_MANAGE_ACCT_SC_GET_HAS_TOTP_SHARED_SECRET_EXAMPLE.get(
1431              EXAMPLE_TARGET_USER_DN));
1432
1433
1434    // Define the subcommand to add a value to the set of TOTP shared secrets
1435    // for a user.
1436    final ArgumentParser addTOTPSharedSecretParser =
1437         createSubCommandParser(
1438              ManageAccountSubCommandType.ADD_TOTP_SHARED_SECRET);
1439
1440    final StringArgument addTOTPSharedSecretValueArg =
1441         new StringArgument('O', "totpSharedSecret", true, 0, null,
1442              INFO_MANAGE_ACCT_SC_ADD_YUBIKEY_ID_ARG_VALUE.get());
1443    addTOTPSharedSecretValueArg.addLongIdentifier("operationValue", true);
1444    addTOTPSharedSecretValueArg.addLongIdentifier("totp-shared-secret", true);
1445    addTOTPSharedSecretValueArg.addLongIdentifier("operation-value", true);
1446    addTOTPSharedSecretValueArg.setSensitive(true);
1447    addTOTPSharedSecretParser.addArgument(
1448         addTOTPSharedSecretValueArg);
1449
1450    createSubCommand(ManageAccountSubCommandType.ADD_TOTP_SHARED_SECRET,
1451         addTOTPSharedSecretParser,
1452         createSubCommandExample(
1453              ManageAccountSubCommandType.ADD_TOTP_SHARED_SECRET,
1454              INFO_MANAGE_ACCT_SC_ADD_TOTP_SHARED_SECRET_EXAMPLE.get(
1455                   "abcdefghijklmnop", EXAMPLE_TARGET_USER_DN),
1456              "--totpSharedSecret", "abcdefghijklmnop"));
1457
1458
1459    // Define the subcommand to remove a value from the set of TOTP shared
1460    // secrets for a user.
1461    final ArgumentParser removeTOTPSharedSecretParser =
1462         createSubCommandParser(
1463              ManageAccountSubCommandType.REMOVE_TOTP_SHARED_SECRET);
1464
1465    final StringArgument removeTOTPSharedSecretValueArg =
1466         new StringArgument('O', "totpSharedSecret", true, 0, null,
1467              INFO_MANAGE_ACCT_SC_REMOVE_YUBIKEY_ID_ARG_VALUE.get());
1468    removeTOTPSharedSecretValueArg.addLongIdentifier("operationValue", true);
1469    removeTOTPSharedSecretValueArg.addLongIdentifier("totp-shared-secret",
1470         true);
1471    removeTOTPSharedSecretValueArg.addLongIdentifier("operation-value", true);
1472    removeTOTPSharedSecretValueArg.setSensitive(true);
1473    removeTOTPSharedSecretParser.addArgument(
1474         removeTOTPSharedSecretValueArg);
1475
1476    createSubCommand(ManageAccountSubCommandType.REMOVE_TOTP_SHARED_SECRET,
1477         removeTOTPSharedSecretParser,
1478         createSubCommandExample(
1479              ManageAccountSubCommandType.REMOVE_TOTP_SHARED_SECRET,
1480              INFO_MANAGE_ACCT_SC_REMOVE_TOTP_SHARED_SECRET_EXAMPLE.get(
1481                   "abcdefghijklmnop", EXAMPLE_TARGET_USER_DN),
1482              "--totpSharedSecret", "abcdefghijklmnop"));
1483
1484
1485    // Define the subcommand to replace set of TOTP shared secrets for a user.
1486    final ArgumentParser setTOTPSharedSecretsParser =
1487         createSubCommandParser(
1488              ManageAccountSubCommandType.SET_TOTP_SHARED_SECRETS);
1489
1490    final StringArgument setTOTPSharedSecretsValueArg =
1491         new StringArgument('O', "totpSharedSecret", true, 0, null,
1492              INFO_MANAGE_ACCT_SC_SET_TOTP_SHARED_SECRETS_ARG_VALUE.get());
1493    setTOTPSharedSecretsValueArg.addLongIdentifier("operationValue", true);
1494    setTOTPSharedSecretsValueArg.addLongIdentifier("totp-shared-secret", true);
1495    setTOTPSharedSecretsValueArg.addLongIdentifier("operation-value", true);
1496    setTOTPSharedSecretsValueArg.setSensitive(true);
1497    setTOTPSharedSecretsParser.addArgument(
1498         setTOTPSharedSecretsValueArg);
1499
1500    createSubCommand(ManageAccountSubCommandType.SET_TOTP_SHARED_SECRETS,
1501         setTOTPSharedSecretsParser,
1502         createSubCommandExample(
1503              ManageAccountSubCommandType.SET_TOTP_SHARED_SECRETS,
1504              INFO_MANAGE_ACCT_SC_SET_TOTP_SHARED_SECRETS_EXAMPLE.get(
1505                   EXAMPLE_TARGET_USER_DN, "abcdefghijklmnop"),
1506              "--totpSharedSecret", "abcdefghijklmnop"));
1507
1508
1509    // Define the subcommand to clear the set of TOTP shared secrets for a user.
1510    createSubCommand(
1511         ManageAccountSubCommandType.CLEAR_TOTP_SHARED_SECRETS,
1512         INFO_MANAGE_ACCT_SC_CLEAR_TOTP_SHARED_SECRETS_EXAMPLE.get(
1513              EXAMPLE_TARGET_USER_DN));
1514
1515
1516    // Define the subcommand to determine whether a user has at least one
1517    // registered YubiKey OTP device public ID.
1518    createSubCommand(
1519         ManageAccountSubCommandType.GET_HAS_REGISTERED_YUBIKEY_PUBLIC_ID,
1520         INFO_MANAGE_ACCT_SC_GET_HAS_YUBIKEY_ID_EXAMPLE.get(
1521              EXAMPLE_TARGET_USER_DN));
1522
1523
1524    // Define the subcommand to get the set of registered YubiKey OTP device
1525    // public IDs for a user.
1526    createSubCommand(
1527         ManageAccountSubCommandType.GET_REGISTERED_YUBIKEY_PUBLIC_IDS,
1528         INFO_MANAGE_ACCT_SC_GET_YUBIKEY_IDS_EXAMPLE.get(
1529              EXAMPLE_TARGET_USER_DN));
1530
1531
1532    // Define the subcommand to add a value to the set of registered YubiKey OTP
1533    // device public IDs for a user.
1534    final ArgumentParser addRegisteredYubiKeyPublicIDParser =
1535         createSubCommandParser(
1536              ManageAccountSubCommandType.ADD_REGISTERED_YUBIKEY_PUBLIC_ID);
1537
1538    final StringArgument addRegisteredYubiKeyPublicIDValueArg =
1539         new StringArgument('O', "publicID", true, 0, null,
1540              INFO_MANAGE_ACCT_SC_ADD_YUBIKEY_ID_ARG_VALUE.get());
1541    addRegisteredYubiKeyPublicIDValueArg.addLongIdentifier("operationValue",
1542         true);
1543    addRegisteredYubiKeyPublicIDValueArg.addLongIdentifier("public-id", true);
1544    addRegisteredYubiKeyPublicIDValueArg.addLongIdentifier("operation-value",
1545         true);
1546    addRegisteredYubiKeyPublicIDParser.addArgument(
1547         addRegisteredYubiKeyPublicIDValueArg);
1548
1549    createSubCommand(
1550         ManageAccountSubCommandType.ADD_REGISTERED_YUBIKEY_PUBLIC_ID,
1551         addRegisteredYubiKeyPublicIDParser,
1552         createSubCommandExample(
1553              ManageAccountSubCommandType.ADD_REGISTERED_YUBIKEY_PUBLIC_ID,
1554              INFO_MANAGE_ACCT_SC_ADD_YUBIKEY_ID_EXAMPLE.get(
1555                   "abcdefghijkl", EXAMPLE_TARGET_USER_DN),
1556              "--publicID", "abcdefghijkl"));
1557
1558
1559    // Define the subcommand to remove a value from the set of registered
1560    // YubiKey OTP device public IDs for a user.
1561    final ArgumentParser removeRegisteredYubiKeyPublicIDParser =
1562         createSubCommandParser(
1563              ManageAccountSubCommandType.REMOVE_REGISTERED_YUBIKEY_PUBLIC_ID);
1564
1565    final StringArgument removeRegisteredYubiKeyPublicIDValueArg =
1566         new StringArgument('O', "publicID", true, 0, null,
1567              INFO_MANAGE_ACCT_SC_REMOVE_YUBIKEY_ID_ARG_VALUE.get());
1568    removeRegisteredYubiKeyPublicIDValueArg.addLongIdentifier("operationValue",
1569         true);
1570    removeRegisteredYubiKeyPublicIDValueArg.addLongIdentifier("public-id",
1571         true);
1572    removeRegisteredYubiKeyPublicIDValueArg.addLongIdentifier("operation-value",
1573         true);
1574    removeRegisteredYubiKeyPublicIDParser.addArgument(
1575         removeRegisteredYubiKeyPublicIDValueArg);
1576
1577    createSubCommand(
1578         ManageAccountSubCommandType.REMOVE_REGISTERED_YUBIKEY_PUBLIC_ID,
1579         removeRegisteredYubiKeyPublicIDParser,
1580         createSubCommandExample(
1581              ManageAccountSubCommandType.REMOVE_REGISTERED_YUBIKEY_PUBLIC_ID,
1582              INFO_MANAGE_ACCT_SC_REMOVE_YUBIKEY_ID_EXAMPLE.get(
1583                   "abcdefghijkl", EXAMPLE_TARGET_USER_DN),
1584              "--publicID", "abcdefghijkl"));
1585
1586
1587    // Define the subcommand to replace set of registered YubiKey OTP device
1588    // public IDs for a user.
1589    final ArgumentParser setRegisteredYubiKeyPublicIDParser =
1590         createSubCommandParser(
1591              ManageAccountSubCommandType.SET_REGISTERED_YUBIKEY_PUBLIC_IDS);
1592
1593    final StringArgument setRegisteredYubiKeyPublicIDValueArg =
1594         new StringArgument('O', "publicID", true, 0, null,
1595              INFO_MANAGE_ACCT_SC_SET_YUBIKEY_IDS_ARG_VALUE.get());
1596    setRegisteredYubiKeyPublicIDValueArg.addLongIdentifier("operationValue",
1597         true);
1598    setRegisteredYubiKeyPublicIDValueArg.addLongIdentifier("public-id", true);
1599    setRegisteredYubiKeyPublicIDValueArg.addLongIdentifier("operation-value",
1600         true);
1601    setRegisteredYubiKeyPublicIDParser.addArgument(
1602         setRegisteredYubiKeyPublicIDValueArg);
1603
1604    createSubCommand(
1605         ManageAccountSubCommandType.SET_REGISTERED_YUBIKEY_PUBLIC_IDS,
1606         setRegisteredYubiKeyPublicIDParser,
1607         createSubCommandExample(
1608              ManageAccountSubCommandType.SET_REGISTERED_YUBIKEY_PUBLIC_IDS,
1609              INFO_MANAGE_ACCT_SC_SET_YUBIKEY_IDS_EXAMPLE.get(
1610                   EXAMPLE_TARGET_USER_DN, "abcdefghijkl"),
1611              "--publicID", "abcdefghijkl"));
1612
1613
1614    // Define the subcommand to clear the set of registered YubiKey OTP device
1615    // public IDs for a user.
1616    createSubCommand(
1617         ManageAccountSubCommandType.CLEAR_REGISTERED_YUBIKEY_PUBLIC_IDS,
1618         INFO_MANAGE_ACCT_SC_CLEAR_YUBIKEY_IDS_EXAMPLE.get(
1619              EXAMPLE_TARGET_USER_DN));
1620
1621
1622    // Define the subcommand to determine whether a user has at least one static
1623    // password.
1624    createSubCommand(ManageAccountSubCommandType.GET_HAS_STATIC_PASSWORD,
1625         INFO_MANAGE_ACCT_SC_GET_HAS_STATIC_PW_EXAMPLE.get(
1626              EXAMPLE_TARGET_USER_DN));
1627  }
1628
1629
1630
1631  /**
1632   * Creates an argument parser for the provided subcommand type.  It will not
1633   * have any arguments associated with it.
1634   *
1635   * @param  type  The subcommand type for which to create the argument parser.
1636   *
1637   * @return  The created argument parser.
1638   *
1639   * @throws  ArgumentException  If a problem is encountered while creating the
1640   *                             argument parser.
1641   */
1642  private static ArgumentParser createSubCommandParser(
1643                                     final ManageAccountSubCommandType type)
1644          throws ArgumentException
1645  {
1646    return new ArgumentParser(type.getPrimaryName(), type.getDescription());
1647  }
1648
1649
1650
1651  /**
1652   * Generates an example usage map for a specified subcommand.
1653   *
1654   * @param  t            The subcommand type.
1655   * @param  description  The description to use for the example.
1656   * @param  args         The set of arguments to include in the example,
1657   *                      excluding the subcommand name and the arguments used
1658   *                      to connect and authenticate to the server.  This may
1659   *                      be empty if no additional arguments are needed.
1660   *
1661   * @return The generated example usage map.
1662   */
1663  private static LinkedHashMap<String[],String> createSubCommandExample(
1664                      final ManageAccountSubCommandType t,
1665                      final String description, final String... args)
1666  {
1667    final LinkedHashMap<String[], String> examples =
1668         new LinkedHashMap<>(StaticUtils.computeMapCapacity(1));
1669    createSubCommandExample(examples, t, description, args);
1670    return examples;
1671  }
1672
1673
1674
1675  /**
1676   * Adds an example for a specified subcommand to the given map.
1677   *
1678   * @param  examples     The map to which the example should be added.
1679   * @param  t            The subcommand type.
1680   * @param  description  The description to use for the example.
1681   * @param  args         The set of arguments to include in the example,
1682   *                      excluding the subcommand name and the arguments used
1683   *                      to connect and authenticate to the server.  This may
1684   *                      be empty if no additional arguments are needed.
1685   */
1686  private static void createSubCommandExample(
1687       final LinkedHashMap<String[], String> examples,
1688       final ManageAccountSubCommandType t, final String description,
1689       final String... args)
1690  {
1691    final ArrayList<String> argList = new ArrayList<>(10 + args.length);
1692    argList.add(t.getPrimaryName());
1693    argList.add("--hostname");
1694    argList.add("server.example.com");
1695    argList.add("--port");
1696    argList.add("389");
1697    argList.add("--bindDN");
1698    argList.add("uid=admin,dc=example,dc=com");
1699    argList.add("--promptForBindPassword");
1700    argList.add("--targetDN");
1701    argList.add("uid=jdoe,ou=People,dc=example,dc=com");
1702
1703    if (args.length > 0)
1704    {
1705      argList.addAll(Arrays.asList(args));
1706    }
1707
1708    final String[] argArray = new String[argList.size()];
1709    argList.toArray(argArray);
1710
1711    examples.put(argArray, description);
1712  }
1713
1714
1715
1716  /**
1717   * Creates a subcommand with the provided information.
1718   *
1719   * @param  subcommandType       The subcommand type.
1720   * @param  exampleDescription   The description to use for the
1721   *                              automatically-generated example.
1722   *
1723   * @throws  ArgumentException  If a problem is encountered while creating the
1724   *                             subcommand.
1725   */
1726  private void createSubCommand(
1727                    final ManageAccountSubCommandType subcommandType,
1728                    final String exampleDescription)
1729          throws ArgumentException
1730  {
1731    final ArgumentParser subcommandParser =
1732         createSubCommandParser(subcommandType);
1733
1734    final LinkedHashMap<String[],String> examples =
1735         createSubCommandExample(subcommandType, exampleDescription);
1736
1737    createSubCommand(subcommandType, subcommandParser, examples);
1738  }
1739
1740
1741
1742  /**
1743   * Creates a subcommand with the provided information.
1744   *
1745   * @param  subcommandType    The subcommand type.
1746   * @param  subcommandParser  The argument parser for the subcommand-specific
1747   *                           arguments.
1748   * @param  examples          The example usages for the subcommand.
1749   *
1750   * @throws  ArgumentException  If a problem is encountered while creating the
1751   *                             subcommand.
1752   */
1753  private void createSubCommand(
1754                    final ManageAccountSubCommandType subcommandType,
1755                    final ArgumentParser subcommandParser,
1756                    final LinkedHashMap<String[],String> examples)
1757          throws ArgumentException
1758  {
1759    final SubCommand subCommand = new SubCommand(
1760         subcommandType.getPrimaryName(), subcommandType.getDescription(),
1761         subcommandParser, examples);
1762
1763    for (final String alternateName : subcommandType.getAlternateNames())
1764    {
1765      subCommand.addName(alternateName, true);
1766    }
1767
1768    parser.addSubCommand(subCommand);
1769  }
1770
1771
1772
1773  /**
1774   * {@inheritDoc}
1775   */
1776  @Override()
1777  public LDAPConnectionOptions getConnectionOptions()
1778  {
1779    return connectionOptions;
1780  }
1781
1782
1783
1784  /**
1785   * {@inheritDoc}
1786   */
1787  @Override()
1788  public ResultCode doToolProcessing()
1789  {
1790    // If we should just generate a sample rate data file, then do that now.
1791    final FileArgument generateSampleRateFile =
1792         parser.getFileArgument(ARG_GENERATE_SAMPLE_RATE_FILE);
1793    if (generateSampleRateFile.isPresent())
1794    {
1795      try
1796      {
1797        RateAdjustor.writeSampleVariableRateFile(
1798             generateSampleRateFile.getValue());
1799        return ResultCode.SUCCESS;
1800      }
1801      catch (final Exception e)
1802      {
1803        Debug.debugException(e);
1804        wrapErr(0, WRAP_COLUMN,
1805             ERR_MANAGE_ACCT_CANNOT_GENERATE_SAMPLE_RATE_FILE.get(
1806                  generateSampleRateFile.getValue().getAbsolutePath(),
1807                  StaticUtils.getExceptionMessage(e)));
1808        return ResultCode.LOCAL_ERROR;
1809      }
1810    }
1811
1812
1813    // If we need to create a fixed-rate barrier and/or use a variable rate
1814    // definition, then set that up.
1815    final IntegerArgument ratePerSecond =
1816         parser.getIntegerArgument(ARG_RATE_PER_SECOND);
1817    final FileArgument variableRateData =
1818         parser.getFileArgument(ARG_VARIABLE_RATE_DATA);
1819    if (ratePerSecond.isPresent() || variableRateData.isPresent())
1820    {
1821      if (ratePerSecond.isPresent())
1822      {
1823        rateLimiter = new FixedRateBarrier(1000L, ratePerSecond.getValue());
1824      }
1825      else
1826      {
1827        rateLimiter = new FixedRateBarrier(1000L, Integer.MAX_VALUE);
1828      }
1829
1830      if (variableRateData.isPresent())
1831      {
1832        try
1833        {
1834          rateAdjustor = RateAdjustor.newInstance(rateLimiter,
1835               ratePerSecond.getValue(), variableRateData.getValue());
1836        }
1837        catch (final Exception e)
1838        {
1839          Debug.debugException(e);
1840          wrapErr(0, WRAP_COLUMN,
1841               ERR_MANAGE_ACCT_CANNOT_CREATE_RATE_ADJUSTOR.get(
1842                    variableRateData.getValue().getAbsolutePath(),
1843                    StaticUtils.getExceptionMessage(e)));
1844          return ResultCode.PARAM_ERROR;
1845        }
1846      }
1847    }
1848
1849
1850    // Create the connection pool to use for all processing.
1851    final LDAPConnectionPool pool;
1852    final int numSearchThreads =
1853         parser.getIntegerArgument(ARG_NUM_SEARCH_THREADS).getValue();
1854    try
1855    {
1856      final int numOperationThreads =
1857           parser.getIntegerArgument(ARG_NUM_THREADS).getValue();
1858      pool = getConnectionPool(numOperationThreads,
1859           (numOperationThreads + numSearchThreads));
1860
1861      // Explicitly disable automatic retry, since it probably won't work
1862      // reliably for extended operations anyway.  We'll handle retry manually.
1863      pool.setRetryFailedOperationsDueToInvalidConnections(false);
1864
1865      // Set a maximum connection age of 30 minutes.
1866      pool.setMaxConnectionAgeMillis(1_800_000L);
1867    }
1868    catch (final LDAPException le)
1869    {
1870      Debug.debugException(le);
1871
1872      wrapErr(0, WRAP_COLUMN,
1873           ERR_MANAGE_ACCT_CANNOT_CREATE_CONNECTION_POOL.get(getToolName(),
1874                le.getMessage()));
1875      return le.getResultCode();
1876    }
1877
1878
1879    try
1880    {
1881      // Create the output writer.  This should always succeed.
1882      outputWriter = new LDIFWriter(getOut());
1883
1884
1885
1886      // Create the reject writer if appropriate.
1887      final FileArgument rejectFile = parser.getFileArgument(ARG_REJECT_FILE);
1888      if (rejectFile.isPresent())
1889      {
1890        final BooleanArgument appendToRejectFile =
1891             parser.getBooleanArgument(ARG_APPEND_TO_REJECT_FILE);
1892
1893        try
1894        {
1895          rejectWriter = new LDIFWriter(new FileOutputStream(
1896               rejectFile.getValue(), appendToRejectFile.isPresent()));
1897        }
1898        catch (final Exception e)
1899        {
1900          Debug.debugException(e);
1901          wrapErr(0, WRAP_COLUMN,
1902               ERR_MANAGE_ACCT_CANNOT_CREATE_REJECT_WRITER.get(
1903                    rejectFile.getValue().getAbsolutePath(),
1904                    StaticUtils.getExceptionMessage(e)));
1905          return ResultCode.LOCAL_ERROR;
1906        }
1907      }
1908
1909
1910      // Create the processor that will be used to actually perform the
1911      // manage-account operation processing for each entry.
1912      final ManageAccountProcessor processor;
1913      try
1914      {
1915        processor = new ManageAccountProcessor(this, pool, rateLimiter,
1916             outputWriter, rejectWriter);
1917      }
1918      catch (final LDAPException le)
1919      {
1920        Debug.debugException(le);
1921        wrapErr(0, WRAP_COLUMN,
1922             ERR_MANAGE_ACCT_CANNOT_CREATE_PROCESSOR.get(
1923                  StaticUtils.getExceptionMessage(le)));
1924        return le.getResultCode();
1925      }
1926
1927
1928      // If we should use a rate adjustor, then start it now.
1929      if (rateAdjustor != null)
1930      {
1931        rateAdjustor.start();
1932      }
1933
1934
1935      // If any targetDN values were provided, then process them now.
1936      final DNArgument targetDN = parser.getDNArgument(ARG_TARGET_DN);
1937      if (targetDN.isPresent())
1938      {
1939        for (final DN dn : targetDN.getValues())
1940        {
1941          if (cancelRequested())
1942          {
1943            return ResultCode.USER_CANCELED;
1944          }
1945
1946          processor.process(dn.toString());
1947        }
1948      }
1949
1950
1951      // If any DN input files were specified, then process them now.
1952      final FileArgument dnInputFile =
1953           parser.getFileArgument(ARG_DN_INPUT_FILE);
1954      if (dnInputFile.isPresent())
1955      {
1956        for (final File f : dnInputFile.getValues())
1957        {
1958          DNFileReader reader = null;
1959          try
1960          {
1961            reader = new DNFileReader(f);
1962            while (true)
1963            {
1964              if (cancelRequested())
1965              {
1966                return ResultCode.USER_CANCELED;
1967              }
1968
1969              final DN dn;
1970              try
1971              {
1972                dn = reader.readDN();
1973              }
1974              catch (final LDAPException le)
1975              {
1976                Debug.debugException(le);
1977                processor.handleMessage(le.getMessage(), true);
1978                continue;
1979              }
1980
1981              if (dn == null)
1982              {
1983                break;
1984              }
1985
1986              processor.process(dn.toString());
1987            }
1988          }
1989          catch (final Exception e)
1990          {
1991            Debug.debugException(e);
1992            processor.handleMessage(
1993                 ERR_MANAGE_ACCT_ERROR_READING_DN_FILE.get(
1994                      f.getAbsolutePath(), StaticUtils.getExceptionMessage(e)),
1995                 true);
1996          }
1997          finally
1998          {
1999            if (reader != null)
2000            {
2001              try
2002              {
2003                reader.close();
2004              }
2005              catch (final Exception e2)
2006              {
2007                Debug.debugException(e2);
2008              }
2009            }
2010          }
2011        }
2012      }
2013
2014
2015      // If any target filters were specified, then process them now.
2016      final FilterArgument targetFilter =
2017           parser.getFilterArgument(ARG_TARGET_FILTER);
2018      if (targetFilter.isPresent())
2019      {
2020        searchProcessor =
2021             new ManageAccountSearchProcessor(this, processor, pool);
2022        for (final Filter f : targetFilter.getValues())
2023        {
2024          searchProcessor.processFilter(f);
2025        }
2026      }
2027
2028
2029      // If any filter input files were specified, then process them now.
2030      final FileArgument filterInputFile =
2031           parser.getFileArgument(ARG_FILTER_INPUT_FILE);
2032      if (filterInputFile.isPresent())
2033      {
2034        if (searchProcessor == null)
2035        {
2036          searchProcessor =
2037               new ManageAccountSearchProcessor(this, processor, pool);
2038        }
2039
2040        for (final File f : filterInputFile.getValues())
2041        {
2042          FilterFileReader reader = null;
2043          try
2044          {
2045            reader = new FilterFileReader(f);
2046            while (true)
2047            {
2048              if (cancelRequested())
2049              {
2050                return ResultCode.USER_CANCELED;
2051              }
2052
2053              final Filter filter;
2054              try
2055              {
2056                filter = reader.readFilter();
2057              }
2058              catch (final LDAPException le)
2059              {
2060                Debug.debugException(le);
2061                processor.handleMessage(le.getMessage(), true);
2062                continue;
2063              }
2064
2065              if (filter == null)
2066              {
2067                break;
2068              }
2069
2070              searchProcessor.processFilter(filter);
2071            }
2072          }
2073          catch (final Exception e)
2074          {
2075            Debug.debugException(e);
2076            processor.handleMessage(
2077                 ERR_MANAGE_ACCT_ERROR_READING_FILTER_FILE.get(
2078                      f.getAbsolutePath(), StaticUtils.getExceptionMessage(e)),
2079                 true);
2080          }
2081          finally
2082          {
2083            if (reader != null)
2084            {
2085              try
2086              {
2087                reader.close();
2088              }
2089              catch (final Exception e2)
2090              {
2091                Debug.debugException(e2);
2092              }
2093            }
2094          }
2095        }
2096      }
2097
2098
2099      // If any target user IDs were specified, then process them now.
2100      final StringArgument targetUserID =
2101           parser.getStringArgument(ARG_TARGET_USER_ID);
2102      if (targetUserID.isPresent())
2103      {
2104        if (searchProcessor == null)
2105        {
2106          searchProcessor =
2107               new ManageAccountSearchProcessor(this, processor, pool);
2108        }
2109
2110        for (final String userID : targetUserID.getValues())
2111        {
2112          searchProcessor.processUserID(userID);
2113        }
2114      }
2115
2116
2117      // If any user ID input files were specified, then process them now.
2118      final FileArgument userIDInputFile =
2119           parser.getFileArgument(ARG_USER_ID_INPUT_FILE);
2120      if (userIDInputFile.isPresent())
2121      {
2122        if (searchProcessor == null)
2123        {
2124          searchProcessor =
2125               new ManageAccountSearchProcessor(this, processor, pool);
2126        }
2127
2128        for (final File f : userIDInputFile.getValues())
2129        {
2130          BufferedReader reader = null;
2131          try
2132          {
2133            reader = new BufferedReader(new FileReader(f));
2134            while (true)
2135            {
2136              if (cancelRequested())
2137              {
2138                return ResultCode.USER_CANCELED;
2139              }
2140
2141              final String line = reader.readLine();
2142              if (line == null)
2143              {
2144                break;
2145              }
2146
2147              if ((line.length() == 0) || line.startsWith("#"))
2148              {
2149                continue;
2150              }
2151
2152              searchProcessor.processUserID(line.trim());
2153            }
2154          }
2155          catch (final Exception e)
2156          {
2157            Debug.debugException(e);
2158            processor.handleMessage(
2159                 ERR_MANAGE_ACCT_ERROR_READING_USER_ID_FILE.get(
2160                      f.getAbsolutePath(), StaticUtils.getExceptionMessage(e)),
2161                 true);
2162          }
2163          finally
2164          {
2165            if (reader != null)
2166            {
2167              try
2168              {
2169                reader.close();
2170              }
2171              catch (final Exception e2)
2172              {
2173                Debug.debugException(e2);
2174              }
2175            }
2176          }
2177        }
2178      }
2179
2180
2181      allFiltersProvided.set(true);
2182      if (searchProcessor != null)
2183      {
2184        searchProcessor.waitForCompletion();
2185      }
2186
2187      allDNsProvided.set(true);
2188      processor.waitForCompletion();
2189    }
2190    finally
2191    {
2192      pool.close();
2193
2194      if (rejectWriter != null)
2195      {
2196        try
2197        {
2198          rejectWriter.close();
2199        }
2200        catch (final Exception e)
2201        {
2202          Debug.debugException(e);
2203        }
2204      }
2205    }
2206
2207
2208    // If we've gotten here, then we can consider the command successful, even
2209    // if some of the operations failed.
2210    return ResultCode.SUCCESS;
2211  }
2212
2213
2214
2215  /**
2216   * Retrieves the argument parser for this tool.
2217   *
2218   * @return  The argument parser for this tool.
2219   */
2220  ArgumentParser getArgumentParser()
2221  {
2222    return parser;
2223  }
2224
2225
2226
2227  /**
2228   * Indicates whether the tool should cancel its processing.
2229   *
2230   * @return  {@code true} if the tool should cancel its processing, or
2231   *          {@code false} if not.
2232   */
2233  boolean cancelRequested()
2234  {
2235    return cancelRequested.get();
2236  }
2237
2238
2239
2240  /**
2241   * Indicates whether the manage-account processor has been provided with all
2242   * of the DNs of all of the entries to process.
2243   *
2244   * @return  {@code true} if the manage-account processor has been provided
2245   *          with all of the DNs of all of the entries to process, or
2246   *          {@code false} if not.
2247   */
2248  boolean allDNsProvided()
2249  {
2250    return allDNsProvided.get();
2251  }
2252
2253
2254
2255  /**
2256   * Indicates whether the manage-account search processor has been provided
2257   * with all of the filters to use to identify entries to process.
2258   *
2259   * @return  {@code true} if the manage-account search processor has been
2260   *          provided with all of the filters to use to identify entries to
2261   *          process, or {@code false} if not.
2262   */
2263  boolean allFiltersProvided()
2264  {
2265    return allFiltersProvided.get();
2266  }
2267
2268
2269
2270  /**
2271   * {@inheritDoc}
2272   */
2273  @Override()
2274  protected boolean registerShutdownHook()
2275  {
2276    return true;
2277  }
2278
2279
2280
2281  /**
2282   * {@inheritDoc}
2283   */
2284  @Override()
2285  protected void doShutdownHookProcessing(final ResultCode resultCode)
2286  {
2287    cancelRequested.set(true);
2288
2289    if (rateLimiter != null)
2290    {
2291      rateLimiter.shutdownRequested();
2292    }
2293
2294    if (searchProcessor != null)
2295    {
2296      searchProcessor.cancelSearches();
2297    }
2298  }
2299
2300
2301
2302  /**
2303   * Performs any processing that may be necessary in response to the provided
2304   * unsolicited notification that has been received from the server.
2305   *
2306   * @param connection   The connection on which the unsolicited notification
2307   *                     was received.
2308   * @param notification The unsolicited notification that has been received
2309   *                     from the server.
2310   */
2311  @Override()
2312  public void handleUnsolicitedNotification(final LDAPConnection connection,
2313                                            final ExtendedResult notification)
2314  {
2315    final String message = NOTE_MANAGE_ACCT_UNSOLICITED_NOTIFICATION.get(
2316         String.valueOf(connection), String.valueOf(notification));
2317    if (outputWriter == null)
2318    {
2319      err();
2320      err("* " + message);
2321      err();
2322    }
2323    else
2324    {
2325      try
2326      {
2327        outputWriter.writeComment(message, true, true);
2328        outputWriter.flush();
2329      }
2330      catch (final Exception e)
2331      {
2332        // We can't really do anything about this.
2333        Debug.debugException(e);
2334      }
2335    }
2336  }
2337
2338
2339
2340  /**
2341   * {@inheritDoc}
2342   */
2343  @Override()
2344  public LinkedHashMap<String[],String> getExampleUsages()
2345  {
2346    final LinkedHashMap<String[],String> examples =
2347         new LinkedHashMap<>(StaticUtils.computeMapCapacity(4));
2348
2349    createSubCommandExample(examples,
2350         ManageAccountSubCommandType.GET_ALL,
2351         INFO_MANAGE_ACCT_SC_GET_ALL_EXAMPLE.get(EXAMPLE_TARGET_USER_DN));
2352
2353    createSubCommandExample(examples,
2354         ManageAccountSubCommandType.GET_ACCOUNT_USABILITY_ERRORS,
2355         INFO_MANAGE_ACCT_SC_GET_USABILITY_ERRORS_EXAMPLE.get(
2356              EXAMPLE_TARGET_USER_DN));
2357
2358    createSubCommandExample(examples,
2359         ManageAccountSubCommandType.SET_ACCOUNT_IS_DISABLED,
2360         INFO_MANAGE_ACCT_SC_SET_IS_DISABLED_EXAMPLE.get(
2361              EXAMPLE_TARGET_USER_DN),
2362         "--accountIsDisabled", "true");
2363
2364    createSubCommandExample(examples,
2365         ManageAccountSubCommandType.CLEAR_AUTHENTICATION_FAILURE_TIMES,
2366         INFO_MANAGE_ACCT_SC_CLEAR_AUTH_FAILURE_TIMES_EXAMPLE.get(
2367              EXAMPLE_TARGET_USER_DN));
2368
2369    return examples;
2370  }
2371}