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