001 /* 002 * Copyright 2009-2015 UnboundID Corp. 003 * All Rights Reserved. 004 */ 005 /* 006 * Copyright (C) 2015 UnboundID Corp. 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 */ 021 package com.unboundid.ldap.sdk.unboundidds.extensions; 022 023 024 025 import java.util.ArrayList; 026 import java.util.Collection; 027 import java.util.Collections; 028 import java.util.Iterator; 029 import java.util.List; 030 031 import com.unboundid.asn1.ASN1Element; 032 import com.unboundid.asn1.ASN1Enumerated; 033 import com.unboundid.asn1.ASN1OctetString; 034 import com.unboundid.asn1.ASN1Sequence; 035 import com.unboundid.asn1.ASN1Set; 036 import com.unboundid.ldap.sdk.Control; 037 import com.unboundid.ldap.sdk.IntermediateResponse; 038 import com.unboundid.ldap.sdk.LDAPException; 039 import com.unboundid.ldap.sdk.ResultCode; 040 import com.unboundid.util.NotMutable; 041 import com.unboundid.util.ThreadSafety; 042 import com.unboundid.util.ThreadSafetyLevel; 043 044 import static com.unboundid.ldap.sdk.unboundidds.extensions.ExtOpMessages.*; 045 import static com.unboundid.util.Debug.*; 046 import static com.unboundid.util.StaticUtils.*; 047 048 049 050 /** 051 * <BLOCKQUOTE> 052 * <B>NOTE:</B> This class is part of the Commercial Edition of the UnboundID 053 * LDAP SDK for Java. It is not available for use in applications that 054 * include only the Standard Edition of the LDAP SDK, and is not supported for 055 * use in conjunction with non-UnboundID products. 056 * </BLOCKQUOTE> 057 * This class provides an implementation of the stream directory values 058 * intermediate response, which may be used to provide a partial or complete 059 * list of the values for a specified attribute, or DNs of entries contained in 060 * a specified portion of the server DIT. This intermediate response has an OID 061 * of "1.3.6.1.4.1.30221.2.6.7" and the value is encoded as follows: 062 * <PRE> 063 * StreamDirectoryValuesIntermediateResponse ::= SEQUENCE { 064 * attributeName [0] LDAPString OPTIONAL, 065 * result [1] ENUMERATED { 066 * allValuesReturned (0), 067 * moreValuesToReturn (1), 068 * attributeNotIndexed (2), 069 * processingError (3), 070 * ... }, 071 * diagnosticMessage [2] OCTET STRING OPTIONAL, 072 * values [3] SET OF OCTET STRING OPTIONAL, 073 * ... } 074 * </PRE> 075 */ 076 @NotMutable() 077 @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 078 public final class StreamDirectoryValuesIntermediateResponse 079 extends IntermediateResponse 080 { 081 /** 082 * The OID (1.3.6.1.4.1.30221.2.6.7) for the get stream directory values 083 * intermediate response. 084 */ 085 public static final String STREAM_DIRECTORY_VALUES_INTERMEDIATE_RESPONSE_OID = 086 "1.3.6.1.4.1.30221.2.6.7"; 087 088 089 090 /** 091 * The integer value for the "all values returned" result. 092 */ 093 public static final int RESULT_ALL_VALUES_RETURNED = 0; 094 095 096 097 /** 098 * The integer value for the "more values to return" result. 099 */ 100 public static final int RESULT_MORE_VALUES_TO_RETURN = 1; 101 102 103 104 /** 105 * The integer value for the "attribute not indexed" result. 106 */ 107 public static final int RESULT_ATTRIBUTE_NOT_INDEXED = 2; 108 109 110 111 /** 112 * The integer value for the "processing error" result. 113 */ 114 public static final int RESULT_PROCESSING_ERROR = 3; 115 116 117 118 /** 119 * The BER type for the attribute name element. 120 */ 121 private static final byte TYPE_ATTRIBUTE_NAME = (byte) 0x80; 122 123 124 125 /** 126 * The BER type for the result element. 127 */ 128 private static final byte TYPE_RESULT = (byte) 0x81; 129 130 131 132 /** 133 * The BER type for the diagnostic message element. 134 */ 135 private static final byte TYPE_DIAGNOSTIC_MESSAGE = (byte) 0x82; 136 137 138 139 /** 140 * The BER type for the values element. 141 */ 142 private static final byte TYPE_VALUES = (byte) 0xA3; 143 144 145 146 /** 147 * The serial version UID for this serializable class. 148 */ 149 private static final long serialVersionUID = -1756020236490168006L; 150 151 152 153 // The result code for this stream directory values intermediate response. 154 private final int result; 155 156 // The list of values for this stream directory values intermediate response. 157 private final List<ASN1OctetString> values; 158 159 // The attribute name for this stream directory values intermediate response, 160 // if any. 161 private final String attributeName; 162 163 // The diagnostic message for this stream directory values intermediate 164 // response, if any. 165 private final String diagnosticMessage; 166 167 168 169 /** 170 * Creates a new stream directory values intermediate response with the 171 * provided information. 172 * 173 * @param attributeName The name of the attribute with which the 174 * included values are associated. This may be 175 * {@code null} if the provided values are DNs. 176 * @param result The integer value that provides information 177 * about the state of the stream directory values 178 * response. 179 * @param diagnosticMessage The diagnostic message that provides more 180 * information about the result, or {@code null} if 181 * none is required. 182 * @param values The set of values included in this stream 183 * directory values intermediate response. It may 184 * be {@code null} or empty if this is an error 185 * result, or there are no values of the specified 186 * type in the server. 187 * @param controls The set of controls to include in this 188 * intermediate response. It may be {@code null} 189 * or empty if there should not be any controls. 190 */ 191 public StreamDirectoryValuesIntermediateResponse(final String attributeName, 192 final int result, final String diagnosticMessage, 193 final Collection<ASN1OctetString> values, 194 final Control... controls) 195 { 196 super(STREAM_DIRECTORY_VALUES_INTERMEDIATE_RESPONSE_OID, 197 encodeValue(attributeName, result, diagnosticMessage, values), 198 controls); 199 200 this.attributeName = attributeName; 201 this.result = result; 202 this.diagnosticMessage = diagnosticMessage; 203 204 if ((values == null) || values.isEmpty()) 205 { 206 this.values = Collections.emptyList(); 207 } 208 else 209 { 210 this.values = Collections.unmodifiableList( 211 new ArrayList<ASN1OctetString>(values)); 212 } 213 } 214 215 216 217 /** 218 * Creates a new stream directory values intermediate response with 219 * information from the provided generic intermediate response. 220 * 221 * @param intermediateResponse The generic intermediate response that should 222 * be used to create this new intermediate 223 * response. 224 * 225 * @throws LDAPException If the provided intermediate response cannot be 226 * parsed as a stream directory values intermediate 227 * response. 228 */ 229 public StreamDirectoryValuesIntermediateResponse( 230 final IntermediateResponse intermediateResponse) 231 throws LDAPException 232 { 233 super(intermediateResponse); 234 235 final ASN1OctetString value = intermediateResponse.getValue(); 236 if (value == null) 237 { 238 throw new LDAPException(ResultCode.DECODING_ERROR, 239 ERR_STREAM_DIRECTORY_VALUES_RESPONSE_NO_VALUE.get()); 240 } 241 242 int tmpResult = -1; 243 String tmpAttr = null; 244 String tmpMessage = null; 245 final ArrayList<ASN1OctetString> tmpValues = 246 new ArrayList<ASN1OctetString>(); 247 248 try 249 { 250 final ASN1Element[] elements = 251 ASN1Element.decode(value.getValue()).decodeAsSequence().elements(); 252 for (final ASN1Element e : elements) 253 { 254 switch (e.getType()) 255 { 256 case TYPE_ATTRIBUTE_NAME: 257 tmpAttr = e.decodeAsOctetString().stringValue(); 258 break; 259 case TYPE_RESULT: 260 tmpResult = e.decodeAsEnumerated().intValue(); 261 if (tmpResult < 0) 262 { 263 throw new LDAPException(ResultCode.DECODING_ERROR, 264 ERR_STREAM_DIRECTORY_VALUES_RESPONSE_INVALID_RESULT.get( 265 tmpResult)); 266 } 267 break; 268 case TYPE_DIAGNOSTIC_MESSAGE: 269 tmpMessage = e.decodeAsOctetString().stringValue(); 270 break; 271 case TYPE_VALUES: 272 final ASN1Element[] valueElements = e.decodeAsSet().elements(); 273 for (final ASN1Element ve : valueElements) 274 { 275 tmpValues.add(ve.decodeAsOctetString()); 276 } 277 break; 278 default: 279 throw new LDAPException(ResultCode.DECODING_ERROR, 280 ERR_STREAM_DIRECTORY_VALUES_RESPONSE_INVALID_SEQUENCE_TYPE.get( 281 toHex(e.getType()))); 282 } 283 } 284 } 285 catch (LDAPException le) 286 { 287 throw le; 288 } 289 catch (Exception e) 290 { 291 debugException(e); 292 throw new LDAPException(ResultCode.DECODING_ERROR, 293 ERR_STREAM_DIRECTORY_VALUES_RESPONSE_CANNOT_DECODE.get( 294 getExceptionMessage(e)), e); 295 } 296 297 if (tmpResult < 0) 298 { 299 throw new LDAPException(ResultCode.DECODING_ERROR, 300 ERR_STREAM_DIRECTORY_VALUES_RESPONSE_NO_RESULT.get()); 301 } 302 303 attributeName = tmpAttr; 304 result = tmpResult; 305 diagnosticMessage = tmpMessage; 306 values = Collections.unmodifiableList(tmpValues); 307 } 308 309 310 311 /** 312 * Encodes the provided information in a form suitable for use as the value of 313 * this intermediate response. 314 * 315 * @param attributeName The name of the attribute with which the 316 * included values are associated. This may be 317 * {@code null} if the provided values are DNs. 318 * @param result The integer value that provides information 319 * about the state of the stream directory values 320 * response. 321 * @param diagnosticMessage The diagnostic message that provides more 322 * information about the result, or {@code null} if 323 * none is required. 324 * @param values The set of values included in this stream 325 * directory values intermediate response. It may 326 * be {@code null} or empty if this is an error 327 * result, or there are no values of the specified 328 * type in the server. 329 * 330 * @return An ASN.1 octet string containing the encoded value to use for this 331 * intermediate response. 332 */ 333 private static ASN1OctetString encodeValue(final String attributeName, 334 final int result, final String diagnosticMessage, 335 final Collection<ASN1OctetString> values) 336 { 337 final ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(4); 338 339 if (attributeName != null) 340 { 341 elements.add(new ASN1OctetString(TYPE_ATTRIBUTE_NAME, attributeName)); 342 } 343 344 elements.add(new ASN1Enumerated(TYPE_RESULT, result)); 345 346 if (diagnosticMessage != null) 347 { 348 elements.add(new ASN1OctetString(TYPE_DIAGNOSTIC_MESSAGE, 349 diagnosticMessage)); 350 } 351 352 if ((values != null) && (! values.isEmpty())) 353 { 354 elements.add(new ASN1Set(TYPE_VALUES, values)); 355 } 356 357 return new ASN1OctetString(new ASN1Sequence(elements).encode()); 358 } 359 360 361 362 /** 363 * Retrieves the name of the attribute with which this stream directory values 364 * intermediate response is associated. 365 * 366 * @return The name of the attribute with which this stream directory values 367 * intermediate response is associated, or {@code null} if the values 368 * are entry DNs rather than attribute values. 369 */ 370 public String getAttributeName() 371 { 372 return attributeName; 373 } 374 375 376 377 /** 378 * Retrieves the integer value of the result for this stream directory values 379 * intermediate response. 380 * 381 * @return The integer value of the result for this stream directory values 382 * intermediate response. 383 */ 384 public int getResult() 385 { 386 return result; 387 } 388 389 390 391 /** 392 * Retrieves the diagnostic message for this stream directory values 393 * intermediate response. 394 * 395 * @return The diagnostic message for this stream directory values 396 * intermediate response, or {@code null} if there is none. 397 */ 398 public String getDiagnosticMessage() 399 { 400 return diagnosticMessage; 401 } 402 403 404 405 /** 406 * Retrieves the list of values for this stream directory values intermediate 407 * response. 408 * 409 * @return The list of values for this stream directory values intermediate 410 * response, or an empty list if there are no values. 411 */ 412 public List<ASN1OctetString> getValues() 413 { 414 return values; 415 } 416 417 418 419 /** 420 * {@inheritDoc} 421 */ 422 @Override() 423 public String getIntermediateResponseName() 424 { 425 return INFO_INTERMEDIATE_RESPONSE_NAME_STREAM_DIRECTORY_VALUES.get(); 426 } 427 428 429 430 /** 431 * {@inheritDoc} 432 */ 433 @Override() 434 public String valueToString() 435 { 436 final StringBuilder buffer = new StringBuilder(); 437 438 if (attributeName != null) 439 { 440 buffer.append("attributeName='"); 441 buffer.append(attributeName); 442 buffer.append("' "); 443 } 444 445 buffer.append("result='"); 446 switch (result) 447 { 448 case RESULT_ALL_VALUES_RETURNED: 449 buffer.append("all values returned"); 450 break; 451 case RESULT_ATTRIBUTE_NOT_INDEXED: 452 buffer.append("attribute not indexed"); 453 break; 454 case RESULT_MORE_VALUES_TO_RETURN: 455 buffer.append("more values to return"); 456 break; 457 case RESULT_PROCESSING_ERROR: 458 buffer.append("processing error"); 459 break; 460 default: 461 buffer.append(result); 462 break; 463 } 464 buffer.append('\''); 465 466 if (diagnosticMessage != null) 467 { 468 buffer.append(" diagnosticMessage='"); 469 buffer.append(diagnosticMessage); 470 buffer.append('\''); 471 } 472 473 buffer.append(" valueCount='"); 474 buffer.append(values.size()); 475 buffer.append('\''); 476 477 return buffer.toString(); 478 } 479 480 481 482 /** 483 * {@inheritDoc} 484 */ 485 @Override() 486 public void toString(final StringBuilder buffer) 487 { 488 buffer.append("StreamDirectoryValuesIntermediateResponse("); 489 490 final int messageID = getMessageID(); 491 if (messageID >= 0) 492 { 493 buffer.append("messageID="); 494 buffer.append(messageID); 495 buffer.append(", "); 496 } 497 498 if (attributeName != null) 499 { 500 buffer.append("attributeName='"); 501 buffer.append(attributeName); 502 buffer.append("', "); 503 } 504 505 buffer.append("result="); 506 buffer.append(result); 507 508 if (diagnosticMessage != null) 509 { 510 buffer.append(", diagnosticMessage='"); 511 buffer.append(diagnosticMessage); 512 buffer.append('\''); 513 } 514 515 buffer.append(", values={"); 516 517 final Iterator<ASN1OctetString> iterator = values.iterator(); 518 while (iterator.hasNext()) 519 { 520 buffer.append('\''); 521 buffer.append(iterator.next().stringValue()); 522 buffer.append('\''); 523 if (iterator.hasNext()) 524 { 525 buffer.append(", "); 526 } 527 } 528 529 final Control[] controls = getControls(); 530 if (controls.length > 0) 531 { 532 buffer.append(", controls={"); 533 for (int i=0; i < controls.length; i++) 534 { 535 if (i > 0) 536 { 537 buffer.append(", "); 538 } 539 540 buffer.append(controls[i]); 541 } 542 buffer.append('}'); 543 } 544 545 buffer.append("})"); 546 } 547 }