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 com.unboundid.util.Debug;
041import com.unboundid.util.LDAPSDKException;
042import com.unboundid.util.NotExtensible;
043import com.unboundid.util.NotMutable;
044import com.unboundid.util.StaticUtils;
045import com.unboundid.util.ThreadSafety;
046import com.unboundid.util.ThreadSafetyLevel;
047
048
049
050/**
051 * This class defines an exception that can be thrown if a problem occurs while
052 * performing LDAP-related processing.  An LDAP exception can include all of
053 * the elements of an {@link LDAPResult}, so that all of the response elements
054 * will be available.
055 */
056@NotExtensible()
057@NotMutable()
058@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
059public class LDAPException
060       extends LDAPSDKException
061{
062  /**
063   * The serial version UID for this serializable class.
064   */
065  private static final long serialVersionUID = -4257171063946350327L;
066
067
068
069  /**
070   * An empty array that will be used when no controls were provided.
071   */
072  protected static final Control[] NO_CONTROLS = StaticUtils.NO_CONTROLS;
073
074
075
076  /**
077   * An empty array that will be used when no referrals were provided.
078   */
079  protected static final String[] NO_REFERRALS = StaticUtils.NO_STRINGS;
080
081
082
083  // The set of response controls for this LDAP exception.
084  private final Control[] responseControls;
085
086  // The result code for this LDAP exception.
087  private final ResultCode resultCode;
088
089  // The set of referral URLs for this LDAP exception.
090  private final String[] referralURLs;
091
092  // The diagnostic message returned by the directory server.
093  private final String diagnosticMessage;
094
095  // The matched DN for this LDAP exception.
096  private final String matchedDN;
097
098
099
100  /**
101   * Creates a new LDAP exception with the provided result code.  A default
102   * message (based on the result code) will be used.
103   *
104   * @param  resultCode  The result code for this LDAP exception.
105   */
106  public LDAPException(final ResultCode resultCode)
107  {
108    super(resultCode.getName());
109
110    this.resultCode = resultCode;
111
112    matchedDN         = null;
113    diagnosticMessage = null;
114    referralURLs      = NO_REFERRALS;
115    responseControls  = NO_CONTROLS;
116  }
117
118
119
120  /**
121   * Creates a new LDAP exception with the provided result code.  A default
122   * message (based on the result code) will be used.
123   *
124   * @param  resultCode  The result code for this LDAP exception.
125   * @param  cause       The underlying exception that triggered this exception.
126   */
127  public LDAPException(final ResultCode resultCode, final Throwable cause)
128  {
129    super(resultCode.getName(), cause);
130
131    this.resultCode = resultCode;
132
133    matchedDN         = null;
134    diagnosticMessage = null;
135    referralURLs      = NO_REFERRALS;
136    responseControls  = NO_CONTROLS;
137  }
138
139
140
141  /**
142   * Creates a new LDAP exception with the provided result code and message.
143   *
144   * @param  resultCode    The result code for this LDAP exception.
145   * @param  errorMessage  The error message for this LDAP exception.
146   */
147  public LDAPException(final ResultCode resultCode, final String errorMessage)
148  {
149    super(errorMessage);
150
151    this.resultCode = resultCode;
152
153    matchedDN         = null;
154    diagnosticMessage = null;
155    referralURLs      = NO_REFERRALS;
156    responseControls  = NO_CONTROLS;
157  }
158
159
160
161  /**
162   * Creates a new LDAP exception with the provided result code and message.
163   *
164   * @param  resultCode    The result code for this LDAP exception.
165   * @param  errorMessage  The error message for this LDAP exception.
166   * @param  cause         The underlying exception that triggered this
167   *                       exception.
168   */
169  public LDAPException(final ResultCode resultCode, final String errorMessage,
170                       final Throwable cause)
171  {
172    super(errorMessage, cause);
173
174    this.resultCode = resultCode;
175
176    matchedDN         = null;
177    diagnosticMessage = null;
178    referralURLs      = NO_REFERRALS;
179    responseControls  = NO_CONTROLS;
180  }
181
182
183
184  /**
185   * Creates a new LDAP exception with the provided information.
186   *
187   * @param  resultCode    The result code for this LDAP exception.
188   * @param  errorMessage  The error message for this LDAP exception.
189   * @param  matchedDN     The matched DN for this LDAP exception.
190   * @param  referralURLs  The set of referral URLs for this LDAP exception.
191   */
192  public LDAPException(final ResultCode resultCode, final String errorMessage,
193                       final String matchedDN, final String[] referralURLs)
194  {
195    super(errorMessage);
196
197    this.resultCode = resultCode;
198    this.matchedDN  = matchedDN;
199
200    if (referralURLs == null)
201    {
202      this.referralURLs = NO_REFERRALS;
203    }
204    else
205    {
206      this.referralURLs = referralURLs;
207    }
208
209    diagnosticMessage = null;
210    responseControls  = NO_CONTROLS;
211  }
212
213
214
215  /**
216   * Creates a new LDAP exception with the provided information.
217   *
218   * @param  resultCode    The result code for this LDAP exception.
219   * @param  errorMessage  The error message for this LDAP exception.
220   * @param  matchedDN     The matched DN for this LDAP exception.
221   * @param  referralURLs  The set of referral URLs for this LDAP exception.
222   * @param  cause         The underlying exception that triggered this
223   *                       exception.
224   */
225  public LDAPException(final ResultCode resultCode, final String errorMessage,
226                       final String matchedDN, final String[] referralURLs,
227                       final Throwable cause)
228  {
229    super(errorMessage, cause);
230
231    this.resultCode = resultCode;
232    this.matchedDN  = matchedDN;
233
234    if (referralURLs == null)
235    {
236      this.referralURLs = NO_REFERRALS;
237    }
238    else
239    {
240      this.referralURLs = referralURLs;
241    }
242
243    diagnosticMessage = null;
244    responseControls  = NO_CONTROLS;
245  }
246
247
248
249  /**
250   * Creates a new LDAP exception with the provided information.
251   *
252   * @param  resultCode    The result code for this LDAP exception.
253   * @param  errorMessage  The error message for this LDAP exception.
254   * @param  matchedDN     The matched DN for this LDAP exception.
255   * @param  referralURLs  The set of referral URLs for this LDAP exception.
256   * @param  controls      The set of response controls for this LDAP exception.
257   */
258  public LDAPException(final ResultCode resultCode, final String errorMessage,
259                       final String matchedDN, final String[] referralURLs,
260                       final Control[] controls)
261  {
262    super(errorMessage);
263
264    this.resultCode = resultCode;
265    this.matchedDN  = matchedDN;
266
267    diagnosticMessage = null;
268
269    if (referralURLs == null)
270    {
271      this.referralURLs = NO_REFERRALS;
272    }
273    else
274    {
275      this.referralURLs = referralURLs;
276    }
277
278    if (controls == null)
279    {
280      responseControls = NO_CONTROLS;
281    }
282    else
283    {
284      responseControls = controls;
285    }
286  }
287
288
289
290  /**
291   * Creates a new LDAP exception with the provided information.
292   *
293   * @param  resultCode    The result code for this LDAP exception.
294   * @param  errorMessage  The error message for this LDAP exception.
295   * @param  matchedDN     The matched DN for this LDAP exception.
296   * @param  referralURLs  The set of referral URLs for this LDAP exception.
297   * @param  controls      The set of response controls for this LDAP exception.
298   * @param  cause         The underlying exception that triggered this
299   *                       exception.
300   */
301  public LDAPException(final ResultCode resultCode, final String errorMessage,
302                       final String matchedDN, final String[] referralURLs,
303                       final Control[] controls, final Throwable cause)
304  {
305    super(errorMessage, cause);
306
307    this.resultCode = resultCode;
308    this.matchedDN  = matchedDN;
309
310    diagnosticMessage = null;
311
312    if (referralURLs == null)
313    {
314      this.referralURLs = NO_REFERRALS;
315    }
316    else
317    {
318      this.referralURLs = referralURLs;
319    }
320
321    if (controls == null)
322    {
323      responseControls = NO_CONTROLS;
324    }
325    else
326    {
327      responseControls = controls;
328    }
329  }
330
331
332
333  /**
334   * Creates a new LDAP exception using the information contained in the
335   * provided LDAP result object.
336   *
337   * @param  ldapResult  The LDAP result object containing the information to
338   *                     use for this LDAP exception.
339   */
340  public LDAPException(final LDAPResult ldapResult)
341  {
342    super((ldapResult.getDiagnosticMessage() == null)
343          ? ldapResult.getResultCode().getName()
344          : ldapResult.getDiagnosticMessage());
345
346    resultCode        = ldapResult.getResultCode();
347    matchedDN         = ldapResult.getMatchedDN();
348    diagnosticMessage = ldapResult.getDiagnosticMessage();
349    referralURLs      = ldapResult.getReferralURLs();
350    responseControls  = ldapResult.getResponseControls();
351  }
352
353
354
355  /**
356   * Creates a new LDAP exception using the information contained in the
357   * provided LDAP result object.
358   *
359   * @param  ldapResult  The LDAP result object containing the information to
360   *                     use for this LDAP exception.
361   * @param  cause       The underlying exception that triggered this exception.
362   */
363  public LDAPException(final LDAPResult ldapResult, final Throwable cause)
364  {
365    super(((ldapResult.getDiagnosticMessage() == null)
366           ? ldapResult.getResultCode().getName()
367           : ldapResult.getDiagnosticMessage()),
368          cause);
369
370    resultCode        = ldapResult.getResultCode();
371    matchedDN         = ldapResult.getMatchedDN();
372    diagnosticMessage = ldapResult.getDiagnosticMessage();
373    referralURLs      = ldapResult.getReferralURLs();
374    responseControls  = ldapResult.getResponseControls();
375  }
376
377
378
379  /**
380   * Creates a new LDAP exception using the information contained in the
381   * provided LDAP exception.
382   *
383   * @param  e  The LDAP exception to use to create this exception.
384   */
385  public LDAPException(final LDAPException e)
386  {
387    super(e.getMessage(), e.getCause());
388
389    resultCode        = e.getResultCode();
390    matchedDN         = e.getMatchedDN();
391    diagnosticMessage = e.getDiagnosticMessage();
392    referralURLs      = e.getReferralURLs();
393    responseControls  = e.getResponseControls();
394  }
395
396
397
398  /**
399   * Retrieves the result code for this LDAP exception.
400   *
401   * @return  The result code for this LDAP exception.
402   */
403  public final ResultCode getResultCode()
404  {
405    return resultCode;
406  }
407
408
409
410  /**
411   * Retrieves the matched DN for this LDAP exception.
412   *
413   * @return  The matched DN for this LDAP exception, or {@code null} if there
414   *          is none.
415   */
416  public final String getMatchedDN()
417  {
418    return matchedDN;
419  }
420
421
422
423  /**
424   * Retrieves the diagnostic message returned by the directory server.
425   *
426   * @return  The diagnostic message returned by the directory server, or
427   *          {@code null} if there is none.
428   */
429  public final String getDiagnosticMessage()
430  {
431    return diagnosticMessage;
432  }
433
434
435
436  /**
437   * Retrieves the set of referral URLs for this LDAP exception.
438   *
439   * @return  The set of referral URLs for this LDAP exception, or an empty
440   *          array if there are none.
441   */
442  public final String[] getReferralURLs()
443  {
444    return referralURLs;
445  }
446
447
448
449  /**
450   * Indicates whether this result contains at least one control.
451   *
452   * @return  {@code true} if this result contains at least one control, or
453   *          {@code false} if not.
454   */
455  public final boolean hasResponseControl()
456  {
457    return (responseControls.length > 0);
458  }
459
460
461
462  /**
463   * Indicates whether this result contains at least one control with the
464   * specified OID.
465   *
466   * @param  oid  The object identifier for which to make the determination.  It
467   *              must not be {@code null}.
468   *
469   * @return  {@code true} if this result contains at least one control with
470   *          the specified OID, or {@code false} if not.
471   */
472  public final boolean hasResponseControl(final String oid)
473  {
474    for (final Control c : responseControls)
475    {
476      if (c.getOID().equals(oid))
477      {
478        return true;
479      }
480    }
481
482    return false;
483  }
484
485
486
487  /**
488   * Retrieves the set of response controls for this LDAP exception.
489   * Individual response controls of a specific type may be retrieved and
490   * decoded using the {@code get} method in the response control class, using
491   * the {@link #toLDAPResult()} method to convert this exception to an
492   * {@link LDAPResult}.
493   *
494   * @return  The set of response controls for this LDAP exception, or an empty
495   *          array if there are none.
496   */
497  public final Control[] getResponseControls()
498  {
499    return responseControls;
500  }
501
502
503
504  /**
505   * Retrieves the response control with the specified OID.
506   *
507   * @param  oid  The OID of the control to retrieve.
508   *
509   * @return  The response control with the specified OID, or {@code null} if
510   *          there is no such control.
511   */
512  public final Control getResponseControl(final String oid)
513  {
514    for (final Control c : responseControls)
515    {
516      if (c.getOID().equals(oid))
517      {
518        return c;
519      }
520    }
521
522    return null;
523  }
524
525
526
527  /**
528   * Creates a new {@code LDAPResult} object from this exception.
529   *
530   * @return  The {@code LDAPResult} object created from this exception.
531   */
532  public LDAPResult toLDAPResult()
533  {
534    if ((diagnosticMessage == null) && (getMessage() != null))
535    {
536      return new LDAPResult(-1, resultCode, getMessage(), matchedDN,
537           referralURLs, responseControls);
538    }
539    else
540    {
541      return new LDAPResult(-1, resultCode, diagnosticMessage, matchedDN,
542           referralURLs, responseControls);
543    }
544  }
545
546
547
548  /**
549   * Retrieves a string representation of this LDAP result, consisting of
550   * the result code, diagnostic message (if present), matched DN (if present),
551   * and referral URLs (if present).
552   *
553   * @return  A string representation of this LDAP result.
554   */
555  public String getResultString()
556  {
557    final StringBuilder buffer = new StringBuilder();
558    buffer.append("result code='");
559    buffer.append(resultCode);
560    buffer.append('\'');
561
562    if ((diagnosticMessage != null) && (! diagnosticMessage.isEmpty()))
563    {
564      buffer.append(" diagnostic message='");
565      buffer.append(diagnosticMessage);
566      buffer.append('\'');
567    }
568
569    if ((matchedDN != null) && (! matchedDN.isEmpty()))
570    {
571      buffer.append("  matched DN='");
572      buffer.append(matchedDN);
573      buffer.append('\'');
574    }
575
576    if ((referralURLs != null) && (referralURLs.length > 0))
577    {
578      buffer.append("  referral URLs={");
579
580      for (int i=0; i < referralURLs.length; i++)
581      {
582        if (i > 0)
583        {
584          buffer.append(", ");
585        }
586
587        buffer.append('\'');
588        buffer.append(referralURLs[i]);
589        buffer.append('\'');
590      }
591
592      buffer.append('}');
593    }
594
595    return buffer.toString();
596  }
597
598
599
600  /**
601   * {@inheritDoc}
602   */
603  @Override()
604  public void toString(final StringBuilder buffer)
605  {
606    final boolean includeCause =
607         Boolean.getBoolean(Debug.PROPERTY_INCLUDE_CAUSE_IN_EXCEPTION_MESSAGES);
608    final boolean includeStackTrace = Boolean.getBoolean(
609         Debug.PROPERTY_INCLUDE_STACK_TRACE_IN_EXCEPTION_MESSAGES);
610
611    toString(buffer, includeCause, includeStackTrace);
612  }
613
614
615
616  /**
617   * Appends a string representation of this {@code LDAPException} to the
618   * provided buffer.
619   *
620   * @param  buffer             The buffer to which the information should be
621   *                            appended.  This must not be {@code null}.
622   * @param  includeCause       Indicates whether to include information about
623   *                            the cause (if any) in the exception message.
624   * @param  includeStackTrace  Indicates whether to include a condensed
625   *                            representation of the stack trace in the
626   *                            exception message.  If a stack trace is
627   *                            included, then the cause (if any) will
628   *                            automatically be included, regardless of the
629   *                            value of the {@code includeCause} argument.
630   */
631  public void toString(final StringBuilder buffer, final boolean includeCause,
632                       final boolean includeStackTrace)
633  {
634    buffer.append("LDAPException(resultCode=");
635    buffer.append(resultCode);
636
637    final String errorMessage = getMessage();
638    if ((errorMessage != null) && (! errorMessage.equals(diagnosticMessage)))
639    {
640      buffer.append(", errorMessage='");
641      buffer.append(errorMessage);
642      buffer.append('\'');
643    }
644
645    if (diagnosticMessage != null)
646    {
647      buffer.append(", diagnosticMessage='");
648      buffer.append(diagnosticMessage);
649      buffer.append('\'');
650    }
651
652    if (matchedDN != null)
653    {
654      buffer.append(", matchedDN='");
655      buffer.append(matchedDN);
656      buffer.append('\'');
657    }
658
659    if (referralURLs.length > 0)
660    {
661      buffer.append(", referralURLs={");
662
663      for (int i=0; i < referralURLs.length; i++)
664      {
665        if (i > 0)
666        {
667          buffer.append(", ");
668        }
669
670        buffer.append('\'');
671        buffer.append(referralURLs[i]);
672        buffer.append('\'');
673      }
674
675      buffer.append('}');
676    }
677
678    if (responseControls.length > 0)
679    {
680      buffer.append(", responseControls={");
681
682      for (int i=0; i < responseControls.length; i++)
683      {
684        if (i > 0)
685        {
686          buffer.append(", ");
687        }
688
689        buffer.append(responseControls[i]);
690      }
691
692      buffer.append('}');
693    }
694
695    if (includeStackTrace)
696    {
697      buffer.append(", trace='");
698      StaticUtils.getStackTrace(getStackTrace(), buffer);
699      buffer.append('\'');
700    }
701
702    if (includeCause || includeStackTrace)
703    {
704      final Throwable cause = getCause();
705      if (cause != null)
706      {
707        buffer.append(", cause=");
708        buffer.append(StaticUtils.getExceptionMessage(cause, true,
709             includeStackTrace));
710      }
711    }
712
713    final String ldapSDKVersionString = ", ldapSDKVersion=" +
714         Version.NUMERIC_VERSION_STRING + ", revision=" + Version.REVISION_ID;
715    if (buffer.indexOf(ldapSDKVersionString) < 0)
716    {
717      buffer.append(ldapSDKVersionString);
718    }
719
720    buffer.append(')');
721  }
722
723
724
725  /**
726   * {@inheritDoc}
727   */
728  @Override()
729  public final String getExceptionMessage()
730  {
731    return toString();
732  }
733
734
735
736  /**
737   * {@inheritDoc}
738   */
739  @Override()
740  public final String getExceptionMessage(final boolean includeCause,
741                                          final boolean includeStackTrace)
742  {
743    final StringBuilder buffer = new StringBuilder();
744    toString(buffer, includeCause, includeStackTrace);
745    return buffer.toString();
746  }
747}