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