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 */ 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.util.ArrayList; 030import java.util.Iterator; 031import java.util.LinkedHashMap; 032import java.util.List; 033import java.util.logging.FileHandler; 034import java.util.logging.Level; 035import java.util.logging.StreamHandler; 036import javax.net.ssl.KeyManager; 037import javax.net.ssl.TrustManager; 038 039import com.unboundid.ldap.sdk.DN; 040import com.unboundid.ldap.sdk.LDAPException; 041import com.unboundid.ldap.sdk.ResultCode; 042import com.unboundid.ldap.sdk.Version; 043import com.unboundid.ldap.sdk.schema.Schema; 044import com.unboundid.util.CommandLineTool; 045import com.unboundid.util.Debug; 046import com.unboundid.util.MinimalLogFormatter; 047import com.unboundid.util.NotMutable; 048import com.unboundid.util.StaticUtils; 049import com.unboundid.util.ThreadSafety; 050import com.unboundid.util.ThreadSafetyLevel; 051import com.unboundid.util.args.ArgumentException; 052import com.unboundid.util.args.ArgumentParser; 053import com.unboundid.util.args.BooleanArgument; 054import com.unboundid.util.args.DNArgument; 055import com.unboundid.util.args.IntegerArgument; 056import com.unboundid.util.args.FileArgument; 057import com.unboundid.util.args.StringArgument; 058import com.unboundid.util.ssl.KeyStoreKeyManager; 059import com.unboundid.util.ssl.SSLUtil; 060import com.unboundid.util.ssl.TrustAllTrustManager; 061import com.unboundid.util.ssl.TrustStoreTrustManager; 062 063import 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) 175public 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}