001    /*
002     * Copyright 2009-2015 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2009-2015 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    }