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}