001/* 002 * Copyright 2009-2018 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2009-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; 027 028import com.unboundid.asn1.ASN1OctetString; 029import com.unboundid.asn1.ASN1StreamReader; 030import com.unboundid.asn1.ASN1StreamReaderSequence; 031import com.unboundid.ldap.protocol.LDAPResponse; 032import com.unboundid.util.Extensible; 033import com.unboundid.util.NotMutable; 034import com.unboundid.util.ThreadSafety; 035import com.unboundid.util.ThreadSafetyLevel; 036 037import static com.unboundid.ldap.sdk.LDAPMessages.*; 038import static com.unboundid.util.Debug.*; 039import static com.unboundid.util.StaticUtils.*; 040 041 042 043/** 044 * This class provides a data structure for holding information about an LDAP 045 * intermediate response, which provides the ability for the directory server to 046 * return multiple messages in response to operations that would not otherwise 047 * support it. Intermediate response messages will only be returned by the 048 * server if the client does something to explicitly indicate that it is able 049 * to accept them (e.g., by requesting an extended operation that may return 050 * intermediate response messages, or by including a control in a request that 051 * may cause the request to return intermediate response messages). 052 * Intermediate response messages may include one or both of the following: 053 * <UL> 054 * <LI>Response OID -- An optional OID that can be used to identify the type 055 * of intermediate response.</LI> 056 * <LI>Value -- An optional element that provides the encoded value for this 057 * intermediate response. If a value is provided, then the encoding for 058 * the value depends on the type of intermediate response.</LI> 059 * </UL> 060 * When requesting an operation which may return intermediate response messages, 061 * an {@link IntermediateResponseListener} must be provided for the associated 062 * request. If an intermediate response message is returned for a request that 063 * does not have a registered {@code IntermediateResponseListener}, then it will 064 * be silently discarded. 065 */ 066@Extensible() 067@NotMutable() 068@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 069public class IntermediateResponse 070 implements Serializable, LDAPResponse 071{ 072 /** 073 * The BER type for the intermediate response OID element. 074 */ 075 protected static final byte TYPE_INTERMEDIATE_RESPONSE_OID = (byte) 0x80; 076 077 078 079 /** 080 * The BER type for the intermediate response value element. 081 */ 082 protected static final byte TYPE_INTERMEDIATE_RESPONSE_VALUE = (byte) 0x81; 083 084 085 086 /** 087 * An empty set of controls that will be used if no controls are provided. 088 */ 089 private static final Control[] NO_CONTROLS = new Control[0]; 090 091 092 093 /** 094 * The serial version UID for this serializable class. 095 */ 096 private static final long serialVersionUID = 218434694212935869L; 097 098 099 100 // The encoded value for this intermediate response, if available. 101 private final ASN1OctetString value; 102 103 // The set of controls for this intermediate response. 104 private final Control[] controls; 105 106 // The message ID for this intermediate response. 107 private final int messageID; 108 109 // The OID for this extended request. 110 private final String oid; 111 112 113 114 /** 115 * Creates a new intermediate response with the provided information. 116 * 117 * @param oid The OID for this intermediate response. It may be 118 * {@code null} if there is no OID. 119 * @param value The value for this intermediate response. It may be 120 * {@code null} if there is no value. 121 */ 122 public IntermediateResponse(final String oid, final ASN1OctetString value) 123 { 124 this(-1, oid, value, NO_CONTROLS); 125 } 126 127 128 129 /** 130 * Creates a new intermediate response with the provided information. 131 * 132 * @param messageID The message ID for the LDAP message containing this 133 * intermediate response. 134 * @param oid The OID for this intermediate response. It may be 135 * {@code null} if there is no OID. 136 * @param value The value for this intermediate response. It may be 137 * {@code null} if there is no value. 138 */ 139 public IntermediateResponse(final int messageID, final String oid, 140 final ASN1OctetString value) 141 { 142 this(messageID, oid, value, NO_CONTROLS); 143 } 144 145 146 147 /** 148 * Creates a new intermediate response with the provided information. 149 * 150 * @param oid The OID for this intermediate response. It may be 151 * {@code null} if there is no OID. 152 * @param value The value for this intermediate response. It may be 153 * {@code null} if there is no value. 154 * @param controls The set of controls for this intermediate response. 155 */ 156 public IntermediateResponse(final String oid, final ASN1OctetString value, 157 final Control[] controls) 158 { 159 this(-1, oid, value, controls); 160 } 161 162 163 164 /** 165 * Creates a new intermediate response with the provided information. 166 * 167 * @param messageID The message ID for the LDAP message containing this 168 * intermediate response. 169 * @param oid The OID for this intermediate response. It may be 170 * {@code null} if there is no OID. 171 * @param value The value for this intermediate response. It may be 172 * {@code null} if there is no value. 173 * @param controls The set of controls for this intermediate response. 174 */ 175 public IntermediateResponse(final int messageID, final String oid, 176 final ASN1OctetString value, 177 final Control[] controls) 178 { 179 this.messageID = messageID; 180 this.oid = oid; 181 this.value = value; 182 183 if (controls == null) 184 { 185 this.controls = NO_CONTROLS; 186 } 187 else 188 { 189 this.controls = controls; 190 } 191 } 192 193 194 195 /** 196 * Creates a new intermediate response with the information from the provided 197 * intermediate response. 198 * 199 * @param intermediateResponse The intermediate response that should be used 200 * to create this new intermediate response. 201 */ 202 protected IntermediateResponse( 203 final IntermediateResponse intermediateResponse) 204 { 205 messageID = intermediateResponse.messageID; 206 oid = intermediateResponse.oid; 207 value = intermediateResponse.value; 208 controls = intermediateResponse.controls; 209 } 210 211 212 213 /** 214 * Creates a new intermediate response object with the provided message ID and 215 * with the protocol op and controls read from the given ASN.1 stream reader. 216 * 217 * @param messageID The LDAP message ID for the LDAP message that is 218 * associated with this intermediate response. 219 * @param messageSequence The ASN.1 stream reader sequence used in the 220 * course of reading the LDAP message elements. 221 * @param reader The ASN.1 stream reader from which to read the 222 * protocol op and controls. 223 * 224 * @return The decoded intermediate response. 225 * 226 * @throws LDAPException If a problem occurs while reading or decoding data 227 * from the ASN.1 stream reader. 228 */ 229 static IntermediateResponse readFrom(final int messageID, 230 final ASN1StreamReaderSequence messageSequence, 231 final ASN1StreamReader reader) 232 throws LDAPException 233 { 234 try 235 { 236 String oid = null; 237 ASN1OctetString value = null; 238 239 final ASN1StreamReaderSequence opSequence = reader.beginSequence(); 240 while (opSequence.hasMoreElements()) 241 { 242 final byte type = (byte) reader.peek(); 243 switch (type) 244 { 245 case TYPE_INTERMEDIATE_RESPONSE_OID: 246 oid = reader.readString(); 247 break; 248 case TYPE_INTERMEDIATE_RESPONSE_VALUE: 249 value = new ASN1OctetString(type, reader.readBytes()); 250 break; 251 default: 252 throw new LDAPException(ResultCode.DECODING_ERROR, 253 ERR_INTERMEDIATE_RESPONSE_INVALID_ELEMENT.get(toHex(type))); 254 } 255 } 256 257 final Control[] controls; 258 if (messageSequence.hasMoreElements()) 259 { 260 final ArrayList<Control> controlList = new ArrayList<Control>(1); 261 final ASN1StreamReaderSequence controlSequence = reader.beginSequence(); 262 while (controlSequence.hasMoreElements()) 263 { 264 controlList.add(Control.readFrom(reader)); 265 } 266 267 controls = new Control[controlList.size()]; 268 controlList.toArray(controls); 269 } 270 else 271 { 272 controls = NO_CONTROLS; 273 } 274 275 return new IntermediateResponse(messageID, oid, value, controls); 276 } 277 catch (final LDAPException le) 278 { 279 debugException(le); 280 throw le; 281 } 282 catch (final Exception e) 283 { 284 debugException(e); 285 throw new LDAPException(ResultCode.DECODING_ERROR, 286 ERR_INTERMEDIATE_RESPONSE_CANNOT_DECODE.get(getExceptionMessage(e)), 287 e); 288 } 289 } 290 291 292 293 /** 294 * {@inheritDoc} 295 */ 296 @Override() 297 public int getMessageID() 298 { 299 return messageID; 300 } 301 302 303 304 /** 305 * Retrieves the OID for this intermediate response, if any. 306 * 307 * @return The OID for this intermediate response, or {@code null} if there 308 * is no OID for this response. 309 */ 310 public final String getOID() 311 { 312 return oid; 313 } 314 315 316 317 /** 318 * Retrieves the encoded value for this intermediate response, if any. 319 * 320 * @return The encoded value for this intermediate response, or {@code null} 321 * if there is no value for this response. 322 */ 323 public final ASN1OctetString getValue() 324 { 325 return value; 326 } 327 328 329 330 /** 331 * Retrieves the set of controls returned with this intermediate response. 332 * Individual response controls of a specific type may be retrieved and 333 * decoded using the {@code get} method in the response control class. 334 * 335 * @return The set of controls returned with this intermediate response. 336 */ 337 public final Control[] getControls() 338 { 339 return controls; 340 } 341 342 343 344 /** 345 * Retrieves the control with the specified OID. If there is more than one 346 * control with the given OID, then the first will be returned. 347 * 348 * @param oid The OID of the control to retrieve. 349 * 350 * @return The control with the requested OID, or {@code null} if there is no 351 * such control for this intermediate response. 352 */ 353 public final Control getControl(final String oid) 354 { 355 for (final Control c : controls) 356 { 357 if (c.getOID().equals(oid)) 358 { 359 return c; 360 } 361 } 362 363 return null; 364 } 365 366 367 368 /** 369 * Retrieves the user-friendly name for the intermediate response, if 370 * available. If no user-friendly name has been defined, but a response OID 371 * is available, then that will be returned. If neither a user-friendly name 372 * nor a response OID are available, then {@code null} will be returned. 373 * 374 * @return The user-friendly name for this intermediate response, the 375 * response OID if a user-friendly name is not available but a 376 * response OID is, or {@code null} if neither a user-friendly name 377 * nor a response OID are available. 378 */ 379 public String getIntermediateResponseName() 380 { 381 // By default, we will return the OID (which may be null). Subclasses 382 // should override this to provide the user-friendly name. 383 return oid; 384 } 385 386 387 388 /** 389 * Retrieves a human-readable string representation for the contents of the 390 * value for this intermediate response, if appropriate. If one is provided, 391 * then it should be a relatively compact single-line representation of the 392 * most important elements of the value. 393 * 394 * @return A human-readable string representation for the contents of the 395 * value for this intermediate response, or {@code null} if there is 396 * no value or no string representation is available. 397 */ 398 public String valueToString() 399 { 400 return null; 401 } 402 403 404 405 /** 406 * Retrieves a string representation of this intermediate response. 407 * 408 * @return A string representation of this intermediate response. 409 */ 410 @Override() 411 public final String toString() 412 { 413 final StringBuilder buffer = new StringBuilder(); 414 toString(buffer); 415 return buffer.toString(); 416 } 417 418 419 420 /** 421 * Appends a string representation of this intermediate response to the 422 * provided buffer. 423 * 424 * @param buffer The buffer to which the string representation should be 425 * appended. 426 */ 427 @Override() 428 public void toString(final StringBuilder buffer) 429 { 430 buffer.append("IntermediateResponse("); 431 432 boolean added = false; 433 434 if (messageID >= 0) 435 { 436 buffer.append("messageID="); 437 buffer.append(messageID); 438 added = true; 439 } 440 441 if (oid != null) 442 { 443 if (added) 444 { 445 buffer.append(", "); 446 } 447 448 buffer.append("oid='"); 449 buffer.append(oid); 450 buffer.append('\''); 451 added = true; 452 } 453 454 if (controls.length > 0) 455 { 456 if (added) 457 { 458 buffer.append(", "); 459 } 460 461 buffer.append("controls={"); 462 for (int i=0; i < controls.length; i++) 463 { 464 if (i > 0) 465 { 466 buffer.append(", "); 467 } 468 469 buffer.append(controls[i]); 470 } 471 buffer.append('}'); 472 } 473 474 buffer.append(')'); 475 } 476}