001/* 002 * Copyright 2009-2020 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2009-2020 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) 2015-2020 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.Collections; 042import java.util.Iterator; 043import java.util.List; 044 045import com.unboundid.asn1.ASN1Boolean; 046import com.unboundid.asn1.ASN1Element; 047import com.unboundid.asn1.ASN1Enumerated; 048import com.unboundid.asn1.ASN1OctetString; 049import com.unboundid.asn1.ASN1Sequence; 050import com.unboundid.asn1.ASN1Integer; 051import com.unboundid.ldap.sdk.Control; 052import com.unboundid.ldap.sdk.ExtendedRequest; 053import com.unboundid.ldap.sdk.LDAPException; 054import com.unboundid.ldap.sdk.ResultCode; 055import com.unboundid.ldap.sdk.SearchScope; 056import com.unboundid.util.Debug; 057import com.unboundid.util.NotMutable; 058import com.unboundid.util.StaticUtils; 059import com.unboundid.util.ThreadSafety; 060import com.unboundid.util.ThreadSafetyLevel; 061import com.unboundid.util.Validator; 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 extended 069 * request as used in the Ping Identity, UnboundID, Nokia/Alcatel-Lucent 8661 070 * Directory Proxy Server. It may be used to obtain all entry DNs and/or all 071 * values for one or more attributes for a specified portion of the 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 extended request has an OID of "1.3.6.1.4.1.30221.2.6.8" and the value 084 * is encoded as follows: 085 * <PRE> 086 * StreamProxyValuesRequest ::= SEQUENCE { 087 * baseDN [0] LDAPDN, 088 * includeDNs [1] DNSelection OPTIONAL, 089 * attributes [2] SEQUENCE OF LDAPString OPTIONAL, 090 * valuesPerResponse [3] INTEGER (1 .. 32767) OPTIONAL, 091 * backendSets [4] SEQUENCE OF BackendSetConfig, 092 * ... } 093 * 094 * DNSelection ::= SEQUENCE { 095 * scope [0] ENUMERATED { 096 * baseObject (0), 097 * singleLevel (1), 098 * wholeSubtree (2), 099 * subordinateSubtree (3), 100 * ... } 101 * relative [1] BOOLEAN DEFAULT TRUE, 102 * ..... } 103 * 104 * BackendSetConfig ::= SEQUENCE { 105 * backendSetID OCTET STRING, 106 * backendServers SEQUENCE OF SEQUENCE { 107 * host OCTET STRING, 108 * port INTEGER (1 .. 65535) } } 109 * </PRE> 110 */ 111@NotMutable() 112@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 113public final class StreamProxyValuesExtendedRequest 114 extends ExtendedRequest 115{ 116 /** 117 * The OID (1.3.6.1.4.1.30221.2.6.8) for the get stream proxy values extended 118 * request. 119 */ 120 public static final String STREAM_PROXY_VALUES_REQUEST_OID = 121 "1.3.6.1.4.1.30221.2.6.8"; 122 123 124 125 /** 126 * The BER type for the baseDN element of the stream proxy values request 127 * sequence. 128 */ 129 private static final byte TYPE_BASE_DN = (byte) 0x80; 130 131 132 133 /** 134 * The BER type for the includeDNs element of the stream proxy values request 135 * sequence. 136 */ 137 private static final byte TYPE_INCLUDE_DNS = (byte) 0xA1; 138 139 140 141 /** 142 * The BER type for the attributes element of the stream proxy values request 143 * sequence. 144 */ 145 private static final byte TYPE_ATTRIBUTES = (byte) 0xA2; 146 147 148 149 /** 150 * The BER type for the valuesPerResponse element of the stream proxy values 151 * request sequence. 152 */ 153 private static final byte TYPE_VALUES_PER_RESPONSE = (byte) 0x83; 154 155 156 157 /** 158 * The BER type for the backendSets element of the stream proxy values request 159 * sequence. 160 */ 161 private static final byte TYPE_BACKEND_SETS = (byte) 0xA4; 162 163 164 165 /** 166 * The BER type for the scope element of the DNSelection sequence. 167 */ 168 private static final byte TYPE_SCOPE = (byte) 0x80; 169 170 171 172 /** 173 * The BER type for the relative element of the DNSelection sequence. 174 */ 175 private static final byte TYPE_RELATIVE = (byte) 0x81; 176 177 178 179 /** 180 * The serial version UID for this serializable class. 181 */ 182 private static final long serialVersionUID = 2528621021697410806L; 183 184 185 186 // Indicates whether to return DN values that are relative to the base DN. 187 private final boolean returnRelativeDNs; 188 189 // The maximum number of values to include per response. 190 private final int valuesPerResponse; 191 192 // The list of backend sets defined in the Directory Proxy Server issuing the 193 // request. 194 private final List<StreamProxyValuesBackendSet> backendSets; 195 196 // The list of attribute values to be returned. 197 private final List<String> attributes; 198 199 // The search scope to use if DN values are to be included. 200 private final SearchScope dnScope; 201 202 // The base DN for this stream proxy values request. 203 private final String baseDN; 204 205 206 207 /** 208 * Creates a new stream proxy values extended request with the provided 209 * information. 210 * 211 * @param baseDN The base DN which indicates the portion of the 212 * DIT to target. It must not be {@code null}. 213 * @param dnScope The scope for which to return information about 214 * entry DNs in the specified portion of the DIT. 215 * This may be {@code null} if information about 216 * entry DNs should not be returned. 217 * @param returnRelativeDNs Indicates whether DNs returned should be 218 * relative to the base DN rather than full DNs. 219 * @param attributes The names of the attributes for which to 220 * retrieve the values. This may be {@code null} 221 * or empty if only entry DNs should be retrieved. 222 * @param valuesPerResponse The maximum number of values to include per 223 * response. A value less than or equal to zero 224 * indicates that the server should choose an 225 * appropriate value. 226 * @param backendSets The list of backend sets defined in the 227 * Directory Proxy Server issuing the request. It 228 * must not be {@code null} or empty. 229 * @param controls The set of controls to include in the request. 230 * It may be {@code null} or empty if no controls 231 * should be included in the request. 232 */ 233 public StreamProxyValuesExtendedRequest(final String baseDN, 234 final SearchScope dnScope, final boolean returnRelativeDNs, 235 final List<String> attributes, final int valuesPerResponse, 236 final List<StreamProxyValuesBackendSet> backendSets, 237 final Control... controls) 238 { 239 super(STREAM_PROXY_VALUES_REQUEST_OID, 240 encodeValue(baseDN, dnScope, returnRelativeDNs, attributes, 241 valuesPerResponse, backendSets), 242 controls); 243 244 this.baseDN = baseDN; 245 this.dnScope = dnScope; 246 this.returnRelativeDNs = returnRelativeDNs; 247 this.backendSets = Collections.unmodifiableList(backendSets); 248 249 if (attributes == null) 250 { 251 this.attributes = Collections.emptyList(); 252 } 253 else 254 { 255 this.attributes = Collections.unmodifiableList(attributes); 256 } 257 258 if (valuesPerResponse < 0) 259 { 260 this.valuesPerResponse = 0; 261 } 262 else 263 { 264 this.valuesPerResponse = valuesPerResponse; 265 } 266 } 267 268 269 270 /** 271 * Creates a new stream proxy values extended request from the provided 272 * generic extended request. 273 * 274 * @param extendedRequest The generic extended request to use to create this 275 * stream proxy values extended request. 276 * 277 * @throws LDAPException If a problem occurs while decoding the request. 278 */ 279 public StreamProxyValuesExtendedRequest( 280 final ExtendedRequest extendedRequest) 281 throws LDAPException 282 { 283 super(extendedRequest); 284 285 final ASN1OctetString value = extendedRequest.getValue(); 286 if (value == null) 287 { 288 throw new LDAPException(ResultCode.DECODING_ERROR, 289 ERR_STREAM_PROXY_VALUES_REQUEST_NO_VALUE.get()); 290 } 291 292 boolean tmpRelative = true; 293 int tmpNumValues = 0; 294 final ArrayList<String> tmpAttrs = new ArrayList<>(10); 295 SearchScope tmpScope = null; 296 String tmpBaseDN = null; 297 298 final ArrayList<StreamProxyValuesBackendSet> tmpBackendSets = 299 new ArrayList<>(10); 300 301 try 302 { 303 final ASN1Element[] svElements = 304 ASN1Element.decode(value.getValue()).decodeAsSequence().elements(); 305 for (final ASN1Element svElement : svElements) 306 { 307 switch (svElement.getType()) 308 { 309 case TYPE_BASE_DN: 310 tmpBaseDN = svElement.decodeAsOctetString().stringValue(); 311 break; 312 313 case TYPE_INCLUDE_DNS: 314 final ASN1Element[] idElements = 315 svElement.decodeAsSequence().elements(); 316 for (final ASN1Element idElement : idElements) 317 { 318 switch (idElement.getType()) 319 { 320 case TYPE_SCOPE: 321 final int scopeValue = 322 idElement.decodeAsEnumerated().intValue(); 323 tmpScope = SearchScope.definedValueOf(scopeValue); 324 if (tmpScope == null) 325 { 326 throw new LDAPException(ResultCode.DECODING_ERROR, 327 ERR_STREAM_PROXY_VALUES_REQUEST_INVALID_SCOPE.get( 328 scopeValue)); 329 } 330 break; 331 case TYPE_RELATIVE: 332 tmpRelative = 333 idElement.decodeAsBoolean().booleanValue(); 334 break; 335 default: 336 throw new LDAPException(ResultCode.DECODING_ERROR, 337 ERR_STREAM_PROXY_VALUES_REQUEST_INVALID_INCLUDE_DNS_TYPE. 338 get(StaticUtils.toHex(idElement.getType()))); 339 } 340 } 341 break; 342 343 case TYPE_ATTRIBUTES: 344 final ASN1Element[] attrElements = 345 svElement.decodeAsSequence().elements(); 346 for (final ASN1Element attrElement : attrElements) 347 { 348 tmpAttrs.add(attrElement.decodeAsOctetString().stringValue()); 349 } 350 break; 351 352 case TYPE_VALUES_PER_RESPONSE: 353 tmpNumValues = svElement.decodeAsInteger().intValue(); 354 if (tmpNumValues < 0) 355 { 356 tmpNumValues = 0; 357 } 358 break; 359 360 case TYPE_BACKEND_SETS: 361 final ASN1Element[] backendSetElements = 362 svElement.decodeAsSequence().elements(); 363 for (final ASN1Element setElement : backendSetElements) 364 { 365 tmpBackendSets.add( 366 StreamProxyValuesBackendSet.decode(setElement)); 367 } 368 break; 369 370 default: 371 throw new LDAPException(ResultCode.DECODING_ERROR, 372 ERR_STREAM_PROXY_VALUES_REQUEST_INVALID_SEQUENCE_TYPE.get( 373 StaticUtils.toHex(svElement.getType()))); 374 } 375 } 376 } 377 catch (final LDAPException le) 378 { 379 throw le; 380 } 381 catch (final Exception e) 382 { 383 Debug.debugException(e); 384 throw new LDAPException(ResultCode.DECODING_ERROR, 385 ERR_STREAM_PROXY_VALUES_REQUEST_CANNOT_DECODE.get( 386 StaticUtils.getExceptionMessage(e)), e); 387 } 388 389 if (tmpBaseDN == null) 390 { 391 throw new LDAPException(ResultCode.DECODING_ERROR, 392 ERR_STREAM_PROXY_VALUES_REQUEST_NO_BASE_DN.get()); 393 } 394 395 baseDN = tmpBaseDN; 396 dnScope = tmpScope; 397 returnRelativeDNs = tmpRelative; 398 backendSets = Collections.unmodifiableList(tmpBackendSets); 399 attributes = Collections.unmodifiableList(tmpAttrs); 400 valuesPerResponse = tmpNumValues; 401 } 402 403 404 405 /** 406 * Encodes the provided information into a form suitable for use as the value 407 * of this extended request. 408 * 409 * @param baseDN The base DN which indicates the portion of the 410 * DIT to target. 411 * @param scope The scope for which to return information about 412 * entry DNs in the specified portion of the DIT. 413 * This may be {@code null} if information about 414 * entry DNs should not be returned. 415 * @param relativeDNs Indicates whether DNs returned should be 416 * relative to the base DN rather than full DNs. 417 * @param attributes The names of the attributes for which to 418 * retrieve the values. This may be {@code null} 419 * or empty if only entry DNs should be retrieved. 420 * @param valuesPerResponse The maximum number of values to include per 421 * response. A value less than or equal to zero 422 * indicates that the server should choose an 423 * appropriate value. 424 * @param backendSets The list of backend sets defined in the 425 * Directory Proxy Server issuing the request. 426 * 427 * @return The ASN.1 octet string containing the encoded value to use for 428 * this extended request. 429 */ 430 private static ASN1OctetString encodeValue(final String baseDN, 431 final SearchScope scope, final boolean relativeDNs, 432 final List<String> attributes, final int valuesPerResponse, 433 final List<StreamProxyValuesBackendSet> backendSets) 434 { 435 Validator.ensureNotNull(baseDN, backendSets); 436 Validator.ensureFalse(backendSets.isEmpty()); 437 438 final ArrayList<ASN1Element> svElements = new ArrayList<>(4); 439 svElements.add(new ASN1OctetString(TYPE_BASE_DN, baseDN)); 440 441 if (scope != null) 442 { 443 final ArrayList<ASN1Element> idElements = new ArrayList<>(2); 444 idElements.add(new ASN1Enumerated(TYPE_SCOPE, scope.intValue())); 445 446 if (! relativeDNs) 447 { 448 idElements.add(new ASN1Boolean(TYPE_RELATIVE, relativeDNs)); 449 } 450 451 svElements.add(new ASN1Sequence(TYPE_INCLUDE_DNS, idElements)); 452 } 453 454 if ((attributes != null) && (! attributes.isEmpty())) 455 { 456 final ArrayList<ASN1Element> attrElements = 457 new ArrayList<>(attributes.size()); 458 for (final String s : attributes) 459 { 460 attrElements.add(new ASN1OctetString(s)); 461 } 462 svElements.add(new ASN1Sequence(TYPE_ATTRIBUTES, attrElements)); 463 } 464 465 if (valuesPerResponse > 0) 466 { 467 svElements.add(new ASN1Integer(TYPE_VALUES_PER_RESPONSE, 468 valuesPerResponse)); 469 } 470 471 final ASN1Element[] backendSetElements = 472 new ASN1Element[backendSets.size()]; 473 for (int i=0; i < backendSetElements.length; i++) 474 { 475 backendSetElements[i] = backendSets.get(i).encode(); 476 } 477 svElements.add(new ASN1Sequence(TYPE_BACKEND_SETS, backendSetElements)); 478 479 return new ASN1OctetString(new ASN1Sequence(svElements).encode()); 480 } 481 482 483 484 /** 485 * Retrieves the base DN for this request. 486 * 487 * @return The base DN for this request. 488 */ 489 public String getBaseDN() 490 { 491 return baseDN; 492 } 493 494 495 496 /** 497 * Retrieves the scope for entry DNs to be included in intermediate responses. 498 * 499 * @return The scope for entry DNs to be included in intermediate responses, 500 * or {@code null} if information about entry DNs should not be 501 * returned. 502 */ 503 public SearchScope getDNScope() 504 { 505 return dnScope; 506 } 507 508 509 510 /** 511 * Indicates whether entry DN values returned should be relative to the 512 * provided base DN. 513 * 514 * @return {@code true} if entry DN values returned should be relative to the 515 * provided base DN, or {@code false} if they should be complete DNs. 516 */ 517 public boolean returnRelativeDNs() 518 { 519 return returnRelativeDNs; 520 } 521 522 523 524 /** 525 * Retrieves the list of names of attributes whose values should be returned 526 * to the client. 527 * 528 * @return The list of names of attributes whose values should be returned to 529 * the client, or an empty list if only information about entry DNs 530 * should be returned. 531 */ 532 public List<String> getAttributes() 533 { 534 return attributes; 535 } 536 537 538 539 /** 540 * Retrieves the maximum number of values that should be included in each 541 * stream proxy values intermediate response. 542 * 543 * @return The maximum number of values that should be included in each 544 * stream proxy values intermediate response, or 0 if the server 545 * should choose the appropriate number of values per response. 546 */ 547 public int getValuesPerResponse() 548 { 549 return valuesPerResponse; 550 } 551 552 553 554 /** 555 * Retrieves the list of backend sets defined in the Directory Proxy Server 556 * instance issuing the request. 557 * 558 * @return The list of backend sets defined in the Directory Proxy Server 559 * instance issuing the request. 560 */ 561 public List<StreamProxyValuesBackendSet> getBackendSets() 562 { 563 return backendSets; 564 } 565 566 567 568 /** 569 * {@inheritDoc} 570 */ 571 @Override() 572 public StreamProxyValuesExtendedRequest duplicate() 573 { 574 return duplicate(getControls()); 575 } 576 577 578 579 /** 580 * {@inheritDoc} 581 */ 582 @Override() 583 public StreamProxyValuesExtendedRequest duplicate( 584 final Control[] controls) 585 { 586 final StreamProxyValuesExtendedRequest r = 587 new StreamProxyValuesExtendedRequest(baseDN, dnScope, 588 returnRelativeDNs, attributes, valuesPerResponse, backendSets, 589 controls); 590 r.setResponseTimeoutMillis(getResponseTimeoutMillis(null)); 591 return r; 592 } 593 594 595 596 /** 597 * {@inheritDoc} 598 */ 599 @Override() 600 public String getExtendedRequestName() 601 { 602 return INFO_EXTENDED_REQUEST_NAME_STREAM_PROXY_VALUES.get(); 603 } 604 605 606 607 /** 608 * {@inheritDoc} 609 */ 610 @Override() 611 public void toString(final StringBuilder buffer) 612 { 613 buffer.append("StreamProxyValuesExtendedRequest(baseDN='"); 614 buffer.append(baseDN); 615 buffer.append('\''); 616 617 if (dnScope != null) 618 { 619 buffer.append(", scope='"); 620 buffer.append(dnScope.getName()); 621 buffer.append("', returnRelativeDNs="); 622 buffer.append(returnRelativeDNs); 623 } 624 625 buffer.append(", attributes={"); 626 if (! attributes.isEmpty()) 627 { 628 final Iterator<String> iterator = attributes.iterator(); 629 while (iterator.hasNext()) 630 { 631 buffer.append('\''); 632 buffer.append(iterator.next()); 633 buffer.append('\''); 634 635 if (iterator.hasNext()) 636 { 637 buffer.append(", "); 638 } 639 } 640 } 641 buffer.append('}'); 642 643 if (valuesPerResponse > 0) 644 { 645 buffer.append(", valuesPerResponse="); 646 buffer.append(valuesPerResponse); 647 } 648 649 buffer.append(", backendSets={"); 650 final Iterator<StreamProxyValuesBackendSet> setIterator = 651 backendSets.iterator(); 652 while (setIterator.hasNext()) 653 { 654 setIterator.next().toString(buffer); 655 if (setIterator.hasNext()) 656 { 657 buffer.append(", "); 658 } 659 } 660 buffer.append('}'); 661 662 final Control[] controls = getControls(); 663 if (controls.length > 0) 664 { 665 buffer.append(", controls={"); 666 for (int i=0; i < controls.length; i++) 667 { 668 if (i > 0) 669 { 670 buffer.append(", "); 671 } 672 673 buffer.append(controls[i]); 674 } 675 buffer.append('}'); 676 } 677 678 buffer.append(')'); 679 } 680}