001/* 002 * Copyright 2008-2019 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2015-2019 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.unboundidds.controls; 022 023 024 025import java.io.Serializable; 026import java.util.ArrayList; 027 028import com.unboundid.asn1.ASN1Boolean; 029import com.unboundid.asn1.ASN1Constants; 030import com.unboundid.asn1.ASN1Element; 031import com.unboundid.asn1.ASN1OctetString; 032import com.unboundid.asn1.ASN1Sequence; 033import com.unboundid.ldap.sdk.LDAPException; 034import com.unboundid.ldap.sdk.ResultCode; 035import com.unboundid.util.Debug; 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.unboundidds.controls.ControlMessages.*; 042 043 044 045/** 046 * This class implements a data structure which encapsulates the value of an 047 * intermediate client response value. It may recursively embed intermediate 048 * client response values from upstream servers. 049 * <BR> 050 * <BLOCKQUOTE> 051 * <B>NOTE:</B> This class, and other classes within the 052 * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only 053 * supported for use against Ping Identity, UnboundID, and 054 * Nokia/Alcatel-Lucent 8661 server products. These classes provide support 055 * for proprietary functionality or for external specifications that are not 056 * considered stable or mature enough to be guaranteed to work in an 057 * interoperable way with other types of LDAP servers. 058 * </BLOCKQUOTE> 059 * <BR> 060 * See the documentation in the {@link IntermediateClientRequestControl} class 061 * for an example of using the intermediate client request and response 062 * controls. 063 */ 064@NotMutable() 065@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 066public final class IntermediateClientResponseValue 067 implements Serializable 068{ 069 /** 070 * The BER type for the upstreamResponse element. 071 */ 072 private static final byte TYPE_UPSTREAM_RESPONSE = (byte) 0xA0; 073 074 075 076 /** 077 * The BER type for the upstreamServerAddress element. 078 */ 079 private static final byte TYPE_UPSTREAM_SERVER_ADDRESS = (byte) 0x81; 080 081 082 083 /** 084 * The BER type for the upstreamServerSecure element. 085 */ 086 private static final byte TYPE_UPSTREAM_SERVER_SECURE = (byte) 0x82; 087 088 089 090 /** 091 * The BER type for the serverName element. 092 */ 093 private static final byte TYPE_SERVER_NAME = (byte) 0x83; 094 095 096 097 /** 098 * The BER type for the serverSessionID element. 099 */ 100 private static final byte TYPE_SERVER_SESSION_ID = (byte) 0x84; 101 102 103 104 /** 105 * The BER type for the serverResponseID element. 106 */ 107 private static final byte TYPE_SERVER_RESPONSE_ID = (byte) 0x85; 108 109 110 111 /** 112 * The serial version UID for this serializable class. 113 */ 114 private static final long serialVersionUID = 5165171788442351399L; 115 116 117 118 // Indicates whether communication with the upstream server is secure. 119 private final Boolean upstreamServerSecure; 120 121 // The upstream response, if available. 122 private final IntermediateClientResponseValue upstreamResponse; 123 124 // The server name, which describes the server application, if present. 125 private final String serverName; 126 127 // The server response ID, if present. 128 private final String serverResponseID; 129 130 // The server session ID, if present. 131 private final String serverSessionID; 132 133 // The address of the upstream server, if available. 134 private final String upstreamServerAddress; 135 136 137 138 /** 139 * Creates a new intermediate client response value with the provided 140 * information. 141 * 142 * @param upstreamResponse A wrapped intermediate client response from 143 * an upstream server. It may be {@code null} 144 * if there is no wrapped upstream response. 145 * @param upstreamServerAddress The IP address or resolvable name of the 146 * upstream server system. It may be 147 * {@code null} if there is no upstream server 148 * or its address is not available. 149 * @param upstreamServerSecure Indicates whether communication with the 150 * upstream server is secure. It may be 151 * {@code null} if there is no upstream server 152 * or it is not known whether the communication 153 * is secure. 154 * @param serverName An identifier string that summarizes the 155 * server application that created this 156 * intermediate client response. It may be 157 * {@code null} if that information is not 158 * available. 159 * @param serverSessionID A string that may be used to identify the 160 * session in the server application. It may 161 * be {@code null} if there is no available 162 * session identifier. 163 * @param serverResponseID A string that may be used to identify the 164 * response in the server application. It may 165 * be {@code null} if there is no available 166 * response identifier. 167 */ 168 public IntermediateClientResponseValue( 169 final IntermediateClientResponseValue upstreamResponse, 170 final String upstreamServerAddress, 171 final Boolean upstreamServerSecure, final String serverName, 172 final String serverSessionID, final String serverResponseID) 173 { 174 this.upstreamResponse = upstreamResponse; 175 this.upstreamServerAddress = upstreamServerAddress; 176 this.upstreamServerSecure = upstreamServerSecure; 177 this.serverName = serverName; 178 this.serverSessionID = serverSessionID; 179 this.serverResponseID = serverResponseID; 180 } 181 182 183 184 /** 185 * Retrieves the wrapped response from an upstream server, if available. 186 * 187 * @return The wrapped response from an upstream server, or {@code null} if 188 * there is none. 189 */ 190 public IntermediateClientResponseValue getUpstreamResponse() 191 { 192 return upstreamResponse; 193 } 194 195 196 197 /** 198 * Retrieves the IP address or resolvable name of the upstream server system, 199 * if available. 200 * 201 * @return The IP address or resolvable name of the upstream server system, 202 * {@code null} if there is no upstream server or its address is not 203 * available. 204 */ 205 public String getUpstreamServerAddress() 206 { 207 return upstreamServerAddress; 208 } 209 210 211 212 /** 213 * Indicates whether the communication with the communication with the 214 * upstream server is secure (i.e., whether communication between the 215 * server application and the upstream server is safe from interpretation or 216 * undetectable alteration by a third party observer or interceptor). 217 * 218 * 219 * @return {@code Boolean.TRUE} if communication with the upstream server is 220 * secure, {@code Boolean.FALSE} if it is not secure, or 221 * {@code null} if there is no upstream server or it is not known 222 * whether the communication is secure. 223 */ 224 public Boolean upstreamServerSecure() 225 { 226 return upstreamServerSecure; 227 } 228 229 230 231 /** 232 * Retrieves a string that identifies the server application that created this 233 * intermediate client response value. 234 * 235 * @return A string that may be used to identify the server application that 236 * created this intermediate client response value. 237 */ 238 public String getServerName() 239 { 240 return serverName; 241 } 242 243 244 245 /** 246 * Retrieves a string that may be used to identify the session in the server 247 * application. 248 * 249 * @return A string that may be used to identify the session in the server 250 * application, or {@code null} if there is none. 251 */ 252 public String getServerSessionID() 253 { 254 return serverSessionID; 255 } 256 257 258 259 /** 260 * Retrieves a string that may be used to identify the response in the server 261 * application. 262 * 263 * @return A string that may be used to identify the response in the server 264 * application, or {@code null} if there is none. 265 */ 266 public String getServerResponseID() 267 { 268 return serverResponseID; 269 } 270 271 272 273 /** 274 * Encodes this intermediate client response value to a form that may be 275 * included in the response control. 276 * 277 * @return An ASN.1 octet string containing the encoded client response 278 * value. 279 */ 280 public ASN1Sequence encode() 281 { 282 return encode(ASN1Constants.UNIVERSAL_SEQUENCE_TYPE); 283 } 284 285 286 287 /** 288 * Encodes this intermediate client response value to a form that may be 289 * included in the response control. 290 * 291 * @param type The BER type to use for this element. 292 * 293 * @return An ASN.1 octet string containing the encoded client response 294 * value. 295 */ 296 private ASN1Sequence encode(final byte type) 297 { 298 final ArrayList<ASN1Element> elements = new ArrayList<>(6); 299 300 if (upstreamResponse != null) 301 { 302 elements.add(upstreamResponse.encode(TYPE_UPSTREAM_RESPONSE)); 303 } 304 305 if (upstreamServerAddress != null) 306 { 307 elements.add(new ASN1OctetString(TYPE_UPSTREAM_SERVER_ADDRESS, 308 upstreamServerAddress)); 309 } 310 311 if (upstreamServerSecure != null) 312 { 313 elements.add(new ASN1Boolean(TYPE_UPSTREAM_SERVER_SECURE, 314 upstreamServerSecure)); 315 } 316 317 if (serverName != null) 318 { 319 elements.add(new ASN1OctetString(TYPE_SERVER_NAME, serverName)); 320 } 321 322 if (serverSessionID != null) 323 { 324 elements.add(new ASN1OctetString(TYPE_SERVER_SESSION_ID, 325 serverSessionID)); 326 } 327 328 if (serverResponseID != null) 329 { 330 elements.add(new ASN1OctetString(TYPE_SERVER_RESPONSE_ID, 331 serverResponseID)); 332 } 333 334 return new ASN1Sequence(type, elements); 335 } 336 337 338 339 /** 340 * Decodes the provided ASN.1 sequence as an intermediate client response 341 * value. 342 * 343 * @param sequence The sequence to be decoded as an intermediate client 344 * response value. 345 * 346 * @return The decoded intermediate client response value. 347 * 348 * @throws LDAPException If the provided sequence cannot be decoded as an 349 * intermediate client response value. 350 */ 351 public static IntermediateClientResponseValue 352 decode(final ASN1Sequence sequence) 353 throws LDAPException 354 { 355 Boolean upstreamServerSecure = null; 356 IntermediateClientResponseValue upstreamResponse = null; 357 String upstreamServerAddress = null; 358 String serverName = null; 359 String serverResponseID = null; 360 String serverSessionID = null; 361 362 for (final ASN1Element element : sequence.elements()) 363 { 364 switch (element.getType()) 365 { 366 case TYPE_UPSTREAM_RESPONSE: 367 try 368 { 369 final ASN1Sequence s = ASN1Sequence.decodeAsSequence(element); 370 upstreamResponse = decode(s); 371 } 372 catch (final LDAPException le) 373 { 374 Debug.debugException(le); 375 throw new LDAPException(ResultCode.DECODING_ERROR, 376 ERR_ICRESP_CANNOT_DECODE_UPSTREAM_RESPONSE.get( 377 le.getMessage()), le); 378 } 379 catch (final Exception e) 380 { 381 Debug.debugException(e); 382 throw new LDAPException(ResultCode.DECODING_ERROR, 383 ERR_ICRESP_CANNOT_DECODE_UPSTREAM_RESPONSE.get( 384 StaticUtils.getExceptionMessage(e)), 385 e); 386 } 387 break; 388 389 case TYPE_UPSTREAM_SERVER_ADDRESS: 390 upstreamServerAddress = 391 ASN1OctetString.decodeAsOctetString(element).stringValue(); 392 break; 393 394 case TYPE_UPSTREAM_SERVER_SECURE: 395 try 396 { 397 upstreamServerSecure = 398 ASN1Boolean.decodeAsBoolean(element).booleanValue(); 399 } 400 catch (final Exception e) 401 { 402 Debug.debugException(e); 403 throw new LDAPException(ResultCode.DECODING_ERROR, 404 ERR_ICRESP_CANNOT_DECODE_UPSTREAM_SECURE.get( 405 StaticUtils.getExceptionMessage(e)), 406 e); 407 } 408 break; 409 410 case TYPE_SERVER_NAME: 411 serverName = 412 ASN1OctetString.decodeAsOctetString(element).stringValue(); 413 break; 414 415 case TYPE_SERVER_SESSION_ID: 416 serverSessionID = 417 ASN1OctetString.decodeAsOctetString(element).stringValue(); 418 break; 419 420 case TYPE_SERVER_RESPONSE_ID: 421 serverResponseID = 422 ASN1OctetString.decodeAsOctetString(element).stringValue(); 423 break; 424 425 default: 426 throw new LDAPException(ResultCode.DECODING_ERROR, 427 ERR_ICRESP_INVALID_ELEMENT_TYPE.get( 428 StaticUtils.toHex(element.getType()))); 429 } 430 } 431 432 return new IntermediateClientResponseValue(upstreamResponse, 433 upstreamServerAddress, 434 upstreamServerSecure, 435 serverName, serverSessionID, 436 serverResponseID); 437 } 438 439 440 441 /** 442 * Generates a hash code for this intermediate client response value. 443 * 444 * @return A hash code for this intermediate client response value. 445 */ 446 @Override() 447 public int hashCode() 448 { 449 int hashCode = 0; 450 451 if (upstreamResponse != null) 452 { 453 hashCode += upstreamResponse.hashCode(); 454 } 455 456 if (upstreamServerAddress != null) 457 { 458 hashCode += upstreamServerAddress.hashCode(); 459 } 460 461 if (upstreamServerSecure != null) 462 { 463 hashCode += upstreamServerSecure.hashCode(); 464 } 465 466 if (serverName != null) 467 { 468 hashCode += serverName.hashCode(); 469 } 470 471 if (serverSessionID != null) 472 { 473 hashCode += serverSessionID.hashCode(); 474 } 475 476 if (serverResponseID != null) 477 { 478 hashCode += serverResponseID.hashCode(); 479 } 480 481 return hashCode; 482 } 483 484 485 486 /** 487 * Indicates whether the provided object is equal to this intermediate client 488 * response value. It will only be considered equal if the provided object is 489 * also an intermediate client response value with all the same fields. 490 * 491 * @param o The object for which to make the determination. 492 * 493 * @return {@code true} if the provided object is considered equal to this 494 * intermediate client response value, or {@code false} if not. 495 */ 496 @Override() 497 public boolean equals(final Object o) 498 { 499 if (o == this) 500 { 501 return true; 502 } 503 else if (o == null) 504 { 505 return false; 506 } 507 else if (! (o instanceof IntermediateClientResponseValue)) 508 { 509 return false; 510 } 511 512 final IntermediateClientResponseValue v = 513 (IntermediateClientResponseValue) o; 514 515 if (upstreamResponse == null) 516 { 517 if (v.upstreamResponse != null) 518 { 519 return false; 520 } 521 } 522 else 523 { 524 if (! upstreamResponse.equals(v.upstreamResponse)) 525 { 526 return false; 527 } 528 } 529 530 if (upstreamServerAddress == null) 531 { 532 if (v.upstreamServerAddress != null) 533 { 534 return false; 535 } 536 } 537 else 538 { 539 if (! upstreamServerAddress.equals(v.upstreamServerAddress)) 540 { 541 return false; 542 } 543 } 544 545 if (upstreamServerSecure == null) 546 { 547 if (v.upstreamServerSecure != null) 548 { 549 return false; 550 } 551 } 552 else 553 { 554 if (! upstreamServerSecure.equals(v.upstreamServerSecure)) 555 { 556 return false; 557 } 558 } 559 560 if (serverName == null) 561 { 562 if (v.serverName != null) 563 { 564 return false; 565 } 566 } 567 else 568 { 569 if (! serverName.equals(v.serverName)) 570 { 571 return false; 572 } 573 } 574 575 if (serverSessionID == null) 576 { 577 if (v.serverSessionID != null) 578 { 579 return false; 580 } 581 } 582 else 583 { 584 if (! serverSessionID.equals(v.serverSessionID)) 585 { 586 return false; 587 } 588 } 589 590 if (serverResponseID == null) 591 { 592 if (v.serverResponseID != null) 593 { 594 return false; 595 } 596 } 597 else 598 { 599 if (! serverResponseID.equals(v.serverResponseID)) 600 { 601 return false; 602 } 603 } 604 605 return true; 606 } 607 608 609 610 /** 611 * Retrieves a string representation of this intermediate client response 612 * value. 613 * 614 * @return A string representation of this intermediate client response 615 * value. 616 */ 617 @Override() 618 public String toString() 619 { 620 final StringBuilder buffer = new StringBuilder(); 621 toString(buffer); 622 return buffer.toString(); 623 } 624 625 626 627 /** 628 * Appends a string representation of this intermediate client response value 629 * to the provided buffer. 630 * 631 * @param buffer The buffer to which the information is to be appended. 632 */ 633 public void toString(final StringBuilder buffer) 634 { 635 buffer.append("IntermediateClientResponseValue("); 636 637 boolean added = false; 638 if (upstreamResponse != null) 639 { 640 buffer.append("upstreamResponse="); 641 upstreamResponse.toString(buffer); 642 added = true; 643 } 644 645 if (upstreamServerAddress != null) 646 { 647 if (added) 648 { 649 buffer.append(", "); 650 } 651 else 652 { 653 added = true; 654 } 655 656 buffer.append("upstreamServerAddress='"); 657 buffer.append(upstreamServerAddress); 658 buffer.append('\''); 659 } 660 661 if (upstreamServerSecure != null) 662 { 663 if (added) 664 { 665 buffer.append(", "); 666 } 667 else 668 { 669 added = true; 670 } 671 672 buffer.append("upstreamServerSecure='"); 673 buffer.append(upstreamServerSecure); 674 buffer.append('\''); 675 } 676 677 if (serverName != null) 678 { 679 if (added) 680 { 681 buffer.append(", "); 682 } 683 else 684 { 685 added = true; 686 } 687 688 buffer.append("serverName='"); 689 buffer.append(serverName); 690 buffer.append('\''); 691 } 692 693 if (serverSessionID != null) 694 { 695 if (added) 696 { 697 buffer.append(", "); 698 } 699 else 700 { 701 added = true; 702 } 703 704 buffer.append("serverSessionID='"); 705 buffer.append(serverSessionID); 706 buffer.append('\''); 707 } 708 709 if (serverResponseID != null) 710 { 711 if (added) 712 { 713 buffer.append(", "); 714 } 715 else 716 { 717 added = true; 718 } 719 720 buffer.append("serverResponseID='"); 721 buffer.append(serverResponseID); 722 buffer.append('\''); 723 } 724 725 buffer.append(')'); 726 } 727}