001/*
002 * Copyright 2007-2017 UnboundID Corp.
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2008-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;
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  public byte getProtocolOpType()
434  {
435    return LDAPMessage.PROTOCOL_OP_TYPE_BIND_REQUEST;
436  }
437
438
439
440  /**
441   * {@inheritDoc}
442   */
443  public void writeTo(final ASN1Buffer buffer)
444  {
445    final ASN1BufferSequence requestSequence =
446         buffer.beginSequence(LDAPMessage.PROTOCOL_OP_TYPE_BIND_REQUEST);
447    buffer.addElement(VERSION_ELEMENT);
448    buffer.addElement(bindDN);
449
450    if (passwordProvider == null)
451    {
452      buffer.addElement(password);
453    }
454    else
455    {
456      byte[] pwBytes;
457      try
458      {
459        pwBytes = passwordProvider.getPasswordBytes();
460      }
461      catch (final LDAPException le)
462      {
463        debugException(le);
464        throw new LDAPRuntimeException(le);
465      }
466
467      final ASN1OctetString pw = new ASN1OctetString(CRED_TYPE_SIMPLE, pwBytes);
468      buffer.addElement(pw);
469      buffer.setZeroBufferOnClear();
470      Arrays.fill(pwBytes, (byte) 0x00);
471    }
472
473    requestSequence.end();
474  }
475
476
477
478  /**
479   * {@inheritDoc}
480   * Use of this method is only supported if the bind request was created with a
481   * static password.  It is not allowed if the password will be obtained
482   * through a password provider.
483   *
484   * @throws  LDAPSDKUsageException  If this bind request was created with a
485   *                                 password provider rather than a static
486   *                                 password.
487   */
488  public ASN1Element encodeProtocolOp()
489         throws LDAPSDKUsageException
490  {
491    if (password == null)
492    {
493      throw new LDAPSDKUsageException(
494           ERR_SIMPLE_BIND_ENCODE_PROTOCOL_OP_WITH_PROVIDER.get());
495    }
496
497    return new ASN1Sequence(LDAPMessage.PROTOCOL_OP_TYPE_BIND_REQUEST,
498         new ASN1Integer(3),
499         bindDN,
500         password);
501  }
502
503
504
505  /**
506   * {@inheritDoc}
507   */
508  @Override()
509  protected BindResult process(final LDAPConnection connection, final int depth)
510            throws LDAPException
511  {
512    if (connection.synchronousMode())
513    {
514      @SuppressWarnings("deprecation")
515      final boolean autoReconnect =
516           connection.getConnectionOptions().autoReconnect();
517      return processSync(connection, autoReconnect);
518    }
519
520    // See if a bind DN was provided without a password.  If that is the case
521    // and this should not be allowed, then throw an exception.
522    if (password != null)
523    {
524      if ((bindDN.getValue().length > 0) && (password.getValue().length == 0) &&
525           connection.getConnectionOptions().bindWithDNRequiresPassword())
526      {
527        final LDAPException le = new LDAPException(ResultCode.PARAM_ERROR,
528             ERR_SIMPLE_BIND_DN_WITHOUT_PASSWORD.get());
529        debugCodingError(le);
530        throw le;
531      }
532    }
533
534
535    // Create the LDAP message.
536    messageID = connection.nextMessageID();
537    final LDAPMessage message = new LDAPMessage(messageID, this, getControls());
538
539
540    // Register with the connection reader to be notified of responses for the
541    // request that we've created.
542    connection.registerResponseAcceptor(messageID, this);
543
544
545    try
546    {
547      // Send the request to the server.
548      debugLDAPRequest(this);
549      final long requestTime = System.nanoTime();
550      connection.getConnectionStatistics().incrementNumBindRequests();
551      connection.sendMessage(message);
552
553      // Wait for and process the response.
554      final LDAPResponse response;
555      try
556      {
557        final long responseTimeout = getResponseTimeoutMillis(connection);
558        if (responseTimeout > 0)
559        {
560          response = responseQueue.poll(responseTimeout, TimeUnit.MILLISECONDS);
561        }
562        else
563        {
564          response = responseQueue.take();
565        }
566      }
567      catch (InterruptedException ie)
568      {
569        debugException(ie);
570        Thread.currentThread().interrupt();
571        throw new LDAPException(ResultCode.LOCAL_ERROR,
572             ERR_BIND_INTERRUPTED.get(connection.getHostPort()), ie);
573      }
574
575      return handleResponse(connection, response, requestTime, false);
576    }
577    finally
578    {
579      connection.deregisterResponseAcceptor(messageID);
580    }
581  }
582
583
584
585  /**
586   * Processes this bind operation in synchronous mode, in which the same
587   * thread will send the request and read the response.
588   *
589   * @param  connection  The connection to use to communicate with the directory
590   *                     server.
591   * @param  allowRetry  Indicates whether the request may be re-tried on a
592   *                     re-established connection if the initial attempt fails
593   *                     in a way that indicates the connection is no longer
594   *                     valid and autoReconnect is true.
595   *
596   * @return  An LDAP result object that provides information about the result
597   *          of the bind processing.
598   *
599   * @throws  LDAPException  If a problem occurs while sending the request or
600   *                         reading the response.
601   */
602  private BindResult processSync(final LDAPConnection connection,
603                                 final boolean allowRetry)
604          throws LDAPException
605  {
606    // Create the LDAP message.
607    messageID = connection.nextMessageID();
608    final LDAPMessage message =
609         new LDAPMessage(messageID, this, getControls());
610
611
612    // Set the appropriate timeout on the socket.
613    try
614    {
615      connection.getConnectionInternals(true).getSocket().setSoTimeout(
616           (int) getResponseTimeoutMillis(connection));
617    }
618    catch (Exception e)
619    {
620      debugException(e);
621    }
622
623
624    // Send the request to the server.
625    final long requestTime = System.nanoTime();
626    debugLDAPRequest(this);
627    connection.getConnectionStatistics().incrementNumBindRequests();
628    try
629    {
630      connection.sendMessage(message);
631    }
632    catch (final LDAPException le)
633    {
634      debugException(le);
635
636      if (allowRetry)
637      {
638        final BindResult bindResult = reconnectAndRetry(connection,
639             le.getResultCode());
640        if (bindResult != null)
641        {
642          return bindResult;
643        }
644      }
645    }
646
647    while (true)
648    {
649      final LDAPResponse response = connection.readResponse(messageID);
650      if (response instanceof IntermediateResponse)
651      {
652        final IntermediateResponseListener listener =
653             getIntermediateResponseListener();
654        if (listener != null)
655        {
656          listener.intermediateResponseReturned(
657               (IntermediateResponse) response);
658        }
659      }
660      else
661      {
662        return handleResponse(connection, response, requestTime, allowRetry);
663      }
664    }
665  }
666
667
668
669  /**
670   * Performs the necessary processing for handling a response.
671   *
672   * @param  connection   The connection used to read the response.
673   * @param  response     The response to be processed.
674   * @param  requestTime  The time the request was sent to the server.
675   * @param  allowRetry   Indicates whether the request may be re-tried on a
676   *                      re-established connection if the initial attempt fails
677   *                      in a way that indicates the connection is no longer
678   *                      valid and autoReconnect is true.
679   *
680   * @return  The bind result.
681   *
682   * @throws  LDAPException  If a problem occurs.
683   */
684  private BindResult handleResponse(final LDAPConnection connection,
685                                    final LDAPResponse response,
686                                    final long requestTime,
687                                    final boolean allowRetry)
688          throws LDAPException
689  {
690    if (response == null)
691    {
692      final long waitTime = nanosToMillis(System.nanoTime() - requestTime);
693      throw new LDAPException(ResultCode.TIMEOUT,
694           ERR_SIMPLE_BIND_CLIENT_TIMEOUT.get(waitTime, messageID,
695                bindDN.stringValue(), connection.getHostPort()));
696    }
697
698    connection.getConnectionStatistics().incrementNumBindResponses(
699         System.nanoTime() - requestTime);
700    if (response instanceof ConnectionClosedResponse)
701    {
702      // The connection was closed while waiting for the response.
703      if (allowRetry)
704      {
705        final BindResult retryResult = reconnectAndRetry(connection,
706             ResultCode.SERVER_DOWN);
707        if (retryResult != null)
708        {
709          return retryResult;
710        }
711      }
712
713      final ConnectionClosedResponse ccr = (ConnectionClosedResponse) response;
714      final String message = ccr.getMessage();
715      if (message == null)
716      {
717        throw new LDAPException(ccr.getResultCode(),
718             ERR_CONN_CLOSED_WAITING_FOR_BIND_RESPONSE.get(
719                  connection.getHostPort(), toString()));
720      }
721      else
722      {
723        throw new LDAPException(ccr.getResultCode(),
724             ERR_CONN_CLOSED_WAITING_FOR_BIND_RESPONSE_WITH_MESSAGE.get(
725                  connection.getHostPort(), toString(), message));
726      }
727    }
728
729    final BindResult bindResult = (BindResult) response;
730    if (allowRetry)
731    {
732      final BindResult retryResult = reconnectAndRetry(connection,
733           bindResult.getResultCode());
734      if (retryResult != null)
735      {
736        return retryResult;
737      }
738    }
739
740    return bindResult;
741  }
742
743
744
745  /**
746   * Attempts to re-establish the connection and retry processing this request
747   * on it.
748   *
749   * @param  connection  The connection to be re-established.
750   * @param  resultCode  The result code for the previous operation attempt.
751   *
752   * @return  The result from re-trying the bind, or {@code null} if it could
753   *          not be re-tried.
754   */
755  private BindResult reconnectAndRetry(final LDAPConnection connection,
756                                       final ResultCode resultCode)
757  {
758    try
759    {
760      // We will only want to retry for certain result codes that indicate a
761      // connection problem.
762      switch (resultCode.intValue())
763      {
764        case ResultCode.SERVER_DOWN_INT_VALUE:
765        case ResultCode.DECODING_ERROR_INT_VALUE:
766        case ResultCode.CONNECT_ERROR_INT_VALUE:
767          connection.reconnect();
768          return processSync(connection, false);
769      }
770    }
771    catch (final Exception e)
772    {
773      debugException(e);
774    }
775
776    return null;
777  }
778
779
780
781  /**
782   * {@inheritDoc}
783   */
784  @Override()
785  public SimpleBindRequest getRebindRequest(final String host, final int port)
786  {
787    return new SimpleBindRequest(bindDN, password, passwordProvider,
788         getControls());
789  }
790
791
792
793  /**
794   * {@inheritDoc}
795   */
796  @InternalUseOnly()
797  public void responseReceived(final LDAPResponse response)
798         throws LDAPException
799  {
800    try
801    {
802      responseQueue.put(response);
803    }
804    catch (Exception e)
805    {
806      debugException(e);
807
808      if (e instanceof InterruptedException)
809      {
810        Thread.currentThread().interrupt();
811      }
812
813      throw new LDAPException(ResultCode.LOCAL_ERROR,
814           ERR_EXCEPTION_HANDLING_RESPONSE.get(getExceptionMessage(e)), e);
815    }
816  }
817
818
819
820  /**
821   * {@inheritDoc}
822   */
823  @Override()
824  public String getBindType()
825  {
826    return "SIMPLE";
827  }
828
829
830
831  /**
832   * {@inheritDoc}
833   */
834  @Override()
835  public int getLastMessageID()
836  {
837    return messageID;
838  }
839
840
841
842  /**
843   * {@inheritDoc}
844   */
845  @Override()
846  public SimpleBindRequest duplicate()
847  {
848    return duplicate(getControls());
849  }
850
851
852
853  /**
854   * {@inheritDoc}
855   */
856  @Override()
857  public SimpleBindRequest duplicate(final Control[] controls)
858  {
859    final SimpleBindRequest bindRequest =
860         new SimpleBindRequest(bindDN, password, passwordProvider, controls);
861    bindRequest.setResponseTimeoutMillis(getResponseTimeoutMillis(null));
862    return bindRequest;
863  }
864
865
866
867  /**
868   * {@inheritDoc}
869   */
870  @Override()
871  public void toString(final StringBuilder buffer)
872  {
873    buffer.append("SimpleBindRequest(dn='");
874    buffer.append(bindDN);
875    buffer.append('\'');
876
877    final Control[] controls = getControls();
878    if (controls.length > 0)
879    {
880      buffer.append(", controls={");
881      for (int i=0; i < controls.length; i++)
882      {
883        if (i > 0)
884        {
885          buffer.append(", ");
886        }
887
888        buffer.append(controls[i]);
889      }
890      buffer.append('}');
891    }
892
893    buffer.append(')');
894  }
895
896
897
898  /**
899   * {@inheritDoc}
900   */
901  public void toCode(final List<String> lineList, final String requestID,
902                     final int indentSpaces, final boolean includeProcessing)
903  {
904    // Create the request variable.
905    final ArrayList<ToCodeArgHelper> constructorArgs =
906         new ArrayList<ToCodeArgHelper>(3);
907    constructorArgs.add(ToCodeArgHelper.createString(bindDN.stringValue(),
908         "Bind DN"));
909    constructorArgs.add(ToCodeArgHelper.createString("---redacted-password---",
910         "Bind Password"));
911
912    final Control[] controls = getControls();
913    if (controls.length > 0)
914    {
915      constructorArgs.add(ToCodeArgHelper.createControlArray(controls,
916           "Bind Controls"));
917    }
918
919    ToCodeHelper.generateMethodCall(lineList, indentSpaces, "SimpleBindRequest",
920         requestID + "Request", "new SimpleBindRequest", constructorArgs);
921
922
923    // Add lines for processing the request and obtaining the result.
924    if (includeProcessing)
925    {
926      // Generate a string with the appropriate indent.
927      final StringBuilder buffer = new StringBuilder();
928      for (int i=0; i < indentSpaces; i++)
929      {
930        buffer.append(' ');
931      }
932      final String indent = buffer.toString();
933
934      lineList.add("");
935      lineList.add(indent + "try");
936      lineList.add(indent + '{');
937      lineList.add(indent + "  BindResult " + requestID +
938           "Result = connection.bind(" + requestID + "Request);");
939      lineList.add(indent + "  // The bind was processed successfully.");
940      lineList.add(indent + '}');
941      lineList.add(indent + "catch (LDAPException e)");
942      lineList.add(indent + '{');
943      lineList.add(indent + "  // The bind failed.  Maybe the following will " +
944           "help explain why.");
945      lineList.add(indent + "  // Note that the connection is now likely in " +
946           "an unauthenticated state.");
947      lineList.add(indent + "  ResultCode resultCode = e.getResultCode();");
948      lineList.add(indent + "  String message = e.getMessage();");
949      lineList.add(indent + "  String matchedDN = e.getMatchedDN();");
950      lineList.add(indent + "  String[] referralURLs = e.getReferralURLs();");
951      lineList.add(indent + "  Control[] responseControls = " +
952           "e.getResponseControls();");
953      lineList.add(indent + '}');
954    }
955  }
956}