001/*
002 * Copyright 2014-2022 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2014-2022 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-2022 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.LinkedHashSet;
045import java.util.Set;
046
047import com.unboundid.asn1.ASN1Element;
048import com.unboundid.asn1.ASN1OctetString;
049import com.unboundid.asn1.ASN1Sequence;
050import com.unboundid.asn1.ASN1Set;
051import com.unboundid.ldap.sdk.Control;
052import com.unboundid.ldap.sdk.LDAPException;
053import com.unboundid.ldap.sdk.ResultCode;
054import com.unboundid.util.Debug;
055import com.unboundid.util.NotMutable;
056import com.unboundid.util.NotNull;
057import com.unboundid.util.Nullable;
058import com.unboundid.util.StaticUtils;
059import com.unboundid.util.ThreadSafety;
060import com.unboundid.util.ThreadSafetyLevel;
061import com.unboundid.util.Validator;
062
063import static com.unboundid.ldap.sdk.unboundidds.controls.ControlMessages.*;
064
065
066
067/**
068 * This class provides a request control which may be used to request that the
069 * Directory Proxy Server forward the associated operation to a specific backend
070 * set associated with an entry-balancing request processor.  It may be either
071 * an absolute routing request, indicating that the target backend set(s) are
072 * the only ones that may be used to process the operation, or it may be used to
073 * provide a routing hint in lieu of accessing the global index.
074 * <BR>
075 * <BLOCKQUOTE>
076 *   <B>NOTE:</B>  This class, and other classes within the
077 *   {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only
078 *   supported for use against Ping Identity, UnboundID, and
079 *   Nokia/Alcatel-Lucent 8661 server products.  These classes provide support
080 *   for proprietary functionality or for external specifications that are not
081 *   considered stable or mature enough to be guaranteed to work in an
082 *   interoperable way with other types of LDAP servers.
083 * </BLOCKQUOTE>
084 * <BR>
085 * This control may be used for a number of different kinds of requests, as
086 * follows:
087 * <UL>
088 *   <LI>For an add request that uses absolute routing, exactly one target
089 *       backend set ID must be specified, and the request will be sent only to
090 *       that backend set.
091 *       <BR>
092 *       <B>WARNING</B>:  The use of absolute routing for an add
093 *       operation bypasses the check to ensure that no entry already exists
094 *       with the same DN as the new entry, so it is possible that an add
095 *       performed immediately below the balancing point could result in
096 *       creating an entry in one backend set with the same DN as another entry
097 *       in a different backend set.  Similarly, if the entry-balancing request
098 *       processor is configured to broadcast add operations outside the
099 *       balancing point rather than relying on those adds to be replicated,
100 *       then it is strongly recommended that absolute routing not be used for
101 *       add operations outside the balancing point because that will cause the
102 *       entry to be added to only one backend set rather than to all backend
103 *       sets.</LI>
104 *   <LI>For an add request that uses a routing hint, exactly one target backend
105 *       set ID must be specified for the first guess, although any number of
106 *       fallback set IDs may be specified.  For entries immediately below the
107 *       balancing point, the routing hint will be used instead of a placement
108 *       algorithm in order to select which backend set should hold the entry
109 *       (and the fallback sets will not be used).  For entries more than one
110 *       level below the balancing point, the routing hint will be used in lieu
111 *       of the global index as an attempt to determine where the parent entry
112 *       exists, and the fallback sets may be used if the parent entry doesn't
113 *       exist in the first guess set.  For entries outside the balancing point,
114 *       if the entry-balancing request processor is configured to add entries
115 *       to one set and allow them to be replicated to other sets, then the
116 *       first guess hint will be used to select the set to which the entry will
117 *       be added (and the fallback sets will not be used).  An add operation
118 *       with a routing hint cannot be used to create multiple entries with the
119 *       same DN in different backend sets, nor can it cause an entry outside
120 *       the balancing point to exist in only one backend set.</LI>
121 *   <LI>For a simple bind request that uses absolute routing, exactly one
122 *       target backend set ID must be specified, and the request will be sent
123 *       only to that backend set.  If the bind fails in that set, even if the
124 *       failure is because the target entry does not exist in that backend set,
125 *       then the failure will be returned to the client rather than attempting
126 *       the operation in a different backend set.</LI>
127 *   <LI>For a simple bind request that uses a routing hint, exactly one target
128 *       backend set ID must be specified for the first guess, although any
129 *       number of fallback set IDs may be specified.  If the bind fails in the
130 *       first guess set, it may be re-attempted in the fallback sets.</LI>
131 *   <LI>For a compare request that uses absolute routing, exactly one target
132 *       backend set ID must be specified, and the request will be sent only to
133 *       that backend set.  If the compare fails in that set, even if the
134 *       failure is because the target entry does not exist in that set, then
135 *       the failure will be returned to the client rather than attempting the
136 *       operation in a different backend set.</LI>
137 *   <LI>For a compare request that uses a routing hint, exactly one target
138 *       backend set ID must be specified for the first guess, although any
139 *       number of fallback set IDs may be specified.  If the compare operation
140 *       fails in the first guess set in a way that suggests the target entry
141 *       does not exist in that backend set, then it will be re-attempted in the
142 *       fallback sets.</LI>
143 *   <LI>For a delete request that uses absolute routing, exactly one target
144 *       backend set ID must be specified, and the request will be sent only to
145 *       that backend set.  If the delete fails in that set, even if the failure
146 *       is because the target entry does not exist in that set, then the
147 *       failure will be returned to the client rather than attempting the
148 *       operation in a different backend set.
149 *       <BR>
150 *       <B>WARNING</B>:  If the entry-balancing request processor is configured
151 *       to broadcast delete operations outside the balancing point rather than
152 *       relying on those deletes to be replicated, then it is strongly
153 *       recommended that absolute routing not be used for delete operations
154 *       outside the balancing point because that will cause the entry to be
155 *       deleted in only one backend set and will remain in all other backend
156 *       sets.</LI>
157 *   <LI>For a delete request that uses a routing hint, exactly one target
158 *       backend set ID must be specified for the first guess, although any
159 *       number of fallback set IDs may be specified.  For entries below the
160 *       balancing point, the routing hint will be used in lieu of the global
161 *       index in order to determine which backend set contains the target
162 *       entry.  If the delete fails in the first guess set in a way that
163 *       suggests that the target entry does not exist in that backend set, then
164 *       it will be re-attempted in the fallback sets.
165 *       <BR>
166 *       For entries outside the balancing point, if the entry-balancing request
167 *       processor is configured to delete entries from only one backend set
168 *       and allow that delete to be replicated to all other sets, then the
169 *       routing hint may be used to select the set from which that entry will
170 *       be deleted.  A delete operation with a routing hint cannot be used to
171 *       cause an entry outside the balancing point to be removed from only one
172 *       backend set while leaving it in the remaining sets.</LI>
173 *   <LI>For an atomic multi-update extended request, only absolute routing is
174 *       supported, and the route to backend set request control must be
175 *       attached to the extended operation itself and not to any of the
176 *       requests contained inside the multi-update.  Exactly one backend set ID
177 *       must be specified, and the multi-update request will be sent only to
178 *       that backend set.</LI>
179 *   <LI>For a non-atomic multi-update extended request, the extended operation
180 *       must not include a route to backend set request control.  However, any
181 *       or all of the requests inside the multi-update request may include a
182 *       route to backend set request control, and in that case it will be
183 *       treated in the same way as for a request of the same type not
184 *       included in multi-update request (e.g., if a multi-update extended
185 *       operation includes an add request with a route to backend set request
186 *       control, then that route control will have the same effect as for the
187 *       same add request with the same control processed outside a multi-update
188 *       operation).</LI>
189 *   <LI>For an extended request that will be processed by a proxied extended
190 *       operation handler, the request may include a route to backend set
191 *       request control and that control will be used to select the target
192 *       backend sets instead of the proxied extended operation handler's
193 *       {@code selectBackendSets} method.</LI>
194 *   <LI>For a modify request that uses absolute routing, exactly one target
195 *       backend set ID must be specified, and the request will be sent only to
196 *       that backend set.  If the modify fails in that set, even if the failure
197 *       is because the target entry does not exist in that set, then the
198 *       failure will be returned to the client rather than attempting the
199 *       operation in a different backend set.
200 *       <BR>
201 *       <B>WARNING</B>:  When processing a modify operation against the
202 *       balancing point entry itself, the Directory Proxy Server will typically
203 *       send that modify request to all backend sets to ensure that it is
204 *       properly applied everywhere.  However, with an absolute routing
205 *       request, the modify operation will be sent only to one backend set,
206 *       which will cause the entry in that set to be out of sync with the entry
207 *       in all other sets.  It is therefore strongly recommended that absolute
208 *       routing not be used for modify operations that target the balancing
209 *       point entry.  Similarly, if the entry-balancing request processor is
210 *       configured to broadcast modify operations targeting entries outside the
211 *       balancing point to all backend sets rather than having those modify
212 *       operations replicated to the other backend sets, it is strongly
213 *       recommended that absolute routing not be used for those operations
214 *       because the request will be sent to only one set, causing the entry in
215 *       that set to be out of sync with the corresponding entry in other
216 *       backend sets.</LI>
217 *   <LI>For a modify request that uses a routing hint, exactly one target
218 *       backend set ID must be specified for the first guess, although any
219 *       number of fallback set IDs may be specified.  For entries below the
220 *       balancing point, the routing hint will be used in lieu of the global
221 *       index in order to determine which backend set contains the target
222 *       entry.  If the modify attempt fails in the first guess set in a way
223 *       that suggests the target entry does not exist in that backend set, then
224 *       it will be re-attempted in the fallback sets.
225 *       <BR>
226 *       For modify operations that target the balancing point entry itself, the
227 *       entry-balancing request processor will send the request to all backend
228 *       sets, and the routing hint will not be used.  Similarly, for entries
229 *       outside the balancing point, if the entry-balancing request processor
230 *       is configured to modify entries in only one backend set and allow that
231 *       modify operation to be replicated to all other sets, then the routing
232 *       hint may be used to select the set in which that entry will be
233 *       modified.  A modify operation with a routing hint cannot be used to
234 *       cause an entry at or outside the balancing point to be updated in only
235 *       one backend set, leaving it out of sync with the corresponding entry in
236 *       the remaining sets.</LI>
237 *   <LI>For a modify DN request that uses absolute routing, exactly one target
238 *       backend set ID must be specified, and the request wil be sent only to
239 *       that backend set.  If the modify DN operation fails in that set, even
240 *       if the failure is because the target entry does not exist in that set,
241 *       then the failure will be returned to the client rather than attempting
242 *       the operation in a different backend set.
243 *       <BR>
244 *       <B>WARNING</B>:  Processing a modify DN operation with absolute routing
245 *       bypasses the check to ensure that the new DN for the target entry does
246 *       not conflict with the DN for an entry that exists in any other backend
247 *       set.  As a result, you are strongly discouraged from using absolute
248 *       routing for any modify DN operation that would cause the new DN for the
249 *       entry to be exactly one level below the balancing point.  Further, for
250 *       entries that exist outside the balancing point, if the entry-balancing
251 *       request processor is configured to broadcast modify DN operations
252 *       rather than expecting them to be replicated to the other backend sets,
253 *       a modify DN operation with absolute routing would cause the change to
254 *       be applied only in one backend set, leaving it out of sync with the
255 *       other sets.</LI>
256 *   <LI>For a modify DN request that uses a routing hint, exactly one target
257 *       backend set ID must be specified for the first guess, although any
258 *       number of fallback set IDs may be specified.  For entries below the
259 *       balancing point, the routing hint will be used in lieu of the global
260 *       index in order to determine which backend set contains the target
261 *       entry.  If the modify attempt fails in the first guess set in a way
262 *       that suggests the target entry does not exist in that backend set, then
263 *       it will be re-attempted in the fallback sets.
264 *       <BR>
265 *       For entries outside the balancing point, if the entry-balancing request
266 *       processor is configured to process modify DN operations in one backend
267 *       set and allow them to be replicated to other backend sets, then the
268 *       routing hint will be used to select which backend set should receive
269 *       the modify DN request.  A modify DN operation with a routing hint
270 *       cannot be used to create a conflict in which the same DN exists in
271 *       multiple backend sets, or a case in which a modify DN operation outside
272 *       the balancing point leaves one backend set out of sync with the other
273 *       sets.</LI>
274 *   <LI>For a search request that uses absolute routing, there may be multiple
275 *       target backend set IDs only if the scope of the search may include
276 *       data from multiple backend sets (i.e., the base DN is at or above the
277 *       balancing point, and the scope include entries at least one level below
278 *       the balancing point entry).  If the base and scope of the search allow
279 *       it to match only entries at or above the balancing point or only
280 *       entries within one backend set, then the absolute routing request must
281 *       target exactly one backend set.</LI>
282 *   <LI>For a search request that uses a routing hint, exactly one target
283 *       backend set ID must be specified for the first guess, although any
284 *       number of fallback set IDs may be specified.  The routing hint will
285 *       only be used for cases in which the entire scope of the search is
286 *       contained entirely within a backend set (i.e., the entire scope is
287 *       at or above the balancing point, or the entire scope is at least one
288 *       level below the balancing point).</LI>
289 * </UL>
290 * <BR><BR>
291 * The OID for a route to backend set request control is
292 * "1.3.6.1.4.1.30221.2.5.35", and the criticality may be either {@code true} or
293 * {@code false}.  It must have a value with the following encoding:
294 * <PRE>
295 *   RouteToBackendSetRequest ::= SEQUENCE {
296 *        entryBalancingRequestProcessorID     OCTET STRING,
297 *        backendSets                          CHOICE {
298 *             absoluteRoutingRequest     [0] SET OF OCTET STRING,
299 *             routingHint                [1] SEQUENCE {
300 *                  firstGuessSetIDs     SET OF OCTET STRING,
301 *                  fallbackSetIDs       SET OF OCTET STRING OPTIONAL }
302 *             ... }
303 *        ... }
304 * </PRE>
305 * The use of the route to backend set request control will also cause the
306 * server to behave as if the get backend set ID request control had been
307 * included, so that the get backend set ID response control may be included in
308 * operation result and search result entry messages as appropriate.
309 */
310@NotMutable()
311@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
312public final class RouteToBackendSetRequestControl
313       extends Control
314{
315  /**
316   * The OID (1.3.6.1.4.1.30221.2.5.35) for the route to server request control.
317   */
318  @NotNull public static final String ROUTE_TO_BACKEND_SET_REQUEST_OID =
319       "1.3.6.1.4.1.30221.2.5.35";
320
321
322
323  /**
324   * The serial version UID for this serializable class.
325   */
326  private static final long serialVersionUID = -2486448910813783450L;
327
328
329
330  // The routing type for the request.
331  @NotNull private final RouteToBackendSetRoutingType routingType;
332
333  // The backend set IDs for an absolute routing request.
334  @Nullable private final Set<String> absoluteBackendSetIDs;
335
336  // The backend set IDs for the fallback sets of a routing hint.
337  @Nullable private final Set<String> routingHintFallbackSetIDs;
338
339  // The backend set IDs for the first guess of a routing hint.
340  @Nullable private final Set<String> routingHintFirstGuessSetIDs;
341
342  // The identifier for the entry-balancing request processor with which the
343  // backend set IDs are associated.
344  @NotNull private final String entryBalancingRequestProcessorID;
345
346
347
348  /**
349   * Creates a new route to backend set request control with the provided
350   * information.
351   *
352   * @param  isCritical                        Indicates whether this control
353   *                                           should be critical.
354   * @param  encodedValue                      The encoded value for this
355   *                                           control.  It must not be
356   *                                           {@code null}.
357   * @param  entryBalancingRequestProcessorID  The identifier for the
358   *                                           entry-balancing request processor
359   *                                           with which the backend set IDs
360   *                                           are associated.  It must not be
361   *                                           {@code null}.
362   * @param  routingType                       The routing type for this
363   *                                           request.  It must not be
364   *                                           {@code null}.
365   * @param  absoluteBackendSetIDs             The collection of backend sets to
366   *                                           which the request should be sent
367   *                                           for an absolute routing request.
368   *                                           It must be non-{@code null} and
369   *                                           non-empty for an absolute routing
370   *                                           request, and must be {@code null}
371   *                                           for a routing hint.
372   * @param  routingHintFirstGuessSetIDs       The collection of backend sets
373   *                                           that should be used as the first
374   *                                           guess for a routing hint request.
375   *                                           It must be {@code null} for an
376   *                                           absolute routing request, and
377   *                                           must be non-{@code null} and
378   *                                           non-empty for a routing hint.
379   * @param  routingHintFallbackSetIDs         The collection of fallback
380   *                                           backend sets that should be used
381   *                                           for a routing hint request if the
382   *                                           first guess was unsuccessful.  It
383   *                                           must be {@code null} for an
384   *                                           absolute routing request, and may
385   *                                           be {@code null} for a routing
386   *                                           hint if the fallback sets should
387   *                                           be all backend sets for the
388   *                                           entry-balancing request processor
389   *                                           that were not included in the
390   *                                           first guess.  If it is
391   *                                           non-{@code null}, then it must
392   *                                           also be non-empty.
393   */
394  private RouteToBackendSetRequestControl(final boolean isCritical,
395               @NotNull final ASN1OctetString encodedValue,
396               @NotNull final String entryBalancingRequestProcessorID,
397               @NotNull final RouteToBackendSetRoutingType routingType,
398               @Nullable final Collection<String> absoluteBackendSetIDs,
399               @Nullable final Collection<String> routingHintFirstGuessSetIDs,
400               @Nullable final Collection<String> routingHintFallbackSetIDs)
401  {
402    super(ROUTE_TO_BACKEND_SET_REQUEST_OID, isCritical, encodedValue);
403
404    this.entryBalancingRequestProcessorID = entryBalancingRequestProcessorID;
405    this.routingType = routingType;
406
407    if (absoluteBackendSetIDs == null)
408    {
409      this.absoluteBackendSetIDs = null;
410    }
411    else
412    {
413      this.absoluteBackendSetIDs = Collections.unmodifiableSet(
414           new LinkedHashSet<>(absoluteBackendSetIDs));
415    }
416
417    if (routingHintFirstGuessSetIDs == null)
418    {
419      this.routingHintFirstGuessSetIDs = null;
420    }
421    else
422    {
423      this.routingHintFirstGuessSetIDs = Collections.unmodifiableSet(
424           new LinkedHashSet<>(routingHintFirstGuessSetIDs));
425    }
426
427    if (routingHintFallbackSetIDs == null)
428    {
429      this.routingHintFallbackSetIDs = null;
430    }
431    else
432    {
433      this.routingHintFallbackSetIDs = Collections.unmodifiableSet(
434           new LinkedHashSet<>(routingHintFallbackSetIDs));
435    }
436  }
437
438
439
440  /**
441   * Creates a new route to backend set request control that is decoded from the
442   * provided generic control.
443   *
444   * @param  control  The control to decode as a route to backend set request
445   *                  control.
446   *
447   * @throws  LDAPException  If the provided control cannot be decoded as a
448   *                         route to backend set request control.
449   */
450  public RouteToBackendSetRequestControl(@NotNull final Control control)
451         throws LDAPException
452  {
453    super(control);
454
455    final ASN1OctetString value = control.getValue();
456    if (value == null)
457    {
458      throw new LDAPException(ResultCode.DECODING_ERROR,
459           ERR_ROUTE_TO_BACKEND_SET_REQUEST_MISSING_VALUE.get());
460    }
461
462    try
463    {
464      final ASN1Element[] elements =
465           ASN1Sequence.decodeAsSequence(value.getValue()).elements();
466      entryBalancingRequestProcessorID =
467           ASN1OctetString.decodeAsOctetString(elements[0]).stringValue();
468
469      routingType = RouteToBackendSetRoutingType.valueOf(elements[1].getType());
470      if (routingType == null)
471      {
472        throw new LDAPException(ResultCode.DECODING_ERROR,
473             ERR_ROUTE_TO_BACKEND_SET_REQUEST_UNKNOWN_ROUTING_TYPE.get(
474                  StaticUtils.toHex(elements[1].getType())));
475      }
476
477      if (routingType == RouteToBackendSetRoutingType.ABSOLUTE_ROUTING)
478      {
479        final ASN1Element[] arElements =
480             ASN1Set.decodeAsSet(elements[1]).elements();
481        final LinkedHashSet<String> arSet = new LinkedHashSet<>(
482             StaticUtils.computeMapCapacity(arElements.length));
483        for (final ASN1Element e : arElements)
484        {
485          arSet.add(ASN1OctetString.decodeAsOctetString(e).stringValue());
486        }
487        absoluteBackendSetIDs = Collections.unmodifiableSet(arSet);
488        if (absoluteBackendSetIDs.isEmpty())
489        {
490          throw new LDAPException(ResultCode.DECODING_ERROR,
491               ERR_ROUTE_TO_BACKEND_SET_REQUEST_ABSOLUTE_SET_EMPTY.get());
492        }
493
494        routingHintFirstGuessSetIDs = null;
495        routingHintFallbackSetIDs = null;
496      }
497      else
498      {
499        final ASN1Element[] hintElements =
500             ASN1Sequence.decodeAsSequence(elements[1]).elements();
501
502        final ASN1Element[] firstGuessElements =
503             ASN1Set.decodeAsSet(hintElements[0]).elements();
504        final LinkedHashSet<String> firstGuessSet = new LinkedHashSet<>(
505             StaticUtils.computeMapCapacity(firstGuessElements.length));
506        for (final ASN1Element e : firstGuessElements)
507        {
508          firstGuessSet.add(
509               ASN1OctetString.decodeAsOctetString(e).stringValue());
510        }
511        routingHintFirstGuessSetIDs =
512             Collections.unmodifiableSet(firstGuessSet);
513        if (routingHintFirstGuessSetIDs.isEmpty())
514        {
515          throw new LDAPException(ResultCode.DECODING_ERROR,
516               ERR_ROUTE_TO_BACKEND_SET_REQUEST_HINT_FIRST_SET_EMPTY.get());
517        }
518
519        if (hintElements.length == 1)
520        {
521          routingHintFallbackSetIDs = null;
522        }
523        else
524        {
525          final ASN1Element[] fallbackElements =
526               ASN1Set.decodeAsSet(hintElements[1]).elements();
527          final LinkedHashSet<String> fallbackSet = new LinkedHashSet<>(
528               StaticUtils.computeMapCapacity(fallbackElements.length));
529          for (final ASN1Element e : fallbackElements)
530          {
531            fallbackSet.add(
532                 ASN1OctetString.decodeAsOctetString(e).stringValue());
533          }
534          routingHintFallbackSetIDs = Collections.unmodifiableSet(fallbackSet);
535          if (routingHintFallbackSetIDs.isEmpty())
536          {
537            throw new LDAPException(ResultCode.DECODING_ERROR,
538                 ERR_ROUTE_TO_BACKEND_SET_REQUEST_HINT_FALLBACK_SET_EMPTY.
539                      get());
540          }
541        }
542
543        absoluteBackendSetIDs = null;
544      }
545    }
546    catch (final LDAPException le)
547    {
548      Debug.debugException(le);
549      throw le;
550    }
551    catch (final Exception e)
552    {
553      Debug.debugException(e);
554      throw new LDAPException(ResultCode.DECODING_ERROR,
555           ERR_ROUTE_TO_BACKEND_SET_REQUEST_CANNOT_DECODE.get(
556                StaticUtils.getExceptionMessage(e)),
557           e);
558    }
559  }
560
561
562
563  /**
564   * Creates a new route to backend set request control that may be used for
565   * absolute routing to the specified backend set.
566   *
567   * @param  isCritical                        Indicates whether the control
568   *                                           should be marked critical.
569   * @param  entryBalancingRequestProcessorID  The identifier for the
570   *                                           entry-balancing request processor
571   *                                           with which the backend set ID
572   *                                           is associated.  It must not be
573   *                                           {@code null}.
574   * @param  backendSetID                      The backend set ID for the
575   *                                           backend set to which the request
576   *                                           should be forwarded.  It must not
577   *                                           be {@code null}.
578   *
579   * @return  The route to backend set request control created from the
580   *          provided information.
581   */
582  @NotNull()
583  public static RouteToBackendSetRequestControl createAbsoluteRoutingRequest(
584                     final boolean isCritical,
585                     @NotNull final String entryBalancingRequestProcessorID,
586                     @NotNull final String backendSetID)
587  {
588    return createAbsoluteRoutingRequest(isCritical,
589         entryBalancingRequestProcessorID,
590         Collections.singletonList(backendSetID));
591  }
592
593
594
595  /**
596   * Creates a new route to backend set request control that may be used for
597   * absolute routing to the specified collection of backend sets.
598   *
599   * @param  isCritical                        Indicates whether the control
600   *                                           should be marked critical.
601   * @param  entryBalancingRequestProcessorID  The identifier for the
602   *                                           entry-balancing request processor
603   *                                           with which the backend set IDs
604   *                                           are associated.  It must not be
605   *                                           {@code null}.
606   * @param  backendSetIDs                     The backend set IDs for the
607   *                                           backend sets to which the request
608   *                                           should be forwarded.  It must not
609   *                                           be {@code null} or empty.
610   *
611   * @return  The route to backend set request control created from the
612   *          provided information.
613   */
614  @NotNull()
615  public static RouteToBackendSetRequestControl createAbsoluteRoutingRequest(
616                     final boolean isCritical,
617                     @NotNull final String entryBalancingRequestProcessorID,
618                     @NotNull final Collection<String> backendSetIDs)
619  {
620    Validator.ensureNotNull(backendSetIDs);
621    Validator.ensureFalse(backendSetIDs.isEmpty());
622
623    final ArrayList<ASN1Element> backendSetIDElements =
624         new ArrayList<>(backendSetIDs.size());
625    for (final String s : backendSetIDs)
626    {
627      backendSetIDElements.add(new ASN1OctetString(s));
628    }
629
630    final RouteToBackendSetRoutingType routingType =
631         RouteToBackendSetRoutingType.ABSOLUTE_ROUTING;
632    final ASN1Sequence valueSequence = new ASN1Sequence(
633         new ASN1OctetString(entryBalancingRequestProcessorID),
634         new ASN1Set(routingType.getBERType(), backendSetIDElements));
635
636    return new RouteToBackendSetRequestControl(isCritical,
637         new ASN1OctetString(valueSequence.encode()),
638         entryBalancingRequestProcessorID, routingType, backendSetIDs, null,
639         null);
640  }
641
642
643
644  /**
645   * Creates a new route to backend set request control that may be used to
646   * provide a hint as to the backend set to which the operation should be
647   * forwarded, and an optional specification of fallback sets.
648   *
649   * @param  isCritical                        Indicates whether the control
650   *                                           should be marked critical.
651   * @param  entryBalancingRequestProcessorID  The identifier for the
652   *                                           entry-balancing request processor
653   *                                           with which the backend set IDs
654   *                                           are associated.  It must not be
655   *                                           {@code null}.
656   * @param  firstGuessSetID                   The backend set ID for the
657   *                                           backend set to try first.  It
658   *                                           must not be {@code null}.
659   * @param  fallbackSetIDs                    The backend set ID(s) for the
660   *                                           backend set(s) to use if none of
661   *                                           the servers in the first guess
662   *                                           set returns a success result.
663   *                                           If this is {@code null}, then the
664   *                                           server will use a default
665   *                                           fallback set of all backend sets
666   *                                           except for the first guess set.
667   *                                           If this is not {@code null}, then
668   *                                           it must also be non-empty.
669   *
670   * @return  The route to backend set request control created from the
671   *          provided information.
672   */
673  @NotNull()
674  public static RouteToBackendSetRequestControl createRoutingHintRequest(
675                     final boolean isCritical,
676                     @NotNull final String entryBalancingRequestProcessorID,
677                     @NotNull final String firstGuessSetID,
678                     @Nullable final Collection<String> fallbackSetIDs)
679  {
680    return createRoutingHintRequest(isCritical,
681         entryBalancingRequestProcessorID,
682         Collections.singletonList(firstGuessSetID),
683         fallbackSetIDs);
684  }
685
686
687
688  /**
689   * Creates a new route to backend set request control that may be used to
690   * provide a hint as to the backend set(s) to which the operation should be
691   * forwarded, and an optional specification of fallback sets.
692   *
693   * @param  isCritical                        Indicates whether the control
694   *                                           should be marked critical.
695   * @param  entryBalancingRequestProcessorID  The identifier for the
696   *                                           entry-balancing request processor
697   *                                           with which the backend set IDs
698   *                                           are associated.  It must not be
699   *                                           {@code null}.
700   * @param  firstGuessSetIDs                  The backend set ID(s) for the
701   *                                           backend set(s) to try first.  It
702   *                                           must not be {@code null} or
703   *                                           empty.
704   * @param  fallbackSetIDs                    The backend set ID(s) for the
705   *                                           backend set(s) to use if none of
706   *                                           the servers in the first guess
707   *                                           set returns a success result.
708   *                                           If this is {@code null}, then the
709   *                                           server will use a default
710   *                                           fallback set of all backend sets
711   *                                           not included in the first guess.
712   *                                           If this is not {@code null}, then
713   *                                           it must also be non-empty.
714   *
715   * @return  The route to backend set request control created from the
716   *          provided information.
717   */
718  @NotNull()
719  public static RouteToBackendSetRequestControl createRoutingHintRequest(
720                     final boolean isCritical,
721                     @NotNull final String entryBalancingRequestProcessorID,
722                     @NotNull final Collection<String> firstGuessSetIDs,
723                     @Nullable final Collection<String> fallbackSetIDs)
724  {
725    Validator.ensureNotNull(firstGuessSetIDs);
726    Validator.ensureFalse(firstGuessSetIDs.isEmpty());
727
728    if (fallbackSetIDs != null)
729    {
730      Validator.ensureFalse(fallbackSetIDs.isEmpty());
731    }
732
733    final ArrayList<ASN1Element> backendSetsElements = new ArrayList<>(2);
734    final ArrayList<ASN1Element> firstGuessElements =
735         new ArrayList<>(firstGuessSetIDs.size());
736    for (final String s : firstGuessSetIDs)
737    {
738      firstGuessElements.add(new ASN1OctetString(s));
739    }
740    backendSetsElements.add(new ASN1Set(firstGuessElements));
741
742    if (fallbackSetIDs != null)
743    {
744      final ArrayList<ASN1Element> fallbackElements =
745           new ArrayList<>(fallbackSetIDs.size());
746      for (final String s : fallbackSetIDs)
747      {
748        fallbackElements.add(new ASN1OctetString(s));
749      }
750      backendSetsElements.add(new ASN1Set(fallbackElements));
751    }
752
753    final RouteToBackendSetRoutingType routingType =
754         RouteToBackendSetRoutingType.ROUTING_HINT;
755    final ASN1Sequence valueSequence = new ASN1Sequence(
756         new ASN1OctetString(entryBalancingRequestProcessorID),
757         new ASN1Sequence(routingType.getBERType(), backendSetsElements));
758
759    return new RouteToBackendSetRequestControl(isCritical,
760         new ASN1OctetString(valueSequence.encode()),
761         entryBalancingRequestProcessorID, routingType, null, firstGuessSetIDs,
762         fallbackSetIDs);
763  }
764
765
766
767  /**
768   * Retrieves the identifier for the entry-balancing request processor with
769   * which the backend set IDs are associated.
770   *
771   * @return  The identifier for the entry-balancing request processor with
772   *          which the backend set IDs are associated.
773   */
774  @NotNull()
775  public String getEntryBalancingRequestProcessorID()
776  {
777    return entryBalancingRequestProcessorID;
778  }
779
780
781
782  /**
783   * Retrieves the type of routing requested by this control.
784   *
785   * @return  The type of routing requested by this control.
786   */
787  @NotNull()
788  public RouteToBackendSetRoutingType getRoutingType()
789  {
790    return routingType;
791  }
792
793
794
795  /**
796   * Retrieves the collection of backend set IDs for the backend sets to which
797   * the request should be forwarded if the control uses absolute routing.
798   *
799   * @return  The collection of backend set IDs for the backend sets to which
800   *          the request should be forwarded if the control uses absolute
801   *          routing, or {@code null} if the control uses a routing hint.
802   */
803  @Nullable()
804  public Set<String> getAbsoluteBackendSetIDs()
805  {
806    return absoluteBackendSetIDs;
807  }
808
809
810
811  /**
812   * Retrieves the collection of backend set IDs for the first guess of backend
813   * sets to which the request should be forwarded if the control uses a routing
814   * hint.
815   *
816   * @return  The collection of backend set IDs for the first guess of backend
817   *          sets to which the request should be forwarded if the control uses
818   *          a routing hint, or {@code null} if the control uses absolute
819   *          routing.
820   */
821  @Nullable()
822  public Set<String> getRoutingHintFirstGuessSetIDs()
823  {
824    return routingHintFirstGuessSetIDs;
825  }
826
827
828
829  /**
830   * Retrieves the collection of backend set IDs to which the request should be
831   * forwarded if the control uses a routing hint and an explicit group of
832   * fallback sets was specified.
833   *
834   * @return  The collection of backend set IDs to which the request should be
835   *          forwarded if the control uses a routing hint and an explicit
836   *          group of fallback sets was specified, or {@code null} if the
837   *          control uses absolute routing or if a default group of fallback
838   *          sets (all sets not included in the first guess) should be used.
839   */
840  @Nullable()
841  public Set<String> getRoutingHintFallbackSetIDs()
842  {
843    return routingHintFallbackSetIDs;
844  }
845
846
847
848  /**
849   * {@inheritDoc}
850   */
851  @Override()
852  @NotNull()
853  public String getControlName()
854  {
855    return INFO_CONTROL_NAME_ROUTE_TO_BACKEND_SET_REQUEST.get();
856  }
857
858
859
860  /**
861   * {@inheritDoc}
862   */
863  @Override()
864  public void toString(@NotNull final StringBuilder buffer)
865  {
866    buffer.append("RouteToBackendSetRequestControl(isCritical=");
867    buffer.append(isCritical());
868    buffer.append(", entryBalancingRequestProcessorID='");
869    buffer.append(entryBalancingRequestProcessorID);
870    buffer.append("', routingType='");
871
872    Iterator<String> iterator = null;
873    switch (routingType)
874    {
875      case ABSOLUTE_ROUTING:
876        buffer.append("absolute', backendSetIDs={");
877        iterator = absoluteBackendSetIDs.iterator();
878        while (iterator.hasNext())
879        {
880          buffer.append('\'');
881          buffer.append(iterator.next());
882          buffer.append('\'');
883
884          if (iterator.hasNext())
885          {
886            buffer.append(", ");
887          }
888        }
889        buffer.append('}');
890        break;
891
892      case ROUTING_HINT:
893        buffer.append("hint', firstGuessSetIDs={");
894        iterator = routingHintFirstGuessSetIDs.iterator();
895        while (iterator.hasNext())
896        {
897          buffer.append('\'');
898          buffer.append(iterator.next());
899          buffer.append('\'');
900
901          if (iterator.hasNext())
902          {
903            buffer.append(", ");
904          }
905        }
906        buffer.append('}');
907
908        if (routingHintFallbackSetIDs != null)
909        {
910          buffer.append(", fallbackSetIDs={");
911          iterator = routingHintFallbackSetIDs.iterator();
912          while (iterator.hasNext())
913          {
914            buffer.append('\'');
915            buffer.append(iterator.next());
916            buffer.append('\'');
917
918            if (iterator.hasNext())
919            {
920              buffer.append(", ");
921            }
922          }
923          buffer.append('}');
924        }
925        break;
926    }
927    buffer.append(')');
928  }
929}