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     */
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    import com.unboundid.util.NotMutable;
043    import com.unboundid.util.ThreadSafety;
044    import com.unboundid.util.ThreadSafetyLevel;
045    
046    import static com.unboundid.ldap.sdk.LDAPMessages.*;
047    import static com.unboundid.util.Debug.*;
048    import 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)
058    public 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    }