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    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    }