001/* 002 * Copyright 2007-2014 UnboundID Corp. 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2008-2014 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 */ 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.Extensible; 035import com.unboundid.util.NotMutable; 036import com.unboundid.util.ThreadSafety; 037import com.unboundid.util.ThreadSafetyLevel; 038 039import static com.unboundid.ldap.sdk.LDAPMessages.*; 040import static com.unboundid.util.Debug.*; 041import static com.unboundid.util.StaticUtils.*; 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, NO_STRINGS, NO_CONTROLS); 154 } 155 156 157 158 /** 159 * Creates a new LDAP result object with the provided information. 160 * 161 * @param messageID The message ID for the LDAP message that is 162 * associated with this LDAP result. 163 * @param resultCode The result code from the response. 164 * @param diagnosticMessage The diagnostic message from the response, if 165 * available. 166 * @param matchedDN The matched DN from the response, if available. 167 * @param referralURLs The set of referral URLs from the response, if 168 * available. 169 * @param responseControls The set of controls from the response, if 170 * available. 171 */ 172 public LDAPResult(final int messageID, final ResultCode resultCode, 173 final String diagnosticMessage, final String matchedDN, 174 final String[] referralURLs, 175 final Control[] responseControls) 176 { 177 this(null, messageID, resultCode, diagnosticMessage, matchedDN, 178 referralURLs, responseControls); 179 } 180 181 182 183 /** 184 * Creates a new LDAP result object with the provided information. 185 * 186 * @param messageID The message ID for the LDAP message that is 187 * associated with this LDAP result. 188 * @param resultCode The result code from the response. 189 * @param diagnosticMessage The diagnostic message from the response, if 190 * available. 191 * @param matchedDN The matched DN from the response, if available. 192 * @param referralURLs The set of referral URLs from the response, if 193 * available. 194 * @param responseControls The set of controls from the response, if 195 * available. 196 */ 197 public LDAPResult(final int messageID, final ResultCode resultCode, 198 final String diagnosticMessage, final String matchedDN, 199 final List<String> referralURLs, 200 final List<Control> responseControls) 201 { 202 this(null, messageID, resultCode, diagnosticMessage, matchedDN, 203 referralURLs, responseControls); 204 } 205 206 207 208 /** 209 * Creates a new LDAP result object with the provided information. 210 * 211 * @param protocolOpType The protocol op type for this result, if 212 * available. 213 * @param messageID The message ID for the LDAP message that is 214 * associated with this LDAP result. 215 * @param resultCode The result code from the response. 216 * @param diagnosticMessage The diagnostic message from the response, if 217 * available. 218 * @param matchedDN The matched DN from the response, if available. 219 * @param referralURLs The set of referral URLs from the response, if 220 * available. 221 * @param responseControls The set of controls from the response, if 222 * available. 223 */ 224 private LDAPResult(final Byte protocolOpType, final int messageID, 225 final ResultCode resultCode, 226 final String diagnosticMessage, final String matchedDN, 227 final String[] referralURLs, 228 final Control[] responseControls) 229 { 230 this.protocolOpType = protocolOpType; 231 this.messageID = messageID; 232 this.resultCode = resultCode; 233 this.diagnosticMessage = diagnosticMessage; 234 this.matchedDN = matchedDN; 235 236 if (referralURLs == null) 237 { 238 this.referralURLs = NO_STRINGS; 239 } 240 else 241 { 242 this.referralURLs = referralURLs; 243 } 244 245 if (responseControls == null) 246 { 247 this.responseControls = NO_CONTROLS; 248 } 249 else 250 { 251 this.responseControls = responseControls; 252 } 253 } 254 255 256 257 /** 258 * Creates a new LDAP result object with the provided information. 259 * 260 * @param protocolOpType The protocol op type for this result, if 261 * available. 262 * @param messageID The message ID for the LDAP message that is 263 * associated with this LDAP result. 264 * @param resultCode The result code from the response. 265 * @param diagnosticMessage The diagnostic message from the response, if 266 * available. 267 * @param matchedDN The matched DN from the response, if available. 268 * @param referralURLs The set of referral URLs from the response, if 269 * available. 270 * @param responseControls The set of controls from the response, if 271 * available. 272 */ 273 private LDAPResult(final Byte protocolOpType, final int messageID, 274 final ResultCode resultCode, 275 final String diagnosticMessage, final String matchedDN, 276 final List<String> referralURLs, 277 final List<Control> responseControls) 278 { 279 this.protocolOpType = protocolOpType; 280 this.messageID = messageID; 281 this.resultCode = resultCode; 282 this.diagnosticMessage = diagnosticMessage; 283 this.matchedDN = matchedDN; 284 285 if ((referralURLs == null) || referralURLs.isEmpty()) 286 { 287 this.referralURLs = NO_STRINGS; 288 } 289 else 290 { 291 this.referralURLs = new String[referralURLs.size()]; 292 referralURLs.toArray(this.referralURLs); 293 } 294 295 if ((responseControls == null) || responseControls.isEmpty()) 296 { 297 this.responseControls = NO_CONTROLS; 298 } 299 else 300 { 301 this.responseControls = new Control[responseControls.size()]; 302 responseControls.toArray(this.responseControls); 303 } 304 } 305 306 307 308 /** 309 * Creates a new LDAP result object with the provided message ID and with the 310 * protocol op and controls read from the given ASN.1 stream reader. 311 * 312 * @param messageID The LDAP message ID for the LDAP message that is 313 * associated with this LDAP result. 314 * @param messageSequence The ASN.1 stream reader sequence used in the 315 * course of reading the LDAP message elements. 316 * @param reader The ASN.1 stream reader from which to read the 317 * protocol op and controls. 318 * 319 * @return The decoded LDAP result. 320 * 321 * @throws LDAPException If a problem occurs while reading or decoding data 322 * from the ASN.1 stream reader. 323 */ 324 static LDAPResult readLDAPResultFrom(final int messageID, 325 final ASN1StreamReaderSequence messageSequence, 326 final ASN1StreamReader reader) 327 throws LDAPException 328 { 329 try 330 { 331 final ASN1StreamReaderSequence protocolOpSequence = 332 reader.beginSequence(); 333 final byte protocolOpType = protocolOpSequence.getType(); 334 335 final ResultCode resultCode = ResultCode.valueOf(reader.readEnumerated()); 336 337 String matchedDN = reader.readString(); 338 if (matchedDN.length() == 0) 339 { 340 matchedDN = null; 341 } 342 343 String diagnosticMessage = reader.readString(); 344 if (diagnosticMessage.length() == 0) 345 { 346 diagnosticMessage = null; 347 } 348 349 String[] referralURLs = NO_STRINGS; 350 if (protocolOpSequence.hasMoreElements()) 351 { 352 final ArrayList<String> refList = new ArrayList<String>(1); 353 final ASN1StreamReaderSequence refSequence = reader.beginSequence(); 354 while (refSequence.hasMoreElements()) 355 { 356 refList.add(reader.readString()); 357 } 358 359 referralURLs = new String[refList.size()]; 360 refList.toArray(referralURLs); 361 } 362 363 Control[] responseControls = NO_CONTROLS; 364 if (messageSequence.hasMoreElements()) 365 { 366 final ArrayList<Control> controlList = new ArrayList<Control>(1); 367 final ASN1StreamReaderSequence controlSequence = reader.beginSequence(); 368 while (controlSequence.hasMoreElements()) 369 { 370 controlList.add(Control.readFrom(reader)); 371 } 372 373 responseControls = new Control[controlList.size()]; 374 controlList.toArray(responseControls); 375 } 376 377 return new LDAPResult(protocolOpType, messageID, resultCode, 378 diagnosticMessage, matchedDN, referralURLs, responseControls); 379 } 380 catch (LDAPException le) 381 { 382 debugException(le); 383 throw le; 384 } 385 catch (final ASN1Exception ae) 386 { 387 debugException(ae); 388 throw new LDAPException(ResultCode.DECODING_ERROR, 389 ERR_RESULT_CANNOT_DECODE.get(ae.getMessage()), ae); 390 } 391 catch (Exception e) 392 { 393 debugException(e); 394 throw new LDAPException(ResultCode.DECODING_ERROR, 395 ERR_RESULT_CANNOT_DECODE.get(getExceptionMessage(e)), e); 396 } 397 } 398 399 400 401 /** 402 * Retrieves the message ID for the LDAP message with which this LDAP result 403 * is associated. 404 * 405 * @return The message ID for the LDAP message with which this LDAP result 406 * is associated. 407 */ 408 public final int getMessageID() 409 { 410 return messageID; 411 } 412 413 414 415 /** 416 * Retrieves the result code from the response. 417 * 418 * @return The result code from the response. 419 */ 420 public final ResultCode getResultCode() 421 { 422 return resultCode; 423 } 424 425 426 427 /** 428 * Retrieves the diagnostic message from the response, if available. 429 * 430 * @return The diagnostic message from the response, or {@code null} if none 431 * was provided. 432 */ 433 public final String getDiagnosticMessage() 434 { 435 return diagnosticMessage; 436 } 437 438 439 440 /** 441 * Retrieves the matched DN from the response, if available. 442 * 443 * @return The matched DN from the response, or {@code null} if none was 444 * provided. 445 */ 446 public final String getMatchedDN() 447 { 448 return matchedDN; 449 } 450 451 452 453 /** 454 * Retrieves the set of referral URLs from the response, if available. 455 * 456 * @return The set of referral URLs from the response. The array returned 457 * may be empty if the response did not include any referral URLs. 458 */ 459 public final String[] getReferralURLs() 460 { 461 return referralURLs; 462 } 463 464 465 466 /** 467 * Retrieves the set of controls from the response, if available. Individual 468 * response controls of a specific type may be retrieved and decoded using the 469 * {@code get} method in the response control class. 470 * 471 * @return The set of controls from the response. The array returned may be 472 * empty if the response did not include any controls. 473 */ 474 public final Control[] getResponseControls() 475 { 476 return responseControls; 477 } 478 479 480 481 /** 482 * Indicates whether this result contains at least one control. 483 * 484 * @return {@code true} if this result contains at least one control, or 485 * {@code false} if not. 486 */ 487 public final boolean hasResponseControl() 488 { 489 return (responseControls.length > 0); 490 } 491 492 493 494 /** 495 * Indicates whether this result contains at least one control with the 496 * specified OID. 497 * 498 * @param oid The object identifier for which to make the determination. It 499 * must not be {@code null}. 500 * 501 * @return {@code true} if this result contains at least one control with 502 * the specified OID, or {@code false} if not. 503 */ 504 public final boolean hasResponseControl(final String oid) 505 { 506 for (final Control c : responseControls) 507 { 508 if (c.getOID().equals(oid)) 509 { 510 return true; 511 } 512 } 513 514 return false; 515 } 516 517 518 519 /** 520 * Retrieves the response control with the specified OID. If there is more 521 * than one response control with the specified OID, then the first will be 522 * returned. 523 * 524 * @param oid The OID for the response control to retrieve. 525 * 526 * @return The requested response control, or {@code null} if there is no 527 * such response control. 528 */ 529 public final Control getResponseControl(final String oid) 530 { 531 for (final Control c : responseControls) 532 { 533 if (c.getOID().equals(oid)) 534 { 535 return c; 536 } 537 } 538 539 return null; 540 } 541 542 543 544 /** 545 * Retrieves a string representation of this LDAP result. 546 * 547 * @return A string representation of this LDAP result. 548 */ 549 @Override() 550 public String toString() 551 { 552 final StringBuilder buffer = new StringBuilder(); 553 toString(buffer); 554 return buffer.toString(); 555 } 556 557 558 559 /** 560 * Appends a string representation of this LDAP result to the provided buffer. 561 * 562 * @param buffer The buffer to which to append a string representation of 563 * this LDAP result. 564 */ 565 public void toString(final StringBuilder buffer) 566 { 567 buffer.append("LDAPResult(resultCode="); 568 buffer.append(resultCode); 569 570 if (messageID >= 0) 571 { 572 buffer.append(", messageID="); 573 buffer.append(messageID); 574 } 575 576 if (protocolOpType != null) 577 { 578 switch (protocolOpType) 579 { 580 case LDAPMessage.PROTOCOL_OP_TYPE_ADD_RESPONSE: 581 buffer.append(", opType='add'"); 582 break; 583 case LDAPMessage.PROTOCOL_OP_TYPE_BIND_RESPONSE: 584 buffer.append(", opType='bind'"); 585 break; 586 case LDAPMessage.PROTOCOL_OP_TYPE_COMPARE_RESPONSE: 587 buffer.append(", opType='compare'"); 588 break; 589 case LDAPMessage.PROTOCOL_OP_TYPE_DELETE_RESPONSE: 590 buffer.append(", opType='delete'"); 591 break; 592 case LDAPMessage.PROTOCOL_OP_TYPE_EXTENDED_RESPONSE: 593 buffer.append(", opType='extended'"); 594 break; 595 case LDAPMessage.PROTOCOL_OP_TYPE_MODIFY_RESPONSE: 596 buffer.append(", opType='modify'"); 597 break; 598 case LDAPMessage.PROTOCOL_OP_TYPE_MODIFY_DN_RESPONSE: 599 buffer.append(", opType='modify DN'"); 600 break; 601 case LDAPMessage.PROTOCOL_OP_TYPE_SEARCH_RESULT_DONE: 602 buffer.append(", opType='search'"); 603 break; 604 } 605 } 606 607 if (diagnosticMessage != null) 608 { 609 buffer.append(", diagnosticMessage='"); 610 buffer.append(diagnosticMessage); 611 buffer.append('\''); 612 } 613 614 if (matchedDN != null) 615 { 616 buffer.append(", matchedDN='"); 617 buffer.append(matchedDN); 618 buffer.append('\''); 619 } 620 621 if (referralURLs.length > 0) 622 { 623 buffer.append(", referralURLs={"); 624 for (int i=0; i < referralURLs.length; i++) 625 { 626 if (i > 0) 627 { 628 buffer.append(", "); 629 } 630 631 buffer.append('\''); 632 buffer.append(referralURLs[i]); 633 buffer.append('\''); 634 } 635 buffer.append('}'); 636 } 637 638 if (responseControls.length > 0) 639 { 640 buffer.append(", responseControls={"); 641 for (int i=0; i < responseControls.length; i++) 642 { 643 if (i > 0) 644 { 645 buffer.append(", "); 646 } 647 648 buffer.append(responseControls[i]); 649 } 650 buffer.append('}'); 651 } 652 653 buffer.append(')'); 654 } 655}