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