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