001/*
002 * Copyright 2009-2018 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2015-2018 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.NotMutable;
041import com.unboundid.util.ThreadSafety;
042import com.unboundid.util.ThreadSafetyLevel;
043
044import static com.unboundid.ldap.sdk.unboundidds.extensions.ExtOpMessages.*;
045import static com.unboundid.util.Debug.*;
046import static com.unboundid.util.StaticUtils.*;
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 Alcatel-Lucent 8661
060 *   server products.  These classes provide support for proprietary
061 *   functionality or for external specifications that are not considered stable
062 *   or mature enough to be guaranteed to work in an interoperable way with
063 *   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(
221           new ArrayList<StreamProxyValuesBackendSetValue>(values));
222    }
223  }
224
225
226
227  /**
228   * Creates a new stream proxy values intermediate response with
229   * information from the provided generic intermediate response.
230   *
231   * @param  intermediateResponse  The generic intermediate response that should
232   *                               be used to create this new intermediate
233   *                               response.
234   *
235   * @throws  LDAPException  If the provided intermediate response cannot be
236   *                         parsed as a stream proxy values intermediate
237   *                         response.
238   */
239  public StreamProxyValuesIntermediateResponse(
240                 final IntermediateResponse intermediateResponse)
241         throws LDAPException
242  {
243    super(intermediateResponse);
244
245    final ASN1OctetString value = intermediateResponse.getValue();
246    if (value == null)
247    {
248      throw new LDAPException(ResultCode.DECODING_ERROR,
249           ERR_STREAM_PROXY_VALUES_RESPONSE_NO_VALUE.get());
250    }
251
252    int    tmpResult  = -1;
253    String tmpAttr    = null;
254    String tmpMessage = null;
255    final ArrayList<StreamProxyValuesBackendSetValue> tmpValues =
256         new ArrayList<StreamProxyValuesBackendSetValue>();
257
258    try
259    {
260      final ASN1Element[] elements =
261           ASN1Element.decode(value.getValue()).decodeAsSequence().elements();
262      for (final ASN1Element e : elements)
263      {
264        switch (e.getType())
265        {
266          case TYPE_ATTRIBUTE_NAME:
267            tmpAttr = e.decodeAsOctetString().stringValue();
268            break;
269          case TYPE_RESULT:
270            tmpResult = e.decodeAsEnumerated().intValue();
271            if (tmpResult < 0)
272            {
273              throw new LDAPException(ResultCode.DECODING_ERROR,
274                   ERR_STREAM_PROXY_VALUES_RESPONSE_INVALID_RESULT.get(
275                        tmpResult));
276            }
277            break;
278          case TYPE_DIAGNOSTIC_MESSAGE:
279            tmpMessage = e.decodeAsOctetString().stringValue();
280            break;
281          case TYPE_VALUES:
282            final ASN1Element[] valueElements = e.decodeAsSet().elements();
283            for (final ASN1Element ve : valueElements)
284            {
285              tmpValues.add(StreamProxyValuesBackendSetValue.decode(ve));
286            }
287            break;
288          default:
289            throw new LDAPException(ResultCode.DECODING_ERROR,
290                 ERR_STREAM_PROXY_VALUES_RESPONSE_INVALID_SEQUENCE_TYPE.get(
291                      toHex(e.getType())));
292        }
293      }
294    }
295    catch (final LDAPException le)
296    {
297      throw le;
298    }
299    catch (final Exception e)
300    {
301      debugException(e);
302      throw new LDAPException(ResultCode.DECODING_ERROR,
303           ERR_STREAM_PROXY_VALUES_RESPONSE_CANNOT_DECODE.get(
304                getExceptionMessage(e)), 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<ASN1Element>(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<ASN1Element>(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}