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