001/*
002 * Copyright 2011-2020 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2011-2020 Ping Identity Corporation
007 *
008 * Licensed under the Apache License, Version 2.0 (the "License");
009 * you may not use this file except in compliance with the License.
010 * You may obtain a copy of the License at
011 *
012 *    http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing, software
015 * distributed under the License is distributed on an "AS IS" BASIS,
016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017 * See the License for the specific language governing permissions and
018 * limitations under the License.
019 */
020/*
021 * Copyright (C) 2011-2020 Ping Identity Corporation
022 *
023 * This program is free software; you can redistribute it and/or modify
024 * it under the terms of the GNU General Public License (GPLv2 only)
025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
026 * as published by the Free Software Foundation.
027 *
028 * This program is distributed in the hope that it will be useful,
029 * but WITHOUT ANY WARRANTY; without even the implied warranty of
030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
031 * GNU General Public License for more details.
032 *
033 * You should have received a copy of the GNU General Public License
034 * along with this program; if not, see <http://www.gnu.org/licenses>.
035 */
036package com.unboundid.ldap.listener;
037
038
039
040import java.io.File;
041import java.io.OutputStream;
042import java.io.Serializable;
043import java.net.Socket;
044import java.security.MessageDigest;
045import java.util.ArrayList;
046import java.util.EnumSet;
047import java.util.Iterator;
048import java.util.LinkedHashMap;
049import java.util.List;
050import java.util.Map;
051import java.util.Set;
052import java.util.TreeMap;
053import java.util.logging.FileHandler;
054import java.util.logging.Level;
055import java.util.logging.StreamHandler;
056import javax.net.ssl.KeyManager;
057import javax.net.ssl.TrustManager;
058
059import com.unboundid.ldap.sdk.DN;
060import com.unboundid.ldap.sdk.LDAPException;
061import com.unboundid.ldap.sdk.OperationType;
062import com.unboundid.ldap.sdk.ResultCode;
063import com.unboundid.ldap.sdk.Version;
064import com.unboundid.ldap.sdk.schema.Schema;
065import com.unboundid.ldap.sdk.schema.SchemaValidator;
066import com.unboundid.util.CommandLineTool;
067import com.unboundid.util.Debug;
068import com.unboundid.util.MinimalLogFormatter;
069import com.unboundid.util.NotMutable;
070import com.unboundid.util.NotNull;
071import com.unboundid.util.Nullable;
072import com.unboundid.util.ObjectPair;
073import com.unboundid.util.StaticUtils;
074import com.unboundid.util.ThreadSafety;
075import com.unboundid.util.ThreadSafetyLevel;
076import com.unboundid.util.args.ArgumentException;
077import com.unboundid.util.args.ArgumentParser;
078import com.unboundid.util.args.BooleanArgument;
079import com.unboundid.util.args.DNArgument;
080import com.unboundid.util.args.IntegerArgument;
081import com.unboundid.util.args.FileArgument;
082import com.unboundid.util.args.StringArgument;
083import com.unboundid.util.ssl.KeyStoreKeyManager;
084import com.unboundid.util.ssl.SSLUtil;
085import com.unboundid.util.ssl.TrustAllTrustManager;
086import com.unboundid.util.ssl.TrustStoreTrustManager;
087import com.unboundid.util.ssl.cert.CertException;
088
089import static com.unboundid.ldap.listener.ListenerMessages.*;
090
091
092
093/**
094 * This class provides a command-line tool that can be used to run an instance
095 * of the in-memory directory server.  Instances of the server may also be
096 * created and controlled programmatically using the
097 * {@link InMemoryDirectoryServer} class.
098 * <BR><BR>
099 * The following command-line arguments may be used with this class:
100 * <UL>
101 *   <LI>"-b {baseDN}" or "--baseDN {baseDN}" -- specifies a base DN to use for
102 *       the server.  At least one base DN must be specified, and multiple
103 *       base DNs may be provided as separate arguments.</LI>
104 *   <LI>"-p {port}" or "--port {port}" -- specifies the port on which the
105 *       server should listen for client connections.  If this is not provided,
106 *       then a free port will be automatically chosen for use by the
107 *       server.</LI>
108 *   <LI>"-l {path}" or "--ldifFile {path}" -- specifies the path to an LDIF
109 *       file to use to initially populate the server.  If this is not provided,
110 *       then the server will initially be empty.  The LDIF file will not be
111 *       updated as operations are processed in the server.</LI>
112 *   <LI>"-D {bindDN}" or "--additionalBindDN {bindDN}" -- specifies an
113 *       additional DN that can be used to authenticate to the server, even if
114 *       there is no account for that user.  If this is provided, then the
115 *       --additionalBindPassword argument must also be given.</LI>
116 *   <LI>"-w {password}" or "--additionalBindPassword {password}" -- specifies
117 *       the password that should be used when attempting to bind as the user
118 *       specified with the "-additionalBindDN" argument.  If this is provided,
119 *       then the --additionalBindDN argument must also be given.</LI>
120 *   <LI>"-c {count}" or "--maxChangeLogEntries {count}" -- Indicates whether an
121 *       LDAP changelog should be enabled, and if so how many changelog records
122 *       should be maintained.  If this argument is not provided, or if it is
123 *       provided with a value of zero, then no changelog will be
124 *       maintained.</LI>
125 *   <LI>"-A" or "--accessLogToStandardOut" -- indicates that access log
126 *       information should be written to standard output.  This cannot be
127 *       provided in conjunction with the "--accessLogFile" argument.  If
128 *       neither argument is provided, then no access logging will be
129 *       performed</LI>
130 *   <LI>"-a {path}" or "--accessLogFile {path}" -- specifies the path to a file
131 *       that should be used as a server access log.  This cannot be provided in
132 *       conjunction with the "--accessLogToStandardOut" argument.  If neither
133 *       argument is provided, then no access logging will be performed</LI>
134 *   <LI>"---jsonAccessLogToStandardOut" -- indicates that JSON-formatted access
135 *       log information should be written to standard output.  This cannot be
136 *       provided in conjunction with the "--jsonAccessLogFile" argument.  If
137 *       neither argument is provided, then no JSON-formatted access logging
138 *       will be performed</LI>
139 *   <LI>"--jsonAccessLogFile {path}" -- specifies the path to a file that
140 *       should be used as a server access log with JSON-formatted messages.
141 *       This cannot be provided in conjunction with the
142 *       "--jsonAccessLogToStandardOut" argument.  If neither argument is
143 *       provided, then no JSON-formatted access logging will be performed</LI>
144 *   <LI>"--ldapDebugLogToStandardOut" -- Indicates that LDAP debug log
145 *       information should be written to standard output.  This cannot be
146 *       provided in conjunction with the "--ldapDebugLogFile" argument.  If
147 *       neither argument is provided, then no debug logging will be
148 *       performed.</LI>
149 *   <LI>"-d {path}" or "--ldapDebugLogFile {path}" -- specifies the path to a
150 *       file that should be used as a server LDAP debug log.  This cannot be
151 *       provided in conjunction with the "--ldapDebugLogToStandardOut"
152 *       argument.  If neither argument is provided, then no debug logging will
153 *       be performed.</LI>
154 *   <LI>"-s" or "--useDefaultSchema" -- Indicates that the server should use
155 *       the default standard schema provided as part of the LDAP SDK.  If
156 *       neither this argument nor the "--useSchemaFile" argument is provided,
157 *       then the server will not enforce schema compliance.</LI>
158 *   <LI>"-S {path}" or "--useSchemaFile {path}" -- specifies the path to a file
159 *       or directory containing schema definitions to use for the server.  If
160 *       neither this argument nor the "--useDefaultSchema" argument is
161 *       provided, then the server will not enforce schema compliance.  If
162 *       the specified path represents a file, then it must be an LDIF file
163 *       containing a valid LDAP subschema subentry.  If the path is a
164 *       directory, then its files will be processed in lexicographic order by
165 *       name.</LI>
166 *   <LI>"--doNotValidateSchemaDefinitions" -- indicates that the server should
167 *       not perform any validation for the custom schema provided using the
168 *       --useSchemaFile argument.  If this argument is not used and a custom
169 *       schema is configured, then the server will validate that schema and
170 *       report errors or warnings for any issues that it identifies with that
171 *       schema.</LI>
172 *   <LI>"-I {attr}" or "--equalityIndex {attr}" -- specifies that an equality
173 *       index should be maintained for the specified attribute.  The equality
174 *       index may be used to speed up certain kinds of searches, although it
175 *       will cause the server to consume more memory.</LI>
176 *   <LI>"-Z" or "--useSSL" -- indicates that the server should encrypt all
177 *       communication using SSL.  If this is provided, then the
178 *       "--keyStorePath" and "--keyStorePassword" arguments must also be
179 *       provided, and the "--useStartTLS" argument must not be provided.</LI>
180 *   <LI>"-q" or "--useStartTLS" -- indicates that the server should support the
181 *       use of the StartTLS extended request.  If this is provided, then the
182 *       "--keyStorePath" and "--keyStorePassword" arguments must also be
183 *       provided, and the "--useSSL" argument must not be provided.</LI>
184 *   <LI>"-K {path}" or "--keyStorePath {path}" -- specifies the path to the JKS
185 *       key store file that should be used to obtain the server certificate to
186 *       use for SSL communication.  If this argument is provided, then the
187 *       "--keyStorePassword" argument must also be provided, along with exactly
188 *       one of the "--useSSL" or "--useStartTLS" arguments.</LI>
189 *   <LI>"-W {password}" or "--keyStorePassword {password}" -- specifies the
190 *       password that should be used to access the contents of the SSL key
191 *       store.  If this argument is provided, then the "--keyStorePath"
192 *       argument must also be provided, along with exactly one of the
193 *       "--useSSL" or "--useStartTLS" arguments.</LI>
194 *   <LI>"--keyStoreType {type}" -- specifies the type of keystore represented
195 *       by the file specified by the keystore path.  If this argument is
196 *       provided, then the "--keyStorePath" argument must also be provided,
197 *       along with exactly one of the "--useSSL" or "--useStartTLS" arguments.
198 *       If this argument is not provided, then a default key store type of
199 *       "JKS" will be assumed.</LI>
200 *   <LI>"--generateSelfSignedCertificate" -- indicates that the server should
201 *       generate a self-signed certificate to use for SSL or StartTLS
202 *       communication.  If this argument is provided, then exactly one of the
203 *       "--useSSL" or "--useStartTLS" arguments must also be specified.</LI>
204 *   <LI>"-P {path}" or "--trustStorePath {path}" -- specifies the path to the
205 *       JKS trust store file that should be used to determine whether to trust
206 *       any SSL certificates that may be presented by the client.  If this
207 *       argument is provided, then exactly one of the "--useSSL" or
208 *       "--useStartTLS" arguments must also be provided.  If this argument is
209 *       not provided but SSL or StartTLS is to be used, then all client
210 *       certificates will be automatically trusted.</LI>
211 *   <LI>"-T {password}" or "--trustStorePassword {password}" -- specifies the
212 *       password that should be used to access the contents of the SSL trust
213 *       store.  If this argument is provided, then the "--trustStorePath"
214 *       argument must also be provided, along with exactly one of the
215 *       "--useSSL" or "--useStartTLS" arguments.  If an SSL trust store path
216 *       was provided without a trust store password, then the server will
217 *       attempt to use the trust store without a password.</LI>
218 *   <LI>"--trustStoreType {type}" -- specifies the type of trust store
219 *       represented by the file specified by the trust store path.  If this
220 *       argument is provided, then the "--trustStorePath" argument must also
221 *       be provided, along with exactly one of the "--useSSL" or
222 *       "--useStartTLS" arguments.  If this argument is not provided, then a
223 *       default trust store type of "JKS" will be assumed.</LI>
224 *   <LI>"--maxConcurrentConnections {num}" -- specifies the maximum number of
225 *       concurrent connections that the server will allow.</LI>
226 *   <LI>"--sizeLimit {num}" -- specifies the maximum number of entries that
227 *       the server will return for a single search operation.</LI>
228 *   <LI>"--passwordAttribute {attr}" -- specifies an attribute that will hold
229 *       user passwords.</LI>
230 *   <LI>"--defaultPasswordEncoding {scheme}" -- specifies the name of the
231 *       default scheme that the server will use to encode clear-text
232 *       passwords.  Allowed values include MD5, SMD5, SHA, SSHA, SHA256,
233 *       SSHA256, SHA384, SSHA384, SHA512, SSHA512, CLEAR, BASE64, and HEX.</LI>
234 *   <LI>"--allowedOperationType {type}" -- specifies a type of operation that
235 *       the server will allow.  Allowed values include add, bind, compare,
236 *       delete, extended, modify, modify-dn, and search.</LI>
237 *   <LI>"--authenticationRequiredOperationType {type}" -- specifies a type of
238 *       operation that the server will only allow for authenticated clients.
239 *       Allowed values include add, compare, delete, extended, modify,
240 *       modify-dn, and search.</LI>
241 *   <LI>"--vendorName {name}" -- specifies the vendor name value to appear in
242 *       the server root DSE.</LI>
243 *   <LI>"--vendorVersion {version}" -- specifies the vendor version value to
244 *       appear in the server root DSE.</LI>
245 * </UL>
246 */
247@NotMutable()
248@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
249public final class InMemoryDirectoryServerTool
250       extends CommandLineTool
251       implements Serializable, LDAPListenerExceptionHandler
252{
253  /**
254   * The column at which long lines should be wrapped.
255   */
256  private static final int WRAP_COLUMN = StaticUtils.TERMINAL_WIDTH_COLUMNS - 1;
257
258
259
260  /**
261   * The serial version UID for this serializable class.
262   */
263  private static final long serialVersionUID = 6484637038039050412L;
264
265
266
267  // The argument used to indicate that access log information should be written
268  // to standard output.
269  @Nullable private BooleanArgument accessLogToStandardOutArgument;
270
271  // Indicates that the should not attempt to validate custom schema definitions
272  // provided by the useSchemaFile argument.
273  @Nullable private BooleanArgument doNotValidateSchemaDefinitions;
274
275  // The argument used to prevent the in-memory server from starting.  This is
276  // only intended to be used for internal testing purposes.
277  @Nullable private BooleanArgument dontStartArgument;
278
279  // The argument used to indicate that the server should generate a self-signed
280  // certificate for use in SSL or StartTLS negotiation.
281  @Nullable private BooleanArgument generateSelfSignedCertificateArgument;
282
283  // The argument used to indicate that JSON-formatted access log information
284  // should be written to standard output.
285  @Nullable private BooleanArgument jsonAccessLogToStandardOutArgument;
286
287  // The argument used to indicate that LDAP debug log information should be
288  // written to standard output.
289  @Nullable private BooleanArgument ldapDebugLogToStandardOutArgument;
290
291  // The argument used to indicate that the default standard schema should be
292  // used.
293  @Nullable private BooleanArgument useDefaultSchemaArgument;
294
295  // The argument used to indicate that the server should use SSL
296  @Nullable private BooleanArgument useSSLArgument;
297
298  // The argument used to indicate that the server should support the StartTLS
299  // extended operation
300  @Nullable private BooleanArgument useStartTLSArgument;
301
302  // The argument used to specify an additional bind DN to use for the server.
303  @Nullable private DNArgument additionalBindDNArgument;
304
305  // The argument used to specify the base DNs to use for the server.
306  @Nullable private DNArgument baseDNArgument;
307
308  // The argument used to specify the path to an access log file to which
309  // information should be written about operations processed by the server.
310  @Nullable private FileArgument accessLogFileArgument;
311
312  // The argument used to specify the code log file to use, if any.
313  @Nullable private FileArgument codeLogFile;
314
315  // The argument used to specify the path to an access log file to which
316  // JSON-formatted operation should be written about operations processed by
317  // the server.
318  @Nullable private FileArgument jsonAccessLogFileArgument;
319
320  // The argument used to specify the path to the SSL key store file.
321  @Nullable private FileArgument keyStorePathArgument;
322
323  // The argument used to specify the path to an LDAP debug log file to which
324  // information should be written about detailed LDAP communication performed
325  // by the server.
326  @Nullable private FileArgument ldapDebugLogFileArgument;
327
328  // The argument used to specify the path to an LDIF file with data to use to
329  // initially populate the server.
330  @Nullable private FileArgument ldifFileArgument;
331
332  // The argument used to specify the path to the SSL trust store file.
333  @Nullable private FileArgument trustStorePathArgument;
334
335  // The argument used to specify the path to a directory containing schema
336  // definitions.
337  @Nullable private FileArgument useSchemaFileArgument;
338
339  // The in-memory directory server instance that has been created by this tool.
340  @Nullable private InMemoryDirectoryServer directoryServer;
341
342  // The argument used to specify the maximum number of changelog entries that
343  // the server should maintain.
344  @Nullable private IntegerArgument maxChangeLogEntriesArgument;
345
346  // The argument used to specify the maximum number of concurrent connections.
347  @Nullable private IntegerArgument maxConcurrentConnectionsArgument;
348
349  // The argument used to specify the port on which the server should listen.
350  @Nullable private IntegerArgument portArgument;
351
352  // The argument used to specify the maximum search size limit.
353  @Nullable private IntegerArgument sizeLimitArgument;
354
355  // The argument used to specify the password for the additional bind DN.
356  @Nullable private StringArgument additionalBindPasswordArgument;
357
358  // The argument used to specify the types of allowed operations.
359  @Nullable private StringArgument allowedOperationTypeArgument;
360
361  // The argument used to specify the types of operations for which
362  // authentication is required.
363  @Nullable private StringArgument authenticationRequiredOperationTypeArgument;
364
365  // The argument used to specify the name of the default encoding scheme to use
366  // use for clear-text passwords.
367  @Nullable private StringArgument defaultPasswordEncodingArgument;
368
369  // The argument used to specify the attributes for which to maintain equality
370  // indexes.
371  @Nullable private StringArgument equalityIndexArgument;
372
373  // The argument used to specify the password to use to access the contents of
374  // the SSL key store
375  @Nullable private StringArgument keyStorePasswordArgument;
376
377  // The argument used to specify the key store type.
378  @Nullable private StringArgument keyStoreTypeArgument;
379
380  // The argument used to specify the password attribute types.
381  @Nullable private StringArgument passwordAttributeArgument;
382
383  // The argument used to specify the password to use to access the contents of
384  // the SSL trust store
385  @Nullable private StringArgument trustStorePasswordArgument;
386
387  // The argument used to specify the trust store type.
388  @Nullable private StringArgument trustStoreTypeArgument;
389
390  // The argument used to specify the server vendor name.
391  @Nullable private StringArgument vendorNameArgument;
392
393  // The argument used to specify the server vendor version.
394  @Nullable private StringArgument vendorVersionArgument;
395
396
397
398  /**
399   * Parse the provided command line arguments and uses them to start the
400   * directory server.
401   *
402   * @param  args  The command line arguments provided to this program.
403   */
404  public static void main(@NotNull final String... args)
405  {
406    final ResultCode resultCode = main(args, System.out, System.err);
407    if (resultCode != ResultCode.SUCCESS)
408    {
409      System.exit(resultCode.intValue());
410    }
411  }
412
413
414
415  /**
416   * Parse the provided command line arguments and uses them to start the
417   * directory server.
418   *
419   * @param  outStream  The output stream to which standard out should be
420   *                    written.  It may be {@code null} if output should be
421   *                    suppressed.
422   * @param  errStream  The output stream to which standard error should be
423   *                    written.  It may be {@code null} if error messages
424   *                    should be suppressed.
425   * @param  args       The command line arguments provided to this program.
426   *
427   * @return  A result code indicating whether the processing was successful.
428   */
429  @NotNull()
430  public static ResultCode main(@NotNull final String[] args,
431                                @Nullable final OutputStream outStream,
432                                @Nullable final OutputStream errStream)
433  {
434    final InMemoryDirectoryServerTool tool =
435         new InMemoryDirectoryServerTool(outStream, errStream);
436    return tool.runTool(args);
437  }
438
439
440
441  /**
442   * Creates a new instance of this tool that use the provided output streams
443   * for standard output and standard error.
444   *
445   * @param  outStream  The output stream to use for standard output.  It may be
446   *                    {@code System.out} for the JVM's default standard output
447   *                    stream, {@code null} if no output should be generated,
448   *                    or a custom output stream if the output should be sent
449   *                    to an alternate location.
450   * @param  errStream  The output stream to use for standard error.  It may be
451   *                    {@code System.err} for the JVM's default standard error
452   *                    stream, {@code null} if no output should be generated,
453   *                    or a custom output stream if the output should be sent
454   *                    to an alternate location.
455   */
456  public InMemoryDirectoryServerTool(@Nullable final OutputStream outStream,
457                                     @Nullable final OutputStream errStream)
458  {
459    super(outStream, errStream);
460
461    directoryServer = null;
462    doNotValidateSchemaDefinitions = null;
463    dontStartArgument = null;
464    generateSelfSignedCertificateArgument = null;
465    useDefaultSchemaArgument = null;
466    useSSLArgument = null;
467    useStartTLSArgument = null;
468    additionalBindDNArgument = null;
469    baseDNArgument = null;
470    accessLogToStandardOutArgument = null;
471    accessLogFileArgument = null;
472    jsonAccessLogToStandardOutArgument = null;
473    jsonAccessLogFileArgument = null;
474    keyStorePathArgument = null;
475    ldapDebugLogToStandardOutArgument = null;
476    ldapDebugLogFileArgument = null;
477    ldifFileArgument = null;
478    trustStorePathArgument = null;
479    useSchemaFileArgument = null;
480    maxChangeLogEntriesArgument = null;
481    maxConcurrentConnectionsArgument = null;
482    portArgument = null;
483    sizeLimitArgument = null;
484    additionalBindPasswordArgument = null;
485    allowedOperationTypeArgument = null;
486    authenticationRequiredOperationTypeArgument = null;
487    defaultPasswordEncodingArgument = null;
488    equalityIndexArgument = null;
489    keyStorePasswordArgument = null;
490    keyStoreTypeArgument = null;
491    passwordAttributeArgument = null;
492    trustStorePasswordArgument = null;
493    trustStoreTypeArgument = null;
494    vendorNameArgument = null;
495    vendorVersionArgument = null;
496  }
497
498
499
500  /**
501   * {@inheritDoc}
502   */
503  @Override()
504  @NotNull()
505  public String getToolName()
506  {
507    return "in-memory-directory-server";
508  }
509
510
511
512  /**
513   * {@inheritDoc}
514   */
515  @Override()
516  @NotNull()
517  public String getToolDescription()
518  {
519    return INFO_MEM_DS_TOOL_DESC.get(InMemoryDirectoryServer.class.getName());
520  }
521
522
523
524  /**
525   * Retrieves the version string for this tool.
526   *
527   * @return  The version string for this tool.
528   */
529  @Override()
530  @NotNull()
531  public String getToolVersion()
532  {
533    return Version.NUMERIC_VERSION_STRING;
534  }
535
536
537
538  /**
539   * {@inheritDoc}
540   */
541  @Override()
542  public void addToolArguments(@NotNull final ArgumentParser parser)
543         throws ArgumentException
544  {
545    portArgument = new IntegerArgument('p', "port", false, 1,
546         INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_PORT.get(),
547         INFO_MEM_DS_TOOL_ARG_DESC_PORT.get(), 0, 65_535);
548    portArgument.setArgumentGroupName(
549         INFO_MEM_DS_TOOL_GROUP_CONNECTIVITY.get());
550    parser.addArgument(portArgument);
551
552    useSSLArgument = new BooleanArgument('Z', "useSSL",
553         INFO_MEM_DS_TOOL_ARG_DESC_USE_SSL.get());
554    useSSLArgument.setArgumentGroupName(
555         INFO_MEM_DS_TOOL_GROUP_CONNECTIVITY.get());
556    useSSLArgument.addLongIdentifier("use-ssl", true);
557    parser.addArgument(useSSLArgument);
558
559    useStartTLSArgument = new BooleanArgument('q', "useStartTLS",
560         INFO_MEM_DS_TOOL_ARG_DESC_USE_START_TLS.get());
561    useStartTLSArgument.setArgumentGroupName(
562         INFO_MEM_DS_TOOL_GROUP_CONNECTIVITY.get());
563    useStartTLSArgument.addLongIdentifier("use-starttls", true);
564    useStartTLSArgument.addLongIdentifier("use-start-tls", true);
565    parser.addArgument(useStartTLSArgument);
566
567    keyStorePathArgument = new FileArgument('K', "keyStorePath", false, 1,
568         INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_PATH.get(),
569         INFO_MEM_DS_TOOL_ARG_DESC_KEY_STORE_PATH.get(), true, true, true,
570         false);
571    keyStorePathArgument.setArgumentGroupName(
572         INFO_MEM_DS_TOOL_GROUP_CONNECTIVITY.get());
573    keyStorePathArgument.addLongIdentifier("key-store-path", true);
574    parser.addArgument(keyStorePathArgument);
575
576    keyStorePasswordArgument = new StringArgument('W', "keyStorePassword",
577         false, 1, INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_PASSWORD.get(),
578         INFO_MEM_DS_TOOL_ARG_DESC_KEY_STORE_PW.get());
579    keyStorePasswordArgument.setSensitive(true);
580    keyStorePasswordArgument.setArgumentGroupName(
581         INFO_MEM_DS_TOOL_GROUP_CONNECTIVITY.get());
582    keyStorePasswordArgument.addLongIdentifier("keyStorePIN", true);
583    keyStorePasswordArgument.addLongIdentifier("key-store-password", true);
584    keyStorePasswordArgument.addLongIdentifier("key-store-pin", true);
585    parser.addArgument(keyStorePasswordArgument);
586
587    keyStoreTypeArgument = new StringArgument(null, "keyStoreType",
588         false, 1, "{type}", INFO_MEM_DS_TOOL_ARG_DESC_KEY_STORE_TYPE.get(),
589         "JKS");
590    keyStoreTypeArgument.setArgumentGroupName(
591         INFO_MEM_DS_TOOL_GROUP_CONNECTIVITY.get());
592    keyStoreTypeArgument.addLongIdentifier("keyStoreFormat", true);
593    keyStoreTypeArgument.addLongIdentifier("key-store-type", true);
594    keyStoreTypeArgument.addLongIdentifier("key-store-format", true);
595    parser.addArgument(keyStoreTypeArgument);
596
597    generateSelfSignedCertificateArgument = new BooleanArgument(null,
598         "generateSelfSignedCertificate", 1,
599         INFO_MEM_DS_TOOL_ARG_DESC_SELF_SIGNED_CERT.get());
600    generateSelfSignedCertificateArgument.setArgumentGroupName(
601         INFO_MEM_DS_TOOL_GROUP_CONNECTIVITY.get());
602    generateSelfSignedCertificateArgument.addLongIdentifier(
603         "useSelfSignedCertificate", true);
604    generateSelfSignedCertificateArgument.addLongIdentifier(
605         "selfSignedCertificate", true);
606    generateSelfSignedCertificateArgument.addLongIdentifier(
607         "generate-self-signed-certificate", true);
608    generateSelfSignedCertificateArgument.addLongIdentifier(
609         "use-self-signed-certificate", true);
610    generateSelfSignedCertificateArgument.addLongIdentifier(
611         "self-signed-certificate", true);
612    parser.addArgument(generateSelfSignedCertificateArgument);
613
614    trustStorePathArgument = new FileArgument('P', "trustStorePath", false, 1,
615         INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_PATH.get(),
616         INFO_MEM_DS_TOOL_ARG_DESC_TRUST_STORE_PATH.get(), true, true, true,
617         false);
618    trustStorePathArgument.setArgumentGroupName(
619         INFO_MEM_DS_TOOL_GROUP_CONNECTIVITY.get());
620    trustStorePathArgument.addLongIdentifier("trust-store-path", true);
621    parser.addArgument(trustStorePathArgument);
622
623    trustStorePasswordArgument = new StringArgument('T', "trustStorePassword",
624         false, 1, INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_PASSWORD.get(),
625         INFO_MEM_DS_TOOL_ARG_DESC_TRUST_STORE_PW.get());
626    trustStorePasswordArgument.setSensitive(true);
627    trustStorePasswordArgument.setArgumentGroupName(
628         INFO_MEM_DS_TOOL_GROUP_CONNECTIVITY.get());
629    trustStorePasswordArgument.addLongIdentifier("trustStorePIN", true);
630    trustStorePasswordArgument.addLongIdentifier("trust-store-password", true);
631    trustStorePasswordArgument.addLongIdentifier("trust-store-pin", true);
632    parser.addArgument(trustStorePasswordArgument);
633
634    trustStoreTypeArgument = new StringArgument(null, "trustStoreType",
635         false, 1, "{type}", INFO_MEM_DS_TOOL_ARG_DESC_TRUST_STORE_TYPE.get(),
636         "JKS");
637    trustStoreTypeArgument.setArgumentGroupName(
638         INFO_MEM_DS_TOOL_GROUP_CONNECTIVITY.get());
639    trustStoreTypeArgument.addLongIdentifier("trustStoreFormat", true);
640    trustStoreTypeArgument.addLongIdentifier("trust-store-type", true);
641    trustStoreTypeArgument.addLongIdentifier("trust-store-format", true);
642    parser.addArgument(trustStoreTypeArgument);
643
644    maxConcurrentConnectionsArgument = new IntegerArgument(null,
645         "maxConcurrentConnections", false, 1, null,
646         INFO_MEM_DS_TOOL_ARG_DESC_MAX_CONNECTIONS.get(), 1,
647         Integer.MAX_VALUE, Integer.MAX_VALUE);
648    maxConcurrentConnectionsArgument.setArgumentGroupName(
649         INFO_MEM_DS_TOOL_GROUP_CONNECTIVITY.get());
650    maxConcurrentConnectionsArgument.addLongIdentifier(
651         "maximumConcurrentConnections", true);
652    maxConcurrentConnectionsArgument.addLongIdentifier(
653         "maxConnections", true);
654    maxConcurrentConnectionsArgument.addLongIdentifier(
655         "maximumConnections", true);
656    maxConcurrentConnectionsArgument.addLongIdentifier(
657         "max-concurrent-connections", true);
658    maxConcurrentConnectionsArgument.addLongIdentifier(
659         "maximum-concurrent-connections", true);
660    maxConcurrentConnectionsArgument.addLongIdentifier(
661         "max-connections", true);
662    maxConcurrentConnectionsArgument.addLongIdentifier(
663         "maximum-connections", true);
664    parser.addArgument(maxConcurrentConnectionsArgument);
665
666    dontStartArgument = new BooleanArgument(null, "dontStart",
667         INFO_MEM_DS_TOOL_ARG_DESC_DONT_START.get());
668    dontStartArgument.setArgumentGroupName(
669         INFO_MEM_DS_TOOL_GROUP_CONNECTIVITY.get());
670    dontStartArgument.setHidden(true);
671    dontStartArgument.addLongIdentifier("doNotStart", true);
672    dontStartArgument.addLongIdentifier("dont-start", true);
673    dontStartArgument.addLongIdentifier("do-not-start", true);
674    parser.addArgument(dontStartArgument);
675
676    baseDNArgument = new DNArgument('b', "baseDN", true, 0,
677         INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_BASE_DN.get(),
678         INFO_MEM_DS_TOOL_ARG_DESC_BASE_DN.get());
679    baseDNArgument.setArgumentGroupName(INFO_MEM_DS_TOOL_GROUP_DATA.get());
680    baseDNArgument.addLongIdentifier("base-dn", true);
681    parser.addArgument(baseDNArgument);
682
683    ldifFileArgument = new FileArgument('l', "ldifFile", false, 1,
684         INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_PATH.get(),
685         INFO_MEM_DS_TOOL_ARG_DESC_LDIF_FILE.get(), true, true, true, false);
686    ldifFileArgument.setArgumentGroupName(INFO_MEM_DS_TOOL_GROUP_DATA.get());
687    ldifFileArgument.addLongIdentifier("ldif-file", true);
688    parser.addArgument(ldifFileArgument);
689
690    additionalBindDNArgument = new DNArgument('D', "additionalBindDN", false, 1,
691         INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_BIND_DN.get(),
692         INFO_MEM_DS_TOOL_ARG_DESC_ADDITIONAL_BIND_DN.get());
693    additionalBindDNArgument.setArgumentGroupName(
694         INFO_MEM_DS_TOOL_GROUP_DATA.get());
695    additionalBindDNArgument.addLongIdentifier("additional-bind-dn", true);
696    parser.addArgument(additionalBindDNArgument);
697
698    additionalBindPasswordArgument = new StringArgument('w',
699         "additionalBindPassword", false, 1,
700         INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_PASSWORD.get(),
701         INFO_MEM_DS_TOOL_ARG_DESC_ADDITIONAL_BIND_PW.get());
702    additionalBindPasswordArgument.setSensitive(true);
703    additionalBindPasswordArgument.setArgumentGroupName(
704         INFO_MEM_DS_TOOL_GROUP_DATA.get());
705    additionalBindPasswordArgument.addLongIdentifier(
706         "additional-bind-password", true);
707    parser.addArgument(additionalBindPasswordArgument);
708
709    useDefaultSchemaArgument = new BooleanArgument('s', "useDefaultSchema",
710         INFO_MEM_DS_TOOL_ARG_DESC_USE_DEFAULT_SCHEMA.get());
711    useDefaultSchemaArgument.setArgumentGroupName(
712         INFO_MEM_DS_TOOL_GROUP_DATA.get());
713    useDefaultSchemaArgument.addLongIdentifier("use-default-schema", true);
714    parser.addArgument(useDefaultSchemaArgument);
715
716    useSchemaFileArgument = new FileArgument('S', "useSchemaFile", false, 0,
717         INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_PATH.get(),
718         INFO_MEM_DS_TOOL_ARG_DESC_USE_SCHEMA_FILE.get(), true, true, false,
719         false);
720    useSchemaFileArgument.setArgumentGroupName(
721         INFO_MEM_DS_TOOL_GROUP_DATA.get());
722    useSchemaFileArgument.addLongIdentifier("use-schema-file", true);
723    parser.addArgument(useSchemaFileArgument);
724
725    doNotValidateSchemaDefinitions = new BooleanArgument(null,
726         "doNotValidateSchemaDefinitions", 1,
727         INFO_MEM_DS_TOOL_ARG_DESC_DO_NOT_VALIDATE_SCHEMA.get(
728              useSchemaFileArgument.getIdentifierString()));
729    doNotValidateSchemaDefinitions.setArgumentGroupName(
730         INFO_MEM_DS_TOOL_GROUP_DATA.get());
731    doNotValidateSchemaDefinitions.addLongIdentifier(
732         "do-not-validate-schema-definitions", true);
733    parser.addArgument(doNotValidateSchemaDefinitions);
734
735    equalityIndexArgument = new StringArgument('I', "equalityIndex", false, 0,
736         INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_ATTR.get(),
737         INFO_MEM_DS_TOOL_ARG_DESC_EQ_INDEX.get());
738    equalityIndexArgument.setArgumentGroupName(
739         INFO_MEM_DS_TOOL_GROUP_DATA.get());
740    equalityIndexArgument.addLongIdentifier("equality-index", true);
741    parser.addArgument(equalityIndexArgument);
742
743    maxChangeLogEntriesArgument = new IntegerArgument('c',
744         "maxChangeLogEntries", false, 1,
745         INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_COUNT.get(),
746         INFO_MEM_DS_TOOL_ARG_DESC_MAX_CHANGELOG_ENTRIES.get(), 0,
747         Integer.MAX_VALUE, 0);
748    maxChangeLogEntriesArgument.setArgumentGroupName(
749         INFO_MEM_DS_TOOL_GROUP_DATA.get());
750    maxChangeLogEntriesArgument.addLongIdentifier("max-changelog-entries",
751         true);
752    maxChangeLogEntriesArgument.addLongIdentifier("max-change-log-entries",
753         true);
754    parser.addArgument(maxChangeLogEntriesArgument);
755
756    sizeLimitArgument = new IntegerArgument(null, "sizeLimit", false, 1, null,
757         INFO_MEM_DS_TOOL_ARG_DESC_SIZE_LIMIT.get(), 1, Integer.MAX_VALUE,
758         Integer.MAX_VALUE);
759    sizeLimitArgument.setArgumentGroupName(INFO_MEM_DS_TOOL_GROUP_DATA.get());
760    sizeLimitArgument.addLongIdentifier("searchSizeLimit", true);
761    sizeLimitArgument.addLongIdentifier("size-limit", true);
762    sizeLimitArgument.addLongIdentifier("search-size-limit", true);
763    parser.addArgument(sizeLimitArgument);
764
765    passwordAttributeArgument = new StringArgument(null, "passwordAttribute",
766         false, 1, INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_ATTR.get(),
767         INFO_MEM_DS_TOOL_ARG_DESC_PASSWORD_ATTRIBUTE.get(), "userPassword");
768    passwordAttributeArgument.setArgumentGroupName(
769         INFO_MEM_DS_TOOL_GROUP_DATA.get());
770    passwordAttributeArgument.addLongIdentifier("passwordAttributeType", true);
771    passwordAttributeArgument.addLongIdentifier("password-attribute", true);
772    passwordAttributeArgument.addLongIdentifier("password-attribute-type",
773         true);
774    parser.addArgument(passwordAttributeArgument);
775
776    final Set<String> allowedSchemes = StaticUtils.setOf("md5", "smd5", "sha",
777         "ssha", "sha256", "ssha256", "sha384", "ssha384", "sha512", "ssha512",
778         "clear", "base64", "hex");
779    defaultPasswordEncodingArgument = new StringArgument(null,
780         "defaultPasswordEncoding", false, 1,
781         INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_SCHEME.get(),
782         INFO_MEM_DS_TOOL_ARG_DESC_DEFAULT_PASSWORD_ENCODING.get(),
783         allowedSchemes);
784    defaultPasswordEncodingArgument.setArgumentGroupName(
785         INFO_MEM_DS_TOOL_GROUP_DATA.get());
786    defaultPasswordEncodingArgument.addLongIdentifier(
787         "defaultPasswordEncodingScheme", true);
788    defaultPasswordEncodingArgument.addLongIdentifier(
789         "defaultPasswordStorageScheme", true);
790    defaultPasswordEncodingArgument.addLongIdentifier(
791         "defaultPasswordScheme", true);
792    defaultPasswordEncodingArgument.addLongIdentifier(
793         "default-password-encoding", true);
794    defaultPasswordEncodingArgument.addLongIdentifier(
795         "default-password-encoding-scheme", true);
796    defaultPasswordEncodingArgument.addLongIdentifier(
797         "default-password-storage-scheme", true);
798    defaultPasswordEncodingArgument.addLongIdentifier(
799         "default-password-scheme", true);
800    parser.addArgument(defaultPasswordEncodingArgument);
801
802    final Set<String> allowedOperationTypeAllowedValues = StaticUtils.setOf(
803         "add", "bind", "compare", "delete", "extended", "modify", "modify-dn",
804         "search");
805    allowedOperationTypeArgument = new StringArgument(null,
806         "allowedOperationType", false, 0,
807         INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_TYPE.get(),
808         INFO_MEM_DS_TOOL_ARG_DESC_ALLOWED_OP_TYPE.get(),
809         allowedOperationTypeAllowedValues);
810    allowedOperationTypeArgument.setArgumentGroupName(
811         INFO_MEM_DS_TOOL_GROUP_DATA.get());
812    allowedOperationTypeArgument.addLongIdentifier("allowed-operation-type",
813         true);
814    parser.addArgument(allowedOperationTypeArgument);
815
816    final Set<String> authRequiredTypeAllowedValues = StaticUtils.setOf("add",
817         "compare", "delete", "extended", "modify", "modify-dn", "search");
818    authenticationRequiredOperationTypeArgument = new StringArgument(null,
819         "authenticationRequiredOperationType", false, 0,
820         INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_TYPE.get(),
821         INFO_MEM_DS_TOOL_ARG_DESC_AUTH_REQUIRED_OP_TYPE.get(),
822         authRequiredTypeAllowedValues);
823    authenticationRequiredOperationTypeArgument.setArgumentGroupName(
824         INFO_MEM_DS_TOOL_GROUP_DATA.get());
825    authenticationRequiredOperationTypeArgument.addLongIdentifier(
826         "requiredAuthenticationOperationType", true);
827    authenticationRequiredOperationTypeArgument.addLongIdentifier(
828         "requireAuthenticationOperationType", true);
829    authenticationRequiredOperationTypeArgument.addLongIdentifier(
830         "authentication-required-operation-type", true);
831    authenticationRequiredOperationTypeArgument.addLongIdentifier(
832         "required-authentication-operation-type", true);
833    authenticationRequiredOperationTypeArgument.addLongIdentifier(
834         "require-authentication-operation-type", true);
835    parser.addArgument(authenticationRequiredOperationTypeArgument);
836
837    vendorNameArgument = new StringArgument(null, "vendorName", false, 1,
838         INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_VALUE.get(),
839         INFO_MEM_DS_TOOL_ARG_DESC_VENDOR_NAME.get());
840    vendorNameArgument.setArgumentGroupName(INFO_MEM_DS_TOOL_GROUP_DATA.get());
841    vendorNameArgument.addLongIdentifier("vendor-name", true);
842    parser.addArgument(vendorNameArgument);
843
844    vendorVersionArgument = new StringArgument(null, "vendorVersion", false, 1,
845         INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_VALUE.get(),
846         INFO_MEM_DS_TOOL_ARG_DESC_VENDOR_VERSION.get());
847    vendorVersionArgument.setArgumentGroupName(
848         INFO_MEM_DS_TOOL_GROUP_DATA.get());
849    vendorVersionArgument.addLongIdentifier("vendor-version", true);
850    parser.addArgument(vendorVersionArgument);
851
852    accessLogToStandardOutArgument = new BooleanArgument('A',
853         "accessLogToStandardOut",
854         INFO_MEM_DS_TOOL_ARG_DESC_ACCESS_LOG_TO_STDOUT.get());
855    accessLogToStandardOutArgument.setArgumentGroupName(
856         INFO_MEM_DS_TOOL_GROUP_LOGGING.get());
857    accessLogToStandardOutArgument.addLongIdentifier(
858         "access-log-to-standard-out", true);
859    parser.addArgument(accessLogToStandardOutArgument);
860
861    accessLogFileArgument = new FileArgument('a', "accessLogFile", false, 1,
862         INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_PATH.get(),
863         INFO_MEM_DS_TOOL_ARG_DESC_ACCESS_LOG_FILE.get(), false, true, true,
864         false);
865    accessLogFileArgument.setArgumentGroupName(
866         INFO_MEM_DS_TOOL_GROUP_LOGGING.get());
867    accessLogFileArgument.addLongIdentifier("access-log-format", true);
868    parser.addArgument(accessLogFileArgument);
869
870    jsonAccessLogToStandardOutArgument = new BooleanArgument(null,
871         "jsonAccessLogToStandardOut",
872         INFO_MEM_DS_TOOL_ARG_DESC_JSON_ACCESS_LOG_TO_STDOUT.get());
873    jsonAccessLogToStandardOutArgument.setArgumentGroupName(
874         INFO_MEM_DS_TOOL_GROUP_LOGGING.get());
875    jsonAccessLogToStandardOutArgument.addLongIdentifier(
876         "json-access-log-to-standard-out", true);
877    parser.addArgument(jsonAccessLogToStandardOutArgument);
878
879    jsonAccessLogFileArgument = new FileArgument(null, "jsonAccessLogFile",
880         false, 1, INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_PATH.get(),
881         INFO_MEM_DS_TOOL_ARG_DESC_JSON_ACCESS_LOG_FILE.get(), false, true,
882         true, false);
883    jsonAccessLogFileArgument.setArgumentGroupName(
884         INFO_MEM_DS_TOOL_GROUP_LOGGING.get());
885    jsonAccessLogFileArgument.addLongIdentifier("json-access-log-format", true);
886    parser.addArgument(jsonAccessLogFileArgument);
887
888    ldapDebugLogToStandardOutArgument = new BooleanArgument(null,
889         "ldapDebugLogToStandardOut",
890         INFO_MEM_DS_TOOL_ARG_DESC_LDAP_DEBUG_LOG_TO_STDOUT.get());
891    ldapDebugLogToStandardOutArgument.setArgumentGroupName(
892         INFO_MEM_DS_TOOL_GROUP_LOGGING.get());
893    ldapDebugLogToStandardOutArgument.addLongIdentifier(
894         "ldap-debug-log-to-standard-out", true);
895    parser.addArgument(ldapDebugLogToStandardOutArgument);
896
897    ldapDebugLogFileArgument = new FileArgument('d', "ldapDebugLogFile", false,
898         1, INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_PATH.get(),
899         INFO_MEM_DS_TOOL_ARG_DESC_LDAP_DEBUG_LOG_FILE.get(), false, true, true,
900         false);
901    ldapDebugLogFileArgument.setArgumentGroupName(
902         INFO_MEM_DS_TOOL_GROUP_LOGGING.get());
903    ldapDebugLogFileArgument.addLongIdentifier("ldap-debug-log-file", true);
904    parser.addArgument(ldapDebugLogFileArgument);
905
906    codeLogFile = new FileArgument('C', "codeLogFile", false, 1, "{path}",
907         INFO_MEM_DS_TOOL_ARG_DESC_CODE_LOG_FILE.get(), false, true, true,
908         false);
909    codeLogFile.setArgumentGroupName(INFO_MEM_DS_TOOL_GROUP_LOGGING.get());
910    codeLogFile.addLongIdentifier("code-log-file", true);
911    parser.addArgument(codeLogFile);
912
913    parser.addExclusiveArgumentSet(useDefaultSchemaArgument,
914         useSchemaFileArgument);
915
916    parser.addDependentArgumentSet(doNotValidateSchemaDefinitions,
917         useSchemaFileArgument);
918
919    parser.addExclusiveArgumentSet(useSSLArgument, useStartTLSArgument);
920
921    parser.addExclusiveArgumentSet(keyStorePathArgument,
922         generateSelfSignedCertificateArgument);
923
924    parser.addExclusiveArgumentSet(accessLogToStandardOutArgument,
925         accessLogFileArgument);
926
927    parser.addExclusiveArgumentSet(jsonAccessLogToStandardOutArgument,
928         jsonAccessLogFileArgument);
929
930    parser.addExclusiveArgumentSet(ldapDebugLogToStandardOutArgument,
931         ldapDebugLogFileArgument);
932
933    parser.addDependentArgumentSet(additionalBindDNArgument,
934         additionalBindPasswordArgument);
935
936    parser.addDependentArgumentSet(additionalBindPasswordArgument,
937         additionalBindDNArgument);
938
939    parser.addDependentArgumentSet(useSSLArgument, keyStorePathArgument,
940         generateSelfSignedCertificateArgument);
941
942    parser.addDependentArgumentSet(keyStorePathArgument,
943         keyStorePasswordArgument);
944
945    parser.addDependentArgumentSet(keyStorePasswordArgument,
946         keyStorePathArgument);
947
948    parser.addDependentArgumentSet(keyStoreTypeArgument,
949         keyStorePathArgument);
950
951    parser.addDependentArgumentSet(useStartTLSArgument, keyStorePathArgument,
952         generateSelfSignedCertificateArgument);
953
954    parser.addDependentArgumentSet(keyStorePathArgument, useSSLArgument,
955         useStartTLSArgument);
956
957    parser.addDependentArgumentSet(generateSelfSignedCertificateArgument,
958         useSSLArgument, useStartTLSArgument);
959
960    parser.addDependentArgumentSet(trustStorePathArgument, useSSLArgument,
961         useStartTLSArgument);
962
963    parser.addDependentArgumentSet(trustStorePasswordArgument,
964         trustStorePathArgument);
965
966    parser.addDependentArgumentSet(trustStoreTypeArgument,
967         trustStorePathArgument);
968  }
969
970
971
972  /**
973   * {@inheritDoc}
974   */
975  @Override()
976  public boolean supportsInteractiveMode()
977  {
978    return true;
979  }
980
981
982
983  /**
984   * {@inheritDoc}
985   */
986  @Override()
987  public boolean defaultsToInteractiveMode()
988  {
989    return true;
990  }
991
992
993
994  /**
995   * Indicates whether this tool supports the use of a properties file for
996   * specifying default values for arguments that aren't specified on the
997   * command line.
998   *
999   * @return  {@code true} if this tool supports the use of a properties file
1000   *          for specifying default values for arguments that aren't specified
1001   *          on the command line, or {@code false} if not.
1002   */
1003  @Override()
1004  public boolean supportsPropertiesFile()
1005  {
1006    return true;
1007  }
1008
1009
1010
1011  /**
1012   * {@inheritDoc}
1013   */
1014  @Override()
1015  @NotNull()
1016  public ResultCode doToolProcessing()
1017  {
1018    // Create a base configuration.
1019    final InMemoryDirectoryServerConfig serverConfig;
1020    try
1021    {
1022      serverConfig = getConfig();
1023    }
1024    catch (final LDAPException le)
1025    {
1026      Debug.debugException(le);
1027      wrapErr(0, WRAP_COLUMN,
1028           ERR_MEM_DS_TOOL_ERROR_INITIALIZING_CONFIG.get(le.getMessage()));
1029      return le.getResultCode();
1030    }
1031
1032
1033    // Create the server instance using the provided configuration, but don't
1034    // start it yet.
1035    try
1036    {
1037      directoryServer = new InMemoryDirectoryServer(serverConfig);
1038    }
1039    catch (final LDAPException le)
1040    {
1041      Debug.debugException(le);
1042      wrapErr(0, WRAP_COLUMN,
1043           ERR_MEM_DS_TOOL_ERROR_CREATING_SERVER_INSTANCE.get(le.getMessage()));
1044      return le.getResultCode();
1045    }
1046
1047
1048    // If an LDIF file was provided, then use it to populate the server.
1049    if (ldifFileArgument.isPresent())
1050    {
1051      final File ldifFile = ldifFileArgument.getValue();
1052      try
1053      {
1054        final int numEntries = directoryServer.importFromLDIF(true,
1055             ldifFile.getAbsolutePath());
1056        wrapOut(0, WRAP_COLUMN,
1057             INFO_MEM_DS_TOOL_ADDED_ENTRIES_FROM_LDIF.get(numEntries,
1058                  ldifFile.getAbsolutePath()));
1059      }
1060      catch (final LDAPException le)
1061      {
1062        Debug.debugException(le);
1063        wrapErr(0, WRAP_COLUMN,
1064             ERR_MEM_DS_TOOL_ERROR_POPULATING_SERVER_INSTANCE.get(
1065                  ldifFile.getAbsolutePath(), le.getMessage()));
1066        return le.getResultCode();
1067      }
1068    }
1069
1070
1071    // Start the server.
1072    try
1073    {
1074      if (! dontStartArgument.isPresent())
1075      {
1076        directoryServer.startListening();
1077        wrapOut(0, WRAP_COLUMN,
1078             INFO_MEM_DS_TOOL_LISTENING.get(directoryServer.getListenPort()));
1079      }
1080    }
1081    catch (final Exception e)
1082    {
1083      Debug.debugException(e);
1084      wrapErr(0, WRAP_COLUMN,
1085           ERR_MEM_DS_TOOL_ERROR_STARTING_SERVER.get(
1086                StaticUtils.getExceptionMessage(e)));
1087      return ResultCode.LOCAL_ERROR;
1088    }
1089
1090    return ResultCode.SUCCESS;
1091  }
1092
1093
1094
1095  /**
1096   * Creates a server configuration based on information provided with
1097   * command line arguments.
1098   *
1099   * @return  The configuration that was created.
1100   *
1101   * @throws  LDAPException  If a problem is encountered while creating the
1102   *                         configuration.
1103   */
1104  @NotNull()
1105  private InMemoryDirectoryServerConfig getConfig()
1106          throws LDAPException
1107  {
1108    final List<DN> dnList = baseDNArgument.getValues();
1109    final DN[] baseDNs = new DN[dnList.size()];
1110    dnList.toArray(baseDNs);
1111
1112    final InMemoryDirectoryServerConfig serverConfig =
1113         new InMemoryDirectoryServerConfig(baseDNs);
1114
1115
1116    // If a listen port was specified, then update the configuration to use it.
1117    int listenPort = 0;
1118    if (portArgument.isPresent())
1119    {
1120      listenPort = portArgument.getValue();
1121    }
1122
1123
1124    // If schema should be used, then get it.
1125    if (useDefaultSchemaArgument.isPresent())
1126    {
1127      serverConfig.setSchema(Schema.getDefaultStandardSchema());
1128    }
1129    else if (useSchemaFileArgument.isPresent())
1130    {
1131      final Map<String,File> schemaFiles = new TreeMap<>();
1132      for (final File f : useSchemaFileArgument.getValues())
1133      {
1134        if (f.exists())
1135        {
1136          if (f.isFile())
1137          {
1138            schemaFiles.put(f.getName(), f);
1139          }
1140          else
1141          {
1142            for (final File subFile : f.listFiles())
1143            {
1144              if (subFile.isFile())
1145              {
1146                schemaFiles.put(subFile.getName(), subFile);
1147              }
1148            }
1149          }
1150        }
1151        else
1152        {
1153          throw new LDAPException(ResultCode.PARAM_ERROR,
1154               ERR_MEM_DS_TOOL_NO_SUCH_SCHEMA_FILE.get(f.getAbsolutePath()));
1155        }
1156      }
1157
1158      if (! doNotValidateSchemaDefinitions.isPresent())
1159      {
1160        Schema schema = null;
1161        final List<String> errorMessages = new ArrayList<>();
1162        final SchemaValidator schemaValidator = new SchemaValidator();
1163        for (final File schemaFile : schemaFiles.values())
1164        {
1165          schema = schemaValidator.validateSchema(schemaFile, schema,
1166               errorMessages);
1167        }
1168
1169        if (! errorMessages.isEmpty())
1170        {
1171          wrapErr(0, WRAP_COLUMN,
1172               WARN_MEM_DS_TOOL_SCHEMA_ISSUES_IDENTIFIED.get());
1173          for (final String message : errorMessages)
1174          {
1175            err();
1176            final List<String> wrappedLines = StaticUtils.wrapLine(message,
1177                 (WRAP_COLUMN - 2));
1178            final Iterator<String> iterator = wrappedLines.iterator();
1179            err("* " + iterator.next());
1180            while (iterator.hasNext())
1181            {
1182              err("  " + iterator.next());
1183            }
1184          }
1185          err();
1186          wrapErr(0, WRAP_COLUMN,
1187               WARN_MEM_DS_TOOL_WILL_CONTINUE_WITH_SCHEMA.get(
1188                    doNotValidateSchemaDefinitions.getIdentifierString()));
1189          err();
1190        }
1191      }
1192
1193      try
1194      {
1195        serverConfig.setSchema(Schema.getSchema(
1196             new ArrayList<File>(schemaFiles.values())));
1197      }
1198      catch (final Exception e)
1199      {
1200        Debug.debugException(e);
1201
1202        final StringBuilder fileList = new StringBuilder();
1203        final Iterator<File> fileIterator = schemaFiles.values().iterator();
1204        while (fileIterator.hasNext())
1205        {
1206          fileList.append(fileIterator.next().getAbsolutePath());
1207          if (fileIterator.hasNext())
1208          {
1209            fileList.append(", ");
1210          }
1211        }
1212
1213        throw new LDAPException(ResultCode.LOCAL_ERROR,
1214             ERR_MEM_DS_TOOL_ERROR_READING_SCHEMA.get(
1215                  fileList, StaticUtils.getExceptionMessage(e)),
1216             e);
1217      }
1218    }
1219    else
1220    {
1221      serverConfig.setSchema(null);
1222    }
1223
1224
1225    // If an additional bind DN and password are provided, then include them in
1226    // the configuration.
1227    if (additionalBindDNArgument.isPresent())
1228    {
1229      serverConfig.addAdditionalBindCredentials(
1230           additionalBindDNArgument.getValue().toString(),
1231           additionalBindPasswordArgument.getValue());
1232    }
1233
1234
1235    // If a maximum number of changelog entries was specified, then update the
1236    // configuration with that.
1237    if (maxChangeLogEntriesArgument.isPresent())
1238    {
1239      serverConfig.setMaxChangeLogEntries(
1240           maxChangeLogEntriesArgument.getValue());
1241    }
1242
1243
1244    // If a maximum number of concurrent connections was specified, then update
1245    // the configuration with that.
1246    if (maxConcurrentConnectionsArgument.isPresent())
1247    {
1248      serverConfig.setMaxConnections(
1249           maxConcurrentConnectionsArgument.getValue());
1250    }
1251
1252
1253    // If a size limit was specified, then update the configuration with that.
1254    if (sizeLimitArgument.isPresent())
1255    {
1256      serverConfig.setMaxSizeLimit(sizeLimitArgument.getValue());
1257    }
1258
1259
1260    // If the password argument was specified, then set the password arguments.
1261    if (passwordAttributeArgument.isPresent())
1262    {
1263      serverConfig.setPasswordAttributes(passwordAttributeArgument.getValues());
1264    }
1265
1266
1267    // Configure password encodings for the server.
1268    final LinkedHashMap<String,InMemoryPasswordEncoder> passwordEncoders =
1269         new LinkedHashMap<>(10);
1270    addUnsaltedEncoder("MD5", "MD5", passwordEncoders);
1271    addUnsaltedEncoder("SHA", "SHA-1", passwordEncoders);
1272    addUnsaltedEncoder("SHA1", "SHA-1", passwordEncoders);
1273    addUnsaltedEncoder("SHA-1", "SHA-1", passwordEncoders);
1274    addUnsaltedEncoder("SHA256", "SHA-256", passwordEncoders);
1275    addUnsaltedEncoder("SHA-256", "SHA-256", passwordEncoders);
1276    addUnsaltedEncoder("SHA384", "SHA-384", passwordEncoders);
1277    addUnsaltedEncoder("SHA-384", "SHA-384", passwordEncoders);
1278    addUnsaltedEncoder("SHA512", "SHA-512", passwordEncoders);
1279    addUnsaltedEncoder("SHA-512", "SHA-512", passwordEncoders);
1280    addSaltedEncoder("SMD5", "MD5", passwordEncoders);
1281    addSaltedEncoder("SSHA", "SHA-1", passwordEncoders);
1282    addSaltedEncoder("SSHA1", "SHA-1", passwordEncoders);
1283    addSaltedEncoder("SSHA-1", "SHA-1", passwordEncoders);
1284    addSaltedEncoder("SSHA256", "SHA-256", passwordEncoders);
1285    addSaltedEncoder("SSHA-256", "SHA-256", passwordEncoders);
1286    addSaltedEncoder("SSHA384", "SHA-384", passwordEncoders);
1287    addSaltedEncoder("SSHA-384", "SHA-384", passwordEncoders);
1288    addSaltedEncoder("SSHA512", "SHA-512", passwordEncoders);
1289    addSaltedEncoder("SSHA-512", "SHA-512", passwordEncoders);
1290    addClearEncoder("CLEAR", null, passwordEncoders);
1291    addClearEncoder("BASE64",
1292         Base64PasswordEncoderOutputFormatter.getInstance(), passwordEncoders);
1293    addClearEncoder("HEX",
1294         HexPasswordEncoderOutputFormatter.getLowercaseInstance(),
1295         passwordEncoders);
1296
1297    final InMemoryPasswordEncoder primaryEncoder;
1298    if (defaultPasswordEncodingArgument.isPresent())
1299    {
1300      primaryEncoder = passwordEncoders.remove(
1301           StaticUtils.toLowerCase(defaultPasswordEncodingArgument.getValue()));
1302      if (primaryEncoder == null)
1303      {
1304        throw new LDAPException(ResultCode.PARAM_ERROR,
1305             ERR_MEM_DS_TOOL_UNAVAILABLE_PW_ENCODING.get(
1306                  defaultPasswordEncodingArgument.getValue(),
1307                  String.valueOf(passwordEncoders.keySet())));
1308      }
1309    }
1310    else
1311    {
1312      primaryEncoder = null;
1313    }
1314
1315    serverConfig.setPasswordEncoders(primaryEncoder,
1316         passwordEncoders.values());
1317
1318
1319    // Configure the allowed operation types.
1320    if (allowedOperationTypeArgument.isPresent())
1321    {
1322      final EnumSet<OperationType> operationTypes =
1323           EnumSet.noneOf(OperationType.class);
1324      for (final String operationTypeName :
1325           allowedOperationTypeArgument.getValues())
1326      {
1327        final OperationType name = OperationType.forName(operationTypeName);
1328        if (name == null)
1329        {
1330          throw new LDAPException(ResultCode.PARAM_ERROR,
1331               ERR_MEM_DS_TOOL_UNSUPPORTED_ALLOWED_OP_TYPE.get(name));
1332        }
1333        else
1334        {
1335          switch (name)
1336          {
1337            case ADD:
1338            case BIND:
1339            case COMPARE:
1340            case DELETE:
1341            case EXTENDED:
1342            case MODIFY:
1343            case MODIFY_DN:
1344            case SEARCH:
1345              operationTypes.add(name);
1346              break;
1347            case ABANDON:
1348            case UNBIND:
1349            default:
1350              throw new LDAPException(ResultCode.PARAM_ERROR,
1351                   ERR_MEM_DS_TOOL_UNSUPPORTED_ALLOWED_OP_TYPE.get(name));
1352          }
1353        }
1354      }
1355
1356      serverConfig.setAllowedOperationTypes(operationTypes);
1357    }
1358
1359
1360    // Configure the authentication required operation types.
1361    if (authenticationRequiredOperationTypeArgument.isPresent())
1362    {
1363      final EnumSet<OperationType> operationTypes =
1364           EnumSet.noneOf(OperationType.class);
1365      for (final String operationTypeName :
1366           authenticationRequiredOperationTypeArgument.getValues())
1367      {
1368        final OperationType name = OperationType.forName(operationTypeName);
1369        if (name == null)
1370        {
1371          throw new LDAPException(ResultCode.PARAM_ERROR,
1372               ERR_MEM_DS_TOOL_UNSUPPORTED_AUTH_REQUIRED_OP_TYPE.get(name));
1373        }
1374        else
1375        {
1376          switch (name)
1377          {
1378            case ADD:
1379            case COMPARE:
1380            case DELETE:
1381            case EXTENDED:
1382            case MODIFY:
1383            case MODIFY_DN:
1384            case SEARCH:
1385              operationTypes.add(name);
1386              break;
1387            case ABANDON:
1388            case UNBIND:
1389            default:
1390              throw new LDAPException(ResultCode.PARAM_ERROR,
1391                   ERR_MEM_DS_TOOL_UNSUPPORTED_AUTH_REQUIRED_OP_TYPE.get(name));
1392          }
1393        }
1394      }
1395
1396      serverConfig.setAuthenticationRequiredOperationTypes(operationTypes);
1397    }
1398
1399
1400    // If an access log file was specified, then create the appropriate log
1401    // handler.
1402    if (accessLogToStandardOutArgument.isPresent())
1403    {
1404      final StreamHandler handler = new StreamHandler(System.out,
1405           new MinimalLogFormatter(null, false, false, true));
1406      StaticUtils.setLogHandlerLevel(handler, Level.INFO);
1407      serverConfig.setAccessLogHandler(handler);
1408    }
1409    else if (accessLogFileArgument.isPresent())
1410    {
1411      final File logFile = accessLogFileArgument.getValue();
1412      try
1413      {
1414        final FileHandler handler =
1415             new FileHandler(logFile.getAbsolutePath(), true);
1416        StaticUtils.setLogHandlerLevel(handler, Level.INFO);
1417        handler.setFormatter(new MinimalLogFormatter(null, false, false,
1418             true));
1419        serverConfig.setAccessLogHandler(handler);
1420      }
1421      catch (final Exception e)
1422      {
1423        Debug.debugException(e);
1424        throw new LDAPException(ResultCode.LOCAL_ERROR,
1425             ERR_MEM_DS_TOOL_ERROR_CREATING_LOG_HANDLER.get(
1426                  logFile.getAbsolutePath(),
1427                  StaticUtils.getExceptionMessage(e)),
1428             e);
1429      }
1430    }
1431
1432
1433    // If a JSON-formatted access log file was specified, then create the
1434    // appropriate log handler.
1435    if (jsonAccessLogToStandardOutArgument.isPresent())
1436    {
1437      final StreamHandler handler = new StreamHandler(System.out,
1438           new MinimalLogFormatter(null, false, false, true));
1439      StaticUtils.setLogHandlerLevel(handler, Level.INFO);
1440      serverConfig.setJSONAccessLogHandler(handler);
1441    }
1442    else if (jsonAccessLogFileArgument.isPresent())
1443    {
1444      final File logFile = jsonAccessLogFileArgument.getValue();
1445      try
1446      {
1447        final FileHandler handler =
1448             new FileHandler(logFile.getAbsolutePath(), true);
1449        StaticUtils.setLogHandlerLevel(handler, Level.INFO);
1450        handler.setFormatter(new MinimalLogFormatter(null, false, false,
1451             true));
1452        serverConfig.setJSONAccessLogHandler(handler);
1453      }
1454      catch (final Exception e)
1455      {
1456        Debug.debugException(e);
1457        throw new LDAPException(ResultCode.LOCAL_ERROR,
1458             ERR_MEM_DS_TOOL_ERROR_CREATING_LOG_HANDLER.get(
1459                  logFile.getAbsolutePath(),
1460                  StaticUtils.getExceptionMessage(e)),
1461             e);
1462      }
1463    }
1464
1465
1466    // If an LDAP debug log file was specified, then create the appropriate log
1467    // handler.
1468    if (ldapDebugLogToStandardOutArgument.isPresent())
1469    {
1470      final StreamHandler handler = new StreamHandler(System.out,
1471           new MinimalLogFormatter(null, false, false, true));
1472      StaticUtils.setLogHandlerLevel(handler, Level.INFO);
1473      serverConfig.setLDAPDebugLogHandler(handler);
1474    }
1475    else if (ldapDebugLogFileArgument.isPresent())
1476    {
1477      final File logFile = ldapDebugLogFileArgument.getValue();
1478      try
1479      {
1480        final FileHandler handler =
1481             new FileHandler(logFile.getAbsolutePath(), true);
1482        StaticUtils.setLogHandlerLevel(handler, Level.INFO);
1483        handler.setFormatter(new MinimalLogFormatter(null, false, false,
1484             true));
1485        serverConfig.setLDAPDebugLogHandler(handler);
1486      }
1487      catch (final Exception e)
1488      {
1489        Debug.debugException(e);
1490        throw new LDAPException(ResultCode.LOCAL_ERROR,
1491             ERR_MEM_DS_TOOL_ERROR_CREATING_LOG_HANDLER.get(
1492                  logFile.getAbsolutePath(),
1493                  StaticUtils.getExceptionMessage(e)),
1494             e);
1495      }
1496    }
1497
1498
1499    // If a code log file was specified, then update the configuration
1500    // accordingly.
1501    if (codeLogFile.isPresent())
1502    {
1503      serverConfig.setCodeLogDetails(codeLogFile.getValue().getAbsolutePath(),
1504           true);
1505    }
1506
1507
1508    // If SSL is to be used, then create the corresponding socket factories.
1509    if (useSSLArgument.isPresent() || useStartTLSArgument.isPresent())
1510    {
1511      final File keyStorePath;
1512      final char[] keyStorePIN;
1513      final String keyStoreType;
1514      if (keyStorePathArgument.isPresent())
1515      {
1516        keyStorePath = keyStorePathArgument.getValue();
1517        keyStorePIN = keyStorePasswordArgument.getValue().toCharArray();
1518        keyStoreType = keyStoreTypeArgument.getValue();
1519      }
1520      else
1521      {
1522        try
1523        {
1524          keyStoreType = "JKS";
1525          final ObjectPair<File,char[]> keyStoreInfo =
1526               SelfSignedCertificateGenerator.
1527                    generateTemporarySelfSignedCertificate(
1528                         getToolName(), keyStoreType);
1529          keyStorePath = keyStoreInfo.getFirst();
1530          keyStorePIN = keyStoreInfo.getSecond();
1531        }
1532        catch (final CertException e)
1533        {
1534          Debug.debugException(e);
1535          throw new LDAPException(ResultCode.LOCAL_ERROR, e.getMessage(), e);
1536        }
1537      }
1538
1539
1540      try
1541      {
1542        final KeyManager keyManager = new KeyStoreKeyManager(keyStorePath,
1543             keyStorePIN, keyStoreType, null, true);
1544
1545        final TrustManager trustManager;
1546        if (trustStorePathArgument.isPresent())
1547        {
1548          final char[] password;
1549          if (trustStorePasswordArgument.isPresent())
1550          {
1551            password = trustStorePasswordArgument.getValue().toCharArray();
1552          }
1553          else
1554          {
1555            password = null;
1556          }
1557
1558          trustManager = new TrustStoreTrustManager(
1559               trustStorePathArgument.getValue(), password,
1560               trustStoreTypeArgument.getValue(), true);
1561        }
1562        else
1563        {
1564          trustManager = new TrustAllTrustManager();
1565        }
1566
1567        final SSLUtil serverSSLUtil = new SSLUtil(keyManager, trustManager);
1568
1569        if (useSSLArgument.isPresent())
1570        {
1571          final SSLUtil clientSSLUtil = new SSLUtil(new TrustAllTrustManager());
1572          serverConfig.setListenerConfigs(
1573               InMemoryListenerConfig.createLDAPSConfig("LDAPS", null,
1574                    listenPort, serverSSLUtil.createSSLServerSocketFactory(),
1575                    clientSSLUtil.createSSLSocketFactory()));
1576        }
1577        else
1578        {
1579          serverConfig.setListenerConfigs(
1580               InMemoryListenerConfig.createLDAPConfig("LDAP+StartTLS", null,
1581                    listenPort, serverSSLUtil.createSSLSocketFactory()));
1582        }
1583      }
1584      catch (final Exception e)
1585      {
1586        Debug.debugException(e);
1587        throw new LDAPException(ResultCode.LOCAL_ERROR,
1588             ERR_MEM_DS_TOOL_ERROR_INITIALIZING_SSL.get(
1589                  StaticUtils.getExceptionMessage(e)),
1590             e);
1591      }
1592    }
1593    else
1594    {
1595      serverConfig.setListenerConfigs(InMemoryListenerConfig.createLDAPConfig(
1596           "LDAP", listenPort));
1597    }
1598
1599
1600    // If vendor name and/or vendor version values were provided, then configure
1601    // them for use.
1602    if (vendorNameArgument.isPresent())
1603    {
1604      serverConfig.setVendorName(vendorNameArgument.getValue());
1605    }
1606
1607    if (vendorVersionArgument.isPresent())
1608    {
1609      serverConfig.setVendorVersion(vendorVersionArgument.getValue());
1610    }
1611
1612
1613    // If equality indexing is to be performed, then configure it.
1614    if (equalityIndexArgument.isPresent())
1615    {
1616      serverConfig.setEqualityIndexAttributes(
1617           equalityIndexArgument.getValues());
1618    }
1619
1620    return serverConfig;
1621  }
1622
1623
1624
1625  /**
1626   * Updates the map with an unsalted password encoder with the provided
1627   * information.
1628   *
1629   * @param  schemeName       The name to use to identify the scheme, without
1630   *                          the curly braces.
1631   * @param  digestAlgorithm  The name of the message digest algorithm to use
1632   *                          for the password encoder.
1633   * @param  encoderMap       The map to which the encoder will bea added.
1634   */
1635  private static void addUnsaltedEncoder(@NotNull final String schemeName,
1636               @NotNull final String digestAlgorithm,
1637               @NotNull final Map<String,InMemoryPasswordEncoder> encoderMap)
1638  {
1639    try
1640    {
1641      final UnsaltedMessageDigestInMemoryPasswordEncoder encoder =
1642           new UnsaltedMessageDigestInMemoryPasswordEncoder(
1643                '{' + schemeName + '}',
1644                Base64PasswordEncoderOutputFormatter.getInstance(),
1645                MessageDigest.getInstance(digestAlgorithm));
1646      encoderMap.put(StaticUtils.toLowerCase(schemeName), encoder);
1647    }
1648    catch (final Exception e)
1649    {
1650      Debug.debugException(e);
1651    }
1652  }
1653
1654
1655
1656  /**
1657   * Updates the map with a salted password encoder with the provided
1658   * information.
1659   *
1660   * @param  schemeName       The name to use to identify the scheme, without
1661   *                          the curly braces.
1662   * @param  digestAlgorithm  The name of the message digest algorithm to use
1663   *                          for the password encoder.
1664   * @param  encoderMap       The map to which the encoder will bea added.
1665   */
1666  private static void addSaltedEncoder(@NotNull final String schemeName,
1667               @NotNull final String digestAlgorithm,
1668               @NotNull final Map<String,InMemoryPasswordEncoder> encoderMap)
1669  {
1670    try
1671    {
1672      final SaltedMessageDigestInMemoryPasswordEncoder encoder =
1673           new SaltedMessageDigestInMemoryPasswordEncoder(
1674                '{' + schemeName + '}',
1675                Base64PasswordEncoderOutputFormatter.getInstance(),
1676                MessageDigest.getInstance(digestAlgorithm), 8, true, true);
1677      encoderMap.put(StaticUtils.toLowerCase(schemeName), encoder);
1678    }
1679    catch (final Exception e)
1680    {
1681      Debug.debugException(e);
1682    }
1683  }
1684
1685
1686
1687  /**
1688   * Updates the map with a clear-text password encoder with the provided
1689   * information.
1690   *
1691   * @param  schemeName       The name to use to identify the scheme, without
1692   *                          the curly braces.
1693   * @param  outputFormatter  The output formatter to use.  It may be
1694   *                          {@code null} if the output should remain in the
1695   *                          clear.
1696   * @param  encoderMap       The map to which the encoder will bea added.
1697   */
1698  private static void addClearEncoder(@NotNull final String schemeName,
1699               @Nullable final PasswordEncoderOutputFormatter outputFormatter,
1700               @NotNull final Map<String,InMemoryPasswordEncoder> encoderMap)
1701  {
1702    final ClearInMemoryPasswordEncoder encoder =
1703         new ClearInMemoryPasswordEncoder('{' + schemeName + '}',
1704              outputFormatter);
1705    encoderMap.put(StaticUtils.toLowerCase(schemeName), encoder);
1706  }
1707
1708
1709
1710  /**
1711   * {@inheritDoc}
1712   */
1713  @Override()
1714  @NotNull()
1715  public LinkedHashMap<String[],String> getExampleUsages()
1716  {
1717    final LinkedHashMap<String[],String> exampleUsages =
1718         new LinkedHashMap<>(StaticUtils.computeMapCapacity(2));
1719
1720    final String[] example1Args =
1721    {
1722      "--baseDN", "dc=example,dc=com"
1723    };
1724    exampleUsages.put(example1Args, INFO_MEM_DS_TOOL_EXAMPLE_1.get());
1725
1726    final String[] example2Args =
1727    {
1728      "--baseDN", "dc=example,dc=com",
1729      "--port", "1389",
1730      "--ldifFile", "test.ldif",
1731      "--accessLogFile", "access.log",
1732      "--useDefaultSchema"
1733    };
1734    exampleUsages.put(example2Args, INFO_MEM_DS_TOOL_EXAMPLE_2.get());
1735
1736    return exampleUsages;
1737  }
1738
1739
1740
1741  /**
1742   * Retrieves the in-memory directory server instance that has been created by
1743   * this tool.  It will only be valid after the {@link #doToolProcessing()}
1744   * method has been called.
1745   *
1746   * @return  The in-memory directory server instance that has been created by
1747   *          this tool, or {@code null} if the directory server instance has
1748   *          not been successfully created.
1749   */
1750  @Nullable()
1751  public InMemoryDirectoryServer getDirectoryServer()
1752  {
1753    return directoryServer;
1754  }
1755
1756
1757
1758  /**
1759   * {@inheritDoc}
1760   */
1761  @Override()
1762  public void connectionCreationFailure(@Nullable final Socket socket,
1763                                        @NotNull final Throwable cause)
1764  {
1765    wrapErr(0, WRAP_COLUMN,
1766         ERR_MEM_DS_TOOL_ERROR_ACCEPTING_CONNECTION.get(
1767              StaticUtils.getExceptionMessage(cause)));
1768  }
1769
1770
1771
1772  /**
1773   * {@inheritDoc}
1774   */
1775  @Override()
1776  public void connectionTerminated(
1777                   @NotNull final LDAPListenerClientConnection connection,
1778                   @NotNull final LDAPException cause)
1779  {
1780    wrapErr(0, WRAP_COLUMN,
1781         ERR_MEM_DS_TOOL_CONNECTION_TERMINATED_BY_EXCEPTION.get(
1782              StaticUtils.getExceptionMessage(cause)));
1783  }
1784}