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 directory values 052 * intermediate response, which may be used to provide a partial or complete 053 * list of the values for a specified attribute, or DNs of entries contained in 054 * a specified 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 067 * of "1.3.6.1.4.1.30221.2.6.7" and the value is encoded as follows: 068 * <PRE> 069 * StreamDirectoryValuesIntermediateResponse ::= 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 [3] SET OF OCTET STRING OPTIONAL, 079 * ... } 080 * </PRE> 081 */ 082@NotMutable() 083@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 084public final class StreamDirectoryValuesIntermediateResponse 085 extends IntermediateResponse 086{ 087 /** 088 * The OID (1.3.6.1.4.1.30221.2.6.7) for the get stream directory values 089 * intermediate response. 090 */ 091 public static final String STREAM_DIRECTORY_VALUES_INTERMEDIATE_RESPONSE_OID = 092 "1.3.6.1.4.1.30221.2.6.7"; 093 094 095 096 /** 097 * The integer value for the "all values returned" result. 098 */ 099 public static final int RESULT_ALL_VALUES_RETURNED = 0; 100 101 102 103 /** 104 * The integer value for the "more values to return" result. 105 */ 106 public static final int RESULT_MORE_VALUES_TO_RETURN = 1; 107 108 109 110 /** 111 * The integer value for the "attribute not indexed" result. 112 */ 113 public static final int RESULT_ATTRIBUTE_NOT_INDEXED = 2; 114 115 116 117 /** 118 * The integer value for the "processing error" result. 119 */ 120 public static final int RESULT_PROCESSING_ERROR = 3; 121 122 123 124 /** 125 * The BER type for the attribute name element. 126 */ 127 private static final byte TYPE_ATTRIBUTE_NAME = (byte) 0x80; 128 129 130 131 /** 132 * The BER type for the result element. 133 */ 134 private static final byte TYPE_RESULT = (byte) 0x81; 135 136 137 138 /** 139 * The BER type for the diagnostic message element. 140 */ 141 private static final byte TYPE_DIAGNOSTIC_MESSAGE = (byte) 0x82; 142 143 144 145 /** 146 * The BER type for the values element. 147 */ 148 private static final byte TYPE_VALUES = (byte) 0xA3; 149 150 151 152 /** 153 * The serial version UID for this serializable class. 154 */ 155 private static final long serialVersionUID = -1756020236490168006L; 156 157 158 159 // The result code for this stream directory values intermediate response. 160 private final int result; 161 162 // The list of values for this stream directory values intermediate response. 163 private final List<ASN1OctetString> values; 164 165 // The attribute name for this stream directory values intermediate response, 166 // if any. 167 private final String attributeName; 168 169 // The diagnostic message for this stream directory values intermediate 170 // response, if any. 171 private final String diagnosticMessage; 172 173 174 175 /** 176 * Creates a new stream directory values intermediate response with the 177 * provided information. 178 * 179 * @param attributeName The name of the attribute with which the 180 * included values are associated. This may be 181 * {@code null} if the provided values are DNs. 182 * @param result The integer value that provides information 183 * about the state of the stream directory values 184 * response. 185 * @param diagnosticMessage The diagnostic message that provides more 186 * information about the result, or {@code null} if 187 * none is required. 188 * @param values The set of values included in this stream 189 * directory values intermediate response. It may 190 * be {@code null} or empty if this is an error 191 * result, or there are no values of the specified 192 * type in the server. 193 * @param controls The set of controls to include in this 194 * intermediate response. It may be {@code null} 195 * or empty if there should not be any controls. 196 */ 197 public StreamDirectoryValuesIntermediateResponse(final String attributeName, 198 final int result, final String diagnosticMessage, 199 final Collection<ASN1OctetString> values, 200 final Control... controls) 201 { 202 super(STREAM_DIRECTORY_VALUES_INTERMEDIATE_RESPONSE_OID, 203 encodeValue(attributeName, result, diagnosticMessage, values), 204 controls); 205 206 this.attributeName = attributeName; 207 this.result = result; 208 this.diagnosticMessage = diagnosticMessage; 209 210 if ((values == null) || values.isEmpty()) 211 { 212 this.values = Collections.emptyList(); 213 } 214 else 215 { 216 this.values = Collections.unmodifiableList(new ArrayList<>(values)); 217 } 218 } 219 220 221 222 /** 223 * Creates a new stream directory values intermediate response with 224 * information from the provided generic intermediate response. 225 * 226 * @param intermediateResponse The generic intermediate response that should 227 * be used to create this new intermediate 228 * response. 229 * 230 * @throws LDAPException If the provided intermediate response cannot be 231 * parsed as a stream directory values intermediate 232 * response. 233 */ 234 public StreamDirectoryValuesIntermediateResponse( 235 final IntermediateResponse intermediateResponse) 236 throws LDAPException 237 { 238 super(intermediateResponse); 239 240 final ASN1OctetString value = intermediateResponse.getValue(); 241 if (value == null) 242 { 243 throw new LDAPException(ResultCode.DECODING_ERROR, 244 ERR_STREAM_DIRECTORY_VALUES_RESPONSE_NO_VALUE.get()); 245 } 246 247 int tmpResult = -1; 248 String tmpAttr = null; 249 String tmpMessage = null; 250 final ArrayList<ASN1OctetString> tmpValues = new ArrayList<>(100); 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_DIRECTORY_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(ve.decodeAsOctetString()); 280 } 281 break; 282 default: 283 throw new LDAPException(ResultCode.DECODING_ERROR, 284 ERR_STREAM_DIRECTORY_VALUES_RESPONSE_INVALID_SEQUENCE_TYPE.get( 285 StaticUtils.toHex(e.getType()))); 286 } 287 } 288 } 289 catch (final LDAPException le) 290 { 291 throw le; 292 } 293 catch (final Exception e) 294 { 295 Debug.debugException(e); 296 throw new LDAPException(ResultCode.DECODING_ERROR, 297 ERR_STREAM_DIRECTORY_VALUES_RESPONSE_CANNOT_DECODE.get( 298 StaticUtils.getExceptionMessage(e)), e); 299 } 300 301 if (tmpResult < 0) 302 { 303 throw new LDAPException(ResultCode.DECODING_ERROR, 304 ERR_STREAM_DIRECTORY_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 directory 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 * directory 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<ASN1OctetString> values) 340 { 341 final ArrayList<ASN1Element> elements = new ArrayList<>(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 elements.add(new ASN1Set(TYPE_VALUES, values)); 359 } 360 361 return new ASN1OctetString(new ASN1Sequence(elements).encode()); 362 } 363 364 365 366 /** 367 * Retrieves the name of the attribute with which this stream directory values 368 * intermediate response is associated. 369 * 370 * @return The name of the attribute with which this stream directory values 371 * intermediate response is associated, or {@code null} if the values 372 * are entry DNs rather than attribute values. 373 */ 374 public String getAttributeName() 375 { 376 return attributeName; 377 } 378 379 380 381 /** 382 * Retrieves the integer value of the result for this stream directory values 383 * intermediate response. 384 * 385 * @return The integer value of the result for this stream directory values 386 * intermediate response. 387 */ 388 public int getResult() 389 { 390 return result; 391 } 392 393 394 395 /** 396 * Retrieves the diagnostic message for this stream directory values 397 * intermediate response. 398 * 399 * @return The diagnostic message for this stream directory values 400 * intermediate response, or {@code null} if there is none. 401 */ 402 public String getDiagnosticMessage() 403 { 404 return diagnosticMessage; 405 } 406 407 408 409 /** 410 * Retrieves the list of values for this stream directory values intermediate 411 * response. 412 * 413 * @return The list of values for this stream directory values intermediate 414 * response, or an empty list if there are no values. 415 */ 416 public List<ASN1OctetString> getValues() 417 { 418 return values; 419 } 420 421 422 423 /** 424 * {@inheritDoc} 425 */ 426 @Override() 427 public String getIntermediateResponseName() 428 { 429 return INFO_INTERMEDIATE_RESPONSE_NAME_STREAM_DIRECTORY_VALUES.get(); 430 } 431 432 433 434 /** 435 * {@inheritDoc} 436 */ 437 @Override() 438 public String valueToString() 439 { 440 final StringBuilder buffer = new StringBuilder(); 441 442 if (attributeName != null) 443 { 444 buffer.append("attributeName='"); 445 buffer.append(attributeName); 446 buffer.append("' "); 447 } 448 449 buffer.append("result='"); 450 switch (result) 451 { 452 case RESULT_ALL_VALUES_RETURNED: 453 buffer.append("all values returned"); 454 break; 455 case RESULT_ATTRIBUTE_NOT_INDEXED: 456 buffer.append("attribute not indexed"); 457 break; 458 case RESULT_MORE_VALUES_TO_RETURN: 459 buffer.append("more values to return"); 460 break; 461 case RESULT_PROCESSING_ERROR: 462 buffer.append("processing error"); 463 break; 464 default: 465 buffer.append(result); 466 break; 467 } 468 buffer.append('\''); 469 470 if (diagnosticMessage != null) 471 { 472 buffer.append(" diagnosticMessage='"); 473 buffer.append(diagnosticMessage); 474 buffer.append('\''); 475 } 476 477 buffer.append(" valueCount='"); 478 buffer.append(values.size()); 479 buffer.append('\''); 480 481 return buffer.toString(); 482 } 483 484 485 486 /** 487 * {@inheritDoc} 488 */ 489 @Override() 490 public void toString(final StringBuilder buffer) 491 { 492 buffer.append("StreamDirectoryValuesIntermediateResponse("); 493 494 final int messageID = getMessageID(); 495 if (messageID >= 0) 496 { 497 buffer.append("messageID="); 498 buffer.append(messageID); 499 buffer.append(", "); 500 } 501 502 if (attributeName != null) 503 { 504 buffer.append("attributeName='"); 505 buffer.append(attributeName); 506 buffer.append("', "); 507 } 508 509 buffer.append("result="); 510 buffer.append(result); 511 512 if (diagnosticMessage != null) 513 { 514 buffer.append(", diagnosticMessage='"); 515 buffer.append(diagnosticMessage); 516 buffer.append('\''); 517 } 518 519 buffer.append(", values={"); 520 521 final Iterator<ASN1OctetString> iterator = values.iterator(); 522 while (iterator.hasNext()) 523 { 524 buffer.append('\''); 525 buffer.append(iterator.next().stringValue()); 526 buffer.append('\''); 527 if (iterator.hasNext()) 528 { 529 buffer.append(", "); 530 } 531 } 532 533 final Control[] controls = getControls(); 534 if (controls.length > 0) 535 { 536 buffer.append(", controls={"); 537 for (int i=0; i < controls.length; i++) 538 { 539 if (i > 0) 540 { 541 buffer.append(", "); 542 } 543 544 buffer.append(controls[i]); 545 } 546 buffer.append('}'); 547 } 548 549 buffer.append("})"); 550 } 551}