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