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