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