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