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