001/*
002 * Copyright 2007-2017 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2008-2017 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;
022
023
024
025import java.util.ArrayList;
026import java.util.Arrays;
027import java.util.List;
028import java.util.concurrent.LinkedBlockingQueue;
029import java.util.concurrent.TimeUnit;
030
031import com.unboundid.asn1.ASN1Buffer;
032import com.unboundid.asn1.ASN1BufferSequence;
033import com.unboundid.asn1.ASN1Element;
034import com.unboundid.asn1.ASN1Integer;
035import com.unboundid.asn1.ASN1OctetString;
036import com.unboundid.asn1.ASN1Sequence;
037import com.unboundid.ldap.protocol.LDAPMessage;
038import com.unboundid.ldap.protocol.LDAPResponse;
039import com.unboundid.ldap.protocol.ProtocolOp;
040import com.unboundid.util.InternalUseOnly;
041import com.unboundid.util.LDAPSDKUsageException;
042import com.unboundid.util.NotMutable;
043import com.unboundid.util.ThreadSafety;
044import com.unboundid.util.ThreadSafetyLevel;
045
046import static com.unboundid.ldap.sdk.LDAPMessages.*;
047import static com.unboundid.util.Debug.*;
048import static com.unboundid.util.StaticUtils.*;
049
050
051
052/**
053 * This class implements the processing necessary to perform an LDAPv3 simple
054 * bind operation, which authenticates using a bind DN and password.
055 */
056@NotMutable()
057@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
058public final class SimpleBindRequest
059       extends BindRequest
060       implements ResponseAcceptor, ProtocolOp
061{
062  /**
063   * The BER type to use for the credentials element in a simple bind request
064   * protocol op.
065   */
066  private static final byte CRED_TYPE_SIMPLE = (byte) 0x80;
067
068
069
070  /**
071   * The ASN.1 octet string that will be used for the bind DN if none was
072   * provided.
073   */
074  private static final ASN1OctetString NO_BIND_DN = new ASN1OctetString();
075
076
077
078  /**
079   * The ASN.1 octet string that will be used for the bind password if none was
080   * provided.
081   */
082  private static final ASN1OctetString NO_PASSWORD =
083       new ASN1OctetString(CRED_TYPE_SIMPLE);
084
085
086
087  /**
088   * The serial version UID for this serializable class.
089   */
090  private static final long serialVersionUID = 4725871243149974407L;
091
092
093
094  // The message ID from the last LDAP message sent from this request.
095  private int messageID = -1;
096
097  // The bind DN for this simple bind request.
098  private final ASN1OctetString bindDN;
099
100  // The password for this simple bind request.
101  private final ASN1OctetString password;
102
103  // The queue that will be used to receive response messages from the server.
104  private final LinkedBlockingQueue<LDAPResponse> responseQueue =
105       new LinkedBlockingQueue<LDAPResponse>();
106
107  // The password provider that should be used to obtain the password for this
108  // simple bind request.
109  private final PasswordProvider passwordProvider;
110
111
112
113  /**
114   * Creates a new simple bind request that may be used to perform an anonymous
115   * bind to the directory server (i.e., with a zero-length bind DN and a
116   * zero-length password).
117   */
118  public SimpleBindRequest()
119  {
120    this(NO_BIND_DN, NO_PASSWORD, null, NO_CONTROLS);
121  }
122
123
124
125  /**
126   * Creates a new simple bind request with the provided bind DN and password.
127   *
128   * @param  bindDN    The bind DN for this simple bind request.
129   * @param  password  The password for this simple bind request.
130   */
131  public SimpleBindRequest(final String bindDN, final String password)
132  {
133    this(bindDN, password, NO_CONTROLS);
134  }
135
136
137
138  /**
139   * Creates a new simple bind request with the provided bind DN and password.
140   *
141   * @param  bindDN    The bind DN for this simple bind request.
142   * @param  password  The password for this simple bind request.
143   */
144  public SimpleBindRequest(final String bindDN, final byte[] password)
145  {
146    this(bindDN, password, NO_CONTROLS);
147  }
148
149
150
151  /**
152   * Creates a new simple bind request with the provided bind DN and password.
153   *
154   * @param  bindDN    The bind DN for this simple bind request.
155   * @param  password  The password for this simple bind request.
156   */
157  public SimpleBindRequest(final DN bindDN, final String password)
158  {
159    this(bindDN, password, NO_CONTROLS);
160  }
161
162
163
164  /**
165   * Creates a new simple bind request with the provided bind DN and password.
166   *
167   * @param  bindDN    The bind DN for this simple bind request.
168   * @param  password  The password for this simple bind request.
169   */
170  public SimpleBindRequest(final DN bindDN, final byte[] password)
171  {
172    this(bindDN, password, NO_CONTROLS);
173  }
174
175
176
177  /**
178   * Creates a new simple bind request with the provided bind DN and password.
179   *
180   * @param  bindDN    The bind DN for this simple bind request.
181   * @param  password  The password for this simple bind request.
182   * @param  controls  The set of controls for this simple bind request.
183   */
184  public SimpleBindRequest(final String bindDN, final String password,
185                           final Control... controls)
186  {
187    super(controls);
188
189    if (bindDN == null)
190    {
191      this.bindDN = NO_BIND_DN;
192    }
193    else
194    {
195      this.bindDN = new ASN1OctetString(bindDN);
196    }
197
198    if (password == null)
199    {
200      this.password = NO_PASSWORD;
201    }
202    else
203    {
204      this.password = new ASN1OctetString(CRED_TYPE_SIMPLE, password);
205    }
206
207    passwordProvider = null;
208  }
209
210
211
212  /**
213   * Creates a new simple bind request with the provided bind DN and password.
214   *
215   * @param  bindDN    The bind DN for this simple bind request.
216   * @param  password  The password for this simple bind request.
217   * @param  controls  The set of controls for this simple bind request.
218   */
219  public SimpleBindRequest(final String bindDN, final byte[] password,
220                           final Control... controls)
221  {
222    super(controls);
223
224    if (bindDN == null)
225    {
226      this.bindDN = NO_BIND_DN;
227    }
228    else
229    {
230      this.bindDN = new ASN1OctetString(bindDN);
231    }
232
233    if (password == null)
234    {
235      this.password = NO_PASSWORD;
236    }
237    else
238    {
239      this.password = new ASN1OctetString(CRED_TYPE_SIMPLE, password);
240    }
241
242    passwordProvider = null;
243  }
244
245
246
247  /**
248   * Creates a new simple bind request with the provided bind DN and password.
249   *
250   * @param  bindDN    The bind DN for this simple bind request.
251   * @param  password  The password for this simple bind request.
252   * @param  controls  The set of controls for this simple bind request.
253   */
254  public SimpleBindRequest(final DN bindDN, final String password,
255                           final Control... controls)
256  {
257    super(controls);
258
259    if (bindDN == null)
260    {
261      this.bindDN = NO_BIND_DN;
262    }
263    else
264    {
265      this.bindDN = new ASN1OctetString(bindDN.toString());
266    }
267
268    if (password == null)
269    {
270      this.password = NO_PASSWORD;
271    }
272    else
273    {
274      this.password = new ASN1OctetString(CRED_TYPE_SIMPLE, password);
275    }
276
277    passwordProvider = null;
278  }
279
280
281
282  /**
283   * Creates a new simple bind request with the provided bind DN and password.
284   *
285   * @param  bindDN    The bind DN for this simple bind request.
286   * @param  password  The password for this simple bind request.
287   * @param  controls  The set of controls for this simple bind request.
288   */
289  public SimpleBindRequest(final DN bindDN, final byte[] password,
290                           final Control... controls)
291  {
292    super(controls);
293
294    if (bindDN == null)
295    {
296      this.bindDN = NO_BIND_DN;
297    }
298    else
299    {
300      this.bindDN = new ASN1OctetString(bindDN.toString());
301    }
302
303    if (password == null)
304    {
305      this.password = NO_PASSWORD;
306    }
307    else
308    {
309      this.password = new ASN1OctetString(CRED_TYPE_SIMPLE, password);
310    }
311
312    passwordProvider = null;
313  }
314
315
316
317  /**
318   * Creates a new simple bind request with the provided bind DN and that will
319   * use a password provider in order to obtain the bind password.
320   *
321   * @param  bindDN            The bind DN for this simple bind request.  It
322   *                           must not be {@code null}.
323   * @param  passwordProvider  The password provider that will be used to obtain
324   *                           the password for this simple bind request.  It
325   *                           must not be {@code null}.
326   * @param  controls          The set of controls for this simple bind request.
327   */
328  public SimpleBindRequest(final String bindDN,
329                           final PasswordProvider passwordProvider,
330                           final Control... controls)
331  {
332    super(controls);
333
334    this.bindDN           = new ASN1OctetString(bindDN);
335    this.passwordProvider = passwordProvider;
336
337    password = null;
338  }
339
340
341
342  /**
343   * Creates a new simple bind request with the provided bind DN and that will
344   * use a password provider in order to obtain the bind password.
345   *
346   * @param  bindDN            The bind DN for this simple bind request.  It
347   *                           must not be {@code null}.
348   * @param  passwordProvider  The password provider that will be used to obtain
349   *                           the password for this simple bind request.  It
350   *                           must not be {@code null}.
351   * @param  controls          The set of controls for this simple bind request.
352   */
353  public SimpleBindRequest(final DN bindDN,
354                           final PasswordProvider passwordProvider,
355                           final Control... controls)
356  {
357    super(controls);
358
359    this.bindDN           = new ASN1OctetString(bindDN.toString());
360    this.passwordProvider = passwordProvider;
361
362    password = null;
363  }
364
365
366
367  /**
368   * Creates a new simple bind request with the provided bind DN and password.
369   *
370   * @param  bindDN            The bind DN for this simple bind request.
371   * @param  password          The password for this simple bind request.
372   * @param  passwordProvider  The password provider that will be used to obtain
373   *                           the password to use for the bind request.
374   * @param  controls          The set of controls for this simple bind request.
375   */
376  private SimpleBindRequest(final ASN1OctetString bindDN,
377                            final ASN1OctetString password,
378                            final PasswordProvider passwordProvider,
379                            final Control... controls)
380  {
381    super(controls);
382
383    this.bindDN           = bindDN;
384    this.password         = password;
385    this.passwordProvider = passwordProvider;
386  }
387
388
389
390  /**
391   * Retrieves the bind DN for this simple bind request.
392   *
393   * @return  The bind DN for this simple bind request.
394   */
395  public String getBindDN()
396  {
397    return bindDN.stringValue();
398  }
399
400
401
402  /**
403   * Retrieves the password for this simple bind request, if no password
404   * provider has been configured.
405   *
406   * @return  The password for this simple bind request, or {@code null} if a
407   *          password provider will be used to obtain the password.
408   */
409  public ASN1OctetString getPassword()
410  {
411    return password;
412  }
413
414
415
416  /**
417   * Retrieves the password provider for this simple bind request, if defined.
418   *
419   * @return  The password provider for this simple bind request, or
420   *          {@code null} if this bind request was created with an explicit
421   *          password rather than a password provider.
422   */
423  public PasswordProvider getPasswordProvider()
424  {
425    return passwordProvider;
426  }
427
428
429
430  /**
431   * {@inheritDoc}
432   */
433  @Override()
434  public byte getProtocolOpType()
435  {
436    return LDAPMessage.PROTOCOL_OP_TYPE_BIND_REQUEST;
437  }
438
439
440
441  /**
442   * {@inheritDoc}
443   */
444  @Override()
445  public void writeTo(final ASN1Buffer buffer)
446  {
447    final ASN1BufferSequence requestSequence =
448         buffer.beginSequence(LDAPMessage.PROTOCOL_OP_TYPE_BIND_REQUEST);
449    buffer.addElement(VERSION_ELEMENT);
450    buffer.addElement(bindDN);
451
452    if (passwordProvider == null)
453    {
454      buffer.addElement(password);
455    }
456    else
457    {
458      final byte[] pwBytes;
459      try
460      {
461        pwBytes = passwordProvider.getPasswordBytes();
462      }
463      catch (final LDAPException le)
464      {
465        debugException(le);
466        throw new LDAPRuntimeException(le);
467      }
468
469      final ASN1OctetString pw = new ASN1OctetString(CRED_TYPE_SIMPLE, pwBytes);
470      buffer.addElement(pw);
471      buffer.setZeroBufferOnClear();
472      Arrays.fill(pwBytes, (byte) 0x00);
473    }
474
475    requestSequence.end();
476  }
477
478
479
480  /**
481   * {@inheritDoc}
482   * Use of this method is only supported if the bind request was created with a
483   * static password.  It is not allowed if the password will be obtained
484   * through a password provider.
485   *
486   * @throws  LDAPSDKUsageException  If this bind request was created with a
487   *                                 password provider rather than a static
488   *                                 password.
489   */
490  @Override()
491  public ASN1Element encodeProtocolOp()
492         throws LDAPSDKUsageException
493  {
494    if (password == null)
495    {
496      throw new LDAPSDKUsageException(
497           ERR_SIMPLE_BIND_ENCODE_PROTOCOL_OP_WITH_PROVIDER.get());
498    }
499
500    return new ASN1Sequence(LDAPMessage.PROTOCOL_OP_TYPE_BIND_REQUEST,
501         new ASN1Integer(3),
502         bindDN,
503         password);
504  }
505
506
507
508  /**
509   * {@inheritDoc}
510   */
511  @Override()
512  protected BindResult process(final LDAPConnection connection, final int depth)
513            throws LDAPException
514  {
515    if (connection.synchronousMode())
516    {
517      @SuppressWarnings("deprecation")
518      final boolean autoReconnect =
519           connection.getConnectionOptions().autoReconnect();
520      return processSync(connection, autoReconnect);
521    }
522
523    // See if a bind DN was provided without a password.  If that is the case
524    // and this should not be allowed, then throw an exception.
525    if (password != null)
526    {
527      if ((bindDN.getValue().length > 0) && (password.getValue().length == 0) &&
528           connection.getConnectionOptions().bindWithDNRequiresPassword())
529      {
530        final LDAPException le = new LDAPException(ResultCode.PARAM_ERROR,
531             ERR_SIMPLE_BIND_DN_WITHOUT_PASSWORD.get());
532        debugCodingError(le);
533        throw le;
534      }
535    }
536
537
538    // Create the LDAP message.
539    messageID = connection.nextMessageID();
540    final LDAPMessage message = new LDAPMessage(messageID, this, getControls());
541
542
543    // Register with the connection reader to be notified of responses for the
544    // request that we've created.
545    connection.registerResponseAcceptor(messageID, this);
546
547
548    try
549    {
550      // Send the request to the server.
551      debugLDAPRequest(this);
552      final long requestTime = System.nanoTime();
553      connection.getConnectionStatistics().incrementNumBindRequests();
554      connection.sendMessage(message);
555
556      // Wait for and process the response.
557      final LDAPResponse response;
558      try
559      {
560        final long responseTimeout = getResponseTimeoutMillis(connection);
561        if (responseTimeout > 0)
562        {
563          response = responseQueue.poll(responseTimeout, TimeUnit.MILLISECONDS);
564        }
565        else
566        {
567          response = responseQueue.take();
568        }
569      }
570      catch (final InterruptedException ie)
571      {
572        debugException(ie);
573        Thread.currentThread().interrupt();
574        throw new LDAPException(ResultCode.LOCAL_ERROR,
575             ERR_BIND_INTERRUPTED.get(connection.getHostPort()), ie);
576      }
577
578      return handleResponse(connection, response, requestTime, false);
579    }
580    finally
581    {
582      connection.deregisterResponseAcceptor(messageID);
583    }
584  }
585
586
587
588  /**
589   * Processes this bind operation in synchronous mode, in which the same
590   * thread will send the request and read the response.
591   *
592   * @param  connection  The connection to use to communicate with the directory
593   *                     server.
594   * @param  allowRetry  Indicates whether the request may be re-tried on a
595   *                     re-established connection if the initial attempt fails
596   *                     in a way that indicates the connection is no longer
597   *                     valid and autoReconnect is true.
598   *
599   * @return  An LDAP result object that provides information about the result
600   *          of the bind processing.
601   *
602   * @throws  LDAPException  If a problem occurs while sending the request or
603   *                         reading the response.
604   */
605  private BindResult processSync(final LDAPConnection connection,
606                                 final boolean allowRetry)
607          throws LDAPException
608  {
609    // Create the LDAP message.
610    messageID = connection.nextMessageID();
611    final LDAPMessage message =
612         new LDAPMessage(messageID, this, getControls());
613
614
615    // Set the appropriate timeout on the socket.
616    try
617    {
618      connection.getConnectionInternals(true).getSocket().setSoTimeout(
619           (int) getResponseTimeoutMillis(connection));
620    }
621    catch (final Exception e)
622    {
623      debugException(e);
624    }
625
626
627    // Send the request to the server.
628    final long requestTime = System.nanoTime();
629    debugLDAPRequest(this);
630    connection.getConnectionStatistics().incrementNumBindRequests();
631    try
632    {
633      connection.sendMessage(message);
634    }
635    catch (final LDAPException le)
636    {
637      debugException(le);
638
639      if (allowRetry)
640      {
641        final BindResult bindResult = reconnectAndRetry(connection,
642             le.getResultCode());
643        if (bindResult != null)
644        {
645          return bindResult;
646        }
647      }
648    }
649
650    while (true)
651    {
652      final LDAPResponse response = connection.readResponse(messageID);
653      if (response instanceof IntermediateResponse)
654      {
655        final IntermediateResponseListener listener =
656             getIntermediateResponseListener();
657        if (listener != null)
658        {
659          listener.intermediateResponseReturned(
660               (IntermediateResponse) response);
661        }
662      }
663      else
664      {
665        return handleResponse(connection, response, requestTime, allowRetry);
666      }
667    }
668  }
669
670
671
672  /**
673   * Performs the necessary processing for handling a response.
674   *
675   * @param  connection   The connection used to read the response.
676   * @param  response     The response to be processed.
677   * @param  requestTime  The time the request was sent to the server.
678   * @param  allowRetry   Indicates whether the request may be re-tried on a
679   *                      re-established connection if the initial attempt fails
680   *                      in a way that indicates the connection is no longer
681   *                      valid and autoReconnect is true.
682   *
683   * @return  The bind result.
684   *
685   * @throws  LDAPException  If a problem occurs.
686   */
687  private BindResult handleResponse(final LDAPConnection connection,
688                                    final LDAPResponse response,
689                                    final long requestTime,
690                                    final boolean allowRetry)
691          throws LDAPException
692  {
693    if (response == null)
694    {
695      final long waitTime = nanosToMillis(System.nanoTime() - requestTime);
696      throw new LDAPException(ResultCode.TIMEOUT,
697           ERR_SIMPLE_BIND_CLIENT_TIMEOUT.get(waitTime, messageID,
698                bindDN.stringValue(), connection.getHostPort()));
699    }
700
701    connection.getConnectionStatistics().incrementNumBindResponses(
702         System.nanoTime() - requestTime);
703    if (response instanceof ConnectionClosedResponse)
704    {
705      // The connection was closed while waiting for the response.
706      if (allowRetry)
707      {
708        final BindResult retryResult = reconnectAndRetry(connection,
709             ResultCode.SERVER_DOWN);
710        if (retryResult != null)
711        {
712          return retryResult;
713        }
714      }
715
716      final ConnectionClosedResponse ccr = (ConnectionClosedResponse) response;
717      final String message = ccr.getMessage();
718      if (message == null)
719      {
720        throw new LDAPException(ccr.getResultCode(),
721             ERR_CONN_CLOSED_WAITING_FOR_BIND_RESPONSE.get(
722                  connection.getHostPort(), toString()));
723      }
724      else
725      {
726        throw new LDAPException(ccr.getResultCode(),
727             ERR_CONN_CLOSED_WAITING_FOR_BIND_RESPONSE_WITH_MESSAGE.get(
728                  connection.getHostPort(), toString(), message));
729      }
730    }
731
732    final BindResult bindResult = (BindResult) response;
733    if (allowRetry)
734    {
735      final BindResult retryResult = reconnectAndRetry(connection,
736           bindResult.getResultCode());
737      if (retryResult != null)
738      {
739        return retryResult;
740      }
741    }
742
743    return bindResult;
744  }
745
746
747
748  /**
749   * Attempts to re-establish the connection and retry processing this request
750   * on it.
751   *
752   * @param  connection  The connection to be re-established.
753   * @param  resultCode  The result code for the previous operation attempt.
754   *
755   * @return  The result from re-trying the bind, or {@code null} if it could
756   *          not be re-tried.
757   */
758  private BindResult reconnectAndRetry(final LDAPConnection connection,
759                                       final ResultCode resultCode)
760  {
761    try
762    {
763      // We will only want to retry for certain result codes that indicate a
764      // connection problem.
765      switch (resultCode.intValue())
766      {
767        case ResultCode.SERVER_DOWN_INT_VALUE:
768        case ResultCode.DECODING_ERROR_INT_VALUE:
769        case ResultCode.CONNECT_ERROR_INT_VALUE:
770          connection.reconnect();
771          return processSync(connection, false);
772      }
773    }
774    catch (final Exception e)
775    {
776      debugException(e);
777    }
778
779    return null;
780  }
781
782
783
784  /**
785   * {@inheritDoc}
786   */
787  @Override()
788  public SimpleBindRequest getRebindRequest(final String host, final int port)
789  {
790    return new SimpleBindRequest(bindDN, password, passwordProvider,
791         getControls());
792  }
793
794
795
796  /**
797   * {@inheritDoc}
798   */
799  @InternalUseOnly()
800  @Override()
801  public void responseReceived(final LDAPResponse response)
802         throws LDAPException
803  {
804    try
805    {
806      responseQueue.put(response);
807    }
808    catch (final Exception e)
809    {
810      debugException(e);
811
812      if (e instanceof InterruptedException)
813      {
814        Thread.currentThread().interrupt();
815      }
816
817      throw new LDAPException(ResultCode.LOCAL_ERROR,
818           ERR_EXCEPTION_HANDLING_RESPONSE.get(getExceptionMessage(e)), e);
819    }
820  }
821
822
823
824  /**
825   * {@inheritDoc}
826   */
827  @Override()
828  public String getBindType()
829  {
830    return "SIMPLE";
831  }
832
833
834
835  /**
836   * {@inheritDoc}
837   */
838  @Override()
839  public int getLastMessageID()
840  {
841    return messageID;
842  }
843
844
845
846  /**
847   * {@inheritDoc}
848   */
849  @Override()
850  public SimpleBindRequest duplicate()
851  {
852    return duplicate(getControls());
853  }
854
855
856
857  /**
858   * {@inheritDoc}
859   */
860  @Override()
861  public SimpleBindRequest duplicate(final Control[] controls)
862  {
863    final SimpleBindRequest bindRequest =
864         new SimpleBindRequest(bindDN, password, passwordProvider, controls);
865    bindRequest.setResponseTimeoutMillis(getResponseTimeoutMillis(null));
866    return bindRequest;
867  }
868
869
870
871  /**
872   * {@inheritDoc}
873   */
874  @Override()
875  public void toString(final StringBuilder buffer)
876  {
877    buffer.append("SimpleBindRequest(dn='");
878    buffer.append(bindDN);
879    buffer.append('\'');
880
881    final Control[] controls = getControls();
882    if (controls.length > 0)
883    {
884      buffer.append(", controls={");
885      for (int i=0; i < controls.length; i++)
886      {
887        if (i > 0)
888        {
889          buffer.append(", ");
890        }
891
892        buffer.append(controls[i]);
893      }
894      buffer.append('}');
895    }
896
897    buffer.append(')');
898  }
899
900
901
902  /**
903   * {@inheritDoc}
904   */
905  @Override()
906  public void toCode(final List<String> lineList, final String requestID,
907                     final int indentSpaces, final boolean includeProcessing)
908  {
909    // Create the request variable.
910    final ArrayList<ToCodeArgHelper> constructorArgs =
911         new ArrayList<ToCodeArgHelper>(3);
912    constructorArgs.add(ToCodeArgHelper.createString(bindDN.stringValue(),
913         "Bind DN"));
914    constructorArgs.add(ToCodeArgHelper.createString("---redacted-password---",
915         "Bind Password"));
916
917    final Control[] controls = getControls();
918    if (controls.length > 0)
919    {
920      constructorArgs.add(ToCodeArgHelper.createControlArray(controls,
921           "Bind Controls"));
922    }
923
924    ToCodeHelper.generateMethodCall(lineList, indentSpaces, "SimpleBindRequest",
925         requestID + "Request", "new SimpleBindRequest", constructorArgs);
926
927
928    // Add lines for processing the request and obtaining the result.
929    if (includeProcessing)
930    {
931      // Generate a string with the appropriate indent.
932      final StringBuilder buffer = new StringBuilder();
933      for (int i=0; i < indentSpaces; i++)
934      {
935        buffer.append(' ');
936      }
937      final String indent = buffer.toString();
938
939      lineList.add("");
940      lineList.add(indent + "try");
941      lineList.add(indent + '{');
942      lineList.add(indent + "  BindResult " + requestID +
943           "Result = connection.bind(" + requestID + "Request);");
944      lineList.add(indent + "  // The bind was processed successfully.");
945      lineList.add(indent + '}');
946      lineList.add(indent + "catch (LDAPException e)");
947      lineList.add(indent + '{');
948      lineList.add(indent + "  // The bind failed.  Maybe the following will " +
949           "help explain why.");
950      lineList.add(indent + "  // Note that the connection is now likely in " +
951           "an unauthenticated state.");
952      lineList.add(indent + "  ResultCode resultCode = e.getResultCode();");
953      lineList.add(indent + "  String message = e.getMessage();");
954      lineList.add(indent + "  String matchedDN = e.getMatchedDN();");
955      lineList.add(indent + "  String[] referralURLs = e.getReferralURLs();");
956      lineList.add(indent + "  Control[] responseControls = " +
957           "e.getResponseControls();");
958      lineList.add(indent + '}');
959    }
960  }
961}