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