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