001/* 002 * Copyright 2014-2024 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2014-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) 2014-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.controls; 037 038 039 040import java.util.ArrayList; 041import java.util.Collection; 042import java.util.Collections; 043import java.util.Iterator; 044import java.util.LinkedHashMap; 045import java.util.LinkedHashSet; 046import java.util.List; 047import java.util.Map; 048import java.util.Set; 049 050import com.unboundid.asn1.ASN1Element; 051import com.unboundid.asn1.ASN1OctetString; 052import com.unboundid.asn1.ASN1Sequence; 053import com.unboundid.asn1.ASN1Set; 054import com.unboundid.ldap.sdk.Control; 055import com.unboundid.ldap.sdk.JSONControlDecodeHelper; 056import com.unboundid.ldap.sdk.LDAPException; 057import com.unboundid.ldap.sdk.ResultCode; 058import com.unboundid.util.Debug; 059import com.unboundid.util.NotMutable; 060import com.unboundid.util.NotNull; 061import com.unboundid.util.Nullable; 062import com.unboundid.util.StaticUtils; 063import com.unboundid.util.ThreadSafety; 064import com.unboundid.util.ThreadSafetyLevel; 065import com.unboundid.util.Validator; 066import com.unboundid.util.json.JSONArray; 067import com.unboundid.util.json.JSONField; 068import com.unboundid.util.json.JSONObject; 069import com.unboundid.util.json.JSONString; 070import com.unboundid.util.json.JSONValue; 071 072import static com.unboundid.ldap.sdk.unboundidds.controls.ControlMessages.*; 073 074 075 076/** 077 * This class provides a request control which may be used to request that the 078 * Directory Proxy Server forward the associated operation to a specific backend 079 * set associated with an entry-balancing request processor. It may be either 080 * an absolute routing request, indicating that the target backend set(s) are 081 * the only ones that may be used to process the operation, or it may be used to 082 * provide a routing hint in lieu of accessing the global index. 083 * <BR> 084 * <BLOCKQUOTE> 085 * <B>NOTE:</B> This class, and other classes within the 086 * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only 087 * supported for use against Ping Identity, UnboundID, and 088 * Nokia/Alcatel-Lucent 8661 server products. These classes provide support 089 * for proprietary functionality or for external specifications that are not 090 * considered stable or mature enough to be guaranteed to work in an 091 * interoperable way with other types of LDAP servers. 092 * </BLOCKQUOTE> 093 * <BR> 094 * This control may be used for a number of different kinds of requests, as 095 * follows: 096 * <UL> 097 * <LI>For an add request that uses absolute routing, exactly one target 098 * backend set ID must be specified, and the request will be sent only to 099 * that backend set. 100 * <BR> 101 * <B>WARNING</B>: The use of absolute routing for an add 102 * operation bypasses the check to ensure that no entry already exists 103 * with the same DN as the new entry, so it is possible that an add 104 * performed immediately below the balancing point could result in 105 * creating an entry in one backend set with the same DN as another entry 106 * in a different backend set. Similarly, if the entry-balancing request 107 * processor is configured to broadcast add operations outside the 108 * balancing point rather than relying on those adds to be replicated, 109 * then it is strongly recommended that absolute routing not be used for 110 * add operations outside the balancing point because that will cause the 111 * entry to be added to only one backend set rather than to all backend 112 * sets.</LI> 113 * <LI>For an add request that uses a routing hint, exactly one target backend 114 * set ID must be specified for the first guess, although any number of 115 * fallback set IDs may be specified. For entries immediately below the 116 * balancing point, the routing hint will be used instead of a placement 117 * algorithm in order to select which backend set should hold the entry 118 * (and the fallback sets will not be used). For entries more than one 119 * level below the balancing point, the routing hint will be used in lieu 120 * of the global index as an attempt to determine where the parent entry 121 * exists, and the fallback sets may be used if the parent entry doesn't 122 * exist in the first guess set. For entries outside the balancing point, 123 * if the entry-balancing request processor is configured to add entries 124 * to one set and allow them to be replicated to other sets, then the 125 * first guess hint will be used to select the set to which the entry will 126 * be added (and the fallback sets will not be used). An add operation 127 * with a routing hint cannot be used to create multiple entries with the 128 * same DN in different backend sets, nor can it cause an entry outside 129 * the balancing point to exist in only one backend set.</LI> 130 * <LI>For a simple bind request that uses absolute routing, exactly one 131 * target backend set ID must be specified, and the request will be sent 132 * only to that backend set. If the bind fails in that set, even if the 133 * failure is because the target entry does not exist in that backend set, 134 * then the failure will be returned to the client rather than attempting 135 * the operation in a different backend set.</LI> 136 * <LI>For a simple bind request that uses a routing hint, exactly one target 137 * backend set ID must be specified for the first guess, although any 138 * number of fallback set IDs may be specified. If the bind fails in the 139 * first guess set, it may be re-attempted in the fallback sets.</LI> 140 * <LI>For a compare request that uses absolute routing, exactly one target 141 * backend set ID must be specified, and the request will be sent only to 142 * that backend set. If the compare fails in that set, even if the 143 * failure is because the target entry does not exist in that set, then 144 * the failure will be returned to the client rather than attempting the 145 * operation in a different backend set.</LI> 146 * <LI>For a compare request that uses a routing hint, exactly one target 147 * backend set ID must be specified for the first guess, although any 148 * number of fallback set IDs may be specified. If the compare operation 149 * fails in the first guess set in a way that suggests the target entry 150 * does not exist in that backend set, then it will be re-attempted in the 151 * fallback sets.</LI> 152 * <LI>For a delete request that uses absolute routing, exactly one target 153 * backend set ID must be specified, and the request will be sent only to 154 * that backend set. If the delete fails in that set, even if the failure 155 * is because the target entry does not exist in that set, then the 156 * failure will be returned to the client rather than attempting the 157 * operation in a different backend set. 158 * <BR> 159 * <B>WARNING</B>: If the entry-balancing request processor is configured 160 * to broadcast delete operations outside the balancing point rather than 161 * relying on those deletes to be replicated, then it is strongly 162 * recommended that absolute routing not be used for delete operations 163 * outside the balancing point because that will cause the entry to be 164 * deleted in only one backend set and will remain in all other backend 165 * sets.</LI> 166 * <LI>For a delete request that uses a routing hint, exactly one target 167 * backend set ID must be specified for the first guess, although any 168 * number of fallback set IDs may be specified. For entries below the 169 * balancing point, the routing hint will be used in lieu of the global 170 * index in order to determine which backend set contains the target 171 * entry. If the delete fails in the first guess set in a way that 172 * suggests that the target entry does not exist in that backend set, then 173 * it will be re-attempted in the fallback sets. 174 * <BR> 175 * For entries outside the balancing point, if the entry-balancing request 176 * processor is configured to delete entries from only one backend set 177 * and allow that delete to be replicated to all other sets, then the 178 * routing hint may be used to select the set from which that entry will 179 * be deleted. A delete operation with a routing hint cannot be used to 180 * cause an entry outside the balancing point to be removed from only one 181 * backend set while leaving it in the remaining sets.</LI> 182 * <LI>For an atomic multi-update extended request, only absolute routing is 183 * supported, and the route to backend set request control must be 184 * attached to the extended operation itself and not to any of the 185 * requests contained inside the multi-update. Exactly one backend set ID 186 * must be specified, and the multi-update request will be sent only to 187 * that backend set.</LI> 188 * <LI>For a non-atomic multi-update extended request, the extended operation 189 * must not include a route to backend set request control. However, any 190 * or all of the requests inside the multi-update request may include a 191 * route to backend set request control, and in that case it will be 192 * treated in the same way as for a request of the same type not 193 * included in multi-update request (e.g., if a multi-update extended 194 * operation includes an add request with a route to backend set request 195 * control, then that route control will have the same effect as for the 196 * same add request with the same control processed outside a multi-update 197 * operation).</LI> 198 * <LI>For an extended request that will be processed by a proxied extended 199 * operation handler, the request may include a route to backend set 200 * request control and that control will be used to select the target 201 * backend sets instead of the proxied extended operation handler's 202 * {@code selectBackendSets} method.</LI> 203 * <LI>For a modify request that uses absolute routing, exactly one target 204 * backend set ID must be specified, and the request will be sent only to 205 * that backend set. If the modify fails in that set, even if the failure 206 * is because the target entry does not exist in that set, then the 207 * failure will be returned to the client rather than attempting the 208 * operation in a different backend set. 209 * <BR> 210 * <B>WARNING</B>: When processing a modify operation against the 211 * balancing point entry itself, the Directory Proxy Server will typically 212 * send that modify request to all backend sets to ensure that it is 213 * properly applied everywhere. However, with an absolute routing 214 * request, the modify operation will be sent only to one backend set, 215 * which will cause the entry in that set to be out of sync with the entry 216 * in all other sets. It is therefore strongly recommended that absolute 217 * routing not be used for modify operations that target the balancing 218 * point entry. Similarly, if the entry-balancing request processor is 219 * configured to broadcast modify operations targeting entries outside the 220 * balancing point to all backend sets rather than having those modify 221 * operations replicated to the other backend sets, it is strongly 222 * recommended that absolute routing not be used for those operations 223 * because the request will be sent to only one set, causing the entry in 224 * that set to be out of sync with the corresponding entry in other 225 * backend sets.</LI> 226 * <LI>For a modify request that uses a routing hint, exactly one target 227 * backend set ID must be specified for the first guess, although any 228 * number of fallback set IDs may be specified. For entries below the 229 * balancing point, the routing hint will be used in lieu of the global 230 * index in order to determine which backend set contains the target 231 * entry. If the modify attempt fails in the first guess set in a way 232 * that suggests the target entry does not exist in that backend set, then 233 * it will be re-attempted in the fallback sets. 234 * <BR> 235 * For modify operations that target the balancing point entry itself, the 236 * entry-balancing request processor will send the request to all backend 237 * sets, and the routing hint will not be used. Similarly, for entries 238 * outside the balancing point, if the entry-balancing request processor 239 * is configured to modify entries in only one backend set and allow that 240 * modify operation to be replicated to all other sets, then the routing 241 * hint may be used to select the set in which that entry will be 242 * modified. A modify operation with a routing hint cannot be used to 243 * cause an entry at or outside the balancing point to be updated in only 244 * one backend set, leaving it out of sync with the corresponding entry in 245 * the remaining sets.</LI> 246 * <LI>For a modify DN request that uses absolute routing, exactly one target 247 * backend set ID must be specified, and the request wil be sent only to 248 * that backend set. If the modify DN operation fails in that set, even 249 * if the failure is because the target entry does not exist in that set, 250 * then the failure will be returned to the client rather than attempting 251 * the operation in a different backend set. 252 * <BR> 253 * <B>WARNING</B>: Processing a modify DN operation with absolute routing 254 * bypasses the check to ensure that the new DN for the target entry does 255 * not conflict with the DN for an entry that exists in any other backend 256 * set. As a result, you are strongly discouraged from using absolute 257 * routing for any modify DN operation that would cause the new DN for the 258 * entry to be exactly one level below the balancing point. Further, for 259 * entries that exist outside the balancing point, if the entry-balancing 260 * request processor is configured to broadcast modify DN operations 261 * rather than expecting them to be replicated to the other backend sets, 262 * a modify DN operation with absolute routing would cause the change to 263 * be applied only in one backend set, leaving it out of sync with the 264 * other sets.</LI> 265 * <LI>For a modify DN request that uses a routing hint, exactly one target 266 * backend set ID must be specified for the first guess, although any 267 * number of fallback set IDs may be specified. For entries below the 268 * balancing point, the routing hint will be used in lieu of the global 269 * index in order to determine which backend set contains the target 270 * entry. If the modify attempt fails in the first guess set in a way 271 * that suggests the target entry does not exist in that backend set, then 272 * it will be re-attempted in the fallback sets. 273 * <BR> 274 * For entries outside the balancing point, if the entry-balancing request 275 * processor is configured to process modify DN operations in one backend 276 * set and allow them to be replicated to other backend sets, then the 277 * routing hint will be used to select which backend set should receive 278 * the modify DN request. A modify DN operation with a routing hint 279 * cannot be used to create a conflict in which the same DN exists in 280 * multiple backend sets, or a case in which a modify DN operation outside 281 * the balancing point leaves one backend set out of sync with the other 282 * sets.</LI> 283 * <LI>For a search request that uses absolute routing, there may be multiple 284 * target backend set IDs only if the scope of the search may include 285 * data from multiple backend sets (i.e., the base DN is at or above the 286 * balancing point, and the scope include entries at least one level below 287 * the balancing point entry). If the base and scope of the search allow 288 * it to match only entries at or above the balancing point or only 289 * entries within one backend set, then the absolute routing request must 290 * target exactly one backend set.</LI> 291 * <LI>For a search request that uses a routing hint, exactly one target 292 * backend set ID must be specified for the first guess, although any 293 * number of fallback set IDs may be specified. The routing hint will 294 * only be used for cases in which the entire scope of the search is 295 * contained entirely within a backend set (i.e., the entire scope is 296 * at or above the balancing point, or the entire scope is at least one 297 * level below the balancing point).</LI> 298 * </UL> 299 * <BR><BR> 300 * The OID for a route to backend set request control is 301 * "1.3.6.1.4.1.30221.2.5.35", and the criticality may be either {@code true} or 302 * {@code false}. It must have a value with the following encoding: 303 * <PRE> 304 * RouteToBackendSetRequest ::= SEQUENCE { 305 * entryBalancingRequestProcessorID OCTET STRING, 306 * backendSets CHOICE { 307 * absoluteRoutingRequest [0] SET OF OCTET STRING, 308 * routingHint [1] SEQUENCE { 309 * firstGuessSetIDs SET OF OCTET STRING, 310 * fallbackSetIDs SET OF OCTET STRING OPTIONAL } 311 * ... } 312 * ... } 313 * </PRE> 314 * The use of the route to backend set request control will also cause the 315 * server to behave as if the get backend set ID request control had been 316 * included, so that the get backend set ID response control may be included in 317 * operation result and search result entry messages as appropriate. 318 */ 319@NotMutable() 320@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 321public final class RouteToBackendSetRequestControl 322 extends Control 323{ 324 /** 325 * The OID (1.3.6.1.4.1.30221.2.5.35) for the route to server request control. 326 */ 327 @NotNull public static final String ROUTE_TO_BACKEND_SET_REQUEST_OID = 328 "1.3.6.1.4.1.30221.2.5.35"; 329 330 331 332 /** 333 * The name of the field used to hold the backend set IDs in the JSON 334 * representation of this control. 335 */ 336 @NotNull private static final String JSON_FIELD_BACKEND_SET_IDS = 337 "backend-set-ids"; 338 339 340 341 /** 342 * The name of the field used to hold the fallback backend set IDs in the JSON 343 * representation of this control. 344 */ 345 @NotNull private static final String JSON_FIELD_FALLBACK_BACKEND_SET_IDS = 346 "fallback-backend-set-ids"; 347 348 349 350 /** 351 * The name of the field used to hold the request processor ID in the JSON 352 * representation of this control. 353 */ 354 @NotNull private static final String JSON_FIELD_REQUEST_PROCESSOR = 355 "request-processor"; 356 357 358 359 /** 360 * The name of the field used to hold the routing type in the JSON 361 * representation of this control. 362 */ 363 @NotNull private static final String JSON_FIELD_ROUTING_TYPE = 364 "routing-type"; 365 366 367 368 /** 369 * The routing type identifier that will be used for absolute routing in the 370 * JSON representation of this control. 371 */ 372 @NotNull private static final String JSON_ROUTING_TYPE_ABSOLUTE_ROUTING = 373 "absolute-routing"; 374 375 376 377 /** 378 * The routing type identifier that will be used for a routing hint in the 379 * JSON representation of this control. 380 */ 381 @NotNull private static final String JSON_ROUTING_TYPE_ROUTING_HINT = 382 "routing-hint"; 383 384 385 386 /** 387 * The serial version UID for this serializable class. 388 */ 389 private static final long serialVersionUID = -2486448910813783450L; 390 391 392 393 // The routing type for the request. 394 @NotNull private final RouteToBackendSetRoutingType routingType; 395 396 // The backend set IDs for an absolute routing request. 397 @Nullable private final Set<String> absoluteBackendSetIDs; 398 399 // The backend set IDs for the fallback sets of a routing hint. 400 @Nullable private final Set<String> routingHintFallbackSetIDs; 401 402 // The backend set IDs for the first guess of a routing hint. 403 @Nullable private final Set<String> routingHintFirstGuessSetIDs; 404 405 // The identifier for the entry-balancing request processor with which the 406 // backend set IDs are associated. 407 @NotNull private final String entryBalancingRequestProcessorID; 408 409 410 411 /** 412 * Creates a new route to backend set request control with the provided 413 * information. 414 * 415 * @param isCritical Indicates whether this control 416 * should be critical. 417 * @param encodedValue The encoded value for this 418 * control. It must not be 419 * {@code null}. 420 * @param entryBalancingRequestProcessorID The identifier for the 421 * entry-balancing request processor 422 * with which the backend set IDs 423 * are associated. It must not be 424 * {@code null}. 425 * @param routingType The routing type for this 426 * request. It must not be 427 * {@code null}. 428 * @param absoluteBackendSetIDs The collection of backend sets to 429 * which the request should be sent 430 * for an absolute routing request. 431 * It must be non-{@code null} and 432 * non-empty for an absolute routing 433 * request, and must be {@code null} 434 * for a routing hint. 435 * @param routingHintFirstGuessSetIDs The collection of backend sets 436 * that should be used as the first 437 * guess for a routing hint request. 438 * It must be {@code null} for an 439 * absolute routing request, and 440 * must be non-{@code null} and 441 * non-empty for a routing hint. 442 * @param routingHintFallbackSetIDs The collection of fallback 443 * backend sets that should be used 444 * for a routing hint request if the 445 * first guess was unsuccessful. It 446 * must be {@code null} for an 447 * absolute routing request, and may 448 * be {@code null} for a routing 449 * hint if the fallback sets should 450 * be all backend sets for the 451 * entry-balancing request processor 452 * that were not included in the 453 * first guess. If it is 454 * non-{@code null}, then it must 455 * also be non-empty. 456 */ 457 private RouteToBackendSetRequestControl(final boolean isCritical, 458 @NotNull final ASN1OctetString encodedValue, 459 @NotNull final String entryBalancingRequestProcessorID, 460 @NotNull final RouteToBackendSetRoutingType routingType, 461 @Nullable final Collection<String> absoluteBackendSetIDs, 462 @Nullable final Collection<String> routingHintFirstGuessSetIDs, 463 @Nullable final Collection<String> routingHintFallbackSetIDs) 464 { 465 super(ROUTE_TO_BACKEND_SET_REQUEST_OID, isCritical, encodedValue); 466 467 this.entryBalancingRequestProcessorID = entryBalancingRequestProcessorID; 468 this.routingType = routingType; 469 470 if (absoluteBackendSetIDs == null) 471 { 472 this.absoluteBackendSetIDs = null; 473 } 474 else 475 { 476 this.absoluteBackendSetIDs = Collections.unmodifiableSet( 477 new LinkedHashSet<>(absoluteBackendSetIDs)); 478 } 479 480 if (routingHintFirstGuessSetIDs == null) 481 { 482 this.routingHintFirstGuessSetIDs = null; 483 } 484 else 485 { 486 this.routingHintFirstGuessSetIDs = Collections.unmodifiableSet( 487 new LinkedHashSet<>(routingHintFirstGuessSetIDs)); 488 } 489 490 if (routingHintFallbackSetIDs == null) 491 { 492 this.routingHintFallbackSetIDs = null; 493 } 494 else 495 { 496 this.routingHintFallbackSetIDs = Collections.unmodifiableSet( 497 new LinkedHashSet<>(routingHintFallbackSetIDs)); 498 } 499 } 500 501 502 503 /** 504 * Creates a new route to backend set request control that is decoded from the 505 * provided generic control. 506 * 507 * @param control The control to decode as a route to backend set request 508 * control. 509 * 510 * @throws LDAPException If the provided control cannot be decoded as a 511 * route to backend set request control. 512 */ 513 public RouteToBackendSetRequestControl(@NotNull final Control control) 514 throws LDAPException 515 { 516 super(control); 517 518 final ASN1OctetString value = control.getValue(); 519 if (value == null) 520 { 521 throw new LDAPException(ResultCode.DECODING_ERROR, 522 ERR_ROUTE_TO_BACKEND_SET_REQUEST_MISSING_VALUE.get()); 523 } 524 525 try 526 { 527 final ASN1Element[] elements = 528 ASN1Sequence.decodeAsSequence(value.getValue()).elements(); 529 entryBalancingRequestProcessorID = 530 ASN1OctetString.decodeAsOctetString(elements[0]).stringValue(); 531 532 routingType = RouteToBackendSetRoutingType.valueOf(elements[1].getType()); 533 if (routingType == null) 534 { 535 throw new LDAPException(ResultCode.DECODING_ERROR, 536 ERR_ROUTE_TO_BACKEND_SET_REQUEST_UNKNOWN_ROUTING_TYPE.get( 537 StaticUtils.toHex(elements[1].getType()))); 538 } 539 540 if (routingType == RouteToBackendSetRoutingType.ABSOLUTE_ROUTING) 541 { 542 final ASN1Element[] arElements = 543 ASN1Set.decodeAsSet(elements[1]).elements(); 544 final LinkedHashSet<String> arSet = new LinkedHashSet<>( 545 StaticUtils.computeMapCapacity(arElements.length)); 546 for (final ASN1Element e : arElements) 547 { 548 arSet.add(ASN1OctetString.decodeAsOctetString(e).stringValue()); 549 } 550 absoluteBackendSetIDs = Collections.unmodifiableSet(arSet); 551 if (absoluteBackendSetIDs.isEmpty()) 552 { 553 throw new LDAPException(ResultCode.DECODING_ERROR, 554 ERR_ROUTE_TO_BACKEND_SET_REQUEST_ABSOLUTE_SET_EMPTY.get()); 555 } 556 557 routingHintFirstGuessSetIDs = null; 558 routingHintFallbackSetIDs = null; 559 } 560 else 561 { 562 final ASN1Element[] hintElements = 563 ASN1Sequence.decodeAsSequence(elements[1]).elements(); 564 565 final ASN1Element[] firstGuessElements = 566 ASN1Set.decodeAsSet(hintElements[0]).elements(); 567 final LinkedHashSet<String> firstGuessSet = new LinkedHashSet<>( 568 StaticUtils.computeMapCapacity(firstGuessElements.length)); 569 for (final ASN1Element e : firstGuessElements) 570 { 571 firstGuessSet.add( 572 ASN1OctetString.decodeAsOctetString(e).stringValue()); 573 } 574 routingHintFirstGuessSetIDs = 575 Collections.unmodifiableSet(firstGuessSet); 576 if (routingHintFirstGuessSetIDs.isEmpty()) 577 { 578 throw new LDAPException(ResultCode.DECODING_ERROR, 579 ERR_ROUTE_TO_BACKEND_SET_REQUEST_HINT_FIRST_SET_EMPTY.get()); 580 } 581 582 if (hintElements.length == 1) 583 { 584 routingHintFallbackSetIDs = null; 585 } 586 else 587 { 588 final ASN1Element[] fallbackElements = 589 ASN1Set.decodeAsSet(hintElements[1]).elements(); 590 final LinkedHashSet<String> fallbackSet = new LinkedHashSet<>( 591 StaticUtils.computeMapCapacity(fallbackElements.length)); 592 for (final ASN1Element e : fallbackElements) 593 { 594 fallbackSet.add( 595 ASN1OctetString.decodeAsOctetString(e).stringValue()); 596 } 597 routingHintFallbackSetIDs = Collections.unmodifiableSet(fallbackSet); 598 if (routingHintFallbackSetIDs.isEmpty()) 599 { 600 throw new LDAPException(ResultCode.DECODING_ERROR, 601 ERR_ROUTE_TO_BACKEND_SET_REQUEST_HINT_FALLBACK_SET_EMPTY. 602 get()); 603 } 604 } 605 606 absoluteBackendSetIDs = null; 607 } 608 } 609 catch (final LDAPException le) 610 { 611 Debug.debugException(le); 612 throw le; 613 } 614 catch (final Exception e) 615 { 616 Debug.debugException(e); 617 throw new LDAPException(ResultCode.DECODING_ERROR, 618 ERR_ROUTE_TO_BACKEND_SET_REQUEST_CANNOT_DECODE.get( 619 StaticUtils.getExceptionMessage(e)), 620 e); 621 } 622 } 623 624 625 626 /** 627 * Creates a new route to backend set request control that may be used for 628 * absolute routing to the specified backend set. 629 * 630 * @param isCritical Indicates whether the control 631 * should be marked critical. 632 * @param entryBalancingRequestProcessorID The identifier for the 633 * entry-balancing request processor 634 * with which the backend set ID 635 * is associated. It must not be 636 * {@code null}. 637 * @param backendSetID The backend set ID for the 638 * backend set to which the request 639 * should be forwarded. It must not 640 * be {@code null}. 641 * 642 * @return The route to backend set request control created from the 643 * provided information. 644 */ 645 @NotNull() 646 public static RouteToBackendSetRequestControl createAbsoluteRoutingRequest( 647 final boolean isCritical, 648 @NotNull final String entryBalancingRequestProcessorID, 649 @NotNull final String backendSetID) 650 { 651 return createAbsoluteRoutingRequest(isCritical, 652 entryBalancingRequestProcessorID, 653 Collections.singletonList(backendSetID)); 654 } 655 656 657 658 /** 659 * Creates a new route to backend set request control that may be used for 660 * absolute routing to the specified collection of backend sets. 661 * 662 * @param isCritical Indicates whether the control 663 * should be marked critical. 664 * @param entryBalancingRequestProcessorID The identifier for the 665 * entry-balancing request processor 666 * with which the backend set IDs 667 * are associated. It must not be 668 * {@code null}. 669 * @param backendSetIDs The backend set IDs for the 670 * backend sets to which the request 671 * should be forwarded. It must not 672 * be {@code null} or empty. 673 * 674 * @return The route to backend set request control created from the 675 * provided information. 676 */ 677 @NotNull() 678 public static RouteToBackendSetRequestControl createAbsoluteRoutingRequest( 679 final boolean isCritical, 680 @NotNull final String entryBalancingRequestProcessorID, 681 @NotNull final Collection<String> backendSetIDs) 682 { 683 Validator.ensureNotNull(backendSetIDs); 684 Validator.ensureFalse(backendSetIDs.isEmpty()); 685 686 final ArrayList<ASN1Element> backendSetIDElements = 687 new ArrayList<>(backendSetIDs.size()); 688 for (final String s : backendSetIDs) 689 { 690 backendSetIDElements.add(new ASN1OctetString(s)); 691 } 692 693 final RouteToBackendSetRoutingType routingType = 694 RouteToBackendSetRoutingType.ABSOLUTE_ROUTING; 695 final ASN1Sequence valueSequence = new ASN1Sequence( 696 new ASN1OctetString(entryBalancingRequestProcessorID), 697 new ASN1Set(routingType.getBERType(), backendSetIDElements)); 698 699 return new RouteToBackendSetRequestControl(isCritical, 700 new ASN1OctetString(valueSequence.encode()), 701 entryBalancingRequestProcessorID, routingType, backendSetIDs, null, 702 null); 703 } 704 705 706 707 /** 708 * Creates a new route to backend set request control that may be used to 709 * provide a hint as to the backend set to which the operation should be 710 * forwarded, and an optional specification of fallback sets. 711 * 712 * @param isCritical Indicates whether the control 713 * should be marked critical. 714 * @param entryBalancingRequestProcessorID The identifier for the 715 * entry-balancing request processor 716 * with which the backend set IDs 717 * are associated. It must not be 718 * {@code null}. 719 * @param firstGuessSetID The backend set ID for the 720 * backend set to try first. It 721 * must not be {@code null}. 722 * @param fallbackSetIDs The backend set ID(s) for the 723 * backend set(s) to use if none of 724 * the servers in the first guess 725 * set returns a success result. 726 * If this is {@code null}, then the 727 * server will use a default 728 * fallback set of all backend sets 729 * except for the first guess set. 730 * If this is not {@code null}, then 731 * it must also be non-empty. 732 * 733 * @return The route to backend set request control created from the 734 * provided information. 735 */ 736 @NotNull() 737 public static RouteToBackendSetRequestControl createRoutingHintRequest( 738 final boolean isCritical, 739 @NotNull final String entryBalancingRequestProcessorID, 740 @NotNull final String firstGuessSetID, 741 @Nullable final Collection<String> fallbackSetIDs) 742 { 743 return createRoutingHintRequest(isCritical, 744 entryBalancingRequestProcessorID, 745 Collections.singletonList(firstGuessSetID), 746 fallbackSetIDs); 747 } 748 749 750 751 /** 752 * Creates a new route to backend set request control that may be used to 753 * provide a hint as to the backend set(s) to which the operation should be 754 * forwarded, and an optional specification of fallback sets. 755 * 756 * @param isCritical Indicates whether the control 757 * should be marked critical. 758 * @param entryBalancingRequestProcessorID The identifier for the 759 * entry-balancing request processor 760 * with which the backend set IDs 761 * are associated. It must not be 762 * {@code null}. 763 * @param firstGuessSetIDs The backend set ID(s) for the 764 * backend set(s) to try first. It 765 * must not be {@code null} or 766 * empty. 767 * @param fallbackSetIDs The backend set ID(s) for the 768 * backend set(s) to use if none of 769 * the servers in the first guess 770 * set returns a success result. 771 * If this is {@code null}, then the 772 * server will use a default 773 * fallback set of all backend sets 774 * not included in the first guess. 775 * If this is not {@code null}, then 776 * it must also be non-empty. 777 * 778 * @return The route to backend set request control created from the 779 * provided information. 780 */ 781 @NotNull() 782 public static RouteToBackendSetRequestControl createRoutingHintRequest( 783 final boolean isCritical, 784 @NotNull final String entryBalancingRequestProcessorID, 785 @NotNull final Collection<String> firstGuessSetIDs, 786 @Nullable final Collection<String> fallbackSetIDs) 787 { 788 Validator.ensureNotNull(firstGuessSetIDs); 789 Validator.ensureFalse(firstGuessSetIDs.isEmpty()); 790 791 if (fallbackSetIDs != null) 792 { 793 Validator.ensureFalse(fallbackSetIDs.isEmpty()); 794 } 795 796 final ArrayList<ASN1Element> backendSetsElements = new ArrayList<>(2); 797 final ArrayList<ASN1Element> firstGuessElements = 798 new ArrayList<>(firstGuessSetIDs.size()); 799 for (final String s : firstGuessSetIDs) 800 { 801 firstGuessElements.add(new ASN1OctetString(s)); 802 } 803 backendSetsElements.add(new ASN1Set(firstGuessElements)); 804 805 if (fallbackSetIDs != null) 806 { 807 final ArrayList<ASN1Element> fallbackElements = 808 new ArrayList<>(fallbackSetIDs.size()); 809 for (final String s : fallbackSetIDs) 810 { 811 fallbackElements.add(new ASN1OctetString(s)); 812 } 813 backendSetsElements.add(new ASN1Set(fallbackElements)); 814 } 815 816 final RouteToBackendSetRoutingType routingType = 817 RouteToBackendSetRoutingType.ROUTING_HINT; 818 final ASN1Sequence valueSequence = new ASN1Sequence( 819 new ASN1OctetString(entryBalancingRequestProcessorID), 820 new ASN1Sequence(routingType.getBERType(), backendSetsElements)); 821 822 return new RouteToBackendSetRequestControl(isCritical, 823 new ASN1OctetString(valueSequence.encode()), 824 entryBalancingRequestProcessorID, routingType, null, firstGuessSetIDs, 825 fallbackSetIDs); 826 } 827 828 829 830 /** 831 * Retrieves the identifier for the entry-balancing request processor with 832 * which the backend set IDs are associated. 833 * 834 * @return The identifier for the entry-balancing request processor with 835 * which the backend set IDs are associated. 836 */ 837 @NotNull() 838 public String getEntryBalancingRequestProcessorID() 839 { 840 return entryBalancingRequestProcessorID; 841 } 842 843 844 845 /** 846 * Retrieves the type of routing requested by this control. 847 * 848 * @return The type of routing requested by this control. 849 */ 850 @NotNull() 851 public RouteToBackendSetRoutingType getRoutingType() 852 { 853 return routingType; 854 } 855 856 857 858 /** 859 * Retrieves the collection of backend set IDs for the backend sets to which 860 * the request should be forwarded if the control uses absolute routing. 861 * 862 * @return The collection of backend set IDs for the backend sets to which 863 * the request should be forwarded if the control uses absolute 864 * routing, or {@code null} if the control uses a routing hint. 865 */ 866 @Nullable() 867 public Set<String> getAbsoluteBackendSetIDs() 868 { 869 return absoluteBackendSetIDs; 870 } 871 872 873 874 /** 875 * Retrieves the collection of backend set IDs for the first guess of backend 876 * sets to which the request should be forwarded if the control uses a routing 877 * hint. 878 * 879 * @return The collection of backend set IDs for the first guess of backend 880 * sets to which the request should be forwarded if the control uses 881 * a routing hint, or {@code null} if the control uses absolute 882 * routing. 883 */ 884 @Nullable() 885 public Set<String> getRoutingHintFirstGuessSetIDs() 886 { 887 return routingHintFirstGuessSetIDs; 888 } 889 890 891 892 /** 893 * Retrieves the collection of backend set IDs to which the request should be 894 * forwarded if the control uses a routing hint and an explicit group of 895 * fallback sets was specified. 896 * 897 * @return The collection of backend set IDs to which the request should be 898 * forwarded if the control uses a routing hint and an explicit 899 * group of fallback sets was specified, or {@code null} if the 900 * control uses absolute routing or if a default group of fallback 901 * sets (all sets not included in the first guess) should be used. 902 */ 903 @Nullable() 904 public Set<String> getRoutingHintFallbackSetIDs() 905 { 906 return routingHintFallbackSetIDs; 907 } 908 909 910 911 /** 912 * {@inheritDoc} 913 */ 914 @Override() 915 @NotNull() 916 public String getControlName() 917 { 918 return INFO_CONTROL_NAME_ROUTE_TO_BACKEND_SET_REQUEST.get(); 919 } 920 921 922 923 /** 924 * Retrieves a representation of this route to backend set request control as 925 * a JSON object. The JSON object uses the following fields: 926 * <UL> 927 * <LI> 928 * {@code oid} -- A mandatory string field whose value is the object 929 * identifier for this control. For the route to backend set request 930 * control, the OID is "1.3.6.1.4.1.30221.2.5.35". 931 * </LI> 932 * <LI> 933 * {@code control-name} -- An optional string field whose value is a 934 * human-readable name for this control. This field is only intended for 935 * descriptive purposes, and when decoding a control, the {@code oid} 936 * field should be used to identify the type of control. 937 * </LI> 938 * <LI> 939 * {@code criticality} -- A mandatory Boolean field used to indicate 940 * whether this control is considered critical. 941 * </LI> 942 * <LI> 943 * {@code value-base64} -- An optional string field whose value is a 944 * base64-encoded representation of the raw value for this route to 945 * backend set request control. Exactly one of the {@code value-base64} 946 * and {@code value-json} fields must be present. 947 * </LI> 948 * <LI> 949 * {@code value-json} -- An optional JSON object field whose value is a 950 * user-friendly representation of the value for this route to backend set 951 * request control. Exactly one of the {@code value-base64} and 952 * {@code value-json} fields must be present, and if the 953 * {@code value-json} field is used, then it will use the following 954 * fields: 955 * <UL> 956 * <LI> 957 * {@code request-processor} -- A mandatory string field whose value 958 * is an identifier for the entry-balancing request processor to which 959 * this control applies. 960 * </LI> 961 * <LI> 962 * {@code routing-type} -- A mandatory string field whose value 963 * specifies the type of routing to perform. The value must be one 964 * of "{@code absolute-routing}" or "{@code routing-hint}". 965 * </LI> 966 * <LI> 967 * {@code backend-set-ids} -- A mandatory, non-empty array field whose 968 * values are strings that specify the primary backend set(s) to use. 969 * </LI> 970 * <LI> 971 * {@code fallback-backend-set-ids} -- An optional array field whose 972 * values specify alternative backend sets that may be used if the 973 * {@code routing-type} value is "{@code routing-hint}" and the 974 * requested operation fails in the primary backend sets. This field 975 * must not be provided with a {@code routing-type} value of 976 * "{@code absolute-routing}", and it may optionally be used with a 977 * {@code routing-type} value of "{@code routing-hint}". 978 * </LI> 979 * </UL> 980 * </LI> 981 * </UL> 982 * 983 * @return A JSON object that contains a representation of this control. 984 */ 985 @Override() 986 @NotNull() 987 public JSONObject toJSONControl() 988 { 989 final Map<String,JSONValue> valueFields = new LinkedHashMap<>(); 990 991 valueFields.put(JSON_FIELD_REQUEST_PROCESSOR, 992 new JSONString(entryBalancingRequestProcessorID)); 993 994 switch (routingType) 995 { 996 case ABSOLUTE_ROUTING: 997 valueFields.put(JSON_FIELD_ROUTING_TYPE, 998 new JSONString(JSON_ROUTING_TYPE_ABSOLUTE_ROUTING)); 999 1000 final List<JSONValue> absoluteBackendSetIDsValues = 1001 new ArrayList<>(absoluteBackendSetIDs.size()); 1002 for (final String id : absoluteBackendSetIDs) 1003 { 1004 absoluteBackendSetIDsValues.add(new JSONString(id)); 1005 } 1006 1007 valueFields.put(JSON_FIELD_BACKEND_SET_IDS, 1008 new JSONArray(absoluteBackendSetIDsValues)); 1009 break; 1010 1011 case ROUTING_HINT: 1012 valueFields.put(JSON_FIELD_ROUTING_TYPE, 1013 new JSONString(JSON_ROUTING_TYPE_ROUTING_HINT)); 1014 1015 final List<JSONValue> firstGuessSetIDsValues = 1016 new ArrayList<>(routingHintFirstGuessSetIDs.size()); 1017 for (final String id : routingHintFirstGuessSetIDs) 1018 { 1019 firstGuessSetIDsValues.add(new JSONString(id)); 1020 } 1021 1022 valueFields.put(JSON_FIELD_BACKEND_SET_IDS, 1023 new JSONArray(firstGuessSetIDsValues)); 1024 1025 if ((routingHintFallbackSetIDs != null) && 1026 (! routingHintFallbackSetIDs.isEmpty())) 1027 { 1028 final List<JSONValue> fallbackSetIDsValues = 1029 new ArrayList<>(routingHintFallbackSetIDs.size()); 1030 for (final String id : routingHintFallbackSetIDs) 1031 { 1032 fallbackSetIDsValues.add(new JSONString(id)); 1033 } 1034 1035 valueFields.put(JSON_FIELD_FALLBACK_BACKEND_SET_IDS, 1036 new JSONArray(fallbackSetIDsValues)); 1037 } 1038 break; 1039 } 1040 1041 return new JSONObject( 1042 new JSONField(JSONControlDecodeHelper.JSON_FIELD_OID, 1043 ROUTE_TO_BACKEND_SET_REQUEST_OID), 1044 new JSONField(JSONControlDecodeHelper.JSON_FIELD_CONTROL_NAME, 1045 INFO_CONTROL_NAME_ROUTE_TO_BACKEND_SET_REQUEST.get()), 1046 new JSONField(JSONControlDecodeHelper.JSON_FIELD_CRITICALITY, 1047 isCritical()), 1048 new JSONField(JSONControlDecodeHelper.JSON_FIELD_VALUE_JSON, 1049 new JSONObject(valueFields))); 1050 } 1051 1052 1053 1054 /** 1055 * Attempts to decode the provided object as a JSON representation of a 1056 * route to backend set request control. 1057 * 1058 * @param controlObject The JSON object to be decoded. It must not be 1059 * {@code null}. 1060 * @param strict Indicates whether to use strict mode when decoding 1061 * the provided JSON object. If this is {@code true}, 1062 * then this method will throw an exception if the 1063 * provided JSON object contains any unrecognized 1064 * fields. If this is {@code false}, then unrecognized 1065 * fields will be ignored. 1066 * 1067 * @return The route to backend set request control that was decoded from 1068 * the provided JSON object. 1069 * 1070 * @throws LDAPException If the provided JSON object cannot be parsed as a 1071 * valid route to backend set request control. 1072 */ 1073 @NotNull() 1074 public static RouteToBackendSetRequestControl decodeJSONControl( 1075 @NotNull final JSONObject controlObject, 1076 final boolean strict) 1077 throws LDAPException 1078 { 1079 final JSONControlDecodeHelper jsonControl = new JSONControlDecodeHelper( 1080 controlObject, strict, true, true); 1081 1082 final ASN1OctetString rawValue = jsonControl.getRawValue(); 1083 if (rawValue != null) 1084 { 1085 return new RouteToBackendSetRequestControl(new Control( 1086 jsonControl.getOID(), jsonControl.getCriticality(), rawValue)); 1087 } 1088 1089 1090 final JSONObject valueObject = jsonControl.getValueObject(); 1091 1092 final String requestProcessor = 1093 valueObject.getFieldAsString(JSON_FIELD_REQUEST_PROCESSOR); 1094 if (requestProcessor == null) 1095 { 1096 throw new LDAPException(ResultCode.DECODING_ERROR, 1097 ERR_ROUTE_TO_BACKEND_SET_REQUEST_JSON_MISSING_FIELD.get( 1098 controlObject.toSingleLineString(), 1099 JSON_FIELD_REQUEST_PROCESSOR)); 1100 } 1101 1102 1103 final String routingTypeStr = 1104 valueObject.getFieldAsString(JSON_FIELD_ROUTING_TYPE); 1105 if (routingTypeStr == null) 1106 { 1107 throw new LDAPException(ResultCode.DECODING_ERROR, 1108 ERR_ROUTE_TO_BACKEND_SET_REQUEST_JSON_MISSING_FIELD.get( 1109 controlObject.toSingleLineString(), 1110 JSON_FIELD_ROUTING_TYPE)); 1111 } 1112 1113 final RouteToBackendSetRoutingType routingType; 1114 switch (routingTypeStr) 1115 { 1116 case JSON_ROUTING_TYPE_ABSOLUTE_ROUTING: 1117 routingType = RouteToBackendSetRoutingType.ABSOLUTE_ROUTING; 1118 break; 1119 case JSON_ROUTING_TYPE_ROUTING_HINT: 1120 routingType = RouteToBackendSetRoutingType.ROUTING_HINT; 1121 break; 1122 default: 1123 throw new LDAPException(ResultCode.DECODING_ERROR, 1124 ERR_ROUTE_TO_BACKEND_SET_REQUEST_JSON_UNKNOWN_ROUTING_TYPE.get( 1125 controlObject.toSingleLineString(), JSON_FIELD_ROUTING_TYPE, 1126 routingTypeStr, JSON_ROUTING_TYPE_ABSOLUTE_ROUTING, 1127 JSON_ROUTING_TYPE_ROUTING_HINT)); 1128 } 1129 1130 1131 final Set<String> backendSetIDs = new LinkedHashSet<>(); 1132 final List<JSONValue> backendSetIDValues = 1133 valueObject.getFieldAsArray(JSON_FIELD_BACKEND_SET_IDS); 1134 if (backendSetIDValues == null) 1135 { 1136 throw new LDAPException(ResultCode.DECODING_ERROR, 1137 ERR_ROUTE_TO_BACKEND_SET_REQUEST_JSON_MISSING_FIELD.get( 1138 controlObject.toSingleLineString(), 1139 JSON_FIELD_BACKEND_SET_IDS)); 1140 } 1141 else if (backendSetIDValues.isEmpty()) 1142 { 1143 throw new LDAPException(ResultCode.DECODING_ERROR, 1144 ERR_ROUTE_TO_BACKEND_SET_REQUEST_JSON_EMPTY_BACKEND_SET_IDS.get( 1145 controlObject.toSingleLineString(), 1146 JSON_FIELD_BACKEND_SET_IDS)); 1147 } 1148 else 1149 { 1150 for (final JSONValue v : backendSetIDValues) 1151 { 1152 if (v instanceof JSONString) 1153 { 1154 backendSetIDs.add(((JSONString) v).stringValue()); 1155 } 1156 else 1157 { 1158 throw new LDAPException(ResultCode.DECODING_ERROR, 1159 ERR_ROUTE_TO_BACKEND_SET_REQUEST_JSON_ID_NOT_STRING.get( 1160 controlObject.toSingleLineString(), 1161 JSON_FIELD_BACKEND_SET_IDS)); 1162 } 1163 } 1164 } 1165 1166 1167 final Set<String> fallbackSetIDs; 1168 final List<JSONValue> fallbackSetIDValues = 1169 valueObject.getFieldAsArray(JSON_FIELD_FALLBACK_BACKEND_SET_IDS); 1170 if ((fallbackSetIDValues == null) || fallbackSetIDValues.isEmpty()) 1171 { 1172 fallbackSetIDs = null; 1173 } 1174 else 1175 { 1176 if (routingType == RouteToBackendSetRoutingType.ABSOLUTE_ROUTING) 1177 { 1178 throw new LDAPException(ResultCode.DECODING_ERROR, 1179 ERR_ROUTE_TO_BACKEND_SET_REQUEST_JSON_ABSOLUTE_WITH_FALLBACK.get( 1180 controlObject.toSingleLineString(), 1181 JSON_FIELD_FALLBACK_BACKEND_SET_IDS, 1182 JSON_FIELD_ROUTING_TYPE, routingTypeStr)); 1183 } 1184 1185 fallbackSetIDs = new LinkedHashSet<>(fallbackSetIDValues.size()); 1186 for (final JSONValue v : fallbackSetIDValues) 1187 { 1188 if (v instanceof JSONString) 1189 { 1190 fallbackSetIDs.add(((JSONString) v).stringValue()); 1191 } 1192 else 1193 { 1194 throw new LDAPException(ResultCode.DECODING_ERROR, 1195 ERR_ROUTE_TO_BACKEND_SET_REQUEST_JSON_ID_NOT_STRING.get( 1196 controlObject.toSingleLineString(), 1197 JSON_FIELD_FALLBACK_BACKEND_SET_IDS)); 1198 } 1199 } 1200 } 1201 1202 1203 if (strict) 1204 { 1205 final List<String> unrecognizedFields = 1206 JSONControlDecodeHelper.getControlObjectUnexpectedFields( 1207 valueObject, JSON_FIELD_REQUEST_PROCESSOR, 1208 JSON_FIELD_ROUTING_TYPE, JSON_FIELD_BACKEND_SET_IDS, 1209 JSON_FIELD_FALLBACK_BACKEND_SET_IDS); 1210 if (! unrecognizedFields.isEmpty()) 1211 { 1212 throw new LDAPException(ResultCode.DECODING_ERROR, 1213 ERR_ROUTE_TO_BACKEND_SET_REQUEST_JSON_UNRECOGNIZED_FIELD.get( 1214 controlObject.toSingleLineString(), 1215 unrecognizedFields.get(0))); 1216 } 1217 } 1218 1219 1220 if (routingType == RouteToBackendSetRoutingType.ABSOLUTE_ROUTING) 1221 { 1222 return createAbsoluteRoutingRequest(jsonControl.getCriticality(), 1223 requestProcessor, backendSetIDs); 1224 } 1225 else 1226 { 1227 return createRoutingHintRequest(jsonControl.getCriticality(), 1228 requestProcessor, backendSetIDs, fallbackSetIDs); 1229 } 1230 } 1231 1232 1233 1234 /** 1235 * {@inheritDoc} 1236 */ 1237 @Override() 1238 public void toString(@NotNull final StringBuilder buffer) 1239 { 1240 buffer.append("RouteToBackendSetRequestControl(isCritical="); 1241 buffer.append(isCritical()); 1242 buffer.append(", entryBalancingRequestProcessorID='"); 1243 buffer.append(entryBalancingRequestProcessorID); 1244 buffer.append("', routingType='"); 1245 1246 Iterator<String> iterator = null; 1247 switch (routingType) 1248 { 1249 case ABSOLUTE_ROUTING: 1250 buffer.append("absolute', backendSetIDs={"); 1251 iterator = absoluteBackendSetIDs.iterator(); 1252 while (iterator.hasNext()) 1253 { 1254 buffer.append('\''); 1255 buffer.append(iterator.next()); 1256 buffer.append('\''); 1257 1258 if (iterator.hasNext()) 1259 { 1260 buffer.append(", "); 1261 } 1262 } 1263 buffer.append('}'); 1264 break; 1265 1266 case ROUTING_HINT: 1267 buffer.append("hint', firstGuessSetIDs={"); 1268 iterator = routingHintFirstGuessSetIDs.iterator(); 1269 while (iterator.hasNext()) 1270 { 1271 buffer.append('\''); 1272 buffer.append(iterator.next()); 1273 buffer.append('\''); 1274 1275 if (iterator.hasNext()) 1276 { 1277 buffer.append(", "); 1278 } 1279 } 1280 buffer.append('}'); 1281 1282 if (routingHintFallbackSetIDs != null) 1283 { 1284 buffer.append(", fallbackSetIDs={"); 1285 iterator = routingHintFallbackSetIDs.iterator(); 1286 while (iterator.hasNext()) 1287 { 1288 buffer.append('\''); 1289 buffer.append(iterator.next()); 1290 buffer.append('\''); 1291 1292 if (iterator.hasNext()) 1293 { 1294 buffer.append(", "); 1295 } 1296 } 1297 buffer.append('}'); 1298 } 1299 break; 1300 } 1301 buffer.append(')'); 1302 } 1303}