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;
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    if (connection.synchronousMode())
517    {
518      @SuppressWarnings("deprecation")
519      final boolean autoReconnect =
520           connection.getConnectionOptions().autoReconnect();
521      return processSync(connection, autoReconnect);
522    }
523
524    // See if a bind DN was provided without a password.  If that is the case
525    // and this should not be allowed, then throw an exception.
526    if (password != null)
527    {
528      if ((bindDN.getValue().length > 0) && (password.getValue().length == 0) &&
529           connection.getConnectionOptions().bindWithDNRequiresPassword())
530      {
531        final LDAPException le = new LDAPException(ResultCode.PARAM_ERROR,
532             ERR_SIMPLE_BIND_DN_WITHOUT_PASSWORD.get());
533        debugCodingError(le);
534        throw le;
535      }
536    }
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      debugLDAPRequest(Level.INFO, this, messageID, connection);
553      final long requestTime = System.nanoTime();
554      connection.getConnectionStatistics().incrementNumBindRequests();
555      connection.sendMessage(message);
556
557      // Wait for and process the response.
558      final LDAPResponse response;
559      try
560      {
561        final long responseTimeout = getResponseTimeoutMillis(connection);
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    // Set the appropriate timeout on the socket.
617    try
618    {
619      InternalSDKHelper.setSoTimeout(connection,
620           (int) getResponseTimeoutMillis(connection));
621    }
622    catch (final Exception e)
623    {
624      debugException(e);
625    }
626
627
628    // Send the request to the server.
629    final long requestTime = System.nanoTime();
630    debugLDAPRequest(Level.INFO, this, messageID, connection);
631    connection.getConnectionStatistics().incrementNumBindRequests();
632    try
633    {
634      connection.sendMessage(message);
635    }
636    catch (final LDAPException le)
637    {
638      debugException(le);
639
640      if (allowRetry)
641      {
642        final BindResult bindResult = reconnectAndRetry(connection,
643             le.getResultCode());
644        if (bindResult != null)
645        {
646          return bindResult;
647        }
648      }
649
650      throw le;
651    }
652
653    while (true)
654    {
655      final LDAPResponse response = connection.readResponse(messageID);
656      if (response instanceof IntermediateResponse)
657      {
658        final IntermediateResponseListener listener =
659             getIntermediateResponseListener();
660        if (listener != null)
661        {
662          listener.intermediateResponseReturned(
663               (IntermediateResponse) response);
664        }
665      }
666      else
667      {
668        return handleResponse(connection, response, requestTime, allowRetry);
669      }
670    }
671  }
672
673
674
675  /**
676   * Performs the necessary processing for handling a response.
677   *
678   * @param  connection   The connection used to read the response.
679   * @param  response     The response to be processed.
680   * @param  requestTime  The time the request was sent to the server.
681   * @param  allowRetry   Indicates whether the request may be re-tried on a
682   *                      re-established connection if the initial attempt fails
683   *                      in a way that indicates the connection is no longer
684   *                      valid and autoReconnect is true.
685   *
686   * @return  The bind result.
687   *
688   * @throws  LDAPException  If a problem occurs.
689   */
690  private BindResult handleResponse(final LDAPConnection connection,
691                                    final LDAPResponse response,
692                                    final long requestTime,
693                                    final boolean allowRetry)
694          throws LDAPException
695  {
696    if (response == null)
697    {
698      final long waitTime = nanosToMillis(System.nanoTime() - requestTime);
699      throw new LDAPException(ResultCode.TIMEOUT,
700           ERR_SIMPLE_BIND_CLIENT_TIMEOUT.get(waitTime, messageID,
701                bindDN.stringValue(), connection.getHostPort()));
702    }
703
704    connection.getConnectionStatistics().incrementNumBindResponses(
705         System.nanoTime() - requestTime);
706    if (response instanceof ConnectionClosedResponse)
707    {
708      // The connection was closed while waiting for the response.
709      if (allowRetry)
710      {
711        final BindResult retryResult = reconnectAndRetry(connection,
712             ResultCode.SERVER_DOWN);
713        if (retryResult != null)
714        {
715          return retryResult;
716        }
717      }
718
719      final ConnectionClosedResponse ccr = (ConnectionClosedResponse) response;
720      final String message = ccr.getMessage();
721      if (message == null)
722      {
723        throw new LDAPException(ccr.getResultCode(),
724             ERR_CONN_CLOSED_WAITING_FOR_BIND_RESPONSE.get(
725                  connection.getHostPort(), toString()));
726      }
727      else
728      {
729        throw new LDAPException(ccr.getResultCode(),
730             ERR_CONN_CLOSED_WAITING_FOR_BIND_RESPONSE_WITH_MESSAGE.get(
731                  connection.getHostPort(), toString(), message));
732      }
733    }
734
735    final BindResult bindResult = (BindResult) response;
736    if (allowRetry)
737    {
738      final BindResult retryResult = reconnectAndRetry(connection,
739           bindResult.getResultCode());
740      if (retryResult != null)
741      {
742        return retryResult;
743      }
744    }
745
746    return bindResult;
747  }
748
749
750
751  /**
752   * Attempts to re-establish the connection and retry processing this request
753   * on it.
754   *
755   * @param  connection  The connection to be re-established.
756   * @param  resultCode  The result code for the previous operation attempt.
757   *
758   * @return  The result from re-trying the bind, or {@code null} if it could
759   *          not be re-tried.
760   */
761  private BindResult reconnectAndRetry(final LDAPConnection connection,
762                                       final ResultCode resultCode)
763  {
764    try
765    {
766      // We will only want to retry for certain result codes that indicate a
767      // connection problem.
768      switch (resultCode.intValue())
769      {
770        case ResultCode.SERVER_DOWN_INT_VALUE:
771        case ResultCode.DECODING_ERROR_INT_VALUE:
772        case ResultCode.CONNECT_ERROR_INT_VALUE:
773          connection.reconnect();
774          return processSync(connection, false);
775      }
776    }
777    catch (final Exception e)
778    {
779      debugException(e);
780    }
781
782    return null;
783  }
784
785
786
787  /**
788   * {@inheritDoc}
789   */
790  @Override()
791  public SimpleBindRequest getRebindRequest(final String host, final int port)
792  {
793    return new SimpleBindRequest(bindDN, password, passwordProvider,
794         getControls());
795  }
796
797
798
799  /**
800   * {@inheritDoc}
801   */
802  @InternalUseOnly()
803  @Override()
804  public void responseReceived(final LDAPResponse response)
805         throws LDAPException
806  {
807    try
808    {
809      responseQueue.put(response);
810    }
811    catch (final Exception e)
812    {
813      debugException(e);
814
815      if (e instanceof InterruptedException)
816      {
817        Thread.currentThread().interrupt();
818      }
819
820      throw new LDAPException(ResultCode.LOCAL_ERROR,
821           ERR_EXCEPTION_HANDLING_RESPONSE.get(getExceptionMessage(e)), e);
822    }
823  }
824
825
826
827  /**
828   * {@inheritDoc}
829   */
830  @Override()
831  public String getBindType()
832  {
833    return "SIMPLE";
834  }
835
836
837
838  /**
839   * {@inheritDoc}
840   */
841  @Override()
842  public int getLastMessageID()
843  {
844    return messageID;
845  }
846
847
848
849  /**
850   * {@inheritDoc}
851   */
852  @Override()
853  public SimpleBindRequest duplicate()
854  {
855    return duplicate(getControls());
856  }
857
858
859
860  /**
861   * {@inheritDoc}
862   */
863  @Override()
864  public SimpleBindRequest duplicate(final Control[] controls)
865  {
866    final SimpleBindRequest bindRequest =
867         new SimpleBindRequest(bindDN, password, passwordProvider, controls);
868    bindRequest.setResponseTimeoutMillis(getResponseTimeoutMillis(null));
869    return bindRequest;
870  }
871
872
873
874  /**
875   * {@inheritDoc}
876   */
877  @Override()
878  public void toString(final StringBuilder buffer)
879  {
880    buffer.append("SimpleBindRequest(dn='");
881    buffer.append(bindDN);
882    buffer.append('\'');
883
884    final Control[] controls = getControls();
885    if (controls.length > 0)
886    {
887      buffer.append(", controls={");
888      for (int i=0; i < controls.length; i++)
889      {
890        if (i > 0)
891        {
892          buffer.append(", ");
893        }
894
895        buffer.append(controls[i]);
896      }
897      buffer.append('}');
898    }
899
900    buffer.append(')');
901  }
902
903
904
905  /**
906   * {@inheritDoc}
907   */
908  @Override()
909  public void toCode(final List<String> lineList, final String requestID,
910                     final int indentSpaces, final boolean includeProcessing)
911  {
912    // Create the request variable.
913    final ArrayList<ToCodeArgHelper> constructorArgs =
914         new ArrayList<ToCodeArgHelper>(3);
915    constructorArgs.add(ToCodeArgHelper.createString(bindDN.stringValue(),
916         "Bind DN"));
917    constructorArgs.add(ToCodeArgHelper.createString("---redacted-password---",
918         "Bind Password"));
919
920    final Control[] controls = getControls();
921    if (controls.length > 0)
922    {
923      constructorArgs.add(ToCodeArgHelper.createControlArray(controls,
924           "Bind Controls"));
925    }
926
927    ToCodeHelper.generateMethodCall(lineList, indentSpaces, "SimpleBindRequest",
928         requestID + "Request", "new SimpleBindRequest", constructorArgs);
929
930
931    // Add lines for processing the request and obtaining the result.
932    if (includeProcessing)
933    {
934      // Generate a string with the appropriate indent.
935      final StringBuilder buffer = new StringBuilder();
936      for (int i=0; i < indentSpaces; i++)
937      {
938        buffer.append(' ');
939      }
940      final String indent = buffer.toString();
941
942      lineList.add("");
943      lineList.add(indent + "try");
944      lineList.add(indent + '{');
945      lineList.add(indent + "  BindResult " + requestID +
946           "Result = connection.bind(" + requestID + "Request);");
947      lineList.add(indent + "  // The bind was processed successfully.");
948      lineList.add(indent + '}');
949      lineList.add(indent + "catch (LDAPException e)");
950      lineList.add(indent + '{');
951      lineList.add(indent + "  // The bind failed.  Maybe the following will " +
952           "help explain why.");
953      lineList.add(indent + "  // Note that the connection is now likely in " +
954           "an unauthenticated state.");
955      lineList.add(indent + "  ResultCode resultCode = e.getResultCode();");
956      lineList.add(indent + "  String message = e.getMessage();");
957      lineList.add(indent + "  String matchedDN = e.getMatchedDN();");
958      lineList.add(indent + "  String[] referralURLs = e.getReferralURLs();");
959      lineList.add(indent + "  Control[] responseControls = " +
960           "e.getResponseControls();");
961      lineList.add(indent + '}');
962    }
963  }
964}