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 2009-2010 Sun Microsystems, Inc.
015 * Portions Copyright 2011-2016 ForgeRock AS.
016 */
017
018package org.forgerock.opendj.ldap;
019
020import java.io.Closeable;
021import java.util.Collection;
022
023import org.forgerock.i18n.LocalizedIllegalArgumentException;
024import org.forgerock.opendj.ldap.requests.AbandonRequest;
025import org.forgerock.opendj.ldap.requests.AddRequest;
026import org.forgerock.opendj.ldap.requests.BindRequest;
027import org.forgerock.opendj.ldap.requests.CompareRequest;
028import org.forgerock.opendj.ldap.requests.DeleteRequest;
029import org.forgerock.opendj.ldap.requests.ExtendedRequest;
030import org.forgerock.opendj.ldap.requests.ModifyDNRequest;
031import org.forgerock.opendj.ldap.requests.ModifyRequest;
032import org.forgerock.opendj.ldap.requests.SearchRequest;
033import org.forgerock.opendj.ldap.requests.UnbindRequest;
034import org.forgerock.opendj.ldap.responses.BindResult;
035import org.forgerock.opendj.ldap.responses.CompareResult;
036import org.forgerock.opendj.ldap.responses.ExtendedResult;
037import org.forgerock.opendj.ldap.responses.GenericExtendedResult;
038import org.forgerock.opendj.ldap.responses.Result;
039import org.forgerock.opendj.ldap.responses.SearchResultEntry;
040import org.forgerock.opendj.ldap.responses.SearchResultReference;
041import org.forgerock.opendj.ldif.ChangeRecord;
042import org.forgerock.opendj.ldif.ConnectionEntryReader;
043
044/**
045 * A connection with a Directory Server over which read and update operations
046 * may be performed. See RFC 4511 for the LDAPv3 protocol specification and more
047 * information about the types of operations defined in LDAP.
048 * <p>
049 * <h3>Operation processing</h3>
050 * <p>
051 * Operations may be performed synchronously or asynchronously depending on the
052 * method chosen. Asynchronous methods can be identified by their {@code Async}
053 * suffix.
054 * <p>
055 * <h4>Performing operations synchronously</h4>
056 * <p>
057 * Synchronous methods block until a response is received from the Directory
058 * Server, at which point an appropriate {@link Result} object is returned if
059 * the operation succeeded, or thrown as an {@link LdapException} if the
060 * operation failed.
061 * <p>
062 * Since synchronous operations block the calling thread, the only way to
063 * abandon a long running operation is to interrupt the calling thread from
064 * another thread. This will cause the calling thread unblock and throw a
065 * {@link CancelledResultException} whose cause is the underlying
066 * {@link InterruptedException}.
067 * <p>
068 * <h4>Performing operations asynchronously</h4>
069 * <p>
070 * Asynchronous methods, identified by their {@code Async} suffix, are
071 * non-blocking, returning a {@link LdapPromise} or sub-type thereof which can
072 * be used for retrieving the result using the {@link LdapPromise#get} method.
073 * Operation failures, for whatever reason, are signaled by the
074 * {@link LdapPromise#get()} method throwing an {@link LdapException}.
075 * <p>
076 * In addition to returning a {@link LdapPromise}, all asynchronous methods
077 * accept a {@link LdapResultHandler} which will be notified upon completion of the
078 * operation.
079 * <p>
080 * Synchronous operations are easily simulated by immediately getting the
081 * result:
082 *
083 * <pre>
084 * Connection connection = ...;
085 * AddRequest request = ...;
086 * // Will block until operation completes, and
087 * // throws exception on failure.
088 * connection.add(request).get();
089 * </pre>
090 *
091 * Operations can be performed in parallel while taking advantage of the
092 * simplicity of a synchronous application design:
093 *
094 * <pre>
095 * Connection connection1 = ...;
096 * Connection connection2 = ...;
097 * AddRequest request = ...;
098 * // Add the entry to the first server (don't block).
099 * LdapPromise promise1 = connection1.add(request);
100 * // Add the entry to the second server (in parallel).
101 * LdapPromise promise2 = connection2.add(request);
102 * // Total time = is O(1) instead of O(n).
103 * promise1.get();
104 * promise2.get();
105 * </pre>
106 *
107 * More complex client applications can take advantage of a fully asynchronous
108 * event driven design using {@link LdapResultHandler}s:
109 *
110 * <pre>
111 * Connection connection = ...;
112 * SearchRequest request = ...;
113 * // Process results in the search result handler
114 * // in a separate thread.
115 * SearchResponseHandler handle = ...;
116 * connection.search(request, handler);
117 * </pre>
118 * <p>
119 * <h3>Closing connections</h3>
120 * <p>
121 * Applications must ensure that a connection is closed by calling
122 * {@link #close()} even if a fatal error occurs on the connection. Once a
123 * connection has been closed by the client application, any attempts to
124 * continue to use the connection will result in an
125 * {@link IllegalStateException} being thrown. Note that, if a fatal error is
126 * encountered on the connection, then the application can continue to use the
127 * connection. In this case all requests subsequent to the failure will fail
128 * with an appropriate {@link LdapException} when their result is
129 * retrieved.
130 * <p>
131 * <h3>Event notification</h3>
132 * <p>
133 * Applications can choose to be notified when a connection is closed by the
134 * application, receives an unsolicited notification, or experiences a fatal
135 * error by registering a {@link ConnectionEventListener} with the connection
136 * using the {@link #addConnectionEventListener} method.
137 *
138 * @see <a href="http://tools.ietf.org/html/rfc4511">RFC 4511 - Lightweight
139 *      Directory Access Protocol (LDAP): The Protocol </a>
140 */
141public interface Connection extends Closeable {
142
143    /**
144     * Abandons the unfinished operation identified in the provided abandon
145     * request.
146     * <p>
147     * Abandon requests do not have a response, so invoking the method get() on
148     * the returned promise will not block, nor return anything (it is Void), but
149     * may throw an exception if a problem occurred while sending the abandon
150     * request. In addition the returned promise may be used in order to
151     * determine the message ID of the abandon request.
152     * <p>
153     * <b>Note:</b> a more convenient approach to abandoning unfinished
154     * asynchronous operations is provided via the
155     * {@link LdapPromise#cancel(boolean)} method.
156     *
157     * @param request
158     *            The request identifying the operation to be abandoned.
159     * @return A promise whose result is Void.
160     * @throws UnsupportedOperationException
161     *             If this connection does not support abandon operations.
162     * @throws IllegalStateException
163     *             If this connection has already been closed, i.e. if
164     *             {@code isClosed() == true}.
165     * @throws NullPointerException
166     *             If {@code request} was {@code null}.
167     */
168    LdapPromise<Void> abandonAsync(AbandonRequest request);
169
170    /**
171     * Adds an entry to the Directory Server using the provided add request.
172     *
173     * @param request
174     *            The add request.
175     * @return The result of the operation.
176     * @throws LdapException
177     *             If the result code indicates that the request failed for some
178     *             reason.
179     * @throws UnsupportedOperationException
180     *             If this connection does not support add operations.
181     * @throws IllegalStateException
182     *             If this connection has already been closed, i.e. if
183     *             {@code isClosed() == true}.
184     * @throws NullPointerException
185     *             If {@code request} was {@code null}.
186     */
187    Result add(AddRequest request) throws LdapException;
188
189    /**
190     * Adds the provided entry to the Directory Server.
191     * <p>
192     * This method is equivalent to the following code:
193     *
194     * <pre>
195     * AddRequest request = new AddRequest(entry);
196     * connection.add(request);
197     * </pre>
198     *
199     * @param entry
200     *            The entry to be added.
201     * @return The result of the operation.
202     * @throws LdapException
203     *             If the result code indicates that the request failed for some
204     *             reason.
205     * @throws UnsupportedOperationException
206     *             If this connection does not support add operations.
207     * @throws IllegalStateException
208     *             If this connection has already been closed, i.e. if
209     *             {@code isClosed() == true}.
210     * @throws NullPointerException
211     *             If {@code entry} was {@code null} .
212     */
213    Result add(Entry entry) throws LdapException;
214
215    /**
216     * Adds an entry to the Directory Server using the provided lines of LDIF.
217     * <p>
218     * This method is equivalent to the following code:
219     *
220     * <pre>
221     * AddRequest request = new AddRequest(ldifLines);
222     * connection.add(request);
223     * </pre>
224     *
225     * @param ldifLines
226     *            Lines of LDIF containing the an LDIF add change record or an
227     *            LDIF entry record.
228     * @return The result of the operation.
229     * @throws LdapException
230     *             If the result code indicates that the request failed for some
231     *             reason.
232     * @throws UnsupportedOperationException
233     *             If this connection does not support add operations.
234     * @throws LocalizedIllegalArgumentException
235     *             If {@code ldifLines} was empty, or contained invalid LDIF, or
236     *             could not be decoded using the default schema.
237     * @throws IllegalStateException
238     *             If this connection has already been closed, i.e. if
239     *             {@code isClosed() == true}.
240     * @throws NullPointerException
241     *             If {@code ldifLines} was {@code null} .
242     */
243    Result add(String... ldifLines) throws LdapException;
244
245    /**
246     * Asynchronously adds an entry to the Directory Server using the provided
247     * add request.
248     *
249     * @param request
250     *            The add request.
251     * @return A promise representing the result of the operation.
252     * @throws UnsupportedOperationException
253     *             If this connection does not support add operations.
254     * @throws IllegalStateException
255     *             If this connection has already been closed, i.e. if
256     *             {@code isClosed() == true}.
257     * @throws NullPointerException
258     *             If {@code request} was {@code null}.
259     */
260    LdapPromise<Result> addAsync(AddRequest request);
261
262    /**
263     * Asynchronously adds an entry to the Directory Server using the provided
264     * add request.
265     *
266     * @param request
267     *            The add request.
268     * @param intermediateResponseHandler
269     *            An intermediate response handler which can be used to process
270     *            any intermediate responses as they are received, may be
271     *            {@code null}.
272     * @return A promise representing the result of the operation.
273     * @throws UnsupportedOperationException
274     *             If this connection does not support add operations.
275     * @throws IllegalStateException
276     *             If this connection has already been closed, i.e. if
277     *             {@code isClosed() == true}.
278     * @throws NullPointerException
279     *             If {@code request} was {@code null}.
280     */
281    LdapPromise<Result> addAsync(AddRequest request, IntermediateResponseHandler intermediateResponseHandler);
282
283    /**
284     * Registers the provided connection event listener so that it will be
285     * notified when this connection is closed by the application, receives an
286     * unsolicited notification, or experiences a fatal error.
287     *
288     * @param listener
289     *            The listener which wants to be notified when events occur on
290     *            this connection.
291     * @throws IllegalStateException
292     *             If this connection has already been closed, i.e. if
293     *             {@code isClosed() == true}.
294     * @throws NullPointerException
295     *             If the {@code listener} was {@code null}.
296     */
297    void addConnectionEventListener(ConnectionEventListener listener);
298
299    /**
300     * Applies the provided change request to the Directory Server.
301     *
302     * @param request
303     *            The change request.
304     * @return The result of the operation.
305     * @throws LdapException
306     *             If the result code indicates that the request failed for some
307     *             reason.
308     * @throws UnsupportedOperationException
309     *             If this connection does not support the provided change
310     *             request.
311     * @throws IllegalStateException
312     *             If this connection has already been closed, i.e. if
313     *             {@code isClosed() == true}.
314     * @throws NullPointerException
315     *             If {@code request} was {@code null}.
316     */
317    Result applyChange(ChangeRecord request) throws LdapException;
318
319    /**
320     * Asynchronously applies the provided change request to the Directory
321     * Server.
322     *
323     * @param request
324     *            The change request.
325     * @return A promise representing the result of the operation.
326     * @throws UnsupportedOperationException
327     *             If this connection does not support the provided change
328     *             request.
329     * @throws IllegalStateException
330     *             If this connection has already been closed, i.e. if
331     *             {@code isClosed() == true}.
332     * @throws NullPointerException
333     *             If {@code request} was {@code null}.
334     */
335    LdapPromise<Result> applyChangeAsync(ChangeRecord request);
336
337    /**
338     * Asynchronously applies the provided change request to the Directory
339     * Server.
340     *
341     * @param request
342     *            The change request.
343     * @param intermediateResponseHandler
344     *            An intermediate response handler which can be used to process
345     *            any intermediate responses as they are received, may be
346     *            {@code null}.
347     * @return A promise representing the result of the operation.
348     * @throws UnsupportedOperationException
349     *             If this connection does not support the provided change
350     *             request.
351     * @throws IllegalStateException
352     *             If this connection has already been closed, i.e. if
353     *             {@code isClosed() == true}.
354     * @throws NullPointerException
355     *             If {@code request} was {@code null}.
356     */
357    LdapPromise<Result> applyChangeAsync(ChangeRecord request,
358        IntermediateResponseHandler intermediateResponseHandler);
359
360    /**
361     * Authenticates to the Directory Server using the provided bind request.
362     *
363     * @param request
364     *            The bind request.
365     * @return The result of the operation.
366     * @throws LdapException
367     *             If the result code indicates that the request failed for some
368     *             reason.
369     * @throws UnsupportedOperationException
370     *             If this connection does not support bind operations.
371     * @throws IllegalStateException
372     *             If this connection has already been closed, i.e. if
373     *             {@code isClosed() == true}.
374     * @throws NullPointerException
375     *             If {@code request} was {@code null}.
376     */
377    BindResult bind(BindRequest request) throws LdapException;
378
379    /**
380     * Authenticates to the Directory Server using simple authentication and the
381     * provided user name and password.
382     * <p>
383     * This method is equivalent to the following code:
384     *
385     * <pre>
386     * BindRequest request = new SimpleBindRequest(name, password);
387     * connection.bind(request);
388     * </pre>
389     *
390     * @param name
391     *            The distinguished name of the Directory object that the client
392     *            wishes to bind as, which may be empty.
393     * @param password
394     *            The password of the Directory object that the client wishes to
395     *            bind as, which may be empty.
396     * @return The result of the operation.
397     * @throws LdapException
398     *             If the result code indicates that the request failed for some
399     *             reason.
400     * @throws LocalizedIllegalArgumentException
401     *             If {@code name} could not be decoded using the default
402     *             schema.
403     * @throws UnsupportedOperationException
404     *             If this connection does not support bind operations.
405     * @throws IllegalStateException
406     *             If this connection has already been closed, i.e. if
407     *             {@code isClosed() == true}.
408     * @throws NullPointerException
409     *             If {@code name} or {@code password} was {@code null}.
410     */
411    BindResult bind(String name, char[] password) throws LdapException;
412
413    /**
414     * Asynchronously authenticates to the Directory Server using the provided
415     * bind request.
416     *
417     * @param request
418     *            The bind request.
419     * @return A promise representing the result of the operation.
420     * @throws UnsupportedOperationException
421     *             If this connection does not support bind operations.
422     * @throws IllegalStateException
423     *             If this connection has already been closed, i.e. if
424     *             {@code isClosed() == true}.
425     * @throws NullPointerException
426     *             If {@code request} was {@code null}.
427     */
428    LdapPromise<BindResult> bindAsync(BindRequest request);
429
430    /**
431     * Asynchronously authenticates to the Directory Server using the provided
432     * bind request.
433     *
434     * @param request
435     *            The bind request.
436     * @param intermediateResponseHandler
437     *            An intermediate response handler which can be used to process
438     *            any intermediate responses as they are received, may be
439     *            {@code null}.
440     * @return A promise representing the result of the operation.
441     * @throws UnsupportedOperationException
442     *             If this connection does not support bind operations.
443     * @throws IllegalStateException
444     *             If this connection has already been closed, i.e. if
445     *             {@code isClosed() == true}.
446     * @throws NullPointerException
447     *             If {@code request} was {@code null}.
448     */
449    LdapPromise<BindResult> bindAsync(BindRequest request, IntermediateResponseHandler intermediateResponseHandler);
450
451    /**
452     * Releases any resources associated with this connection. For physical
453     * connections to a Directory Server this will mean that an unbind request
454     * is sent and the underlying socket is closed.
455     * <p>
456     * Other connection implementations may behave differently, and may choose
457     * not to send an unbind request if its use is inappropriate (for example a
458     * pooled connection will be released and returned to its connection pool
459     * without ever issuing an unbind request).
460     * <p>
461     * This method is equivalent to the following code:
462     *
463     * <pre>
464     * UnbindRequest request = new UnbindRequest();
465     * connection.close(request);
466     * </pre>
467     *
468     * Calling {@code close} on a connection that is already closed has no
469     * effect.
470     *
471     * @see Connections#uncloseable(Connection)
472     */
473    @Override
474    void close();
475
476    /**
477     * Releases any resources associated with this connection. For physical
478     * connections to a Directory Server this will mean that the provided unbind
479     * request is sent and the underlying socket is closed.
480     * <p>
481     * Other connection implementations may behave differently, and may choose
482     * to ignore the provided unbind request if its use is inappropriate (for
483     * example a pooled connection will be released and returned to its
484     * connection pool without ever issuing an unbind request).
485     * <p>
486     * Calling {@code close} on a connection that is already closed has no
487     * effect.
488     *
489     * @param request
490     *            The unbind request to use in the case where a physical
491     *            connection is closed.
492     * @param reason
493     *            A reason describing why the connection was closed.
494     * @throws NullPointerException
495     *             If {@code request} was {@code null}.
496     */
497    void close(UnbindRequest request, String reason);
498
499    /**
500     * Compares an entry in the Directory Server using the provided compare
501     * request.
502     *
503     * @param request
504     *            The compare request.
505     * @return The result of the operation.
506     * @throws LdapException
507     *             If the result code indicates that the request failed for some
508     *             reason.
509     * @throws UnsupportedOperationException
510     *             If this connection does not support compare operations.
511     * @throws IllegalStateException
512     *             If this connection has already been closed, i.e. if
513     *             {@code isClosed() == true}.
514     * @throws NullPointerException
515     *             If {@code request} was {@code null}.
516     */
517    CompareResult compare(CompareRequest request) throws LdapException;
518
519    /**
520     * Compares the named entry in the Directory Server against the provided
521     * attribute value assertion.
522     * <p>
523     * This method is equivalent to the following code:
524     *
525     * <pre>
526     * CompareRequest request = new CompareRequest(name, attributeDescription, assertionValue);
527     * connection.compare(request);
528     * </pre>
529     *
530     * @param name
531     *            The distinguished name of the entry to be compared.
532     * @param attributeDescription
533     *            The name of the attribute to be compared.
534     * @param assertionValue
535     *            The assertion value to be compared.
536     * @return The result of the operation.
537     * @throws LdapException
538     *             If the result code indicates that the request failed for some
539     *             reason.
540     * @throws LocalizedIllegalArgumentException
541     *             If {@code name} or {@code AttributeDescription} could not be
542     *             decoded using the default schema.
543     * @throws UnsupportedOperationException
544     *             If this connection does not support compare operations.
545     * @throws IllegalStateException
546     *             If this connection has already been closed, i.e. if
547     *             {@code isClosed() == true}.
548     * @throws NullPointerException
549     *             If {@code name}, {@code attributeDescription}, or
550     *             {@code assertionValue} was {@code null}.
551     */
552    CompareResult compare(String name, String attributeDescription, String assertionValue)
553            throws LdapException;
554
555    /**
556     * Asynchronously compares an entry in the Directory Server using the
557     * provided compare request.
558     *
559     * @param request
560     *            The compare request.
561     * @return A promise representing the result of the operation.
562     * @throws UnsupportedOperationException
563     *             If this connection does not support compare operations.
564     * @throws IllegalStateException
565     *             If this connection has already been closed, i.e. if
566     *             {@code isClosed() == true}.
567     * @throws NullPointerException
568     *             If {@code request} was {@code null}.
569     */
570    LdapPromise<CompareResult> compareAsync(CompareRequest request);
571
572    /**
573     * Asynchronously compares an entry in the Directory Server using the
574     * provided compare request.
575     *
576     * @param request
577     *            The compare request.
578     * @param intermediateResponseHandler
579     *            An intermediate response handler which can be used to process
580     *            any intermediate responses as they are received, may be
581     *            {@code null}.
582     * @return A promise representing the result of the operation.
583     * @throws UnsupportedOperationException
584     *             If this connection does not support compare operations.
585     * @throws IllegalStateException
586     *             If this connection has already been closed, i.e. if
587     *             {@code isClosed() == true}.
588     * @throws NullPointerException
589     *             If {@code request} was {@code null}.
590     */
591    LdapPromise<CompareResult> compareAsync(CompareRequest request,
592        IntermediateResponseHandler intermediateResponseHandler);
593
594    /**
595     * Deletes an entry from the Directory Server using the provided delete
596     * request.
597     *
598     * @param request
599     *            The delete request.
600     * @return The result of the operation.
601     * @throws LdapException
602     *             If the result code indicates that the request failed for some
603     *             reason.
604     * @throws UnsupportedOperationException
605     *             If this connection does not support delete operations.
606     * @throws IllegalStateException
607     *             If this connection has already been closed, i.e. if
608     *             {@code isClosed() == true}.
609     * @throws NullPointerException
610     *             If {@code request} was {@code null}.
611     */
612    Result delete(DeleteRequest request) throws LdapException;
613
614    /**
615     * Deletes the named entry from the Directory Server.
616     * <p>
617     * This method is equivalent to the following code:
618     *
619     * <pre>
620     * DeleteRequest request = new DeleteRequest(name);
621     * connection.delete(request);
622     * </pre>
623     *
624     * @param name
625     *            The distinguished name of the entry to be deleted.
626     * @return The result of the operation.
627     * @throws LdapException
628     *             If the result code indicates that the request failed for some
629     *             reason.
630     * @throws LocalizedIllegalArgumentException
631     *             If {@code name} could not be decoded using the default
632     *             schema.
633     * @throws UnsupportedOperationException
634     *             If this connection does not support delete operations.
635     * @throws IllegalStateException
636     *             If this connection has already been closed, i.e. if
637     *             {@code isClosed() == true}.
638     * @throws NullPointerException
639     *             If {@code name} was {@code null}.
640     */
641    Result delete(String name) throws LdapException;
642
643    /**
644     * Deletes the named entry and all of its subordinates from the Directory
645     * Server.
646     * <p>
647     * This method is equivalent to the following code:
648     *
649     * <pre>
650     * DeleteRequest request = new DeleteRequest(name).addControl(
651     * connection.delete(request);
652     * </pre>
653     *
654     * @param name
655     *            The distinguished name of the subtree base entry to be
656     *            deleted.
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 LocalizedIllegalArgumentException
662     *             If {@code name} could not be decoded using the default
663     *             schema.
664     * @throws UnsupportedOperationException
665     *             If this connection does not support delete operations.
666     * @throws IllegalStateException
667     *             If this connection has already been closed, i.e. if
668     *             {@code isClosed() == true}.
669     * @throws NullPointerException
670     *             If {@code name} was {@code null}.
671     */
672    Result deleteSubtree(String name) throws LdapException;
673
674    /**
675     * Asynchronously deletes an entry from the Directory Server using the
676     * provided delete request.
677     *
678     * @param request
679     *            The delete request.
680     * @return A promise representing the result of the operation.
681     * @throws UnsupportedOperationException
682     *             If this connection does not support delete operations.
683     * @throws IllegalStateException
684     *             If this connection has already been closed, i.e. if
685     *             {@code isClosed() == true}.
686     * @throws NullPointerException
687     *             If {@code request} was {@code null}.
688     */
689    LdapPromise<Result> deleteAsync(DeleteRequest request);
690
691    /**
692     * Asynchronously deletes an entry from the Directory Server using the
693     * provided delete request.
694     *
695     * @param request
696     *            The delete request.
697     * @param intermediateResponseHandler
698     *            An intermediate response handler which can be used to process
699     *            any intermediate responses as they are received, may be
700     *            {@code null}.
701     * @return A promise representing the result of the operation.
702     * @throws UnsupportedOperationException
703     *             If this connection does not support delete operations.
704     * @throws IllegalStateException
705     *             If this connection has already been closed, i.e. if
706     *             {@code isClosed() == true}.
707     * @throws NullPointerException
708     *             If {@code request} was {@code null}.
709     */
710    LdapPromise<Result> deleteAsync(DeleteRequest request, IntermediateResponseHandler intermediateResponseHandler);
711
712    /**
713     * Requests that the Directory Server performs the provided extended
714     * request.
715     *
716     * @param <R>
717     *            The type of result returned by the extended request.
718     * @param request
719     *            The extended request.
720     * @return The result of the operation.
721     * @throws LdapException
722     *             If the result code indicates that the request failed for some
723     *             reason.
724     * @throws UnsupportedOperationException
725     *             If this connection does not support extended operations.
726     * @throws IllegalStateException
727     *             If this connection has already been closed, i.e. if
728     *             {@code isClosed() == true}.
729     * @throws NullPointerException
730     *             If {@code request} was {@code null}.
731     */
732    <R extends ExtendedResult> R extendedRequest(ExtendedRequest<R> request) throws LdapException;
733
734    /**
735     * Requests that the Directory Server performs the provided extended
736     * request, optionally listening for any intermediate responses.
737     *
738     * @param <R>
739     *            The type of result returned by the extended request.
740     * @param request
741     *            The extended request.
742     * @param handler
743     *            An intermediate response handler which can be used to process
744     *            any intermediate responses as they are received, may be
745     *            {@code null}.
746     * @return The result of the operation.
747     * @throws LdapException
748     *             If the result code indicates that the request failed for some
749     *             reason.
750     * @throws UnsupportedOperationException
751     *             If this connection does not support extended operations.
752     * @throws IllegalStateException
753     *             If this connection has already been closed, i.e. if
754     *             {@code isClosed() == true}.
755     * @throws NullPointerException
756     *             If {@code request} was {@code null}.
757     */
758    <R extends ExtendedResult> R extendedRequest(ExtendedRequest<R> request, IntermediateResponseHandler handler)
759            throws LdapException;
760
761    /**
762     * Requests that the Directory Server performs the provided extended
763     * request.
764     * <p>
765     * This method is equivalent to the following code:
766     *
767     * <pre>
768     * GenericExtendedRequest request = new GenericExtendedRequest(requestName, requestValue);
769     * connection.extendedRequest(request);
770     * </pre>
771     *
772     * @param requestName
773     *            The dotted-decimal representation of the unique OID
774     *            corresponding to the extended request.
775     * @param requestValue
776     *            The content of the extended request in a form defined by the
777     *            extended operation, or {@code null} if there is no content.
778     * @return The result of the operation.
779     * @throws LdapException
780     *             If the result code indicates that the request failed for some
781     *             reason.
782     * @throws UnsupportedOperationException
783     *             If this connection does not support extended operations.
784     * @throws IllegalStateException
785     *             If this connection has already been closed, i.e. if
786     *             {@code isClosed() == true}.
787     * @throws NullPointerException
788     *             If {@code requestName} was {@code null}.
789     */
790    GenericExtendedResult extendedRequest(String requestName, ByteString requestValue) throws LdapException;
791
792    /**
793     * Asynchronously performs the provided extended request in the Directory
794     * Server.
795     *
796     * @param <R>
797     *            The type of result returned by the extended request.
798     * @param request
799     *            The extended request.
800     * @return A promise representing the result of the operation.
801     * @throws UnsupportedOperationException
802     *             If this connection does not support extended operations.
803     * @throws IllegalStateException
804     *             If this connection has already been closed, i.e. if
805     *             {@code isClosed() == true}.
806     * @throws NullPointerException
807     *             If {@code request} was {@code null}.
808     */
809    <R extends ExtendedResult> LdapPromise<R> extendedRequestAsync(ExtendedRequest<R> request);
810
811    /**
812     * Asynchronously performs the provided extended request in the Directory
813     * Server.
814     *
815     * @param <R>
816     *            The type of result returned by the extended request.
817     * @param request
818     *            The extended request.
819     * @param intermediateResponseHandler
820     *            An intermediate response handler which can be used to process
821     *            any intermediate responses as they are received, may be
822     *            {@code null}.
823     * @return A promise representing the result of the operation.
824     * @throws UnsupportedOperationException
825     *             If this connection does not support extended operations.
826     * @throws IllegalStateException
827     *             If this connection has already been closed, i.e. if
828     *             {@code isClosed() == true}.
829     * @throws NullPointerException
830     *             If {@code request} was {@code null}.
831     */
832    <R extends ExtendedResult> LdapPromise<R> extendedRequestAsync(ExtendedRequest<R> request,
833        IntermediateResponseHandler intermediateResponseHandler);
834
835    /**
836     * Indicates whether this connection has been explicitly closed by
837     * calling {@code close}. This method will not return {@code true} if a
838     * fatal error has occurred on the connection unless {@code close} has been
839     * called.
840     *
841     * @return {@code true} if this connection has been explicitly closed by
842     *         calling {@code close}, or {@code false} otherwise.
843     */
844    boolean isClosed();
845
846    /**
847     * Returns {@code true} if this connection has not been closed and no fatal
848     * errors have been detected. This method is guaranteed to return
849     * {@code false} only when it is called after the method {@code close} has
850     * been called.
851     *
852     * @return {@code true} if this connection is valid, {@code false}
853     *         otherwise.
854     */
855    boolean isValid();
856
857    /**
858     * Modifies an entry in the Directory Server using the provided modify
859     * request.
860     *
861     * @param request
862     *            The modify request.
863     * @return The result of the operation.
864     * @throws LdapException
865     *             If the result code indicates that the request failed for some
866     *             reason.
867     * @throws UnsupportedOperationException
868     *             If this connection does not support modify operations.
869     * @throws IllegalStateException
870     *             If this connection has already been closed, i.e. if
871     *             {@code isClosed() == true}.
872     * @throws NullPointerException
873     *             If {@code request} was {@code null}.
874     */
875    Result modify(ModifyRequest request) throws LdapException;
876
877    /**
878     * Modifies an entry in the Directory Server using the provided lines of
879     * LDIF.
880     * <p>
881     * This method is equivalent to the following code:
882     *
883     * <pre>
884     * ModifyRequest request = new ModifyRequest(name, ldifChanges);
885     * connection.modify(request);
886     * </pre>
887     *
888     * @param ldifLines
889     *            Lines of LDIF containing the a single LDIF modify change
890     *            record.
891     * @return The result of the operation.
892     * @throws LdapException
893     *             If the result code indicates that the request failed for some
894     *             reason.
895     * @throws UnsupportedOperationException
896     *             If this connection does not support modify operations.
897     * @throws LocalizedIllegalArgumentException
898     *             If {@code ldifLines} was empty, or contained invalid LDIF, or
899     *             could not be decoded using the default schema.
900     * @throws IllegalStateException
901     *             If this connection has already been closed, i.e. if
902     *             {@code isClosed() == true}.
903     * @throws NullPointerException
904     *             If {@code ldifLines} was {@code null} .
905     */
906    Result modify(String... ldifLines) throws LdapException;
907
908    /**
909     * Asynchronously modifies an entry in the Directory Server using the
910     * provided modify request.
911     *
912     * @param request
913     *            The modify request.
914     * @return A promise representing the result of the operation.
915     * @throws UnsupportedOperationException
916     *             If this connection does not support modify operations.
917     * @throws IllegalStateException
918     *             If this connection has already been closed, i.e. if
919     *             {@code isClosed() == true}.
920     * @throws NullPointerException
921     *             If {@code request} was {@code null}.
922     */
923    LdapPromise<Result> modifyAsync(ModifyRequest request);
924
925    /**
926     * Asynchronously modifies an entry in the Directory Server using the
927     * provided modify request.
928     *
929     * @param request
930     *            The modify request.
931     * @param intermediateResponseHandler
932     *            An intermediate response handler which can be used to process
933     *            any intermediate responses as they are received, may be
934     *            {@code null}.
935     * @return A promise representing the result of the operation.
936     * @throws UnsupportedOperationException
937     *             If this connection does not support modify operations.
938     * @throws IllegalStateException
939     *             If this connection has already been closed, i.e. if
940     *             {@code isClosed() == true}.
941     * @throws NullPointerException
942     *             If {@code request} was {@code null}.
943     */
944    LdapPromise<Result> modifyAsync(ModifyRequest request, IntermediateResponseHandler intermediateResponseHandler);
945
946    /**
947     * Renames an entry in the Directory Server using the provided modify DN
948     * request.
949     *
950     * @param request
951     *            The modify DN request.
952     * @return The result of the operation.
953     * @throws LdapException
954     *             If the result code indicates that the request failed for some
955     *             reason.
956     * @throws UnsupportedOperationException
957     *             If this connection does not support modify DN operations.
958     * @throws IllegalStateException
959     *             If this connection has already been closed, i.e. if
960     *             {@code isClosed() == true}.
961     * @throws NullPointerException
962     *             If {@code request} was {@code null}.
963     */
964    Result modifyDN(ModifyDNRequest request) throws LdapException;
965
966    /**
967     * Renames the named entry in the Directory Server using the provided new
968     * RDN.
969     * <p>
970     * This method is equivalent to the following code:
971     *
972     * <pre>
973     * ModifyDNRequest request = new ModifyDNRequest(name, newRDN);
974     * connection.modifyDN(request);
975     * </pre>
976     *
977     * @param name
978     *            The distinguished name of the entry to be renamed.
979     * @param newRDN
980     *            The new RDN of the entry.
981     * @return The result of the operation.
982     * @throws LdapException
983     *             If the result code indicates that the request failed for some
984     *             reason.
985     * @throws LocalizedIllegalArgumentException
986     *             If {@code name} or {@code newRDN} could not be decoded using
987     *             the default schema.
988     * @throws UnsupportedOperationException
989     *             If this connection does not support modify DN operations.
990     * @throws IllegalStateException
991     *             If this connection has already been closed, i.e. if
992     *             {@code isClosed() == true}.
993     * @throws NullPointerException
994     *             If {@code name} or {@code newRDN} was {@code null}.
995     */
996    Result modifyDN(String name, String newRDN) throws LdapException;
997
998    /**
999     * Asynchronously renames an entry in the Directory Server using the
1000     * provided modify DN request.
1001     *
1002     * @param request
1003     *            The modify DN request.
1004     * @return A promise representing the result of the operation.
1005     * @throws UnsupportedOperationException
1006     *             If this connection does not support modify DN operations.
1007     * @throws IllegalStateException
1008     *             If this connection has already been closed, i.e. if
1009     *             {@code isClosed() == true}.
1010     * @throws NullPointerException
1011     *             If {@code request} was {@code null}.
1012     */
1013    LdapPromise<Result> modifyDNAsync(ModifyDNRequest request);
1014
1015    /**
1016     * Asynchronously renames an entry in the Directory Server using the
1017     * provided modify DN request.
1018     *
1019     * @param request
1020     *            The modify DN request.
1021     * @param intermediateResponseHandler
1022     *            An intermediate response handler which can be used to process
1023     *            any intermediate responses as they are received, may be
1024     *            {@code null}.
1025     * @return A promise representing the result of the operation.
1026     * @throws UnsupportedOperationException
1027     *             If this connection does not support modify DN operations.
1028     * @throws IllegalStateException
1029     *             If this connection has already been closed, i.e. if
1030     *             {@code isClosed() == true}.
1031     * @throws NullPointerException
1032     *             If {@code request} was {@code null}.
1033     */
1034    LdapPromise<Result> modifyDNAsync(ModifyDNRequest request,
1035        IntermediateResponseHandler intermediateResponseHandler);
1036
1037    /**
1038     * Reads the named entry from the Directory Server.
1039     * <p>
1040     * If the requested entry is not returned by the Directory Server then the
1041     * request will fail with an {@link EntryNotFoundException}. More
1042     * specifically, this method will never return {@code null}.
1043     * <p>
1044     * This method is equivalent to the following code:
1045     *
1046     * <pre>
1047     * SearchRequest request = new SearchRequest(name, SearchScope.BASE_OBJECT,
1048     * &quot;(objectClass=*)&quot;, attributeDescriptions);
1049     * connection.searchSingleEntry(request);
1050     * </pre>
1051     *
1052     * @param name
1053     *            The distinguished name of the entry to be read.
1054     * @param attributeDescriptions
1055     *            The names of the attributes to be included with the entry,
1056     *            which may be {@code null} or empty indicating that all user
1057     *            attributes should be returned.
1058     * @return The single search result entry returned from the search.
1059     * @throws LdapException
1060     *             If the result code indicates that the request failed for some
1061     *             reason.
1062     * @throws UnsupportedOperationException
1063     *             If this connection does not support search operations.
1064     * @throws IllegalStateException
1065     *             If this connection has already been closed, i.e. if
1066     *             {@code isClosed() == true}.
1067     * @throws NullPointerException
1068     *             If the {@code name} was {@code null}.
1069     */
1070    SearchResultEntry readEntry(DN name, String... attributeDescriptions) throws LdapException;
1071
1072    /**
1073     * Reads the named entry from the Directory Server.
1074     * <p>
1075     * If the requested entry is not returned by the Directory Server then the
1076     * request will fail with an {@link EntryNotFoundException}. More
1077     * specifically, this method will never return {@code null}.
1078     * <p>
1079     * This method is equivalent to the following code:
1080     *
1081     * <pre>
1082     * SearchRequest request =
1083     *         new SearchRequest(name, SearchScope.BASE_OBJECT, &quot;(objectClass=*)&quot;, attributeDescriptions);
1084     * connection.searchSingleEntry(request);
1085     * </pre>
1086     *
1087     * @param name
1088     *            The distinguished name of the entry to be read.
1089     * @param attributeDescriptions
1090     *            The names of the attributes to be included with the entry.
1091     * @return The single search result entry returned from the search.
1092     * @throws LdapException
1093     *             If the result code indicates that the request failed for some
1094     *             reason.
1095     * @throws LocalizedIllegalArgumentException
1096     *             If {@code baseObject} could not be decoded using the default
1097     *             schema.
1098     * @throws UnsupportedOperationException
1099     *             If this connection does not support search operations.
1100     * @throws IllegalStateException
1101     *             If this connection has already been closed, i.e. if
1102     *             {@code isClosed() == true}.
1103     * @throws NullPointerException
1104     *             If the {@code name} was {@code null}.
1105     */
1106    SearchResultEntry readEntry(String name, String... attributeDescriptions) throws LdapException;
1107
1108    /**
1109     * Asynchronously reads the named entry from the Directory Server.
1110     * <p>
1111     * If the requested entry is not returned by the Directory Server then the
1112     * request will fail with an {@link EntryNotFoundException}. More
1113     * specifically, the returned promise will never return {@code null}.
1114     * <p>
1115     * This method is equivalent to the following code:
1116     *
1117     * <pre>
1118     * SearchRequest request =
1119     *         new SearchRequest(name, SearchScope.BASE_OBJECT, &quot;(objectClass=*)&quot;, attributeDescriptions);
1120     * connection.searchSingleEntryAsync(request, resultHandler, p);
1121     * </pre>
1122     *
1123     * @param name
1124     *            The distinguished name of the entry to be read.
1125     * @param attributeDescriptions
1126     *            The names of the attributes to be included with the entry,
1127     *            which may be {@code null} or empty indicating that all user
1128     *            attributes should be returned.
1129     * @return A promise representing the result of the operation.
1130     * @throws UnsupportedOperationException
1131     *             If this connection does not support search operations.
1132     * @throws IllegalStateException
1133     *             If this connection has already been closed, i.e. if
1134     *             {@code isClosed() == true}.
1135     * @throws NullPointerException
1136     *             If the {@code name} was {@code null}.
1137     */
1138    LdapPromise<SearchResultEntry> readEntryAsync(DN name, Collection<String> attributeDescriptions);
1139
1140    /**
1141     * Removes the provided connection event listener from this connection so
1142     * that it will no longer be notified when this connection is closed by the
1143     * application, receives an unsolicited notification, or experiences a fatal
1144     * error.
1145     *
1146     * @param listener
1147     *            The listener which no longer wants to be notified when events
1148     *            occur on this connection.
1149     * @throws NullPointerException
1150     *             If the {@code listener} was {@code null}.
1151     */
1152    void removeConnectionEventListener(ConnectionEventListener listener);
1153
1154    /**
1155     * Searches the Directory Server using the provided search parameters. Any
1156     * matching entries returned by the search will be exposed through the
1157     * returned {@code ConnectionEntryReader}.
1158     * <p>
1159     * Unless otherwise specified, calling this method is equivalent to:
1160     *
1161     * <pre>
1162     * ConnectionEntryReader reader = new ConnectionEntryReader(this, request);
1163     * </pre>
1164     *
1165     * @param request
1166     *            The search request.
1167     * @return The result of the operation.
1168     * @throws UnsupportedOperationException
1169     *             If this connection does not support search operations.
1170     * @throws IllegalStateException
1171     *             If this connection has already been closed, i.e. if
1172     *             {@code isClosed() == true}.
1173     * @throws NullPointerException
1174     *             If {@code request} or {@code entries} was {@code null}.
1175     */
1176    ConnectionEntryReader search(SearchRequest request);
1177
1178    /**
1179     * Searches the Directory Server using the provided search request. Any
1180     * matching entries returned by the search will be added to {@code entries},
1181     * even if the final search result indicates that the search failed. Search
1182     * result references will be discarded.
1183     * <p>
1184     * <b>Warning:</b> Usage of this method is discouraged if the search request
1185     * is expected to yield a large number of search results since the entire
1186     * set of results will be stored in memory, potentially causing an
1187     * {@code OutOfMemoryError}.
1188     * <p>
1189     * This method is equivalent to the following code:
1190     *
1191     * <pre>
1192     * connection.search(request, entries, null);
1193     * </pre>
1194     *
1195     * @param request
1196     *            The search request.
1197     * @param entries
1198     *            The collection to which matching entries should be added.
1199     * @return The result of the operation.
1200     * @throws LdapException
1201     *             If the result code indicates that the request failed for some
1202     *             reason.
1203     * @throws UnsupportedOperationException
1204     *             If this connection does not support search operations.
1205     * @throws IllegalStateException
1206     *             If this connection has already been closed, i.e. if
1207     *             {@code isClosed() == true}.
1208     * @throws NullPointerException
1209     *             If {@code request} or {@code entries} was {@code null}.
1210     */
1211    Result search(SearchRequest request, Collection<? super SearchResultEntry> entries) throws LdapException;
1212
1213    /**
1214     * Searches the Directory Server using the provided search request. Any
1215     * matching entries returned by the search will be added to {@code entries},
1216     * even if the final search result indicates that the search failed.
1217     * Similarly, search result references returned by the search will be added
1218     * to {@code references}.
1219     * <p>
1220     * <b>Warning:</b> Usage of this method is discouraged if the search request
1221     * is expected to yield a large number of search results since the entire
1222     * set of results will be stored in memory, potentially causing an
1223     * {@code OutOfMemoryError}.
1224     *
1225     * @param request
1226     *            The search request.
1227     * @param entries
1228     *            The collection to which matching entries should be added.
1229     * @param references
1230     *            The collection to which search result references should be
1231     *            added, or {@code null} if references are to be discarded.
1232     * @return The result of the operation.
1233     * @throws LdapException
1234     *             If the result code indicates that the request failed for some
1235     *             reason.
1236     * @throws UnsupportedOperationException
1237     *             If this connection does not support search operations.
1238     * @throws IllegalStateException
1239     *             If this connection has already been closed, i.e. if
1240     *             {@code isClosed() == true}.
1241     * @throws NullPointerException
1242     *             If {@code request} or {@code entries} was {@code null}.
1243     */
1244    Result search(SearchRequest request, Collection<? super SearchResultEntry> entries,
1245            Collection<? super SearchResultReference> references) throws LdapException;
1246
1247    /**
1248     * Searches the Directory Server using the provided search request. Any
1249     * matching entries returned by the search as well as any search result
1250     * references will be passed to the provided search result handler.
1251     *
1252     * @param request
1253     *            The search request.
1254     * @param handler
1255     *            A search result handler which can be used to process the
1256     *            search result entries and references as they are received, may
1257     *            be {@code null}.
1258     * @return The result of the operation.
1259     * @throws LdapException
1260     *             If the result code indicates that the request failed for some
1261     *             reason.
1262     * @throws UnsupportedOperationException
1263     *             If this connection does not support search operations.
1264     * @throws IllegalStateException
1265     *             If this connection has already been closed, i.e. if
1266     *             {@code isClosed() == true}.
1267     * @throws NullPointerException
1268     *             If {@code request} was {@code null}.
1269     */
1270    Result search(SearchRequest request, SearchResultHandler handler) throws LdapException;
1271
1272    /**
1273     * Searches the Directory Server using the provided search parameters. Any
1274     * matching entries returned by the search will be exposed through the
1275     * {@code EntryReader} interface.
1276     * <p>
1277     * <b>Warning:</b> When using a queue with an optional capacity bound, the
1278     * connection will stop reading responses and wait if necessary for space to
1279     * become available.
1280     * <p>
1281     * This method is equivalent to the following code:
1282     *
1283     * <pre>
1284     * SearchRequest request = new SearchRequest(baseDN, scope, filter, attributeDescriptions);
1285     * connection.search(request, new LinkedBlockingQueue&lt;Response&gt;());
1286     * </pre>
1287     *
1288     * @param baseObject
1289     *            The distinguished name of the base entry relative to which the
1290     *            search is to be performed.
1291     * @param scope
1292     *            The scope of the search.
1293     * @param filter
1294     *            The filter that defines the conditions that must be fulfilled
1295     *            in order for an entry to be returned.
1296     * @param attributeDescriptions
1297     *            The names of the attributes to be included with each entry.
1298     * @return An entry reader exposing the returned entries.
1299     * @throws UnsupportedOperationException
1300     *             If this connection does not support search operations.
1301     * @throws IllegalStateException
1302     *             If this connection has already been closed, i.e. if
1303     *             {@code isClosed() == true}.
1304     * @throws NullPointerException
1305     *             If the {@code baseObject}, {@code scope}, or {@code filter}
1306     *             were {@code null}.
1307     */
1308    ConnectionEntryReader search(String baseObject, SearchScope scope, String filter,
1309            String... attributeDescriptions);
1310
1311    /**
1312     * Asynchronously searches the Directory Server using the provided search
1313     * request.
1314     *
1315     * @param request
1316     *            The search request.
1317     * @param entryHandler
1318     *            A search result handler which can be used to asynchronously
1319     *            process the search result entries and references as they are
1320     *            received, may be {@code null}.
1321     * @return A promise representing the result of the operation.
1322     * @throws UnsupportedOperationException
1323     *             If this connection does not support search operations.
1324     * @throws IllegalStateException
1325     *             If this connection has already been closed, i.e. if
1326     *             {@code isClosed() == true}.
1327     * @throws NullPointerException
1328     *             If {@code request} was {@code null}.
1329     */
1330    LdapPromise<Result> searchAsync(SearchRequest request, SearchResultHandler entryHandler);
1331
1332    /**
1333     * Asynchronously searches the Directory Server using the provided search
1334     * request.
1335     *
1336     * @param request
1337     *            The search request.
1338     * @param intermediateResponseHandler
1339     *            An intermediate response handler which can be used to process
1340     *            any intermediate responses as they are received, may be
1341     *            {@code null}.
1342     * @param entryHandler
1343     *            A search result handler which can be used to asynchronously
1344     *            process the search result entries and references as they are
1345     *            received, may be {@code null}.
1346     * @return A promise representing the result of the operation.
1347     * @throws UnsupportedOperationException
1348     *             If this connection does not support search operations.
1349     * @throws IllegalStateException
1350     *             If this connection has already been closed, i.e. if
1351     *             {@code isClosed() == true}.
1352     * @throws NullPointerException
1353     *             If {@code request} was {@code null}.
1354     */
1355    LdapPromise<Result> searchAsync(SearchRequest request, IntermediateResponseHandler intermediateResponseHandler,
1356        SearchResultHandler entryHandler);
1357
1358    /**
1359     * Searches the Directory Server for a single entry using the provided
1360     * search request.
1361     * <p>
1362     * If the requested entry is not returned by the Directory Server then the
1363     * request will fail with an {@link EntryNotFoundException}. More
1364     * specifically, this method will never return {@code null}. If multiple
1365     * matching entries are returned by the Directory Server then the request
1366     * will fail with an {@link MultipleEntriesFoundException}.
1367     *
1368     * @param request
1369     *            The search request.
1370     * @return The single search result entry returned from the search.
1371     * @throws LdapException
1372     *             If the result code indicates that the request failed for some
1373     *             reason.
1374     * @throws UnsupportedOperationException
1375     *             If this connection does not support search operations.
1376     * @throws IllegalStateException
1377     *             If this connection has already been closed, i.e. if
1378     *             {@code isClosed() == true}.
1379     * @throws NullPointerException
1380     *             If the {@code request} was {@code null}.
1381     */
1382    SearchResultEntry searchSingleEntry(SearchRequest request) throws LdapException;
1383
1384    /**
1385     * Searches the Directory Server for a single entry using the provided
1386     * search parameters.
1387     * <p>
1388     * If the requested entry is not returned by the Directory Server then the
1389     * request will fail with an {@link EntryNotFoundException}. More
1390     * specifically, this method will never return {@code null}. If multiple
1391     * matching entries are returned by the Directory Server then the request
1392     * will fail with an {@link MultipleEntriesFoundException}.
1393     * <p>
1394     * This method is equivalent to the following code:
1395     *
1396     * <pre>
1397     * SearchRequest request = new SearchRequest(baseObject, scope, filter, attributeDescriptions);
1398     * connection.searchSingleEntry(request);
1399     * </pre>
1400     *
1401     * @param baseObject
1402     *            The distinguished name of the base entry relative to which the
1403     *            search is to be performed.
1404     * @param scope
1405     *            The scope of the search.
1406     * @param filter
1407     *            The filter that defines the conditions that must be fulfilled
1408     *            in order for an entry to be returned.
1409     * @param attributeDescriptions
1410     *            The names of the attributes to be included with each entry.
1411     * @return The single search result entry returned from the search.
1412     * @throws LdapException
1413     *             If the result code indicates that the request failed for some
1414     *             reason.
1415     * @throws LocalizedIllegalArgumentException
1416     *             If {@code baseObject} could not be decoded using the default
1417     *             schema or if {@code filter} is not a valid LDAP string
1418     *             representation of a filter.
1419     * @throws UnsupportedOperationException
1420     *             If this connection does not support search operations.
1421     * @throws IllegalStateException
1422     *             If this connection has already been closed, i.e. if
1423     *             {@code isClosed() == true}.
1424     * @throws NullPointerException
1425     *             If the {@code baseObject}, {@code scope}, or {@code filter}
1426     *             were {@code null}.
1427     */
1428    SearchResultEntry searchSingleEntry(String baseObject, SearchScope scope, String filter,
1429            String... attributeDescriptions) throws LdapException;
1430
1431    /**
1432     * Asynchronously searches the Directory Server for a single entry using the
1433     * provided search request.
1434     * <p>
1435     * If the requested entry is not returned by the Directory Server then the
1436     * request will fail with an {@link EntryNotFoundException}. More
1437     * specifically, the returned promise will never return {@code null}. If
1438     * multiple matching entries are returned by the Directory Server then the
1439     * request will fail with an {@link MultipleEntriesFoundException}.
1440     *
1441     * @param request
1442     *            The search request.
1443     * @return A promise representing the result of the operation.
1444     * @throws UnsupportedOperationException
1445     *             If this connection does not support search operations.
1446     * @throws IllegalStateException
1447     *             If this connection has already been closed, i.e. if
1448     *             {@code isClosed() == true}.
1449     * @throws NullPointerException
1450     *             If the {@code request} was {@code null}.
1451     */
1452    LdapPromise<SearchResultEntry> searchSingleEntryAsync(SearchRequest request);
1453}