001/*
002 * Copyright 2007-2016 UnboundID Corp.
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2008-2016 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        throw new LDAPException(ResultCode.LOCAL_ERROR,
571             ERR_BIND_INTERRUPTED.get(connection.getHostPort()), ie);
572      }
573
574      return handleResponse(connection, response, requestTime, false);
575    }
576    finally
577    {
578      connection.deregisterResponseAcceptor(messageID);
579    }
580  }
581
582
583
584  /**
585   * Processes this bind operation in synchronous mode, in which the same
586   * thread will send the request and read the response.
587   *
588   * @param  connection  The connection to use to communicate with the directory
589   *                     server.
590   * @param  allowRetry  Indicates whether the request may be re-tried on a
591   *                     re-established connection if the initial attempt fails
592   *                     in a way that indicates the connection is no longer
593   *                     valid and autoReconnect is true.
594   *
595   * @return  An LDAP result object that provides information about the result
596   *          of the bind processing.
597   *
598   * @throws  LDAPException  If a problem occurs while sending the request or
599   *                         reading the response.
600   */
601  private BindResult processSync(final LDAPConnection connection,
602                                 final boolean allowRetry)
603          throws LDAPException
604  {
605    // Create the LDAP message.
606    messageID = connection.nextMessageID();
607    final LDAPMessage message =
608         new LDAPMessage(messageID, this, getControls());
609
610
611    // Set the appropriate timeout on the socket.
612    try
613    {
614      connection.getConnectionInternals(true).getSocket().setSoTimeout(
615           (int) getResponseTimeoutMillis(connection));
616    }
617    catch (Exception e)
618    {
619      debugException(e);
620    }
621
622
623    // Send the request to the server.
624    final long requestTime = System.nanoTime();
625    debugLDAPRequest(this);
626    connection.getConnectionStatistics().incrementNumBindRequests();
627    try
628    {
629      connection.sendMessage(message);
630    }
631    catch (final LDAPException le)
632    {
633      debugException(le);
634
635      if (allowRetry)
636      {
637        final BindResult bindResult = reconnectAndRetry(connection,
638             le.getResultCode());
639        if (bindResult != null)
640        {
641          return bindResult;
642        }
643      }
644    }
645
646    while (true)
647    {
648      final LDAPResponse response = connection.readResponse(messageID);
649      if (response instanceof IntermediateResponse)
650      {
651        final IntermediateResponseListener listener =
652             getIntermediateResponseListener();
653        if (listener != null)
654        {
655          listener.intermediateResponseReturned(
656               (IntermediateResponse) response);
657        }
658      }
659      else
660      {
661        return handleResponse(connection, response, requestTime, allowRetry);
662      }
663    }
664  }
665
666
667
668  /**
669   * Performs the necessary processing for handling a response.
670   *
671   * @param  connection   The connection used to read the response.
672   * @param  response     The response to be processed.
673   * @param  requestTime  The time the request was sent to the server.
674   * @param  allowRetry   Indicates whether the request may be re-tried on a
675   *                      re-established connection if the initial attempt fails
676   *                      in a way that indicates the connection is no longer
677   *                      valid and autoReconnect is true.
678   *
679   * @return  The bind result.
680   *
681   * @throws  LDAPException  If a problem occurs.
682   */
683  private BindResult handleResponse(final LDAPConnection connection,
684                                    final LDAPResponse response,
685                                    final long requestTime,
686                                    final boolean allowRetry)
687          throws LDAPException
688  {
689    if (response == null)
690    {
691      final long waitTime = nanosToMillis(System.nanoTime() - requestTime);
692      throw new LDAPException(ResultCode.TIMEOUT,
693           ERR_SIMPLE_BIND_CLIENT_TIMEOUT.get(waitTime, messageID,
694                bindDN.stringValue(), connection.getHostPort()));
695    }
696
697    connection.getConnectionStatistics().incrementNumBindResponses(
698         System.nanoTime() - requestTime);
699    if (response instanceof ConnectionClosedResponse)
700    {
701      // The connection was closed while waiting for the response.
702      if (allowRetry)
703      {
704        final BindResult retryResult = reconnectAndRetry(connection,
705             ResultCode.SERVER_DOWN);
706        if (retryResult != null)
707        {
708          return retryResult;
709        }
710      }
711
712      final ConnectionClosedResponse ccr = (ConnectionClosedResponse) response;
713      final String message = ccr.getMessage();
714      if (message == null)
715      {
716        throw new LDAPException(ccr.getResultCode(),
717             ERR_CONN_CLOSED_WAITING_FOR_BIND_RESPONSE.get(
718                  connection.getHostPort(), toString()));
719      }
720      else
721      {
722        throw new LDAPException(ccr.getResultCode(),
723             ERR_CONN_CLOSED_WAITING_FOR_BIND_RESPONSE_WITH_MESSAGE.get(
724                  connection.getHostPort(), toString(), message));
725      }
726    }
727
728    final BindResult bindResult = (BindResult) response;
729    if (allowRetry)
730    {
731      final BindResult retryResult = reconnectAndRetry(connection,
732           bindResult.getResultCode());
733      if (retryResult != null)
734      {
735        return retryResult;
736      }
737    }
738
739    return bindResult;
740  }
741
742
743
744  /**
745   * Attempts to re-establish the connection and retry processing this request
746   * on it.
747   *
748   * @param  connection  The connection to be re-established.
749   * @param  resultCode  The result code for the previous operation attempt.
750   *
751   * @return  The result from re-trying the bind, or {@code null} if it could
752   *          not be re-tried.
753   */
754  private BindResult reconnectAndRetry(final LDAPConnection connection,
755                                       final ResultCode resultCode)
756  {
757    try
758    {
759      // We will only want to retry for certain result codes that indicate a
760      // connection problem.
761      switch (resultCode.intValue())
762      {
763        case ResultCode.SERVER_DOWN_INT_VALUE:
764        case ResultCode.DECODING_ERROR_INT_VALUE:
765        case ResultCode.CONNECT_ERROR_INT_VALUE:
766          connection.reconnect();
767          return processSync(connection, false);
768      }
769    }
770    catch (final Exception e)
771    {
772      debugException(e);
773    }
774
775    return null;
776  }
777
778
779
780  /**
781   * {@inheritDoc}
782   */
783  @Override()
784  public SimpleBindRequest getRebindRequest(final String host, final int port)
785  {
786    return new SimpleBindRequest(bindDN, password, passwordProvider,
787         getControls());
788  }
789
790
791
792  /**
793   * {@inheritDoc}
794   */
795  @InternalUseOnly()
796  public void responseReceived(final LDAPResponse response)
797         throws LDAPException
798  {
799    try
800    {
801      responseQueue.put(response);
802    }
803    catch (Exception e)
804    {
805      debugException(e);
806      throw new LDAPException(ResultCode.LOCAL_ERROR,
807           ERR_EXCEPTION_HANDLING_RESPONSE.get(getExceptionMessage(e)), e);
808    }
809  }
810
811
812
813  /**
814   * {@inheritDoc}
815   */
816  @Override()
817  public String getBindType()
818  {
819    return "SIMPLE";
820  }
821
822
823
824  /**
825   * {@inheritDoc}
826   */
827  @Override()
828  public int getLastMessageID()
829  {
830    return messageID;
831  }
832
833
834
835  /**
836   * {@inheritDoc}
837   */
838  @Override()
839  public SimpleBindRequest duplicate()
840  {
841    return duplicate(getControls());
842  }
843
844
845
846  /**
847   * {@inheritDoc}
848   */
849  @Override()
850  public SimpleBindRequest duplicate(final Control[] controls)
851  {
852    final SimpleBindRequest bindRequest =
853         new SimpleBindRequest(bindDN, password, passwordProvider, controls);
854    bindRequest.setResponseTimeoutMillis(getResponseTimeoutMillis(null));
855    return bindRequest;
856  }
857
858
859
860  /**
861   * {@inheritDoc}
862   */
863  @Override()
864  public void toString(final StringBuilder buffer)
865  {
866    buffer.append("SimpleBindRequest(dn='");
867    buffer.append(bindDN);
868    buffer.append('\'');
869
870    final Control[] controls = getControls();
871    if (controls.length > 0)
872    {
873      buffer.append(", controls={");
874      for (int i=0; i < controls.length; i++)
875      {
876        if (i > 0)
877        {
878          buffer.append(", ");
879        }
880
881        buffer.append(controls[i]);
882      }
883      buffer.append('}');
884    }
885
886    buffer.append(')');
887  }
888
889
890
891  /**
892   * {@inheritDoc}
893   */
894  public void toCode(final List<String> lineList, final String requestID,
895                     final int indentSpaces, final boolean includeProcessing)
896  {
897    // Create the request variable.
898    final ArrayList<ToCodeArgHelper> constructorArgs =
899         new ArrayList<ToCodeArgHelper>(3);
900    constructorArgs.add(ToCodeArgHelper.createString(bindDN.stringValue(),
901         "Bind DN"));
902    constructorArgs.add(ToCodeArgHelper.createString("---redacted-password---",
903         "Bind Password"));
904
905    final Control[] controls = getControls();
906    if (controls.length > 0)
907    {
908      constructorArgs.add(ToCodeArgHelper.createControlArray(controls,
909           "Bind Controls"));
910    }
911
912    ToCodeHelper.generateMethodCall(lineList, indentSpaces, "SimpleBindRequest",
913         requestID + "Request", "new SimpleBindRequest", constructorArgs);
914
915
916    // Add lines for processing the request and obtaining the result.
917    if (includeProcessing)
918    {
919      // Generate a string with the appropriate indent.
920      final StringBuilder buffer = new StringBuilder();
921      for (int i=0; i < indentSpaces; i++)
922      {
923        buffer.append(' ');
924      }
925      final String indent = buffer.toString();
926
927      lineList.add("");
928      lineList.add(indent + "try");
929      lineList.add(indent + '{');
930      lineList.add(indent + "  BindResult " + requestID +
931           "Result = connection.bind(" + requestID + "Request);");
932      lineList.add(indent + "  // The bind was processed successfully.");
933      lineList.add(indent + '}');
934      lineList.add(indent + "catch (LDAPException e)");
935      lineList.add(indent + '{');
936      lineList.add(indent + "  // The bind failed.  Maybe the following will " +
937           "help explain why.");
938      lineList.add(indent + "  // Note that the connection is now likely in " +
939           "an unauthenticated state.");
940      lineList.add(indent + "  ResultCode resultCode = e.getResultCode();");
941      lineList.add(indent + "  String message = e.getMessage();");
942      lineList.add(indent + "  String matchedDN = e.getMatchedDN();");
943      lineList.add(indent + "  String[] referralURLs = e.getReferralURLs();");
944      lineList.add(indent + "  Control[] responseControls = " +
945           "e.getResponseControls();");
946      lineList.add(indent + '}');
947    }
948  }
949}