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