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.Arrays;
027import java.util.Collection;
028import java.util.Collections;
029import java.util.Iterator;
030import java.util.LinkedHashSet;
031import java.util.Set;
032
033import com.unboundid.asn1.ASN1Element;
034import com.unboundid.asn1.ASN1OctetString;
035import com.unboundid.asn1.ASN1Sequence;
036import com.unboundid.asn1.ASN1Set;
037import com.unboundid.ldap.sdk.Control;
038import com.unboundid.ldap.sdk.LDAPException;
039import com.unboundid.ldap.sdk.ResultCode;
040import com.unboundid.util.Debug;
041import com.unboundid.util.NotMutable;
042import com.unboundid.util.StaticUtils;
043import com.unboundid.util.ThreadSafety;
044import com.unboundid.util.ThreadSafetyLevel;
045import com.unboundid.util.Validator;
046
047import static com.unboundid.ldap.sdk.unboundidds.controls.ControlMessages.*;
048
049
050
051/**
052 * This class provides a request control which may be used to request that the
053 * Directory Proxy Server forward the associated operation to a specific backend
054 * set associated with an entry-balancing request processor.  It may be either
055 * an absolute routing request, indicating that the target backend set(s) are
056 * the only ones that may be used to process the operation, or it may be used to
057 * provide a routing hint in lieu of accessing the global index.
058 * <BR>
059 * <BLOCKQUOTE>
060 *   <B>NOTE:</B>  This class, and other classes within the
061 *   {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only
062 *   supported for use against Ping Identity, UnboundID, and Alcatel-Lucent 8661
063 *   server products.  These classes provide support for proprietary
064 *   functionality or for external specifications that are not considered stable
065 *   or mature enough to be guaranteed to work in an interoperable way with
066 *   other types of LDAP servers.
067 * </BLOCKQUOTE>
068 * <BR>
069 * This control may be used for a number of different kinds of requests, as
070 * follows:
071 * <UL>
072 *   <LI>For an add request that uses absolute routing, exactly one target
073 *       backend set ID must be specified, and the request will be sent only to
074 *       that backend set.
075 *       <BR>
076 *       <B>WARNING</B>:  The use of absolute routing for an add
077 *       operation bypasses the check to ensure that no entry already exists
078 *       with the same DN as the new entry, so it is possible that an add
079 *       performed immediately below the balancing point could result in
080 *       creating an entry in one backend set with the same DN as another entry
081 *       in a different backend set.  Similarly, if the entry-balancing request
082 *       processor is configured to broadcast add operations outside the
083 *       balancing point rather than relying on those adds to be replicated,
084 *       then it is strongly recommended that absolute routing not be used for
085 *       add operations outside the balancing point because that will cause the
086 *       entry to be added to only one backend set rather than to all backend
087 *       sets.</LI>
088 *   <LI>For an add request that uses a routing hint, exactly one target backend
089 *       set ID must be specified for the first guess, although any number of
090 *       fallback set IDs may be specified.  For entries immediately below the
091 *       balancing point, the routing hint will be used instead of a placement
092 *       algorithm in order to select which backend set should hold the entry
093 *       (and the fallback sets will not be used).  For entries more than one
094 *       level below the balancing point, the routing hint will be used in lieu
095 *       of the global index as an attempt to determine where the parent entry
096 *       exists, and the fallback sets may be used if the parent entry doesn't
097 *       exist in the first guess set.  For entries outside the balancing point,
098 *       if the entry-balancing request processor is configured to add entries
099 *       to one set and allow them to be replicated to other sets, then the
100 *       first guess hint will be used to select the set to which the entry will
101 *       be added (and the fallback sets will not be used).  An add operation
102 *       with a routing hint cannot be used to create multiple entries with the
103 *       same DN in different backend sets, nor can it cause an entry outside
104 *       the balancing point to exist in only one backend set.</LI>
105 *   <LI>For a simple bind request that uses absolute routing, exactly one
106 *       target backend set ID must be specified, and the request will be sent
107 *       only to that backend set.  If the bind fails in that set, even if the
108 *       failure is because the target entry does not exist in that backend set,
109 *       then the failure will be returned to the client rather than attempting
110 *       the operation in a different backend set.</LI>
111 *   <LI>For a simple bind request that uses a routing hint, exactly one target
112 *       backend set ID must be specified for the first guess, although any
113 *       number of fallback set IDs may be specified.  If the bind fails in the
114 *       first guess set, it may be re-attempted in the fallback sets.</LI>
115 *   <LI>For a compare request that uses absolute routing, exactly one target
116 *       backend set ID must be specified, and the request will be sent only to
117 *       that backend set.  If the compare fails in that set, even if the
118 *       failure is because the target entry does not exist in that set, then
119 *       the failure will be returned to the client rather than attempting the
120 *       operation in a different backend set.</LI>
121 *   <LI>For a compare request that uses a routing hint, exactly one target
122 *       backend set ID must be specified for the first guess, although any
123 *       number of fallback set IDs may be specified.  If the compare operation
124 *       fails in the first guess set in a way that suggests the target entry
125 *       does not exist in that backend set, then it will be re-attempted in the
126 *       fallback sets.</LI>
127 *   <LI>For a delete request that uses absolute routing, exactly one target
128 *       backend set ID must be specified, and the request will be sent only to
129 *       that backend set.  If the delete fails in that set, even if the failure
130 *       is because the target entry does not exist in that set, then the
131 *       failure will be returned to the client rather than attempting the
132 *       operation in a different backend set.
133 *       <BR>
134 *       <B>WARNING</B>:  If the entry-balancing request processor is configured
135 *       to broadcast delete operations outside the balancing point rather than
136 *       relying on those deletes to be replicated, then it is strongly
137 *       recommended that absolute routing not be used for delete operations
138 *       outside the balancing point because that will cause the entry to be
139 *       deleted in only one backend set and will remain in all other backend
140 *       sets.</LI>
141 *   <LI>For a delete request that uses a routing hint, exactly one target
142 *       backend set ID must be specified for the first guess, although any
143 *       number of fallback set IDs may be specified.  For entries below the
144 *       balancing point, the routing hint will be used in lieu of the global
145 *       index in order to determine which backend set contains the target
146 *       entry.  If the delete fails in the first guess set in a way that
147 *       suggests that the target entry does not exist in that backend set, then
148 *       it will be re-attempted in the fallback sets.
149 *       <BR>
150 *       For entries outside the balancing point, if the entry-balancing request
151 *       processor is configured to delete entries from only one backend set
152 *       and allow that delete to be replicated to all other sets, then the
153 *       routing hint may be used to select the set from which that entry will
154 *       be deleted.  A delete operation with a routing hint cannot be used to
155 *       cause an entry outside the balancing point to be removed from only one
156 *       backend set while leaving it in the remaining sets.</LI>
157 *   <LI>For an atomic multi-update extended request, only absolute routing is
158 *       supported, and the route to backend set request control must be
159 *       attached to the extended operation itself and not to any of the
160 *       requests contained inside the multi-update.  Exactly one backend set ID
161 *       must be specified, and the multi-update request will be sent only to
162 *       that backend set.</LI>
163 *   <LI>For a non-atomic multi-update extended request, the extended operation
164 *       must not include a route to backend set request control.  However, any
165 *       or all of the requests inside the multi-update request may include a
166 *       route to backend set request control, and in that case it will be
167 *       treated in the same way as for a request of the same type not
168 *       included in multi-update request (e.g., if a multi-update extended
169 *       operation includes an add request with a route to backend set request
170 *       control, then that route control will have the same effect as for the
171 *       same add request with the same control processed outside a multi-update
172 *       operation).</LI>
173 *   <LI>For an extended request that will be processed by a proxied extended
174 *       operation handler, the request may include a route to backend set
175 *       request control and that control will be used to select the target
176 *       backend sets instead of the proxied extended operation handler's
177 *       {@code selectBackendSets} method.</LI>
178 *   <LI>For a modify request that uses absolute routing, exactly one target
179 *       backend set ID must be specified, and the request will be sent only to
180 *       that backend set.  If the modify fails in that set, even if the failure
181 *       is because the target entry does not exist in that set, then the
182 *       failure will be returned to the client rather than attempting the
183 *       operation in a different backend set.
184 *       <BR>
185 *       <B>WARNING</B>:  When processing a modify operation against the
186 *       balancing point entry itself, the Directory Proxy Server will typically
187 *       send that modify request to all backend sets to ensure that it is
188 *       properly applied everywhere.  However, with an absolute routing
189 *       request, the modify operation will be sent only to one backend set,
190 *       which will cause the entry in that set to be out of sync with the entry
191 *       in all other sets.  It is therefore strongly recommended that absolute
192 *       routing not be used for modify operations that target the balancing
193 *       point entry.  Similarly, if the entry-balancing request processor is
194 *       configured to broadcast modify operations targeting entries outside the
195 *       balancing point to all backend sets rather than having those modify
196 *       operations replicated to the other backend sets, it is strongly
197 *       recommended that absolute routing not be used for those operations
198 *       because the request will be sent to only one set, causing the entry in
199 *       that set to be out of sync with the corresponding entry in other
200 *       backend sets.</LI>
201 *   <LI>For a modify request that uses a routing hint, exactly one target
202 *       backend set ID must be specified for the first guess, although any
203 *       number of fallback set IDs may be specified.  For entries below the
204 *       balancing point, the routing hint will be used in lieu of the global
205 *       index in order to determine which backend set contains the target
206 *       entry.  If the modify attempt fails in the first guess set in a way
207 *       that suggests the target entry does not exist in that backend set, then
208 *       it will be re-attempted in the fallback sets.
209 *       <BR>
210 *       For modify operations that target the balancing point entry itself, the
211 *       entry-balancing request processor will send the request to all backend
212 *       sets, and the routing hint will not be used.  Similarly, for entries
213 *       outside the balancing point, if the entry-balancing request processor
214 *       is configured to modify entries in only one backend set and allow that
215 *       modify operation to be replicated to all other sets, then the routing
216 *       hint may be used to select the set in which that entry will be
217 *       modified.  A modify operation with a routing hint cannot be used to
218 *       cause an entry at or outside the balancing point to be updated in only
219 *       one backend set, leaving it out of sync with the corresponding entry in
220 *       the remaining sets.</LI>
221 *   <LI>For a modify DN request that uses absolute routing, exactly one target
222 *       backend set ID must be specified, and the request wil be sent only to
223 *       that backend set.  If the modify DN operation fails in that set, even
224 *       if the failure is because the target entry does not exist in that set,
225 *       then the failure will be returned to the client rather than attempting
226 *       the operation in a different backend set.
227 *       <BR>
228 *       <B>WARNING</B>:  Processing a modify DN operation with absolute routing
229 *       bypasses the check to ensure that the new DN for the target entry does
230 *       not conflict with the DN for an entry that exists in any other backend
231 *       set.  As a result, you are strongly discouraged from using absolute
232 *       routing for any modify DN operation that would cause the new DN for the
233 *       entry to be exactly one level below the balancing point.  Further, for
234 *       entries that exist outside the balancing point, if the entry-balancing
235 *       request processor is configured to broadcast modify DN operations
236 *       rather than expecting them to be replicated to the other backend sets,
237 *       a modify DN operation with absolute routing would cause the change to
238 *       be applied only in one backend set, leaving it out of sync with the
239 *       other sets.</LI>
240 *   <LI>For a modify DN request that uses a routing hint, exactly one target
241 *       backend set ID must be specified for the first guess, although any
242 *       number of fallback set IDs may be specified.  For entries below the
243 *       balancing point, the routing hint will be used in lieu of the global
244 *       index in order to determine which backend set contains the target
245 *       entry.  If the modify attempt fails in the first guess set in a way
246 *       that suggests the target entry does not exist in that backend set, then
247 *       it will be re-attempted in the fallback sets.
248 *       <BR>
249 *       For entries outside the balancing point, if the entry-balancing request
250 *       processor is configured to process modify DN operations in one backend
251 *       set and allow them to be replicated to other backend sets, then the
252 *       routing hint will be used to select which backend set should receive
253 *       the modify DN request.  A modify DN operation with a routing hint
254 *       cannot be used to create a conflict in which the same DN exists in
255 *       multiple backend sets, or a case in which a modify DN operation outside
256 *       the balancing point leaves one backend set out of sync with the other
257 *       sets.</LI>
258 *   <LI>For a search request that uses absolute routing, there may be multiple
259 *       target backend set IDs only if the scope of the search may include
260 *       data from multiple backend sets (i.e., the base DN is at or above the
261 *       balancing point, and the scope include entries at least one level below
262 *       the balancing point entry).  If the base and scope of the search allow
263 *       it to match only entries at or above the balancing point or only
264 *       entries within one backend set, then the absolute routing request must
265 *       target exactly one backend set.</LI>
266 *   <LI>For a search request that uses a routing hint, exactly one target
267 *       backend set ID must be specified for the first guess, although any
268 *       number of fallback set IDs may be specified.  The routing hint will
269 *       only be used for cases in which the entire scope of the search is
270 *       contained entirely within a backend set (i.e., the entire scope is
271 *       at or above the balancing point, or the entire scope is at least one
272 *       level below the balancing point).</LI>
273 * </UL>
274 * <BR><BR>
275 * The OID for a route to backend set request control is
276 * "1.3.6.1.4.1.30221.2.5.35", and the criticality may be either {@code true} or
277 * {@code false}.  It must have a value with the following encoding:
278 * <PRE>
279 *   RouteToBackendSetRequest ::= SEQUENCE {
280 *        entryBalancingRequestProcessorID     OCTET STRING,
281 *        backendSets                          CHOICE {
282 *             absoluteRoutingRequest     [0] SET OF OCTET STRING,
283 *             routingHint                [1] SEQUENCE {
284 *                  firstGuessSetIDs     SET OF OCTET STRING,
285 *                  fallbackSetIDs       SET OF OCTET STRING OPTIONAL }
286 *             ... }
287 *        ... }
288 * </PRE>
289 * The use of the route to backend set request control will also cause the
290 * server to behave as if the get backend set ID request control had been
291 * included, so that the get backend set ID response control may be included in
292 * operation result and search result entry messages as appropriate.
293 */
294@NotMutable()
295@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
296public final class RouteToBackendSetRequestControl
297       extends Control
298{
299  /**
300   * The OID (1.3.6.1.4.1.30221.2.5.35) for the route to server request control.
301   */
302  public static final String ROUTE_TO_BACKEND_SET_REQUEST_OID =
303       "1.3.6.1.4.1.30221.2.5.35";
304
305
306
307  /**
308   * The serial version UID for this serializable class.
309   */
310  private static final long serialVersionUID = -2486448910813783450L;
311
312
313
314  // The routing type for the request.
315  private final RouteToBackendSetRoutingType routingType;
316
317  // The backend set IDs for an absolute routing request.
318  private final Set<String> absoluteBackendSetIDs;
319
320  // The backend set IDs for the fallback sets of a routing hint.
321  private final Set<String> routingHintFallbackSetIDs;
322
323  // The backend set IDs for the first guess of a routing hint.
324  private final Set<String> routingHintFirstGuessSetIDs;
325
326  // The identifier for the entry-balancing request processor with which the
327  // backend set IDs are associated.
328  private final String entryBalancingRequestProcessorID;
329
330
331
332  /**
333   * Creates a new route to backend set request control with the provided
334   * information.
335   *
336   * @param  isCritical                        Indicates whether this control
337   *                                           should be critical.
338   * @param  encodedValue                      The encoded value for this
339   *                                           control.  It must not be
340   *                                           {@code null}.
341   * @param  entryBalancingRequestProcessorID  The identifier for the
342   *                                           entry-balancing request processor
343   *                                           with which the backend set IDs
344   *                                           are associated.  It must not be
345   *                                           {@code null}.
346   * @param  routingType                       The routing type for this
347   *                                           request.  It must not be
348   *                                           {@code null}.
349   * @param  absoluteBackendSetIDs             The collection of backend sets to
350   *                                           which the request should be sent
351   *                                           for an absolute routing request.
352   *                                           It must be non-{@code null} and
353   *                                           non-empty for an absolute routing
354   *                                           request, and must be {@code null}
355   *                                           for a routing hint.
356   * @param  routingHintFirstGuessSetIDs       The collection of backend sets
357   *                                           that should be used as the first
358   *                                           guess for a routing hint request.
359   *                                           It must be {@code null} for an
360   *                                           absolute routing request, and
361   *                                           must be non-{@code null} and
362   *                                           non-empty for a routing hint.
363   * @param  routingHintFallbackSetIDs         The collection of fallback
364   *                                           backend sets that should be used
365   *                                           for a routing hint request if the
366   *                                           first guess was unsuccessful.  It
367   *                                           must be {@code null} for an
368   *                                           absolute routing request, and may
369   *                                           be {@code null} for a routing
370   *                                           hint if the fallback sets should
371   *                                           be all backend sets for the
372   *                                           entry-balancing request processor
373   *                                           that were not included in the
374   *                                           first guess.  If it is
375   *                                           non-{@code null}, then it must
376   *                                           also be non-empty.
377   */
378  private RouteToBackendSetRequestControl(final boolean isCritical,
379               final ASN1OctetString encodedValue,
380               final String entryBalancingRequestProcessorID,
381               final RouteToBackendSetRoutingType routingType,
382               final Collection<String> absoluteBackendSetIDs,
383               final Collection<String> routingHintFirstGuessSetIDs,
384               final Collection<String> routingHintFallbackSetIDs)
385  {
386    super(ROUTE_TO_BACKEND_SET_REQUEST_OID, isCritical, encodedValue);
387
388    this.entryBalancingRequestProcessorID = entryBalancingRequestProcessorID;
389    this.routingType = routingType;
390
391    if (absoluteBackendSetIDs == null)
392    {
393      this.absoluteBackendSetIDs = null;
394    }
395    else
396    {
397      this.absoluteBackendSetIDs = Collections.unmodifiableSet(
398           new LinkedHashSet<String>(absoluteBackendSetIDs));
399    }
400
401    if (routingHintFirstGuessSetIDs == null)
402    {
403      this.routingHintFirstGuessSetIDs = null;
404    }
405    else
406    {
407      this.routingHintFirstGuessSetIDs = Collections.unmodifiableSet(
408           new LinkedHashSet<String>(routingHintFirstGuessSetIDs));
409    }
410
411    if (routingHintFallbackSetIDs == null)
412    {
413      this.routingHintFallbackSetIDs = null;
414    }
415    else
416    {
417      this.routingHintFallbackSetIDs = Collections.unmodifiableSet(
418           new LinkedHashSet<String>(routingHintFallbackSetIDs));
419    }
420  }
421
422
423
424  /**
425   * Creates a new route to backend set request control that is decoded from the
426   * provided generic control.
427   *
428   * @param  control  The control to decode as a route to backend set request
429   *                  control.
430   *
431   * @throws  LDAPException  If the provided control cannot be decoded as a
432   *                         route to backend set request control.
433   */
434  public RouteToBackendSetRequestControl(final Control control)
435         throws LDAPException
436  {
437    super(control);
438
439    final ASN1OctetString value = control.getValue();
440    if (value == null)
441    {
442      throw new LDAPException(ResultCode.DECODING_ERROR,
443           ERR_ROUTE_TO_BACKEND_SET_REQUEST_MISSING_VALUE.get());
444    }
445
446    try
447    {
448      final ASN1Element[] elements =
449           ASN1Sequence.decodeAsSequence(value.getValue()).elements();
450      entryBalancingRequestProcessorID =
451           ASN1OctetString.decodeAsOctetString(elements[0]).stringValue();
452
453      routingType = RouteToBackendSetRoutingType.valueOf(elements[1].getType());
454      if (routingType == null)
455      {
456        throw new LDAPException(ResultCode.DECODING_ERROR,
457             ERR_ROUTE_TO_BACKEND_SET_REQUEST_UNKNOWN_ROUTING_TYPE.get(
458                  StaticUtils.toHex(elements[1].getType())));
459      }
460
461      if (routingType == RouteToBackendSetRoutingType.ABSOLUTE_ROUTING)
462      {
463        final ASN1Element[] arElements =
464             ASN1Set.decodeAsSet(elements[1]).elements();
465        final LinkedHashSet<String> arSet =
466             new LinkedHashSet<String>(arElements.length);
467        for (final ASN1Element e : arElements)
468        {
469          arSet.add(ASN1OctetString.decodeAsOctetString(e).stringValue());
470        }
471        absoluteBackendSetIDs = Collections.unmodifiableSet(arSet);
472        if (absoluteBackendSetIDs.isEmpty())
473        {
474          throw new LDAPException(ResultCode.DECODING_ERROR,
475               ERR_ROUTE_TO_BACKEND_SET_REQUEST_ABSOLUTE_SET_EMPTY.get());
476        }
477
478        routingHintFirstGuessSetIDs = null;
479        routingHintFallbackSetIDs = null;
480      }
481      else
482      {
483        final ASN1Element[] hintElements =
484             ASN1Sequence.decodeAsSequence(elements[1]).elements();
485
486        final ASN1Element[] firstGuessElements =
487             ASN1Set.decodeAsSet(hintElements[0]).elements();
488        final LinkedHashSet<String> firstGuessSet =
489             new LinkedHashSet<String>(firstGuessElements.length);
490        for (final ASN1Element e : firstGuessElements)
491        {
492          firstGuessSet.add(
493               ASN1OctetString.decodeAsOctetString(e).stringValue());
494        }
495        routingHintFirstGuessSetIDs =
496             Collections.unmodifiableSet(firstGuessSet);
497        if (routingHintFirstGuessSetIDs.isEmpty())
498        {
499          throw new LDAPException(ResultCode.DECODING_ERROR,
500               ERR_ROUTE_TO_BACKEND_SET_REQUEST_HINT_FIRST_SET_EMPTY.get());
501        }
502
503        if (hintElements.length == 1)
504        {
505          routingHintFallbackSetIDs = null;
506        }
507        else
508        {
509          final ASN1Element[] fallbackElements =
510               ASN1Set.decodeAsSet(hintElements[1]).elements();
511          final LinkedHashSet<String> fallbackSet =
512               new LinkedHashSet<String>(fallbackElements.length);
513          for (final ASN1Element e : fallbackElements)
514          {
515            fallbackSet.add(
516                 ASN1OctetString.decodeAsOctetString(e).stringValue());
517          }
518          routingHintFallbackSetIDs = Collections.unmodifiableSet(fallbackSet);
519          if (routingHintFallbackSetIDs.isEmpty())
520          {
521            throw new LDAPException(ResultCode.DECODING_ERROR,
522                 ERR_ROUTE_TO_BACKEND_SET_REQUEST_HINT_FALLBACK_SET_EMPTY.
523                      get());
524          }
525        }
526
527        absoluteBackendSetIDs = null;
528      }
529    }
530    catch (final LDAPException le)
531    {
532      Debug.debugException(le);
533      throw le;
534    }
535    catch (final Exception e)
536    {
537      Debug.debugException(e);
538      throw new LDAPException(ResultCode.DECODING_ERROR,
539           ERR_ROUTE_TO_BACKEND_SET_REQUEST_CANNOT_DECODE.get(
540                StaticUtils.getExceptionMessage(e)),
541           e);
542    }
543  }
544
545
546
547  /**
548   * Creates a new route to backend set request control that may be used for
549   * absolute routing to the specified backend set.
550   *
551   * @param  isCritical                        Indicates whether the control
552   *                                           should be marked critical.
553   * @param  entryBalancingRequestProcessorID  The identifier for the
554   *                                           entry-balancing request processor
555   *                                           with which the backend set ID
556   *                                           is associated.  It must not be
557   *                                           {@code null}.
558   * @param  backendSetID                      The backend set ID for the
559   *                                           backend set to which the request
560   *                                           should be forwarded.  It must not
561   *                                           be {@code null}.
562   *
563   * @return  The route to backend set request control created from the
564   *          provided information.
565   */
566  public static RouteToBackendSetRequestControl createAbsoluteRoutingRequest(
567                     final boolean isCritical,
568                     final String entryBalancingRequestProcessorID,
569                     final String backendSetID)
570  {
571    return createAbsoluteRoutingRequest(isCritical,
572         entryBalancingRequestProcessorID, Arrays.asList(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<ASN1Element>(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, Arrays.asList(firstGuessSetID),
662         fallbackSetIDs);
663  }
664
665
666
667  /**
668   * Creates a new route to backend set request control that may be used to
669   * provide a hint as to the backend set(s) to which the operation should be
670   * forwarded, and an optional specification of fallback sets.
671   *
672   * @param  isCritical                        Indicates whether the control
673   *                                           should be marked critical.
674   * @param  entryBalancingRequestProcessorID  The identifier for the
675   *                                           entry-balancing request processor
676   *                                           with which the backend set IDs
677   *                                           are associated.  It must not be
678   *                                           {@code null}.
679   * @param  firstGuessSetIDs                  The backend set ID(s) for the
680   *                                           backend set(s) to try first.  It
681   *                                           must not be {@code null} or
682   *                                           empty.
683   * @param  fallbackSetIDs                    The backend set ID(s) for the
684   *                                           backend set(s) to use if none of
685   *                                           the servers in the first guess
686   *                                           set returns a success result.
687   *                                           If this is {@code null}, then the
688   *                                           server will use a default
689   *                                           fallback set of all backend sets
690   *                                           not included in the first guess.
691   *                                           If this is not {@code null}, then
692   *                                           it must also be non-empty.
693   *
694   * @return  The route to backend set request control created from the
695   *          provided information.
696   */
697  public static RouteToBackendSetRequestControl createRoutingHintRequest(
698                     final boolean isCritical,
699                     final String entryBalancingRequestProcessorID,
700                     final Collection<String> firstGuessSetIDs,
701                     final Collection<String> fallbackSetIDs)
702  {
703    Validator.ensureNotNull(firstGuessSetIDs);
704    Validator.ensureFalse(firstGuessSetIDs.isEmpty());
705
706    if (fallbackSetIDs != null)
707    {
708      Validator.ensureFalse(fallbackSetIDs.isEmpty());
709    }
710
711    final ArrayList<ASN1Element> backendSetsElements =
712         new ArrayList<ASN1Element>(2);
713    final ArrayList<ASN1Element> firstGuessElements =
714         new ArrayList<ASN1Element>(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<ASN1Element>(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}