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