001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2014-2015 ForgeRock AS. 015 */ 016package org.forgerock.openig.ldap; 017 018import static org.forgerock.opendj.ldap.requests.Requests.newAddRequest; 019import static org.forgerock.opendj.ldap.requests.Requests.newCompareRequest; 020import static org.forgerock.opendj.ldap.requests.Requests.newDeleteRequest; 021import static org.forgerock.opendj.ldap.requests.Requests.newModifyDNRequest; 022import static org.forgerock.opendj.ldap.requests.Requests.newModifyRequest; 023import static org.forgerock.opendj.ldap.requests.Requests.newSearchRequest; 024import static org.forgerock.opendj.ldap.requests.Requests.newSimpleBindRequest; 025 026import java.io.Closeable; 027import java.util.Collection; 028 029import org.forgerock.opendj.ldap.Connection; 030import org.forgerock.opendj.ldap.DN; 031import org.forgerock.opendj.ldap.Entry; 032import org.forgerock.opendj.ldap.LdapException; 033import org.forgerock.opendj.ldap.SearchScope; 034import org.forgerock.opendj.ldap.controls.SubtreeDeleteRequestControl; 035import org.forgerock.opendj.ldap.requests.AddRequest; 036import org.forgerock.opendj.ldap.requests.BindRequest; 037import org.forgerock.opendj.ldap.requests.CompareRequest; 038import org.forgerock.opendj.ldap.requests.DeleteRequest; 039import org.forgerock.opendj.ldap.requests.ModifyDNRequest; 040import org.forgerock.opendj.ldap.requests.ModifyRequest; 041import org.forgerock.opendj.ldap.requests.Request; 042import org.forgerock.opendj.ldap.requests.SearchRequest; 043import org.forgerock.opendj.ldap.responses.BindResult; 044import org.forgerock.opendj.ldap.responses.CompareResult; 045import org.forgerock.opendj.ldap.responses.Result; 046import org.forgerock.opendj.ldap.responses.SearchResultEntry; 047import org.forgerock.opendj.ldap.responses.SearchResultReference; 048import org.forgerock.opendj.ldif.ConnectionEntryReader; 049import org.forgerock.services.TransactionId; 050 051import com.forgerock.opendj.ldap.controls.TransactionIdControl; 052 053/** 054 * Provides an adapted view of an OpenDJ LDAP connection exposing only the 055 * synchronous methods and protecting against future evolution of the 056 * {@link Connection} interface (e.g. migration to Promises). 057 */ 058public final class LdapConnection implements Closeable { 059 private final Connection connection; 060 private final TransactionId rootTransactionId; 061 062 LdapConnection(final Connection connection) { 063 this(connection, null); 064 } 065 066 LdapConnection(final Connection connection, final TransactionId rootTransactionId) { 067 this.connection = connection; 068 this.rootTransactionId = rootTransactionId; 069 } 070 071 /** 072 * Adds an entry to the Directory Server using the provided add request. 073 * 074 * @param request The add request. 075 * @return The result of the operation. 076 * @throws LdapException If the result code indicates that the request failed for some 077 * reason. 078 * @throws UnsupportedOperationException If this connection does not support add operations. 079 * @throws IllegalStateException If this connection has already been closed, i.e. if 080 * {@code isClosed() == true}. 081 * @throws NullPointerException If {@code request} was {@code null}. 082 */ 083 public Result add(AddRequest request) throws LdapException { 084 addTransactionIdControl(request); 085 return connection.add(request); 086 } 087 088 /** 089 * Adds the provided entry to the Directory Server. 090 * <p> 091 * This method is equivalent to the following code: 092 * 093 * <pre> 094 * {@code 095 * AddRequest request = newAddRequest(entry); 096 * connection.add(request); 097 * } 098 * </pre> 099 * 100 * @param entry 101 * The entry to be added. 102 * @return The result of the operation. 103 * @throws LdapException 104 * If the result code indicates that the request failed for some 105 * reason. 106 * @throws UnsupportedOperationException 107 * If this connection does not support add operations. 108 * @throws IllegalStateException 109 * If this connection has already been closed, i.e. if 110 * {@code isClosed() == true}. 111 * @throws NullPointerException 112 * If {@code entry} was {@code null} . 113 */ 114 public Result add(Entry entry) throws LdapException { 115 return add(newAddRequest(entry)); 116 } 117 118 /** 119 * Adds an entry to the Directory Server using the provided lines of LDIF. 120 * <p> 121 * This method is equivalent to the following code: 122 * 123 * <pre> 124 * {@code 125 * AddRequest request = newAddRequest(ldifLines); 126 * connection.add(request); 127 * } 128 * </pre> 129 * 130 * @param ldifLines 131 * Lines of LDIF containing the an LDIF add change record or an 132 * LDIF entry record. 133 * @return The result of the operation. 134 * @throws LdapException 135 * If the result code indicates that the request failed for some 136 * reason. 137 * @throws UnsupportedOperationException 138 * If this connection does not support add operations. 139 * @throws org.forgerock.i18n.LocalizedIllegalArgumentException 140 * If {@code ldifLines} was empty, or contained invalid LDIF, or 141 * could not be decoded using the default schema. 142 * @throws IllegalStateException 143 * If this connection has already been closed, i.e. if 144 * {@code isClosed() == true}. 145 * @throws NullPointerException 146 * If {@code ldifLines} was {@code null} . 147 */ 148 public Result add(String... ldifLines) throws LdapException { 149 return add(newAddRequest(ldifLines)); 150 } 151 152 /** 153 * Authenticates to the Directory Server using the provided bind request. 154 * 155 * @param request The bind request. 156 * @return The result of the operation. 157 * @throws LdapException If the result code indicates that the request failed for some 158 * reason. 159 * @throws UnsupportedOperationException If this connection does not support bind operations. 160 * @throws IllegalStateException If this connection has already been closed, i.e. if 161 * {@code isClosed() == true}. 162 * @throws NullPointerException If {@code request} was {@code null}. 163 */ 164 public BindResult bind(BindRequest request) throws LdapException { 165 addTransactionIdControl(request); 166 return connection.bind(request); 167 } 168 169 /** 170 * Authenticates to the Directory Server using simple authentication and the 171 * provided user name and password. 172 * <p> 173 * This method is equivalent to the following code: 174 * 175 * <pre> 176 * {@code 177 * BindRequest request = newSimpleBindRequest(name, password); 178 * connection.bind(request); 179 * } 180 * </pre> 181 * 182 * @param name 183 * The distinguished name of the Directory object that the client 184 * wishes to bind as, which may be empty. 185 * @param password 186 * The password of the Directory object that the client wishes to 187 * bind as, which may be empty. 188 * @return The result of the operation. 189 * @throws LdapException 190 * If the result code indicates that the request failed for some 191 * reason. 192 * @throws org.forgerock.i18n.LocalizedIllegalArgumentException 193 * If {@code name} could not be decoded using the default 194 * schema. 195 * @throws UnsupportedOperationException 196 * If this connection does not support bind operations. 197 * @throws IllegalStateException 198 * If this connection has already been closed, i.e. if 199 * {@code isClosed() == true}. 200 * @throws NullPointerException 201 * If {@code name} or {@code password} was {@code null}. 202 */ 203 public BindResult bind(String name, char[] password) throws LdapException { 204 return bind(newSimpleBindRequest(name, password)); 205 } 206 207 /** 208 * Releases any resources associated with this connection. For physical 209 * connections to a Directory Server this will mean that an unbind request 210 * is sent and the underlying socket is closed. 211 * <p> 212 * Other connection implementations may behave differently, and may choose 213 * not to send an unbind request if its use is inappropriate (for example a 214 * pooled connection will be released and returned to its connection pool 215 * without ever issuing an unbind request). 216 * <p> 217 * This method is equivalent to the following code: 218 * 219 * <pre> 220 * {@code 221 * UnbindRequest request = new UnbindRequest(); 222 * connection.close(request); 223 * } 224 * </pre> 225 * <p> 226 * Calling {@code close} on a connection that is already closed has no 227 * effect. 228 * 229 * @see org.forgerock.opendj.ldap.Connections#uncloseable(Connection) 230 */ 231 @Override 232 public void close() { 233 connection.close(); 234 } 235 236 /** 237 * Compares an entry in the Directory Server using the provided compare 238 * request. 239 * 240 * @param request The compare request. 241 * @return The result of the operation. 242 * @throws LdapException If the result code indicates that the request failed for some 243 * reason. 244 * @throws UnsupportedOperationException If this connection does not support compare operations. 245 * @throws IllegalStateException If this connection has already been closed, i.e. if 246 * {@code isClosed() == true}. 247 * @throws NullPointerException If {@code request} was {@code null}. 248 */ 249 public CompareResult compare(CompareRequest request) throws LdapException { 250 addTransactionIdControl(request); 251 return connection.compare(request); 252 } 253 254 /** 255 * Compares the named entry in the Directory Server against the provided 256 * attribute value assertion. 257 * <p> 258 * This method is equivalent to the following code: 259 * 260 * <pre> 261 * {@code 262 * CompareRequest request = newCompareRequest(name, attributeDescription, assertionValue); 263 * connection.compare(request); 264 * } 265 * </pre> 266 * 267 * @param name 268 * The distinguished name of the entry to be compared. 269 * @param attributeDescription 270 * The name of the attribute to be compared. 271 * @param assertionValue 272 * The assertion value to be compared. 273 * @return The result of the operation. 274 * @throws LdapException 275 * If the result code indicates that the request failed for some 276 * reason. 277 * @throws org.forgerock.i18n.LocalizedIllegalArgumentException 278 * If {@code name} or {@code AttributeDescription} could not be 279 * decoded using the default schema. 280 * @throws UnsupportedOperationException 281 * If this connection does not support compare operations. 282 * @throws IllegalStateException 283 * If this connection has already been closed, i.e. if 284 * {@code isClosed() == true}. 285 * @throws NullPointerException 286 * If {@code name}, {@code attributeDescription}, or 287 * {@code assertionValue} was {@code null}. 288 */ 289 public CompareResult compare(String name, String attributeDescription, String assertionValue) 290 throws LdapException { 291 return compare(newCompareRequest(name, attributeDescription, assertionValue)); 292 } 293 294 /** 295 * Deletes an entry from the Directory Server using the provided delete 296 * request. 297 * 298 * @param request The delete request. 299 * @return The result of the operation. 300 * @throws LdapException If the result code indicates that the request failed for some 301 * reason. 302 * @throws UnsupportedOperationException If this connection does not support delete operations. 303 * @throws IllegalStateException If this connection has already been closed, i.e. if 304 * {@code isClosed() == true}. 305 * @throws NullPointerException If {@code request} was {@code null}. 306 */ 307 public Result delete(DeleteRequest request) throws LdapException { 308 addTransactionIdControl(request); 309 return connection.delete(request); 310 } 311 312 /** 313 * Deletes the named entry from the Directory Server. 314 * <p> 315 * This method is equivalent to the following code: 316 * 317 * <pre> 318 * {@code 319 * DeleteRequest request = newDeleteRequest(name); 320 * connection.delete(request); 321 * } 322 * </pre> 323 * 324 * @param name 325 * The distinguished name of the entry to be deleted. 326 * @return The result of the operation. 327 * @throws LdapException 328 * If the result code indicates that the request failed for some 329 * reason. 330 * @throws org.forgerock.i18n.LocalizedIllegalArgumentException 331 * If {@code name} could not be decoded using the default 332 * schema. 333 * @throws UnsupportedOperationException 334 * If this connection does not support delete operations. 335 * @throws IllegalStateException 336 * If this connection has already been closed, i.e. if 337 * {@code isClosed() == true}. 338 * @throws NullPointerException 339 * If {@code name} was {@code null}. 340 */ 341 public Result delete(String name) throws LdapException { 342 return delete(newDeleteRequest(name)); 343 } 344 345 /** 346 * Deletes the named entry and all of its subordinates from the Directory 347 * Server. 348 * <p> 349 * This method is equivalent to the following code: 350 * 351 * <pre> 352 * {@code 353 * DeleteRequest request = newDeleteRequest(name).addControl(SubtreeDeleteRequestControl.newControl(true))); 354 * connection.delete(request); 355 * } 356 * </pre> 357 * 358 * @param name 359 * The distinguished name of the subtree base entry to be 360 * deleted. 361 * @return The result of the operation. 362 * @throws LdapException 363 * If the result code indicates that the request failed for some 364 * reason. 365 * @throws org.forgerock.i18n.LocalizedIllegalArgumentException 366 * If {@code name} could not be decoded using the default 367 * schema. 368 * @throws UnsupportedOperationException 369 * If this connection does not support delete operations. 370 * @throws IllegalStateException 371 * If this connection has already been closed, i.e. if 372 * {@code isClosed() == true}. 373 * @throws NullPointerException 374 * If {@code name} was {@code null}. 375 */ 376 public Result deleteSubtree(String name) throws LdapException { 377 return delete(newDeleteRequest(name).addControl(SubtreeDeleteRequestControl.newControl(true))); 378 } 379 380 /** 381 * Modifies an entry in the Directory Server using the provided modify 382 * request. 383 * 384 * @param request The modify request. 385 * @return The result of the operation. 386 * @throws LdapException If the result code indicates that the request failed for some 387 * reason. 388 * @throws UnsupportedOperationException If this connection does not support modify operations. 389 * @throws IllegalStateException If this connection has already been closed, i.e. if 390 * {@code isClosed() == true}. 391 * @throws NullPointerException If {@code request} was {@code null}. 392 */ 393 public Result modify(ModifyRequest request) throws LdapException { 394 addTransactionIdControl(request); 395 return connection.modify(request); 396 } 397 398 /** 399 * Modifies an entry in the Directory Server using the provided lines of 400 * LDIF. 401 * <p> 402 * This method is equivalent to the following code: 403 * 404 * <pre> 405 * {@code 406 * ModifyRequest request = newModifyRequest(ldifLines); 407 * connection.modify(request); 408 * } 409 * </pre> 410 * 411 * @param ldifLines 412 * Lines of LDIF containing the a single LDIF modify change 413 * record. 414 * @return The result of the operation. 415 * @throws LdapException 416 * If the result code indicates that the request failed for some 417 * reason. 418 * @throws UnsupportedOperationException 419 * If this connection does not support modify operations. 420 * @throws org.forgerock.i18n.LocalizedIllegalArgumentException 421 * If {@code ldifLines} was empty, or contained invalid LDIF, or 422 * could not be decoded using the default schema. 423 * @throws IllegalStateException 424 * If this connection has already been closed, i.e. if 425 * {@code isClosed() == true}. 426 * @throws NullPointerException 427 * If {@code ldifLines} was {@code null} . 428 */ 429 public Result modify(String... ldifLines) throws LdapException { 430 return modify(newModifyRequest(ldifLines)); 431 } 432 433 /** 434 * Renames an entry in the Directory Server using the provided modify DN 435 * request. 436 * 437 * @param request The modify DN request. 438 * @return The result of the operation. 439 * @throws LdapException If the result code indicates that the request failed for some 440 * reason. 441 * @throws UnsupportedOperationException If this connection does not support modify DN operations. 442 * @throws IllegalStateException If this connection has already been closed, i.e. if 443 * {@code isClosed() == true}. 444 * @throws NullPointerException If {@code request} was {@code null}. 445 */ 446 public Result modifyDN(ModifyDNRequest request) throws LdapException { 447 addTransactionIdControl(request); 448 return connection.modifyDN(request); 449 } 450 451 /** 452 * Renames the named entry in the Directory Server using the provided new 453 * RDN. 454 * <p> 455 * This method is equivalent to the following code: 456 * 457 * <pre> 458 * {@code 459 * ModifyDNRequest request = newModifyDNRequest(name, newRDN); 460 * connection.modifyDN(request); 461 * } 462 * </pre> 463 * 464 * @param name 465 * The distinguished name of the entry to be renamed. 466 * @param newRDN 467 * The new RDN of the entry. 468 * @return The result of the operation. 469 * @throws LdapException 470 * If the result code indicates that the request failed for some 471 * reason. 472 * @throws org.forgerock.i18n.LocalizedIllegalArgumentException 473 * If {@code name} or {@code newRDN} could not be decoded using 474 * the default schema. 475 * @throws UnsupportedOperationException 476 * If this connection does not support modify DN operations. 477 * @throws IllegalStateException 478 * If this connection has already been closed, i.e. if 479 * {@code isClosed() == true}. 480 * @throws NullPointerException 481 * If {@code name} or {@code newRDN} was {@code null}. 482 */ 483 public Result modifyDN(String name, String newRDN) throws LdapException { 484 return modifyDN(newModifyDNRequest(name, newRDN)); 485 } 486 487 /** 488 * Reads the named entry from the Directory Server. 489 * <p> 490 * If the requested entry is not returned by the Directory Server then the 491 * request will fail with an {@link org.forgerock.opendj.ldap.EntryNotFoundException}. More 492 * specifically, this method will never return {@code null}. 493 * <p> 494 * This method is equivalent to the following code: 495 * 496 * <pre> 497 * {@code 498 * SearchRequest request = 499 * new SearchRequest(name, SearchScope.BASE_OBJECT, "(objectClass=*)", attributeDescriptions); 500 * connection.searchSingleEntry(request); 501 * } 502 * </pre> 503 * 504 * @param name 505 * The distinguished name of the entry to be read. 506 * @param attributeDescriptions 507 * The names of the attributes to be included with the entry, 508 * which may be {@code null} or empty indicating that all user 509 * attributes should be returned. 510 * @return The single search result entry returned from the search. 511 * @throws LdapException 512 * If the result code indicates that the request failed for some 513 * reason. 514 * @throws UnsupportedOperationException 515 * If this connection does not support search operations. 516 * @throws IllegalStateException 517 * If this connection has already been closed, i.e. if 518 * {@code isClosed() == true}. 519 * @throws NullPointerException 520 * If the {@code name} was {@code null}. 521 */ 522 public SearchResultEntry readEntry(DN name, String... attributeDescriptions) 523 throws LdapException { 524 return connection.readEntry(name, attributeDescriptions); 525 } 526 527 /** 528 * Reads the named entry from the Directory Server. 529 * <p> 530 * If the requested entry is not returned by the Directory Server then the 531 * request will fail with an {@link org.forgerock.opendj.ldap.EntryNotFoundException}. More 532 * specifically, this method will never return {@code null}. 533 * <p> 534 * This method is equivalent to the following code: 535 * 536 * <pre> 537 * {@code 538 * SearchRequest request = 539 * new SearchRequest(name, SearchScope.BASE_OBJECT, "(objectClass=*)", attributeDescriptions); 540 * connection.searchSingleEntry(request); 541 * } 542 * </pre> 543 * 544 * @param name 545 * The distinguished name of the entry to be read. 546 * @param attributeDescriptions 547 * The names of the attributes to be included with the entry. 548 * @return The single search result entry returned from the search. 549 * @throws LdapException 550 * If the result code indicates that the request failed for some 551 * reason. 552 * @throws org.forgerock.i18n.LocalizedIllegalArgumentException 553 * If {@code baseObject} could not be decoded using the default 554 * schema. 555 * @throws UnsupportedOperationException 556 * If this connection does not support search operations. 557 * @throws IllegalStateException 558 * If this connection has already been closed, i.e. if 559 * {@code isClosed() == true}. 560 * @throws NullPointerException 561 * If the {@code name} was {@code null}. 562 */ 563 public SearchResultEntry readEntry(String name, String... attributeDescriptions) 564 throws LdapException { 565 return connection.readEntry(name, attributeDescriptions); 566 } 567 568 /** 569 * Searches the Directory Server using the provided search parameters. Any 570 * matching entries returned by the search will be exposed through the 571 * returned {@code ConnectionEntryReader}. 572 * <p> 573 * Unless otherwise specified, calling this method is equivalent to: 574 * 575 * <pre> 576 * {@code 577 * ConnectionEntryReader reader = new ConnectionEntryReader(this, request); 578 * } 579 * </pre> 580 * 581 * @param request 582 * The search request. 583 * @return The result of the operation. 584 * @throws UnsupportedOperationException 585 * If this connection does not support search operations. 586 * @throws IllegalStateException 587 * If this connection has already been closed, i.e. if 588 * {@code isClosed() == true}. 589 * @throws NullPointerException 590 * If {@code request} or {@code entries} was {@code null}. 591 */ 592 public ConnectionEntryReader search(SearchRequest request) { 593 addTransactionIdControl(request); 594 return connection.search(request); 595 } 596 597 /** 598 * Searches the Directory Server using the provided search request. Any 599 * matching entries returned by the search will be added to {@code entries}, 600 * even if the final search result indicates that the search failed. Search 601 * result references will be discarded. 602 * <p> 603 * <b>Warning:</b> Usage of this method is discouraged if the search request 604 * is expected to yield a large number of search results since the entire 605 * set of results will be stored in memory, potentially causing an 606 * {@code OutOfMemoryError}. 607 * <p> 608 * This method is equivalent to the following code: 609 * 610 * <pre> 611 * {@code 612 * connection.search(request, entries, null); 613 * } 614 * </pre> 615 * 616 * @param request 617 * The search request. 618 * @param entries 619 * The collection to which matching entries should be added. 620 * @return The result of the operation. 621 * @throws LdapException 622 * If the result code indicates that the request failed for some 623 * reason. 624 * @throws UnsupportedOperationException 625 * If this connection does not support search operations. 626 * @throws IllegalStateException 627 * If this connection has already been closed, i.e. if 628 * {@code isClosed() == true}. 629 * @throws NullPointerException 630 * If {@code request} or {@code entries} was {@code null}. 631 */ 632 public Result search(SearchRequest request, Collection<? super SearchResultEntry> entries) 633 throws LdapException { 634 addTransactionIdControl(request); 635 return connection.search(request, entries); 636 } 637 638 /** 639 * Searches the Directory Server using the provided search request. Any 640 * matching entries returned by the search will be added to {@code entries}, 641 * even if the final search result indicates that the search failed. 642 * Similarly, search result references returned by the search will be added 643 * to {@code references}. 644 * <p> 645 * <b>Warning:</b> Usage of this method is discouraged if the search request 646 * is expected to yield a large number of search results since the entire 647 * set of results will be stored in memory, potentially causing an 648 * {@code OutOfMemoryError}. 649 * 650 * @param request 651 * The search request. 652 * @param entries 653 * The collection to which matching entries should be added. 654 * @param references 655 * The collection to which search result references should be 656 * added, or {@code null} if references are to be discarded. 657 * @return The result of the operation. 658 * @throws LdapException 659 * If the result code indicates that the request failed for some 660 * reason. 661 * @throws UnsupportedOperationException 662 * If this connection does not support search operations. 663 * @throws IllegalStateException 664 * If this connection has already been closed, i.e. if 665 * {@code isClosed() == true}. 666 * @throws NullPointerException 667 * If {@code request} or {@code entries} was {@code null}. 668 */ 669 public Result search(SearchRequest request, Collection<? super SearchResultEntry> entries, 670 Collection<? super SearchResultReference> references) throws LdapException { 671 addTransactionIdControl(request); 672 return connection.search(request, entries, references); 673 } 674 675 /** 676 * Searches the Directory Server using the provided search parameters. Any 677 * matching entries returned by the search will be exposed through the 678 * {@code EntryReader} interface. 679 * <p> 680 * <b>Warning:</b> When using a queue with an optional capacity bound, the 681 * connection will stop reading responses and wait if necessary for space to 682 * become available. 683 * <p> 684 * This method is equivalent to the following code: 685 * 686 * <pre> 687 * {@code 688 * SearchRequest request = new SearchRequest(baseDN, scope, filter, attributeDescriptions); 689 * connection.search(request, new LinkedBlockingQueue<Response>()); 690 * } 691 * </pre> 692 * 693 * @param baseObject 694 * The distinguished name of the base entry relative to which the 695 * search is to be performed. 696 * @param scope 697 * The scope of the search. 698 * @param filter 699 * The filter that defines the conditions that must be fulfilled 700 * in order for an entry to be returned. 701 * @param attributeDescriptions 702 * The names of the attributes to be included with each entry. 703 * @return An entry reader exposing the returned entries. 704 * @throws UnsupportedOperationException 705 * If this connection does not support search operations. 706 * @throws IllegalStateException 707 * If this connection has already been closed, i.e. if 708 * {@code isClosed() == true}. 709 * @throws NullPointerException 710 * If the {@code baseObject}, {@code scope}, or {@code filter} 711 * were {@code null}. 712 */ 713 public ConnectionEntryReader search(String baseObject, SearchScope scope, String filter, 714 String... attributeDescriptions) { 715 return search(newSearchRequest(baseObject, scope, filter, attributeDescriptions)); 716 } 717 718 /** 719 * Searches the Directory Server for a single entry using the provided 720 * search request. 721 * <p> 722 * If the requested entry is not returned by the Directory Server then the 723 * request will fail with an {@link org.forgerock.opendj.ldap.EntryNotFoundException}. More 724 * specifically, this method will never return {@code null}. If multiple 725 * matching entries are returned by the Directory Server then the request 726 * will fail with an {@link org.forgerock.opendj.ldap.MultipleEntriesFoundException}. 727 * 728 * @param request 729 * The search request. 730 * @return The single search result entry returned from the search. 731 * @throws LdapException 732 * If the result code indicates that the request failed for some 733 * reason. 734 * @throws UnsupportedOperationException 735 * If this connection does not support search operations. 736 * @throws IllegalStateException 737 * If this connection has already been closed, i.e. if 738 * {@code isClosed() == true}. 739 * @throws NullPointerException 740 * If the {@code request} was {@code null}. 741 */ 742 public SearchResultEntry searchSingleEntry(SearchRequest request) throws LdapException { 743 addTransactionIdControl(request); 744 return connection.searchSingleEntry(request); 745 } 746 747 /** 748 * Searches the Directory Server for a single entry using the provided 749 * search parameters. 750 * <p> 751 * If the requested entry is not returned by the Directory Server then the 752 * request will fail with an {@link org.forgerock.opendj.ldap.EntryNotFoundException}. More 753 * specifically, this method will never return {@code null}. If multiple 754 * matching entries are returned by the Directory Server then the request 755 * will fail with an {@link org.forgerock.opendj.ldap.MultipleEntriesFoundException}. 756 * <p> 757 * This method is equivalent to the following code: 758 * 759 * <pre> 760 * {@code 761 * SearchRequest request = new SearchRequest(baseObject, scope, filter, attributeDescriptions); 762 * connection.searchSingleEntry(request); 763 * } 764 * </pre> 765 * 766 * @param baseObject 767 * The distinguished name of the base entry relative to which the 768 * search is to be performed. 769 * @param scope 770 * The scope of the search. 771 * @param filter 772 * The filter that defines the conditions that must be fulfilled 773 * in order for an entry to be returned. 774 * @param attributeDescriptions 775 * The names of the attributes to be included with each entry. 776 * @return The single search result entry returned from the search. 777 * @throws LdapException 778 * If the result code indicates that the request failed for some 779 * reason. 780 * @throws org.forgerock.i18n.LocalizedIllegalArgumentException 781 * If {@code baseObject} could not be decoded using the default 782 * schema or if {@code filter} is not a valid LDAP string 783 * representation of a filter. 784 * @throws UnsupportedOperationException 785 * If this connection does not support search operations. 786 * @throws IllegalStateException 787 * If this connection has already been closed, i.e. if 788 * {@code isClosed() == true}. 789 * @throws NullPointerException 790 * If the {@code baseObject}, {@code scope}, or {@code filter} 791 * were {@code null}. 792 */ 793 public SearchResultEntry searchSingleEntry(String baseObject, SearchScope scope, String filter, 794 String... attributeDescriptions) throws LdapException { 795 return searchSingleEntry(newSearchRequest(baseObject, scope, filter, attributeDescriptions)); 796 } 797 798 private void addTransactionIdControl(Request request) { 799 if (rootTransactionId != null && !request.containsControl(TransactionIdControl.OID)) { 800 request.addControl(TransactionIdControl.newControl(rootTransactionId.createSubTransactionId().getValue())); 801 } 802 } 803 804}