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