001    /*
002     * Copyright 2009-2015 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2009-2015 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 final 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.connect(host, port);
460        }
461        catch (com.unboundid.ldap.sdk.LDAPException le)
462        {
463          debugException(le);
464          throw new LDAPException(le);
465        }
466      }
467    
468    
469    
470      /**
471       * Attempts to establish and authenticate this connection with the provided
472       * information.
473       *
474       * @param  host      The address of the server to which the connection should
475       *                   be established.
476       * @param  port      The port of the server to which the connection should be
477       *                   established.
478       * @param  dn        The DN to use to bind to the server.
479       * @param  password  The password to use to bind to the server.
480       *
481       * @throws  LDAPException  If a problem occurs while attempting to establish
482       *                         or authenticate this connection.  If an exception
483       *                         is thrown, then the connection will not be
484       *                         established.
485       */
486      public void connect(final String host, final int port, final String dn,
487                          final String password)
488             throws LDAPException
489      {
490        connect(3, host, port, dn, password, null);
491      }
492    
493    
494    
495      /**
496       * Attempts to establish and authenticate this connection with the provided
497       * information.
498       *
499       * @param  host         The address of the server to which the connection
500       *                      should be established.
501       * @param  port         The port of the server to which the connection should
502       *                      be established.
503       * @param  dn           The DN to use to bind to the server.
504       * @param  password     The password to use to bind to the server.
505       * @param  constraints  The constraints to use when processing the bind.
506       *
507       * @throws  LDAPException  If a problem occurs while attempting to establish
508       *                         or authenticate this connection.  If an exception
509       *                         is thrown, then the connection will not be
510       *                         established.
511       */
512      public void connect(final String host, final int port, final String dn,
513                          final String password, final LDAPConstraints constraints)
514             throws LDAPException
515      {
516        connect(3, host, port, dn, password, constraints);
517      }
518    
519    
520    
521      /**
522       * Attempts to establish and authenticate this connection with the provided
523       * information.
524       *
525       * @param  version   The LDAP protocol version to use for the connection.
526       *                   This will be ignored, since this implementation only
527       *                   supports LDAPv3.
528       * @param  host      The address of the server to which the connection should
529       *                   be established.
530       * @param  port      The port of the server to which the connection should be
531       *                   established.
532       * @param  dn        The DN to use to bind to the server.
533       * @param  password  The password to use to bind to the server.
534       *
535       * @throws  LDAPException  If a problem occurs while attempting to establish
536       *                         or authenticate this connection.  If an exception
537       *                         is thrown, then the connection will not be
538       *                         established.
539       */
540      public void connect(final int version, final String host, final int port,
541                          final String dn, final String password)
542             throws LDAPException
543      {
544        connect(version, host, port, dn, password, null);
545      }
546    
547    
548    
549      /**
550       * Attempts to establish and authenticate this connection with the provided
551       * information.
552       *
553       * @param  version      The LDAP protocol version to use for the connection.
554       *                      This will be ignored, since this implementation only
555       *                      supports LDAPv3.
556       * @param  host         The address of the server to which the connection
557       *                      should be established.
558       * @param  port         The port of the server to which the connection should
559       *                      be established.
560       * @param  dn           The DN to use to bind to the server.
561       * @param  password     The password to use to bind to the server.
562       * @param  constraints  The constraints to use when processing the bind.
563       *
564       * @throws  LDAPException  If a problem occurs while attempting to establish
565       *                         or authenticate this connection.  If an exception
566       *                         is thrown, then the connection will not be
567       *                         established.
568       */
569      public void connect(final int version, final String host, final int port,
570                          final String dn, final String password,
571                          final LDAPConstraints constraints)
572             throws LDAPException
573      {
574        connect(host, port);
575    
576        try
577        {
578          if ((dn != null) && (password != null))
579          {
580            bind(version, dn, password, constraints);
581          }
582        }
583        catch (LDAPException le)
584        {
585          conn.close();
586          throw le;
587        }
588      }
589    
590    
591    
592      /**
593       * Unbinds and disconnects from the directory server.
594       *
595       * @throws  LDAPException  If a problem occurs.
596       */
597      public void disconnect()
598             throws LDAPException
599      {
600        conn.close();
601        authDN = null;
602        authPW = null;
603      }
604    
605    
606    
607      /**
608       * Disconnects from the directory server and attempts to re-connect and
609       * re-authenticate.
610       *
611       * @throws  LDAPException  If a problem occurs.  If an exception is thrown,
612       *                         the connection will have been closed.
613       */
614      public void reconnect()
615             throws LDAPException
616      {
617        final String host = getHost();
618        final int    port = getPort();
619        final String dn   = authDN;
620        final String pw   = authPW;
621    
622        conn.close();
623    
624        if ((dn == null) || (pw == null))
625        {
626          connect(host, port);
627        }
628        else
629        {
630          connect(host, port, dn, pw);
631        }
632      }
633    
634    
635    
636      /**
637       * Sends a request to abandon the request with the specified message ID.
638       *
639       * @param  id  The message ID of the operation to abandon.
640       *
641       * @throws  LDAPException  If a problem occurs while sending the request.
642       */
643      public void abandon(final int id)
644             throws LDAPException
645      {
646        try
647        {
648          conn.abandon(InternalSDKHelper.createAsyncRequestID(id, conn),
649                       getControls(null));
650        }
651        catch (com.unboundid.ldap.sdk.LDAPException le)
652        {
653          debugException(le);
654          throw new LDAPException(le);
655        }
656      }
657    
658    
659    
660      /**
661       * Adds the provided entry to the directory.
662       *
663       * @param  entry  The entry to be added.
664       *
665       * @throws  LDAPException  If a problem occurs while adding the entry.
666       */
667      public void add(final LDAPEntry entry)
668             throws LDAPException
669      {
670        add(entry, null);
671      }
672    
673    
674    
675      /**
676       * Adds the provided entry to the directory.
677       *
678       * @param  entry        The entry to be added.
679       * @param  constraints  The constraints to use for the add operation.
680       *
681       * @throws  LDAPException  If a problem occurs while adding the entry.
682       */
683      public void add(final LDAPEntry entry, final LDAPConstraints constraints)
684             throws LDAPException
685      {
686        final AddRequest addRequest = new AddRequest(entry.toEntry());
687        update(addRequest, constraints);
688    
689        try
690        {
691          final LDAPResult result = conn.add(addRequest);
692          setResponseControls(result);
693        }
694        catch (com.unboundid.ldap.sdk.LDAPException le)
695        {
696          debugException(le);
697          setResponseControls(le);
698          throw new LDAPException(le);
699        }
700      }
701    
702    
703    
704    
705      /**
706       * Authenticates to the directory server using a simple bind with the provided
707       * information.
708       *
709       * @param  dn        The DN of the user for the bind.
710       * @param  password  The password to use for the bind.
711       *
712       * @throws  LDAPException  If the bind attempt fails.
713       */
714      public void authenticate(final String dn, final String password)
715             throws LDAPException
716      {
717        bind(3, dn, password, null);
718      }
719    
720    
721    
722      /**
723       * Authenticates to the directory server using a simple bind with the provided
724       * information.
725       *
726       * @param  dn           The DN of the user for the bind.
727       * @param  password     The password to use for the bind.
728       * @param  constraints  The constraints to use for the bind operation.
729       *
730       * @throws  LDAPException  If the bind attempt fails.
731       */
732      public void authenticate(final String dn, final String password,
733                               final LDAPConstraints constraints)
734             throws LDAPException
735      {
736        bind(3, dn, password, constraints);
737      }
738    
739    
740    
741      /**
742       * Authenticates to the directory server using a simple bind with the provided
743       * information.
744       *
745       * @param  version   The LDAP protocol version to use.  This will be ignored,
746       *                   since this implementation only supports LDAPv3.
747       * @param  dn        The DN of the user for the bind.
748       * @param  password  The password to use for the bind.
749       *
750       * @throws  LDAPException  If the bind attempt fails.
751       */
752      public void authenticate(final int version, final String dn,
753                               final String password)
754             throws LDAPException
755      {
756        bind(version, dn, password, null);
757      }
758    
759    
760    
761      /**
762       * Authenticates to the directory server using a simple bind with the provided
763       * information.
764       *
765       * @param  version      The LDAP protocol version to use.  This will be
766       *                      ignored, since this implementation only supports
767       *                      LDAPv3.
768       * @param  dn           The DN of the user for the bind.
769       * @param  password     The password to use for the bind.
770       * @param  constraints  The constraints to use for the bind operation.
771       *
772       * @throws  LDAPException  If the bind attempt fails.
773       */
774      public void authenticate(final int version, final String dn,
775                               final String password,
776                               final LDAPConstraints constraints)
777             throws LDAPException
778      {
779        bind(version, dn, password, constraints);
780      }
781    
782    
783    
784      /**
785       * Authenticates to the directory server using a simple bind with the provided
786       * information.
787       *
788       * @param  dn        The DN of the user for the bind.
789       * @param  password  The password to use for the bind.
790       *
791       * @throws  LDAPException  If the bind attempt fails.
792       */
793      public void bind(final String dn, final String password)
794             throws LDAPException
795      {
796        bind(3, dn, password, null);
797      }
798    
799    
800    
801      /**
802       * Authenticates to the directory server using a simple bind with the provided
803       * information.
804       *
805       * @param  dn           The DN of the user for the bind.
806       * @param  password     The password to use for the bind.
807       * @param  constraints  The constraints to use for the bind operation.
808       *
809       * @throws  LDAPException  If the bind attempt fails.
810       */
811      public void bind(final String dn, final String password,
812                       final LDAPConstraints constraints)
813             throws LDAPException
814      {
815        bind(3, dn, password, constraints);
816      }
817    
818    
819    
820      /**
821       * Authenticates to the directory server using a simple bind with the provided
822       * information.
823       *
824       * @param  version   The LDAP protocol version to use.  This will be ignored,
825       *                   since this implementation only supports LDAPv3.
826       * @param  dn        The DN of the user for the bind.
827       * @param  password  The password to use for the bind.
828       *
829       * @throws  LDAPException  If the bind attempt fails.
830       */
831      public void bind(final int version, final String dn, final String password)
832             throws LDAPException
833      {
834        bind(version, dn, password, null);
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
844       *                      ignored, since this implementation only supports
845       *                      LDAPv3.
846       * @param  dn           The DN of the user for the bind.
847       * @param  password     The password to use for the bind.
848       * @param  constraints  The constraints to use for the bind operation.
849       *
850       * @throws  LDAPException  If the bind attempt fails.
851       */
852      public void bind(final int version, final String dn, final String password,
853                       final LDAPConstraints constraints)
854             throws LDAPException
855      {
856        final SimpleBindRequest bindRequest =
857             new SimpleBindRequest(dn, password, getControls(constraints));
858        authDN = null;
859        authPW = null;
860    
861        try
862        {
863          final BindResult bindResult = conn.bind(bindRequest);
864          setResponseControls(bindResult);
865          if (bindResult.getResultCode() == ResultCode.SUCCESS)
866          {
867            authDN = dn;
868            authPW = password;
869          }
870        }
871        catch (com.unboundid.ldap.sdk.LDAPException le)
872        {
873          debugException(le);
874          setResponseControls(le);
875          throw new LDAPException(le);
876        }
877      }
878    
879    
880    
881      /**
882       * Indicates whether the specified entry has the given attribute value.
883       *
884       * @param  dn         The DN of the entry to compare.
885       * @param  attribute  The attribute (which must have exactly one value) to use
886       *                    for the comparison.
887       *
888       * @return  {@code true} if the compare matched the target entry, or
889       *          {@code false} if not.
890       *
891       * @throws  LDAPException  If a problem occurs while processing the compare.
892       */
893      public boolean compare(final String dn, final LDAPAttribute attribute)
894             throws LDAPException
895      {
896        return compare(dn, attribute, null);
897      }
898    
899    
900    
901      /**
902       * Indicates whether the specified entry has the given attribute value.
903       *
904       * @param  dn           The DN of the entry to compare.
905       * @param  attribute    The attribute (which must have exactly one value) to
906       *                      use for the comparison.
907       * @param  constraints  The constraints to use for the compare operation.
908       *
909       * @return  {@code true} if the compare matched the target entry, or
910       *          {@code false} if not.
911       *
912       * @throws  LDAPException  If a problem occurs while processing the compare.
913       */
914      public boolean compare(final String dn, final LDAPAttribute attribute,
915                             final LDAPConstraints constraints)
916             throws LDAPException
917      {
918        final CompareRequest compareRequest = new CompareRequest(dn,
919             attribute.getName(), attribute.getByteValueArray()[0]);
920        update(compareRequest, constraints);
921    
922        try
923        {
924          final CompareResult result = conn.compare(compareRequest);
925          setResponseControls(result);
926          return result.compareMatched();
927        }
928        catch (com.unboundid.ldap.sdk.LDAPException le)
929        {
930          debugException(le);
931          setResponseControls(le);
932          throw new LDAPException(le);
933        }
934      }
935    
936    
937    
938      /**
939       * Removes an entry from the directory.
940       *
941       * @param  dn  The DN of the entry to delete.
942       *
943       * @throws  LDAPException  If a problem occurs while processing the delete.
944       */
945      public void delete(final String dn)
946             throws LDAPException
947      {
948        delete(dn, null);
949      }
950    
951    
952    
953      /**
954       * Removes an entry from the directory.
955       *
956       * @param  dn           The DN of the entry to delete.
957       * @param  constraints  The constraints to use for the delete operation.
958       *
959       * @throws  LDAPException  If a problem occurs while processing the delete.
960       */
961      public void delete(final String dn, final LDAPConstraints constraints)
962             throws LDAPException
963      {
964        final DeleteRequest deleteRequest = new DeleteRequest(dn);
965        update(deleteRequest, constraints);
966    
967        try
968        {
969          final LDAPResult result = conn.delete(deleteRequest);
970          setResponseControls(result);
971        }
972        catch (com.unboundid.ldap.sdk.LDAPException le)
973        {
974          debugException(le);
975          setResponseControls(le);
976          throw new LDAPException(le);
977        }
978      }
979    
980    
981    
982      /**
983       * Processes an extended operation in the directory.
984       *
985       * @param  extendedOperation  The extended operation to process.
986       *
987       * @return  The result returned from the extended operation.
988       *
989       * @throws  LDAPException  If a problem occurs while processing the operation.
990       */
991      public LDAPExtendedOperation extendedOperation(
992                  final LDAPExtendedOperation extendedOperation)
993             throws LDAPException
994      {
995        return extendedOperation(extendedOperation,  null);
996      }
997    
998    
999    
1000      /**
1001       * Processes an extended operation in the directory.
1002       *
1003       * @param  extendedOperation  The extended operation to process.
1004       * @param  constraints        The constraints to use for the operation.
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                  final LDAPConstraints constraints)
1013             throws LDAPException
1014      {
1015        final ExtendedRequest extendedRequest = new ExtendedRequest(
1016             extendedOperation.getID(),
1017             new ASN1OctetString(extendedOperation.getValue()),
1018             getControls(constraints));
1019    
1020        try
1021        {
1022          final ExtendedResult result =
1023               conn.processExtendedOperation(extendedRequest);
1024          setResponseControls(result);
1025    
1026          if (result.getResultCode() != ResultCode.SUCCESS)
1027          {
1028            throw new LDAPException(result.getDiagnosticMessage(),
1029                 result.getResultCode().intValue(), result.getDiagnosticMessage(),
1030                 result.getMatchedDN());
1031          }
1032    
1033          final byte[] valueBytes;
1034          final ASN1OctetString value = result.getValue();
1035          if (value == null)
1036          {
1037            valueBytes = null;
1038          }
1039          else
1040          {
1041            valueBytes = value.getValue();
1042          }
1043    
1044          return new LDAPExtendedOperation(result.getOID(), valueBytes);
1045        }
1046        catch (com.unboundid.ldap.sdk.LDAPException le)
1047        {
1048          debugException(le);
1049          setResponseControls(le);
1050          throw new LDAPException(le);
1051        }
1052      }
1053    
1054    
1055    
1056      /**
1057       * Modifies an entry in the directory.
1058       *
1059       * @param  dn   The DN of the entry to modify.
1060       * @param  mod  The modification to apply to the entry.
1061       *
1062       * @throws  LDAPException  If a problem occurs while processing the delete.
1063       */
1064      public void modify(final String dn, final LDAPModification mod)
1065             throws LDAPException
1066      {
1067        modify(dn, new LDAPModification[] { mod }, null);
1068      }
1069    
1070    
1071    
1072      /**
1073       * Modifies an entry in the directory.
1074       *
1075       * @param  dn    The DN of the entry to modify.
1076       * @param  mods  The modifications to apply to the entry.
1077       *
1078       * @throws  LDAPException  If a problem occurs while processing the delete.
1079       */
1080      public void modify(final String dn, final LDAPModification[] mods)
1081             throws LDAPException
1082      {
1083        modify(dn, mods, null);
1084      }
1085    
1086    
1087    
1088      /**
1089       * Modifies an entry in the directory.
1090       *
1091       * @param  dn           The DN of the entry to modify.
1092       * @param  mod          The modification to apply to the entry.
1093       * @param  constraints  The constraints to use for the modify operation.
1094       *
1095       * @throws  LDAPException  If a problem occurs while processing the delete.
1096       */
1097      public void modify(final String dn, final LDAPModification mod,
1098                         final LDAPConstraints constraints)
1099             throws LDAPException
1100      {
1101        modify(dn, new LDAPModification[] { mod }, constraints);
1102      }
1103    
1104    
1105    
1106      /**
1107       * Modifies an entry in the directory.
1108       *
1109       * @param  dn           The DN of the entry to modify.
1110       * @param  mods         The modifications to apply to the entry.
1111       * @param  constraints  The constraints to use for the modify operation.
1112       *
1113       * @throws  LDAPException  If a problem occurs while processing the delete.
1114       */
1115      public void modify(final String dn, final LDAPModification[] mods,
1116                         final LDAPConstraints constraints)
1117             throws LDAPException
1118      {
1119        final Modification[] m = new Modification[mods.length];
1120        for (int i=0; i < mods.length; i++)
1121        {
1122          m[i] = mods[i].toModification();
1123        }
1124    
1125        final ModifyRequest modifyRequest = new ModifyRequest(dn, m);
1126        update(modifyRequest, constraints);
1127    
1128        try
1129        {
1130          final LDAPResult result = conn.modify(modifyRequest);
1131          setResponseControls(result);
1132        }
1133        catch (com.unboundid.ldap.sdk.LDAPException le)
1134        {
1135          debugException(le);
1136          setResponseControls(le);
1137          throw new LDAPException(le);
1138        }
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  mods  The modifications to apply to the entry.
1148       *
1149       * @throws  LDAPException  If a problem occurs while processing the delete.
1150       */
1151      public void modify(final String dn, final LDAPModificationSet mods)
1152             throws LDAPException
1153      {
1154        modify(dn, mods.toArray(), null);
1155      }
1156    
1157    
1158    
1159      /**
1160       * Modifies an entry in the directory.
1161       *
1162       * @param  dn           The DN of the entry to modify.
1163       * @param  mods         The modifications to apply to the entry.
1164       * @param  constraints  The constraints to use for the modify operation.
1165       *
1166       * @throws  LDAPException  If a problem occurs while processing the delete.
1167       */
1168      public void modify(final String dn, final LDAPModificationSet mods,
1169                         final LDAPConstraints constraints)
1170             throws LDAPException
1171      {
1172        modify(dn, mods.toArray(), constraints);
1173      }
1174    
1175    
1176    
1177      /**
1178       * Retrieves an entry from the directory server.
1179       *
1180       * @param  dn  The DN of the entry to retrieve.
1181       *
1182       * @return  The entry that was read.
1183       *
1184       * @throws  LDAPException  If a problem occurs while performing the search.
1185       */
1186      public LDAPEntry read(final String dn)
1187             throws LDAPException
1188      {
1189        return read(dn, null, null);
1190      }
1191    
1192    
1193    
1194      /**
1195       * Retrieves an entry from the directory server.
1196       *
1197       * @param  dn           The DN of the entry to retrieve.
1198       * @param  constraints  The constraints to use for the search operation.
1199       *
1200       * @return  The entry that was read.
1201       *
1202       * @throws  LDAPException  If a problem occurs while performing the search.
1203       */
1204      public LDAPEntry read(final String dn,
1205                            final LDAPSearchConstraints constraints)
1206             throws LDAPException
1207      {
1208        return read(dn, null, constraints);
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  attrs  The set of attributes to request.
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, final String[] attrs)
1224             throws LDAPException
1225      {
1226        return read(dn, attrs, null);
1227      }
1228    
1229    
1230    
1231      /**
1232       * Retrieves an entry from the directory server.
1233       *
1234       * @param  dn           The DN of the entry to retrieve.
1235       * @param  attrs        The set of attributes to request.
1236       * @param  constraints  The constraints to use for the search operation.
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                            final LDAPSearchConstraints constraints)
1244             throws LDAPException
1245      {
1246        final Filter filter = Filter.createORFilter(
1247             Filter.createPresenceFilter("objectClass"),
1248             Filter.createEqualityFilter("objectClass", "ldapSubentry"));
1249    
1250        final SearchRequest searchRequest =
1251             new SearchRequest(dn, SearchScope.BASE, filter, attrs);
1252        update(searchRequest, constraints);
1253    
1254        try
1255        {
1256          final SearchResult searchResult = conn.search(searchRequest);
1257          setResponseControls(searchResult);
1258    
1259          if (searchResult.getEntryCount() != 1)
1260          {
1261            throw new LDAPException(null, LDAPException.NO_RESULTS_RETURNED);
1262          }
1263    
1264          return new LDAPEntry(searchResult.getSearchEntries().get(0));
1265        }
1266        catch (com.unboundid.ldap.sdk.LDAPException le)
1267        {
1268          debugException(le);
1269          setResponseControls(le);
1270          throw new LDAPException(le);
1271        }
1272      }
1273    
1274    
1275    
1276      /**
1277       * Alters the DN of an entry in the directory.
1278       *
1279       * @param  dn            The DN of the entry to modify.
1280       * @param  newRDN        The new RDN to use for the entry.
1281       * @param  deleteOldRDN  Indicates whether to remove the old RDN value(s).
1282       *
1283       * @throws  LDAPException  If a problem occurs while processing the delete.
1284       */
1285      public void rename(final String dn, final String newRDN,
1286                         final boolean deleteOldRDN)
1287             throws LDAPException
1288      {
1289        rename(dn, newRDN, null, deleteOldRDN, null);
1290      }
1291    
1292    
1293    
1294      /**
1295       * Alters the DN of an entry in the directory.
1296       *
1297       * @param  dn            The DN of the entry to modify.
1298       * @param  newRDN        The new RDN to use for the entry.
1299       * @param  deleteOldRDN  Indicates whether to remove the old RDN value(s).
1300       * @param  constraints   The constraints to use for the modify operation.
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                         final LDAPConstraints constraints)
1307             throws LDAPException
1308      {
1309        rename(dn, newRDN, null, deleteOldRDN, constraints);
1310      }
1311    
1312    
1313    
1314      /**
1315       * Alters the DN of an entry in the directory.
1316       *
1317       * @param  dn            The DN of the entry to modify.
1318       * @param  newRDN        The new RDN to use for the entry.
1319       * @param  newParentDN   The DN of the new parent, or {@code null} if it
1320       *                       should not be moved below a new parent.
1321       * @param  deleteOldRDN  Indicates whether to remove the old RDN value(s).
1322       *
1323       * @throws  LDAPException  If a problem occurs while processing the delete.
1324       */
1325      public void rename(final String dn, final String newRDN,
1326                         final String newParentDN, final boolean deleteOldRDN)
1327             throws LDAPException
1328      {
1329        rename(dn, newRDN, newParentDN, deleteOldRDN, null);
1330      }
1331    
1332    
1333    
1334      /**
1335       * Alters the DN of an entry in the directory.
1336       *
1337       * @param  dn            The DN of the entry to modify.
1338       * @param  newRDN        The new RDN to use for the entry.
1339       * @param  newParentDN   The DN of the new parent, or {@code null} if it
1340       *                       should not be moved below a new parent.
1341       * @param  deleteOldRDN  Indicates whether to remove the old RDN value(s).
1342       * @param  constraints   The constraints to use for the modify operation.
1343       *
1344       * @throws  LDAPException  If a problem occurs while processing the delete.
1345       */
1346      public void rename(final String dn, final String newRDN,
1347                         final String newParentDN, final boolean deleteOldRDN,
1348                         final LDAPConstraints constraints)
1349             throws LDAPException
1350      {
1351        final ModifyDNRequest modifyDNRequest =
1352             new ModifyDNRequest(dn, newRDN, deleteOldRDN, newParentDN);
1353        update(modifyDNRequest, constraints);
1354    
1355        try
1356        {
1357          final LDAPResult result = conn.modifyDN(modifyDNRequest);
1358          setResponseControls(result);
1359        }
1360        catch (com.unboundid.ldap.sdk.LDAPException le)
1361        {
1362          debugException(le);
1363          setResponseControls(le);
1364          throw new LDAPException(le);
1365        }
1366      }
1367    
1368    
1369    
1370      /**
1371       * Processes a search in the directory server.
1372       *
1373       * @param  baseDN       The base DN for the search.
1374       * @param  scope        The scope for the search.
1375       * @param  filter       The filter for the search.
1376       * @param  attributes   The set of attributes to request.
1377       * @param  typesOnly    Indicates whether to return attribute types only or
1378       *                      both types and values.
1379       *
1380       * @return  The entry that was read.
1381       *
1382       * @throws  LDAPException  If a problem occurs while performing the search.
1383       */
1384      public LDAPSearchResults search(final String baseDN, final int scope,
1385                  final String filter, final String[] attributes,
1386                  final boolean typesOnly)
1387             throws LDAPException
1388      {
1389        return search(baseDN, scope, filter, attributes, typesOnly, null);
1390      }
1391    
1392    
1393    
1394      /**
1395       * Processes a search in the directory server.
1396       *
1397       * @param  baseDN       The base DN for the search.
1398       * @param  scope        The scope for the search.
1399       * @param  filter       The filter for the search.
1400       * @param  attributes   The set of attributes to request.
1401       * @param  typesOnly    Indicates whether to return attribute types only or
1402       *                      both types and values.
1403       * @param  constraints  The constraints to use for the search operation.
1404       *
1405       * @return  The entry that was read.
1406       *
1407       * @throws  LDAPException  If a problem occurs while performing the search.
1408       */
1409      public LDAPSearchResults search(final String baseDN, final int scope,
1410                  final String filter, final String[] attributes,
1411                  final boolean typesOnly, final LDAPSearchConstraints constraints)
1412             throws LDAPException
1413      {
1414        final LDAPSearchResults results;
1415        final LDAPSearchConstraints c =
1416             (constraints == null) ? searchConstraints : constraints;
1417        results = new LDAPSearchResults(c.getTimeLimit());
1418    
1419        try
1420        {
1421          final SearchRequest searchRequest = new SearchRequest(results, baseDN,
1422               SearchScope.valueOf(scope), filter, attributes);
1423    
1424          searchRequest.setDerefPolicy(
1425               DereferencePolicy.valueOf(c.getDereference()));
1426          searchRequest.setSizeLimit(c.getMaxResults());
1427          searchRequest.setTimeLimitSeconds(c.getServerTimeLimit());
1428          searchRequest.setTypesOnly(typesOnly);
1429    
1430          update(searchRequest, constraints);
1431    
1432          conn.asyncSearch(searchRequest);
1433          return results;
1434        }
1435        catch (com.unboundid.ldap.sdk.LDAPException le)
1436        {
1437          debugException(le);
1438          setResponseControls(le);
1439          throw new LDAPException(le);
1440        }
1441      }
1442    
1443    
1444    
1445      /**
1446       * Retrieves the set of controls to use in a request.
1447       *
1448       * @param  c  The constraints to be applied.
1449       *
1450       * @return  The set of controls to use in a request.
1451       */
1452      private Control[] getControls(final LDAPConstraints c)
1453      {
1454        Control[] controls = null;
1455        if (c != null)
1456        {
1457          controls = LDAPControl.toControls(c.getServerControls());
1458        }
1459        else if (constraints != null)
1460        {
1461          controls = LDAPControl.toControls(constraints.getServerControls());
1462        }
1463    
1464        if (controls == null)
1465        {
1466          return new Control[0];
1467        }
1468        else
1469        {
1470          return controls;
1471        }
1472      }
1473    
1474    
1475    
1476      /**
1477       * Updates the provided request to account for the given set of constraints.
1478       *
1479       * @param  request      The request to be updated.
1480       * @param  constraints  The constraints to be applied.
1481       */
1482      private void update(final UpdatableLDAPRequest request,
1483                          final LDAPConstraints constraints)
1484      {
1485        final LDAPConstraints c =
1486             (constraints == null) ? this.constraints : constraints;
1487    
1488        request.setControls(LDAPControl.toControls(c.getServerControls()));
1489        request.setResponseTimeoutMillis(c.getTimeLimit());
1490        request.setFollowReferrals(c.getReferrals());
1491      }
1492    
1493    
1494    
1495      /**
1496       * Sets the response controls for this connection.
1497       *
1498       * @param  ldapResult  The result containing the controls to use.
1499       */
1500      private void setResponseControls(final LDAPResult ldapResult)
1501      {
1502        if (ldapResult.hasResponseControl())
1503        {
1504          responseControls =
1505               LDAPControl.toLDAPControls(ldapResult.getResponseControls());
1506        }
1507        else
1508        {
1509          responseControls = null;
1510        }
1511      }
1512    
1513    
1514    
1515      /**
1516       * Sets the response controls for this connection.
1517       *
1518       * @param  ldapException  The exception containing the controls to use.
1519       */
1520      private void setResponseControls(
1521                        final com.unboundid.ldap.sdk.LDAPException ldapException)
1522      {
1523        if (ldapException.hasResponseControl())
1524        {
1525          responseControls =
1526               LDAPControl.toLDAPControls(ldapException.getResponseControls());
1527        }
1528        else
1529        {
1530          responseControls = null;
1531        }
1532      }
1533    }