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