001 /* 002 * Copyright 2009-2015 UnboundID Corp. 003 * All Rights Reserved. 004 */ 005 /* 006 * Copyright (C) 2009-2015 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.protocol; 022 023 024 025 import java.util.ArrayList; 026 import java.util.Collections; 027 import java.util.Iterator; 028 import java.util.List; 029 030 import com.unboundid.asn1.ASN1Buffer; 031 import com.unboundid.asn1.ASN1BufferSequence; 032 import com.unboundid.asn1.ASN1Element; 033 import com.unboundid.asn1.ASN1Enumerated; 034 import com.unboundid.asn1.ASN1OctetString; 035 import com.unboundid.asn1.ASN1Sequence; 036 import com.unboundid.asn1.ASN1StreamReader; 037 import com.unboundid.asn1.ASN1StreamReaderSequence; 038 import com.unboundid.ldap.sdk.Control; 039 import com.unboundid.ldap.sdk.ExtendedResult; 040 import com.unboundid.ldap.sdk.LDAPException; 041 import com.unboundid.ldap.sdk.LDAPResult; 042 import com.unboundid.ldap.sdk.ResultCode; 043 import com.unboundid.util.InternalUseOnly; 044 045 import static com.unboundid.ldap.protocol.ProtocolMessages.*; 046 import static com.unboundid.util.Debug.*; 047 import static com.unboundid.util.StaticUtils.*; 048 import static com.unboundid.util.Validator.*; 049 050 051 052 /** 053 * This class provides an implementation of a extended response protocol op. 054 */ 055 @InternalUseOnly() 056 public final class ExtendedResponseProtocolOp 057 implements ProtocolOp 058 { 059 /** 060 * The BER type for the response OID element. 061 */ 062 public static final byte TYPE_RESPONSE_OID = (byte) 0x8A; 063 064 065 066 /** 067 * The BER type for the response value element. 068 */ 069 public static final byte TYPE_RESPONSE_VALUE = (byte) 0x8B; 070 071 072 073 /** 074 * The serial version UID for this serializable class. 075 */ 076 private static final long serialVersionUID = -7757619031268544913L; 077 078 079 080 // The value for this extended response. 081 private final ASN1OctetString responseValue; 082 083 // The result code for this extended response. 084 private final int resultCode; 085 086 // The referral URLs for this extended response. 087 private final List<String> referralURLs; 088 089 // The diagnostic message for this extended response. 090 private final String diagnosticMessage; 091 092 // The matched DN for this extended response. 093 private final String matchedDN; 094 095 // The OID for this extended response. 096 private final String responseOID; 097 098 099 100 /** 101 * Creates a new instance of this extended response protocol op with the 102 * provided information. 103 * 104 * @param resultCode The result code for this response. 105 * @param matchedDN The matched DN for this response, if available. 106 * @param diagnosticMessage The diagnostic message for this response, if 107 * any. 108 * @param referralURLs The list of referral URLs for this response, if 109 * any. 110 * @param responseOID The response OID for this response, if any. 111 * @param responseValue The value for this response, if any. 112 */ 113 public ExtendedResponseProtocolOp(final int resultCode, 114 final String matchedDN, 115 final String diagnosticMessage, 116 final List<String> referralURLs, 117 final String responseOID, 118 final ASN1OctetString responseValue) 119 { 120 this.resultCode = resultCode; 121 this.matchedDN = matchedDN; 122 this.diagnosticMessage = diagnosticMessage; 123 this.responseOID = responseOID; 124 125 if (referralURLs == null) 126 { 127 this.referralURLs = Collections.emptyList(); 128 } 129 else 130 { 131 this.referralURLs = Collections.unmodifiableList(referralURLs); 132 } 133 134 if (responseValue == null) 135 { 136 this.responseValue = null; 137 } 138 else 139 { 140 this.responseValue = 141 new ASN1OctetString(TYPE_RESPONSE_VALUE, responseValue.getValue()); 142 } 143 } 144 145 146 147 /** 148 * Creates a new extended response protocol op from the provided extended 149 * result object. 150 * 151 * @param result The extended result object to use to create this protocol 152 * op. 153 */ 154 public ExtendedResponseProtocolOp(final LDAPResult result) 155 { 156 resultCode = result.getResultCode().intValue(); 157 matchedDN = result.getMatchedDN(); 158 diagnosticMessage = result.getDiagnosticMessage(); 159 referralURLs = toList(result.getReferralURLs()); 160 161 if (result instanceof ExtendedResult) 162 { 163 final ExtendedResult r = (ExtendedResult) result; 164 responseOID = r.getOID(); 165 responseValue = r.getValue(); 166 } 167 else 168 { 169 responseOID = null; 170 responseValue = null; 171 } 172 } 173 174 175 176 /** 177 * Creates a new extended response protocol op read from the provided ASN.1 178 * stream reader. 179 * 180 * @param reader The ASN.1 stream reader from which to read the extended 181 * response. 182 * 183 * @throws LDAPException If a problem occurs while reading or parsing the 184 * extended response. 185 */ 186 ExtendedResponseProtocolOp(final ASN1StreamReader reader) 187 throws LDAPException 188 { 189 try 190 { 191 final ASN1StreamReaderSequence opSequence = reader.beginSequence(); 192 resultCode = reader.readEnumerated(); 193 194 String s = reader.readString(); 195 ensureNotNull(s); 196 if (s.length() == 0) 197 { 198 matchedDN = null; 199 } 200 else 201 { 202 matchedDN = s; 203 } 204 205 s = reader.readString(); 206 ensureNotNull(s); 207 if (s.length() == 0) 208 { 209 diagnosticMessage = null; 210 } 211 else 212 { 213 diagnosticMessage = s; 214 } 215 216 ASN1OctetString value = null; 217 String oid = null; 218 final ArrayList<String> refs = new ArrayList<String>(1); 219 while (opSequence.hasMoreElements()) 220 { 221 final byte type = (byte) reader.peek(); 222 if (type == GenericResponseProtocolOp.TYPE_REFERRALS) 223 { 224 final ASN1StreamReaderSequence refSequence = reader.beginSequence(); 225 while (refSequence.hasMoreElements()) 226 { 227 refs.add(reader.readString()); 228 } 229 } 230 else if (type == TYPE_RESPONSE_OID) 231 { 232 oid = reader.readString(); 233 } 234 else if (type == TYPE_RESPONSE_VALUE) 235 { 236 value = new ASN1OctetString(type, reader.readBytes()); 237 } 238 else 239 { 240 throw new LDAPException(ResultCode.DECODING_ERROR, 241 ERR_EXTENDED_RESPONSE_INVALID_ELEMENT.get(toHex(type))); 242 } 243 } 244 245 referralURLs = Collections.unmodifiableList(refs); 246 responseOID = oid; 247 responseValue = value; 248 } 249 catch (LDAPException le) 250 { 251 debugException(le); 252 throw le; 253 } 254 catch (Exception e) 255 { 256 debugException(e); 257 throw new LDAPException(ResultCode.DECODING_ERROR, 258 ERR_EXTENDED_RESPONSE_CANNOT_DECODE.get(getExceptionMessage(e)), e); 259 } 260 } 261 262 263 264 /** 265 * Retrieves the result code for this extended response. 266 * 267 * @return The result code for this extended response. 268 */ 269 public int getResultCode() 270 { 271 return resultCode; 272 } 273 274 275 276 /** 277 * Retrieves the matched DN for this extended response, if any. 278 * 279 * @return The matched DN for this extended response, or {@code null} if 280 * there is no matched DN. 281 */ 282 public String getMatchedDN() 283 { 284 return matchedDN; 285 } 286 287 288 289 /** 290 * Retrieves the diagnostic message for this extended response, if any. 291 * 292 * @return The diagnostic message for this extended response, or {@code null} 293 * if there is no diagnostic message. 294 */ 295 public String getDiagnosticMessage() 296 { 297 return diagnosticMessage; 298 } 299 300 301 302 /** 303 * Retrieves the list of referral URLs for this extended response. 304 * 305 * @return The list of referral URLs for this extended response, or an empty 306 * list if there are no referral URLs. 307 */ 308 public List<String> getReferralURLs() 309 { 310 return referralURLs; 311 } 312 313 314 315 /** 316 * Retrieves the OID for this extended response, if any. 317 * 318 * @return The OID for this extended response, or {@code null} if there is no 319 * response OID. 320 */ 321 public String getResponseOID() 322 { 323 return responseOID; 324 } 325 326 327 328 /** 329 * Retrieves the value for this extended response, if any. 330 * 331 * @return The value for this extended response, or {@code null} if there is 332 * no response value. 333 */ 334 public ASN1OctetString getResponseValue() 335 { 336 return responseValue; 337 } 338 339 340 341 /** 342 * {@inheritDoc} 343 */ 344 public byte getProtocolOpType() 345 { 346 return LDAPMessage.PROTOCOL_OP_TYPE_EXTENDED_RESPONSE; 347 } 348 349 350 351 /** 352 * {@inheritDoc} 353 */ 354 public ASN1Element encodeProtocolOp() 355 { 356 final ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(6); 357 elements.add(new ASN1Enumerated(getResultCode())); 358 359 final String mdn = getMatchedDN(); 360 if (mdn == null) 361 { 362 elements.add(new ASN1OctetString()); 363 } 364 else 365 { 366 elements.add(new ASN1OctetString(mdn)); 367 } 368 369 final String dm = getDiagnosticMessage(); 370 if (dm == null) 371 { 372 elements.add(new ASN1OctetString()); 373 } 374 else 375 { 376 elements.add(new ASN1OctetString(dm)); 377 } 378 379 final List<String> refs = getReferralURLs(); 380 if (! refs.isEmpty()) 381 { 382 final ArrayList<ASN1Element> refElements = 383 new ArrayList<ASN1Element>(refs.size()); 384 for (final String r : refs) 385 { 386 refElements.add(new ASN1OctetString(r)); 387 } 388 elements.add(new ASN1Sequence(GenericResponseProtocolOp.TYPE_REFERRALS, 389 refElements)); 390 } 391 392 if (responseOID != null) 393 { 394 elements.add(new ASN1OctetString(TYPE_RESPONSE_OID, responseOID)); 395 } 396 397 if (responseValue != null) 398 { 399 elements.add(responseValue); 400 } 401 402 return new ASN1Sequence(LDAPMessage.PROTOCOL_OP_TYPE_EXTENDED_RESPONSE, 403 elements); 404 } 405 406 407 408 /** 409 * Decodes the provided ASN.1 element as an extended response protocol op. 410 * 411 * @param element The ASN.1 element to be decoded. 412 * 413 * @return The decoded extended response protocol op. 414 * 415 * @throws LDAPException If the provided ASN.1 element cannot be decoded as 416 * an extended response protocol op. 417 */ 418 public static ExtendedResponseProtocolOp decodeProtocolOp( 419 final ASN1Element element) 420 throws LDAPException 421 { 422 try 423 { 424 final ASN1Element[] elements = 425 ASN1Sequence.decodeAsSequence(element).elements(); 426 final int resultCode = 427 ASN1Enumerated.decodeAsEnumerated(elements[0]).intValue(); 428 429 final String matchedDN; 430 final String md = 431 ASN1OctetString.decodeAsOctetString(elements[1]).stringValue(); 432 if (md.length() > 0) 433 { 434 matchedDN = md; 435 } 436 else 437 { 438 matchedDN = null; 439 } 440 441 final String diagnosticMessage; 442 final String dm = 443 ASN1OctetString.decodeAsOctetString(elements[2]).stringValue(); 444 if (dm.length() > 0) 445 { 446 diagnosticMessage = dm; 447 } 448 else 449 { 450 diagnosticMessage = null; 451 } 452 453 ASN1OctetString responseValue = null; 454 List<String> referralURLs = null; 455 String responseOID = null; 456 if (elements.length > 3) 457 { 458 for (int i=3; i < elements.length; i++) 459 { 460 switch (elements[i].getType()) 461 { 462 case GenericResponseProtocolOp.TYPE_REFERRALS: 463 final ASN1Element[] refElements = 464 ASN1Sequence.decodeAsSequence(elements[3]).elements(); 465 referralURLs = new ArrayList<String>(refElements.length); 466 for (final ASN1Element e : refElements) 467 { 468 referralURLs.add( 469 ASN1OctetString.decodeAsOctetString(e).stringValue()); 470 } 471 break; 472 473 case TYPE_RESPONSE_OID: 474 responseOID = ASN1OctetString.decodeAsOctetString(elements[i]). 475 stringValue(); 476 break; 477 478 case TYPE_RESPONSE_VALUE: 479 responseValue = ASN1OctetString.decodeAsOctetString(elements[i]); 480 break; 481 482 default: 483 throw new LDAPException(ResultCode.DECODING_ERROR, 484 ERR_EXTENDED_RESPONSE_INVALID_ELEMENT.get( 485 toHex(elements[i].getType()))); 486 } 487 } 488 } 489 490 return new ExtendedResponseProtocolOp(resultCode, matchedDN, 491 diagnosticMessage, referralURLs, responseOID, responseValue); 492 } 493 catch (final LDAPException le) 494 { 495 debugException(le); 496 throw le; 497 } 498 catch (final Exception e) 499 { 500 debugException(e); 501 throw new LDAPException(ResultCode.DECODING_ERROR, 502 ERR_EXTENDED_RESPONSE_CANNOT_DECODE.get(getExceptionMessage(e)), 503 e); 504 } 505 } 506 507 508 509 /** 510 * {@inheritDoc} 511 */ 512 public void writeTo(final ASN1Buffer buffer) 513 { 514 final ASN1BufferSequence opSequence = 515 buffer.beginSequence(LDAPMessage.PROTOCOL_OP_TYPE_EXTENDED_RESPONSE); 516 buffer.addEnumerated(resultCode); 517 buffer.addOctetString(matchedDN); 518 buffer.addOctetString(diagnosticMessage); 519 520 if (! referralURLs.isEmpty()) 521 { 522 final ASN1BufferSequence refSequence = 523 buffer.beginSequence(GenericResponseProtocolOp.TYPE_REFERRALS); 524 for (final String s : referralURLs) 525 { 526 buffer.addOctetString(s); 527 } 528 refSequence.end(); 529 } 530 531 if (responseOID != null) 532 { 533 buffer.addOctetString(TYPE_RESPONSE_OID, responseOID); 534 } 535 536 if (responseValue != null) 537 { 538 buffer.addOctetString(TYPE_RESPONSE_VALUE, responseValue.getValue()); 539 } 540 541 opSequence.end(); 542 } 543 544 545 546 /** 547 * Creates a extended result from this protocol op. 548 * 549 * @param controls The set of controls to include in the extended result. 550 * It may be empty or {@code null} if no controls should be 551 * included. 552 * 553 * @return The extended result that was created. 554 */ 555 public ExtendedResult toExtendedResult(final Control... controls) 556 { 557 final String[] referralArray = new String[referralURLs.size()]; 558 referralURLs.toArray(referralArray); 559 560 return new ExtendedResult(-1, ResultCode.valueOf(resultCode), 561 diagnosticMessage, matchedDN, referralArray, responseOID, 562 responseValue, controls); 563 } 564 565 566 567 /** 568 * Retrieves a string representation of this protocol op. 569 * 570 * @return A string representation of this protocol op. 571 */ 572 @Override() 573 public String toString() 574 { 575 final StringBuilder buffer = new StringBuilder(); 576 toString(buffer); 577 return buffer.toString(); 578 } 579 580 581 582 /** 583 * {@inheritDoc} 584 */ 585 public void toString(final StringBuilder buffer) 586 { 587 buffer.append("ExtendedResponseProtocolOp(resultCode="); 588 buffer.append(resultCode); 589 590 if (matchedDN != null) 591 { 592 buffer.append(", matchedDN='"); 593 buffer.append(matchedDN); 594 buffer.append('\''); 595 } 596 597 if (diagnosticMessage != null) 598 { 599 buffer.append(", diagnosticMessage='"); 600 buffer.append(diagnosticMessage); 601 buffer.append('\''); 602 } 603 604 if (! referralURLs.isEmpty()) 605 { 606 buffer.append(", referralURLs={"); 607 608 final Iterator<String> iterator = referralURLs.iterator(); 609 while (iterator.hasNext()) 610 { 611 buffer.append('\''); 612 buffer.append(iterator.next()); 613 buffer.append('\''); 614 if (iterator.hasNext()) 615 { 616 buffer.append(','); 617 } 618 } 619 620 buffer.append('}'); 621 } 622 623 if (responseOID != null) 624 { 625 buffer.append(", responseOID='"); 626 buffer.append(responseOID); 627 buffer.append('\''); 628 } 629 630 buffer.append(')'); 631 } 632 }