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