001 /*
002 * Copyright 2009-2016 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2009-2016 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 import com.unboundid.util.Extensible;
033 import com.unboundid.util.NotMutable;
034 import com.unboundid.util.ThreadSafety;
035 import com.unboundid.util.ThreadSafetyLevel;
036
037 import static com.unboundid.ldap.sdk.LDAPMessages.*;
038 import static com.unboundid.util.Debug.*;
039 import 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)
069 public 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 }