001    /*
002     * Copyright 2009-2016 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2009-2016 UnboundID Corp.
007     *
008     * This program is free software; you can redistribute it and/or modify
009     * it under the terms of the GNU General Public License (GPLv2 only)
010     * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011     * as published by the Free Software Foundation.
012     *
013     * This program is distributed in the hope that it will be useful,
014     * but WITHOUT ANY WARRANTY; without even the implied warranty of
015     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016     * GNU General Public License for more details.
017     *
018     * You should have received a copy of the GNU General Public License
019     * along with this program; if not, see <http://www.gnu.org/licenses>.
020     */
021    package com.unboundid.ldap.sdk.migrate.ldapjdk;
022    
023    
024    
025    import com.unboundid.asn1.ASN1OctetString;
026    import com.unboundid.ldap.sdk.AddRequest;
027    import com.unboundid.ldap.sdk.BindResult;
028    import com.unboundid.ldap.sdk.CompareRequest;
029    import com.unboundid.ldap.sdk.CompareResult;
030    import com.unboundid.ldap.sdk.Control;
031    import com.unboundid.ldap.sdk.DeleteRequest;
032    import com.unboundid.ldap.sdk.DereferencePolicy;
033    import com.unboundid.ldap.sdk.ExtendedRequest;
034    import com.unboundid.ldap.sdk.ExtendedResult;
035    import com.unboundid.ldap.sdk.Filter;
036    import com.unboundid.ldap.sdk.InternalSDKHelper;
037    import com.unboundid.ldap.sdk.LDAPConnectionOptions;
038    import com.unboundid.ldap.sdk.LDAPResult;
039    import com.unboundid.ldap.sdk.Modification;
040    import com.unboundid.ldap.sdk.ModifyDNRequest;
041    import com.unboundid.ldap.sdk.ModifyRequest;
042    import com.unboundid.ldap.sdk.ResultCode;
043    import com.unboundid.ldap.sdk.SearchRequest;
044    import com.unboundid.ldap.sdk.SearchResult;
045    import com.unboundid.ldap.sdk.SearchScope;
046    import com.unboundid.ldap.sdk.SimpleBindRequest;
047    import com.unboundid.ldap.sdk.UpdatableLDAPRequest;
048    import com.unboundid.util.Mutable;
049    import com.unboundid.util.NotExtensible;
050    import com.unboundid.util.ThreadSafety;
051    import com.unboundid.util.ThreadSafetyLevel;
052    
053    import static com.unboundid.util.Debug.*;
054    
055    
056    
057    /**
058     * This class provides an object that may be used to communicate with an LDAP
059     * directory server.
060     * <BR><BR>
061     * This class is primarily intended to be used in the process of updating
062     * applications which use the Netscape Directory SDK for Java to switch to or
063     * coexist with the UnboundID LDAP SDK for Java.  For applications not written
064     * using the Netscape Directory SDK for Java, the
065     * {@link com.unboundid.ldap.sdk.LDAPConnection} class should be used instead.
066     */
067    @Mutable()
068    @NotExtensible()
069    @ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
070    public class LDAPConnection
071    {
072      /**
073       * The integer value for the DEREF_NEVER dereference policy.
074       */
075      public static final int DEREF_NEVER = DereferencePolicy.NEVER.intValue();
076    
077    
078    
079      /**
080       * The integer value for the DEREF_SEARCHING dereference policy.
081       */
082      public static final int DEREF_SEARCHING =
083           DereferencePolicy.SEARCHING.intValue();
084    
085    
086    
087      /**
088       * The integer value for the DEREF_FINDING dereference policy.
089       */
090      public static final int DEREF_FINDING =
091           DereferencePolicy.FINDING.intValue();
092    
093    
094    
095      /**
096       * The integer value for the DEREF_ALWAYS dereference policy.
097       */
098      public static final int DEREF_ALWAYS =
099           DereferencePolicy.ALWAYS.intValue();
100    
101    
102    
103      /**
104       * The integer value for the SCOPE_BASE search scope.
105       */
106      public static final int SCOPE_BASE = SearchScope.BASE_INT_VALUE;
107    
108    
109    
110      /**
111       * The integer value for the SCOPE_ONE search scope.
112       */
113      public static final int SCOPE_ONE = SearchScope.ONE_INT_VALUE;
114    
115    
116    
117      /**
118       * The integer value for the SCOPE_SUB search scope.
119       */
120      public static final int SCOPE_SUB = SearchScope.SUB_INT_VALUE;
121    
122    
123    
124      // The connection used to perform the actual communication with the server.
125      private volatile com.unboundid.ldap.sdk.LDAPConnection conn;
126    
127      // The default constraints that will be used for non-search operations.
128      private LDAPConstraints constraints;
129    
130      // The set of controls returned from the last operation.
131      private LDAPControl[] responseControls;
132    
133      // The default constraints that will be used for search operations.
134      private LDAPSearchConstraints searchConstraints;
135    
136      // The socket factory for this connection.
137      private LDAPSocketFactory socketFactory;
138    
139      // The DN last used to bind to the server.
140      private String authDN;
141    
142      // The password last used to bind to the server.
143      private String authPW;
144    
145    
146    
147      /**
148       * Creates a new LDAP connection which will use the default socket factory.
149       */
150      public LDAPConnection()
151      {
152        this(null);
153      }
154    
155    
156    
157      /**
158       * Creates a new LDAP connection which will use the provided socket factory.
159       *
160       * @param  socketFactory  The socket factory to use when creating the socket
161       *                        to use for communicating with the server.
162       */
163      public LDAPConnection(final LDAPSocketFactory socketFactory)
164      {
165        this.socketFactory = socketFactory;
166        if (socketFactory == null)
167        {
168          conn = new com.unboundid.ldap.sdk.LDAPConnection();
169        }
170        else
171        {
172    
173          conn = new com.unboundid.ldap.sdk.LDAPConnection(
174               new LDAPToJavaSocketFactory(socketFactory));
175        }
176    
177        authDN = null;
178        authPW = null;
179    
180        constraints       = new LDAPConstraints();
181        searchConstraints = new LDAPSearchConstraints();
182      }
183    
184    
185    
186      /**
187       * Closes the connection to the server if the client forgets to do so.
188       *
189       * @throws  Throwable  If a problem occurs.
190       */
191      @Override()
192      protected void finalize()
193                throws Throwable
194      {
195        conn.close();
196    
197        super.finalize();
198      }
199    
200    
201    
202      /**
203       * Retrieves the {@link com.unboundid.ldap.sdk.LDAPConnection} object used to
204       * back this connection.
205       *
206       * @return  The {@code com.unboundid.ldap.sdk.LDAPConnection} object used to
207       *          back this connection.
208       */
209      public com.unboundid.ldap.sdk.LDAPConnection getSDKConnection()
210      {
211        return conn;
212      }
213    
214    
215    
216      /**
217       * Retrieves the address to which the connection is established.
218       *
219       * @return  The address to which the connection is established.
220       */
221      public String getHost()
222      {
223        return conn.getConnectedAddress();
224      }
225    
226    
227    
228      /**
229       * Retrieves the port to which the connection is established.
230       *
231       * @return  The port to which the connection is established.
232       */
233      public int getPort()
234      {
235        return conn.getConnectedPort();
236      }
237    
238    
239    
240      /**
241       * Retrieves the DN of the user that last authenticated on this connection.
242       *
243       * @return  The DN of the user that last authenticated on this connection,
244       *          or {@code null} if it is not available.
245       */
246      public String getAuthenticationDN()
247      {
248        return authDN;
249      }
250    
251    
252    
253      /**
254       * Retrieves the password of the user that last authenticated on this
255       * connection.
256       *
257       * @return  The password of the user that last authenticated on this
258       *           connection, or {@code null} if it is not available.
259       */
260      public String getAuthenticationPassword()
261      {
262        return authPW;
263      }
264    
265    
266    
267      /**
268       * Retrieves the maximum length of time to wait for the connection to be
269       * established, in seconds.
270       *
271       * @return  The maximum length of time to wait for the connection to be
272       *          established.
273       */
274      public int getConnectTimeout()
275      {
276        final int connectTimeoutMillis =
277             conn.getConnectionOptions().getConnectTimeoutMillis();
278        if (connectTimeoutMillis > 0)
279        {
280          return Math.max(1, (connectTimeoutMillis / 1000));
281        }
282        else
283        {
284          return 0;
285        }
286      }
287    
288    
289    
290      /**
291       * Specifies the maximum length of time to wait for the connection to be
292       * established, in seconds.
293       *
294       * @param  timeout  The maximum length of time to wait for the connection to
295       *                  be established.
296       */
297      public void setConnectTimeout(final int timeout)
298      {
299        final LDAPConnectionOptions options = conn.getConnectionOptions();
300    
301        if (timeout > 0)
302        {
303          options.setConnectTimeoutMillis(1000 * timeout);
304        }
305        else
306        {
307          options.setConnectTimeoutMillis(0);
308        }
309    
310        conn.setConnectionOptions(options);
311      }
312    
313    
314    
315      /**
316       * Retrieves the socket factory for this LDAP connection, if specified.
317       *
318       * @return  The socket factory for this LDAP connection, or {@code null} if
319       *          none has been provided.
320       */
321      public LDAPSocketFactory getSocketFactory()
322      {
323        return socketFactory;
324      }
325    
326    
327    
328      /**
329       * Sets the socket factory for this LDAP connection.
330       *
331       * @param  socketFactory  The socket factory for this LDAP connection.
332       */
333      public void setSocketFactory(final LDAPSocketFactory socketFactory)
334      {
335        this.socketFactory = socketFactory;
336    
337        if (socketFactory == null)
338        {
339          conn.setSocketFactory(null);
340        }
341        else
342        {
343          conn.setSocketFactory(new LDAPToJavaSocketFactory(socketFactory));
344        }
345      }
346    
347    
348    
349      /**
350       * Retrieves the constraints for this connection.
351       *
352       * @return  The constraints for this connection.
353       */
354      public LDAPConstraints getConstraints()
355      {
356        return constraints;
357      }
358    
359    
360    
361      /**
362       * Updates the constraints for this connection.
363       *
364       * @param  constraints  The constraints for this connection.
365       */
366      public void setConstraints(final LDAPConstraints constraints)
367      {
368        if (constraints == null)
369        {
370          this.constraints = new LDAPConstraints();
371        }
372        else
373        {
374          this.constraints = constraints;
375        }
376      }
377    
378    
379    
380      /**
381       * Retrieves the search constraints for this connection.
382       *
383       * @return  The search constraints for this connection.
384       */
385      public LDAPSearchConstraints getSearchConstraints()
386      {
387        return searchConstraints;
388      }
389    
390    
391    
392      /**
393       * Updates the search constraints for this connection.
394       *
395       * @param  searchConstraints  The search constraints for this connection.
396       */
397      public void setSearchConstraints(
398                       final LDAPSearchConstraints searchConstraints)
399      {
400        if (searchConstraints == null)
401        {
402          this.searchConstraints = new LDAPSearchConstraints();
403        }
404        else
405        {
406          this.searchConstraints = searchConstraints;
407        }
408      }
409    
410    
411    
412      /**
413       * Retrieves the response controls from the last operation processed on this
414       * connection.
415       *
416       * @return  The response controls from the last operation processed on this
417       *          connection, or {@code null} if there were none.
418       */
419      public LDAPControl[] getResponseControls()
420      {
421        return responseControls;
422      }
423    
424    
425    
426      /**
427       * Indicates whether this connection is currently established.
428       *
429       * @return  {@code true} if this connection is currently established, or
430       *          {@code false} if not.
431       */
432      public boolean isConnected()
433      {
434        return conn.isConnected();
435      }
436    
437    
438    
439      /**
440       * Attempts to establish this connection with the provided information.
441       *
442       * @param  host  The address of the server to which the connection should be
443       *               established.
444       * @param  port  The port of the server to which the connection should be
445       *               established.
446       *
447       * @throws  LDAPException  If a problem occurs while attempting to establish
448       *                         this connection.
449       */
450      public void connect(final String host, final int port)
451             throws LDAPException
452      {
453        authDN           = null;
454        authPW           = null;
455        responseControls = null;
456    
457        try
458        {
459          conn.close();
460          if (socketFactory == null)
461          {
462            conn = new com.unboundid.ldap.sdk.LDAPConnection(host, port);
463          }
464          else
465          {
466    
467            conn = new com.unboundid.ldap.sdk.LDAPConnection(
468                 new LDAPToJavaSocketFactory(socketFactory), host, port);
469          }
470        }
471        catch (final com.unboundid.ldap.sdk.LDAPException le)
472        {
473          debugException(le);
474          throw new LDAPException(le);
475        }
476      }
477    
478    
479    
480      /**
481       * Attempts to establish and authenticate this connection with the provided
482       * information.
483       *
484       * @param  host      The address of the server to which the connection should
485       *                   be established.
486       * @param  port      The port of the server to which the connection should be
487       *                   established.
488       * @param  dn        The DN to use to bind to the server.
489       * @param  password  The password to use to bind to the server.
490       *
491       * @throws  LDAPException  If a problem occurs while attempting to establish
492       *                         or authenticate this connection.  If an exception
493       *                         is thrown, then the connection will not be
494       *                         established.
495       */
496      public void connect(final String host, final int port, final String dn,
497                          final String password)
498             throws LDAPException
499      {
500        connect(3, host, port, dn, password, null);
501      }
502    
503    
504    
505      /**
506       * Attempts to establish and authenticate this connection with the provided
507       * information.
508       *
509       * @param  host         The address of the server to which the connection
510       *                      should be established.
511       * @param  port         The port of the server to which the connection should
512       *                      be established.
513       * @param  dn           The DN to use to bind to the server.
514       * @param  password     The password to use to bind to the server.
515       * @param  constraints  The constraints to use when processing the bind.
516       *
517       * @throws  LDAPException  If a problem occurs while attempting to establish
518       *                         or authenticate this connection.  If an exception
519       *                         is thrown, then the connection will not be
520       *                         established.
521       */
522      public void connect(final String host, final int port, final String dn,
523                          final String password, final LDAPConstraints constraints)
524             throws LDAPException
525      {
526        connect(3, host, port, dn, password, constraints);
527      }
528    
529    
530    
531      /**
532       * Attempts to establish and authenticate this connection with the provided
533       * information.
534       *
535       * @param  version   The LDAP protocol version to use for the connection.
536       *                   This will be ignored, since this implementation only
537       *                   supports LDAPv3.
538       * @param  host      The address of the server to which the connection should
539       *                   be established.
540       * @param  port      The port of the server to which the connection should be
541       *                   established.
542       * @param  dn        The DN to use to bind to the server.
543       * @param  password  The password to use to bind to the server.
544       *
545       * @throws  LDAPException  If a problem occurs while attempting to establish
546       *                         or authenticate this connection.  If an exception
547       *                         is thrown, then the connection will not be
548       *                         established.
549       */
550      public void connect(final int version, final String host, final int port,
551                          final String dn, final String password)
552             throws LDAPException
553      {
554        connect(version, host, port, dn, password, null);
555      }
556    
557    
558    
559      /**
560       * Attempts to establish and authenticate this connection with the provided
561       * information.
562       *
563       * @param  version      The LDAP protocol version to use for the connection.
564       *                      This will be ignored, since this implementation only
565       *                      supports LDAPv3.
566       * @param  host         The address of the server to which the connection
567       *                      should be established.
568       * @param  port         The port of the server to which the connection should
569       *                      be established.
570       * @param  dn           The DN to use to bind to the server.
571       * @param  password     The password to use to bind to the server.
572       * @param  constraints  The constraints to use when processing the bind.
573       *
574       * @throws  LDAPException  If a problem occurs while attempting to establish
575       *                         or authenticate this connection.  If an exception
576       *                         is thrown, then the connection will not be
577       *                         established.
578       */
579      public void connect(final int version, final String host, final int port,
580                          final String dn, final String password,
581                          final LDAPConstraints constraints)
582             throws LDAPException
583      {
584        connect(host, port);
585    
586        try
587        {
588          if ((dn != null) && (password != null))
589          {
590            bind(version, dn, password, constraints);
591          }
592        }
593        catch (LDAPException le)
594        {
595          conn.close();
596          throw le;
597        }
598      }
599    
600    
601    
602      /**
603       * Unbinds and disconnects from the directory server.
604       *
605       * @throws  LDAPException  If a problem occurs.
606       */
607      public void disconnect()
608             throws LDAPException
609      {
610        authDN = null;
611        authPW = null;
612    
613        conn.close();
614        if (socketFactory == null)
615        {
616          conn = new com.unboundid.ldap.sdk.LDAPConnection();
617        }
618        else
619        {
620    
621          conn = new com.unboundid.ldap.sdk.LDAPConnection(
622               new LDAPToJavaSocketFactory(socketFactory));
623        }
624      }
625    
626    
627    
628      /**
629       * Disconnects from the directory server and attempts to re-connect and
630       * re-authenticate.
631       *
632       * @throws  LDAPException  If a problem occurs.  If an exception is thrown,
633       *                         the connection will have been closed.
634       */
635      public void reconnect()
636             throws LDAPException
637      {
638        final String host = getHost();
639        final int    port = getPort();
640        final String dn   = authDN;
641        final String pw   = authPW;
642    
643        if ((dn == null) || (pw == null))
644        {
645          connect(host, port);
646        }
647        else
648        {
649          connect(host, port, dn, pw);
650        }
651      }
652    
653    
654    
655      /**
656       * Sends a request to abandon the request with the specified message ID.
657       *
658       * @param  id  The message ID of the operation to abandon.
659       *
660       * @throws  LDAPException  If a problem occurs while sending the request.
661       */
662      public void abandon(final int id)
663             throws LDAPException
664      {
665        try
666        {
667          conn.abandon(InternalSDKHelper.createAsyncRequestID(id, conn),
668                       getControls(null));
669        }
670        catch (com.unboundid.ldap.sdk.LDAPException le)
671        {
672          debugException(le);
673          throw new LDAPException(le);
674        }
675      }
676    
677    
678    
679      /**
680       * Adds the provided entry to the directory.
681       *
682       * @param  entry  The entry to be added.
683       *
684       * @throws  LDAPException  If a problem occurs while adding the entry.
685       */
686      public void add(final LDAPEntry entry)
687             throws LDAPException
688      {
689        add(entry, null);
690      }
691    
692    
693    
694      /**
695       * Adds the provided entry to the directory.
696       *
697       * @param  entry        The entry to be added.
698       * @param  constraints  The constraints to use for the add operation.
699       *
700       * @throws  LDAPException  If a problem occurs while adding the entry.
701       */
702      public void add(final LDAPEntry entry, final LDAPConstraints constraints)
703             throws LDAPException
704      {
705        final AddRequest addRequest = new AddRequest(entry.toEntry());
706        update(addRequest, constraints);
707    
708        try
709        {
710          final LDAPResult result = conn.add(addRequest);
711          setResponseControls(result);
712        }
713        catch (com.unboundid.ldap.sdk.LDAPException le)
714        {
715          debugException(le);
716          setResponseControls(le);
717          throw new LDAPException(le);
718        }
719      }
720    
721    
722    
723    
724      /**
725       * Authenticates to the directory server using a simple bind with the provided
726       * information.
727       *
728       * @param  dn        The DN of the user for the bind.
729       * @param  password  The password to use for the bind.
730       *
731       * @throws  LDAPException  If the bind attempt fails.
732       */
733      public void authenticate(final String dn, final String password)
734             throws LDAPException
735      {
736        bind(3, dn, password, null);
737      }
738    
739    
740    
741      /**
742       * Authenticates to the directory server using a simple bind with the provided
743       * information.
744       *
745       * @param  dn           The DN of the user for the bind.
746       * @param  password     The password to use for the bind.
747       * @param  constraints  The constraints to use for the bind operation.
748       *
749       * @throws  LDAPException  If the bind attempt fails.
750       */
751      public void authenticate(final String dn, final String password,
752                               final LDAPConstraints constraints)
753             throws LDAPException
754      {
755        bind(3, dn, password, constraints);
756      }
757    
758    
759    
760      /**
761       * Authenticates to the directory server using a simple bind with the provided
762       * information.
763       *
764       * @param  version   The LDAP protocol version to use.  This will be ignored,
765       *                   since this implementation only supports LDAPv3.
766       * @param  dn        The DN of the user for the bind.
767       * @param  password  The password to use for the bind.
768       *
769       * @throws  LDAPException  If the bind attempt fails.
770       */
771      public void authenticate(final int version, final String dn,
772                               final String password)
773             throws LDAPException
774      {
775        bind(version, dn, password, null);
776      }
777    
778    
779    
780      /**
781       * Authenticates to the directory server using a simple bind with the provided
782       * information.
783       *
784       * @param  version      The LDAP protocol version to use.  This will be
785       *                      ignored, since this implementation only supports
786       *                      LDAPv3.
787       * @param  dn           The DN of the user for the bind.
788       * @param  password     The password to use for the bind.
789       * @param  constraints  The constraints to use for the bind operation.
790       *
791       * @throws  LDAPException  If the bind attempt fails.
792       */
793      public void authenticate(final int version, final String dn,
794                               final String password,
795                               final LDAPConstraints constraints)
796             throws LDAPException
797      {
798        bind(version, dn, password, constraints);
799      }
800    
801    
802    
803      /**
804       * Authenticates to the directory server using a simple bind with the provided
805       * information.
806       *
807       * @param  dn        The DN of the user for the bind.
808       * @param  password  The password to use for the bind.
809       *
810       * @throws  LDAPException  If the bind attempt fails.
811       */
812      public void bind(final String dn, final String password)
813             throws LDAPException
814      {
815        bind(3, dn, password, null);
816      }
817    
818    
819    
820      /**
821       * Authenticates to the directory server using a simple bind with the provided
822       * information.
823       *
824       * @param  dn           The DN of the user for the bind.
825       * @param  password     The password to use for the bind.
826       * @param  constraints  The constraints to use for the bind operation.
827       *
828       * @throws  LDAPException  If the bind attempt fails.
829       */
830      public void bind(final String dn, final String password,
831                       final LDAPConstraints constraints)
832             throws LDAPException
833      {
834        bind(3, dn, password, constraints);
835      }
836    
837    
838    
839      /**
840       * Authenticates to the directory server using a simple bind with the provided
841       * information.
842       *
843       * @param  version   The LDAP protocol version to use.  This will be ignored,
844       *                   since this implementation only supports LDAPv3.
845       * @param  dn        The DN of the user for the bind.
846       * @param  password  The password to use for the bind.
847       *
848       * @throws  LDAPException  If the bind attempt fails.
849       */
850      public void bind(final int version, final String dn, final String password)
851             throws LDAPException
852      {
853        bind(version, dn, password, null);
854      }
855    
856    
857    
858      /**
859       * Authenticates to the directory server using a simple bind with the provided
860       * information.
861       *
862       * @param  version      The LDAP protocol version to use.  This will be
863       *                      ignored, since this implementation only supports
864       *                      LDAPv3.
865       * @param  dn           The DN of the user for the bind.
866       * @param  password     The password to use for the bind.
867       * @param  constraints  The constraints to use for the bind operation.
868       *
869       * @throws  LDAPException  If the bind attempt fails.
870       */
871      public void bind(final int version, final String dn, final String password,
872                       final LDAPConstraints constraints)
873             throws LDAPException
874      {
875        final SimpleBindRequest bindRequest =
876             new SimpleBindRequest(dn, password, getControls(constraints));
877        authDN = null;
878        authPW = null;
879    
880        try
881        {
882          final BindResult bindResult = conn.bind(bindRequest);
883          setResponseControls(bindResult);
884          if (bindResult.getResultCode() == ResultCode.SUCCESS)
885          {
886            authDN = dn;
887            authPW = password;
888          }
889        }
890        catch (com.unboundid.ldap.sdk.LDAPException le)
891        {
892          debugException(le);
893          setResponseControls(le);
894          throw new LDAPException(le);
895        }
896      }
897    
898    
899    
900      /**
901       * Indicates whether the specified entry has the given attribute value.
902       *
903       * @param  dn         The DN of the entry to compare.
904       * @param  attribute  The attribute (which must have exactly one value) to use
905       *                    for the comparison.
906       *
907       * @return  {@code true} if the compare matched the target entry, or
908       *          {@code false} if not.
909       *
910       * @throws  LDAPException  If a problem occurs while processing the compare.
911       */
912      public boolean compare(final String dn, final LDAPAttribute attribute)
913             throws LDAPException
914      {
915        return compare(dn, attribute, null);
916      }
917    
918    
919    
920      /**
921       * Indicates whether the specified entry has the given attribute value.
922       *
923       * @param  dn           The DN of the entry to compare.
924       * @param  attribute    The attribute (which must have exactly one value) to
925       *                      use for the comparison.
926       * @param  constraints  The constraints to use for the compare operation.
927       *
928       * @return  {@code true} if the compare matched the target entry, or
929       *          {@code false} if not.
930       *
931       * @throws  LDAPException  If a problem occurs while processing the compare.
932       */
933      public boolean compare(final String dn, final LDAPAttribute attribute,
934                             final LDAPConstraints constraints)
935             throws LDAPException
936      {
937        final CompareRequest compareRequest = new CompareRequest(dn,
938             attribute.getName(), attribute.getByteValueArray()[0]);
939        update(compareRequest, constraints);
940    
941        try
942        {
943          final CompareResult result = conn.compare(compareRequest);
944          setResponseControls(result);
945          return result.compareMatched();
946        }
947        catch (com.unboundid.ldap.sdk.LDAPException le)
948        {
949          debugException(le);
950          setResponseControls(le);
951          throw new LDAPException(le);
952        }
953      }
954    
955    
956    
957      /**
958       * Removes an entry from the directory.
959       *
960       * @param  dn  The DN of the entry to delete.
961       *
962       * @throws  LDAPException  If a problem occurs while processing the delete.
963       */
964      public void delete(final String dn)
965             throws LDAPException
966      {
967        delete(dn, null);
968      }
969    
970    
971    
972      /**
973       * Removes an entry from the directory.
974       *
975       * @param  dn           The DN of the entry to delete.
976       * @param  constraints  The constraints to use for the delete operation.
977       *
978       * @throws  LDAPException  If a problem occurs while processing the delete.
979       */
980      public void delete(final String dn, final LDAPConstraints constraints)
981             throws LDAPException
982      {
983        final DeleteRequest deleteRequest = new DeleteRequest(dn);
984        update(deleteRequest, constraints);
985    
986        try
987        {
988          final LDAPResult result = conn.delete(deleteRequest);
989          setResponseControls(result);
990        }
991        catch (com.unboundid.ldap.sdk.LDAPException le)
992        {
993          debugException(le);
994          setResponseControls(le);
995          throw new LDAPException(le);
996        }
997      }
998    
999    
1000    
1001      /**
1002       * Processes an extended operation in the directory.
1003       *
1004       * @param  extendedOperation  The extended operation to process.
1005       *
1006       * @return  The result returned from the extended operation.
1007       *
1008       * @throws  LDAPException  If a problem occurs while processing the operation.
1009       */
1010      public LDAPExtendedOperation extendedOperation(
1011                  final LDAPExtendedOperation extendedOperation)
1012             throws LDAPException
1013      {
1014        return extendedOperation(extendedOperation,  null);
1015      }
1016    
1017    
1018    
1019      /**
1020       * Processes an extended operation in the directory.
1021       *
1022       * @param  extendedOperation  The extended operation to process.
1023       * @param  constraints        The constraints to use for the operation.
1024       *
1025       * @return  The result returned from the extended operation.
1026       *
1027       * @throws  LDAPException  If a problem occurs while processing the operation.
1028       */
1029      public LDAPExtendedOperation extendedOperation(
1030                  final LDAPExtendedOperation extendedOperation,
1031                  final LDAPConstraints constraints)
1032             throws LDAPException
1033      {
1034        final ExtendedRequest extendedRequest = new ExtendedRequest(
1035             extendedOperation.getID(),
1036             new ASN1OctetString(extendedOperation.getValue()),
1037             getControls(constraints));
1038    
1039        try
1040        {
1041          final ExtendedResult result =
1042               conn.processExtendedOperation(extendedRequest);
1043          setResponseControls(result);
1044    
1045          if (result.getResultCode() != ResultCode.SUCCESS)
1046          {
1047            throw new LDAPException(result.getDiagnosticMessage(),
1048                 result.getResultCode().intValue(), result.getDiagnosticMessage(),
1049                 result.getMatchedDN());
1050          }
1051    
1052          final byte[] valueBytes;
1053          final ASN1OctetString value = result.getValue();
1054          if (value == null)
1055          {
1056            valueBytes = null;
1057          }
1058          else
1059          {
1060            valueBytes = value.getValue();
1061          }
1062    
1063          return new LDAPExtendedOperation(result.getOID(), valueBytes);
1064        }
1065        catch (com.unboundid.ldap.sdk.LDAPException le)
1066        {
1067          debugException(le);
1068          setResponseControls(le);
1069          throw new LDAPException(le);
1070        }
1071      }
1072    
1073    
1074    
1075      /**
1076       * Modifies an entry in the directory.
1077       *
1078       * @param  dn   The DN of the entry to modify.
1079       * @param  mod  The modification to apply to the entry.
1080       *
1081       * @throws  LDAPException  If a problem occurs while processing the delete.
1082       */
1083      public void modify(final String dn, final LDAPModification mod)
1084             throws LDAPException
1085      {
1086        modify(dn, new LDAPModification[] { mod }, null);
1087      }
1088    
1089    
1090    
1091      /**
1092       * Modifies an entry in the directory.
1093       *
1094       * @param  dn    The DN of the entry to modify.
1095       * @param  mods  The modifications to apply to the entry.
1096       *
1097       * @throws  LDAPException  If a problem occurs while processing the delete.
1098       */
1099      public void modify(final String dn, final LDAPModification[] mods)
1100             throws LDAPException
1101      {
1102        modify(dn, mods, null);
1103      }
1104    
1105    
1106    
1107      /**
1108       * Modifies an entry in the directory.
1109       *
1110       * @param  dn           The DN of the entry to modify.
1111       * @param  mod          The modification to apply to the entry.
1112       * @param  constraints  The constraints to use for the modify operation.
1113       *
1114       * @throws  LDAPException  If a problem occurs while processing the delete.
1115       */
1116      public void modify(final String dn, final LDAPModification mod,
1117                         final LDAPConstraints constraints)
1118             throws LDAPException
1119      {
1120        modify(dn, new LDAPModification[] { mod }, constraints);
1121      }
1122    
1123    
1124    
1125      /**
1126       * Modifies an entry in the directory.
1127       *
1128       * @param  dn           The DN of the entry to modify.
1129       * @param  mods         The modifications to apply to the entry.
1130       * @param  constraints  The constraints to use for the modify operation.
1131       *
1132       * @throws  LDAPException  If a problem occurs while processing the delete.
1133       */
1134      public void modify(final String dn, final LDAPModification[] mods,
1135                         final LDAPConstraints constraints)
1136             throws LDAPException
1137      {
1138        final Modification[] m = new Modification[mods.length];
1139        for (int i=0; i < mods.length; i++)
1140        {
1141          m[i] = mods[i].toModification();
1142        }
1143    
1144        final ModifyRequest modifyRequest = new ModifyRequest(dn, m);
1145        update(modifyRequest, constraints);
1146    
1147        try
1148        {
1149          final LDAPResult result = conn.modify(modifyRequest);
1150          setResponseControls(result);
1151        }
1152        catch (com.unboundid.ldap.sdk.LDAPException le)
1153        {
1154          debugException(le);
1155          setResponseControls(le);
1156          throw new LDAPException(le);
1157        }
1158      }
1159    
1160    
1161    
1162      /**
1163       * Modifies an entry in the directory.
1164       *
1165       * @param  dn    The DN of the entry to modify.
1166       * @param  mods  The modifications to apply to the entry.
1167       *
1168       * @throws  LDAPException  If a problem occurs while processing the delete.
1169       */
1170      public void modify(final String dn, final LDAPModificationSet mods)
1171             throws LDAPException
1172      {
1173        modify(dn, mods.toArray(), null);
1174      }
1175    
1176    
1177    
1178      /**
1179       * Modifies an entry in the directory.
1180       *
1181       * @param  dn           The DN of the entry to modify.
1182       * @param  mods         The modifications to apply to the entry.
1183       * @param  constraints  The constraints to use for the modify operation.
1184       *
1185       * @throws  LDAPException  If a problem occurs while processing the delete.
1186       */
1187      public void modify(final String dn, final LDAPModificationSet mods,
1188                         final LDAPConstraints constraints)
1189             throws LDAPException
1190      {
1191        modify(dn, mods.toArray(), constraints);
1192      }
1193    
1194    
1195    
1196      /**
1197       * Retrieves an entry from the directory server.
1198       *
1199       * @param  dn  The DN of the entry to retrieve.
1200       *
1201       * @return  The entry that was read.
1202       *
1203       * @throws  LDAPException  If a problem occurs while performing the search.
1204       */
1205      public LDAPEntry read(final String dn)
1206             throws LDAPException
1207      {
1208        return read(dn, null, null);
1209      }
1210    
1211    
1212    
1213      /**
1214       * Retrieves an entry from the directory server.
1215       *
1216       * @param  dn           The DN of the entry to retrieve.
1217       * @param  constraints  The constraints to use for the search operation.
1218       *
1219       * @return  The entry that was read.
1220       *
1221       * @throws  LDAPException  If a problem occurs while performing the search.
1222       */
1223      public LDAPEntry read(final String dn,
1224                            final LDAPSearchConstraints constraints)
1225             throws LDAPException
1226      {
1227        return read(dn, null, constraints);
1228      }
1229    
1230    
1231    
1232      /**
1233       * Retrieves an entry from the directory server.
1234       *
1235       * @param  dn     The DN of the entry to retrieve.
1236       * @param  attrs  The set of attributes to request.
1237       *
1238       * @return  The entry that was read.
1239       *
1240       * @throws  LDAPException  If a problem occurs while performing the search.
1241       */
1242      public LDAPEntry read(final String dn, final String[] attrs)
1243             throws LDAPException
1244      {
1245        return read(dn, attrs, null);
1246      }
1247    
1248    
1249    
1250      /**
1251       * Retrieves an entry from the directory server.
1252       *
1253       * @param  dn           The DN of the entry to retrieve.
1254       * @param  attrs        The set of attributes to request.
1255       * @param  constraints  The constraints to use for the search operation.
1256       *
1257       * @return  The entry that was read.
1258       *
1259       * @throws  LDAPException  If a problem occurs while performing the search.
1260       */
1261      public LDAPEntry read(final String dn, final String[] attrs,
1262                            final LDAPSearchConstraints constraints)
1263             throws LDAPException
1264      {
1265        final Filter filter = Filter.createORFilter(
1266             Filter.createPresenceFilter("objectClass"),
1267             Filter.createEqualityFilter("objectClass", "ldapSubentry"));
1268    
1269        final SearchRequest searchRequest =
1270             new SearchRequest(dn, SearchScope.BASE, filter, attrs);
1271        update(searchRequest, constraints);
1272    
1273        try
1274        {
1275          final SearchResult searchResult = conn.search(searchRequest);
1276          setResponseControls(searchResult);
1277    
1278          if (searchResult.getEntryCount() != 1)
1279          {
1280            throw new LDAPException(null, LDAPException.NO_RESULTS_RETURNED);
1281          }
1282    
1283          return new LDAPEntry(searchResult.getSearchEntries().get(0));
1284        }
1285        catch (com.unboundid.ldap.sdk.LDAPException le)
1286        {
1287          debugException(le);
1288          setResponseControls(le);
1289          throw new LDAPException(le);
1290        }
1291      }
1292    
1293    
1294    
1295      /**
1296       * Alters the DN of an entry in the directory.
1297       *
1298       * @param  dn            The DN of the entry to modify.
1299       * @param  newRDN        The new RDN to use for the entry.
1300       * @param  deleteOldRDN  Indicates whether to remove the old RDN value(s).
1301       *
1302       * @throws  LDAPException  If a problem occurs while processing the delete.
1303       */
1304      public void rename(final String dn, final String newRDN,
1305                         final boolean deleteOldRDN)
1306             throws LDAPException
1307      {
1308        rename(dn, newRDN, null, deleteOldRDN, null);
1309      }
1310    
1311    
1312    
1313      /**
1314       * Alters the DN of an entry in the directory.
1315       *
1316       * @param  dn            The DN of the entry to modify.
1317       * @param  newRDN        The new RDN to use for the entry.
1318       * @param  deleteOldRDN  Indicates whether to remove the old RDN value(s).
1319       * @param  constraints   The constraints to use for the modify operation.
1320       *
1321       * @throws  LDAPException  If a problem occurs while processing the delete.
1322       */
1323      public void rename(final String dn, final String newRDN,
1324                         final boolean deleteOldRDN,
1325                         final LDAPConstraints constraints)
1326             throws LDAPException
1327      {
1328        rename(dn, newRDN, null, deleteOldRDN, constraints);
1329      }
1330    
1331    
1332    
1333      /**
1334       * Alters the DN of an entry in the directory.
1335       *
1336       * @param  dn            The DN of the entry to modify.
1337       * @param  newRDN        The new RDN to use for the entry.
1338       * @param  newParentDN   The DN of the new parent, or {@code null} if it
1339       *                       should not be moved below a new parent.
1340       * @param  deleteOldRDN  Indicates whether to remove the old RDN value(s).
1341       *
1342       * @throws  LDAPException  If a problem occurs while processing the delete.
1343       */
1344      public void rename(final String dn, final String newRDN,
1345                         final String newParentDN, final boolean deleteOldRDN)
1346             throws LDAPException
1347      {
1348        rename(dn, newRDN, newParentDN, deleteOldRDN, null);
1349      }
1350    
1351    
1352    
1353      /**
1354       * Alters the DN of an entry in the directory.
1355       *
1356       * @param  dn            The DN of the entry to modify.
1357       * @param  newRDN        The new RDN to use for the entry.
1358       * @param  newParentDN   The DN of the new parent, or {@code null} if it
1359       *                       should not be moved below a new parent.
1360       * @param  deleteOldRDN  Indicates whether to remove the old RDN value(s).
1361       * @param  constraints   The constraints to use for the modify operation.
1362       *
1363       * @throws  LDAPException  If a problem occurs while processing the delete.
1364       */
1365      public void rename(final String dn, final String newRDN,
1366                         final String newParentDN, final boolean deleteOldRDN,
1367                         final LDAPConstraints constraints)
1368             throws LDAPException
1369      {
1370        final ModifyDNRequest modifyDNRequest =
1371             new ModifyDNRequest(dn, newRDN, deleteOldRDN, newParentDN);
1372        update(modifyDNRequest, constraints);
1373    
1374        try
1375        {
1376          final LDAPResult result = conn.modifyDN(modifyDNRequest);
1377          setResponseControls(result);
1378        }
1379        catch (com.unboundid.ldap.sdk.LDAPException le)
1380        {
1381          debugException(le);
1382          setResponseControls(le);
1383          throw new LDAPException(le);
1384        }
1385      }
1386    
1387    
1388    
1389      /**
1390       * Processes a search in the directory server.
1391       *
1392       * @param  baseDN       The base DN for the search.
1393       * @param  scope        The scope for the search.
1394       * @param  filter       The filter for the search.
1395       * @param  attributes   The set of attributes to request.
1396       * @param  typesOnly    Indicates whether to return attribute types only or
1397       *                      both types and values.
1398       *
1399       * @return  The entry that was read.
1400       *
1401       * @throws  LDAPException  If a problem occurs while performing the search.
1402       */
1403      public LDAPSearchResults search(final String baseDN, final int scope,
1404                  final String filter, final String[] attributes,
1405                  final boolean typesOnly)
1406             throws LDAPException
1407      {
1408        return search(baseDN, scope, filter, attributes, typesOnly, null);
1409      }
1410    
1411    
1412    
1413      /**
1414       * Processes a search in the directory server.
1415       *
1416       * @param  baseDN       The base DN for the search.
1417       * @param  scope        The scope for the search.
1418       * @param  filter       The filter for the search.
1419       * @param  attributes   The set of attributes to request.
1420       * @param  typesOnly    Indicates whether to return attribute types only or
1421       *                      both types and values.
1422       * @param  constraints  The constraints to use for the search operation.
1423       *
1424       * @return  The entry that was read.
1425       *
1426       * @throws  LDAPException  If a problem occurs while performing the search.
1427       */
1428      public LDAPSearchResults search(final String baseDN, final int scope,
1429                  final String filter, final String[] attributes,
1430                  final boolean typesOnly, final LDAPSearchConstraints constraints)
1431             throws LDAPException
1432      {
1433        final LDAPSearchResults results;
1434        final LDAPSearchConstraints c =
1435             (constraints == null) ? searchConstraints : constraints;
1436        results = new LDAPSearchResults(c.getTimeLimit());
1437    
1438        try
1439        {
1440          final SearchRequest searchRequest = new SearchRequest(results, baseDN,
1441               SearchScope.valueOf(scope), filter, attributes);
1442    
1443          searchRequest.setDerefPolicy(
1444               DereferencePolicy.valueOf(c.getDereference()));
1445          searchRequest.setSizeLimit(c.getMaxResults());
1446          searchRequest.setTimeLimitSeconds(c.getServerTimeLimit());
1447          searchRequest.setTypesOnly(typesOnly);
1448    
1449          update(searchRequest, constraints);
1450    
1451          conn.asyncSearch(searchRequest);
1452          return results;
1453        }
1454        catch (com.unboundid.ldap.sdk.LDAPException le)
1455        {
1456          debugException(le);
1457          setResponseControls(le);
1458          throw new LDAPException(le);
1459        }
1460      }
1461    
1462    
1463    
1464      /**
1465       * Retrieves the set of controls to use in a request.
1466       *
1467       * @param  c  The constraints to be applied.
1468       *
1469       * @return  The set of controls to use in a request.
1470       */
1471      private Control[] getControls(final LDAPConstraints c)
1472      {
1473        Control[] controls = null;
1474        if (c != null)
1475        {
1476          controls = LDAPControl.toControls(c.getServerControls());
1477        }
1478        else if (constraints != null)
1479        {
1480          controls = LDAPControl.toControls(constraints.getServerControls());
1481        }
1482    
1483        if (controls == null)
1484        {
1485          return new Control[0];
1486        }
1487        else
1488        {
1489          return controls;
1490        }
1491      }
1492    
1493    
1494    
1495      /**
1496       * Updates the provided request to account for the given set of constraints.
1497       *
1498       * @param  request      The request to be updated.
1499       * @param  constraints  The constraints to be applied.
1500       */
1501      private void update(final UpdatableLDAPRequest request,
1502                          final LDAPConstraints constraints)
1503      {
1504        final LDAPConstraints c =
1505             (constraints == null) ? this.constraints : constraints;
1506    
1507        request.setControls(LDAPControl.toControls(c.getServerControls()));
1508        request.setResponseTimeoutMillis(c.getTimeLimit());
1509        request.setFollowReferrals(c.getReferrals());
1510      }
1511    
1512    
1513    
1514      /**
1515       * Sets the response controls for this connection.
1516       *
1517       * @param  ldapResult  The result containing the controls to use.
1518       */
1519      private void setResponseControls(final LDAPResult ldapResult)
1520      {
1521        if (ldapResult.hasResponseControl())
1522        {
1523          responseControls =
1524               LDAPControl.toLDAPControls(ldapResult.getResponseControls());
1525        }
1526        else
1527        {
1528          responseControls = null;
1529        }
1530      }
1531    
1532    
1533    
1534      /**
1535       * Sets the response controls for this connection.
1536       *
1537       * @param  ldapException  The exception containing the controls to use.
1538       */
1539      private void setResponseControls(
1540                        final com.unboundid.ldap.sdk.LDAPException ldapException)
1541      {
1542        if (ldapException.hasResponseControl())
1543        {
1544          responseControls =
1545               LDAPControl.toLDAPControls(ldapException.getResponseControls());
1546        }
1547        else
1548        {
1549          responseControls = null;
1550        }
1551      }
1552    }