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