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 */ 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>"--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) 187public 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}