001/* 002 * Copyright 2011-2017 UnboundID Corp. 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2011-2017 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 code log file to use, if any. 232 private FileArgument codeLogFile; 233 234 // The argument used to specify the path to the SSL key store file. 235 private FileArgument keyStorePathArgument; 236 237 // The argument used to specify the path to an LDAP debug log file to which 238 // information should be written about detailed LDAP communication performed 239 // by the server. 240 private FileArgument ldapDebugLogFileArgument; 241 242 // The argument used to specify the path to an LDIF file with data to use to 243 // initially populate the server. 244 private FileArgument ldifFileArgument; 245 246 // The argument used to specify the path to the SSL trust store file. 247 private FileArgument trustStorePathArgument; 248 249 // The argument used to specify the path to a directory containing schema 250 // definitions. 251 private FileArgument useSchemaFileArgument; 252 253 // The in-memory directory server instance that has been created by this tool. 254 private InMemoryDirectoryServer directoryServer; 255 256 // The argument used to specify the maximum number of changelog entries that 257 // the server should maintain. 258 private IntegerArgument maxChangeLogEntriesArgument; 259 260 // The argument used to specify the port on which the server should listen. 261 private IntegerArgument portArgument; 262 263 // The argument used to specify the password for the additional bind DN. 264 private StringArgument additionalBindPasswordArgument; 265 266 // The argument used to specify the attributes for which to maintain equality 267 // indexes. 268 private StringArgument equalityIndexArgument; 269 270 // The argument used to specify the password to use to access the contents of 271 // the SSL key store 272 private StringArgument keyStorePasswordArgument; 273 274 // The argument used to specify the key store type. 275 private StringArgument keyStoreTypeArgument; 276 277 // The argument used to specify the password to use to access the contents of 278 // the SSL trust store 279 private StringArgument trustStorePasswordArgument; 280 281 // The argument used to specify the trust store type. 282 private StringArgument trustStoreTypeArgument; 283 284 // The argument used to specify the server vendor name. 285 private StringArgument vendorNameArgument; 286 287 // The argument used to specify the server vendor veresion. 288 private StringArgument vendorVersionArgument; 289 290 291 292 /** 293 * Parse the provided command line arguments and uses them to start the 294 * directory server. 295 * 296 * @param args The command line arguments provided to this program. 297 */ 298 public static void main(final String... args) 299 { 300 final ResultCode resultCode = main(args, System.out, System.err); 301 if (resultCode != ResultCode.SUCCESS) 302 { 303 System.exit(resultCode.intValue()); 304 } 305 } 306 307 308 309 /** 310 * Parse the provided command line arguments and uses them to start the 311 * directory server. 312 * 313 * @param outStream The output stream to which standard out should be 314 * written. It may be {@code null} if output should be 315 * suppressed. 316 * @param errStream The output stream to which standard error should be 317 * written. It may be {@code null} if error messages 318 * should be suppressed. 319 * @param args The command line arguments provided to this program. 320 * 321 * @return A result code indicating whether the processing was successful. 322 */ 323 public static ResultCode main(final String[] args, 324 final OutputStream outStream, 325 final OutputStream errStream) 326 { 327 final InMemoryDirectoryServerTool tool = 328 new InMemoryDirectoryServerTool(outStream, errStream); 329 return tool.runTool(args); 330 } 331 332 333 334 /** 335 * Creates a new instance of this tool that use the provided output streams 336 * for standard output and standard error. 337 * 338 * @param outStream The output stream to use for standard output. It may be 339 * {@code System.out} for the JVM's default standard output 340 * stream, {@code null} if no output should be generated, 341 * or a custom output stream if the output should be sent 342 * to an alternate location. 343 * @param errStream The output stream to use for standard error. It may be 344 * {@code System.err} for the JVM's default standard error 345 * stream, {@code null} if no output should be generated, 346 * or a custom output stream if the output should be sent 347 * to an alternate location. 348 */ 349 public InMemoryDirectoryServerTool(final OutputStream outStream, 350 final OutputStream errStream) 351 { 352 super(outStream, errStream); 353 354 directoryServer = null; 355 dontStartArgument = null; 356 useDefaultSchemaArgument = null; 357 useSSLArgument = null; 358 useStartTLSArgument = null; 359 additionalBindDNArgument = null; 360 baseDNArgument = null; 361 accessLogToStandardOutArgument = null; 362 accessLogFileArgument = null; 363 keyStorePathArgument = null; 364 ldapDebugLogToStandardOutArgument = null; 365 ldapDebugLogFileArgument = null; 366 ldifFileArgument = null; 367 trustStorePathArgument = null; 368 useSchemaFileArgument = null; 369 maxChangeLogEntriesArgument = null; 370 portArgument = null; 371 additionalBindPasswordArgument = null; 372 equalityIndexArgument = null; 373 keyStorePasswordArgument = null; 374 keyStoreTypeArgument = null; 375 trustStorePasswordArgument = null; 376 trustStoreTypeArgument = null; 377 vendorNameArgument = null; 378 vendorVersionArgument = null; 379 } 380 381 382 383 /** 384 * {@inheritDoc} 385 */ 386 @Override() 387 public String getToolName() 388 { 389 return "in-memory-directory-server"; 390 } 391 392 393 394 /** 395 * {@inheritDoc} 396 */ 397 @Override() 398 public String getToolDescription() 399 { 400 return INFO_MEM_DS_TOOL_DESC.get(InMemoryDirectoryServer.class.getName()); 401 } 402 403 404 405 /** 406 * Retrieves the version string for this tool. 407 * 408 * @return The version string for this tool. 409 */ 410 @Override() 411 public String getToolVersion() 412 { 413 return Version.NUMERIC_VERSION_STRING; 414 } 415 416 417 418 /** 419 * {@inheritDoc} 420 */ 421 @Override() 422 public void addToolArguments(final ArgumentParser parser) 423 throws ArgumentException 424 { 425 portArgument = new IntegerArgument('p', "port", false, 1, 426 INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_PORT.get(), 427 INFO_MEM_DS_TOOL_ARG_DESC_PORT.get(), 0, 65535); 428 portArgument.setArgumentGroupName( 429 INFO_MEM_DS_TOOL_GROUP_CONNECTIVITY.get()); 430 parser.addArgument(portArgument); 431 432 useSSLArgument = new BooleanArgument('Z', "useSSL", 433 INFO_MEM_DS_TOOL_ARG_DESC_USE_SSL.get()); 434 useSSLArgument.setArgumentGroupName( 435 INFO_MEM_DS_TOOL_GROUP_CONNECTIVITY.get()); 436 useSSLArgument.addLongIdentifier("use-ssl"); 437 parser.addArgument(useSSLArgument); 438 439 useStartTLSArgument = new BooleanArgument('q', "useStartTLS", 440 INFO_MEM_DS_TOOL_ARG_DESC_USE_START_TLS.get()); 441 useStartTLSArgument.setArgumentGroupName( 442 INFO_MEM_DS_TOOL_GROUP_CONNECTIVITY.get()); 443 useStartTLSArgument.addLongIdentifier("use-starttls"); 444 useStartTLSArgument.addLongIdentifier("use-start-tls"); 445 parser.addArgument(useStartTLSArgument); 446 447 keyStorePathArgument = new FileArgument('K', "keyStorePath", false, 1, 448 INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_PATH.get(), 449 INFO_MEM_DS_TOOL_ARG_DESC_KEY_STORE_PATH.get(), true, true, true, 450 false); 451 keyStorePathArgument.setArgumentGroupName( 452 INFO_MEM_DS_TOOL_GROUP_CONNECTIVITY.get()); 453 keyStorePathArgument.addLongIdentifier("key-store-path"); 454 parser.addArgument(keyStorePathArgument); 455 456 keyStorePasswordArgument = new StringArgument('W', "keyStorePassword", 457 false, 1, INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_PASSWORD.get(), 458 INFO_MEM_DS_TOOL_ARG_DESC_KEY_STORE_PW.get()); 459 keyStorePasswordArgument.setSensitive(true); 460 keyStorePasswordArgument.setArgumentGroupName( 461 INFO_MEM_DS_TOOL_GROUP_CONNECTIVITY.get()); 462 keyStorePasswordArgument.addLongIdentifier("keyStorePIN"); 463 keyStorePasswordArgument.addLongIdentifier("key-store-password"); 464 keyStorePasswordArgument.addLongIdentifier("key-store-pin"); 465 parser.addArgument(keyStorePasswordArgument); 466 467 keyStoreTypeArgument = new StringArgument(null, "keyStoreType", 468 false, 1, "{type}", "The keystore type.", "JKS"); 469 keyStoreTypeArgument.setArgumentGroupName( 470 INFO_MEM_DS_TOOL_GROUP_CONNECTIVITY.get()); 471 keyStoreTypeArgument.addLongIdentifier("keyStoreFormat"); 472 keyStoreTypeArgument.addLongIdentifier("key-store-type"); 473 keyStoreTypeArgument.addLongIdentifier("key-store-format"); 474 parser.addArgument(keyStoreTypeArgument); 475 476 trustStorePathArgument = new FileArgument('P', "trustStorePath", false, 1, 477 INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_PATH.get(), 478 INFO_MEM_DS_TOOL_ARG_DESC_TRUST_STORE_PATH.get(), true, true, true, 479 false); 480 trustStorePathArgument.setArgumentGroupName( 481 INFO_MEM_DS_TOOL_GROUP_CONNECTIVITY.get()); 482 trustStorePathArgument.addLongIdentifier("trust-store-path"); 483 parser.addArgument(trustStorePathArgument); 484 485 trustStorePasswordArgument = new StringArgument('T', "trustStorePassword", 486 false, 1, INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_PASSWORD.get(), 487 INFO_MEM_DS_TOOL_ARG_DESC_TRUST_STORE_PW.get()); 488 trustStorePasswordArgument.setSensitive(true); 489 trustStorePasswordArgument.setArgumentGroupName( 490 INFO_MEM_DS_TOOL_GROUP_CONNECTIVITY.get()); 491 trustStorePasswordArgument.addLongIdentifier("trustStorePIN"); 492 trustStorePasswordArgument.addLongIdentifier("trust-store-password"); 493 trustStorePasswordArgument.addLongIdentifier("trust-store-pin"); 494 parser.addArgument(trustStorePasswordArgument); 495 496 trustStoreTypeArgument = new StringArgument(null, "trustStoreType", 497 false, 1, "{type}", "The trust store type.", "JKS"); 498 trustStoreTypeArgument.setArgumentGroupName( 499 INFO_MEM_DS_TOOL_GROUP_CONNECTIVITY.get()); 500 trustStoreTypeArgument.addLongIdentifier("trustStoreFormat"); 501 trustStoreTypeArgument.addLongIdentifier("trust-store-type"); 502 trustStoreTypeArgument.addLongIdentifier("trust-store-format"); 503 parser.addArgument(trustStoreTypeArgument); 504 505 dontStartArgument = new BooleanArgument(null, "dontStart", 506 INFO_MEM_DS_TOOL_ARG_DESC_DONT_START.get()); 507 dontStartArgument.setArgumentGroupName( 508 INFO_MEM_DS_TOOL_GROUP_CONNECTIVITY.get()); 509 dontStartArgument.setHidden(true); 510 dontStartArgument.addLongIdentifier("doNotStart"); 511 dontStartArgument.addLongIdentifier("dont-start"); 512 dontStartArgument.addLongIdentifier("do-not-start"); 513 parser.addArgument(dontStartArgument); 514 515 baseDNArgument = new DNArgument('b', "baseDN", true, 0, 516 INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_BASE_DN.get(), 517 INFO_MEM_DS_TOOL_ARG_DESC_BASE_DN.get()); 518 baseDNArgument.setArgumentGroupName(INFO_MEM_DS_TOOL_GROUP_DATA.get()); 519 baseDNArgument.addLongIdentifier("base-dn"); 520 parser.addArgument(baseDNArgument); 521 522 ldifFileArgument = new FileArgument('l', "ldifFile", false, 1, 523 INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_PATH.get(), 524 INFO_MEM_DS_TOOL_ARG_DESC_LDIF_FILE.get(), true, true, true, false); 525 ldifFileArgument.setArgumentGroupName(INFO_MEM_DS_TOOL_GROUP_DATA.get()); 526 ldifFileArgument.addLongIdentifier("ldif-file"); 527 parser.addArgument(ldifFileArgument); 528 529 additionalBindDNArgument = new DNArgument('D', "additionalBindDN", false, 1, 530 INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_BIND_DN.get(), 531 INFO_MEM_DS_TOOL_ARG_DESC_ADDITIONAL_BIND_DN.get()); 532 additionalBindDNArgument.setArgumentGroupName( 533 INFO_MEM_DS_TOOL_GROUP_DATA.get()); 534 additionalBindDNArgument.addLongIdentifier("additional-bind-dn"); 535 parser.addArgument(additionalBindDNArgument); 536 537 additionalBindPasswordArgument = new StringArgument('w', 538 "additionalBindPassword", false, 1, 539 INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_PASSWORD.get(), 540 INFO_MEM_DS_TOOL_ARG_DESC_ADDITIONAL_BIND_PW.get()); 541 additionalBindPasswordArgument.setSensitive(true); 542 additionalBindPasswordArgument.setArgumentGroupName( 543 INFO_MEM_DS_TOOL_GROUP_DATA.get()); 544 additionalBindPasswordArgument.addLongIdentifier( 545 "additional-bind-password"); 546 parser.addArgument(additionalBindPasswordArgument); 547 548 useDefaultSchemaArgument = new BooleanArgument('s', "useDefaultSchema", 549 INFO_MEM_DS_TOOL_ARG_DESC_USE_DEFAULT_SCHEMA.get()); 550 useDefaultSchemaArgument.setArgumentGroupName( 551 INFO_MEM_DS_TOOL_GROUP_DATA.get()); 552 useDefaultSchemaArgument.addLongIdentifier("use-default-schema"); 553 parser.addArgument(useDefaultSchemaArgument); 554 555 useSchemaFileArgument = new FileArgument('S', "useSchemaFile", false, 0, 556 INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_PATH.get(), 557 INFO_MEM_DS_TOOL_ARG_DESC_USE_SCHEMA_FILE.get(), true, true, false, 558 false); 559 useSchemaFileArgument.setArgumentGroupName( 560 INFO_MEM_DS_TOOL_GROUP_DATA.get()); 561 useSchemaFileArgument.addLongIdentifier("use-schema-file"); 562 parser.addArgument(useSchemaFileArgument); 563 564 equalityIndexArgument = new StringArgument('I', "equalityIndex", false, 0, 565 INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_ATTR.get(), 566 INFO_MEM_DS_TOOL_ARG_DESC_EQ_INDEX.get()); 567 equalityIndexArgument.setArgumentGroupName( 568 INFO_MEM_DS_TOOL_GROUP_DATA.get()); 569 equalityIndexArgument.addLongIdentifier("equality-index"); 570 parser.addArgument(equalityIndexArgument); 571 572 maxChangeLogEntriesArgument = new IntegerArgument('c', 573 "maxChangeLogEntries", false, 1, 574 INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_COUNT.get(), 575 INFO_MEM_DS_TOOL_ARG_DESC_MAX_CHANGELOG_ENTRIES.get(), 0, 576 Integer.MAX_VALUE, 0); 577 maxChangeLogEntriesArgument.setArgumentGroupName( 578 INFO_MEM_DS_TOOL_GROUP_DATA.get()); 579 maxChangeLogEntriesArgument.addLongIdentifier("max-changelog-entries"); 580 maxChangeLogEntriesArgument.addLongIdentifier("max-change-log-entries"); 581 parser.addArgument(maxChangeLogEntriesArgument); 582 583 vendorNameArgument = new StringArgument(null, "vendorName", false, 1, 584 INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_VALUE.get(), 585 INFO_MEM_DS_TOOL_ARG_DESC_VENDOR_NAME.get()); 586 vendorNameArgument.setArgumentGroupName(INFO_MEM_DS_TOOL_GROUP_DATA.get()); 587 vendorNameArgument.addLongIdentifier("vendor-name"); 588 parser.addArgument(vendorNameArgument); 589 590 vendorVersionArgument = new StringArgument(null, "vendorVersion", false, 1, 591 INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_VALUE.get(), 592 INFO_MEM_DS_TOOL_ARG_DESC_VENDOR_VERSION.get()); 593 vendorVersionArgument.setArgumentGroupName( 594 INFO_MEM_DS_TOOL_GROUP_DATA.get()); 595 vendorVersionArgument.addLongIdentifier("vendor-version"); 596 parser.addArgument(vendorVersionArgument); 597 598 accessLogToStandardOutArgument = new BooleanArgument('A', 599 "accessLogToStandardOut", 600 INFO_MEM_DS_TOOL_ARG_DESC_ACCESS_LOG_TO_STDOUT.get()); 601 accessLogToStandardOutArgument.setArgumentGroupName( 602 INFO_MEM_DS_TOOL_GROUP_LOGGING.get()); 603 accessLogToStandardOutArgument.addLongIdentifier( 604 "access-log-to-standard-out"); 605 parser.addArgument(accessLogToStandardOutArgument); 606 607 accessLogFileArgument = new FileArgument('a', "accessLogFile", false, 1, 608 INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_PATH.get(), 609 INFO_MEM_DS_TOOL_ARG_DESC_ACCESS_LOG_FILE.get(), false, true, true, 610 false); 611 accessLogFileArgument.setArgumentGroupName( 612 INFO_MEM_DS_TOOL_GROUP_LOGGING.get()); 613 accessLogFileArgument.addLongIdentifier("access-log-format"); 614 parser.addArgument(accessLogFileArgument); 615 616 ldapDebugLogToStandardOutArgument = new BooleanArgument(null, 617 "ldapDebugLogToStandardOut", 618 INFO_MEM_DS_TOOL_ARG_DESC_LDAP_DEBUG_LOG_TO_STDOUT.get()); 619 ldapDebugLogToStandardOutArgument.setArgumentGroupName( 620 INFO_MEM_DS_TOOL_GROUP_LOGGING.get()); 621 ldapDebugLogToStandardOutArgument.addLongIdentifier( 622 "ldap-debug-log-to-standard-out"); 623 parser.addArgument(ldapDebugLogToStandardOutArgument); 624 625 ldapDebugLogFileArgument = new FileArgument('d', "ldapDebugLogFile", false, 626 1, INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_PATH.get(), 627 INFO_MEM_DS_TOOL_ARG_DESC_LDAP_DEBUG_LOG_FILE.get(), false, true, true, 628 false); 629 ldapDebugLogFileArgument.setArgumentGroupName( 630 INFO_MEM_DS_TOOL_GROUP_LOGGING.get()); 631 ldapDebugLogFileArgument.addLongIdentifier("ldap-debug-log-file"); 632 parser.addArgument(ldapDebugLogFileArgument); 633 634 codeLogFile = new FileArgument('C', "codeLogFile", false, 1, "{path}", 635 INFO_MEM_DS_TOOL_ARG_DESC_CODE_LOG_FILE.get(), false, true, true, 636 false); 637 codeLogFile.setArgumentGroupName(INFO_MEM_DS_TOOL_GROUP_LOGGING.get()); 638 codeLogFile.addLongIdentifier("code-log-file"); 639 parser.addArgument(codeLogFile); 640 641 parser.addExclusiveArgumentSet(useDefaultSchemaArgument, 642 useSchemaFileArgument); 643 parser.addExclusiveArgumentSet(useSSLArgument, useStartTLSArgument); 644 645 parser.addExclusiveArgumentSet(accessLogToStandardOutArgument, 646 accessLogFileArgument); 647 parser.addExclusiveArgumentSet(ldapDebugLogToStandardOutArgument, 648 ldapDebugLogFileArgument); 649 650 parser.addDependentArgumentSet(additionalBindDNArgument, 651 additionalBindPasswordArgument); 652 parser.addDependentArgumentSet(additionalBindPasswordArgument, 653 additionalBindDNArgument); 654 655 parser.addDependentArgumentSet(useSSLArgument, keyStorePathArgument); 656 parser.addDependentArgumentSet(useSSLArgument, keyStorePasswordArgument); 657 parser.addDependentArgumentSet(useStartTLSArgument, keyStorePathArgument); 658 parser.addDependentArgumentSet(useStartTLSArgument, 659 keyStorePasswordArgument); 660 parser.addDependentArgumentSet(keyStorePathArgument, useSSLArgument, 661 useStartTLSArgument); 662 parser.addDependentArgumentSet(keyStorePasswordArgument, useSSLArgument, 663 useStartTLSArgument); 664 parser.addDependentArgumentSet(trustStorePathArgument, useSSLArgument, 665 useStartTLSArgument); 666 parser.addDependentArgumentSet(trustStorePasswordArgument, 667 trustStorePathArgument); 668 } 669 670 671 672 /** 673 * {@inheritDoc} 674 */ 675 @Override() 676 public boolean supportsInteractiveMode() 677 { 678 return true; 679 } 680 681 682 683 /** 684 * {@inheritDoc} 685 */ 686 @Override() 687 public boolean defaultsToInteractiveMode() 688 { 689 return true; 690 } 691 692 693 694 /** 695 * Indicates whether this tool supports the use of a properties file for 696 * specifying default values for arguments that aren't specified on the 697 * command line. 698 * 699 * @return {@code true} if this tool supports the use of a properties file 700 * for specifying default values for arguments that aren't specified 701 * on the command line, or {@code false} if not. 702 */ 703 @Override() 704 public boolean supportsPropertiesFile() 705 { 706 return true; 707 } 708 709 710 711 /** 712 * {@inheritDoc} 713 */ 714 @Override() 715 public ResultCode doToolProcessing() 716 { 717 // Create a base configuration. 718 final InMemoryDirectoryServerConfig serverConfig; 719 try 720 { 721 serverConfig = getConfig(); 722 } 723 catch (final LDAPException le) 724 { 725 Debug.debugException(le); 726 err(ERR_MEM_DS_TOOL_ERROR_INITIALIZING_CONFIG.get(le.getMessage())); 727 return le.getResultCode(); 728 } 729 730 731 // Create the server instance using the provided configuration, but don't 732 // start it yet. 733 try 734 { 735 directoryServer = new InMemoryDirectoryServer(serverConfig); 736 } 737 catch (final LDAPException le) 738 { 739 Debug.debugException(le); 740 err(ERR_MEM_DS_TOOL_ERROR_CREATING_SERVER_INSTANCE.get(le.getMessage())); 741 return le.getResultCode(); 742 } 743 744 745 // If an LDIF file was provided, then use it to populate the server. 746 if (ldifFileArgument.isPresent()) 747 { 748 final File ldifFile = ldifFileArgument.getValue(); 749 try 750 { 751 final int numEntries = directoryServer.importFromLDIF(true, 752 ldifFile.getAbsolutePath()); 753 out(INFO_MEM_DS_TOOL_ADDED_ENTRIES_FROM_LDIF.get(numEntries, 754 ldifFile.getAbsolutePath())); 755 } 756 catch (final LDAPException le) 757 { 758 Debug.debugException(le); 759 err(ERR_MEM_DS_TOOL_ERROR_POPULATING_SERVER_INSTANCE.get( 760 ldifFile.getAbsolutePath(), le.getMessage())); 761 return le.getResultCode(); 762 } 763 } 764 765 766 // Start the server. 767 try 768 { 769 if (! dontStartArgument.isPresent()) 770 { 771 directoryServer.startListening(); 772 out(INFO_MEM_DS_TOOL_LISTENING.get(directoryServer.getListenPort())); 773 } 774 } 775 catch (final Exception e) 776 { 777 Debug.debugException(e); 778 err(ERR_MEM_DS_TOOL_ERROR_STARTING_SERVER.get( 779 StaticUtils.getExceptionMessage(e))); 780 return ResultCode.LOCAL_ERROR; 781 } 782 783 return ResultCode.SUCCESS; 784 } 785 786 787 788 /** 789 * Creates a server configuration based on information provided with 790 * command line arguments. 791 * 792 * @return The configuration that was created. 793 * 794 * @throws LDAPException If a problem is encountered while creating the 795 * configuration. 796 */ 797 private InMemoryDirectoryServerConfig getConfig() 798 throws LDAPException 799 { 800 final List<DN> dnList = baseDNArgument.getValues(); 801 final DN[] baseDNs = new DN[dnList.size()]; 802 dnList.toArray(baseDNs); 803 804 final InMemoryDirectoryServerConfig serverConfig = 805 new InMemoryDirectoryServerConfig(baseDNs); 806 807 808 // If a listen port was specified, then update the configuration to use it. 809 int listenPort = 0; 810 if (portArgument.isPresent()) 811 { 812 listenPort = portArgument.getValue(); 813 } 814 815 816 // If schema should be used, then get it. 817 if (useDefaultSchemaArgument.isPresent()) 818 { 819 serverConfig.setSchema(Schema.getDefaultStandardSchema()); 820 } 821 else if (useSchemaFileArgument.isPresent()) 822 { 823 final ArrayList<File> schemaFiles = new ArrayList<File>(10); 824 for (final File f : useSchemaFileArgument.getValues()) 825 { 826 if (f.exists()) 827 { 828 if (f.isFile()) 829 { 830 schemaFiles.add(f); 831 } 832 else 833 { 834 for (final File subFile : f.listFiles()) 835 { 836 if (subFile.isFile()) 837 { 838 schemaFiles.add(subFile); 839 } 840 } 841 } 842 } 843 else 844 { 845 throw new LDAPException(ResultCode.PARAM_ERROR, 846 ERR_MEM_DS_TOOL_NO_SUCH_SCHEMA_FILE.get(f.getAbsolutePath())); 847 } 848 } 849 850 try 851 { 852 serverConfig.setSchema(Schema.getSchema(schemaFiles)); 853 } 854 catch (final Exception e) 855 { 856 Debug.debugException(e); 857 858 final StringBuilder fileList = new StringBuilder(); 859 final Iterator<File> fileIterator = schemaFiles.iterator(); 860 while (fileIterator.hasNext()) 861 { 862 fileList.append(fileIterator.next().getAbsolutePath()); 863 if (fileIterator.hasNext()) 864 { 865 fileList.append(", "); 866 } 867 } 868 869 throw new LDAPException(ResultCode.LOCAL_ERROR, 870 ERR_MEM_DS_TOOL_ERROR_READING_SCHEMA.get( 871 fileList, StaticUtils.getExceptionMessage(e)), 872 e); 873 } 874 } 875 else 876 { 877 serverConfig.setSchema(null); 878 } 879 880 881 // If an additional bind DN and password are provided, then include them in 882 // the configuration. 883 if (additionalBindDNArgument.isPresent()) 884 { 885 serverConfig.addAdditionalBindCredentials( 886 additionalBindDNArgument.getValue().toString(), 887 additionalBindPasswordArgument.getValue()); 888 } 889 890 891 // If a maximum number of changelog entries was specified, then update the 892 // configuration with that. 893 if (maxChangeLogEntriesArgument.isPresent()) 894 { 895 serverConfig.setMaxChangeLogEntries( 896 maxChangeLogEntriesArgument.getValue()); 897 } 898 899 900 // If an access log file was specified, then create the appropriate log 901 // handler. 902 if (accessLogToStandardOutArgument.isPresent()) 903 { 904 final StreamHandler handler = new StreamHandler(System.out, 905 new MinimalLogFormatter(null, false, false, true)); 906 handler.setLevel(Level.INFO); 907 serverConfig.setAccessLogHandler(handler); 908 } 909 else if (accessLogFileArgument.isPresent()) 910 { 911 final File logFile = accessLogFileArgument.getValue(); 912 try 913 { 914 final FileHandler handler = 915 new FileHandler(logFile.getAbsolutePath(), true); 916 handler.setLevel(Level.INFO); 917 handler.setFormatter(new MinimalLogFormatter(null, false, false, 918 true)); 919 serverConfig.setAccessLogHandler(handler); 920 } 921 catch (final Exception e) 922 { 923 Debug.debugException(e); 924 throw new LDAPException(ResultCode.LOCAL_ERROR, 925 ERR_MEM_DS_TOOL_ERROR_CREATING_LOG_HANDLER.get( 926 logFile.getAbsolutePath(), 927 StaticUtils.getExceptionMessage(e)), 928 e); 929 } 930 } 931 932 933 // If an LDAP debug log file was specified, then create the appropriate log 934 // handler. 935 if (ldapDebugLogToStandardOutArgument.isPresent()) 936 { 937 final StreamHandler handler = new StreamHandler(System.out, 938 new MinimalLogFormatter(null, false, false, true)); 939 handler.setLevel(Level.INFO); 940 serverConfig.setLDAPDebugLogHandler(handler); 941 } 942 else if (ldapDebugLogFileArgument.isPresent()) 943 { 944 final File logFile = ldapDebugLogFileArgument.getValue(); 945 try 946 { 947 final FileHandler handler = 948 new FileHandler(logFile.getAbsolutePath(), true); 949 handler.setLevel(Level.INFO); 950 handler.setFormatter(new MinimalLogFormatter(null, false, false, 951 true)); 952 serverConfig.setLDAPDebugLogHandler(handler); 953 } 954 catch (final Exception e) 955 { 956 Debug.debugException(e); 957 throw new LDAPException(ResultCode.LOCAL_ERROR, 958 ERR_MEM_DS_TOOL_ERROR_CREATING_LOG_HANDLER.get( 959 logFile.getAbsolutePath(), 960 StaticUtils.getExceptionMessage(e)), 961 e); 962 } 963 } 964 965 966 // If a code log file was specified, then update the configuration 967 // accordingly. 968 if (codeLogFile.isPresent()) 969 { 970 serverConfig.setCodeLogDetails(codeLogFile.getValue().getAbsolutePath(), 971 true); 972 } 973 974 975 // If SSL is to be used, then create the corresponding socket factories. 976 if (useSSLArgument.isPresent() || useStartTLSArgument.isPresent()) 977 { 978 try 979 { 980 final KeyManager keyManager = new KeyStoreKeyManager( 981 keyStorePathArgument.getValue(), 982 keyStorePasswordArgument.getValue().toCharArray(), 983 keyStoreTypeArgument.getValue(), null); 984 985 final TrustManager trustManager; 986 if (trustStorePathArgument.isPresent()) 987 { 988 final char[] password; 989 if (trustStorePasswordArgument.isPresent()) 990 { 991 password = trustStorePasswordArgument.getValue().toCharArray(); 992 } 993 else 994 { 995 password = null; 996 } 997 998 trustManager = new TrustStoreTrustManager( 999 trustStorePathArgument.getValue(), password, 1000 trustStoreTypeArgument.getValue(), true); 1001 } 1002 else 1003 { 1004 trustManager = new TrustAllTrustManager(); 1005 } 1006 1007 final SSLUtil serverSSLUtil = new SSLUtil(keyManager, trustManager); 1008 1009 if (useSSLArgument.isPresent()) 1010 { 1011 final SSLUtil clientSSLUtil = new SSLUtil(new TrustAllTrustManager()); 1012 serverConfig.setListenerConfigs( 1013 InMemoryListenerConfig.createLDAPSConfig("LDAPS", null, 1014 listenPort, serverSSLUtil.createSSLServerSocketFactory(), 1015 clientSSLUtil.createSSLSocketFactory())); 1016 } 1017 else 1018 { 1019 serverConfig.setListenerConfigs( 1020 InMemoryListenerConfig.createLDAPConfig("LDAP+StartTLS", null, 1021 listenPort, serverSSLUtil.createSSLSocketFactory())); 1022 } 1023 } 1024 catch (final Exception e) 1025 { 1026 Debug.debugException(e); 1027 throw new LDAPException(ResultCode.LOCAL_ERROR, 1028 ERR_MEM_DS_TOOL_ERROR_INITIALIZING_SSL.get( 1029 StaticUtils.getExceptionMessage(e)), 1030 e); 1031 } 1032 } 1033 else 1034 { 1035 serverConfig.setListenerConfigs(InMemoryListenerConfig.createLDAPConfig( 1036 "LDAP", listenPort)); 1037 } 1038 1039 1040 // If vendor name and/or vendor version values were provided, then configure 1041 // them for use. 1042 if (vendorNameArgument.isPresent()) 1043 { 1044 serverConfig.setVendorName(vendorNameArgument.getValue()); 1045 } 1046 1047 if (vendorVersionArgument.isPresent()) 1048 { 1049 serverConfig.setVendorVersion(vendorVersionArgument.getValue()); 1050 } 1051 1052 1053 // If equality indexing is to be performed, then configure it. 1054 if (equalityIndexArgument.isPresent()) 1055 { 1056 serverConfig.setEqualityIndexAttributes( 1057 equalityIndexArgument.getValues()); 1058 } 1059 1060 return serverConfig; 1061 } 1062 1063 1064 1065 /** 1066 * {@inheritDoc} 1067 */ 1068 @Override() 1069 public LinkedHashMap<String[],String> getExampleUsages() 1070 { 1071 final LinkedHashMap<String[],String> exampleUsages = 1072 new LinkedHashMap<String[],String>(2); 1073 1074 final String[] example1Args = 1075 { 1076 "--baseDN", "dc=example,dc=com" 1077 }; 1078 exampleUsages.put(example1Args, INFO_MEM_DS_TOOL_EXAMPLE_1.get()); 1079 1080 final String[] example2Args = 1081 { 1082 "--baseDN", "dc=example,dc=com", 1083 "--port", "1389", 1084 "--ldifFile", "test.ldif", 1085 "--accessLogFile", "access.log", 1086 "--useDefaultSchema" 1087 }; 1088 exampleUsages.put(example2Args, INFO_MEM_DS_TOOL_EXAMPLE_2.get()); 1089 1090 return exampleUsages; 1091 } 1092 1093 1094 1095 /** 1096 * Retrieves the in-memory directory server instance that has been created by 1097 * this tool. It will only be valid after the {@link #doToolProcessing()} 1098 * method has been called. 1099 * 1100 * @return The in-memory directory server instance that has been created by 1101 * this tool, or {@code null} if the directory server instance has 1102 * not been successfully created. 1103 */ 1104 public InMemoryDirectoryServer getDirectoryServer() 1105 { 1106 return directoryServer; 1107 } 1108 1109 1110 1111 /** 1112 * {@inheritDoc} 1113 */ 1114 public void connectionCreationFailure(final Socket socket, 1115 final Throwable cause) 1116 { 1117 err(ERR_MEM_DS_TOOL_ERROR_ACCEPTING_CONNECTION.get( 1118 StaticUtils.getExceptionMessage(cause))); 1119 } 1120 1121 1122 1123 /** 1124 * {@inheritDoc} 1125 */ 1126 public void connectionTerminated( 1127 final LDAPListenerClientConnection connection, 1128 final LDAPException cause) 1129 { 1130 err(ERR_MEM_DS_TOOL_CONNECTION_TERMINATED_BY_EXCEPTION.get( 1131 StaticUtils.getExceptionMessage(cause))); 1132 } 1133}