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}