001 /*
002 * Copyright 2011-2015 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2011-2015 UnboundID Corp.
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 */
021 package com.unboundid.ldap.listener;
022
023
024
025 import java.io.File;
026 import java.io.OutputStream;
027 import java.io.Serializable;
028 import java.net.Socket;
029 import java.util.ArrayList;
030 import java.util.Iterator;
031 import java.util.LinkedHashMap;
032 import java.util.List;
033 import java.util.logging.FileHandler;
034 import java.util.logging.Level;
035 import java.util.logging.StreamHandler;
036 import javax.net.ssl.KeyManager;
037 import javax.net.ssl.TrustManager;
038
039 import com.unboundid.ldap.sdk.DN;
040 import com.unboundid.ldap.sdk.LDAPException;
041 import com.unboundid.ldap.sdk.ResultCode;
042 import com.unboundid.ldap.sdk.Version;
043 import com.unboundid.ldap.sdk.schema.Schema;
044 import com.unboundid.util.CommandLineTool;
045 import com.unboundid.util.Debug;
046 import com.unboundid.util.MinimalLogFormatter;
047 import com.unboundid.util.NotMutable;
048 import com.unboundid.util.StaticUtils;
049 import com.unboundid.util.ThreadSafety;
050 import com.unboundid.util.ThreadSafetyLevel;
051 import com.unboundid.util.args.ArgumentException;
052 import com.unboundid.util.args.ArgumentParser;
053 import com.unboundid.util.args.BooleanArgument;
054 import com.unboundid.util.args.DNArgument;
055 import com.unboundid.util.args.IntegerArgument;
056 import com.unboundid.util.args.FileArgument;
057 import com.unboundid.util.args.StringArgument;
058 import com.unboundid.util.ssl.KeyStoreKeyManager;
059 import com.unboundid.util.ssl.SSLUtil;
060 import com.unboundid.util.ssl.TrustAllTrustManager;
061 import com.unboundid.util.ssl.TrustStoreTrustManager;
062
063 import static com.unboundid.ldap.listener.ListenerMessages.*;
064
065
066
067 /**
068 * This class provides a command-line tool that can be used to run an instance
069 * of the in-memory directory server. Instances of the server may also be
070 * created and controlled programmatically using the
071 * {@link InMemoryDirectoryServer} class.
072 * <BR><BR>
073 * The following command-line arguments may be used with this class:
074 * <UL>
075 * <LI>"-b {baseDN}" or "--baseDN {baseDN}" -- specifies a base DN to use for
076 * the server. At least one base DN must be specified, and multiple
077 * base DNs may be provided as separate arguments.</LI>
078 * <LI>"-p {port}" or "--port {port}" -- specifies the port on which the
079 * server should listen for client connections. If this is not provided,
080 * then a free port will be automatically chosen for use by the
081 * server.</LI>
082 * <LI>"-l {path}" or "--ldifFile {path}" -- specifies the path to an LDIF
083 * file to use to initially populate the server. If this is not provided,
084 * then the server will initially be empty. The LDIF file will not be
085 * updated as operations are processed in the server.</LI>
086 * <LI>"-D {bindDN}" or "--additionalBindDN {bindDN}" -- specifies an
087 * additional DN that can be used to authenticate to the server, even if
088 * there is no account for that user. If this is provided, then the
089 * --additionalBindPassword argument must also be given.</LI>
090 * <LI>"-w {password}" or "--additionalBindPassword {password}" -- specifies
091 * the password that should be used when attempting to bind as the user
092 * specified with the "-additionalBindDN" argument. If this is provided,
093 * then the --additionalBindDN argument must also be given.</LI>
094 * <LI>"-c {count}" or "--maxChangeLogEntries {count}" -- Indicates whether an
095 * LDAP changelog should be enabled, and if so how many changelog records
096 * should be maintained. If this argument is not provided, or if it is
097 * provided with a value of zero, then no changelog will be
098 * maintained.</LI>
099 * <LI>"-A" or "--accessLogToStandardOut" -- indicates that access log
100 * information should be written to standard output. This cannot be
101 * provided in conjunction with the "--accessLogFile" argument. If
102 * that should be used as a server access log. This cannot be provided in
103 * neither argument is provided, then no access logging will be
104 * performed</LI>
105 * <LI>"-a {path}" or "--accessLogFile {path}" -- specifies the path to a file
106 * that should be used as a server access log. This cannot be provided in
107 * conjunction with the "--accessLogToStandardOut" argument. If neither
108 * argument is provided, then no access logging will be performed</LI>
109 * <LI>"--ldapDebugLogToStandardOut" -- Indicates that LDAP debug log
110 * information should be written to standard output. This cannot be
111 * provided in conjunction with the "--ldapDebugLogFile" argument. If
112 * neither argument is provided, then no debug logging will be
113 * performed.</LI>
114 * <LI>"-d {path}" or "--ldapDebugLogFile {path}" -- specifies the path to a
115 * file that should be used as a server LDAP debug log. This cannot be
116 * provided in conjunction with the "--ldapDebugLogToStandardOut"
117 * argument. If neither argument is provided, then no debug logging will
118 * be performed.</LI>
119 * <LI>"-s" or "--useDefaultSchema" -- Indicates that the server should use
120 * the default standard schema provided as part of the LDAP SDK. If
121 * neither this argument nor the "--useSchemaFile" argument is provided,
122 * then the server will not perform any schema validation.</LI>
123 * <LI>"-S {path}" or "--useSchemaFile {path}" -- specifies the path to a file
124 * or directory containing schema definitions to use for the server. If
125 * neither this argument nor the "--useDefaultSchema" argument is
126 * provided, then the server will not perform any schema validation. If
127 * the specified path represents a file, then it must be an LDIF file
128 * containing a valid LDAP subschema subentry. If the path is a
129 * directory, then its files will be processed in lexicographic order by
130 * name.</LI>
131 * <LI>"-I {attr}" or "--equalityIndex {attr}" -- specifies that an equality
132 * index should be maintained for the specified attribute. The equality
133 * index may be used to speed up certain kinds of searches, although it
134 * will cause the server to consume more memory.</LI>
135 * <LI>"-Z" or "--useSSL" -- indicates that the server should encrypt all
136 * communication using SSL. If this is provided, then the
137 * "--keyStorePath" and "--keyStorePassword" arguments must also be
138 * provided, and the "--useStartTLS" argument must not be provided.</LI>
139 * <LI>"-q" or "--useStartTLS" -- indicates that the server should support the
140 * use of the StartTLS extended request. If this is provided, then the
141 * "--keyStorePath" and "--keyStorePassword" arguments must also be
142 * provided, and the "--useSSL" argument must not be provided.</LI>
143 * <LI>"-K {path}" or "--keyStorePath {path}" -- specifies the path to the JKS
144 * key store file that should be used to obtain the server certificate to
145 * use for SSL communication. If this argument is provided, then the
146 * "--keyStorePassword" argument must also be provided, along with exactly
147 * one of the "--useSSL" or "--useStartTLS" arguments.</LI>
148 * <LI>"-W {password}" or "--keyStorePassword {password}" -- specifies the
149 * password that should be used to access the contents of the SSL key
150 * store. If this argument is provided, then the "--keyStorePath"
151 * argument must also be provided, along with exactly one of the
152 * "--useSSL" or "--useStartTLS" arguments.</LI>
153 * <LI>"--keyStoreType {type}" -- specifies the type of keystore represented
154 * by the file specified by the keystore path. If this argument is
155 * provided, then the "--keyStorePath" argument must also be provided,
156 * along with exactly one of the "--useSSL" or "--useStartTLS" arguments.
157 * If this argument is not provided, then a default key store type of
158 * "JKS" will be assumed.</LI>
159 * <LI>"-P {path}" or "--trustStorePath {path}" -- specifies the path to the
160 * JKS trust store file that should be used to determine whether to trust
161 * any SSL certificates that may be presented by the client. If this
162 * argument is provided, then exactly one of the "--useSSL" or
163 * "--useStartTLS" arguments must also be provided. If this argument is
164 * not provided but SSL or StartTLS is to be used, then all client
165 * certificates will be automatically trusted.</LI>
166 * <LI>"-T {password}" or "--trustStorePassword {password}" -- specifies the
167 * password that should be used to access the contents of the SSL trust
168 * store. If this argument is provided, then the "--trustStorePath"
169 * argument must also be provided, along with exactly one of the
170 * "--useSSL" or "--useStartTLS" arguments. If an SSL trust store path
171 * was provided without a trust store password, then the server will
172 * attempt to use the trust store without a password.</LI>
173 * <LI>"--trustStoreType {type}" -- specifies the type of trust store
174 * represented by the file specified by the trust store path. If this
175 * argument is provided, then the "--trustStorePath" argument must also
176 * be provided, along with exactly one of the "--useSSL" or
177 * "--useStartTLS" arguments. If this argument is not provided, then a
178 * default trust store type of "JKS" will be assumed.</LI>
179 * <LI>"--vendorName {name}" -- specifies the vendor name value to appear in
180 * the server root DSE.</LI>
181 * <LI>"--vendorVersion {version}" -- specifies the vendor version value to
182 * appear in the server root DSE.</LI>
183 * </UL>
184 */
185 @NotMutable()
186 @ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
187 public final class InMemoryDirectoryServerTool
188 extends CommandLineTool
189 implements Serializable, LDAPListenerExceptionHandler
190 {
191 /**
192 * The serial version UID for this serializable class.
193 */
194 private static final long serialVersionUID = 6484637038039050412L;
195
196
197
198 // The argument used to indicate that access log information should be written
199 // to standard output.
200 private BooleanArgument accessLogToStandardOutArgument;
201
202 // The argument used to prevent the in-memory server from starting. This is
203 // only intended to be used for internal testing purposes.
204 private BooleanArgument dontStartArgument;
205
206 // The argument used to indicate that LDAP debug log information should be
207 // written to standard output.
208 private BooleanArgument ldapDebugLogToStandardOutArgument;
209
210 // The argument used to indicate that the default standard schema should be
211 // used.
212 private BooleanArgument useDefaultSchemaArgument;
213
214 // The argument used to indicate that the server should use SSL
215 private BooleanArgument useSSLArgument;
216
217 // The argument used to indicate that the server should support the StartTLS
218 // extended operation
219 private BooleanArgument useStartTLSArgument;
220
221 // The argument used to specify an additional bind DN to use for the server.
222 private DNArgument additionalBindDNArgument;
223
224 // The argument used to specify the base DNs to use for the server.
225 private DNArgument baseDNArgument;
226
227 // The argument used to specify the path to an access log file to which
228 // information should be written about operations processed by the server.
229 private FileArgument accessLogFileArgument;
230
231 // The argument used to specify the path to the SSL key store file.
232 private FileArgument keyStorePathArgument;
233
234 // The argument used to specify the path to an LDAP debug log file to which
235 // information should be written about detailed LDAP communication performed
236 // by the server.
237 private FileArgument ldapDebugLogFileArgument;
238
239 // The argument used to specify the path to an LDIF file with data to use to
240 // initially populate the server.
241 private FileArgument ldifFileArgument;
242
243 // The argument used to specify the path to the SSL trust store file.
244 private FileArgument trustStorePathArgument;
245
246 // The argument used to specify the path to a directory containing schema
247 // definitions.
248 private FileArgument useSchemaFileArgument;
249
250 // The in-memory directory server instance that has been created by this tool.
251 private InMemoryDirectoryServer directoryServer;
252
253 // The argument used to specify the maximum number of changelog entries that
254 // the server should maintain.
255 private IntegerArgument maxChangeLogEntriesArgument;
256
257 // The argument used to specify the port on which the server should listen.
258 private IntegerArgument portArgument;
259
260 // The argument used to specify the password for the additional bind DN.
261 private StringArgument additionalBindPasswordArgument;
262
263 // The argument used to specify the attributes for which to maintain equality
264 // indexes.
265 private StringArgument equalityIndexArgument;
266
267 // The argument used to specify the password to use to access the contents of
268 // the SSL key store
269 private StringArgument keyStorePasswordArgument;
270
271 // The argument used to specify the key store type.
272 private StringArgument keyStoreTypeArgument;
273
274 // The argument used to specify the password to use to access the contents of
275 // the SSL trust store
276 private StringArgument trustStorePasswordArgument;
277
278 // The argument used to specify the trust store type.
279 private StringArgument trustStoreTypeArgument;
280
281 // The argument used to specify the server vendor name.
282 private StringArgument vendorNameArgument;
283
284 // The argument used to specify the server vendor veresion.
285 private StringArgument vendorVersionArgument;
286
287
288
289 /**
290 * Parse the provided command line arguments and uses them to start the
291 * directory server.
292 *
293 * @param args The command line arguments provided to this program.
294 */
295 public static void main(final String... args)
296 {
297 final ResultCode resultCode = main(args, System.out, System.err);
298 if (resultCode != ResultCode.SUCCESS)
299 {
300 System.exit(resultCode.intValue());
301 }
302 }
303
304
305
306 /**
307 * Parse the provided command line arguments and uses them to start the
308 * directory server.
309 *
310 * @param outStream The output stream to which standard out should be
311 * written. It may be {@code null} if output should be
312 * suppressed.
313 * @param errStream The output stream to which standard error should be
314 * written. It may be {@code null} if error messages
315 * should be suppressed.
316 * @param args The command line arguments provided to this program.
317 *
318 * @return A result code indicating whether the processing was successful.
319 */
320 public static ResultCode main(final String[] args,
321 final OutputStream outStream,
322 final OutputStream errStream)
323 {
324 final InMemoryDirectoryServerTool tool =
325 new InMemoryDirectoryServerTool(outStream, errStream);
326 return tool.runTool(args);
327 }
328
329
330
331 /**
332 * Creates a new instance of this tool that use the provided output streams
333 * for standard output and standard error.
334 *
335 * @param outStream The output stream to use for standard output. It may be
336 * {@code System.out} for the JVM's default standard output
337 * stream, {@code null} if no output should be generated,
338 * or a custom output stream if the output should be sent
339 * to an alternate location.
340 * @param errStream The output stream to use for standard error. It may be
341 * {@code System.err} for the JVM's default standard error
342 * stream, {@code null} if no output should be generated,
343 * or a custom output stream if the output should be sent
344 * to an alternate location.
345 */
346 public InMemoryDirectoryServerTool(final OutputStream outStream,
347 final OutputStream errStream)
348 {
349 super(outStream, errStream);
350
351 directoryServer = null;
352 dontStartArgument = null;
353 useDefaultSchemaArgument = null;
354 useSSLArgument = null;
355 useStartTLSArgument = null;
356 additionalBindDNArgument = null;
357 baseDNArgument = null;
358 accessLogToStandardOutArgument = null;
359 accessLogFileArgument = null;
360 keyStorePathArgument = null;
361 ldapDebugLogToStandardOutArgument = null;
362 ldapDebugLogFileArgument = null;
363 ldifFileArgument = null;
364 trustStorePathArgument = null;
365 useSchemaFileArgument = null;
366 maxChangeLogEntriesArgument = null;
367 portArgument = null;
368 additionalBindPasswordArgument = null;
369 equalityIndexArgument = null;
370 keyStorePasswordArgument = null;
371 keyStoreTypeArgument = null;
372 trustStorePasswordArgument = null;
373 trustStoreTypeArgument = null;
374 vendorNameArgument = null;
375 vendorVersionArgument = null;
376 }
377
378
379
380 /**
381 * {@inheritDoc}
382 */
383 @Override()
384 public String getToolName()
385 {
386 return "in-memory-directory-server";
387 }
388
389
390
391 /**
392 * {@inheritDoc}
393 */
394 @Override()
395 public String getToolDescription()
396 {
397 return INFO_MEM_DS_TOOL_DESC.get(InMemoryDirectoryServer.class.getName());
398 }
399
400
401
402 /**
403 * Retrieves the version string for this tool.
404 *
405 * @return The version string for this tool.
406 */
407 @Override()
408 public String getToolVersion()
409 {
410 return Version.NUMERIC_VERSION_STRING;
411 }
412
413
414
415 /**
416 * {@inheritDoc}
417 */
418 @Override()
419 public void addToolArguments(final ArgumentParser parser)
420 throws ArgumentException
421 {
422 baseDNArgument = new DNArgument('b', "baseDN", true, 0,
423 INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_BASE_DN.get(),
424 INFO_MEM_DS_TOOL_ARG_DESC_BASE_DN.get());
425 parser.addArgument(baseDNArgument);
426
427 portArgument = new IntegerArgument('p', "port", false, 1,
428 INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_PORT.get(),
429 INFO_MEM_DS_TOOL_ARG_DESC_PORT.get(), 0, 65535);
430 parser.addArgument(portArgument);
431
432 ldifFileArgument = new FileArgument('l', "ldifFile", false, 1,
433 INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_PATH.get(),
434 INFO_MEM_DS_TOOL_ARG_DESC_LDIF_FILE.get(), true, true, true, false);
435 parser.addArgument(ldifFileArgument);
436
437 additionalBindDNArgument = new DNArgument('D', "additionalBindDN", false, 1,
438 INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_BIND_DN.get(),
439 INFO_MEM_DS_TOOL_ARG_DESC_ADDITIONAL_BIND_DN.get());
440 parser.addArgument(additionalBindDNArgument);
441
442 additionalBindPasswordArgument = new StringArgument('w',
443 "additionalBindPassword", false, 1,
444 INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_PASSWORD.get(),
445 INFO_MEM_DS_TOOL_ARG_DESC_ADDITIONAL_BIND_PW.get());
446 parser.addArgument(additionalBindPasswordArgument);
447
448 maxChangeLogEntriesArgument = new IntegerArgument('c',
449 "maxChangeLogEntries", false, 1,
450 INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_COUNT.get(),
451 INFO_MEM_DS_TOOL_ARG_DESC_MAX_CHANGELOG_ENTRIES.get(), 0,
452 Integer.MAX_VALUE, 0);
453 parser.addArgument(maxChangeLogEntriesArgument);
454
455 accessLogToStandardOutArgument = new BooleanArgument('A',
456 "accessLogToStandardOut",
457 INFO_MEM_DS_TOOL_ARG_DESC_ACCESS_LOG_TO_STDOUT.get());
458 parser.addArgument(accessLogToStandardOutArgument);
459
460 accessLogFileArgument = new FileArgument('a', "accessLogFile", false, 1,
461 INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_PATH.get(),
462 INFO_MEM_DS_TOOL_ARG_DESC_ACCESS_LOG_FILE.get(), false, true, true,
463 false);
464 parser.addArgument(accessLogFileArgument);
465
466 ldapDebugLogToStandardOutArgument = new BooleanArgument(null,
467 "ldapDebugLogToStandardOut",
468 INFO_MEM_DS_TOOL_ARG_DESC_LDAP_DEBUG_LOG_TO_STDOUT.get());
469 parser.addArgument(ldapDebugLogToStandardOutArgument);
470
471 ldapDebugLogFileArgument = new FileArgument('d', "ldapDebugLogFile", false,
472 1, INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_PATH.get(),
473 INFO_MEM_DS_TOOL_ARG_DESC_LDAP_DEBUG_LOG_FILE.get(), false, true, true,
474 false);
475 parser.addArgument(ldapDebugLogFileArgument);
476
477 useDefaultSchemaArgument = new BooleanArgument('s', "useDefaultSchema",
478 INFO_MEM_DS_TOOL_ARG_DESC_USE_DEFAULT_SCHEMA.get());
479 parser.addArgument(useDefaultSchemaArgument);
480
481 useSchemaFileArgument = new FileArgument('S', "useSchemaFile", false, 0,
482 INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_PATH.get(),
483 INFO_MEM_DS_TOOL_ARG_DESC_USE_SCHEMA_FILE.get(), true, true, false,
484 false);
485 parser.addArgument(useSchemaFileArgument);
486
487 equalityIndexArgument = new StringArgument('I', "equalityIndex", false, 0,
488 INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_ATTR.get(),
489 INFO_MEM_DS_TOOL_ARG_DESC_EQ_INDEX.get());
490 parser.addArgument(equalityIndexArgument);
491
492 useSSLArgument = new BooleanArgument('Z', "useSSL",
493 INFO_MEM_DS_TOOL_ARG_DESC_USE_SSL.get());
494 parser.addArgument(useSSLArgument);
495
496 useStartTLSArgument = new BooleanArgument('q', "useStartTLS",
497 INFO_MEM_DS_TOOL_ARG_DESC_USE_START_TLS.get());
498 parser.addArgument(useStartTLSArgument);
499
500 keyStorePathArgument = new FileArgument('K', "keyStorePath", false, 1,
501 INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_PATH.get(),
502 INFO_MEM_DS_TOOL_ARG_DESC_KEY_STORE_PATH.get(), true, true, true,
503 false);
504 parser.addArgument(keyStorePathArgument);
505
506 keyStorePasswordArgument = new StringArgument('W', "keyStorePassword",
507 false, 1, INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_PASSWORD.get(),
508 INFO_MEM_DS_TOOL_ARG_DESC_KEY_STORE_PW.get());
509 parser.addArgument(keyStorePasswordArgument);
510
511 keyStoreTypeArgument = new StringArgument(null, "keyStoreType",
512 false, 1, "{type}", "The keystore type.", "JKS");
513 parser.addArgument(keyStoreTypeArgument);
514
515 trustStorePathArgument = new FileArgument('P', "trustStorePath", false, 1,
516 INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_PATH.get(),
517 INFO_MEM_DS_TOOL_ARG_DESC_TRUST_STORE_PATH.get(), true, true, true,
518 false);
519 parser.addArgument(trustStorePathArgument);
520
521 trustStorePasswordArgument = new StringArgument('T', "trustStorePassword",
522 false, 1, INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_PASSWORD.get(),
523 INFO_MEM_DS_TOOL_ARG_DESC_TRUST_STORE_PW.get());
524 parser.addArgument(trustStorePasswordArgument);
525
526 trustStoreTypeArgument = new StringArgument(null, "trustStoreType",
527 false, 1, "{type}", "The trust store type.", "JKS");
528 parser.addArgument(trustStoreTypeArgument);
529
530 vendorNameArgument = new StringArgument(null, "vendorName", false, 1,
531 INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_VALUE.get(),
532 INFO_MEM_DS_TOOL_ARG_DESC_VENDOR_NAME.get());
533 parser.addArgument(vendorNameArgument);
534
535 vendorVersionArgument = new StringArgument(null, "vendorVersion", false, 1,
536 INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_VALUE.get(),
537 INFO_MEM_DS_TOOL_ARG_DESC_VENDOR_VERSION.get());
538 parser.addArgument(vendorVersionArgument);
539
540 dontStartArgument = new BooleanArgument(null, "dontStart",
541 INFO_MEM_DS_TOOL_ARG_DESC_DONT_START.get());
542 dontStartArgument.setHidden(true);
543 parser.addArgument(dontStartArgument);
544
545 parser.addExclusiveArgumentSet(useDefaultSchemaArgument,
546 useSchemaFileArgument);
547 parser.addExclusiveArgumentSet(useSSLArgument, useStartTLSArgument);
548
549 parser.addExclusiveArgumentSet(accessLogToStandardOutArgument,
550 accessLogFileArgument);
551 parser.addExclusiveArgumentSet(ldapDebugLogToStandardOutArgument,
552 ldapDebugLogFileArgument);
553
554 parser.addDependentArgumentSet(additionalBindDNArgument,
555 additionalBindPasswordArgument);
556 parser.addDependentArgumentSet(additionalBindPasswordArgument,
557 additionalBindDNArgument);
558
559 parser.addDependentArgumentSet(useSSLArgument, keyStorePathArgument);
560 parser.addDependentArgumentSet(useSSLArgument, keyStorePasswordArgument);
561 parser.addDependentArgumentSet(useStartTLSArgument, keyStorePathArgument);
562 parser.addDependentArgumentSet(useStartTLSArgument,
563 keyStorePasswordArgument);
564 parser.addDependentArgumentSet(keyStorePathArgument, useSSLArgument,
565 useStartTLSArgument);
566 parser.addDependentArgumentSet(keyStorePasswordArgument, useSSLArgument,
567 useStartTLSArgument);
568 parser.addDependentArgumentSet(trustStorePathArgument, useSSLArgument,
569 useStartTLSArgument);
570 parser.addDependentArgumentSet(trustStorePasswordArgument,
571 trustStorePathArgument);
572 }
573
574
575
576 /**
577 * {@inheritDoc}
578 */
579 @Override()
580 public ResultCode doToolProcessing()
581 {
582 // Create a base configuration.
583 final InMemoryDirectoryServerConfig serverConfig;
584 try
585 {
586 serverConfig = getConfig();
587 }
588 catch (final LDAPException le)
589 {
590 Debug.debugException(le);
591 err(ERR_MEM_DS_TOOL_ERROR_INITIALIZING_CONFIG.get(le.getMessage()));
592 return le.getResultCode();
593 }
594
595
596 // Create the server instance using the provided configuration, but don't
597 // start it yet.
598 try
599 {
600 directoryServer = new InMemoryDirectoryServer(serverConfig);
601 }
602 catch (final LDAPException le)
603 {
604 Debug.debugException(le);
605 err(ERR_MEM_DS_TOOL_ERROR_CREATING_SERVER_INSTANCE.get(le.getMessage()));
606 return le.getResultCode();
607 }
608
609
610 // If an LDIF file was provided, then use it to populate the server.
611 if (ldifFileArgument.isPresent())
612 {
613 final File ldifFile = ldifFileArgument.getValue();
614 try
615 {
616 final int numEntries = directoryServer.importFromLDIF(true,
617 ldifFile.getAbsolutePath());
618 out(INFO_MEM_DS_TOOL_ADDED_ENTRIES_FROM_LDIF.get(numEntries,
619 ldifFile.getAbsolutePath()));
620 }
621 catch (final LDAPException le)
622 {
623 Debug.debugException(le);
624 err(ERR_MEM_DS_TOOL_ERROR_POPULATING_SERVER_INSTANCE.get(
625 ldifFile.getAbsolutePath(), le.getMessage()));
626 return le.getResultCode();
627 }
628 }
629
630
631 // Start the server.
632 try
633 {
634 if (! dontStartArgument.isPresent())
635 {
636 directoryServer.startListening();
637 out(INFO_MEM_DS_TOOL_LISTENING.get(directoryServer.getListenPort()));
638 }
639 }
640 catch (final Exception e)
641 {
642 Debug.debugException(e);
643 err(ERR_MEM_DS_TOOL_ERROR_STARTING_SERVER.get(
644 StaticUtils.getExceptionMessage(e)));
645 return ResultCode.LOCAL_ERROR;
646 }
647
648 return ResultCode.SUCCESS;
649 }
650
651
652
653 /**
654 * Creates a server configuration based on information provided with
655 * command line arguments.
656 *
657 * @return The configuration that was created.
658 *
659 * @throws LDAPException If a problem is encountered while creating the
660 * configuration.
661 */
662 private InMemoryDirectoryServerConfig getConfig()
663 throws LDAPException
664 {
665 final List<DN> dnList = baseDNArgument.getValues();
666 final DN[] baseDNs = new DN[dnList.size()];
667 dnList.toArray(baseDNs);
668
669 final InMemoryDirectoryServerConfig serverConfig =
670 new InMemoryDirectoryServerConfig(baseDNs);
671
672
673 // If a listen port was specified, then update the configuration to use it.
674 int listenPort = 0;
675 if (portArgument.isPresent())
676 {
677 listenPort = portArgument.getValue();
678 }
679
680
681 // If schema should be used, then get it.
682 if (useDefaultSchemaArgument.isPresent())
683 {
684 serverConfig.setSchema(Schema.getDefaultStandardSchema());
685 }
686 else if (useSchemaFileArgument.isPresent())
687 {
688 final ArrayList<File> schemaFiles = new ArrayList<File>(10);
689 for (final File f : useSchemaFileArgument.getValues())
690 {
691 if (f.exists())
692 {
693 if (f.isFile())
694 {
695 schemaFiles.add(f);
696 }
697 else
698 {
699 for (final File subFile : f.listFiles())
700 {
701 if (subFile.isFile())
702 {
703 schemaFiles.add(subFile);
704 }
705 }
706 }
707 }
708 else
709 {
710 throw new LDAPException(ResultCode.PARAM_ERROR,
711 ERR_MEM_DS_TOOL_NO_SUCH_SCHEMA_FILE.get(f.getAbsolutePath()));
712 }
713 }
714
715 try
716 {
717 serverConfig.setSchema(Schema.getSchema(schemaFiles));
718 }
719 catch (final Exception e)
720 {
721 Debug.debugException(e);
722
723 final StringBuilder fileList = new StringBuilder();
724 final Iterator<File> fileIterator = schemaFiles.iterator();
725 while (fileIterator.hasNext())
726 {
727 fileList.append(fileIterator.next().getAbsolutePath());
728 if (fileIterator.hasNext())
729 {
730 fileList.append(", ");
731 }
732 }
733
734 throw new LDAPException(ResultCode.LOCAL_ERROR,
735 ERR_MEM_DS_TOOL_ERROR_READING_SCHEMA.get(
736 fileList, StaticUtils.getExceptionMessage(e)),
737 e);
738 }
739 }
740 else
741 {
742 serverConfig.setSchema(null);
743 }
744
745
746 // If an additional bind DN and password are provided, then include them in
747 // the configuration.
748 if (additionalBindDNArgument.isPresent())
749 {
750 serverConfig.addAdditionalBindCredentials(
751 additionalBindDNArgument.getValue().toString(),
752 additionalBindPasswordArgument.getValue());
753 }
754
755
756 // If a maximum number of changelog entries was specified, then update the
757 // configuration with that.
758 if (maxChangeLogEntriesArgument.isPresent())
759 {
760 serverConfig.setMaxChangeLogEntries(
761 maxChangeLogEntriesArgument.getValue());
762 }
763
764
765 // If an access log file was specified, then create the appropriate log
766 // handler.
767 if (accessLogToStandardOutArgument.isPresent())
768 {
769 final StreamHandler handler = new StreamHandler(System.out,
770 new MinimalLogFormatter(null, false, false, true));
771 handler.setLevel(Level.INFO);
772 serverConfig.setAccessLogHandler(handler);
773 }
774 else if (accessLogFileArgument.isPresent())
775 {
776 final File logFile = accessLogFileArgument.getValue();
777 try
778 {
779 final FileHandler handler =
780 new FileHandler(logFile.getAbsolutePath(), true);
781 handler.setLevel(Level.INFO);
782 handler.setFormatter(new MinimalLogFormatter(null, false, false,
783 true));
784 serverConfig.setAccessLogHandler(handler);
785 }
786 catch (final Exception e)
787 {
788 Debug.debugException(e);
789 throw new LDAPException(ResultCode.LOCAL_ERROR,
790 ERR_MEM_DS_TOOL_ERROR_CREATING_LOG_HANDLER.get(
791 logFile.getAbsolutePath(),
792 StaticUtils.getExceptionMessage(e)),
793 e);
794 }
795 }
796
797
798 // If an LDAP debug log file was specified, then create the appropriate log
799 // handler.
800 if (ldapDebugLogToStandardOutArgument.isPresent())
801 {
802 final StreamHandler handler = new StreamHandler(System.out,
803 new MinimalLogFormatter(null, false, false, true));
804 handler.setLevel(Level.INFO);
805 serverConfig.setLDAPDebugLogHandler(handler);
806 }
807 else if (ldapDebugLogFileArgument.isPresent())
808 {
809 final File logFile = ldapDebugLogFileArgument.getValue();
810 try
811 {
812 final FileHandler handler =
813 new FileHandler(logFile.getAbsolutePath(), true);
814 handler.setLevel(Level.INFO);
815 handler.setFormatter(new MinimalLogFormatter(null, false, false,
816 true));
817 serverConfig.setLDAPDebugLogHandler(handler);
818 }
819 catch (final Exception e)
820 {
821 Debug.debugException(e);
822 throw new LDAPException(ResultCode.LOCAL_ERROR,
823 ERR_MEM_DS_TOOL_ERROR_CREATING_LOG_HANDLER.get(
824 logFile.getAbsolutePath(),
825 StaticUtils.getExceptionMessage(e)),
826 e);
827 }
828 }
829
830
831 // If SSL is to be used, then create the corresponding socket factories.
832 if (useSSLArgument.isPresent() || useStartTLSArgument.isPresent())
833 {
834 try
835 {
836 final KeyManager keyManager = new KeyStoreKeyManager(
837 keyStorePathArgument.getValue(),
838 keyStorePasswordArgument.getValue().toCharArray(),
839 keyStoreTypeArgument.getValue(), null);
840
841 final TrustManager trustManager;
842 if (trustStorePathArgument.isPresent())
843 {
844 final char[] password;
845 if (trustStorePasswordArgument.isPresent())
846 {
847 password = trustStorePasswordArgument.getValue().toCharArray();
848 }
849 else
850 {
851 password = null;
852 }
853
854 trustManager = new TrustStoreTrustManager(
855 trustStorePathArgument.getValue(), password,
856 trustStoreTypeArgument.getValue(), true);
857 }
858 else
859 {
860 trustManager = new TrustAllTrustManager();
861 }
862
863 final SSLUtil serverSSLUtil = new SSLUtil(keyManager, trustManager);
864
865 if (useSSLArgument.isPresent())
866 {
867 final SSLUtil clientSSLUtil = new SSLUtil(new TrustAllTrustManager());
868 serverConfig.setListenerConfigs(
869 InMemoryListenerConfig.createLDAPSConfig("LDAPS", null,
870 listenPort, serverSSLUtil.createSSLServerSocketFactory(),
871 clientSSLUtil.createSSLSocketFactory()));
872 }
873 else
874 {
875 serverConfig.setListenerConfigs(
876 InMemoryListenerConfig.createLDAPConfig("LDAP+StartTLS", null,
877 listenPort, serverSSLUtil.createSSLSocketFactory()));
878 }
879 }
880 catch (final Exception e)
881 {
882 Debug.debugException(e);
883 throw new LDAPException(ResultCode.LOCAL_ERROR,
884 ERR_MEM_DS_TOOL_ERROR_INITIALIZING_SSL.get(
885 StaticUtils.getExceptionMessage(e)),
886 e);
887 }
888 }
889 else
890 {
891 serverConfig.setListenerConfigs(InMemoryListenerConfig.createLDAPConfig(
892 "LDAP", listenPort));
893 }
894
895
896 // If vendor name and/or vendor version values were provided, then configure
897 // them for use.
898 if (vendorNameArgument.isPresent())
899 {
900 serverConfig.setVendorName(vendorNameArgument.getValue());
901 }
902
903 if (vendorVersionArgument.isPresent())
904 {
905 serverConfig.setVendorVersion(vendorVersionArgument.getValue());
906 }
907
908
909 // If equality indexing is to be performed, then configure it.
910 if (equalityIndexArgument.isPresent())
911 {
912 serverConfig.setEqualityIndexAttributes(
913 equalityIndexArgument.getValues());
914 }
915
916 return serverConfig;
917 }
918
919
920
921 /**
922 * {@inheritDoc}
923 */
924 @Override()
925 public LinkedHashMap<String[],String> getExampleUsages()
926 {
927 final LinkedHashMap<String[],String> exampleUsages =
928 new LinkedHashMap<String[],String>(2);
929
930 final String[] example1Args =
931 {
932 "--baseDN", "dc=example,dc=com"
933 };
934 exampleUsages.put(example1Args, INFO_MEM_DS_TOOL_EXAMPLE_1.get());
935
936 final String[] example2Args =
937 {
938 "--baseDN", "dc=example,dc=com",
939 "--port", "1389",
940 "--ldifFile", "test.ldif",
941 "--accessLogFile", "access.log",
942 "--useDefaultSchema"
943 };
944 exampleUsages.put(example2Args, INFO_MEM_DS_TOOL_EXAMPLE_2.get());
945
946 return exampleUsages;
947 }
948
949
950
951 /**
952 * Retrieves the in-memory directory server instance that has been created by
953 * this tool. It will only be valid after the {@link #doToolProcessing()}
954 * method has been called.
955 *
956 * @return The in-memory directory server instance that has been created by
957 * this tool, or {@code null} if the directory server instance has
958 * not been successfully created.
959 */
960 public InMemoryDirectoryServer getDirectoryServer()
961 {
962 return directoryServer;
963 }
964
965
966
967 /**
968 * {@inheritDoc}
969 */
970 public void connectionCreationFailure(final Socket socket,
971 final Throwable cause)
972 {
973 err(ERR_MEM_DS_TOOL_ERROR_ACCEPTING_CONNECTION.get(
974 StaticUtils.getExceptionMessage(cause)));
975 }
976
977
978
979 /**
980 * {@inheritDoc}
981 */
982 public void connectionTerminated(
983 final LDAPListenerClientConnection connection,
984 final LDAPException cause)
985 {
986 err(ERR_MEM_DS_TOOL_CONNECTION_TERMINATED_BY_EXCEPTION.get(
987 StaticUtils.getExceptionMessage(cause)));
988 }
989 }