001/* 002 * Copyright 2011-2024 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2011-2024 Ping Identity Corporation 007 * 008 * Licensed under the Apache License, Version 2.0 (the "License"); 009 * you may not use this file except in compliance with the License. 010 * You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, software 015 * distributed under the License is distributed on an "AS IS" BASIS, 016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 017 * See the License for the specific language governing permissions and 018 * limitations under the License. 019 */ 020/* 021 * Copyright (C) 2011-2024 Ping Identity Corporation 022 * 023 * This program is free software; you can redistribute it and/or modify 024 * it under the terms of the GNU General Public License (GPLv2 only) 025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 026 * as published by the Free Software Foundation. 027 * 028 * This program is distributed in the hope that it will be useful, 029 * but WITHOUT ANY WARRANTY; without even the implied warranty of 030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 031 * GNU General Public License for more details. 032 * 033 * You should have received a copy of the GNU General Public License 034 * along with this program; if not, see <http://www.gnu.org/licenses>. 035 */ 036package com.unboundid.ldap.listener; 037 038 039 040import java.io.File; 041import java.io.IOException; 042import java.net.InetAddress; 043import java.util.ArrayList; 044import java.util.Arrays; 045import java.util.Collection; 046import java.util.Collections; 047import java.util.LinkedHashMap; 048import java.util.List; 049import java.util.Map; 050import javax.net.SocketFactory; 051 052import com.unboundid.asn1.ASN1OctetString; 053import com.unboundid.ldap.listener.interceptor. 054 InMemoryOperationInterceptorRequestHandler; 055import com.unboundid.ldap.protocol.BindRequestProtocolOp; 056import com.unboundid.ldap.protocol.BindResponseProtocolOp; 057import com.unboundid.ldap.protocol.CompareRequestProtocolOp; 058import com.unboundid.ldap.protocol.CompareResponseProtocolOp; 059import com.unboundid.ldap.protocol.ExtendedRequestProtocolOp; 060import com.unboundid.ldap.protocol.ExtendedResponseProtocolOp; 061import com.unboundid.ldap.protocol.LDAPMessage; 062import com.unboundid.ldap.protocol.SearchRequestProtocolOp; 063import com.unboundid.ldap.protocol.SearchResultDoneProtocolOp; 064import com.unboundid.ldap.sdk.AddRequest; 065import com.unboundid.ldap.sdk.Attribute; 066import com.unboundid.ldap.sdk.BindRequest; 067import com.unboundid.ldap.sdk.BindResult; 068import com.unboundid.ldap.sdk.CompareRequest; 069import com.unboundid.ldap.sdk.CompareResult; 070import com.unboundid.ldap.sdk.Control; 071import com.unboundid.ldap.sdk.DeleteRequest; 072import com.unboundid.ldap.sdk.DereferencePolicy; 073import com.unboundid.ldap.sdk.DN; 074import com.unboundid.ldap.sdk.Entry; 075import com.unboundid.ldap.sdk.ExtendedRequest; 076import com.unboundid.ldap.sdk.ExtendedResult; 077import com.unboundid.ldap.sdk.Filter; 078import com.unboundid.ldap.sdk.FullLDAPInterface; 079import com.unboundid.ldap.sdk.InternalSDKHelper; 080import com.unboundid.ldap.sdk.LDAPConnection; 081import com.unboundid.ldap.sdk.LDAPConnectionOptions; 082import com.unboundid.ldap.sdk.LDAPConnectionPool; 083import com.unboundid.ldap.sdk.LDAPException; 084import com.unboundid.ldap.sdk.LDAPResult; 085import com.unboundid.ldap.sdk.LDAPSearchException; 086import com.unboundid.ldap.sdk.Modification; 087import com.unboundid.ldap.sdk.ModifyRequest; 088import com.unboundid.ldap.sdk.ModifyDNRequest; 089import com.unboundid.ldap.sdk.PLAINBindRequest; 090import com.unboundid.ldap.sdk.ReadOnlyAddRequest; 091import com.unboundid.ldap.sdk.ReadOnlyCompareRequest; 092import com.unboundid.ldap.sdk.ReadOnlyDeleteRequest; 093import com.unboundid.ldap.sdk.ReadOnlyModifyRequest; 094import com.unboundid.ldap.sdk.ReadOnlyModifyDNRequest; 095import com.unboundid.ldap.sdk.ReadOnlySearchRequest; 096import com.unboundid.ldap.sdk.ResultCode; 097import com.unboundid.ldap.sdk.RootDSE; 098import com.unboundid.ldap.sdk.SearchRequest; 099import com.unboundid.ldap.sdk.SearchResult; 100import com.unboundid.ldap.sdk.SearchResultEntry; 101import com.unboundid.ldap.sdk.SearchResultListener; 102import com.unboundid.ldap.sdk.SearchResultReference; 103import com.unboundid.ldap.sdk.SearchScope; 104import com.unboundid.ldap.sdk.SimpleBindRequest; 105import com.unboundid.ldap.sdk.schema.Schema; 106import com.unboundid.ldif.LDIFException; 107import com.unboundid.ldif.LDIFReader; 108import com.unboundid.ldif.LDIFWriter; 109import com.unboundid.util.ByteStringBuffer; 110import com.unboundid.util.Debug; 111import com.unboundid.util.Mutable; 112import com.unboundid.util.NotNull; 113import com.unboundid.util.Nullable; 114import com.unboundid.util.StaticUtils; 115import com.unboundid.util.ThreadSafety; 116import com.unboundid.util.ThreadSafetyLevel; 117import com.unboundid.util.Validator; 118 119import static com.unboundid.ldap.listener.ListenerMessages.*; 120 121 122 123/** 124 * This class provides a utility that may be used to create a simple LDAP server 125 * instance that will hold all of its information in memory. It is intended to 126 * be very easy to use, particularly as an embeddable server for testing 127 * directory-enabled applications. It can be easily created, configured, 128 * populated, and shut down with only a few lines of code, and it provides a 129 * number of convenience methods that can be very helpful in writing test cases 130 * that validate the content of the server. 131 * <BR><BR> 132 * Some notes about the capabilities of this server: 133 * <UL> 134 * <LI>It provides reasonably complete support for add, compare, delete, 135 * modify, modify DN (including new superior and subtree move/rename), 136 * search, and unbind operations.</LI> 137 * <LI>It will accept abandon requests, but will not do anything with 138 * them.</LI> 139 * <LI>It provides support for simple bind operations, and for the SASL PLAIN 140 * mechanism. It also provides an API that can be used to add support for 141 * additional SASL mechanisms.</LI> 142 * <LI>It provides support for the password modify, StartTLS, and "who am I?" 143 * extended operations, as well as an API that can be used to add support 144 * for additional types of extended operations.</LI> 145 * <LI>It provides support for the LDAP assertions, authorization identity, 146 * don't use copy, manage DSA IT, permissive modify, pre-read, post-read, 147 * proxied authorization v1 and v2, server-side sort, simple paged 148 * results, LDAP subentries, subtree delete, and virtual list view request 149 * controls.</LI> 150 * <LI>It supports the use of schema (if provided), but it does not currently 151 * allow updating the schema on the fly.</LI> 152 * <LI>It has the ability to maintain a log of operations processed, as a 153 * simple access log, a more detailed LDAP debug log, or even a log with 154 * generated code that may be used to construct and issue the requests 155 * received by clients.</LI> 156 * <LI>It has the ability to maintain an LDAP-accessible changelog.</LI> 157 * <LI>It provides an option to generate a number of operational attributes, 158 * including entryDN, entryUUID, creatorsName, createTimestamp, 159 * modifiersName, modifyTimestamp, and subschemaSubentry.</LI> 160 * <LI>It provides support for referential integrity, in which case specified 161 * attributes whose values are DNs may be updated if the entries they 162 * reference are deleted or renamed.</LI> 163 * <LI>It provides methods for importing data from and exporting data to LDIF 164 * files, and it has the ability to capture a point-in-time snapshot of 165 * the data (including changelog information) that may be restored at any 166 * point.</LI> 167 * <LI>It implements the {@link FullLDAPInterface} interface, which means that 168 * in many cases it can be used as a drop-in replacement for an 169 * {@link LDAPConnection}.</LI> 170 * </UL> 171 * <BR><BR> 172 * In order to create an in-memory directory server instance, you should first 173 * create an {@link InMemoryDirectoryServerConfig} object with the desired 174 * settings. Then use that configuration object to initialize the directory 175 * server instance, and call the {@link #startListening} method to start 176 * accepting connections from LDAP clients. The {@link #getConnection} and 177 * {@link #getConnectionPool} methods may be used to obtain connections to the 178 * server and you can also manually create connections using the information 179 * obtained via the {@link #getListenAddress}, {@link #getListenPort}, and 180 * {@link #getClientSocketFactory} methods. When the server is no longer 181 * needed, the {@link #shutDown} method should be used to stop the server. Any 182 * number of in-memory directory server instances can be created and running in 183 * a single JVM at any time, and many of the methods provided in this class can 184 * be used without the server running if operations are to be performed using 185 * only method calls rather than via LDAP clients. 186 * <BR><BR> 187 * <H2>Example</H2> 188 * The following example demonstrates the process that can be used to create, 189 * start, and use an in-memory directory server instance, including support for 190 * secure communication using both SSL and StartTLS: 191 * <PRE> 192 * // Create a base configuration for the server. 193 * InMemoryDirectoryServerConfig config = 194 * new InMemoryDirectoryServerConfig("dc=example,dc=com"); 195 * config.addAdditionalBindCredentials("cn=Directory Manager", 196 * "password"); 197 * 198 * // Update the configuration to support LDAP (with StartTLS) and LDAPS 199 * // listeners. 200 * final SSLUtil serverSSLUtil = new SSLUtil( 201 * new KeyStoreKeyManager(serverKeyStorePath, serverKeyStorePIN, "JKS", 202 * "server-cert"), 203 * new TrustStoreTrustManager(serverTrustStorePath)); 204 * final SSLUtil clientSSLUtil = new SSLUtil( 205 * new TrustStoreTrustManager(clientTrustStorePath)); 206 * config.setListenerConfigs( 207 * InMemoryListenerConfig.createLDAPConfig("LDAP", // Listener name 208 * null, // Listen address. (null = listen on all interfaces) 209 * 0, // Listen port (0 = automatically choose an available port) 210 * serverSSLUtil.createSSLSocketFactory()), // StartTLS factory 211 * InMemoryListenerConfig.createLDAPSConfig("LDAPS", // Listener name 212 * null, // Listen address. (null = listen on all interfaces) 213 * 0, // Listen port (0 = automatically choose an available port) 214 * serverSSLUtil.createSSLServerSocketFactory(), // Server factory 215 * clientSSLUtil.createSSLSocketFactory())); // Client factory 216 * 217 * // Create and start the server instance and populate it with an initial set 218 * // of data from an LDIF file. 219 * InMemoryDirectoryServer server = new InMemoryDirectoryServer(config); 220 * server.importFromLDIF(true, ldifFilePath); 221 * 222 * // Start the server so it will accept client connections. 223 * server.startListening(); 224 * 225 * // Get an unencrypted connection to the server's LDAP listener, then use 226 * // StartTLS to secure that connection. Make sure the connection is usable 227 * // by retrieving the server root DSE. 228 * LDAPConnection connection = server.getConnection("LDAP"); 229 * connection.processExtendedOperation(new StartTLSExtendedRequest( 230 * clientSSLUtil.createSSLContext())); 231 * LDAPTestUtils.assertEntryExists(connection, ""); 232 * connection.close(); 233 * 234 * // Establish an SSL-based connection to the LDAPS listener, and make sure 235 * // that connection is also usable. 236 * connection = server.getConnection("LDAPS"); 237 * LDAPTestUtils.assertEntryExists(connection, ""); 238 * connection.close(); 239 * 240 * // Shut down the server so that it will no longer accept client 241 * // connections, and close all existing connections. 242 * server.shutDown(true); 243 * </PRE> 244 */ 245@Mutable() 246@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 247public final class InMemoryDirectoryServer 248 implements FullLDAPInterface 249{ 250 // The in-memory request handler that will be used for the server. 251 @NotNull private final InMemoryRequestHandler inMemoryHandler; 252 253 // The set of listeners that have been configured for this server, mapped by 254 // listener name. 255 @NotNull private final Map<String,LDAPListener> listeners; 256 257 // The set of configurations for all the LDAP listeners to be used. 258 @NotNull private final Map<String,LDAPListenerConfig> ldapListenerConfigs; 259 260 // The set of client socket factories associated with each of the listeners. 261 @NotNull private final Map<String,SocketFactory> clientSocketFactories; 262 263 // A read-only representation of the configuration used to create this 264 // in-memory directory server. 265 @NotNull private final ReadOnlyInMemoryDirectoryServerConfig config; 266 267 268 269 /** 270 * Creates a very simple instance of an in-memory directory server with the 271 * specified set of base DNs. It will not use a well-defined schema, and will 272 * pick a listen port at random. 273 * 274 * @param baseDNs The base DNs to use for the server. It must not be 275 * {@code null} or empty. 276 * 277 * @throws LDAPException If a problem occurs while attempting to initialize 278 * the server. 279 */ 280 public InMemoryDirectoryServer(@NotNull final String... baseDNs) 281 throws LDAPException 282 { 283 this(new InMemoryDirectoryServerConfig(baseDNs)); 284 } 285 286 287 288 /** 289 * Creates a new instance of an in-memory directory server with the provided 290 * configuration. 291 * 292 * @param cfg The configuration to use for the server. It must not be 293 * {@code null}. 294 * 295 * @throws LDAPException If a problem occurs while trying to initialize the 296 * directory server with the provided configuration. 297 */ 298 public InMemoryDirectoryServer( 299 @NotNull final InMemoryDirectoryServerConfig cfg) 300 throws LDAPException 301 { 302 Validator.ensureNotNull(cfg); 303 304 config = new ReadOnlyInMemoryDirectoryServerConfig(cfg); 305 inMemoryHandler = new InMemoryRequestHandler(config); 306 307 LDAPListenerRequestHandler requestHandler = inMemoryHandler; 308 309 if (config.getAccessLogHandler() != null) 310 { 311 requestHandler = new AccessLogRequestHandler(config.getAccessLogHandler(), 312 requestHandler); 313 } 314 315 if (config.getJSONAccessLogHandler() != null) 316 { 317 requestHandler = new JSONAccessLogRequestHandler( 318 config.getJSONAccessLogHandler(), requestHandler); 319 } 320 321 if (config.getLDAPDebugLogHandler() != null) 322 { 323 requestHandler = new LDAPDebuggerRequestHandler( 324 config.getLDAPDebugLogHandler(), requestHandler); 325 } 326 327 if (config.getCodeLogPath() != null) 328 { 329 try 330 { 331 requestHandler = new ToCodeRequestHandler(config.getCodeLogPath(), 332 config.includeRequestProcessingInCodeLog(), requestHandler); 333 } 334 catch (final IOException ioe) 335 { 336 Debug.debugException(ioe); 337 throw new LDAPException(ResultCode.LOCAL_ERROR, 338 ERR_MEM_DS_CANNOT_OPEN_CODE_LOG.get(config.getCodeLogPath(), 339 StaticUtils.getExceptionMessage(ioe)), 340 ioe); 341 } 342 } 343 344 if (! config.getOperationInterceptors().isEmpty()) 345 { 346 requestHandler = new InMemoryOperationInterceptorRequestHandler( 347 config.getOperationInterceptors(), requestHandler); 348 } 349 350 351 final List<InMemoryListenerConfig> listenerConfigs = 352 config.getListenerConfigs(); 353 354 listeners = new LinkedHashMap<>( 355 StaticUtils.computeMapCapacity(listenerConfigs.size())); 356 ldapListenerConfigs = new LinkedHashMap<>( 357 StaticUtils.computeMapCapacity(listenerConfigs.size())); 358 clientSocketFactories = new LinkedHashMap<>( 359 StaticUtils.computeMapCapacity(listenerConfigs.size())); 360 361 for (final InMemoryListenerConfig c : listenerConfigs) 362 { 363 final String name = StaticUtils.toLowerCase(c.getListenerName()); 364 365 final LDAPListenerRequestHandler listenerRequestHandler; 366 if (c.getStartTLSSocketFactory() == null) 367 { 368 listenerRequestHandler = requestHandler; 369 } 370 else 371 { 372 listenerRequestHandler = 373 new StartTLSRequestHandler(c.getStartTLSSocketFactory(), 374 requestHandler, c.requestClientCertificate(), 375 c.requireClientCertificate()); 376 } 377 378 final LDAPListenerConfig listenerCfg = new LDAPListenerConfig( 379 c.getListenPort(), listenerRequestHandler); 380 listenerCfg.setMaxConnections(config.getMaxConnections()); 381 listenerCfg.setMaxMessageSizeBytes(config.getMaxMessageSizeBytes()); 382 listenerCfg.setExceptionHandler(config.getListenerExceptionHandler()); 383 listenerCfg.setListenAddress(c.getListenAddress()); 384 listenerCfg.setServerSocketFactory(c.getServerSocketFactory()); 385 listenerCfg.setRequestClientCertificate(c.requestClientCertificate()); 386 listenerCfg.setRequireClientCertificate(c.requireClientCertificate()); 387 388 ldapListenerConfigs.put(name, listenerCfg); 389 390 if (c.getClientSocketFactory() != null) 391 { 392 clientSocketFactories.put(name, c.getClientSocketFactory()); 393 } 394 } 395 } 396 397 398 399 /** 400 * Attempts to start listening for client connections on all configured 401 * listeners. Any listeners that are already running will be unaffected. 402 * 403 * @throws LDAPException If a problem occurs while attempting to create any 404 * of the configured listeners. Even if an exception 405 * is thrown, then as many listeners as possible will 406 * be started. 407 */ 408 public synchronized void startListening() 409 throws LDAPException 410 { 411 final ArrayList<String> messages = new ArrayList<>(listeners.size()); 412 413 for (final Map.Entry<String,LDAPListenerConfig> cfgEntry : 414 ldapListenerConfigs.entrySet()) 415 { 416 final String name = cfgEntry.getKey(); 417 418 if (listeners.containsKey(name)) 419 { 420 // This listener is already running. 421 continue; 422 } 423 424 final LDAPListenerConfig listenerConfig = cfgEntry.getValue(); 425 final LDAPListener listener = new LDAPListener(listenerConfig); 426 427 try 428 { 429 listener.startListening(); 430 listenerConfig.setListenPort(listener.getListenPort()); 431 listeners.put(name, listener); 432 } 433 catch (final Exception e) 434 { 435 Debug.debugException(e); 436 messages.add(ERR_MEM_DS_START_FAILED.get(name, 437 StaticUtils.getExceptionMessage(e))); 438 } 439 } 440 441 if (! messages.isEmpty()) 442 { 443 throw new LDAPException(ResultCode.LOCAL_ERROR, 444 StaticUtils.concatenateStrings(messages)); 445 } 446 } 447 448 449 450 /** 451 * Attempts to start listening for client connections on the specified 452 * listener. If the listener is already running, then it will be unaffected. 453 * 454 * @param listenerName The name of the listener to be started. It must not 455 * be {@code null}. 456 * 457 * @throws LDAPException If a problem occurs while attempting to start the 458 * requested listener. 459 */ 460 public synchronized void startListening(@NotNull final String listenerName) 461 throws LDAPException 462 { 463 // If the listener is already running, then there's nothing to do. 464 final String name = StaticUtils .toLowerCase(listenerName); 465 if (listeners.containsKey(name)) 466 { 467 return; 468 } 469 470 // Get the configuration to use for the listener. 471 final LDAPListenerConfig listenerConfig = ldapListenerConfigs.get(name); 472 if (listenerConfig == null) 473 { 474 throw new LDAPException(ResultCode.PARAM_ERROR, 475 ERR_MEM_DS_NO_SUCH_LISTENER.get(listenerName)); 476 } 477 478 479 final LDAPListener listener = new LDAPListener(listenerConfig); 480 481 try 482 { 483 listener.startListening(); 484 listenerConfig.setListenPort(listener.getListenPort()); 485 listeners.put(name, listener); 486 } 487 catch (final Exception e) 488 { 489 Debug.debugException(e); 490 throw new LDAPException(ResultCode.LOCAL_ERROR, 491 ERR_MEM_DS_START_FAILED.get(name, 492 StaticUtils.getExceptionMessage(e)), 493 e); 494 } 495 } 496 497 498 499 /** 500 * {@inheritDoc} 501 */ 502 @Override() 503 public void close() 504 { 505 shutDown(true); 506 } 507 508 509 510 /** 511 * Closes all connections that are currently established to the server. This 512 * has no effect on the ability to accept new connections. 513 * 514 * @param sendNoticeOfDisconnection Indicates whether to send the client a 515 * notice of disconnection unsolicited 516 * notification before closing the 517 * connection. 518 */ 519 public synchronized void closeAllConnections( 520 final boolean sendNoticeOfDisconnection) 521 { 522 for (final LDAPListener l : listeners.values()) 523 { 524 try 525 { 526 l.closeAllConnections(sendNoticeOfDisconnection); 527 } 528 catch (final Exception e) 529 { 530 Debug.debugException(e); 531 } 532 } 533 } 534 535 536 537 /** 538 * Shuts down all configured listeners. Any listeners that are already 539 * stopped will be unaffected. 540 * 541 * @param closeExistingConnections Indicates whether to close all existing 542 * connections, or merely to stop accepting 543 * new connections. 544 */ 545 public synchronized void shutDown(final boolean closeExistingConnections) 546 { 547 for (final LDAPListener l : listeners.values()) 548 { 549 try 550 { 551 l.shutDown(closeExistingConnections); 552 } 553 catch (final Exception e) 554 { 555 Debug.debugException(e); 556 } 557 } 558 559 listeners.clear(); 560 } 561 562 563 564 /** 565 * Shuts down the specified listener. If there is no such listener defined, 566 * or if the specified listener is not running, then no action will be taken. 567 * 568 * @param listenerName The name of the listener to be shut down. 569 * It must not be {@code null}. 570 * @param closeExistingConnections Indicates whether to close all existing 571 * connections, or merely to stop accepting 572 * new connections. 573 */ 574 public synchronized void shutDown(@NotNull final String listenerName, 575 final boolean closeExistingConnections) 576 { 577 final String name = StaticUtils.toLowerCase(listenerName); 578 final LDAPListener listener = listeners.remove(name); 579 if (listener != null) 580 { 581 listener.shutDown(closeExistingConnections); 582 } 583 } 584 585 586 587 /** 588 * Attempts to restart all listeners defined in the server. All running 589 * listeners will be stopped, and all configured listeners will be started. 590 * 591 * @throws LDAPException If a problem occurs while attempting to restart any 592 * of the listeners. Even if an exception is thrown, 593 * as many listeners as possible will be started. 594 */ 595 public synchronized void restartServer() 596 throws LDAPException 597 { 598 shutDown(true); 599 600 try 601 { 602 Thread.sleep(100L); 603 } 604 catch (final Exception e) 605 { 606 Debug.debugException(e); 607 608 if (e instanceof InterruptedException) 609 { 610 Thread.currentThread().interrupt(); 611 } 612 } 613 614 startListening(); 615 } 616 617 618 619 /** 620 * Attempts to restart the specified listener. If it is running, it will be 621 * stopped. It will then be started. 622 * 623 * @param listenerName The name of the listener to be restarted. It must 624 * not be {@code null}. 625 * 626 * @throws LDAPException If a problem occurs while attempting to restart the 627 * specified listener. 628 */ 629 public synchronized void restartListener(@NotNull final String listenerName) 630 throws LDAPException 631 { 632 shutDown(listenerName, true); 633 634 try 635 { 636 Thread.sleep(100L); 637 } 638 catch (final Exception e) 639 { 640 Debug.debugException(e); 641 642 if (e instanceof InterruptedException) 643 { 644 Thread.currentThread().interrupt(); 645 } 646 } 647 648 startListening(listenerName); 649 } 650 651 652 653 /** 654 * Retrieves a read-only representation of the configuration used to create 655 * this in-memory directory server instance. 656 * 657 * @return A read-only representation of the configuration used to create 658 * this in-memory directory server instance. 659 */ 660 @NotNull() 661 public ReadOnlyInMemoryDirectoryServerConfig getConfig() 662 { 663 return config; 664 } 665 666 667 668 /** 669 * Retrieves the in-memory request handler that is used to perform the real 670 * server processing. 671 * 672 * @return The in-memory request handler that is used to perform the real 673 * server processing. 674 */ 675 @NotNull() 676 InMemoryRequestHandler getInMemoryRequestHandler() 677 { 678 return inMemoryHandler; 679 } 680 681 682 683 /** 684 * Creates a point-in-time snapshot of the information contained in this 685 * in-memory directory server instance. It may be restored using the 686 * {@link #restoreSnapshot} method. 687 * <BR><BR> 688 * This method may be used regardless of whether the server is listening for 689 * client connections. 690 * 691 * @return The snapshot created based on the current content of this 692 * in-memory directory server instance. 693 */ 694 @NotNull() 695 public InMemoryDirectoryServerSnapshot createSnapshot() 696 { 697 return inMemoryHandler.createSnapshot(); 698 } 699 700 701 702 /** 703 * Restores the this in-memory directory server instance to match the content 704 * it held at the time the snapshot was created. 705 * <BR><BR> 706 * This method may be used regardless of whether the server is listening for 707 * client connections. 708 * 709 * @param snapshot The snapshot to be restored. It must not be 710 * {@code null}. 711 */ 712 public void restoreSnapshot( 713 @NotNull final InMemoryDirectoryServerSnapshot snapshot) 714 { 715 inMemoryHandler.restoreSnapshot(snapshot); 716 } 717 718 719 720 /** 721 * Retrieves the list of base DNs configured for use by the server. 722 * 723 * @return The list of base DNs configured for use by the server. 724 */ 725 @NotNull() 726 public List<DN> getBaseDNs() 727 { 728 return inMemoryHandler.getBaseDNs(); 729 } 730 731 732 733 /** 734 * Attempts to establish a client connection to the server. If multiple 735 * listeners are configured, then it will attempt to establish a connection to 736 * the first configured listener that is running. 737 * 738 * @return The client connection that has been established. 739 * 740 * @throws LDAPException If a problem is encountered while attempting to 741 * create the connection. 742 */ 743 @NotNull() 744 public LDAPConnection getConnection() 745 throws LDAPException 746 { 747 return getConnection(null, null); 748 } 749 750 751 752 /** 753 * Attempts to establish a client connection to the server. 754 * 755 * @param options The connection options to use when creating the 756 * connection. It may be {@code null} if a default set of 757 * options should be used. 758 * 759 * @return The client connection that has been established. 760 * 761 * @throws LDAPException If a problem is encountered while attempting to 762 * create the connection. 763 */ 764 @NotNull() 765 public LDAPConnection getConnection( 766 @Nullable final LDAPConnectionOptions options) 767 throws LDAPException 768 { 769 return getConnection(null, options); 770 } 771 772 773 774 /** 775 * Attempts to establish a client connection to the specified listener. 776 * 777 * @param listenerName The name of the listener to which to establish the 778 * connection. It may be {@code null} if a connection 779 * should be established to the first available 780 * listener. 781 * 782 * @return The client connection that has been established. 783 * 784 * @throws LDAPException If a problem is encountered while attempting to 785 * create the connection. 786 */ 787 @NotNull() 788 public LDAPConnection getConnection(@Nullable final String listenerName) 789 throws LDAPException 790 { 791 return getConnection(listenerName, null); 792 } 793 794 795 796 /** 797 * Attempts to establish a client connection to the specified listener. 798 * 799 * @param listenerName The name of the listener to which to establish the 800 * connection. It may be {@code null} if a connection 801 * should be established to the first available 802 * listener. 803 * @param options The set of LDAP connection options to use for the 804 * connection that is created. 805 * 806 * @return The client connection that has been established. 807 * 808 * @throws LDAPException If a problem is encountered while attempting to 809 * create the connection. 810 */ 811 @NotNull() 812 public synchronized LDAPConnection getConnection( 813 @Nullable final String listenerName, 814 @Nullable final LDAPConnectionOptions options) 815 throws LDAPException 816 { 817 final LDAPListenerConfig listenerConfig; 818 final SocketFactory clientSocketFactory; 819 820 if (listenerName == null) 821 { 822 final String name = getFirstListenerName(); 823 if (name == null) 824 { 825 throw new LDAPException(ResultCode.CONNECT_ERROR, 826 ERR_MEM_DS_GET_CONNECTION_NO_LISTENERS.get()); 827 } 828 829 listenerConfig = ldapListenerConfigs.get(name); 830 clientSocketFactory = clientSocketFactories.get(name); 831 } 832 else 833 { 834 final String name = StaticUtils.toLowerCase(listenerName); 835 if (! listeners.containsKey(name)) 836 { 837 throw new LDAPException(ResultCode.CONNECT_ERROR, 838 ERR_MEM_DS_GET_CONNECTION_LISTENER_NOT_RUNNING.get(listenerName)); 839 } 840 841 listenerConfig = ldapListenerConfigs.get(name); 842 clientSocketFactory = clientSocketFactories.get(name); 843 } 844 845 String hostAddress; 846 if (StaticUtils.isWithinUnitTest()) 847 { 848 // If we're running in the unit test framework, always use the IPv4 849 // loopback address. This helps work around a name resolution problem on 850 // Windows systems when connected to the Ping Identity VPN. 851 hostAddress = "127.0.0.1"; 852 } 853 else 854 { 855 final InetAddress listenAddress = listenerConfig.getListenAddress(); 856 if ((listenAddress == null) || (listenAddress.isAnyLocalAddress())) 857 { 858 try 859 { 860 hostAddress = LDAPConnectionOptions.DEFAULT_NAME_RESOLVER. 861 getLocalHost().getHostAddress(); 862 } 863 catch (final Exception e) 864 { 865 Debug.debugException(e); 866 hostAddress = "127.0.0.1"; 867 } 868 } 869 else 870 { 871 hostAddress = listenAddress.getHostAddress(); 872 } 873 } 874 875 return new LDAPConnection(clientSocketFactory, options, hostAddress, 876 listenerConfig.getListenPort()); 877 } 878 879 880 881 /** 882 * Attempts to establish a connection pool to the server with the specified 883 * maximum number of connections. 884 * 885 * @param maxConnections The maximum number of connections to maintain in 886 * the connection pool. It must be greater than or 887 * equal to one. 888 * 889 * @return The connection pool that has been created. 890 * 891 * @throws LDAPException If a problem occurs while attempting to create the 892 * connection pool. 893 */ 894 @NotNull() 895 public LDAPConnectionPool getConnectionPool(final int maxConnections) 896 throws LDAPException 897 { 898 return getConnectionPool(null, null, 1, maxConnections); 899 } 900 901 902 903 /** 904 * Attempts to establish a connection pool to the server with the provided 905 * settings. 906 * 907 * @param listenerName The name of the listener to which the 908 * connections should be established. 909 * @param options The connection options to use when creating 910 * connections for use in the pool. It may be 911 * {@code null} if a default set of options should 912 * be used. 913 * @param initialConnections The initial number of connections to establish 914 * in the connection pool. It must be greater 915 * than or equal to one. 916 * @param maxConnections The maximum number of connections to maintain 917 * in the connection pool. It must be greater 918 * than or equal to the initial number of 919 * connections. 920 * 921 * @return The connection pool that has been created. 922 * 923 * @throws LDAPException If a problem occurs while attempting to create the 924 * connection pool. 925 */ 926 @NotNull() 927 public LDAPConnectionPool getConnectionPool( 928 @Nullable final String listenerName, 929 @Nullable final LDAPConnectionOptions options, 930 final int initialConnections, 931 final int maxConnections) 932 throws LDAPException 933 { 934 final LDAPConnection conn = getConnection(listenerName, options); 935 return new LDAPConnectionPool(conn, initialConnections, maxConnections); 936 } 937 938 939 940 /** 941 * Retrieves the configured listen address for the first active listener, if 942 * defined. 943 * 944 * @return The configured listen address for the first active listener, or 945 * {@code null} if that listener does not have an 946 * explicitly-configured listen address or there are no active 947 * listeners. 948 */ 949 @Nullable() 950 public InetAddress getListenAddress() 951 { 952 return getListenAddress(null); 953 } 954 955 956 957 /** 958 * Retrieves the configured listen address for the specified listener, if 959 * defined. 960 * 961 * @param listenerName The name of the listener for which to retrieve the 962 * listen address. It may be {@code null} in order to 963 * obtain the listen address for the first active 964 * listener. 965 * 966 * @return The configured listen address for the specified listener, or 967 * {@code null} if there is no such listener or the listener does not 968 * have an explicitly-configured listen address. 969 */ 970 @Nullable() 971 public synchronized InetAddress getListenAddress( 972 @Nullable final String listenerName) 973 { 974 // If we're running in the unit test framework, always return the IPv4 975 // loopback address. This helps work around a name resolution problem on 976 // Windows systems when connected to the Ping Identity VPN. 977 if (StaticUtils.isWithinUnitTest()) 978 { 979 try 980 { 981 return InetAddress.getByName("127.0.0.1"); 982 } 983 catch (final Exception e) 984 { 985 Debug.debugException(e); 986 } 987 } 988 989 final String name; 990 if (listenerName == null) 991 { 992 name = getFirstListenerName(); 993 } 994 else 995 { 996 name = StaticUtils.toLowerCase(listenerName); 997 } 998 999 final LDAPListenerConfig listenerCfg = ldapListenerConfigs.get(name); 1000 if (listenerCfg == null) 1001 { 1002 return null; 1003 } 1004 else 1005 { 1006 return listenerCfg.getListenAddress(); 1007 } 1008 } 1009 1010 1011 1012 /** 1013 * Retrieves the configured listen port for the first active listener. 1014 * 1015 * @return The configured listen port for the first active listener, or -1 if 1016 * there are no active listeners. 1017 */ 1018 public int getListenPort() 1019 { 1020 return getListenPort(null); 1021 } 1022 1023 1024 1025 /** 1026 * Retrieves the configured listen port for the specified listener, if 1027 * available. 1028 * 1029 * @param listenerName The name of the listener for which to retrieve the 1030 * listen port. It may be {@code null} in order to 1031 * obtain the listen port for the first active 1032 * listener. 1033 * 1034 * @return The configured listen port for the specified listener, or -1 if 1035 * there is no such listener or the listener is not active. 1036 */ 1037 public synchronized int getListenPort(@Nullable final String listenerName) 1038 { 1039 final String name; 1040 if (listenerName == null) 1041 { 1042 name = getFirstListenerName(); 1043 } 1044 else 1045 { 1046 name = StaticUtils.toLowerCase(listenerName); 1047 } 1048 1049 final LDAPListener listener = listeners.get(name); 1050 if (listener == null) 1051 { 1052 return -1; 1053 } 1054 else 1055 { 1056 return listener.getListenPort(); 1057 } 1058 } 1059 1060 1061 1062 /** 1063 * Retrieves the configured client socket factory for the first active 1064 * listener. 1065 * 1066 * @return The configured client socket factory for the first active 1067 * listener, or {@code null} if that listener does not have an 1068 * explicitly-configured socket factory or there are no active 1069 * listeners. 1070 */ 1071 @Nullable() 1072 public SocketFactory getClientSocketFactory() 1073 { 1074 return getClientSocketFactory(null); 1075 } 1076 1077 1078 1079 /** 1080 * Retrieves the configured client socket factory for the specified listener, 1081 * if available. 1082 * 1083 * @param listenerName The name of the listener for which to retrieve the 1084 * client socket factory. It may be {@code null} in 1085 * order to obtain the client socket factory for the 1086 * first active listener. 1087 * 1088 * @return The configured client socket factory for the specified listener, 1089 * or {@code null} if there is no such listener or that listener does 1090 * not have an explicitly-configured client socket factory. 1091 */ 1092 @Nullable() 1093 public synchronized SocketFactory getClientSocketFactory( 1094 @Nullable final String listenerName) 1095 { 1096 final String name; 1097 if (listenerName == null) 1098 { 1099 name = getFirstListenerName(); 1100 } 1101 else 1102 { 1103 name = StaticUtils.toLowerCase(listenerName); 1104 } 1105 1106 return clientSocketFactories.get(name); 1107 } 1108 1109 1110 1111 /** 1112 * Retrieves the name of the first running listener. 1113 * 1114 * @return The name of the first running listener, or {@code null} if there 1115 * are no active listeners. 1116 */ 1117 @Nullable() 1118 private String getFirstListenerName() 1119 { 1120 for (final Map.Entry<String,LDAPListenerConfig> e : 1121 ldapListenerConfigs.entrySet()) 1122 { 1123 final String name = e.getKey(); 1124 if (listeners.containsKey(name)) 1125 { 1126 return name; 1127 } 1128 } 1129 1130 return null; 1131 } 1132 1133 1134 1135 /** 1136 * Retrieves the delay in milliseconds that the server should impose before 1137 * beginning processing for operations. 1138 * 1139 * @return The delay in milliseconds that the server should impose before 1140 * beginning processing for operations, or 0 if there should be no 1141 * delay inserted when processing operations. 1142 */ 1143 public long getProcessingDelayMillis() 1144 { 1145 return inMemoryHandler.getProcessingDelayMillis(); 1146 } 1147 1148 1149 1150 /** 1151 * Specifies the delay in milliseconds that the server should impose before 1152 * beginning processing for operations. 1153 * 1154 * @param processingDelayMillis The delay in milliseconds that the server 1155 * should impose before beginning processing 1156 * for operations. A value less than or equal 1157 * to zero may be used to indicate that there 1158 * should be no delay. 1159 */ 1160 public void setProcessingDelayMillis(final long processingDelayMillis) 1161 { 1162 inMemoryHandler.setProcessingDelayMillis(processingDelayMillis); 1163 } 1164 1165 1166 1167 /** 1168 * Retrieves the number of entries currently held in the server. The count 1169 * returned will not include entries which are part of the changelog. 1170 * <BR><BR> 1171 * This method may be used regardless of whether the server is listening for 1172 * client connections. 1173 * 1174 * @return The number of entries currently held in the server. 1175 */ 1176 public int countEntries() 1177 { 1178 return countEntries(false); 1179 } 1180 1181 1182 1183 /** 1184 * Retrieves the number of entries currently held in the server, optionally 1185 * including those entries which are part of the changelog. 1186 * <BR><BR> 1187 * This method may be used regardless of whether the server is listening for 1188 * client connections. 1189 * 1190 * @param includeChangeLog Indicates whether to include entries that are 1191 * part of the changelog in the count. 1192 * 1193 * @return The number of entries currently held in the server. 1194 */ 1195 public int countEntries(final boolean includeChangeLog) 1196 { 1197 return inMemoryHandler.countEntries(includeChangeLog); 1198 } 1199 1200 1201 1202 /** 1203 * Retrieves the number of entries currently held in the server whose DN 1204 * matches or is subordinate to the provided base DN. 1205 * <BR><BR> 1206 * This method may be used regardless of whether the server is listening for 1207 * client connections. 1208 * 1209 * @param baseDN The base DN to use for the determination. 1210 * 1211 * @return The number of entries currently held in the server whose DN 1212 * matches or is subordinate to the provided base DN. 1213 * 1214 * @throws LDAPException If the provided string cannot be parsed as a valid 1215 * DN. 1216 */ 1217 public int countEntriesBelow(@NotNull final String baseDN) 1218 throws LDAPException 1219 { 1220 return inMemoryHandler.countEntriesBelow(baseDN); 1221 } 1222 1223 1224 1225 /** 1226 * Removes all entries currently held in the server. If a changelog is 1227 * enabled, then all changelog entries will also be cleared but the base 1228 * "cn=changelog" entry will be retained. 1229 * <BR><BR> 1230 * This method may be used regardless of whether the server is listening for 1231 * client connections. 1232 */ 1233 public void clear() 1234 { 1235 inMemoryHandler.clear(); 1236 } 1237 1238 1239 1240 /** 1241 * Reads entries from the specified LDIF file and adds them to the server, 1242 * optionally clearing any existing entries before beginning to add the new 1243 * entries. If an error is encountered while adding entries from LDIF then 1244 * the server will remain populated with the data it held before the import 1245 * attempt (even if the {@code clear} is given with a value of {@code true}). 1246 * <BR><BR> 1247 * This method may be used regardless of whether the server is listening for 1248 * client connections. 1249 * 1250 * @param clear Indicates whether to remove all existing entries prior to 1251 * adding entries read from LDIF. 1252 * @param path The path to the LDIF file from which the entries should be 1253 * read. It must not be {@code null}. 1254 * 1255 * @return The number of entries read from LDIF and added to the server. 1256 * 1257 * @throws LDAPException If a problem occurs while reading entries or adding 1258 * them to the server. 1259 */ 1260 public int importFromLDIF(final boolean clear, @NotNull final String path) 1261 throws LDAPException 1262 { 1263 return importFromLDIF(clear, new File(path)); 1264 } 1265 1266 1267 1268 /** 1269 * Reads entries from the specified LDIF file and adds them to the server, 1270 * optionally clearing any existing entries before beginning to add the new 1271 * entries. If an error is encountered while adding entries from LDIF then 1272 * the server will remain populated with the data it held before the import 1273 * attempt (even if the {@code clear} is given with a value of {@code true}). 1274 * <BR><BR> 1275 * This method may be used regardless of whether the server is listening for 1276 * client connections. 1277 * 1278 * @param clear Indicates whether to remove all existing entries prior to 1279 * adding entries read from LDIF. 1280 * @param ldifFile The LDIF file from which the entries should be read. It 1281 * must not be {@code null}. 1282 * 1283 * @return The number of entries read from LDIF and added to the server. 1284 * 1285 * @throws LDAPException If a problem occurs while reading entries or adding 1286 * them to the server. 1287 */ 1288 public int importFromLDIF(final boolean clear, @NotNull final File ldifFile) 1289 throws LDAPException 1290 { 1291 final LDIFReader reader; 1292 try 1293 { 1294 reader = new LDIFReader(ldifFile); 1295 1296 final Schema schema = getSchema(); 1297 if (schema != null) 1298 { 1299 reader.setSchema(schema); 1300 } 1301 } 1302 catch (final Exception e) 1303 { 1304 Debug.debugException(e); 1305 throw new LDAPException(ResultCode.LOCAL_ERROR, 1306 ERR_MEM_DS_INIT_FROM_LDIF_CANNOT_CREATE_READER.get( 1307 ldifFile.getAbsolutePath(), StaticUtils.getExceptionMessage(e)), 1308 e); 1309 } 1310 1311 return importFromLDIF(clear, reader); 1312 } 1313 1314 1315 1316 /** 1317 * Reads entries from the provided LDIF reader and adds them to the server, 1318 * optionally clearing any existing entries before beginning to add the new 1319 * entries. If an error is encountered while adding entries from LDIF then 1320 * the server will remain populated with the data it held before the import 1321 * attempt (even if the {@code clear} is given with a value of {@code true}). 1322 * <BR><BR> 1323 * This method may be used regardless of whether the server is listening for 1324 * client connections. 1325 * 1326 * @param clear Indicates whether to remove all existing entries prior to 1327 * adding entries read from LDIF. 1328 * @param reader The LDIF reader to use to obtain the entries to be 1329 * imported. 1330 * 1331 * @return The number of entries read from LDIF and added to the server. 1332 * 1333 * @throws LDAPException If a problem occurs while reading entries or adding 1334 * them to the server. 1335 */ 1336 public int importFromLDIF(final boolean clear, 1337 @NotNull final LDIFReader reader) 1338 throws LDAPException 1339 { 1340 return inMemoryHandler.importFromLDIF(clear, reader); 1341 } 1342 1343 1344 1345 /** 1346 * Writes the current contents of the server in LDIF form to the specified 1347 * file. 1348 * <BR><BR> 1349 * This method may be used regardless of whether the server is listening for 1350 * client connections. 1351 * 1352 * @param path The path of the file to which the LDIF 1353 * entries should be written. 1354 * @param excludeGeneratedAttrs Indicates whether to exclude automatically 1355 * generated operational attributes like 1356 * entryUUID, entryDN, creatorsName, etc. 1357 * @param excludeChangeLog Indicates whether to exclude entries 1358 * contained in the changelog. 1359 * 1360 * @return The number of entries written to LDIF. 1361 * 1362 * @throws LDAPException If a problem occurs while writing entries to LDIF. 1363 */ 1364 public int exportToLDIF(@NotNull final String path, 1365 final boolean excludeGeneratedAttrs, 1366 final boolean excludeChangeLog) 1367 throws LDAPException 1368 { 1369 final LDIFWriter ldifWriter; 1370 try 1371 { 1372 ldifWriter = new LDIFWriter(path); 1373 } 1374 catch (final Exception e) 1375 { 1376 Debug.debugException(e); 1377 throw new LDAPException(ResultCode.LOCAL_ERROR, 1378 ERR_MEM_DS_EXPORT_TO_LDIF_CANNOT_CREATE_WRITER.get(path, 1379 StaticUtils.getExceptionMessage(e)), 1380 e); 1381 } 1382 1383 return exportToLDIF(ldifWriter, excludeGeneratedAttrs, excludeChangeLog, 1384 true); 1385 } 1386 1387 1388 1389 /** 1390 * Writes the current contents of the server in LDIF form using the provided 1391 * LDIF writer. 1392 * <BR><BR> 1393 * This method may be used regardless of whether the server is listening for 1394 * client connections. 1395 * 1396 * @param ldifWriter The LDIF writer to use when writing the 1397 * entries. It must not be {@code null}. 1398 * @param excludeGeneratedAttrs Indicates whether to exclude automatically 1399 * generated operational attributes like 1400 * entryUUID, entryDN, creatorsName, etc. 1401 * @param excludeChangeLog Indicates whether to exclude entries 1402 * contained in the changelog. 1403 * @param closeWriter Indicates whether the LDIF writer should be 1404 * closed after all entries have been written. 1405 * 1406 * @return The number of entries written to LDIF. 1407 * 1408 * @throws LDAPException If a problem occurs while writing entries to LDIF. 1409 */ 1410 public int exportToLDIF(@NotNull final LDIFWriter ldifWriter, 1411 final boolean excludeGeneratedAttrs, 1412 final boolean excludeChangeLog, 1413 final boolean closeWriter) 1414 throws LDAPException 1415 { 1416 return inMemoryHandler.exportToLDIF(ldifWriter, excludeGeneratedAttrs, 1417 excludeChangeLog, closeWriter); 1418 } 1419 1420 1421 1422 /** 1423 * Reads LDIF change records from the specified LDIF file and applies them 1424 * to the data in the server. Any LDIF records without a changetype will be 1425 * treated as add change records. If an error is encountered while attempting 1426 * to apply the requested changes, then the server will remain populated with 1427 * the data it held before this method was called, even if earlier changes 1428 * could have been applied successfully. 1429 * <BR><BR> 1430 * This method may be used regardless of whether the server is listening for 1431 * client connections. 1432 * 1433 * @param path The path to the LDIF file from which the LDIF change 1434 * records should be read. It must not be {@code null}. 1435 * 1436 * @return The number of changes applied from the LDIF file. 1437 * 1438 * @throws LDAPException If a problem occurs while reading change records 1439 * or applying them to the server. 1440 */ 1441 public int applyChangesFromLDIF(@NotNull final String path) 1442 throws LDAPException 1443 { 1444 return applyChangesFromLDIF(new File(path)); 1445 } 1446 1447 1448 1449 /** 1450 * Reads LDIF change records from the specified LDIF file and applies them 1451 * to the data in the server. Any LDIF records without a changetype will be 1452 * treated as add change records. If an error is encountered while attempting 1453 * to apply the requested changes, then the server will remain populated with 1454 * the data it held before this method was called, even if earlier changes 1455 * could have been applied successfully. 1456 * <BR><BR> 1457 * This method may be used regardless of whether the server is listening for 1458 * client connections. 1459 * 1460 * @param ldifFile The LDIF file from which the LDIF change records should 1461 * be read. It must not be {@code null}. 1462 * 1463 * @return The number of changes applied from the LDIF file. 1464 * 1465 * @throws LDAPException If a problem occurs while reading change records 1466 * or applying them to the server. 1467 */ 1468 public int applyChangesFromLDIF(@NotNull final File ldifFile) 1469 throws LDAPException 1470 { 1471 final LDIFReader reader; 1472 try 1473 { 1474 reader = new LDIFReader(ldifFile); 1475 1476 final Schema schema = getSchema(); 1477 if (schema != null) 1478 { 1479 reader.setSchema(schema); 1480 } 1481 } 1482 catch (final Exception e) 1483 { 1484 Debug.debugException(e); 1485 throw new LDAPException(ResultCode.LOCAL_ERROR, 1486 ERR_MEM_DS_APPLY_CHANGES_FROM_LDIF_CANNOT_CREATE_READER.get( 1487 ldifFile.getAbsolutePath(), StaticUtils.getExceptionMessage(e)), 1488 e); 1489 } 1490 1491 return applyChangesFromLDIF(reader); 1492 } 1493 1494 1495 1496 /** 1497 * Reads LDIF change records from the provided LDIF reader file and applies 1498 * them to the data in the server. Any LDIF records without a changetype will 1499 * be treated as add change records. If an error is encountered while 1500 * attempting to apply the requested changes, then the server will remain 1501 * populated with the data it held before this method was called, even if 1502 * earlier changes could have been applied successfully. 1503 * <BR><BR> 1504 * This method may be used regardless of whether the server is listening for 1505 * client connections. 1506 * 1507 * @param reader The LDIF reader to use to obtain the change records to be 1508 * applied. 1509 * 1510 * @return The number of changes applied from the LDIF file. 1511 * 1512 * @throws LDAPException If a problem occurs while reading change records 1513 * or applying them to the server. 1514 */ 1515 public int applyChangesFromLDIF(@NotNull final LDIFReader reader) 1516 throws LDAPException 1517 { 1518 return inMemoryHandler.applyChangesFromLDIF(reader); 1519 } 1520 1521 1522 1523 /** 1524 * {@inheritDoc} 1525 * <BR><BR> 1526 * This method may be used regardless of whether the server is listening for 1527 * client connections. 1528 */ 1529 @Override() 1530 @Nullable() 1531 public RootDSE getRootDSE() 1532 throws LDAPException 1533 { 1534 return new RootDSE(inMemoryHandler.getEntry("")); 1535 } 1536 1537 1538 1539 /** 1540 * {@inheritDoc} 1541 * <BR><BR> 1542 * This method may be used regardless of whether the server is listening for 1543 * client connections. 1544 */ 1545 @Override() 1546 @Nullable() 1547 public Schema getSchema() 1548 throws LDAPException 1549 { 1550 return inMemoryHandler.getSchema(); 1551 } 1552 1553 1554 1555 /** 1556 * {@inheritDoc} 1557 * <BR><BR> 1558 * This method may be used regardless of whether the server is listening for 1559 * client connections. 1560 */ 1561 @Override() 1562 @Nullable() 1563 public Schema getSchema(@Nullable final String entryDN) 1564 throws LDAPException 1565 { 1566 return inMemoryHandler.getSchema(); 1567 } 1568 1569 1570 1571 /** 1572 * {@inheritDoc} 1573 * <BR><BR> 1574 * This method may be used regardless of whether the server is listening for 1575 * client connections. 1576 */ 1577 @Override() 1578 @Nullable() 1579 public SearchResultEntry getEntry(@NotNull final String dn) 1580 throws LDAPException 1581 { 1582 return searchForEntry(dn, SearchScope.BASE, Filter.createANDFilter()); 1583 } 1584 1585 1586 1587 /** 1588 * {@inheritDoc} 1589 * <BR><BR> 1590 * This method may be used regardless of whether the server is listening for 1591 * client connections, and regardless of whether search operations are 1592 * allowed in the server. 1593 */ 1594 @Override() 1595 @Nullable() 1596 public SearchResultEntry getEntry(@NotNull final String dn, 1597 @Nullable final String... attributes) 1598 throws LDAPException 1599 { 1600 return searchForEntry(dn, SearchScope.BASE, Filter.createANDFilter(), 1601 attributes); 1602 } 1603 1604 1605 1606 /** 1607 * {@inheritDoc} 1608 * <BR><BR> 1609 * This method may be used regardless of whether the server is listening for 1610 * client connections, and regardless of whether add operations are allowed in 1611 * the server. 1612 */ 1613 @Override() 1614 @NotNull() 1615 public LDAPResult add(@NotNull final String dn, 1616 @NotNull final Attribute... attributes) 1617 throws LDAPException 1618 { 1619 return add(new AddRequest(dn, attributes)); 1620 } 1621 1622 1623 1624 /** 1625 * {@inheritDoc} 1626 * <BR><BR> 1627 * This method may be used regardless of whether the server is listening for 1628 * client connections, and regardless of whether add operations are allowed in 1629 * the server. 1630 */ 1631 @Override() 1632 @NotNull() 1633 public LDAPResult add(@NotNull final String dn, 1634 @NotNull final Collection<Attribute> attributes) 1635 throws LDAPException 1636 { 1637 return add(new AddRequest(dn, attributes)); 1638 } 1639 1640 1641 1642 /** 1643 * {@inheritDoc} 1644 * <BR><BR> 1645 * This method may be used regardless of whether the server is listening for 1646 * client connections, and regardless of whether add operations are allowed in 1647 * the server. 1648 */ 1649 @Override() 1650 @NotNull() 1651 public LDAPResult add(@NotNull final Entry entry) 1652 throws LDAPException 1653 { 1654 return add(new AddRequest(entry)); 1655 } 1656 1657 1658 1659 /** 1660 * {@inheritDoc} 1661 * <BR><BR> 1662 * This method may be used regardless of whether the server is listening for 1663 * client connections, and regardless of whether add operations are allowed in 1664 * the server. 1665 */ 1666 @Override() 1667 @NotNull() 1668 public LDAPResult add(@NotNull final String... ldifLines) 1669 throws LDIFException, LDAPException 1670 { 1671 return add(new AddRequest(ldifLines)); 1672 } 1673 1674 1675 1676 /** 1677 * {@inheritDoc} 1678 * <BR><BR> 1679 * This method may be used regardless of whether the server is listening for 1680 * client connections, and regardless of whether add operations are allowed in 1681 * the server. 1682 */ 1683 @Override() 1684 @NotNull() 1685 public LDAPResult add(@NotNull final AddRequest addRequest) 1686 throws LDAPException 1687 { 1688 return inMemoryHandler.add(addRequest); 1689 } 1690 1691 1692 1693 /** 1694 * {@inheritDoc} 1695 * <BR><BR> 1696 * This method may be used regardless of whether the server is listening for 1697 * client connections, and regardless of whether add operations are allowed in 1698 * the server. 1699 */ 1700 @Override() 1701 @NotNull() 1702 public LDAPResult add(@NotNull final ReadOnlyAddRequest addRequest) 1703 throws LDAPException 1704 { 1705 return add(addRequest.duplicate()); 1706 } 1707 1708 1709 1710 /** 1711 * Attempts to add all of the provided entries to the server. If a problem is 1712 * encountered while attempting to add any of the provided entries, then the 1713 * server will remain populated with the data it held before this method was 1714 * called. 1715 * <BR><BR> 1716 * This method may be used regardless of whether the server is listening for 1717 * client connections, and regardless of whether add operations are allowed in 1718 * the server. 1719 * 1720 * @param entries The entries to be added to the server. 1721 * 1722 * @throws LDAPException If a problem is encountered while attempting to add 1723 * any of the provided entries. 1724 */ 1725 public void addEntries(@NotNull final Entry... entries) 1726 throws LDAPException 1727 { 1728 addEntries(Arrays.asList(entries)); 1729 } 1730 1731 1732 1733 /** 1734 * Attempts to add all of the provided entries to the server. If a problem is 1735 * encountered while attempting to add any of the provided entries, then the 1736 * server will remain populated with the data it held before this method was 1737 * called. 1738 * <BR><BR> 1739 * This method may be used regardless of whether the server is listening for 1740 * client connections, and regardless of whether add operations are allowed in 1741 * the server. 1742 * 1743 * @param entries The entries to be added to the server. 1744 * 1745 * @throws LDAPException If a problem is encountered while attempting to add 1746 * any of the provided entries. 1747 */ 1748 public void addEntries(@NotNull final List<? extends Entry> entries) 1749 throws LDAPException 1750 { 1751 inMemoryHandler.addEntries(entries); 1752 } 1753 1754 1755 1756 /** 1757 * Attempts to add a set of entries provided in LDIF form in which each 1758 * element of the provided array is a line of the LDIF representation, with 1759 * empty strings as separators between entries (as you would have for blank 1760 * lines in an LDIF file). If a problem is encountered while attempting to 1761 * add any of the provided entries, then the server will remain populated with 1762 * the data it held before this method was called. 1763 * <BR><BR> 1764 * This method may be used regardless of whether the server is listening for 1765 * client connections, and regardless of whether add operations are allowed in 1766 * the server. 1767 * 1768 * @param ldifEntryLines The lines comprising the LDIF representation of the 1769 * entries to be added. 1770 * 1771 * @throws LDAPException If a problem is encountered while attempting to add 1772 * any of the provided entries. 1773 */ 1774 public void addEntries(@NotNull final String... ldifEntryLines) 1775 throws LDAPException 1776 { 1777 final ByteStringBuffer buffer = new ByteStringBuffer(); 1778 for (final String line : ldifEntryLines) 1779 { 1780 buffer.append(line); 1781 buffer.append(StaticUtils.EOL_BYTES); 1782 } 1783 1784 final ArrayList<Entry> entryList = new ArrayList<>(10); 1785 final LDIFReader reader = new LDIFReader(buffer.asInputStream()); 1786 1787 final Schema schema = getSchema(); 1788 if (schema != null) 1789 { 1790 reader.setSchema(schema); 1791 } 1792 1793 while (true) 1794 { 1795 try 1796 { 1797 final Entry entry = reader.readEntry(); 1798 if (entry == null) 1799 { 1800 break; 1801 } 1802 else 1803 { 1804 entryList.add(entry); 1805 } 1806 } 1807 catch (final Exception e) 1808 { 1809 Debug.debugException(e); 1810 throw new LDAPException(ResultCode.PARAM_ERROR, 1811 ERR_MEM_DS_ADD_ENTRIES_LDIF_PARSE_EXCEPTION.get( 1812 StaticUtils.getExceptionMessage(e)), 1813 e); 1814 } 1815 } 1816 1817 addEntries(entryList); 1818 } 1819 1820 1821 1822 /** 1823 * Processes a simple bind request with the provided DN and password. Note 1824 * that the bind processing will verify that the provided credentials are 1825 * valid, but it will not alter the server in any way. 1826 * 1827 * @param bindDN The bind DN for the bind operation. 1828 * @param password The password for the simple bind operation. 1829 * 1830 * @return The result of processing the bind operation. 1831 * 1832 * @throws LDAPException If the server rejects the bind request, or if a 1833 * problem occurs while sending the request or reading 1834 * the response. 1835 */ 1836 @NotNull() 1837 public BindResult bind(@Nullable final String bindDN, 1838 @Nullable final String password) 1839 throws LDAPException 1840 { 1841 return bind(new SimpleBindRequest(bindDN, password)); 1842 } 1843 1844 1845 1846 /** 1847 * Processes the provided bind request. Only simple and SASL PLAIN bind 1848 * requests are supported. Note that the bind processing will verify that the 1849 * provided credentials are valid, but it will not alter the server in any 1850 * way. 1851 * 1852 * @param bindRequest The bind request to be processed. It must not be 1853 * {@code null}. 1854 * 1855 * @return The result of processing the bind operation. 1856 * 1857 * @throws LDAPException If the server rejects the bind request, or if a 1858 * problem occurs while sending the request or reading 1859 * the response. 1860 */ 1861 @NotNull() 1862 public BindResult bind(@NotNull final BindRequest bindRequest) 1863 throws LDAPException 1864 { 1865 final ArrayList<Control> requestControlList = 1866 new ArrayList<>(bindRequest.getControlList()); 1867 requestControlList.add(new Control( 1868 InMemoryRequestHandler.OID_INTERNAL_OPERATION_REQUEST_CONTROL, false)); 1869 1870 final BindRequestProtocolOp bindOp; 1871 if (bindRequest instanceof SimpleBindRequest) 1872 { 1873 final SimpleBindRequest r = (SimpleBindRequest) bindRequest; 1874 bindOp = new BindRequestProtocolOp(r.getBindDN(), 1875 r.getPassword().getValue()); 1876 } 1877 else if (bindRequest instanceof PLAINBindRequest) 1878 { 1879 final PLAINBindRequest r = (PLAINBindRequest) bindRequest; 1880 1881 // Create the byte array that should comprise the credentials. 1882 final byte[] authZIDBytes = StaticUtils.getBytes(r.getAuthorizationID()); 1883 final byte[] authNIDBytes = StaticUtils.getBytes(r.getAuthenticationID()); 1884 final byte[] passwordBytes = r.getPasswordBytes(); 1885 1886 final byte[] credBytes = new byte[2 + authZIDBytes.length + 1887 authNIDBytes.length + passwordBytes.length]; 1888 System.arraycopy(authZIDBytes, 0, credBytes, 0, authZIDBytes.length); 1889 1890 int pos = authZIDBytes.length + 1; 1891 System.arraycopy(authNIDBytes, 0, credBytes, pos, authNIDBytes.length); 1892 1893 pos += authNIDBytes.length + 1; 1894 System.arraycopy(passwordBytes, 0, credBytes, pos, passwordBytes.length); 1895 1896 bindOp = new BindRequestProtocolOp(null, "PLAIN", 1897 new ASN1OctetString(credBytes)); 1898 } 1899 else 1900 { 1901 throw new LDAPException(ResultCode.AUTH_METHOD_NOT_SUPPORTED, 1902 ERR_MEM_DS_UNSUPPORTED_BIND_TYPE.get()); 1903 } 1904 1905 final LDAPMessage responseMessage = inMemoryHandler.processBindRequest(1, 1906 bindOp, requestControlList); 1907 final BindResponseProtocolOp bindResponse = 1908 responseMessage.getBindResponseProtocolOp(); 1909 1910 final BindResult bindResult = new BindResult(new LDAPResult( 1911 responseMessage.getMessageID(), 1912 ResultCode.valueOf(bindResponse.getResultCode()), 1913 bindResponse.getDiagnosticMessage(), bindResponse.getMatchedDN(), 1914 bindResponse.getReferralURLs(), responseMessage.getControls())); 1915 1916 switch (bindResponse.getResultCode()) 1917 { 1918 case ResultCode.SUCCESS_INT_VALUE: 1919 return bindResult; 1920 default: 1921 throw new LDAPException(bindResult); 1922 } 1923 } 1924 1925 1926 1927 /** 1928 * {@inheritDoc} 1929 * <BR><BR> 1930 * This method may be used regardless of whether the server is listening for 1931 * client connections, and regardless of whether compare operations are 1932 * allowed in the server. 1933 */ 1934 @Override() 1935 @NotNull() 1936 public CompareResult compare(@NotNull final String dn, 1937 @NotNull final String attributeName, 1938 @NotNull final String assertionValue) 1939 throws LDAPException 1940 { 1941 return compare(new CompareRequest(dn, attributeName, assertionValue)); 1942 } 1943 1944 1945 1946 /** 1947 * {@inheritDoc} 1948 * <BR><BR> 1949 * This method may be used regardless of whether the server is listening for 1950 * client connections, and regardless of whether compare operations are 1951 * allowed in the server. 1952 */ 1953 @Override() 1954 @NotNull() 1955 public CompareResult compare(@NotNull final CompareRequest compareRequest) 1956 throws LDAPException 1957 { 1958 final ArrayList<Control> requestControlList = 1959 new ArrayList<>(compareRequest.getControlList()); 1960 requestControlList.add(new Control( 1961 InMemoryRequestHandler.OID_INTERNAL_OPERATION_REQUEST_CONTROL, false)); 1962 1963 final LDAPMessage responseMessage = inMemoryHandler.processCompareRequest(1, 1964 new CompareRequestProtocolOp(compareRequest.getDN(), 1965 compareRequest.getAttributeName(), 1966 compareRequest.getRawAssertionValue()), 1967 requestControlList); 1968 1969 final CompareResponseProtocolOp compareResponse = 1970 responseMessage.getCompareResponseProtocolOp(); 1971 1972 final LDAPResult compareResult = new LDAPResult( 1973 responseMessage.getMessageID(), 1974 ResultCode.valueOf(compareResponse.getResultCode()), 1975 compareResponse.getDiagnosticMessage(), compareResponse.getMatchedDN(), 1976 compareResponse.getReferralURLs(), responseMessage.getControls()); 1977 1978 switch (compareResponse.getResultCode()) 1979 { 1980 case ResultCode.COMPARE_TRUE_INT_VALUE: 1981 case ResultCode.COMPARE_FALSE_INT_VALUE: 1982 return new CompareResult(compareResult); 1983 default: 1984 throw new LDAPException(compareResult); 1985 } 1986 } 1987 1988 1989 1990 /** 1991 * {@inheritDoc} 1992 * <BR><BR> 1993 * This method may be used regardless of whether the server is listening for 1994 * client connections, and regardless of whether compare operations are 1995 * allowed in the server. 1996 */ 1997 @Override() 1998 @NotNull() 1999 public CompareResult compare( 2000 @NotNull final ReadOnlyCompareRequest compareRequest) 2001 throws LDAPException 2002 { 2003 return compare(compareRequest.duplicate()); 2004 } 2005 2006 2007 2008 /** 2009 * {@inheritDoc} 2010 * <BR><BR> 2011 * This method may be used regardless of whether the server is listening for 2012 * client connections, and regardless of whether delete operations are 2013 * allowed in the server. 2014 */ 2015 @Override() 2016 @NotNull() 2017 public LDAPResult delete(@NotNull final String dn) 2018 throws LDAPException 2019 { 2020 return delete(new DeleteRequest(dn)); 2021 } 2022 2023 2024 2025 /** 2026 * {@inheritDoc} 2027 * <BR><BR> 2028 * This method may be used regardless of whether the server is listening for 2029 * client connections, and regardless of whether delete operations are 2030 * allowed in the server. 2031 */ 2032 @Override() 2033 @NotNull() 2034 public LDAPResult delete(@NotNull final DeleteRequest deleteRequest) 2035 throws LDAPException 2036 { 2037 return inMemoryHandler.delete(deleteRequest); 2038 } 2039 2040 2041 2042 /** 2043 * {@inheritDoc} 2044 * <BR><BR> 2045 * This method may be used regardless of whether the server is listening for 2046 * client connections, and regardless of whether delete operations are 2047 * allowed in the server. 2048 */ 2049 @Override() 2050 @NotNull() 2051 public LDAPResult delete(@NotNull final ReadOnlyDeleteRequest deleteRequest) 2052 throws LDAPException 2053 { 2054 return delete(deleteRequest.duplicate()); 2055 } 2056 2057 2058 2059 /** 2060 * Attempts to delete the specified entry and all entries below it from the 2061 * server. 2062 * <BR><BR> 2063 * This method may be used regardless of whether the server is listening for 2064 * client connections, and regardless of whether compare operations are 2065 * allowed in the server. 2066 * 2067 * @param baseDN The DN of the entry to remove, along with all of its 2068 * subordinates. 2069 * 2070 * @return The number of entries removed from the server, or zero if the 2071 * specified entry was not found. 2072 * 2073 * @throws LDAPException If a problem is encountered while attempting to 2074 * remove the entries. 2075 */ 2076 public int deleteSubtree(@NotNull final String baseDN) 2077 throws LDAPException 2078 { 2079 return inMemoryHandler.deleteSubtree(baseDN); 2080 } 2081 2082 2083 2084 /** 2085 * Processes an extended request with the provided request OID. Note that 2086 * because some types of extended operations return unusual result codes under 2087 * "normal" conditions, the server may not always throw an exception for a 2088 * failed extended operation like it does for other types of operations. It 2089 * will throw an exception under conditions where there appears to be a 2090 * problem with the connection or the server to which the connection is 2091 * established, but there may be many circumstances in which an extended 2092 * operation is not processed correctly but this method does not throw an 2093 * exception. In the event that no exception is thrown, it is the 2094 * responsibility of the caller to interpret the result to determine whether 2095 * the operation was processed as expected. 2096 * <BR><BR> 2097 * This method may be used regardless of whether the server is listening for 2098 * client connections, and regardless of whether extended operations are 2099 * allowed in the server. 2100 * 2101 * @param requestOID The OID for the extended request to process. It must 2102 * not be {@code null}. 2103 * 2104 * @return The extended result object that provides information about the 2105 * result of the request processing. It may or may not indicate that 2106 * the operation was successful. 2107 * 2108 * @throws LDAPException If a problem occurs while sending the request or 2109 * reading the response. 2110 */ 2111 @NotNull() 2112 public ExtendedResult processExtendedOperation( 2113 @NotNull final String requestOID) 2114 throws LDAPException 2115 { 2116 Validator.ensureNotNull(requestOID); 2117 2118 return processExtendedOperation(new ExtendedRequest(requestOID)); 2119 } 2120 2121 2122 2123 /** 2124 * Processes an extended request with the provided request OID and value. 2125 * Note that because some types of extended operations return unusual result 2126 * codes under "normal" conditions, the server may not always throw an 2127 * exception for a failed extended operation like it does for other types of 2128 * operations. It will throw an exception under conditions where there 2129 * appears to be a problem with the connection or the server to which the 2130 * connection is established, but there may be many circumstances in which an 2131 * extended operation is not processed correctly but this method does not 2132 * throw an exception. In the event that no exception is thrown, it is the 2133 * responsibility of the caller to interpret the result to determine whether 2134 * the operation was processed as expected. 2135 * <BR><BR> 2136 * This method may be used regardless of whether the server is listening for 2137 * client connections, and regardless of whether extended operations are 2138 * allowed in the server. 2139 * 2140 * @param requestOID The OID for the extended request to process. It must 2141 * not be {@code null}. 2142 * @param requestValue The encoded value for the extended request to 2143 * process. It may be {@code null} if there does not 2144 * need to be a value for the requested operation. 2145 * 2146 * @return The extended result object that provides information about the 2147 * result of the request processing. It may or may not indicate that 2148 * the operation was successful. 2149 * 2150 * @throws LDAPException If a problem occurs while sending the request or 2151 * reading the response. 2152 */ 2153 @NotNull() 2154 public ExtendedResult processExtendedOperation( 2155 @NotNull final String requestOID, 2156 @Nullable final ASN1OctetString requestValue) 2157 throws LDAPException 2158 { 2159 Validator.ensureNotNull(requestOID); 2160 2161 return processExtendedOperation(new ExtendedRequest(requestOID, 2162 requestValue)); 2163 } 2164 2165 2166 2167 /** 2168 * Processes the provided extended request. Note that because some types of 2169 * extended operations return unusual result codes under "normal" conditions, 2170 * the server may not always throw an exception for a failed extended 2171 * operation like it does for other types of operations. It will throw an 2172 * exception under conditions where there appears to be a problem with the 2173 * connection or the server to which the connection is established, but there 2174 * may be many circumstances in which an extended operation is not processed 2175 * correctly but this method does not throw an exception. In the event that 2176 * no exception is thrown, it is the responsibility of the caller to interpret 2177 * the result to determine whether the operation was processed as expected. 2178 * <BR><BR> 2179 * This method may be used regardless of whether the server is listening for 2180 * client connections, and regardless of whether extended operations are 2181 * allowed in the server. 2182 * 2183 * @param extendedRequest The extended request to be processed. It must not 2184 * be {@code null}. 2185 * 2186 * @return The extended result object that provides information about the 2187 * result of the request processing. It may or may not indicate that 2188 * the operation was successful. 2189 * 2190 * @throws LDAPException If a problem occurs while sending the request or 2191 * reading the response. 2192 */ 2193 @NotNull() 2194 public ExtendedResult processExtendedOperation( 2195 @NotNull final ExtendedRequest extendedRequest) 2196 throws LDAPException 2197 { 2198 Validator.ensureNotNull(extendedRequest); 2199 2200 final ArrayList<Control> requestControlList = 2201 new ArrayList<>(extendedRequest.getControlList()); 2202 requestControlList.add(new Control( 2203 InMemoryRequestHandler.OID_INTERNAL_OPERATION_REQUEST_CONTROL, false)); 2204 2205 2206 final LDAPMessage responseMessage = 2207 inMemoryHandler.processExtendedRequest(1, 2208 new ExtendedRequestProtocolOp(extendedRequest.getOID(), 2209 extendedRequest.getValue()), 2210 requestControlList); 2211 2212 final ExtendedResponseProtocolOp extendedResponse = 2213 responseMessage.getExtendedResponseProtocolOp(); 2214 2215 final ResultCode rc = ResultCode.valueOf(extendedResponse.getResultCode()); 2216 2217 final String[] referralURLs; 2218 final List<String> referralURLList = extendedResponse.getReferralURLs(); 2219 if ((referralURLList == null) || referralURLList.isEmpty()) 2220 { 2221 referralURLs = StaticUtils.NO_STRINGS; 2222 } 2223 else 2224 { 2225 referralURLs = new String[referralURLList.size()]; 2226 referralURLList.toArray(referralURLs); 2227 } 2228 2229 final Control[] responseControls; 2230 final List<Control> controlList = responseMessage.getControls(); 2231 if ((controlList == null) || controlList.isEmpty()) 2232 { 2233 responseControls = StaticUtils.NO_CONTROLS; 2234 } 2235 else 2236 { 2237 responseControls = new Control[controlList.size()]; 2238 controlList.toArray(responseControls); 2239 } 2240 2241 final ExtendedResult extendedResult = new ExtendedResult( 2242 responseMessage.getMessageID(), rc, 2243 extendedResponse.getDiagnosticMessage(), 2244 extendedResponse.getMatchedDN(), referralURLs, 2245 extendedResponse.getResponseOID(), 2246 extendedResponse.getResponseValue(), responseControls); 2247 2248 if ((extendedResult.getOID() == null) && 2249 (extendedResult.getValue() == null)) 2250 { 2251 switch (rc.intValue()) 2252 { 2253 case ResultCode.OPERATIONS_ERROR_INT_VALUE: 2254 case ResultCode.PROTOCOL_ERROR_INT_VALUE: 2255 case ResultCode.BUSY_INT_VALUE: 2256 case ResultCode.UNAVAILABLE_INT_VALUE: 2257 case ResultCode.OTHER_INT_VALUE: 2258 case ResultCode.SERVER_DOWN_INT_VALUE: 2259 case ResultCode.LOCAL_ERROR_INT_VALUE: 2260 case ResultCode.ENCODING_ERROR_INT_VALUE: 2261 case ResultCode.DECODING_ERROR_INT_VALUE: 2262 case ResultCode.TIMEOUT_INT_VALUE: 2263 case ResultCode.NO_MEMORY_INT_VALUE: 2264 case ResultCode.CONNECT_ERROR_INT_VALUE: 2265 throw new LDAPException(extendedResult); 2266 } 2267 } 2268 2269 return extendedResult; 2270 } 2271 2272 2273 2274 /** 2275 * {@inheritDoc} 2276 * <BR><BR> 2277 * This method may be used regardless of whether the server is listening for 2278 * client connections, and regardless of whether modify operations are allowed 2279 * in the server. 2280 */ 2281 @Override() 2282 @NotNull() 2283 public LDAPResult modify(@NotNull final String dn, 2284 @NotNull final Modification mod) 2285 throws LDAPException 2286 { 2287 return modify(new ModifyRequest(dn, mod)); 2288 } 2289 2290 2291 2292 /** 2293 * {@inheritDoc} 2294 * <BR><BR> 2295 * This method may be used regardless of whether the server is listening for 2296 * client connections, and regardless of whether modify operations are allowed 2297 * in the server. 2298 */ 2299 @Override() 2300 @NotNull() 2301 public LDAPResult modify(@NotNull final String dn, 2302 @NotNull final Modification... mods) 2303 throws LDAPException 2304 { 2305 return modify(new ModifyRequest(dn, mods)); 2306 } 2307 2308 2309 2310 /** 2311 * {@inheritDoc} 2312 * <BR><BR> 2313 * This method may be used regardless of whether the server is listening for 2314 * client connections, and regardless of whether modify operations are allowed 2315 * in the server. 2316 */ 2317 @Override() 2318 @NotNull() 2319 public LDAPResult modify(@NotNull final String dn, 2320 @NotNull final List<Modification> mods) 2321 throws LDAPException 2322 { 2323 return modify(new ModifyRequest(dn, mods)); 2324 } 2325 2326 2327 2328 /** 2329 * {@inheritDoc} 2330 * <BR><BR> 2331 * This method may be used regardless of whether the server is listening for 2332 * client connections, and regardless of whether modify operations are allowed 2333 * in the server. 2334 */ 2335 @Override() 2336 @NotNull() 2337 public LDAPResult modify(@NotNull final String... ldifModificationLines) 2338 throws LDIFException, LDAPException 2339 { 2340 return modify(new ModifyRequest(ldifModificationLines)); 2341 } 2342 2343 2344 2345 /** 2346 * {@inheritDoc} 2347 * <BR><BR> 2348 * This method may be used regardless of whether the server is listening for 2349 * client connections, and regardless of whether modify operations are allowed 2350 * in the server. 2351 */ 2352 @Override() 2353 @NotNull() 2354 public LDAPResult modify(@NotNull final ModifyRequest modifyRequest) 2355 throws LDAPException 2356 { 2357 return inMemoryHandler.modify(modifyRequest); 2358 } 2359 2360 2361 2362 /** 2363 * {@inheritDoc} 2364 * <BR><BR> 2365 * This method may be used regardless of whether the server is listening for 2366 * client connections, and regardless of whether modify operations are allowed 2367 * in the server. 2368 */ 2369 @Override() 2370 @NotNull() 2371 public LDAPResult modify(@NotNull final ReadOnlyModifyRequest modifyRequest) 2372 throws LDAPException 2373 { 2374 return modify(modifyRequest.duplicate()); 2375 } 2376 2377 2378 2379 /** 2380 * {@inheritDoc} 2381 * <BR><BR> 2382 * This method may be used regardless of whether the server is listening for 2383 * client connections, and regardless of whether modify DN operations are 2384 * allowed in the server. 2385 */ 2386 @Override() 2387 @NotNull() 2388 public LDAPResult modifyDN(@NotNull final String dn, 2389 @NotNull final String newRDN, 2390 final boolean deleteOldRDN) 2391 throws LDAPException 2392 { 2393 return modifyDN(new ModifyDNRequest(dn, newRDN, deleteOldRDN)); 2394 } 2395 2396 2397 2398 /** 2399 * {@inheritDoc} 2400 * <BR><BR> 2401 * This method may be used regardless of whether the server is listening for 2402 * client connections, and regardless of whether modify DN operations are 2403 * allowed in the server. 2404 */ 2405 @Override() 2406 @NotNull() 2407 public LDAPResult modifyDN(@NotNull final String dn, 2408 @NotNull final String newRDN, 2409 final boolean deleteOldRDN, 2410 @Nullable final String newSuperiorDN) 2411 throws LDAPException 2412 { 2413 return modifyDN(new ModifyDNRequest(dn, newRDN, deleteOldRDN, 2414 newSuperiorDN)); 2415 } 2416 2417 2418 2419 /** 2420 * {@inheritDoc} 2421 * <BR><BR> 2422 * This method may be used regardless of whether the server is listening for 2423 * client connections, and regardless of whether modify DN operations are 2424 * allowed in the server. 2425 */ 2426 @Override() 2427 @NotNull() 2428 public LDAPResult modifyDN(@NotNull final ModifyDNRequest modifyDNRequest) 2429 throws LDAPException 2430 { 2431 return inMemoryHandler.modifyDN(modifyDNRequest); 2432 } 2433 2434 2435 2436 /** 2437 * {@inheritDoc} 2438 * <BR><BR> 2439 * This method may be used regardless of whether the server is listening for 2440 * client connections, and regardless of whether modify DN operations are 2441 * allowed in the server. 2442 */ 2443 @Override() 2444 @NotNull() 2445 public LDAPResult modifyDN( 2446 @NotNull final ReadOnlyModifyDNRequest modifyDNRequest) 2447 throws LDAPException 2448 { 2449 return modifyDN(modifyDNRequest.duplicate()); 2450 } 2451 2452 2453 2454 /** 2455 * {@inheritDoc} 2456 * <BR><BR> 2457 * This method may be used regardless of whether the server is listening for 2458 * client connections, and regardless of whether search operations are allowed 2459 * in the server. 2460 */ 2461 @Override() 2462 @NotNull() 2463 public SearchResult search(@NotNull final String baseDN, 2464 @NotNull final SearchScope scope, 2465 @NotNull final String filter, 2466 @Nullable final String... attributes) 2467 throws LDAPSearchException 2468 { 2469 return search(new SearchRequest(baseDN, scope, parseFilter(filter), 2470 attributes)); 2471 } 2472 2473 2474 2475 /** 2476 * {@inheritDoc} 2477 * <BR><BR> 2478 * This method may be used regardless of whether the server is listening for 2479 * client connections, and regardless of whether search operations are allowed 2480 * in the server. 2481 */ 2482 @Override() 2483 @NotNull() 2484 public SearchResult search(@NotNull final String baseDN, 2485 @NotNull final SearchScope scope, 2486 @NotNull final Filter filter, 2487 @Nullable final String... attributes) 2488 throws LDAPSearchException 2489 { 2490 return search(new SearchRequest(baseDN, scope, filter, attributes)); 2491 } 2492 2493 2494 2495 /** 2496 * {@inheritDoc} 2497 * <BR><BR> 2498 * This method may be used regardless of whether the server is listening for 2499 * client connections, and regardless of whether search operations are allowed 2500 * in the server. 2501 */ 2502 @Override() 2503 @NotNull() 2504 public SearchResult search( 2505 @Nullable final SearchResultListener searchResultListener, 2506 @NotNull final String baseDN, @NotNull final SearchScope scope, 2507 @NotNull final String filter, 2508 @Nullable final String... attributes) 2509 throws LDAPSearchException 2510 { 2511 return search(new SearchRequest(searchResultListener, baseDN, scope, 2512 parseFilter(filter), attributes)); 2513 } 2514 2515 2516 2517 /** 2518 * {@inheritDoc} 2519 * <BR><BR> 2520 * This method may be used regardless of whether the server is listening for 2521 * client connections, and regardless of whether search operations are allowed 2522 * in the server. 2523 */ 2524 @Override() 2525 @NotNull() 2526 public SearchResult search( 2527 @Nullable final SearchResultListener searchResultListener, 2528 @NotNull final String baseDN, @NotNull final SearchScope scope, 2529 @NotNull final Filter filter, 2530 @Nullable final String... attributes) 2531 throws LDAPSearchException 2532 { 2533 return search(new SearchRequest(searchResultListener, baseDN, scope, 2534 filter, attributes)); 2535 } 2536 2537 2538 2539 /** 2540 * {@inheritDoc} 2541 * <BR><BR> 2542 * This method may be used regardless of whether the server is listening for 2543 * client connections, and regardless of whether search operations are allowed 2544 * in the server. 2545 */ 2546 @Override() 2547 @NotNull() 2548 public SearchResult search(@NotNull final String baseDN, 2549 @NotNull final SearchScope scope, 2550 @NotNull final DereferencePolicy derefPolicy, 2551 final int sizeLimit, final int timeLimit, 2552 final boolean typesOnly, 2553 @NotNull final String filter, 2554 @Nullable final String... attributes) 2555 throws LDAPSearchException 2556 { 2557 return search(new SearchRequest(baseDN, scope, derefPolicy, sizeLimit, 2558 timeLimit, typesOnly, parseFilter(filter), attributes)); 2559 } 2560 2561 2562 2563 /** 2564 * {@inheritDoc} 2565 * <BR><BR> 2566 * This method may be used regardless of whether the server is listening for 2567 * client connections, and regardless of whether search operations are allowed 2568 * in the server. 2569 */ 2570 @Override() 2571 @NotNull() 2572 public SearchResult search(@NotNull final String baseDN, 2573 @NotNull final SearchScope scope, 2574 @NotNull final DereferencePolicy derefPolicy, 2575 final int sizeLimit, final int timeLimit, 2576 final boolean typesOnly, 2577 @NotNull final Filter filter, 2578 @Nullable final String... attributes) 2579 throws LDAPSearchException 2580 { 2581 return search(new SearchRequest(baseDN, scope, derefPolicy, sizeLimit, 2582 timeLimit, typesOnly, filter, attributes)); 2583 } 2584 2585 2586 2587 /** 2588 * {@inheritDoc} 2589 * <BR><BR> 2590 * This method may be used regardless of whether the server is listening for 2591 * client connections, and regardless of whether search operations are allowed 2592 * in the server. 2593 */ 2594 @Override() 2595 @NotNull() 2596 public SearchResult search( 2597 @Nullable final SearchResultListener searchResultListener, 2598 @NotNull final String baseDN, @NotNull final SearchScope scope, 2599 @NotNull final DereferencePolicy derefPolicy, final int sizeLimit, 2600 final int timeLimit, final boolean typesOnly, 2601 @NotNull final String filter, 2602 @Nullable final String... attributes) 2603 throws LDAPSearchException 2604 { 2605 return search(new SearchRequest(searchResultListener, baseDN, scope, 2606 derefPolicy, sizeLimit, timeLimit, typesOnly, parseFilter(filter), 2607 attributes)); 2608 } 2609 2610 2611 2612 /** 2613 * {@inheritDoc} 2614 * <BR><BR> 2615 * This method may be used regardless of whether the server is listening for 2616 * client connections, and regardless of whether search operations are allowed 2617 * in the server. 2618 */ 2619 @Override() 2620 @NotNull() 2621 public SearchResult search( 2622 @Nullable final SearchResultListener searchResultListener, 2623 @NotNull final String baseDN, @NotNull final SearchScope scope, 2624 @NotNull final DereferencePolicy derefPolicy, final int sizeLimit, 2625 final int timeLimit, final boolean typesOnly, 2626 @NotNull final Filter filter, 2627 @Nullable final String... attributes) 2628 throws LDAPSearchException 2629 { 2630 return search(new SearchRequest(searchResultListener, baseDN, scope, 2631 derefPolicy, sizeLimit, timeLimit, typesOnly, filter, attributes)); 2632 } 2633 2634 2635 2636 /** 2637 * {@inheritDoc} 2638 * <BR><BR> 2639 * This method may be used regardless of whether the server is listening for 2640 * client connections, and regardless of whether search operations are allowed 2641 * in the server. 2642 */ 2643 @Override() 2644 @NotNull() 2645 public SearchResult search(@NotNull final SearchRequest searchRequest) 2646 throws LDAPSearchException 2647 { 2648 final ArrayList<Control> requestControlList = 2649 new ArrayList<>(searchRequest.getControlList()); 2650 requestControlList.add(new Control( 2651 InMemoryRequestHandler.OID_INTERNAL_OPERATION_REQUEST_CONTROL, false)); 2652 2653 final List<SearchResultEntry> entryList = 2654 new ArrayList<>(10); 2655 final List<SearchResultReference> referenceList = 2656 new ArrayList<>(10); 2657 2658 final LDAPMessage responseMessage = inMemoryHandler.processSearchRequest(1, 2659 new SearchRequestProtocolOp(searchRequest.getBaseDN(), 2660 searchRequest.getScope(), searchRequest.getDereferencePolicy(), 2661 searchRequest.getSizeLimit(), searchRequest.getTimeLimitSeconds(), 2662 searchRequest.typesOnly(), searchRequest.getFilter(), 2663 searchRequest.getAttributeList()), 2664 requestControlList, entryList, referenceList); 2665 2666 2667 final List<SearchResultEntry> returnEntryList; 2668 final List<SearchResultReference> returnReferenceList; 2669 final SearchResultListener searchListener = 2670 searchRequest.getSearchResultListener(); 2671 if (searchListener == null) 2672 { 2673 returnEntryList = Collections.unmodifiableList(entryList); 2674 returnReferenceList = Collections.unmodifiableList(referenceList); 2675 } 2676 else 2677 { 2678 returnEntryList = null; 2679 returnReferenceList = null; 2680 2681 for (final SearchResultEntry e : entryList) 2682 { 2683 searchListener.searchEntryReturned(e); 2684 } 2685 2686 for (final SearchResultReference r : referenceList) 2687 { 2688 searchListener.searchReferenceReturned(r); 2689 } 2690 } 2691 2692 2693 final SearchResultDoneProtocolOp searchDone = 2694 responseMessage.getSearchResultDoneProtocolOp(); 2695 2696 final ResultCode rc = ResultCode.valueOf(searchDone.getResultCode()); 2697 2698 final String[] referralURLs; 2699 final List<String> referralURLList = searchDone.getReferralURLs(); 2700 if ((referralURLList == null) || referralURLList.isEmpty()) 2701 { 2702 referralURLs = StaticUtils.NO_STRINGS; 2703 } 2704 else 2705 { 2706 referralURLs = new String[referralURLList.size()]; 2707 referralURLList.toArray(referralURLs); 2708 } 2709 2710 final Control[] responseControls; 2711 final List<Control> controlList = responseMessage.getControls(); 2712 if ((controlList == null) || controlList.isEmpty()) 2713 { 2714 responseControls = StaticUtils.NO_CONTROLS; 2715 } 2716 else 2717 { 2718 responseControls = new Control[controlList.size()]; 2719 controlList.toArray(responseControls); 2720 } 2721 2722 final SearchResult searchResult =new SearchResult( 2723 responseMessage.getMessageID(), rc, searchDone.getDiagnosticMessage(), 2724 searchDone.getMatchedDN(), referralURLs, returnEntryList, 2725 returnReferenceList, entryList.size(), referenceList.size(), 2726 responseControls); 2727 2728 if (rc == ResultCode.SUCCESS) 2729 { 2730 return searchResult; 2731 } 2732 else 2733 { 2734 throw new LDAPSearchException(searchResult); 2735 } 2736 } 2737 2738 2739 2740 /** 2741 * {@inheritDoc} 2742 * <BR><BR> 2743 * This method may be used regardless of whether the server is listening for 2744 * client connections, and regardless of whether search operations are allowed 2745 * in the server. 2746 */ 2747 @Override() 2748 @NotNull() 2749 public SearchResult search(@NotNull final ReadOnlySearchRequest searchRequest) 2750 throws LDAPSearchException 2751 { 2752 return search(searchRequest.duplicate()); 2753 } 2754 2755 2756 2757 /** 2758 * {@inheritDoc} 2759 * <BR><BR> 2760 * This method may be used regardless of whether the server is listening for 2761 * client connections, and regardless of whether search operations are allowed 2762 * in the server. 2763 */ 2764 @Override() 2765 @Nullable() 2766 public SearchResultEntry searchForEntry(@NotNull final String baseDN, 2767 @NotNull final SearchScope scope, 2768 @NotNull final String filter, 2769 @Nullable final String... attributes) 2770 throws LDAPSearchException 2771 { 2772 return searchForEntry(new SearchRequest(baseDN, scope, parseFilter(filter), 2773 attributes)); 2774 } 2775 2776 2777 2778 /** 2779 * {@inheritDoc} 2780 * <BR><BR> 2781 * This method may be used regardless of whether the server is listening for 2782 * client connections, and regardless of whether search operations are allowed 2783 * in the server. 2784 */ 2785 @Override() 2786 @Nullable() 2787 public SearchResultEntry searchForEntry(@NotNull final String baseDN, 2788 @NotNull final SearchScope scope, 2789 @NotNull final Filter filter, 2790 @Nullable final String... attributes) 2791 throws LDAPSearchException 2792 { 2793 return searchForEntry(new SearchRequest(baseDN, scope, filter, attributes)); 2794 } 2795 2796 2797 2798 /** 2799 * {@inheritDoc} 2800 * <BR><BR> 2801 * This method may be used regardless of whether the server is listening for 2802 * client connections, and regardless of whether search operations are allowed 2803 * in the server. 2804 */ 2805 @Override() 2806 @Nullable() 2807 public SearchResultEntry searchForEntry(@NotNull final String baseDN, 2808 @NotNull final SearchScope scope, 2809 @NotNull final DereferencePolicy derefPolicy, 2810 final int timeLimit, final boolean typesOnly, 2811 @NotNull final String filter, 2812 @Nullable final String... attributes) 2813 throws LDAPSearchException 2814 { 2815 return searchForEntry(new SearchRequest(baseDN, scope, derefPolicy, 1, 2816 timeLimit, typesOnly, parseFilter(filter), attributes)); 2817 } 2818 2819 2820 2821 /** 2822 * {@inheritDoc} 2823 * <BR><BR> 2824 * This method may be used regardless of whether the server is listening for 2825 * client connections, and regardless of whether search operations are allowed 2826 * in the server. 2827 */ 2828 @Override() 2829 @Nullable() 2830 public SearchResultEntry searchForEntry(@NotNull final String baseDN, 2831 @NotNull final SearchScope scope, 2832 @NotNull final DereferencePolicy derefPolicy, 2833 final int timeLimit, final boolean typesOnly, 2834 @NotNull final Filter filter, 2835 @Nullable final String... attributes) 2836 throws LDAPSearchException 2837 { 2838 return searchForEntry(new SearchRequest(baseDN, scope, derefPolicy, 1, 2839 timeLimit, typesOnly, filter, attributes)); 2840 } 2841 2842 2843 2844 /** 2845 * {@inheritDoc} 2846 * <BR><BR> 2847 * This method may be used regardless of whether the server is listening for 2848 * client connections, and regardless of whether search operations are allowed 2849 * in the server. 2850 */ 2851 @Override() 2852 @Nullable() 2853 public SearchResultEntry searchForEntry( 2854 @NotNull final SearchRequest searchRequest) 2855 throws LDAPSearchException 2856 { 2857 final ArrayList<Control> requestControlList = 2858 new ArrayList<>(searchRequest.getControlList()); 2859 requestControlList.add(new Control( 2860 InMemoryRequestHandler.OID_INTERNAL_OPERATION_REQUEST_CONTROL, false)); 2861 2862 final SearchRequest r; 2863 if ((searchRequest.getSizeLimit() == 1) && 2864 (searchRequest.getSearchResultListener() == null)) 2865 { 2866 r = searchRequest; 2867 } 2868 else 2869 { 2870 r = new SearchRequest(searchRequest.getBaseDN(), searchRequest.getScope(), 2871 searchRequest.getDereferencePolicy(), 1, 2872 searchRequest.getTimeLimitSeconds(), searchRequest.typesOnly(), 2873 searchRequest.getFilter(), searchRequest.getAttributes()); 2874 2875 r.setFollowReferrals(InternalSDKHelper.followReferralsInternal(r)); 2876 r.setReferralConnector(InternalSDKHelper.getReferralConnectorInternal(r)); 2877 r.setResponseTimeoutMillis(searchRequest.getResponseTimeoutMillis(null)); 2878 r.setControls(requestControlList); 2879 } 2880 2881 final SearchResult result; 2882 try 2883 { 2884 result = search(r); 2885 } 2886 catch (final LDAPSearchException lse) 2887 { 2888 Debug.debugException(lse); 2889 2890 if (lse.getResultCode() == ResultCode.NO_SUCH_OBJECT) 2891 { 2892 return null; 2893 } 2894 2895 throw lse; 2896 } 2897 2898 if (result.getEntryCount() == 0) 2899 { 2900 return null; 2901 } 2902 else 2903 { 2904 return result.getSearchEntries().get(0); 2905 } 2906 } 2907 2908 2909 2910 /** 2911 * {@inheritDoc} 2912 * <BR><BR> 2913 * This method may be used regardless of whether the server is listening for 2914 * client connections, and regardless of whether search operations are allowed 2915 * in the server. 2916 */ 2917 @Override() 2918 @Nullable() 2919 public SearchResultEntry searchForEntry( 2920 @NotNull final ReadOnlySearchRequest searchRequest) 2921 throws LDAPSearchException 2922 { 2923 return searchForEntry(searchRequest.duplicate()); 2924 } 2925 2926 2927 2928 /** 2929 * Retrieves the configured list of password attributes. 2930 * 2931 * @return The configured list of password attributes. 2932 */ 2933 @NotNull() 2934 public List<String> getPasswordAttributes() 2935 { 2936 return inMemoryHandler.getPasswordAttributes(); 2937 } 2938 2939 2940 2941 /** 2942 * Retrieves the primary password encoder that has been configured for the 2943 * server. 2944 * 2945 * @return The primary password encoder that has been configured for the 2946 * server. 2947 */ 2948 @Nullable() 2949 public InMemoryPasswordEncoder getPrimaryPasswordEncoder() 2950 { 2951 return inMemoryHandler.getPrimaryPasswordEncoder(); 2952 } 2953 2954 2955 2956 /** 2957 * Retrieves a list of all password encoders configured for the server. 2958 * 2959 * @return A list of all password encoders configured for the server. 2960 */ 2961 @NotNull() 2962 public List<InMemoryPasswordEncoder> getAllPasswordEncoders() 2963 { 2964 return inMemoryHandler.getAllPasswordEncoders(); 2965 } 2966 2967 2968 2969 /** 2970 * Retrieves a list of the passwords contained in the provided entry. 2971 * 2972 * @param entry The entry from which to obtain the list of 2973 * passwords. It must not be {@code null}. 2974 * @param clearPasswordToMatch An optional clear-text password that should 2975 * match the values that are returned. If this 2976 * is {@code null}, then all passwords contained 2977 * in the provided entry will be returned. If 2978 * this is non-{@code null}, then only passwords 2979 * matching the clear-text password will be 2980 * returned. 2981 * 2982 * @return A list of the passwords contained in the provided entry, 2983 * optionally restricted to those matching the provided clear-text 2984 * password, or an empty list if the entry does not contain any 2985 * passwords. 2986 */ 2987 @NotNull() 2988 public List<InMemoryDirectoryServerPassword> getPasswordsInEntry( 2989 @NotNull final Entry entry, 2990 @Nullable final ASN1OctetString clearPasswordToMatch) 2991 { 2992 return inMemoryHandler.getPasswordsInEntry(entry, clearPasswordToMatch); 2993 } 2994 2995 2996 2997 /** 2998 * Parses the provided string as a search filter. 2999 * 3000 * @param s The string to be parsed. 3001 * 3002 * @return The parsed filter. 3003 * 3004 * @throws LDAPSearchException If the provided string could not be parsed as 3005 * a valid search filter. 3006 */ 3007 @NotNull() 3008 private static Filter parseFilter(@NotNull final String s) 3009 throws LDAPSearchException 3010 { 3011 try 3012 { 3013 return Filter.create(s); 3014 } 3015 catch (final LDAPException le) 3016 { 3017 throw new LDAPSearchException(le); 3018 } 3019 } 3020 3021 3022 3023 /** 3024 * Indicates whether the specified entry exists in the server. 3025 * <BR><BR> 3026 * This method may be used regardless of whether the server is listening for 3027 * client connections. 3028 * 3029 * @param dn The DN of the entry for which to make the determination. 3030 * 3031 * @return {@code true} if the entry exists, or {@code false} if not. 3032 * 3033 * @throws LDAPException If a problem is encountered while trying to 3034 * communicate with the directory server. 3035 */ 3036 public boolean entryExists(@NotNull final String dn) 3037 throws LDAPException 3038 { 3039 return inMemoryHandler.entryExists(dn); 3040 } 3041 3042 3043 3044 /** 3045 * Indicates whether the specified entry exists in the server and matches the 3046 * given filter. 3047 * <BR><BR> 3048 * This method may be used regardless of whether the server is listening for 3049 * client connections. 3050 * 3051 * @param dn The DN of the entry for which to make the determination. 3052 * @param filter The filter the entry is expected to match. 3053 * 3054 * @return {@code true} if the entry exists and matches the specified filter, 3055 * or {@code false} if not. 3056 * 3057 * @throws LDAPException If a problem is encountered while trying to 3058 * communicate with the directory server. 3059 */ 3060 public boolean entryExists(@NotNull final String dn, 3061 @NotNull final String filter) 3062 throws LDAPException 3063 { 3064 return inMemoryHandler.entryExists(dn, filter); 3065 } 3066 3067 3068 3069 /** 3070 * Indicates whether the specified entry exists in the server. This will 3071 * return {@code true} only if the target entry exists and contains all values 3072 * for all attributes of the provided entry. The entry will be allowed to 3073 * have attribute values not included in the provided entry. 3074 * <BR><BR> 3075 * This method may be used regardless of whether the server is listening for 3076 * client connections. 3077 * 3078 * @param entry The entry to compare against the directory server. 3079 * 3080 * @return {@code true} if the entry exists in the server and is a superset 3081 * of the provided entry, or {@code false} if not. 3082 * 3083 * @throws LDAPException If a problem is encountered while trying to 3084 * communicate with the directory server. 3085 */ 3086 public boolean entryExists(@NotNull final Entry entry) 3087 throws LDAPException 3088 { 3089 return inMemoryHandler.entryExists(entry); 3090 } 3091 3092 3093 3094 /** 3095 * Ensures that an entry with the provided DN exists in the directory. 3096 * <BR><BR> 3097 * This method may be used regardless of whether the server is listening for 3098 * client connections. 3099 * 3100 * @param dn The DN of the entry for which to make the determination. 3101 * 3102 * @throws LDAPException If a problem is encountered while trying to 3103 * communicate with the directory server. 3104 * 3105 * @throws AssertionError If the target entry does not exist. 3106 */ 3107 public void assertEntryExists(@NotNull final String dn) 3108 throws LDAPException, AssertionError 3109 { 3110 inMemoryHandler.assertEntryExists(dn); 3111 } 3112 3113 3114 3115 /** 3116 * Ensures that an entry with the provided DN exists in the directory. 3117 * <BR><BR> 3118 * This method may be used regardless of whether the server is listening for 3119 * client connections. 3120 * 3121 * @param dn The DN of the entry for which to make the determination. 3122 * @param filter A filter that the target entry must match. 3123 * 3124 * @throws LDAPException If a problem is encountered while trying to 3125 * communicate with the directory server. 3126 * 3127 * @throws AssertionError If the target entry does not exist or does not 3128 * match the provided filter. 3129 */ 3130 public void assertEntryExists(@NotNull final String dn, 3131 @NotNull final String filter) 3132 throws LDAPException, AssertionError 3133 { 3134 inMemoryHandler.assertEntryExists(dn, filter); 3135 } 3136 3137 3138 3139 /** 3140 * Ensures that an entry exists in the directory with the same DN and all 3141 * attribute values contained in the provided entry. The server entry may 3142 * contain additional attributes and/or attribute values not included in the 3143 * provided entry. 3144 * <BR><BR> 3145 * This method may be used regardless of whether the server is listening for 3146 * client connections. 3147 * 3148 * @param entry The entry expected to be present in the directory server. 3149 * 3150 * @throws LDAPException If a problem is encountered while trying to 3151 * communicate with the directory server. 3152 * 3153 * @throws AssertionError If the target entry does not exist or does not 3154 * match the provided filter. 3155 */ 3156 public void assertEntryExists(@NotNull final Entry entry) 3157 throws LDAPException, AssertionError 3158 { 3159 inMemoryHandler.assertEntryExists(entry); 3160 } 3161 3162 3163 3164 /** 3165 * Retrieves a list containing the DNs of the entries which are missing from 3166 * the directory server. 3167 * <BR><BR> 3168 * This method may be used regardless of whether the server is listening for 3169 * client connections. 3170 * 3171 * @param dns The DNs of the entries to try to find in the server. 3172 * 3173 * @return A list containing all of the provided DNs that were not found in 3174 * the server, or an empty list if all entries were found. 3175 * 3176 * @throws LDAPException If a problem is encountered while trying to 3177 * communicate with the directory server. 3178 */ 3179 @NotNull() 3180 public List<String> getMissingEntryDNs(@NotNull final String... dns) 3181 throws LDAPException 3182 { 3183 return inMemoryHandler.getMissingEntryDNs(StaticUtils.toList(dns)); 3184 } 3185 3186 3187 3188 /** 3189 * Retrieves a list containing the DNs of the entries which are missing from 3190 * the directory server. 3191 * <BR><BR> 3192 * This method may be used regardless of whether the server is listening for 3193 * client connections. 3194 * 3195 * @param dns The DNs of the entries to try to find in the server. 3196 * 3197 * @return A list containing all of the provided DNs that were not found in 3198 * the server, or an empty list if all entries were found. 3199 * 3200 * @throws LDAPException If a problem is encountered while trying to 3201 * communicate with the directory server. 3202 */ 3203 @NotNull() 3204 public List<String> getMissingEntryDNs(@NotNull final Collection<String> dns) 3205 throws LDAPException 3206 { 3207 return inMemoryHandler.getMissingEntryDNs(dns); 3208 } 3209 3210 3211 3212 /** 3213 * Ensures that all of the entries with the provided DNs exist in the 3214 * directory. 3215 * <BR><BR> 3216 * This method may be used regardless of whether the server is listening for 3217 * client connections. 3218 * 3219 * @param dns The DNs of the entries for which to make the determination. 3220 * 3221 * @throws LDAPException If a problem is encountered while trying to 3222 * communicate with the directory server. 3223 * 3224 * @throws AssertionError If any of the target entries does not exist. 3225 */ 3226 public void assertEntriesExist(@NotNull final String... dns) 3227 throws LDAPException, AssertionError 3228 { 3229 inMemoryHandler.assertEntriesExist(StaticUtils.toList(dns)); 3230 } 3231 3232 3233 3234 /** 3235 * Ensures that all of the entries with the provided DNs exist in the 3236 * directory. 3237 * <BR><BR> 3238 * This method may be used regardless of whether the server is listening for 3239 * client connections. 3240 * 3241 * @param dns The DNs of the entries for which to make the determination. 3242 * 3243 * @throws LDAPException If a problem is encountered while trying to 3244 * communicate with the directory server. 3245 * 3246 * @throws AssertionError If any of the target entries does not exist. 3247 */ 3248 public void assertEntriesExist(@NotNull final Collection<String> dns) 3249 throws LDAPException, AssertionError 3250 { 3251 inMemoryHandler.assertEntriesExist(dns); 3252 } 3253 3254 3255 3256 /** 3257 * Retrieves a list containing all of the named attributes which do not exist 3258 * in the target entry. 3259 * <BR><BR> 3260 * This method may be used regardless of whether the server is listening for 3261 * client connections. 3262 * 3263 * @param dn The DN of the entry to examine. 3264 * @param attributeNames The names of the attributes expected to be present 3265 * in the target entry. 3266 * 3267 * @return A list containing the names of the attributes which were not 3268 * present in the target entry, an empty list if all specified 3269 * attributes were found in the entry, or {@code null} if the target 3270 * entry does not exist. 3271 * 3272 * @throws LDAPException If a problem is encountered while trying to 3273 * communicate with the directory server. 3274 */ 3275 @Nullable() 3276 public List<String> getMissingAttributeNames(@NotNull final String dn, 3277 @NotNull final String... attributeNames) 3278 throws LDAPException 3279 { 3280 return inMemoryHandler.getMissingAttributeNames(dn, 3281 StaticUtils.toList(attributeNames)); 3282 } 3283 3284 3285 3286 /** 3287 * Retrieves a list containing all of the named attributes which do not exist 3288 * in the target entry. 3289 * <BR><BR> 3290 * This method may be used regardless of whether the server is listening for 3291 * client connections. 3292 * 3293 * @param dn The DN of the entry to examine. 3294 * @param attributeNames The names of the attributes expected to be present 3295 * in the target entry. 3296 * 3297 * @return A list containing the names of the attributes which were not 3298 * present in the target entry, an empty list if all specified 3299 * attributes were found in the entry, or {@code null} if the target 3300 * entry does not exist. 3301 * 3302 * @throws LDAPException If a problem is encountered while trying to 3303 * communicate with the directory server. 3304 */ 3305 @Nullable() 3306 public List<String> getMissingAttributeNames(@NotNull final String dn, 3307 @NotNull final Collection<String> attributeNames) 3308 throws LDAPException 3309 { 3310 return inMemoryHandler.getMissingAttributeNames(dn, attributeNames); 3311 } 3312 3313 3314 3315 /** 3316 * Ensures that the specified entry exists in the directory with all of the 3317 * specified attributes. 3318 * <BR><BR> 3319 * This method may be used regardless of whether the server is listening for 3320 * client connections. 3321 * 3322 * @param dn The DN of the entry to examine. 3323 * @param attributeNames The names of the attributes that are expected to be 3324 * present in the provided entry. 3325 * 3326 * @throws LDAPException If a problem is encountered while trying to 3327 * communicate with the directory server. 3328 * 3329 * @throws AssertionError If the target entry does not exist or does not 3330 * contain all of the specified attributes. 3331 */ 3332 public void assertAttributeExists(@NotNull final String dn, 3333 @NotNull final String... attributeNames) 3334 throws LDAPException, AssertionError 3335 { 3336 inMemoryHandler.assertAttributeExists(dn, 3337 StaticUtils.toList(attributeNames)); 3338 } 3339 3340 3341 3342 /** 3343 * Ensures that the specified entry exists in the directory with all of the 3344 * specified attributes. 3345 * <BR><BR> 3346 * This method may be used regardless of whether the server is listening for 3347 * client connections. 3348 * 3349 * @param dn The DN of the entry to examine. 3350 * @param attributeNames The names of the attributes that are expected to be 3351 * present in the provided entry. 3352 * 3353 * @throws LDAPException If a problem is encountered while trying to 3354 * communicate with the directory server. 3355 * 3356 * @throws AssertionError If the target entry does not exist or does not 3357 * contain all of the specified attributes. 3358 */ 3359 public void assertAttributeExists(@NotNull final String dn, 3360 @NotNull final Collection<String> attributeNames) 3361 throws LDAPException, AssertionError 3362 { 3363 inMemoryHandler.assertAttributeExists(dn, attributeNames); 3364 } 3365 3366 3367 3368 /** 3369 * Retrieves a list of all provided attribute values which are missing from 3370 * the specified entry. 3371 * <BR><BR> 3372 * This method may be used regardless of whether the server is listening for 3373 * client connections. 3374 * 3375 * @param dn The DN of the entry to examine. 3376 * @param attributeName The attribute expected to be present in the target 3377 * entry with the given values. 3378 * @param attributeValues The values expected to be present in the target 3379 * entry. 3380 * 3381 * @return A list containing all of the provided values which were not found 3382 * in the entry, an empty list if all provided attribute values were 3383 * found, or {@code null} if the target entry does not exist. 3384 * 3385 * @throws LDAPException If a problem is encountered while trying to 3386 * communicate with the directory server. 3387 */ 3388 @Nullable() 3389 public List<String> getMissingAttributeValues(@NotNull final String dn, 3390 @NotNull final String attributeName, 3391 @NotNull final String... attributeValues) 3392 throws LDAPException 3393 { 3394 return inMemoryHandler.getMissingAttributeValues(dn, attributeName, 3395 StaticUtils.toList(attributeValues)); 3396 } 3397 3398 3399 3400 /** 3401 * Retrieves a list of all provided attribute values which are missing from 3402 * the specified entry. The target attribute may or may not contain 3403 * additional values. 3404 * <BR><BR> 3405 * This method may be used regardless of whether the server is listening for 3406 * client connections. 3407 * 3408 * @param dn The DN of the entry to examine. 3409 * @param attributeName The attribute expected to be present in the target 3410 * entry with the given values. 3411 * @param attributeValues The values expected to be present in the target 3412 * entry. 3413 * 3414 * @return A list containing all of the provided values which were not found 3415 * in the entry, an empty list if all provided attribute values were 3416 * found, or {@code null} if the target entry does not exist. 3417 * 3418 * @throws LDAPException If a problem is encountered while trying to 3419 * communicate with the directory server. 3420 */ 3421 @Nullable() 3422 public List<String> getMissingAttributeValues(@NotNull final String dn, 3423 @NotNull final String attributeName, 3424 @NotNull final Collection<String> attributeValues) 3425 throws LDAPException 3426 { 3427 return inMemoryHandler.getMissingAttributeValues(dn, attributeName, 3428 attributeValues); 3429 } 3430 3431 3432 3433 /** 3434 * Ensures that the specified entry exists in the directory with all of the 3435 * specified values for the given attribute. The attribute may or may not 3436 * contain additional values. 3437 * <BR><BR> 3438 * This method may be used regardless of whether the server is listening for 3439 * client connections. 3440 * 3441 * @param dn The DN of the entry to examine. 3442 * @param attributeName The name of the attribute to examine. 3443 * @param attributeValues The set of values which must exist for the given 3444 * attribute. 3445 * 3446 * @throws LDAPException If a problem is encountered while trying to 3447 * communicate with the directory server. 3448 * 3449 * @throws AssertionError If the target entry does not exist, does not 3450 * contain the specified attribute, or that attribute 3451 * does not have all of the specified values. 3452 */ 3453 public void assertValueExists(@NotNull final String dn, 3454 @NotNull final String attributeName, 3455 @NotNull final String... attributeValues) 3456 throws LDAPException, AssertionError 3457 { 3458 inMemoryHandler.assertValueExists(dn, attributeName, 3459 StaticUtils.toList(attributeValues)); 3460 } 3461 3462 3463 3464 /** 3465 * Ensures that the specified entry exists in the directory with all of the 3466 * specified values for the given attribute. The attribute may or may not 3467 * contain additional values. 3468 * <BR><BR> 3469 * This method may be used regardless of whether the server is listening for 3470 * client connections. 3471 * 3472 * @param dn The DN of the entry to examine. 3473 * @param attributeName The name of the attribute to examine. 3474 * @param attributeValues The set of values which must exist for the given 3475 * attribute. 3476 * 3477 * @throws LDAPException If a problem is encountered while trying to 3478 * communicate with the directory server. 3479 * 3480 * @throws AssertionError If the target entry does not exist, does not 3481 * contain the specified attribute, or that attribute 3482 * does not have all of the specified values. 3483 */ 3484 public void assertValueExists(@NotNull final String dn, 3485 @NotNull final String attributeName, 3486 @NotNull final Collection<String> attributeValues) 3487 throws LDAPException, AssertionError 3488 { 3489 inMemoryHandler.assertValueExists(dn, attributeName, attributeValues); 3490 } 3491 3492 3493 3494 /** 3495 * Ensures that the specified entry does not exist in the directory. 3496 * <BR><BR> 3497 * This method may be used regardless of whether the server is listening for 3498 * client connections. 3499 * 3500 * @param dn The DN of the entry expected to be missing. 3501 * 3502 * @throws LDAPException If a problem is encountered while trying to 3503 * communicate with the directory server. 3504 * 3505 * @throws AssertionError If the target entry is found in the server. 3506 */ 3507 public void assertEntryMissing(@NotNull final String dn) 3508 throws LDAPException, AssertionError 3509 { 3510 inMemoryHandler.assertEntryMissing(dn); 3511 } 3512 3513 3514 3515 /** 3516 * Ensures that the specified entry exists in the directory but does not 3517 * contain any of the specified attributes. 3518 * <BR><BR> 3519 * This method may be used regardless of whether the server is listening for 3520 * client connections. 3521 * 3522 * @param dn The DN of the entry expected to be present. 3523 * @param attributeNames The names of the attributes expected to be missing 3524 * from the entry. 3525 * 3526 * @throws LDAPException If a problem is encountered while trying to 3527 * communicate with the directory server. 3528 * 3529 * @throws AssertionError If the target entry is missing from the server, or 3530 * if it contains any of the target attributes. 3531 */ 3532 public void assertAttributeMissing(@NotNull final String dn, 3533 @NotNull final String... attributeNames) 3534 throws LDAPException, AssertionError 3535 { 3536 inMemoryHandler.assertAttributeMissing(dn, 3537 StaticUtils.toList(attributeNames)); 3538 } 3539 3540 3541 3542 /** 3543 * Ensures that the specified entry exists in the directory but does not 3544 * contain any of the specified attributes. 3545 * <BR><BR> 3546 * This method may be used regardless of whether the server is listening for 3547 * client connections. 3548 * 3549 * @param dn The DN of the entry expected to be present. 3550 * @param attributeNames The names of the attributes expected to be missing 3551 * from the entry. 3552 * 3553 * @throws LDAPException If a problem is encountered while trying to 3554 * communicate with the directory server. 3555 * 3556 * @throws AssertionError If the target entry is missing from the server, or 3557 * if it contains any of the target attributes. 3558 */ 3559 public void assertAttributeMissing(@NotNull final String dn, 3560 @NotNull final Collection<String> attributeNames) 3561 throws LDAPException, AssertionError 3562 { 3563 inMemoryHandler.assertAttributeMissing(dn, attributeNames); 3564 } 3565 3566 3567 3568 /** 3569 * Ensures that the specified entry exists in the directory but does not 3570 * contain any of the specified attribute values. 3571 * <BR><BR> 3572 * This method may be used regardless of whether the server is listening for 3573 * client connections. 3574 * 3575 * @param dn The DN of the entry expected to be present. 3576 * @param attributeName The name of the attribute to examine. 3577 * @param attributeValues The values expected to be missing from the target 3578 * entry. 3579 * 3580 * @throws LDAPException If a problem is encountered while trying to 3581 * communicate with the directory server. 3582 * 3583 * @throws AssertionError If the target entry is missing from the server, or 3584 * if it contains any of the target attribute values. 3585 */ 3586 public void assertValueMissing(@NotNull final String dn, 3587 @NotNull final String attributeName, 3588 @NotNull final String... attributeValues) 3589 throws LDAPException, AssertionError 3590 { 3591 inMemoryHandler.assertValueMissing(dn, attributeName, 3592 StaticUtils.toList(attributeValues)); 3593 } 3594 3595 3596 3597 /** 3598 * Ensures that the specified entry exists in the directory but does not 3599 * contain any of the specified attribute values. 3600 * <BR><BR> 3601 * This method may be used regardless of whether the server is listening for 3602 * client connections. 3603 * 3604 * @param dn The DN of the entry expected to be present. 3605 * @param attributeName The name of the attribute to examine. 3606 * @param attributeValues The values expected to be missing from the target 3607 * entry. 3608 * 3609 * @throws LDAPException If a problem is encountered while trying to 3610 * communicate with the directory server. 3611 * 3612 * @throws AssertionError If the target entry is missing from the server, or 3613 * if it contains any of the target attribute values. 3614 */ 3615 public void assertValueMissing(@NotNull final String dn, 3616 @NotNull final String attributeName, 3617 @NotNull final Collection<String> attributeValues) 3618 throws LDAPException, AssertionError 3619 { 3620 inMemoryHandler.assertValueMissing(dn, attributeName, attributeValues); 3621 } 3622}