001/* 002 * Copyright 2007-2018 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2008-2018 Ping Identity Corporation 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU General Public License (GPLv2 only) 010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 011 * as published by the Free Software Foundation. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Public License for more details. 017 * 018 * You should have received a copy of the GNU General Public License 019 * along with this program; if not, see <http://www.gnu.org/licenses>. 020 */ 021package com.unboundid.ldap.sdk; 022 023 024 025import java.io.Serializable; 026import java.util.ArrayList; 027import java.util.List; 028 029import com.unboundid.asn1.ASN1Exception; 030import com.unboundid.asn1.ASN1StreamReader; 031import com.unboundid.asn1.ASN1StreamReaderSequence; 032import com.unboundid.ldap.protocol.LDAPMessage; 033import com.unboundid.ldap.protocol.LDAPResponse; 034import com.unboundid.util.Debug; 035import com.unboundid.util.Extensible; 036import com.unboundid.util.NotMutable; 037import com.unboundid.util.StaticUtils; 038import com.unboundid.util.ThreadSafety; 039import com.unboundid.util.ThreadSafetyLevel; 040 041import static com.unboundid.ldap.sdk.LDAPMessages.*; 042 043 044 045/** 046 * This class provides a data structure for holding the elements that are common 047 * to most types of LDAP responses. The elements contained in an LDAP result 048 * include: 049 * <UL> 050 * <LI>Result Code -- An integer value that provides information about the 051 * status of the operation. See the {@link ResultCode} class for 052 * information about a number of result codes defined in LDAP.</LI> 053 * <LI>Diagnostic Message -- An optional string that may provide additional 054 * information about the operation. For example, if the operation failed, 055 * it may include information about the reason for the failure. It will 056 * often (but not always) be absent in the result for successful 057 * operations, and it may be absent in the result for failed 058 * operations.</LI> 059 * <LI>Matched DN -- An optional DN which specifies the entry that most 060 * closely matched the DN of a non-existent entry in the server. For 061 * example, if an operation failed because the target entry did not exist, 062 * then the matched DN field may specify the DN of the closest ancestor 063 * to that entry that does exist in the server.</LI> 064 * <LI>Referral URLs -- An optional set of LDAP URLs which refer to other 065 * directories and/or locations within the DIT in which the operation may 066 * be attempted. If multiple referral URLs are provided, then they should 067 * all be considered equivalent for the purpose of attempting the 068 * operation (e.g., the different URLs may simply refer to different 069 * servers in which the operation could be processed).</LI> 070 * <LI>Response Controls -- An optional set of controls included in the 071 * response from the server. If any controls are included, then they may 072 * provide additional information about the processing that was performed 073 * by the server.</LI> 074 * </UL> 075 * <BR><BR> 076 * Note that even though this class is marked with the @Extensible annotation 077 * type, it should not be directly subclassed by third-party code. Only the 078 * {@link BindResult} and {@link ExtendedResult} subclasses are actually 079 * intended to be extended by third-party code. 080 */ 081@Extensible() 082@NotMutable() 083@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 084public class LDAPResult 085 implements Serializable, LDAPResponse 086{ 087 /** 088 * The BER type for the set of referral URLs. 089 */ 090 static final byte TYPE_REFERRAL_URLS = (byte) 0xA3; 091 092 093 094 /** 095 * The serial version UID for this serializable class. 096 */ 097 private static final long serialVersionUID = 2215819095653175991L; 098 099 100 101 // The protocol op type for this result, if available. 102 private final Byte protocolOpType; 103 104 // The set of controls from the response. 105 private final Control[] responseControls; 106 107 // The message ID for the LDAP message that is associated with this LDAP 108 // result. 109 private final int messageID; 110 111 // The result code from the response. 112 private final ResultCode resultCode; 113 114 // The diagnostic message from the response, if available. 115 private final String diagnosticMessage; 116 117 // The matched DN from the response, if available. 118 private final String matchedDN; 119 120 // The set of referral URLs from the response, if available. 121 private final String[] referralURLs; 122 123 124 125 /** 126 * Creates a new LDAP result object based on the provided result. 127 * 128 * @param result The LDAP result object to use to initialize this result. 129 */ 130 protected LDAPResult(final LDAPResult result) 131 { 132 protocolOpType = result.protocolOpType; 133 messageID = result.messageID; 134 resultCode = result.resultCode; 135 diagnosticMessage = result.diagnosticMessage; 136 matchedDN = result.matchedDN; 137 referralURLs = result.referralURLs; 138 responseControls = result.responseControls; 139 } 140 141 142 143 /** 144 * Creates a new LDAP result object with the provided message ID and result 145 * code, and no other information. 146 * 147 * @param messageID The message ID for the LDAP message that is associated 148 * with this LDAP result. 149 * @param resultCode The result code from the response. 150 */ 151 public LDAPResult(final int messageID, final ResultCode resultCode) 152 { 153 this(null, messageID, resultCode, null, null, StaticUtils.NO_STRINGS, 154 NO_CONTROLS); 155 } 156 157 158 159 /** 160 * Creates a new LDAP result object with the provided information. 161 * 162 * @param messageID The message ID for the LDAP message that is 163 * associated with this LDAP result. 164 * @param resultCode The result code from the response. 165 * @param diagnosticMessage The diagnostic message from the response, if 166 * available. 167 * @param matchedDN The matched DN from the response, if available. 168 * @param referralURLs The set of referral URLs from the response, if 169 * available. 170 * @param responseControls The set of controls from the response, if 171 * available. 172 */ 173 public LDAPResult(final int messageID, final ResultCode resultCode, 174 final String diagnosticMessage, final String matchedDN, 175 final String[] referralURLs, 176 final Control[] responseControls) 177 { 178 this(null, messageID, resultCode, diagnosticMessage, matchedDN, 179 referralURLs, responseControls); 180 } 181 182 183 184 /** 185 * Creates a new LDAP result object with the provided information. 186 * 187 * @param messageID The message ID for the LDAP message that is 188 * associated with this LDAP result. 189 * @param resultCode The result code from the response. 190 * @param diagnosticMessage The diagnostic message from the response, if 191 * available. 192 * @param matchedDN The matched DN from the response, if available. 193 * @param referralURLs The set of referral URLs from the response, if 194 * available. 195 * @param responseControls The set of controls from the response, if 196 * available. 197 */ 198 public LDAPResult(final int messageID, final ResultCode resultCode, 199 final String diagnosticMessage, final String matchedDN, 200 final List<String> referralURLs, 201 final List<Control> responseControls) 202 { 203 this(null, messageID, resultCode, diagnosticMessage, matchedDN, 204 referralURLs, responseControls); 205 } 206 207 208 209 /** 210 * Creates a new LDAP result object with the provided information. 211 * 212 * @param protocolOpType The protocol op type for this result, if 213 * available. 214 * @param messageID The message ID for the LDAP message that is 215 * associated with this LDAP result. 216 * @param resultCode The result code from the response. 217 * @param diagnosticMessage The diagnostic message from the response, if 218 * available. 219 * @param matchedDN The matched DN from the response, if available. 220 * @param referralURLs The set of referral URLs from the response, if 221 * available. 222 * @param responseControls The set of controls from the response, if 223 * available. 224 */ 225 private LDAPResult(final Byte protocolOpType, final int messageID, 226 final ResultCode resultCode, 227 final String diagnosticMessage, final String matchedDN, 228 final String[] referralURLs, 229 final Control[] responseControls) 230 { 231 this.protocolOpType = protocolOpType; 232 this.messageID = messageID; 233 this.resultCode = resultCode; 234 this.diagnosticMessage = diagnosticMessage; 235 this.matchedDN = matchedDN; 236 237 if (referralURLs == null) 238 { 239 this.referralURLs = StaticUtils.NO_STRINGS; 240 } 241 else 242 { 243 this.referralURLs = referralURLs; 244 } 245 246 if (responseControls == null) 247 { 248 this.responseControls = NO_CONTROLS; 249 } 250 else 251 { 252 this.responseControls = responseControls; 253 } 254 } 255 256 257 258 /** 259 * Creates a new LDAP result object with the provided information. 260 * 261 * @param protocolOpType The protocol op type for this result, if 262 * available. 263 * @param messageID The message ID for the LDAP message that is 264 * associated with this LDAP result. 265 * @param resultCode The result code from the response. 266 * @param diagnosticMessage The diagnostic message from the response, if 267 * available. 268 * @param matchedDN The matched DN from the response, if available. 269 * @param referralURLs The set of referral URLs from the response, if 270 * available. 271 * @param responseControls The set of controls from the response, if 272 * available. 273 */ 274 private LDAPResult(final Byte protocolOpType, final int messageID, 275 final ResultCode resultCode, 276 final String diagnosticMessage, final String matchedDN, 277 final List<String> referralURLs, 278 final List<Control> responseControls) 279 { 280 this.protocolOpType = protocolOpType; 281 this.messageID = messageID; 282 this.resultCode = resultCode; 283 this.diagnosticMessage = diagnosticMessage; 284 this.matchedDN = matchedDN; 285 286 if ((referralURLs == null) || referralURLs.isEmpty()) 287 { 288 this.referralURLs = StaticUtils.NO_STRINGS; 289 } 290 else 291 { 292 this.referralURLs = new String[referralURLs.size()]; 293 referralURLs.toArray(this.referralURLs); 294 } 295 296 if ((responseControls == null) || responseControls.isEmpty()) 297 { 298 this.responseControls = NO_CONTROLS; 299 } 300 else 301 { 302 this.responseControls = new Control[responseControls.size()]; 303 responseControls.toArray(this.responseControls); 304 } 305 } 306 307 308 309 /** 310 * Creates a new LDAP result object with the provided message ID and with the 311 * protocol op and controls read from the given ASN.1 stream reader. 312 * 313 * @param messageID The LDAP message ID for the LDAP message that is 314 * associated with this LDAP result. 315 * @param messageSequence The ASN.1 stream reader sequence used in the 316 * course of reading the LDAP message elements. 317 * @param reader The ASN.1 stream reader from which to read the 318 * protocol op and controls. 319 * 320 * @return The decoded LDAP result. 321 * 322 * @throws LDAPException If a problem occurs while reading or decoding data 323 * from the ASN.1 stream reader. 324 */ 325 static LDAPResult readLDAPResultFrom(final int messageID, 326 final ASN1StreamReaderSequence messageSequence, 327 final ASN1StreamReader reader) 328 throws LDAPException 329 { 330 try 331 { 332 final ASN1StreamReaderSequence protocolOpSequence = 333 reader.beginSequence(); 334 final byte protocolOpType = protocolOpSequence.getType(); 335 336 final ResultCode resultCode = ResultCode.valueOf(reader.readEnumerated()); 337 338 String matchedDN = reader.readString(); 339 if (matchedDN.isEmpty()) 340 { 341 matchedDN = null; 342 } 343 344 String diagnosticMessage = reader.readString(); 345 if (diagnosticMessage.isEmpty()) 346 { 347 diagnosticMessage = null; 348 } 349 350 String[] referralURLs = StaticUtils.NO_STRINGS; 351 if (protocolOpSequence.hasMoreElements()) 352 { 353 final ArrayList<String> refList = new ArrayList<>(1); 354 final ASN1StreamReaderSequence refSequence = reader.beginSequence(); 355 while (refSequence.hasMoreElements()) 356 { 357 refList.add(reader.readString()); 358 } 359 360 referralURLs = new String[refList.size()]; 361 refList.toArray(referralURLs); 362 } 363 364 Control[] responseControls = NO_CONTROLS; 365 if (messageSequence.hasMoreElements()) 366 { 367 final ArrayList<Control> controlList = new ArrayList<>(1); 368 final ASN1StreamReaderSequence controlSequence = reader.beginSequence(); 369 while (controlSequence.hasMoreElements()) 370 { 371 controlList.add(Control.readFrom(reader)); 372 } 373 374 responseControls = new Control[controlList.size()]; 375 controlList.toArray(responseControls); 376 } 377 378 return new LDAPResult(protocolOpType, messageID, resultCode, 379 diagnosticMessage, matchedDN, referralURLs, responseControls); 380 } 381 catch (final LDAPException le) 382 { 383 Debug.debugException(le); 384 throw le; 385 } 386 catch (final ASN1Exception ae) 387 { 388 Debug.debugException(ae); 389 throw new LDAPException(ResultCode.DECODING_ERROR, 390 ERR_RESULT_CANNOT_DECODE.get(ae.getMessage()), ae); 391 } 392 catch (final Exception e) 393 { 394 Debug.debugException(e); 395 throw new LDAPException(ResultCode.DECODING_ERROR, 396 ERR_RESULT_CANNOT_DECODE.get(StaticUtils.getExceptionMessage(e)), e); 397 } 398 } 399 400 401 402 /** 403 * Retrieves the message ID for the LDAP message with which this LDAP result 404 * is associated. 405 * 406 * @return The message ID for the LDAP message with which this LDAP result 407 * is associated. 408 */ 409 @Override() 410 public final int getMessageID() 411 { 412 return messageID; 413 } 414 415 416 417 /** 418 * Retrieves the result code from the response. 419 * 420 * @return The result code from the response. 421 */ 422 public final ResultCode getResultCode() 423 { 424 return resultCode; 425 } 426 427 428 429 /** 430 * Retrieves the diagnostic message from the response, if available. 431 * 432 * @return The diagnostic message from the response, or {@code null} if none 433 * was provided. 434 */ 435 public final String getDiagnosticMessage() 436 { 437 return diagnosticMessage; 438 } 439 440 441 442 /** 443 * Retrieves the matched DN from the response, if available. 444 * 445 * @return The matched DN from the response, or {@code null} if none was 446 * provided. 447 */ 448 public final String getMatchedDN() 449 { 450 return matchedDN; 451 } 452 453 454 455 /** 456 * Retrieves the set of referral URLs from the response, if available. 457 * 458 * @return The set of referral URLs from the response. The array returned 459 * may be empty if the response did not include any referral URLs. 460 */ 461 public final String[] getReferralURLs() 462 { 463 return referralURLs; 464 } 465 466 467 468 /** 469 * Retrieves the set of controls from the response, if available. Individual 470 * response controls of a specific type may be retrieved and decoded using the 471 * {@code get} method in the response control class. 472 * 473 * @return The set of controls from the response. The array returned may be 474 * empty if the response did not include any controls. 475 */ 476 public final Control[] getResponseControls() 477 { 478 return responseControls; 479 } 480 481 482 483 /** 484 * Indicates whether this result contains at least one control. 485 * 486 * @return {@code true} if this result contains at least one control, or 487 * {@code false} if not. 488 */ 489 public final boolean hasResponseControl() 490 { 491 return (responseControls.length > 0); 492 } 493 494 495 496 /** 497 * Indicates whether this result contains at least one control with the 498 * specified OID. 499 * 500 * @param oid The object identifier for which to make the determination. It 501 * must not be {@code null}. 502 * 503 * @return {@code true} if this result contains at least one control with 504 * the specified OID, or {@code false} if not. 505 */ 506 public final boolean hasResponseControl(final String oid) 507 { 508 for (final Control c : responseControls) 509 { 510 if (c.getOID().equals(oid)) 511 { 512 return true; 513 } 514 } 515 516 return false; 517 } 518 519 520 521 /** 522 * Retrieves the response control with the specified OID. If there is more 523 * than one response control with the specified OID, then the first will be 524 * returned. 525 * 526 * @param oid The OID for the response control to retrieve. 527 * 528 * @return The requested response control, or {@code null} if there is no 529 * such response control. 530 */ 531 public final Control getResponseControl(final String oid) 532 { 533 for (final Control c : responseControls) 534 { 535 if (c.getOID().equals(oid)) 536 { 537 return c; 538 } 539 } 540 541 return null; 542 } 543 544 545 546 /** 547 * Retrieves a string representation of this LDAP result, consisting of 548 * the result code, diagnostic message (if present), matched DN (if present), 549 * and referral URLs (if present). 550 * 551 * @return A string representation of this LDAP result. 552 */ 553 public String getResultString() 554 { 555 final StringBuilder buffer = new StringBuilder(); 556 buffer.append("result code='"); 557 buffer.append(resultCode); 558 buffer.append('\''); 559 560 if ((diagnosticMessage != null) && (! diagnosticMessage.isEmpty())) 561 { 562 buffer.append(" diagnostic message='"); 563 buffer.append(diagnosticMessage); 564 buffer.append('\''); 565 } 566 567 if ((matchedDN != null) && (! matchedDN.isEmpty())) 568 { 569 buffer.append(" matched DN='"); 570 buffer.append(matchedDN); 571 buffer.append('\''); 572 } 573 574 if ((referralURLs != null) && (referralURLs.length > 0)) 575 { 576 buffer.append(" referral URLs={"); 577 578 for (int i=0; i < referralURLs.length; i++) 579 { 580 if (i > 0) 581 { 582 buffer.append(", "); 583 } 584 585 buffer.append('\''); 586 buffer.append(referralURLs[i]); 587 buffer.append('\''); 588 } 589 590 buffer.append('}'); 591 } 592 593 return buffer.toString(); 594 } 595 596 597 598 /** 599 * Retrieves a string representation of this LDAP result. 600 * 601 * @return A string representation of this LDAP result. 602 */ 603 @Override() 604 public String toString() 605 { 606 final StringBuilder buffer = new StringBuilder(); 607 toString(buffer); 608 return buffer.toString(); 609 } 610 611 612 613 /** 614 * Appends a string representation of this LDAP result to the provided buffer. 615 * 616 * @param buffer The buffer to which to append a string representation of 617 * this LDAP result. 618 */ 619 @Override() 620 public void toString(final StringBuilder buffer) 621 { 622 buffer.append("LDAPResult(resultCode="); 623 buffer.append(resultCode); 624 625 if (messageID >= 0) 626 { 627 buffer.append(", messageID="); 628 buffer.append(messageID); 629 } 630 631 if (protocolOpType != null) 632 { 633 switch (protocolOpType) 634 { 635 case LDAPMessage.PROTOCOL_OP_TYPE_ADD_RESPONSE: 636 buffer.append(", opType='add'"); 637 break; 638 case LDAPMessage.PROTOCOL_OP_TYPE_BIND_RESPONSE: 639 buffer.append(", opType='bind'"); 640 break; 641 case LDAPMessage.PROTOCOL_OP_TYPE_COMPARE_RESPONSE: 642 buffer.append(", opType='compare'"); 643 break; 644 case LDAPMessage.PROTOCOL_OP_TYPE_DELETE_RESPONSE: 645 buffer.append(", opType='delete'"); 646 break; 647 case LDAPMessage.PROTOCOL_OP_TYPE_EXTENDED_RESPONSE: 648 buffer.append(", opType='extended'"); 649 break; 650 case LDAPMessage.PROTOCOL_OP_TYPE_MODIFY_RESPONSE: 651 buffer.append(", opType='modify'"); 652 break; 653 case LDAPMessage.PROTOCOL_OP_TYPE_MODIFY_DN_RESPONSE: 654 buffer.append(", opType='modify DN'"); 655 break; 656 case LDAPMessage.PROTOCOL_OP_TYPE_SEARCH_RESULT_DONE: 657 buffer.append(", opType='search'"); 658 break; 659 } 660 } 661 662 if (diagnosticMessage != null) 663 { 664 buffer.append(", diagnosticMessage='"); 665 buffer.append(diagnosticMessage); 666 buffer.append('\''); 667 } 668 669 if (matchedDN != null) 670 { 671 buffer.append(", matchedDN='"); 672 buffer.append(matchedDN); 673 buffer.append('\''); 674 } 675 676 if (referralURLs.length > 0) 677 { 678 buffer.append(", referralURLs={"); 679 for (int i=0; i < referralURLs.length; i++) 680 { 681 if (i > 0) 682 { 683 buffer.append(", "); 684 } 685 686 buffer.append('\''); 687 buffer.append(referralURLs[i]); 688 buffer.append('\''); 689 } 690 buffer.append('}'); 691 } 692 693 if (responseControls.length > 0) 694 { 695 buffer.append(", responseControls={"); 696 for (int i=0; i < responseControls.length; i++) 697 { 698 if (i > 0) 699 { 700 buffer.append(", "); 701 } 702 703 buffer.append(responseControls[i]); 704 } 705 buffer.append('}'); 706 } 707 708 buffer.append(')'); 709 } 710}