001    /*
002     * Copyright 2009-2015 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 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.unboundidds.extensions;
022    
023    
024    
025    import java.util.ArrayList;
026    import java.util.Collection;
027    import java.util.Collections;
028    import java.util.Iterator;
029    import java.util.List;
030    
031    import com.unboundid.asn1.ASN1Element;
032    import com.unboundid.asn1.ASN1Enumerated;
033    import com.unboundid.asn1.ASN1OctetString;
034    import com.unboundid.asn1.ASN1Sequence;
035    import com.unboundid.asn1.ASN1Set;
036    import com.unboundid.ldap.sdk.Control;
037    import com.unboundid.ldap.sdk.IntermediateResponse;
038    import com.unboundid.ldap.sdk.LDAPException;
039    import com.unboundid.ldap.sdk.ResultCode;
040    import com.unboundid.util.NotMutable;
041    import com.unboundid.util.ThreadSafety;
042    import com.unboundid.util.ThreadSafetyLevel;
043    
044    import static com.unboundid.ldap.sdk.unboundidds.extensions.ExtOpMessages.*;
045    import static com.unboundid.util.Debug.*;
046    import static com.unboundid.util.StaticUtils.*;
047    
048    
049    
050    /**
051     * <BLOCKQUOTE>
052     *   <B>NOTE:</B>  This class is part of the Commercial Edition of the UnboundID
053     *   LDAP SDK for Java.  It is not available for use in applications that
054     *   include only the Standard Edition of the LDAP SDK, and is not supported for
055     *   use in conjunction with non-UnboundID products.
056     * </BLOCKQUOTE>
057     * This class provides an implementation of the stream directory values
058     * intermediate response, which may be used to provide a partial or complete
059     * list of the values for a specified attribute, or DNs of entries contained in
060     * a specified portion of the server DIT.  This intermediate response has an OID
061     * of "1.3.6.1.4.1.30221.2.6.7" and the value is encoded as follows:
062     * <PRE>
063     *   StreamDirectoryValuesIntermediateResponse ::= SEQUENCE {
064     *        attributeName         [0] LDAPString OPTIONAL,
065     *        result                [1] ENUMERATED {
066     *             allValuesReturned       (0),
067     *             moreValuesToReturn      (1),
068     *             attributeNotIndexed     (2),
069     *             processingError         (3),
070     *             ... },
071     *        diagnosticMessage     [2] OCTET STRING OPTIONAL,
072     *        values                [3] SET OF OCTET STRING OPTIONAL,
073     *        ... }
074     * </PRE>
075     */
076    @NotMutable()
077    @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
078    public final class StreamDirectoryValuesIntermediateResponse
079           extends IntermediateResponse
080    {
081      /**
082       * The OID (1.3.6.1.4.1.30221.2.6.7) for the get stream directory values
083       * intermediate response.
084       */
085      public static final String STREAM_DIRECTORY_VALUES_INTERMEDIATE_RESPONSE_OID =
086           "1.3.6.1.4.1.30221.2.6.7";
087    
088    
089    
090      /**
091       * The integer value for the "all values returned" result.
092       */
093      public static final int RESULT_ALL_VALUES_RETURNED = 0;
094    
095    
096    
097      /**
098       * The integer value for the "more values to return" result.
099       */
100      public static final int RESULT_MORE_VALUES_TO_RETURN = 1;
101    
102    
103    
104      /**
105       * The integer value for the "attribute not indexed" result.
106       */
107      public static final int RESULT_ATTRIBUTE_NOT_INDEXED = 2;
108    
109    
110    
111      /**
112       * The integer value for the "processing error" result.
113       */
114      public static final int RESULT_PROCESSING_ERROR = 3;
115    
116    
117    
118      /**
119       * The BER type for the attribute name element.
120       */
121      private static final byte TYPE_ATTRIBUTE_NAME = (byte) 0x80;
122    
123    
124    
125      /**
126       * The BER type for the result element.
127       */
128      private static final byte TYPE_RESULT = (byte) 0x81;
129    
130    
131    
132      /**
133       * The BER type for the diagnostic message element.
134       */
135      private static final byte TYPE_DIAGNOSTIC_MESSAGE = (byte) 0x82;
136    
137    
138    
139      /**
140       * The BER type for the values element.
141       */
142      private static final byte TYPE_VALUES = (byte) 0xA3;
143    
144    
145    
146      /**
147       * The serial version UID for this serializable class.
148       */
149      private static final long serialVersionUID = -1756020236490168006L;
150    
151    
152    
153      // The result code for this stream directory values intermediate response.
154      private final int result;
155    
156      // The list of values for this stream directory values intermediate response.
157      private final List<ASN1OctetString> values;
158    
159      // The attribute name for this stream directory values intermediate response,
160      // if any.
161      private final String attributeName;
162    
163      // The diagnostic message for this stream directory values intermediate
164      // response, if any.
165      private final String diagnosticMessage;
166    
167    
168    
169      /**
170       * Creates a new stream directory values intermediate response with the
171       * provided information.
172       *
173       * @param  attributeName      The name of the attribute with which the
174       *                            included values are associated.  This may be
175       *                            {@code null} if the provided values are DNs.
176       * @param  result             The integer value that provides information
177       *                            about the state of the stream directory values
178       *                            response.
179       * @param  diagnosticMessage  The diagnostic message that provides more
180       *                            information about the result, or {@code null} if
181       *                            none is required.
182       * @param  values             The set of values included in this stream
183       *                            directory values intermediate response.  It may
184       *                            be {@code null} or empty if this is an error
185       *                            result, or there are no values of the specified
186       *                            type in the server.
187       * @param  controls           The set of controls to include in this
188       *                            intermediate response.  It may be {@code null}
189       *                            or empty if there should not be any controls.
190       */
191      public StreamDirectoryValuesIntermediateResponse(final String attributeName,
192                  final int result, final String diagnosticMessage,
193                  final Collection<ASN1OctetString> values,
194                  final Control... controls)
195      {
196        super(STREAM_DIRECTORY_VALUES_INTERMEDIATE_RESPONSE_OID,
197              encodeValue(attributeName, result, diagnosticMessage, values),
198              controls);
199    
200        this.attributeName     = attributeName;
201        this.result            = result;
202        this.diagnosticMessage = diagnosticMessage;
203    
204        if ((values == null) || values.isEmpty())
205        {
206          this.values = Collections.emptyList();
207        }
208        else
209        {
210          this.values = Collections.unmodifiableList(
211               new ArrayList<ASN1OctetString>(values));
212        }
213      }
214    
215    
216    
217      /**
218       * Creates a new stream directory values intermediate response with
219       * information from the provided generic intermediate response.
220       *
221       * @param  intermediateResponse  The generic intermediate response that should
222       *                               be used to create this new intermediate
223       *                               response.
224       *
225       * @throws  LDAPException  If the provided intermediate response cannot be
226       *                         parsed as a stream directory values intermediate
227       *                         response.
228       */
229      public StreamDirectoryValuesIntermediateResponse(
230                     final IntermediateResponse intermediateResponse)
231             throws LDAPException
232      {
233        super(intermediateResponse);
234    
235        final ASN1OctetString value = intermediateResponse.getValue();
236        if (value == null)
237        {
238          throw new LDAPException(ResultCode.DECODING_ERROR,
239               ERR_STREAM_DIRECTORY_VALUES_RESPONSE_NO_VALUE.get());
240        }
241    
242        int    tmpResult  = -1;
243        String tmpAttr    = null;
244        String tmpMessage = null;
245        final ArrayList<ASN1OctetString> tmpValues  =
246             new ArrayList<ASN1OctetString>();
247    
248        try
249        {
250          final ASN1Element[] elements =
251               ASN1Element.decode(value.getValue()).decodeAsSequence().elements();
252          for (final ASN1Element e : elements)
253          {
254            switch (e.getType())
255            {
256              case TYPE_ATTRIBUTE_NAME:
257                tmpAttr = e.decodeAsOctetString().stringValue();
258                break;
259              case TYPE_RESULT:
260                tmpResult = e.decodeAsEnumerated().intValue();
261                if (tmpResult < 0)
262                {
263                  throw new LDAPException(ResultCode.DECODING_ERROR,
264                       ERR_STREAM_DIRECTORY_VALUES_RESPONSE_INVALID_RESULT.get(
265                            tmpResult));
266                }
267                break;
268              case TYPE_DIAGNOSTIC_MESSAGE:
269                tmpMessage = e.decodeAsOctetString().stringValue();
270                break;
271              case TYPE_VALUES:
272                final ASN1Element[] valueElements = e.decodeAsSet().elements();
273                for (final ASN1Element ve : valueElements)
274                {
275                  tmpValues.add(ve.decodeAsOctetString());
276                }
277                break;
278              default:
279                throw new LDAPException(ResultCode.DECODING_ERROR,
280                     ERR_STREAM_DIRECTORY_VALUES_RESPONSE_INVALID_SEQUENCE_TYPE.get(
281                          toHex(e.getType())));
282            }
283          }
284        }
285        catch (LDAPException le)
286        {
287          throw le;
288        }
289        catch (Exception e)
290        {
291          debugException(e);
292          throw new LDAPException(ResultCode.DECODING_ERROR,
293               ERR_STREAM_DIRECTORY_VALUES_RESPONSE_CANNOT_DECODE.get(
294                    getExceptionMessage(e)), e);
295        }
296    
297        if (tmpResult < 0)
298        {
299          throw new LDAPException(ResultCode.DECODING_ERROR,
300               ERR_STREAM_DIRECTORY_VALUES_RESPONSE_NO_RESULT.get());
301        }
302    
303        attributeName     = tmpAttr;
304        result            = tmpResult;
305        diagnosticMessage = tmpMessage;
306        values            = Collections.unmodifiableList(tmpValues);
307      }
308    
309    
310    
311      /**
312       * Encodes the provided information in a form suitable for use as the value of
313       * this intermediate response.
314       *
315       * @param  attributeName      The name of the attribute with which the
316       *                            included values are associated.  This may be
317       *                            {@code null} if the provided values are DNs.
318       * @param  result             The integer value that provides information
319       *                            about the state of the stream directory values
320       *                            response.
321       * @param  diagnosticMessage  The diagnostic message that provides more
322       *                            information about the result, or {@code null} if
323       *                            none is required.
324       * @param  values             The set of values included in this stream
325       *                            directory values intermediate response.  It may
326       *                            be {@code null} or empty if this is an error
327       *                            result, or there are no values of the specified
328       *                            type in the server.
329       *
330       * @return  An ASN.1 octet string containing the encoded value to use for this
331       *          intermediate response.
332       */
333      private static ASN1OctetString encodeValue(final String attributeName,
334                   final int result, final String diagnosticMessage,
335                   final Collection<ASN1OctetString> values)
336      {
337        final ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(4);
338    
339        if (attributeName != null)
340        {
341          elements.add(new ASN1OctetString(TYPE_ATTRIBUTE_NAME, attributeName));
342        }
343    
344        elements.add(new ASN1Enumerated(TYPE_RESULT, result));
345    
346        if (diagnosticMessage != null)
347        {
348          elements.add(new ASN1OctetString(TYPE_DIAGNOSTIC_MESSAGE,
349                                           diagnosticMessage));
350        }
351    
352        if ((values != null) && (! values.isEmpty()))
353        {
354          elements.add(new ASN1Set(TYPE_VALUES, values));
355        }
356    
357        return new ASN1OctetString(new ASN1Sequence(elements).encode());
358      }
359    
360    
361    
362      /**
363       * Retrieves the name of the attribute with which this stream directory values
364       * intermediate response is associated.
365       *
366       * @return  The name of the attribute with which this stream directory values
367       *          intermediate response is associated, or {@code null} if the values
368       *          are entry DNs rather than attribute values.
369       */
370      public String getAttributeName()
371      {
372        return attributeName;
373      }
374    
375    
376    
377      /**
378       * Retrieves the integer value of the result for this stream directory values
379       * intermediate response.
380       *
381       * @return  The integer value of the result for this stream directory values
382       *          intermediate response.
383       */
384      public int getResult()
385      {
386        return result;
387      }
388    
389    
390    
391      /**
392       * Retrieves the diagnostic message for this stream directory values
393       * intermediate response.
394       *
395       * @return  The diagnostic message for this stream directory values
396       *          intermediate response, or {@code null} if there is none.
397       */
398      public String getDiagnosticMessage()
399      {
400        return diagnosticMessage;
401      }
402    
403    
404    
405      /**
406       * Retrieves the list of values for this stream directory values intermediate
407       * response.
408       *
409       * @return  The list of values for this stream directory values intermediate
410       *          response, or an empty list if there are no values.
411       */
412      public List<ASN1OctetString> getValues()
413      {
414        return values;
415      }
416    
417    
418    
419      /**
420       * {@inheritDoc}
421       */
422      @Override()
423      public String getIntermediateResponseName()
424      {
425        return INFO_INTERMEDIATE_RESPONSE_NAME_STREAM_DIRECTORY_VALUES.get();
426      }
427    
428    
429    
430      /**
431       * {@inheritDoc}
432       */
433      @Override()
434      public String valueToString()
435      {
436        final StringBuilder buffer = new StringBuilder();
437    
438        if (attributeName != null)
439        {
440          buffer.append("attributeName='");
441          buffer.append(attributeName);
442          buffer.append("' ");
443        }
444    
445        buffer.append("result='");
446        switch (result)
447        {
448          case RESULT_ALL_VALUES_RETURNED:
449            buffer.append("all values returned");
450            break;
451          case RESULT_ATTRIBUTE_NOT_INDEXED:
452            buffer.append("attribute not indexed");
453            break;
454          case RESULT_MORE_VALUES_TO_RETURN:
455            buffer.append("more values to return");
456            break;
457          case RESULT_PROCESSING_ERROR:
458            buffer.append("processing error");
459            break;
460          default:
461            buffer.append(result);
462            break;
463        }
464        buffer.append('\'');
465    
466        if (diagnosticMessage != null)
467        {
468          buffer.append(" diagnosticMessage='");
469          buffer.append(diagnosticMessage);
470          buffer.append('\'');
471        }
472    
473        buffer.append(" valueCount='");
474        buffer.append(values.size());
475        buffer.append('\'');
476    
477        return buffer.toString();
478      }
479    
480    
481    
482      /**
483       * {@inheritDoc}
484       */
485      @Override()
486      public void toString(final StringBuilder buffer)
487      {
488        buffer.append("StreamDirectoryValuesIntermediateResponse(");
489    
490        final int messageID = getMessageID();
491        if (messageID >= 0)
492        {
493          buffer.append("messageID=");
494          buffer.append(messageID);
495          buffer.append(", ");
496        }
497    
498        if (attributeName != null)
499        {
500          buffer.append("attributeName='");
501          buffer.append(attributeName);
502          buffer.append("', ");
503        }
504    
505        buffer.append("result=");
506        buffer.append(result);
507    
508        if (diagnosticMessage != null)
509        {
510          buffer.append(", diagnosticMessage='");
511          buffer.append(diagnosticMessage);
512          buffer.append('\'');
513        }
514    
515        buffer.append(", values={");
516    
517        final Iterator<ASN1OctetString> iterator = values.iterator();
518        while (iterator.hasNext())
519        {
520          buffer.append('\'');
521          buffer.append(iterator.next().stringValue());
522          buffer.append('\'');
523          if (iterator.hasNext())
524          {
525            buffer.append(", ");
526          }
527        }
528    
529        final Control[] controls = getControls();
530        if (controls.length > 0)
531        {
532          buffer.append(", controls={");
533          for (int i=0; i < controls.length; i++)
534          {
535            if (i > 0)
536            {
537              buffer.append(", ");
538            }
539    
540            buffer.append(controls[i]);
541          }
542          buffer.append('}');
543        }
544    
545        buffer.append("})");
546      }
547    }