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