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