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 */ 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 (LDAPException le) 278 { 279 debugException(le); 280 throw le; 281 } 282 catch (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 public int getMessageID() 297 { 298 return messageID; 299 } 300 301 302 303 /** 304 * Retrieves the OID for this intermediate response, if any. 305 * 306 * @return The OID for this intermediate response, or {@code null} if there 307 * is no OID for this response. 308 */ 309 public final String getOID() 310 { 311 return oid; 312 } 313 314 315 316 /** 317 * Retrieves the encoded value for this intermediate response, if any. 318 * 319 * @return The encoded value for this intermediate response, or {@code null} 320 * if there is no value for this response. 321 */ 322 public final ASN1OctetString getValue() 323 { 324 return value; 325 } 326 327 328 329 /** 330 * Retrieves the set of controls returned with this intermediate response. 331 * Individual response controls of a specific type may be retrieved and 332 * decoded using the {@code get} method in the response control class. 333 * 334 * @return The set of controls returned with this intermediate response. 335 */ 336 public final Control[] getControls() 337 { 338 return controls; 339 } 340 341 342 343 /** 344 * Retrieves the control with the specified OID. If there is more than one 345 * control with the given OID, then the first will be returned. 346 * 347 * @param oid The OID of the control to retrieve. 348 * 349 * @return The control with the requested OID, or {@code null} if there is no 350 * such control for this intermediate response. 351 */ 352 public final Control getControl(final String oid) 353 { 354 for (final Control c : controls) 355 { 356 if (c.getOID().equals(oid)) 357 { 358 return c; 359 } 360 } 361 362 return null; 363 } 364 365 366 367 /** 368 * Retrieves the user-friendly name for the intermediate response, if 369 * available. If no user-friendly name has been defined, but a response OID 370 * is available, then that will be returned. If neither a user-friendly name 371 * nor a response OID are available, then {@code null} will be returned. 372 * 373 * @return The user-friendly name for this intermediate response, the 374 * response OID if a user-friendly name is not available but a 375 * response OID is, or {@code null} if neither a user-friendly name 376 * nor a response OID are available. 377 */ 378 public String getIntermediateResponseName() 379 { 380 // By default, we will return the OID (which may be null). Subclasses 381 // should override this to provide the user-friendly name. 382 return oid; 383 } 384 385 386 387 /** 388 * Retrieves a human-readable string representation for the contents of the 389 * value for this intermediate response, if appropriate. If one is provided, 390 * then it should be a relatively compact single-line representation of the 391 * most important elements of the value. 392 * 393 * @return A human-readable string representation for the contents of the 394 * value for this intermediate response, or {@code null} if there is 395 * no value or no string representation is available. 396 */ 397 public String valueToString() 398 { 399 return null; 400 } 401 402 403 404 /** 405 * Retrieves a string representation of this intermediate response. 406 * 407 * @return A string representation of this intermediate response. 408 */ 409 @Override() 410 public final String toString() 411 { 412 final StringBuilder buffer = new StringBuilder(); 413 toString(buffer); 414 return buffer.toString(); 415 } 416 417 418 419 /** 420 * Appends a string representation of this intermediate response to the 421 * provided buffer. 422 * 423 * @param buffer The buffer to which the string representation should be 424 * appended. 425 */ 426 public void toString(final StringBuilder buffer) 427 { 428 buffer.append("IntermediateResponse("); 429 430 boolean added = false; 431 432 if (messageID >= 0) 433 { 434 buffer.append("messageID="); 435 buffer.append(messageID); 436 added = true; 437 } 438 439 if (oid != null) 440 { 441 if (added) 442 { 443 buffer.append(", "); 444 } 445 446 buffer.append("oid='"); 447 buffer.append(oid); 448 buffer.append('\''); 449 added = true; 450 } 451 452 if (controls.length > 0) 453 { 454 if (added) 455 { 456 buffer.append(", "); 457 } 458 459 buffer.append("controls={"); 460 for (int i=0; i < controls.length; i++) 461 { 462 if (i > 0) 463 { 464 buffer.append(", "); 465 } 466 467 buffer.append(controls[i]); 468 } 469 buffer.append('}'); 470 } 471 472 buffer.append(')'); 473 } 474}