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 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 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 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( 217 new ArrayList<ASN1OctetString>(values)); 218 } 219 } 220 221 222 223 /** 224 * Creates a new stream directory values intermediate response with 225 * information from the provided generic intermediate response. 226 * 227 * @param intermediateResponse The generic intermediate response that should 228 * be used to create this new intermediate 229 * response. 230 * 231 * @throws LDAPException If the provided intermediate response cannot be 232 * parsed as a stream directory values intermediate 233 * response. 234 */ 235 public StreamDirectoryValuesIntermediateResponse( 236 final IntermediateResponse intermediateResponse) 237 throws LDAPException 238 { 239 super(intermediateResponse); 240 241 final ASN1OctetString value = intermediateResponse.getValue(); 242 if (value == null) 243 { 244 throw new LDAPException(ResultCode.DECODING_ERROR, 245 ERR_STREAM_DIRECTORY_VALUES_RESPONSE_NO_VALUE.get()); 246 } 247 248 int tmpResult = -1; 249 String tmpAttr = null; 250 String tmpMessage = null; 251 final ArrayList<ASN1OctetString> tmpValues = 252 new ArrayList<ASN1OctetString>(); 253 254 try 255 { 256 final ASN1Element[] elements = 257 ASN1Element.decode(value.getValue()).decodeAsSequence().elements(); 258 for (final ASN1Element e : elements) 259 { 260 switch (e.getType()) 261 { 262 case TYPE_ATTRIBUTE_NAME: 263 tmpAttr = e.decodeAsOctetString().stringValue(); 264 break; 265 case TYPE_RESULT: 266 tmpResult = e.decodeAsEnumerated().intValue(); 267 if (tmpResult < 0) 268 { 269 throw new LDAPException(ResultCode.DECODING_ERROR, 270 ERR_STREAM_DIRECTORY_VALUES_RESPONSE_INVALID_RESULT.get( 271 tmpResult)); 272 } 273 break; 274 case TYPE_DIAGNOSTIC_MESSAGE: 275 tmpMessage = e.decodeAsOctetString().stringValue(); 276 break; 277 case TYPE_VALUES: 278 final ASN1Element[] valueElements = e.decodeAsSet().elements(); 279 for (final ASN1Element ve : valueElements) 280 { 281 tmpValues.add(ve.decodeAsOctetString()); 282 } 283 break; 284 default: 285 throw new LDAPException(ResultCode.DECODING_ERROR, 286 ERR_STREAM_DIRECTORY_VALUES_RESPONSE_INVALID_SEQUENCE_TYPE.get( 287 toHex(e.getType()))); 288 } 289 } 290 } 291 catch (final LDAPException le) 292 { 293 throw le; 294 } 295 catch (final Exception e) 296 { 297 debugException(e); 298 throw new LDAPException(ResultCode.DECODING_ERROR, 299 ERR_STREAM_DIRECTORY_VALUES_RESPONSE_CANNOT_DECODE.get( 300 getExceptionMessage(e)), e); 301 } 302 303 if (tmpResult < 0) 304 { 305 throw new LDAPException(ResultCode.DECODING_ERROR, 306 ERR_STREAM_DIRECTORY_VALUES_RESPONSE_NO_RESULT.get()); 307 } 308 309 attributeName = tmpAttr; 310 result = tmpResult; 311 diagnosticMessage = tmpMessage; 312 values = Collections.unmodifiableList(tmpValues); 313 } 314 315 316 317 /** 318 * Encodes the provided information in a form suitable for use as the value of 319 * this intermediate response. 320 * 321 * @param attributeName The name of the attribute with which the 322 * included values are associated. This may be 323 * {@code null} if the provided values are DNs. 324 * @param result The integer value that provides information 325 * about the state of the stream directory values 326 * response. 327 * @param diagnosticMessage The diagnostic message that provides more 328 * information about the result, or {@code null} if 329 * none is required. 330 * @param values The set of values included in this stream 331 * directory values intermediate response. It may 332 * be {@code null} or empty if this is an error 333 * result, or there are no values of the specified 334 * type in the server. 335 * 336 * @return An ASN.1 octet string containing the encoded value to use for this 337 * intermediate response. 338 */ 339 private static ASN1OctetString encodeValue(final String attributeName, 340 final int result, final String diagnosticMessage, 341 final Collection<ASN1OctetString> values) 342 { 343 final ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(4); 344 345 if (attributeName != null) 346 { 347 elements.add(new ASN1OctetString(TYPE_ATTRIBUTE_NAME, attributeName)); 348 } 349 350 elements.add(new ASN1Enumerated(TYPE_RESULT, result)); 351 352 if (diagnosticMessage != null) 353 { 354 elements.add(new ASN1OctetString(TYPE_DIAGNOSTIC_MESSAGE, 355 diagnosticMessage)); 356 } 357 358 if ((values != null) && (! values.isEmpty())) 359 { 360 elements.add(new ASN1Set(TYPE_VALUES, values)); 361 } 362 363 return new ASN1OctetString(new ASN1Sequence(elements).encode()); 364 } 365 366 367 368 /** 369 * Retrieves the name of the attribute with which this stream directory values 370 * intermediate response is associated. 371 * 372 * @return The name of the attribute with which this stream directory values 373 * intermediate response is associated, or {@code null} if the values 374 * are entry DNs rather than attribute values. 375 */ 376 public String getAttributeName() 377 { 378 return attributeName; 379 } 380 381 382 383 /** 384 * Retrieves the integer value of the result for this stream directory values 385 * intermediate response. 386 * 387 * @return The integer value of the result for this stream directory values 388 * intermediate response. 389 */ 390 public int getResult() 391 { 392 return result; 393 } 394 395 396 397 /** 398 * Retrieves the diagnostic message for this stream directory values 399 * intermediate response. 400 * 401 * @return The diagnostic message for this stream directory values 402 * intermediate response, or {@code null} if there is none. 403 */ 404 public String getDiagnosticMessage() 405 { 406 return diagnosticMessage; 407 } 408 409 410 411 /** 412 * Retrieves the list of values for this stream directory values intermediate 413 * response. 414 * 415 * @return The list of values for this stream directory values intermediate 416 * response, or an empty list if there are no values. 417 */ 418 public List<ASN1OctetString> getValues() 419 { 420 return values; 421 } 422 423 424 425 /** 426 * {@inheritDoc} 427 */ 428 @Override() 429 public String getIntermediateResponseName() 430 { 431 return INFO_INTERMEDIATE_RESPONSE_NAME_STREAM_DIRECTORY_VALUES.get(); 432 } 433 434 435 436 /** 437 * {@inheritDoc} 438 */ 439 @Override() 440 public String valueToString() 441 { 442 final StringBuilder buffer = new StringBuilder(); 443 444 if (attributeName != null) 445 { 446 buffer.append("attributeName='"); 447 buffer.append(attributeName); 448 buffer.append("' "); 449 } 450 451 buffer.append("result='"); 452 switch (result) 453 { 454 case RESULT_ALL_VALUES_RETURNED: 455 buffer.append("all values returned"); 456 break; 457 case RESULT_ATTRIBUTE_NOT_INDEXED: 458 buffer.append("attribute not indexed"); 459 break; 460 case RESULT_MORE_VALUES_TO_RETURN: 461 buffer.append("more values to return"); 462 break; 463 case RESULT_PROCESSING_ERROR: 464 buffer.append("processing error"); 465 break; 466 default: 467 buffer.append(result); 468 break; 469 } 470 buffer.append('\''); 471 472 if (diagnosticMessage != null) 473 { 474 buffer.append(" diagnosticMessage='"); 475 buffer.append(diagnosticMessage); 476 buffer.append('\''); 477 } 478 479 buffer.append(" valueCount='"); 480 buffer.append(values.size()); 481 buffer.append('\''); 482 483 return buffer.toString(); 484 } 485 486 487 488 /** 489 * {@inheritDoc} 490 */ 491 @Override() 492 public void toString(final StringBuilder buffer) 493 { 494 buffer.append("StreamDirectoryValuesIntermediateResponse("); 495 496 final int messageID = getMessageID(); 497 if (messageID >= 0) 498 { 499 buffer.append("messageID="); 500 buffer.append(messageID); 501 buffer.append(", "); 502 } 503 504 if (attributeName != null) 505 { 506 buffer.append("attributeName='"); 507 buffer.append(attributeName); 508 buffer.append("', "); 509 } 510 511 buffer.append("result="); 512 buffer.append(result); 513 514 if (diagnosticMessage != null) 515 { 516 buffer.append(", diagnosticMessage='"); 517 buffer.append(diagnosticMessage); 518 buffer.append('\''); 519 } 520 521 buffer.append(", values={"); 522 523 final Iterator<ASN1OctetString> iterator = values.iterator(); 524 while (iterator.hasNext()) 525 { 526 buffer.append('\''); 527 buffer.append(iterator.next().stringValue()); 528 buffer.append('\''); 529 if (iterator.hasNext()) 530 { 531 buffer.append(", "); 532 } 533 } 534 535 final Control[] controls = getControls(); 536 if (controls.length > 0) 537 { 538 buffer.append(", controls={"); 539 for (int i=0; i < controls.length; i++) 540 { 541 if (i > 0) 542 { 543 buffer.append(", "); 544 } 545 546 buffer.append(controls[i]); 547 } 548 buffer.append('}'); 549 } 550 551 buffer.append("})"); 552 } 553}