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