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.Debug;
033import com.unboundid.util.Extensible;
034import com.unboundid.util.NotMutable;
035import com.unboundid.util.StaticUtils;
036import com.unboundid.util.ThreadSafety;
037import com.unboundid.util.ThreadSafetyLevel;
038
039import static com.unboundid.ldap.sdk.LDAPMessages.*;
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(
254                      StaticUtils.toHex(type)));
255        }
256      }
257
258      final Control[] controls;
259      if (messageSequence.hasMoreElements())
260      {
261        final ArrayList<Control> controlList = new ArrayList<>(1);
262        final ASN1StreamReaderSequence controlSequence = reader.beginSequence();
263        while (controlSequence.hasMoreElements())
264        {
265          controlList.add(Control.readFrom(reader));
266        }
267
268        controls = new Control[controlList.size()];
269        controlList.toArray(controls);
270      }
271      else
272      {
273        controls = NO_CONTROLS;
274      }
275
276      return new IntermediateResponse(messageID, oid, value, controls);
277    }
278    catch (final LDAPException le)
279    {
280      Debug.debugException(le);
281      throw le;
282    }
283    catch (final Exception e)
284    {
285      Debug.debugException(e);
286      throw new LDAPException(ResultCode.DECODING_ERROR,
287           ERR_INTERMEDIATE_RESPONSE_CANNOT_DECODE.get(
288                StaticUtils.getExceptionMessage(e)),
289           e);
290    }
291  }
292
293
294
295  /**
296   * {@inheritDoc}
297   */
298  @Override()
299  public int getMessageID()
300  {
301    return messageID;
302  }
303
304
305
306  /**
307   * Retrieves the OID for this intermediate response, if any.
308   *
309   * @return  The OID for this intermediate response, or {@code null} if there
310   *          is no OID for this response.
311   */
312  public final String getOID()
313  {
314    return oid;
315  }
316
317
318
319  /**
320   * Retrieves the encoded value for this intermediate response, if any.
321   *
322   * @return  The encoded value for this intermediate response, or {@code null}
323   *          if there is no value for this response.
324   */
325  public final ASN1OctetString getValue()
326  {
327    return value;
328  }
329
330
331
332  /**
333   * Retrieves the set of controls returned with this intermediate response.
334   * Individual response controls of a specific type may be retrieved and
335   * decoded using the {@code get} method in the response control class.
336   *
337   * @return  The set of controls returned with this intermediate response.
338   */
339  public final Control[] getControls()
340  {
341    return controls;
342  }
343
344
345
346  /**
347   * Retrieves the control with the specified OID.  If there is more than one
348   * control with the given OID, then the first will be returned.
349   *
350   * @param  oid  The OID of the control to retrieve.
351   *
352   * @return  The control with the requested OID, or {@code null} if there is no
353   *          such control for this intermediate response.
354   */
355  public final Control getControl(final String oid)
356  {
357    for (final Control c : controls)
358    {
359      if (c.getOID().equals(oid))
360      {
361        return c;
362      }
363    }
364
365    return null;
366  }
367
368
369
370  /**
371   * Retrieves the user-friendly name for the intermediate response, if
372   * available.  If no user-friendly name has been defined, but a response OID
373   * is available, then that will be returned.  If neither a user-friendly name
374   * nor a response OID are available, then {@code null} will be returned.
375   *
376   * @return  The user-friendly name for this intermediate response, the
377   *          response OID if a user-friendly name is not available but a
378   *          response OID is, or {@code null} if neither a user-friendly name
379   *          nor a response OID are available.
380   */
381  public String getIntermediateResponseName()
382  {
383    // By default, we will return the OID (which may be null).  Subclasses
384    // should override this to provide the user-friendly name.
385    return oid;
386  }
387
388
389
390  /**
391   * Retrieves a human-readable string representation for the contents of the
392   * value for this intermediate response, if appropriate.  If one is provided,
393   * then it should be a relatively compact single-line representation of the
394   * most important elements of the value.
395   *
396   * @return  A human-readable string representation for the contents of the
397   *          value for this intermediate response, or {@code null} if there is
398   *          no value or no string representation is available.
399   */
400  public String valueToString()
401  {
402    return null;
403  }
404
405
406
407  /**
408   * Retrieves a string representation of this intermediate response.
409   *
410   * @return  A string representation of this intermediate response.
411   */
412  @Override()
413  public final String toString()
414  {
415    final StringBuilder buffer = new StringBuilder();
416    toString(buffer);
417    return buffer.toString();
418  }
419
420
421
422  /**
423   * Appends a string representation of this intermediate response to the
424   * provided buffer.
425   *
426   * @param  buffer  The buffer to which the string representation should be
427   *                 appended.
428   */
429  @Override()
430  public void toString(final StringBuilder buffer)
431  {
432    buffer.append("IntermediateResponse(");
433
434    boolean added = false;
435
436    if (messageID >= 0)
437    {
438      buffer.append("messageID=");
439      buffer.append(messageID);
440      added = true;
441    }
442
443    if (oid != null)
444    {
445      if (added)
446      {
447        buffer.append(", ");
448      }
449
450      buffer.append("oid='");
451      buffer.append(oid);
452      buffer.append('\'');
453      added = true;
454    }
455
456    if (controls.length > 0)
457    {
458      if (added)
459      {
460        buffer.append(", ");
461      }
462
463      buffer.append("controls={");
464      for (int i=0; i < controls.length; i++)
465      {
466        if (i > 0)
467        {
468          buffer.append(", ");
469        }
470
471        buffer.append(controls[i]);
472      }
473      buffer.append('}');
474    }
475
476    buffer.append(')');
477  }
478}