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