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