001/*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License").  You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2010 Sun Microsystems, Inc.
025 *      Portions copyright 2011-2014 ForgeRock AS
026 */
027
028package org.forgerock.opendj.ldap.requests;
029
030import static com.forgerock.opendj.util.StaticUtils.EMPTY_BYTES;
031import static com.forgerock.opendj.util.StaticUtils.getBytes;
032import static com.forgerock.opendj.ldap.CoreMessages.WARN_READ_LDIF_RECORD_CHANGE_RECORD_WRONG_TYPE;
033
034import javax.net.ssl.SSLContext;
035import javax.security.auth.Subject;
036
037import org.forgerock.i18n.LocalizableMessage;
038import org.forgerock.i18n.LocalizedIllegalArgumentException;
039import org.forgerock.opendj.ldap.AttributeDescription;
040import org.forgerock.opendj.ldap.ByteString;
041import org.forgerock.opendj.ldap.DN;
042import org.forgerock.opendj.ldap.Entries;
043import org.forgerock.opendj.ldap.Entry;
044import org.forgerock.opendj.ldap.Filter;
045import org.forgerock.opendj.ldap.LinkedHashMapEntry;
046import org.forgerock.opendj.ldap.ModificationType;
047import org.forgerock.opendj.ldap.RDN;
048import org.forgerock.opendj.ldap.SearchScope;
049import org.forgerock.opendj.ldif.ChangeRecord;
050import org.forgerock.opendj.ldif.LDIFChangeRecordReader;
051import org.forgerock.util.Reject;
052
053/**
054 * This class contains various methods for creating and manipulating requests.
055 * <p>
056 * All copy constructors of the form {@code copyOfXXXRequest} perform deep
057 * copies of their request parameter. More specifically, any controls,
058 * modifications, and attributes contained within the response will be
059 * duplicated.
060 * <p>
061 * Similarly, all unmodifiable views of request returned by methods of the form
062 * {@code unmodifiableXXXRequest} return deep unmodifiable views of their
063 * request parameter. More specifically, any controls, modifications, and
064 * attributes contained within the returned request will be unmodifiable.
065 */
066public final class Requests {
067
068    // TODO: search request from LDAP URL.
069
070    // TODO: update request from persistent search result.
071
072    // TODO: synchronized requests?
073
074    /**
075     * Creates a new abandon request that is an exact copy of the provided
076     * request.
077     *
078     * @param request
079     *            The abandon request to be copied.
080     * @return The new abandon request.
081     * @throws NullPointerException
082     *             If {@code request} was {@code null}
083     */
084    public static AbandonRequest copyOfAbandonRequest(final AbandonRequest request) {
085        return new AbandonRequestImpl(request);
086    }
087
088    /**
089     * Creates a new add request that is an exact copy of the provided request.
090     *
091     * @param request
092     *            The add request to be copied.
093     * @return The new add request.
094     * @throws NullPointerException
095     *             If {@code request} was {@code null} .
096     */
097    public static AddRequest copyOfAddRequest(final AddRequest request) {
098        return new AddRequestImpl(request);
099    }
100
101    /**
102     * Creates a new anonymous SASL bind request that is an exact copy of the
103     * provided request.
104     *
105     * @param request
106     *            The anonymous SASL bind request to be copied.
107     * @return The new anonymous SASL bind request.
108     * @throws NullPointerException
109     *             If {@code request} was {@code null} .
110     */
111    public static AnonymousSASLBindRequest copyOfAnonymousSASLBindRequest(
112            final AnonymousSASLBindRequest request) {
113        return new AnonymousSASLBindRequestImpl(request);
114    }
115
116    /**
117     * Creates a new cancel extended request that is an exact copy of the
118     * provided request.
119     *
120     * @param request
121     *            The cancel extended request to be copied.
122     * @return The new cancel extended request.
123     * @throws NullPointerException
124     *             If {@code request} was {@code null} .
125     */
126    public static CancelExtendedRequest copyOfCancelExtendedRequest(
127            final CancelExtendedRequest request) {
128        return new CancelExtendedRequestImpl(request);
129    }
130
131    /**
132     * Creates a new compare request that is an exact copy of the provided
133     * request.
134     *
135     * @param request
136     *            The compare request to be copied.
137     * @return The new compare request.
138     * @throws NullPointerException
139     *             If {@code request} was {@code null} .
140     */
141    public static CompareRequest copyOfCompareRequest(final CompareRequest request) {
142        return new CompareRequestImpl(request);
143    }
144
145    /**
146     * Creates a new CRAM MD5 SASL bind request that is an exact copy of the
147     * provided request.
148     *
149     * @param request
150     *            The CRAM MD5 SASL bind request to be copied.
151     * @return The new CRAM-MD5 SASL bind request.
152     * @throws NullPointerException
153     *             If {@code request} was {@code null}.
154     */
155    public static CRAMMD5SASLBindRequest copyOfCRAMMD5SASLBindRequest(
156            final CRAMMD5SASLBindRequest request) {
157        return new CRAMMD5SASLBindRequestImpl(request);
158    }
159
160    /**
161     * Creates a new delete request that is an exact copy of the provided
162     * request.
163     *
164     * @param request
165     *            The add request to be copied.
166     * @return The new delete request.
167     * @throws NullPointerException
168     *             If {@code request} was {@code null}.
169     */
170    public static DeleteRequest copyOfDeleteRequest(final DeleteRequest request) {
171        return new DeleteRequestImpl(request);
172    }
173
174    /**
175     * Creates a new digest MD5 SASL bind request that is an exact copy of the
176     * provided request.
177     *
178     * @param request
179     *            The digest MD5 SASL bind request to be copied.
180     * @return The new DIGEST-MD5 SASL bind request.
181     * @throws NullPointerException
182     *             If {@code request} was {@code null}.
183     */
184    public static DigestMD5SASLBindRequest copyOfDigestMD5SASLBindRequest(
185            final DigestMD5SASLBindRequest request) {
186        return new DigestMD5SASLBindRequestImpl(request);
187    }
188
189    /**
190     * Creates a new external SASL bind request that is an exact copy of the
191     * provided request.
192     *
193     * @param request
194     *            The external SASL bind request to be copied.
195     * @return The new External SASL bind request.
196     * @throws NullPointerException
197     *             If {@code request} was {@code null} .
198     */
199    public static ExternalSASLBindRequest copyOfExternalSASLBindRequest(
200            final ExternalSASLBindRequest request) {
201        return new ExternalSASLBindRequestImpl(request);
202    }
203
204    /**
205     * Creates a new generic bind request that is an exact copy of the provided
206     * request.
207     *
208     * @param request
209     *            The generic bind request to be copied.
210     * @return The new generic bind request.
211     * @throws NullPointerException
212     *             If {@code request} was {@code null} .
213     */
214    public static GenericBindRequest copyOfGenericBindRequest(final GenericBindRequest request) {
215        return new GenericBindRequestImpl(request);
216    }
217
218    /**
219     * Creates a new generic extended request that is an exact copy of the
220     * provided request.
221     *
222     * @param request
223     *            The generic extended request to be copied.
224     * @return The new generic extended request.
225     * @throws NullPointerException
226     *             If {@code request} was {@code null} .
227     */
228    public static GenericExtendedRequest copyOfGenericExtendedRequest(
229            final GenericExtendedRequest request) {
230        return new GenericExtendedRequestImpl(request);
231    }
232
233    /**
234     * Creates a new GSSAPI SASL bind request that is an exact copy of the
235     * provided request.
236     *
237     * @param request
238     *            The GSSAPI SASL bind request to be copied.
239     * @return The new GSSAPI SASL bind request.
240     * @throws NullPointerException
241     *             If {@code request} was {@code null}.
242     */
243    public static GSSAPISASLBindRequest copyOfGSSAPISASLBindRequest(
244            final GSSAPISASLBindRequest request) {
245        return new GSSAPISASLBindRequestImpl(request);
246    }
247
248    /**
249     * Creates a new modify DN request that is an exact copy of the provided
250     * request.
251     *
252     * @param request
253     *            The modify DN request to be copied.
254     * @return The new modify DN request.
255     * @throws NullPointerException
256     *             If {@code request} was {@code null} .
257     */
258    public static ModifyDNRequest copyOfModifyDNRequest(final ModifyDNRequest request) {
259        return new ModifyDNRequestImpl(request);
260    }
261
262    /**
263     * Creates a new modify request that is an exact copy of the provided
264     * request.
265     *
266     * @param request
267     *            The modify request to be copied.
268     * @return The new modify request.
269     * @throws NullPointerException
270     *             If {@code request} was {@code null} .
271     */
272    public static ModifyRequest copyOfModifyRequest(final ModifyRequest request) {
273        return new ModifyRequestImpl(request);
274    }
275
276    /**
277     * Creates a new password modify extended request that is an exact copy of
278     * the provided request.
279     *
280     * @param request
281     *            The password modify extended request to be copied.
282     * @return The new password modify extended request.
283     * @throws NullPointerException
284     *             If {@code request} was {@code null} .
285     */
286    public static PasswordModifyExtendedRequest copyOfPasswordModifyExtendedRequest(
287            final PasswordModifyExtendedRequest request) {
288        return new PasswordModifyExtendedRequestImpl(request);
289    }
290
291    /**
292     * Creates a new plain SASL bind request that is an exact copy of the
293     * provided request.
294     *
295     * @param request
296     *            The plain SASL bind request to be copied.
297     * @return The new Plain SASL bind request.
298     * @throws NullPointerException
299     *             If {@code request} was {@code null} .
300     */
301    public static PlainSASLBindRequest copyOfPlainSASLBindRequest(final PlainSASLBindRequest request) {
302        return new PlainSASLBindRequestImpl(request);
303    }
304
305    /**
306     * Creates a new search request that is an exact copy of the provided
307     * request.
308     *
309     * @param request
310     *            The search request to be copied.
311     * @return The new search request.
312     * @throws NullPointerException
313     *             If {@code request} was {@code null} .
314     */
315    public static SearchRequest copyOfSearchRequest(final SearchRequest request) {
316        return new SearchRequestImpl(request);
317    }
318
319    /**
320     * Creates a new simple bind request that is an exact copy of the provided
321     * request.
322     *
323     * @param request
324     *            The simple bind request to be copied.
325     * @return The new simple bind request.
326     * @throws NullPointerException
327     *             If {@code request} was {@code null} .
328     */
329    public static SimpleBindRequest copyOfSimpleBindRequest(final SimpleBindRequest request) {
330        return new SimpleBindRequestImpl(request);
331    }
332
333    /**
334     * Creates a new startTLS extended request that is an exact copy of the
335     * provided request.
336     *
337     * @param request
338     *            The startTLS extended request to be copied.
339     * @return The new start TLS extended request.
340     * @throws NullPointerException
341     *             If {@code request} was {@code null} .
342     */
343    public static StartTLSExtendedRequest copyOfStartTLSExtendedRequest(
344            final StartTLSExtendedRequest request) {
345        return new StartTLSExtendedRequestImpl(request);
346    }
347
348    /**
349     * Creates a new unbind request that is an exact copy of the provided
350     * request.
351     *
352     * @param request
353     *            The unbind request to be copied.
354     * @return The new unbind request.
355     * @throws NullPointerException
356     *             If {@code request} was {@code null} .
357     */
358    public static UnbindRequest copyOfUnbindRequest(final UnbindRequest request) {
359        return new UnbindRequestImpl(request);
360    }
361
362    /**
363     * Creates a new Who Am I extended request that is an exact copy of the
364     * provided request.
365     *
366     * @param request
367     *            The who Am I extended request to be copied.
368     * @return The new Who Am I extended request.
369     * @throws NullPointerException
370     *             If {@code request} was {@code null} .
371     */
372    public static WhoAmIExtendedRequest copyOfWhoAmIExtendedRequest(
373            final WhoAmIExtendedRequest request) {
374        return new WhoAmIExtendedRequestImpl(request);
375    }
376
377    /**
378     * Creates a new abandon request using the provided message ID.
379     *
380     * @param requestID
381     *            The request ID of the request to be abandoned.
382     * @return The new abandon request.
383     */
384    public static AbandonRequest newAbandonRequest(final int requestID) {
385        return new AbandonRequestImpl(requestID);
386    }
387
388    /**
389     * Creates a new add request using the provided distinguished name.
390     *
391     * @param name
392     *            The distinguished name of the entry to be added.
393     * @return The new add request.
394     * @throws NullPointerException
395     *             If {@code name} was {@code null}.
396     */
397    public static AddRequest newAddRequest(final DN name) {
398        final Entry entry = new LinkedHashMapEntry().setName(name);
399        return new AddRequestImpl(entry);
400    }
401
402    /**
403     * Creates a new add request backed by the provided entry. Modifications
404     * made to {@code entry} will be reflected in the returned add request. The
405     * returned add request supports updates to its list of controls, as well as
406     * updates to the name and attributes if the underlying entry allows.
407     *
408     * @param entry
409     *            The entry to be added.
410     * @return The new add request.
411     * @throws NullPointerException
412     *             If {@code entry} was {@code null} .
413     */
414    public static AddRequest newAddRequest(final Entry entry) {
415        Reject.ifNull(entry);
416        return new AddRequestImpl(entry);
417    }
418
419    /**
420     * Creates a new add request using the provided distinguished name decoded
421     * using the default schema.
422     *
423     * @param name
424     *            The distinguished name of the entry to be added.
425     * @return The new add request.
426     * @throws LocalizedIllegalArgumentException
427     *             If {@code name} could not be decoded using the default
428     *             schema.
429     * @throws NullPointerException
430     *             If {@code name} was {@code null}.
431     */
432    public static AddRequest newAddRequest(final String name) {
433        final Entry entry = new LinkedHashMapEntry().setName(name);
434        return new AddRequestImpl(entry);
435    }
436
437    /**
438     * Creates a new add request using the provided lines of LDIF decoded using
439     * the default schema.
440     *
441     * @param ldifLines
442     *            Lines of LDIF containing an LDIF add change record or an LDIF
443     *            entry record.
444     * @return The new add request.
445     * @throws LocalizedIllegalArgumentException
446     *             If {@code ldifLines} was empty, or contained invalid LDIF, or
447     *             could not be decoded using the default schema.
448     * @throws NullPointerException
449     *             If {@code ldifLines} was {@code null} .
450     */
451    public static AddRequest newAddRequest(final String... ldifLines) {
452        // LDIF change record reader is tolerant to missing change types.
453        final ChangeRecord record = LDIFChangeRecordReader.valueOfLDIFChangeRecord(ldifLines);
454
455        if (record instanceof AddRequest) {
456            return (AddRequest) record;
457        } else {
458            // Wrong change type.
459            final LocalizableMessage message =
460                    WARN_READ_LDIF_RECORD_CHANGE_RECORD_WRONG_TYPE.get("add");
461            throw new LocalizedIllegalArgumentException(message);
462        }
463    }
464
465    /**
466     * Creates a new anonymous SASL bind request having the provided trace
467     * string.
468     *
469     * @param traceString
470     *            The trace information, which has no semantic value, and can be
471     *            used by administrators in order to identify the user.
472     * @return The new anonymous SASL bind request.
473     * @throws NullPointerException
474     *             If {@code traceString} was {@code null}.
475     */
476    public static AnonymousSASLBindRequest newAnonymousSASLBindRequest(final String traceString) {
477        return new AnonymousSASLBindRequestImpl(traceString);
478    }
479
480    /**
481     * Creates a new cancel extended request using the provided message ID.
482     *
483     * @param requestID
484     *            The request ID of the request to be abandoned.
485     * @return The new cancel extended request.
486     */
487    public static CancelExtendedRequest newCancelExtendedRequest(final int requestID) {
488        return new CancelExtendedRequestImpl(requestID);
489    }
490
491    /**
492     * Creates a new change record (an add, delete, modify, or modify DN
493     * request) using the provided lines of LDIF decoded using the default
494     * schema.
495     *
496     * @param ldifLines
497     *            Lines of LDIF containing an LDIF change record or an LDIF
498     *            entry record.
499     * @return The new change record.
500     * @throws LocalizedIllegalArgumentException
501     *             If {@code ldifLines} was empty, or contained invalid LDIF, or
502     *             could not be decoded using the default schema.
503     * @throws NullPointerException
504     *             If {@code ldifLines} was {@code null} .
505     */
506    public static ChangeRecord newChangeRecord(final String... ldifLines) {
507        // LDIF change record reader is tolerant to missing change types.
508        return LDIFChangeRecordReader.valueOfLDIFChangeRecord(ldifLines);
509    }
510
511    /**
512     * Creates a new compare request using the provided distinguished name,
513     * attribute name, and assertion value.
514     * <p>
515     * If the assertion value is not an instance of {@code ByteString} then it
516     * will be converted using the {@link ByteString#valueOf(Object)} method.
517     *
518     * @param name
519     *            The distinguished name of the entry to be compared.
520     * @param attributeDescription
521     *            The name of the attribute to be compared.
522     * @param assertionValue
523     *            The assertion value to be compared.
524     * @return The new compare request.
525     * @throws NullPointerException
526     *             If {@code name}, {@code attributeDescription}, or
527     *             {@code assertionValue} was {@code null}.
528     */
529    public static CompareRequest newCompareRequest(final DN name,
530            final AttributeDescription attributeDescription, final Object assertionValue) {
531        Reject.ifNull(name, attributeDescription, assertionValue);
532        return new CompareRequestImpl(name, attributeDescription, ByteString
533                .valueOf(assertionValue));
534    }
535
536    /**
537     * Creates a new compare request using the provided distinguished name,
538     * attribute name, and assertion value decoded using the default schema.
539     * <p>
540     * If the assertion value is not an instance of {@code ByteString} then it
541     * will be converted using the {@link ByteString#valueOf(Object)} method.
542     *
543     * @param name
544     *            The distinguished name of the entry to be compared.
545     * @param attributeDescription
546     *            The name of the attribute to be compared.
547     * @param assertionValue
548     *            The assertion value to be compared.
549     * @return The new compare request.
550     * @throws LocalizedIllegalArgumentException
551     *             If {@code name} or {@code attributeDescription} could not be
552     *             decoded using the default schema.
553     * @throws NullPointerException
554     *             If {@code name}, {@code attributeDescription}, or
555     *             {@code assertionValue} was {@code null}.
556     */
557    public static CompareRequest newCompareRequest(final String name,
558            final String attributeDescription, final Object assertionValue) {
559        Reject.ifNull(name, attributeDescription, assertionValue);
560        return new CompareRequestImpl(DN.valueOf(name), AttributeDescription
561                .valueOf(attributeDescription), ByteString.valueOf(assertionValue));
562    }
563
564    /**
565     * Creates a new CRAM-MD5 SASL bind request having the provided
566     * authentication ID and password.
567     *
568     * @param authenticationID
569     *            The authentication ID of the user. The authentication ID
570     *            usually has the form "dn:" immediately followed by the
571     *            distinguished name of the user, or "u:" followed by a user ID
572     *            string, but other forms are permitted.
573     * @param password
574     *            The password of the user that the client wishes to bind as.
575     * @return The new CRAM-MD5 SASL bind request.
576     * @throws NullPointerException
577     *             If {@code authenticationID} or {@code password} was
578     *             {@code null}.
579     */
580    public static CRAMMD5SASLBindRequest newCRAMMD5SASLBindRequest(final String authenticationID,
581            final byte[] password) {
582        return new CRAMMD5SASLBindRequestImpl(authenticationID, password);
583    }
584
585    /**
586     * Creates a new CRAM-MD5 SASL bind request having the provided
587     * authentication ID and password.
588     *
589     * @param authenticationID
590     *            The authentication ID of the user. The authentication ID
591     *            usually has the form "dn:" immediately followed by the
592     *            distinguished name of the user, or "u:" followed by a user ID
593     *            string, but other forms are permitted.
594     * @param password
595     *            The password of the user that the client wishes to bind as.
596     *            The password will be converted to a UTF-8 octet string.
597     * @return The new CRAM-MD5 SASL bind request.
598     * @throws NullPointerException
599     *             If {@code authenticationID} or {@code password} was
600     *             {@code null}.
601     */
602    public static CRAMMD5SASLBindRequest newCRAMMD5SASLBindRequest(final String authenticationID,
603            final char[] password) {
604        return new CRAMMD5SASLBindRequestImpl(authenticationID, getBytes(password));
605    }
606
607    /**
608     * Creates a new delete request using the provided distinguished name.
609     *
610     * @param name
611     *            The distinguished name of the entry to be deleted.
612     * @return The new delete request.
613     * @throws NullPointerException
614     *             If {@code name} was {@code null}.
615     */
616    public static DeleteRequest newDeleteRequest(final DN name) {
617        Reject.ifNull(name);
618        return new DeleteRequestImpl(name);
619    }
620
621    /**
622     * Creates a new delete request using the provided distinguished name
623     * decoded using the default schema.
624     *
625     * @param name
626     *            The distinguished name of the entry to be deleted.
627     * @return The new delete request.
628     * @throws LocalizedIllegalArgumentException
629     *             If {@code name} could not be decoded using the default
630     *             schema.
631     * @throws NullPointerException
632     *             If {@code name} was {@code null}.
633     */
634    public static DeleteRequest newDeleteRequest(final String name) {
635        Reject.ifNull(name);
636        return new DeleteRequestImpl(DN.valueOf(name));
637    }
638
639    /**
640     * Creates a new DIGEST-MD5 SASL bind request having the provided
641     * authentication ID and password, but no realm or authorization ID.
642     *
643     * @param authenticationID
644     *            The authentication ID of the user. The authentication ID
645     *            usually has the form "dn:" immediately followed by the
646     *            distinguished name of the user, or "u:" followed by a user ID
647     *            string, but other forms are permitted.
648     * @param password
649     *            The password of the user that the client wishes to bind as.
650     * @return The new DIGEST-MD5 SASL bind request.
651     * @throws NullPointerException
652     *             If {@code authenticationID} or {@code password} was
653     *             {@code null}.
654     */
655    public static DigestMD5SASLBindRequest newDigestMD5SASLBindRequest(
656            final String authenticationID, final byte[] password) {
657        return new DigestMD5SASLBindRequestImpl(authenticationID, password);
658    }
659
660    /**
661     * Creates a new DIGEST-MD5 SASL bind request having the provided
662     * authentication ID and password, but no realm or authorization ID.
663     *
664     * @param authenticationID
665     *            The authentication ID of the user. The authentication ID
666     *            usually has the form "dn:" immediately followed by the
667     *            distinguished name of the user, or "u:" followed by a user ID
668     *            string, but other forms are permitted.
669     * @param password
670     *            The password of the user that the client wishes to bind as.
671     *            The password will be converted to a UTF-8 octet string.
672     * @return The new DIGEST-MD5 SASL bind request.
673     * @throws NullPointerException
674     *             If {@code authenticationID} or {@code password} was
675     *             {@code null}.
676     */
677    public static DigestMD5SASLBindRequest newDigestMD5SASLBindRequest(
678            final String authenticationID, final char[] password) {
679        return new DigestMD5SASLBindRequestImpl(authenticationID, getBytes(password));
680    }
681
682    /**
683     * Creates a new External SASL bind request with no authorization ID.
684     *
685     * @return The new External SASL bind request.
686     */
687    public static ExternalSASLBindRequest newExternalSASLBindRequest() {
688        return new ExternalSASLBindRequestImpl();
689    }
690
691    /**
692     * Creates a new generic bind request using an empty distinguished name,
693     * authentication type, and authentication information.
694     *
695     * @param authenticationType
696     *            The authentication mechanism identifier for this generic bind
697     *            request.
698     * @param authenticationValue
699     *            The authentication information for this generic bind request
700     *            in a form defined by the authentication mechanism.
701     * @return The new generic bind request.
702     * @throws NullPointerException
703     *             If {@code authenticationValue} was {@code null}.
704     */
705    public static GenericBindRequest newGenericBindRequest(final byte authenticationType,
706            final byte[] authenticationValue) {
707        Reject.ifNull(authenticationValue);
708        return new GenericBindRequestImpl("", authenticationType, authenticationValue);
709    }
710
711    /**
712     * Creates a new generic bind request using the provided name,
713     * authentication type, and authentication information.
714     * <p>
715     * The LDAP protocol defines the Bind name to be a distinguished name,
716     * however some LDAP implementations have relaxed this constraint and allow
717     * other identities to be used, such as the user's email address.
718     *
719     * @param name
720     *            The name of the Directory object that the client wishes to
721     *            bind as (may be empty).
722     * @param authenticationType
723     *            The authentication mechanism identifier for this generic bind
724     *            request.
725     * @param authenticationValue
726     *            The authentication information for this generic bind request
727     *            in a form defined by the authentication mechanism.
728     * @return The new generic bind request.
729     * @throws NullPointerException
730     *             If {@code name} or {@code authenticationValue} was
731     *             {@code null}.
732     */
733    public static GenericBindRequest newGenericBindRequest(final String name,
734            final byte authenticationType, final byte[] authenticationValue) {
735        Reject.ifNull(name, authenticationValue);
736        return new GenericBindRequestImpl(name, authenticationType, authenticationValue);
737    }
738
739    /**
740     * Creates a new generic extended request using the provided name and no
741     * value.
742     *
743     * @param requestName
744     *            The dotted-decimal representation of the unique OID
745     *            corresponding to this extended request.
746     * @return The new generic extended request.
747     * @throws NullPointerException
748     *             If {@code requestName} was {@code null}.
749     */
750    public static GenericExtendedRequest newGenericExtendedRequest(final String requestName) {
751        Reject.ifNull(requestName);
752        return new GenericExtendedRequestImpl(requestName);
753    }
754
755    /**
756     * Creates a new generic extended request using the provided name and
757     * optional value.
758     * <p>
759     * If the request value is not an instance of {@code ByteString} then it
760     * will be converted using the {@link ByteString#valueOf(Object)} method.
761     *
762     * @param requestName
763     *            The dotted-decimal representation of the unique OID
764     *            corresponding to this extended request.
765     * @param requestValue
766     *            The content of this generic extended request in a form defined
767     *            by the extended operation, or {@code null} if there is no
768     *            content.
769     * @return The new generic extended request.
770     * @throws NullPointerException
771     *             If {@code requestName} was {@code null}.
772     */
773    public static GenericExtendedRequest newGenericExtendedRequest(final String requestName,
774            final Object requestValue) {
775        Reject.ifNull(requestName);
776        return new GenericExtendedRequestImpl(requestName).setValue(requestValue);
777    }
778
779    /**
780     * Creates a new GSSAPI SASL bind request having the provided authentication
781     * ID and password, but no realm, KDC address, or authorization ID.
782     *
783     * @param authenticationID
784     *            The authentication ID of the user. The authentication ID
785     *            usually has the form "dn:" immediately followed by the
786     *            distinguished name of the user, or "u:" followed by a user ID
787     *            string, but other forms are permitted.
788     * @param password
789     *            The password of the user that the client wishes to bind as.
790     * @return The new GSSAPI SASL bind request.
791     * @throws NullPointerException
792     *             If {@code authenticationID} or {@code password} was
793     *             {@code null}.
794     */
795    public static GSSAPISASLBindRequest newGSSAPISASLBindRequest(final String authenticationID,
796            final byte[] password) {
797        return new GSSAPISASLBindRequestImpl(authenticationID, password);
798    }
799
800    /**
801     * Creates a new GSSAPI SASL bind request having the provided authentication
802     * ID and password, but no realm, KDC address, or authorization ID.
803     *
804     * @param authenticationID
805     *            The authentication ID of the user. The authentication ID
806     *            usually has the form "dn:" immediately followed by the
807     *            distinguished name of the user, or "u:" followed by a user ID
808     *            string, but other forms are permitted.
809     * @param password
810     *            The password of the user that the client wishes to bind as.
811     *            The password will be converted to a UTF-8 octet string.
812     * @return The new GSSAPI SASL bind request.
813     * @throws NullPointerException
814     *             If {@code authenticationID} or {@code password} was
815     *             {@code null}.
816     */
817    public static GSSAPISASLBindRequest newGSSAPISASLBindRequest(final String authenticationID,
818            final char[] password) {
819        return new GSSAPISASLBindRequestImpl(authenticationID, getBytes(password));
820    }
821
822    /**
823     * Creates a new GSSAPI SASL bind request having the provided subject, but
824     * no authorization ID.
825     *
826     * @param subject
827     *            The Kerberos subject of the user to be authenticated.
828     * @return The new GSSAPI SASL bind request.
829     * @throws NullPointerException
830     *             If {@code subject} was {@code null}.
831     */
832    public static GSSAPISASLBindRequest newGSSAPISASLBindRequest(final Subject subject) {
833        return new GSSAPISASLBindRequestImpl(subject);
834    }
835
836    /**
837     * Creates a new modify DN request using the provided distinguished name and
838     * new RDN. The new superior will be {@code null}, indicating that the
839     * renamed entry will remain under the same parent entry, and the old RDN
840     * attribute values will not be deleted.
841     *
842     * @param name
843     *            The distinguished name of the entry to be renamed.
844     * @param newRDN
845     *            The new RDN of the entry.
846     * @return The new modify DN request.
847     * @throws NullPointerException
848     *             If {@code name} or {@code newRDN} was {@code null}.
849     */
850    public static ModifyDNRequest newModifyDNRequest(final DN name, final RDN newRDN) {
851        Reject.ifNull(name);
852        Reject.ifNull(newRDN);
853        return new ModifyDNRequestImpl(name, newRDN);
854    }
855
856    /**
857     * Creates a new modify DN request using the provided distinguished name and
858     * new RDN decoded using the default schema. The new superior will be
859     * {@code null}, indicating that the renamed entry will remain under the
860     * same parent entry, and the old RDN attribute values will not be deleted.
861     *
862     * @param name
863     *            The distinguished name of the entry to be renamed.
864     * @param newRDN
865     *            The new RDN of the entry.
866     * @return The new modify DN request.
867     * @throws LocalizedIllegalArgumentException
868     *             If {@code name} or {@code newRDN} could not be decoded using
869     *             the default schema.
870     * @throws NullPointerException
871     *             If {@code name} or {@code newRDN} was {@code null}.
872     */
873    public static ModifyDNRequest newModifyDNRequest(final String name, final String newRDN) {
874        Reject.ifNull(name, newRDN);
875        return new ModifyDNRequestImpl(DN.valueOf(name), RDN.valueOf(newRDN));
876    }
877
878    /**
879     * Creates a new modify request using the provided distinguished name.
880     *
881     * @param name
882     *            The distinguished name of the entry to be modified.
883     * @return The new modify request.
884     * @throws NullPointerException
885     *             If {@code name} was {@code null}.
886     */
887    public static ModifyRequest newModifyRequest(final DN name) {
888        Reject.ifNull(name);
889        return new ModifyRequestImpl(name);
890    }
891
892    /**
893     * Creates a new modify request containing a list of modifications which can
894     * be used to transform {@code fromEntry} into entry {@code toEntry}.
895     * <p>
896     * The changes will be generated using a default set of
897     * {@link org.forgerock.opendj.ldap.Entries.DiffOptions options}. More
898     * specifically, only user attributes will be compared, attributes will be
899     * compared using their matching rules, and all generated changes will be
900     * reversible: it will contain only modifications of type
901     * {@link ModificationType#DELETE DELETE} then {@link ModificationType#ADD
902     * ADD}.
903     * <p>
904     * Finally, the modify request will use the distinguished name taken from
905     * {@code fromEntry}. Moreover, this method will not check to see if both
906     * {@code fromEntry} and {@code toEntry} have the same distinguished name.
907     * <p>
908     * This method is equivalent to:
909     *
910     * <pre>
911     * ModifyRequest request = Entries.diffEntries(fromEntry, toEntry);
912     * </pre>
913     *
914     * Or:
915     *
916     * <pre>
917     * ModifyRequest request = Entries.diffEntries(fromEntry, toEntry, Entries.diffOptions());
918     * </pre>
919     *
920     * @param fromEntry
921     *            The source entry.
922     * @param toEntry
923     *            The destination entry.
924     * @return A modify request containing a list of modifications which can be
925     *         used to transform {@code fromEntry} into entry {@code toEntry}.
926     *         The returned request will always be non-{@code null} but may not
927     *         contain any modifications.
928     * @throws NullPointerException
929     *             If {@code fromEntry} or {@code toEntry} were {@code null}.
930     * @see Entries#diffEntries(Entry, Entry)
931     */
932    public static ModifyRequest newModifyRequest(final Entry fromEntry, final Entry toEntry) {
933        return Entries.diffEntries(fromEntry, toEntry);
934    }
935
936    /**
937     * Creates a new modify request using the provided distinguished name
938     * decoded using the default schema.
939     *
940     * @param name
941     *            The distinguished name of the entry to be modified.
942     * @return The new modify request.
943     * @throws LocalizedIllegalArgumentException
944     *             If {@code name} could not be decoded using the default
945     *             schema.
946     * @throws NullPointerException
947     *             If {@code name} was {@code null}.
948     */
949    public static ModifyRequest newModifyRequest(final String name) {
950        Reject.ifNull(name);
951        return new ModifyRequestImpl(DN.valueOf(name));
952    }
953
954    /**
955     * Creates a new modify request using the provided lines of LDIF decoded
956     * using the default schema.
957     *
958     * @param ldifLines
959     *            Lines of LDIF containing a single LDIF modify change record.
960     * @return The new modify request.
961     * @throws LocalizedIllegalArgumentException
962     *             If {@code ldifLines} was empty, or contained invalid LDIF, or
963     *             could not be decoded using the default schema.
964     * @throws NullPointerException
965     *             If {@code ldifLines} was {@code null} .
966     */
967    public static ModifyRequest newModifyRequest(final String... ldifLines) {
968        // LDIF change record reader is tolerant to missing change types.
969        final ChangeRecord record = LDIFChangeRecordReader.valueOfLDIFChangeRecord(ldifLines);
970
971        if (record instanceof ModifyRequest) {
972            return (ModifyRequest) record;
973        } else {
974            // Wrong change type.
975            final LocalizableMessage message =
976                    WARN_READ_LDIF_RECORD_CHANGE_RECORD_WRONG_TYPE.get("modify");
977            throw new LocalizedIllegalArgumentException(message);
978        }
979    }
980
981    /**
982     * Creates a new password modify extended request, with no user identity,
983     * old password, or new password.
984     *
985     * @return The new password modify extended request.
986     */
987    public static PasswordModifyExtendedRequest newPasswordModifyExtendedRequest() {
988        return new PasswordModifyExtendedRequestImpl();
989    }
990
991    /**
992     * Creates a new Plain SASL bind request having the provided authentication
993     * ID and password, but no authorization ID.
994     *
995     * @param authenticationID
996     *            The authentication ID of the user. The authentication ID
997     *            usually has the form "dn:" immediately followed by the
998     *            distinguished name of the user, or "u:" followed by a user ID
999     *            string, but other forms are permitted.
1000     * @param password
1001     *            The password of the user that the client wishes to bind as.
1002     * @return The new Plain SASL bind request.
1003     * @throws NullPointerException
1004     *             If {@code authenticationID} or {@code password} was
1005     *             {@code null}.
1006     */
1007    public static PlainSASLBindRequest newPlainSASLBindRequest(final String authenticationID,
1008            final byte[] password) {
1009        return new PlainSASLBindRequestImpl(authenticationID, password);
1010    }
1011
1012    /**
1013     * Creates a new Plain SASL bind request having the provided authentication
1014     * ID and password, but no authorization ID.
1015     *
1016     * @param authenticationID
1017     *            The authentication ID of the user. The authentication ID
1018     *            usually has the form "dn:" immediately followed by the
1019     *            distinguished name of the user, or "u:" followed by a user ID
1020     *            string, but other forms are permitted.
1021     * @param password
1022     *            The password of the user that the client wishes to bind as.
1023     *            The password will be converted to a UTF-8 octet string.
1024     * @return The new Plain SASL bind request.
1025     * @throws NullPointerException
1026     *             If {@code authenticationID} or {@code password} was
1027     *             {@code null}.
1028     */
1029    public static PlainSASLBindRequest newPlainSASLBindRequest(final String authenticationID,
1030            final char[] password) {
1031        return new PlainSASLBindRequestImpl(authenticationID, getBytes(password));
1032    }
1033
1034    /**
1035     * Creates a new search request using the provided distinguished name,
1036     * scope, and filter.
1037     *
1038     * @param name
1039     *            The distinguished name of the base entry relative to which the
1040     *            search is to be performed.
1041     * @param scope
1042     *            The scope of the search.
1043     * @param filter
1044     *            The filter that defines the conditions that must be fulfilled
1045     *            in order for an entry to be returned.
1046     * @param attributeDescriptions
1047     *            The names of the attributes to be included with each entry.
1048     * @return The new search request.
1049     * @throws NullPointerException
1050     *             If the {@code name}, {@code scope}, or {@code filter} were
1051     *             {@code null}.
1052     */
1053    public static SearchRequest newSearchRequest(final DN name, final SearchScope scope,
1054            final Filter filter, final String... attributeDescriptions) {
1055        Reject.ifNull(name, scope, filter);
1056        final SearchRequest request = new SearchRequestImpl(name, scope, filter);
1057        for (final String attributeDescription : attributeDescriptions) {
1058            request.addAttribute(attributeDescription);
1059        }
1060        return request;
1061    }
1062
1063    /**
1064     * Creates a new search request using the provided distinguished name,
1065     * scope, and filter, decoded using the default schema.
1066     *
1067     * @param name
1068     *            The distinguished name of the base entry relative to which the
1069     *            search is to be performed.
1070     * @param scope
1071     *            The scope of the search.
1072     * @param filter
1073     *            The filter that defines the conditions that must be fulfilled
1074     *            in order for an entry to be returned.
1075     * @param attributeDescriptions
1076     *            The names of the attributes to be included with each entry.
1077     * @return The new search request.
1078     * @throws LocalizedIllegalArgumentException
1079     *             If {@code name} could not be decoded using the default
1080     *             schema, or if {@code filter} is not a valid LDAP string
1081     *             representation of a filter.
1082     * @throws NullPointerException
1083     *             If the {@code name}, {@code scope}, or {@code filter} were
1084     *             {@code null}.
1085     */
1086    public static SearchRequest newSearchRequest(final String name, final SearchScope scope,
1087            final String filter, final String... attributeDescriptions) {
1088        Reject.ifNull(name, scope, filter);
1089        final SearchRequest request =
1090                new SearchRequestImpl(DN.valueOf(name), scope, Filter.valueOf(filter));
1091        for (final String attributeDescription : attributeDescriptions) {
1092            request.addAttribute(attributeDescription);
1093        }
1094        return request;
1095    }
1096
1097    /**
1098     * Creates a new search request for a single entry, using the provided distinguished name,
1099     * scope, and filter.
1100     *
1101     * @param name
1102     *            The distinguished name of the base entry relative to which the
1103     *            search is to be performed.
1104     * @param scope
1105     *            The scope of the search.
1106     * @param filter
1107     *            The filter that defines the conditions that must be fulfilled
1108     *            in order for an entry to be returned.
1109     * @param attributeDescriptions
1110     *            The names of the attributes to be included with each entry.
1111     * @return The new search request.
1112     * @throws NullPointerException
1113     *             If the {@code name}, {@code scope}, or {@code filter} were
1114     *             {@code null}.
1115     */
1116    public static SearchRequest newSingleEntrySearchRequest(final DN name, final SearchScope scope,
1117            final Filter filter, final String... attributeDescriptions) {
1118        return newSearchRequest(name, scope, filter, attributeDescriptions).setSizeLimit(1);
1119    }
1120
1121    /**
1122     * Creates a new search request for a single entry, using the provided distinguished name,
1123     * scope, and filter, decoded using the default schema.
1124     *
1125     * @param name
1126     *            The distinguished name of the base entry relative to which the
1127     *            search is to be performed.
1128     * @param scope
1129     *            The scope of the search.
1130     * @param filter
1131     *            The filter that defines the conditions that must be fulfilled
1132     *            in order for an entry to be returned.
1133     * @param attributeDescriptions
1134     *            The names of the attributes to be included with each entry.
1135     * @return The new search request.
1136     * @throws LocalizedIllegalArgumentException
1137     *             If {@code name} could not be decoded using the default
1138     *             schema, or if {@code filter} is not a valid LDAP string
1139     *             representation of a filter.
1140     * @throws NullPointerException
1141     *             If the {@code name}, {@code scope}, or {@code filter} were
1142     *             {@code null}.
1143     */
1144    public static SearchRequest newSingleEntrySearchRequest(final String name, final SearchScope scope,
1145            final String filter, final String... attributeDescriptions) {
1146        return newSearchRequest(name, scope, filter, attributeDescriptions).setSizeLimit(1);
1147    }
1148
1149    /**
1150     * Creates a new simple bind request having an empty name and password
1151     * suitable for anonymous authentication.
1152     *
1153     * @return The new simple bind request.
1154     */
1155    public static SimpleBindRequest newSimpleBindRequest() {
1156        return new SimpleBindRequestImpl("", EMPTY_BYTES);
1157    }
1158
1159    /**
1160     * Creates a new simple bind request having the provided name and password
1161     * suitable for name/password authentication. The name will be decoded using
1162     * the default schema.
1163     * <p>
1164     * The LDAP protocol defines the Bind name to be a distinguished name,
1165     * however some LDAP implementations have relaxed this constraint and allow
1166     * other identities to be used, such as the user's email address.
1167     *
1168     * @param name
1169     *            The name of the Directory object that the client wishes to
1170     *            bind as, which may be empty.
1171     * @param password
1172     *            The password of the Directory object that the client wishes to
1173     *            bind as, which may be empty indicating that an unauthenticated
1174     *            bind is to be performed.
1175     * @return The new simple bind request.
1176     * @throws NullPointerException
1177     *             If {@code name} or {@code password} was {@code null}.
1178     */
1179    public static SimpleBindRequest newSimpleBindRequest(final String name, final byte[] password) {
1180        Reject.ifNull(name, password);
1181        return new SimpleBindRequestImpl(name, password);
1182    }
1183
1184    /**
1185     * Creates a new simple bind request having the provided name and password
1186     * suitable for name/password authentication. The name will be decoded using
1187     * the default schema.
1188     * <p>
1189     * The LDAP protocol defines the Bind name to be a distinguished name,
1190     * however some LDAP implementations have relaxed this constraint and allow
1191     * other identities to be used, such as the user's email address.
1192     *
1193     * @param name
1194     *            The name of the Directory object that the client wishes to
1195     *            bind as, which may be empty.
1196     * @param password
1197     *            The password of the Directory object that the client wishes to
1198     *            bind as, which may be empty indicating that an unauthenticated
1199     *            bind is to be performed. The password will be converted to a
1200     *            UTF-8 octet string.
1201     * @return The new simple bind request.
1202     * @throws NullPointerException
1203     *             If {@code name} or {@code password} was {@code null}.
1204     */
1205    public static SimpleBindRequest newSimpleBindRequest(final String name, final char[] password) {
1206        Reject.ifNull(name, password);
1207        return new SimpleBindRequestImpl(name, getBytes(password));
1208    }
1209
1210    /**
1211     * Creates a new start TLS extended request which will use the provided SSL
1212     * context.
1213     *
1214     * @param sslContext
1215     *            The SSLContext that should be used when installing the TLS
1216     *            layer.
1217     * @return The new start TLS extended request.
1218     * @throws NullPointerException
1219     *             If {@code sslContext} was {@code null}.
1220     */
1221    public static StartTLSExtendedRequest newStartTLSExtendedRequest(final SSLContext sslContext) {
1222        return new StartTLSExtendedRequestImpl(sslContext);
1223    }
1224
1225    /**
1226     * Creates a new unbind request.
1227     *
1228     * @return The new unbind request.
1229     */
1230    public static UnbindRequest newUnbindRequest() {
1231        return new UnbindRequestImpl();
1232    }
1233
1234    /**
1235     * Creates a new Who Am I extended request.
1236     *
1237     * @return The new Who Am I extended request.
1238     */
1239    public static WhoAmIExtendedRequest newWhoAmIExtendedRequest() {
1240        return new WhoAmIExtendedRequestImpl();
1241    }
1242
1243    /**
1244     * Creates an unmodifiable abandon request of the provided request.
1245     *
1246     * @param request
1247     *            The abandon request to be copied.
1248     * @return The new abandon request.
1249     * @throws NullPointerException
1250     *             If {@code request} was {@code null}
1251     */
1252    public static AbandonRequest unmodifiableAbandonRequest(final AbandonRequest request) {
1253        if (request instanceof UnmodifiableAbandonRequestImpl) {
1254            return request;
1255        }
1256        return new UnmodifiableAbandonRequestImpl(request);
1257    }
1258
1259    /**
1260     * Creates an unmodifiable add request of the provided request.
1261     *
1262     * @param request
1263     *            The add request to be copied.
1264     * @return The new add request.
1265     * @throws NullPointerException
1266     *             If {@code request} was {@code null} .
1267     */
1268    public static AddRequest unmodifiableAddRequest(final AddRequest request) {
1269        if (request instanceof UnmodifiableAddRequestImpl) {
1270            return request;
1271        }
1272        return new UnmodifiableAddRequestImpl(request);
1273    }
1274
1275    /**
1276     * Creates an unmodifiable anonymous SASL bind request of the provided
1277     * request.
1278     *
1279     * @param request
1280     *            The anonymous SASL bind request to be copied.
1281     * @return The new anonymous SASL bind request.
1282     * @throws NullPointerException
1283     *             If {@code request} was {@code null} .
1284     */
1285    public static AnonymousSASLBindRequest unmodifiableAnonymousSASLBindRequest(
1286            final AnonymousSASLBindRequest request) {
1287        if (request instanceof UnmodifiableAnonymousSASLBindRequestImpl) {
1288            return request;
1289        }
1290        return new UnmodifiableAnonymousSASLBindRequestImpl(request);
1291    }
1292
1293    /**
1294     * Creates an unmodifiable cancel extended request of the provided request.
1295     *
1296     * @param request
1297     *            The cancel extended request to be copied.
1298     * @return The new cancel extended request.
1299     * @throws NullPointerException
1300     *             If {@code request} was {@code null} .
1301     */
1302    public static CancelExtendedRequest unmodifiableCancelExtendedRequest(
1303            final CancelExtendedRequest request) {
1304        if (request instanceof UnmodifiableCancelExtendedRequestImpl) {
1305            return request;
1306        }
1307        return new UnmodifiableCancelExtendedRequestImpl(request);
1308    }
1309
1310    /**
1311     * Creates an unmodifiable compare request of the provided request.
1312     *
1313     * @param request
1314     *            The compare request to be copied.
1315     * @return The new compare request.
1316     * @throws NullPointerException
1317     *             If {@code request} was {@code null} .
1318     */
1319    public static CompareRequest unmodifiableCompareRequest(final CompareRequest request) {
1320        if (request instanceof UnmodifiableCompareRequestImpl) {
1321            return request;
1322        }
1323        return new UnmodifiableCompareRequestImpl(request);
1324    }
1325
1326    /**
1327     * Creates an unmodifiable CRAM MD5 SASL bind request of the provided
1328     * request.
1329     * <p>
1330     * The returned bind request creates defensive copies of the password in
1331     * order to maintain immutability.
1332     *
1333     * @param request
1334     *            The CRAM MD5 SASL bind request to be copied.
1335     * @return The new CRAM-MD5 SASL bind request.
1336     * @throws NullPointerException
1337     *             If {@code request} was {@code null}.
1338     */
1339    public static CRAMMD5SASLBindRequest unmodifiableCRAMMD5SASLBindRequest(
1340            final CRAMMD5SASLBindRequest request) {
1341        if (request instanceof UnmodifiableCRAMMD5SASLBindRequestImpl) {
1342            return request;
1343        }
1344        return new UnmodifiableCRAMMD5SASLBindRequestImpl(request);
1345    }
1346
1347    /**
1348     * Creates an unmodifiable delete request of the provided request.
1349     *
1350     * @param request
1351     *            The add request to be copied.
1352     * @return The new delete request.
1353     * @throws NullPointerException
1354     *             If {@code request} was {@code null}.
1355     */
1356    public static DeleteRequest unmodifiableDeleteRequest(final DeleteRequest request) {
1357        if (request instanceof UnmodifiableDeleteRequestImpl) {
1358            return request;
1359        }
1360        return new UnmodifiableDeleteRequestImpl(request);
1361    }
1362
1363    /**
1364     * Creates an unmodifiable digest MD5 SASL bind request of the provided
1365     * request.
1366     * <p>
1367     * The returned bind request creates defensive copies of the password in
1368     * order to maintain immutability.
1369     *
1370     * @param request
1371     *            The digest MD5 SASL bind request to be copied.
1372     * @return The new DIGEST-MD5 SASL bind request.
1373     * @throws NullPointerException
1374     *             If {@code request} was {@code null}.
1375     */
1376    public static DigestMD5SASLBindRequest unmodifiableDigestMD5SASLBindRequest(
1377            final DigestMD5SASLBindRequest request) {
1378        if (request instanceof UnmodifiableDigestMD5SASLBindRequestImpl) {
1379            return request;
1380        }
1381        return new UnmodifiableDigestMD5SASLBindRequestImpl(request);
1382    }
1383
1384    /**
1385     * Creates an unmodifiable external SASL bind request of the provided
1386     * request.
1387     *
1388     * @param request
1389     *            The external SASL bind request to be copied.
1390     * @return The new External SASL bind request.
1391     * @throws NullPointerException
1392     *             If {@code request} was {@code null} .
1393     */
1394    public static ExternalSASLBindRequest unmodifiableExternalSASLBindRequest(
1395            final ExternalSASLBindRequest request) {
1396        if (request instanceof UnmodifiableExternalSASLBindRequestImpl) {
1397            return request;
1398        }
1399        return new UnmodifiableExternalSASLBindRequestImpl(request);
1400    }
1401
1402    /**
1403     * Creates an unmodifiable generic bind request of the provided request.
1404     * <p>
1405     * The returned bind request creates defensive copies of the authentication
1406     * value in order to maintain immutability.
1407     *
1408     * @param request
1409     *            The generic bind request to be copied.
1410     * @return The new generic bind request.
1411     * @throws NullPointerException
1412     *             If {@code request} was {@code null} .
1413     */
1414    public static GenericBindRequest unmodifiableGenericBindRequest(final GenericBindRequest request) {
1415        if (request instanceof UnmodifiableGenericBindRequestImpl) {
1416            return request;
1417        }
1418        return new UnmodifiableGenericBindRequestImpl(request);
1419    }
1420
1421    /**
1422     * Creates an unmodifiable generic extended request of the provided request.
1423     *
1424     * @param request
1425     *            The generic extended request to be copied.
1426     * @return The new generic extended request.
1427     * @throws NullPointerException
1428     *             If {@code request} was {@code null} .
1429     */
1430    public static GenericExtendedRequest unmodifiableGenericExtendedRequest(
1431            final GenericExtendedRequest request) {
1432        if (request instanceof UnmodifiableGenericExtendedRequestImpl) {
1433            return request;
1434        }
1435        return new UnmodifiableGenericExtendedRequestImpl(request);
1436    }
1437
1438    /**
1439     * Creates an unmodifiable GSSAPI SASL bind request of the provided request.
1440     * <p>
1441     * The returned bind request creates defensive copies of the password in
1442     * order to maintain immutability.
1443     *
1444     * @param request
1445     *            The GSSAPI SASL bind request to be copied.
1446     * @return The new GSSAPI SASL bind request.
1447     * @throws NullPointerException
1448     *             If {@code request} was {@code null}.
1449     */
1450    public static GSSAPISASLBindRequest unmodifiableGSSAPISASLBindRequest(
1451            final GSSAPISASLBindRequest request) {
1452        if (request instanceof UnmodifiableGSSAPISASLBindRequestImpl) {
1453            return request;
1454        }
1455        return new UnmodifiableGSSAPISASLBindRequestImpl(request);
1456    }
1457
1458    /**
1459     * Creates an unmodifiable modify DN request of the provided request.
1460     *
1461     * @param request
1462     *            The modify DN request to be copied.
1463     * @return The new modify DN request.
1464     * @throws NullPointerException
1465     *             If {@code request} was {@code null} .
1466     */
1467    public static ModifyDNRequest unmodifiableModifyDNRequest(final ModifyDNRequest request) {
1468        if (request instanceof UnmodifiableModifyDNRequestImpl) {
1469            return request;
1470        }
1471        return new UnmodifiableModifyDNRequestImpl(request);
1472    }
1473
1474    /**
1475     * Creates an unmodifiable modify request of the provided request.
1476     *
1477     * @param request
1478     *            The modify request to be copied.
1479     * @return The new modify request.
1480     * @throws NullPointerException
1481     *             If {@code request} was {@code null} .
1482     */
1483    public static ModifyRequest unmodifiableModifyRequest(final ModifyRequest request) {
1484        if (request instanceof UnmodifiableModifyRequestImpl) {
1485            return request;
1486        }
1487        return new UnmodifiableModifyRequestImpl(request);
1488    }
1489
1490    /**
1491     * Creates an unmodifiable password modify extended request of the provided
1492     * request.
1493     *
1494     * @param request
1495     *            The password modify extended request to be copied.
1496     * @return The new password modify extended request.
1497     * @throws NullPointerException
1498     *             If {@code request} was {@code null} .
1499     */
1500    public static PasswordModifyExtendedRequest unmodifiablePasswordModifyExtendedRequest(
1501            final PasswordModifyExtendedRequest request) {
1502        if (request instanceof UnmodifiablePasswordModifyExtendedRequestImpl) {
1503            return request;
1504        }
1505        return new UnmodifiablePasswordModifyExtendedRequestImpl(request);
1506    }
1507
1508    /**
1509     * Creates an unmodifiable plain SASL bind request of the provided request.
1510     * <p>
1511     * The returned bind request creates defensive copies of the password in
1512     * order to maintain immutability.
1513     *
1514     * @param request
1515     *            The plain SASL bind request to be copied.
1516     * @return The new Plain SASL bind request.
1517     * @throws NullPointerException
1518     *             If {@code request} was {@code null} .
1519     */
1520    public static PlainSASLBindRequest unmodifiablePlainSASLBindRequest(
1521            final PlainSASLBindRequest request) {
1522        if (request instanceof UnmodifiablePlainSASLBindRequestImpl) {
1523            return request;
1524        }
1525        return new UnmodifiablePlainSASLBindRequestImpl(request);
1526    }
1527
1528    /**
1529     * Creates an unmodifiable search request of the provided request.
1530     *
1531     * @param request
1532     *            The search request to be copied.
1533     * @return The new search request.
1534     * @throws NullPointerException
1535     *             If {@code request} was {@code null} .
1536     */
1537    public static SearchRequest unmodifiableSearchRequest(final SearchRequest request) {
1538        if (request instanceof UnmodifiableSearchRequestImpl) {
1539            return request;
1540        }
1541        return new UnmodifiableSearchRequestImpl(request);
1542    }
1543
1544    /**
1545     * Creates an unmodifiable simple bind request of the provided request.
1546     * <p>
1547     * The returned bind request creates defensive copies of the password in
1548     * order to maintain immutability.
1549     *
1550     * @param request
1551     *            The simple bind request to be copied.
1552     * @return The new simple bind request.
1553     * @throws NullPointerException
1554     *             If {@code request} was {@code null} .
1555     */
1556    public static SimpleBindRequest unmodifiableSimpleBindRequest(final SimpleBindRequest request) {
1557        if (request instanceof UnmodifiableSimpleBindRequestImpl) {
1558            return request;
1559        }
1560        return new UnmodifiableSimpleBindRequestImpl(request);
1561    }
1562
1563    /**
1564     * Creates an unmodifiable startTLS extended request of the provided
1565     * request.
1566     *
1567     * @param request
1568     *            The startTLS extended request to be copied.
1569     * @return The new start TLS extended request.
1570     * @throws NullPointerException
1571     *             If {@code request} was {@code null} .
1572     */
1573    public static StartTLSExtendedRequest unmodifiableStartTLSExtendedRequest(
1574            final StartTLSExtendedRequest request) {
1575        if (request instanceof UnmodifiableStartTLSExtendedRequestImpl) {
1576            return request;
1577        }
1578        return new UnmodifiableStartTLSExtendedRequestImpl(request);
1579    }
1580
1581    /**
1582     * Creates an unmodifiable unbind request of the provided request.
1583     *
1584     * @param request
1585     *            The unbind request to be copied.
1586     * @return The new unbind request.
1587     * @throws NullPointerException
1588     *             If {@code request} was {@code null} .
1589     */
1590    public static UnbindRequest unmodifiableUnbindRequest(final UnbindRequest request) {
1591        if (request instanceof UnmodifiableUnbindRequestImpl) {
1592            return request;
1593        }
1594        return new UnmodifiableUnbindRequestImpl(request);
1595    }
1596
1597    /**
1598     * Creates an unmodifiable new Who Am I extended request of the provided
1599     * request.
1600     *
1601     * @param request
1602     *            The who Am I extended request to be copied.
1603     * @return The new Who Am I extended request.
1604     * @throws NullPointerException
1605     *             If {@code request} was {@code null} .
1606     */
1607    public static WhoAmIExtendedRequest unmodifiableWhoAmIExtendedRequest(
1608            final WhoAmIExtendedRequest request) {
1609        if (request instanceof UnmodifiableWhoAmIExtendedRequestImpl) {
1610            return request;
1611        }
1612        return new UnmodifiableWhoAmIExtendedRequestImpl(request);
1613    }
1614
1615    private Requests() {
1616        // Prevent instantiation.
1617    }
1618}