001/*
002 * Copyright 2014-2022 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2014-2022 Ping Identity Corporation
007 *
008 * Licensed under the Apache License, Version 2.0 (the "License");
009 * you may not use this file except in compliance with the License.
010 * You may obtain a copy of the License at
011 *
012 *    http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing, software
015 * distributed under the License is distributed on an "AS IS" BASIS,
016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017 * See the License for the specific language governing permissions and
018 * limitations under the License.
019 */
020/*
021 * Copyright (C) 2014-2022 Ping Identity Corporation
022 *
023 * This program is free software; you can redistribute it and/or modify
024 * it under the terms of the GNU General Public License (GPLv2 only)
025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
026 * as published by the Free Software Foundation.
027 *
028 * This program is distributed in the hope that it will be useful,
029 * but WITHOUT ANY WARRANTY; without even the implied warranty of
030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
031 * GNU General Public License for more details.
032 *
033 * You should have received a copy of the GNU General Public License
034 * along with this program; if not, see <http://www.gnu.org/licenses>.
035 */
036package com.unboundid.ldap.sdk.unboundidds.controls;
037
038
039
040import java.util.ArrayList;
041import java.util.Collection;
042import java.util.Collections;
043import java.util.Iterator;
044import java.util.LinkedHashSet;
045import java.util.List;
046import java.util.Set;
047
048import com.unboundid.asn1.ASN1Element;
049import com.unboundid.asn1.ASN1OctetString;
050import com.unboundid.asn1.ASN1Sequence;
051import com.unboundid.asn1.ASN1Set;
052import com.unboundid.ldap.sdk.Control;
053import com.unboundid.ldap.sdk.DecodeableControl;
054import com.unboundid.ldap.sdk.ExtendedResult;
055import com.unboundid.ldap.sdk.LDAPException;
056import com.unboundid.ldap.sdk.LDAPResult;
057import com.unboundid.ldap.sdk.ResultCode;
058import com.unboundid.ldap.sdk.SearchResultEntry;
059import com.unboundid.util.Debug;
060import com.unboundid.util.NotMutable;
061import com.unboundid.util.NotNull;
062import com.unboundid.util.Nullable;
063import com.unboundid.util.StaticUtils;
064import com.unboundid.util.ThreadSafety;
065import com.unboundid.util.ThreadSafetyLevel;
066import com.unboundid.util.Validator;
067
068import static com.unboundid.ldap.sdk.unboundidds.controls.ControlMessages.*;
069
070
071
072/**
073 * This class provides a response control that may be used to provide the
074 * backend set ID(s) for any relevant backend sets accessed during the course
075 * of processing an operation.  It may be returned in response to a request
076 * containing either the get backend set ID request control or the route to
077 * backend set request control.  For add, simple bind, compare, delete,
078 * modify, and modify DN operations, the LDAP result message for the operation
079 * may contain zero or one get backend set ID response control.  For extended
080 * operations, the extended result message may contain zero, one, or multiple
081 * get backend set ID response controls.  For search operations, each search
082 * result entry may contain zero or one get backend set ID response control,
083 * while the search result done message will not contain any such control.  See
084 * the {@link GetBackendSetIDRequestControl} class documentation for a more
085 * complete description of the usage for these controls.
086 * <BR>
087 * <BLOCKQUOTE>
088 *   <B>NOTE:</B>  This class, and other classes within the
089 *   {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only
090 *   supported for use against Ping Identity, UnboundID, and
091 *   Nokia/Alcatel-Lucent 8661 server products.  These classes provide support
092 *   for proprietary functionality or for external specifications that are not
093 *   considered stable or mature enough to be guaranteed to work in an
094 *   interoperable way with other types of LDAP servers.
095 * </BLOCKQUOTE>
096 * <BR>
097 * The get backend set ID response control has an OID of
098 * "1.3.6.1.4.1.30221.2.5.34", a criticality of false, and a value with the
099 * following encoding:
100 * <PRE>
101 *   GET_BACKEND_SET_ID_RESPONSE_VALUE ::= SEQUENCE {
102 *     entryBalancingRequestProcessorID     OCTET STRING,
103 *     backendSetIDs                        SET SIZE (1..MAX) OF OCTET STRING,
104 *     ... }
105 * </PRE>
106 *
107 * @see  GetBackendSetIDRequestControl
108 * @see  RouteToBackendSetRequestControl
109 */
110@NotMutable()
111@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
112public final class GetBackendSetIDResponseControl
113       extends Control
114       implements DecodeableControl
115{
116  /**
117   * The OID (1.3.6.1.4.1.30221.2.5.34) for the get backend set ID response
118   * control.
119   */
120  @NotNull public static final  String GET_BACKEND_SET_ID_RESPONSE_OID =
121       "1.3.6.1.4.1.30221.2.5.34";
122
123
124
125  /**
126   * The serial version UID for this serializable class.
127   */
128  private static final long serialVersionUID = 117359364981309726L;
129
130
131
132  // The backend set IDs for backend sets used during processing.
133  @NotNull private final Set<String> backendSetIDs;
134
135  // The identifier for the entry-balancing request processor with which the
136  // backend set IDs are associated.
137  @NotNull private final String entryBalancingRequestProcessorID;
138
139
140
141  /**
142   * Creates a new empty control instance that is intended to be used only for
143   * decoding controls via the {@code DecodeableControl} interface.
144   */
145  GetBackendSetIDResponseControl()
146  {
147    entryBalancingRequestProcessorID = null;
148    backendSetIDs = null;
149  }
150
151
152
153  /**
154   * Creates a new get backend set ID response control with the provided
155   * information.
156   *
157   * @param  entryBalancingRequestProcessorID  The identifier for the
158   *                                           entry-balancing request processor
159   *                                           with which the backend set IDs
160   *                                           are associated.  It must not be
161   *                                           {@code null}.
162   * @param  backendSetID                      The backend set ID for the
163   *                                           backend set used during
164   *                                           processing.  It must not be
165   *                                           {@code null}.
166   */
167  public GetBackendSetIDResponseControl(
168              @NotNull final String entryBalancingRequestProcessorID,
169              @NotNull final String backendSetID)
170  {
171    this(entryBalancingRequestProcessorID,
172         Collections.singletonList(backendSetID));
173  }
174
175
176
177  /**
178   * Creates a new get backend set ID response control with the provided
179   * information.
180   *
181   * @param  entryBalancingRequestProcessorID  The identifier for the
182   *                                           entry-balancing request processor
183   *                                           with which the backend set IDs
184   *                                           are associated.  It must not be
185   *                                           {@code null}.
186   * @param  backendSetIDs                     The backend set IDs for backend
187   *                                           sets used during processing.  It
188   *                                           must not be {@code null} or
189   *                                           empty.
190   */
191  public GetBackendSetIDResponseControl(
192              @NotNull final String entryBalancingRequestProcessorID,
193              @NotNull final Collection<String> backendSetIDs)
194  {
195    super(GET_BACKEND_SET_ID_RESPONSE_OID, false,
196         encodeValue(entryBalancingRequestProcessorID, backendSetIDs));
197
198    this.entryBalancingRequestProcessorID = entryBalancingRequestProcessorID;
199    this.backendSetIDs =
200         Collections.unmodifiableSet(new LinkedHashSet<>(backendSetIDs));
201  }
202
203
204
205  /**
206   * Creates a new get backend set ID response control decoded from the given
207   * generic control contents.
208   *
209   * @param  oid         The OID for the control.
210   * @param  isCritical  Indicates whether this control should be marked
211   *                     critical.
212   * @param  value       The encoded value for the control.
213   *
214   * @throws LDAPException  If a problem occurs while attempting to decode the
215   *                        generic control as a get backend set ID response
216   *                        control.
217   */
218  public GetBackendSetIDResponseControl(@NotNull final String oid,
219                                        final boolean isCritical,
220                                        @Nullable final ASN1OctetString value)
221       throws LDAPException
222  {
223    super(oid, isCritical, value);
224
225    if (value == null)
226    {
227      throw new LDAPException(ResultCode.DECODING_ERROR,
228           ERR_GET_BACKEND_SET_ID_RESPONSE_MISSING_VALUE.get());
229    }
230
231    try
232    {
233      final ASN1Element[] elements =
234           ASN1Sequence.decodeAsSequence(value.getValue()).elements();
235      entryBalancingRequestProcessorID =
236           ASN1OctetString.decodeAsOctetString(elements[0]).stringValue();
237
238      final ASN1Element[] backendSetIDElements =
239           ASN1Set.decodeAsSet(elements[1]).elements();
240      final LinkedHashSet<String> setIDs = new LinkedHashSet<>(
241           StaticUtils.computeMapCapacity(backendSetIDElements.length));
242      for (final ASN1Element e : backendSetIDElements)
243      {
244        setIDs.add(ASN1OctetString.decodeAsOctetString(e).stringValue());
245      }
246      backendSetIDs = Collections.unmodifiableSet(setIDs);
247    }
248    catch (final Exception e)
249    {
250      Debug.debugException(e);
251      throw new LDAPException(ResultCode.DECODING_ERROR,
252           ERR_GET_BACKEND_SET_ID_RESPONSE_CANNOT_DECODE.get(
253                StaticUtils.getExceptionMessage(e)),
254           e);
255    }
256  }
257
258
259
260  /**
261   * Encodes the provided information into an octet string suitable for use as
262   * the value of this control.
263   *
264   * @param  entryBalancingRequestProcessorID  The identifier for the
265   *                                           entry-balancing request processor
266   *                                           with which the backend set IDs
267   *                                           are associated.  It must not be
268   *                                           {@code null}.
269   * @param  backendSetIDs                     The backend set IDs for backend
270   *                                           sets used during processing.  It
271   *                                           must not be {@code null} or
272   *                                           empty.
273   *
274   * @return  The encoded representation of the control value.
275   */
276  @NotNull()
277  private static ASN1OctetString encodeValue(
278                      @NotNull final String entryBalancingRequestProcessorID,
279                      @NotNull final Collection<String> backendSetIDs)
280  {
281    Validator.ensureNotNull(entryBalancingRequestProcessorID);
282    Validator.ensureNotNull(backendSetIDs);
283    Validator.ensureFalse(backendSetIDs.isEmpty());
284
285    final ArrayList<ASN1Element> backendSetIDElements =
286         new ArrayList<>(backendSetIDs.size());
287    for (final String s : backendSetIDs)
288    {
289      backendSetIDElements.add(new ASN1OctetString(s));
290    }
291
292    final ASN1Sequence valueSequence = new ASN1Sequence(
293         new ASN1OctetString(entryBalancingRequestProcessorID),
294         new ASN1Set(backendSetIDElements));
295    return new ASN1OctetString(valueSequence.encode());
296  }
297
298
299
300  /**
301   * {@inheritDoc}
302   */
303  @Override()
304  @NotNull()
305  public GetBackendSetIDResponseControl decodeControl(@NotNull final String oid,
306              final boolean isCritical,
307              @Nullable final ASN1OctetString value)
308         throws LDAPException
309  {
310    return new GetBackendSetIDResponseControl(oid, isCritical, value);
311  }
312
313
314
315  /**
316   * Retrieves the identifier for the entry-balancing request processor with
317   * which the backend sets IDs are associated.
318   *
319   * @return  The identifier for the entry-balancing request processor with
320   *          which the backend set IDs are associated.
321   */
322  @NotNull()
323  public String getEntryBalancingRequestProcessorID()
324  {
325    return entryBalancingRequestProcessorID;
326  }
327
328
329
330  /**
331   * Retrieves the backend set IDs for the backend sets used during processing.
332   *
333   * @return  The backend set IDs for the backend sets used during processing.
334   */
335  @NotNull()
336  public Set<String> getBackendSetIDs()
337  {
338    return backendSetIDs;
339  }
340
341
342
343  /**
344   * Extracts a get backend set ID response control from the provided result.
345   *
346   * @param  result  The result from which to retrieve the get backend set ID
347   *                 response control.
348   *
349   * @return  The get backend set ID response control contained in the provided
350   *          result, or {@code null} if the result did not contain a get
351   *          backend set ID response control.
352   *
353   * @throws  LDAPException  If a problem is encountered while attempting to
354   *                         decode the get backend set ID response control
355   *                         contained in the provided result.
356   */
357  @Nullable()
358  public static GetBackendSetIDResponseControl get(
359                     @NotNull final LDAPResult result)
360         throws LDAPException
361  {
362    final Control c =
363         result.getResponseControl(GET_BACKEND_SET_ID_RESPONSE_OID);
364    if (c == null)
365    {
366      return null;
367    }
368
369    if (c instanceof GetBackendSetIDResponseControl)
370    {
371      return (GetBackendSetIDResponseControl) c;
372    }
373    else
374    {
375      return new GetBackendSetIDResponseControl(c.getOID(), c.isCritical(),
376           c.getValue());
377    }
378  }
379
380
381
382  /**
383   * Extracts a get backend set ID response control from the provided search
384   * result entry.
385   *
386   * @param  entry  The entry from which to retrieve the get backend set ID
387   *                response control.
388   *
389   * @return  The get backend set ID response control contained in the provided
390   *          entry, or {@code null} if the entry did not contain a get backend
391   *          set ID response control.
392   *
393   * @throws  LDAPException  If a problem is encountered while attempting to
394   *                         decode the get backend set ID response control
395   *                         contained in the provided result.
396   */
397  @Nullable()
398  public static GetBackendSetIDResponseControl get(
399                     @NotNull final SearchResultEntry entry)
400         throws LDAPException
401  {
402    final Control c = entry.getControl(GET_BACKEND_SET_ID_RESPONSE_OID);
403    if (c == null)
404    {
405      return null;
406    }
407
408    if (c instanceof GetBackendSetIDResponseControl)
409    {
410      return (GetBackendSetIDResponseControl) c;
411    }
412    else
413    {
414      return new GetBackendSetIDResponseControl(c.getOID(), c.isCritical(),
415           c.getValue());
416    }
417  }
418
419
420
421  /**
422   * Extracts any get backend set ID response controls from the provided
423   * extended result.
424   *
425   * @param  result  The extended result from which to retrieve the get backend
426   *                 set ID response control(s).
427   *
428   * @return  A list of get backend set ID response controls contained in the
429   *          provided extended result, or an empty list if the result did not
430   *          contain a get any backend set ID response controls.
431   *
432   * @throws  LDAPException  If a problem is encountered while attempting to
433   *                         decode the any backend set ID response control
434   *                         contained in the provided result.
435   */
436  @NotNull()
437  public static List<GetBackendSetIDResponseControl> get(
438                     @NotNull final ExtendedResult result)
439         throws LDAPException
440  {
441    final Control[] controls = result.getResponseControls();
442    if (controls.length == 0)
443    {
444      return Collections.emptyList();
445    }
446
447    final ArrayList<GetBackendSetIDResponseControl> decodedControls =
448         new ArrayList<>(controls.length);
449    for (final Control c : controls)
450    {
451      if (c instanceof GetBackendSetIDResponseControl)
452      {
453        decodedControls.add((GetBackendSetIDResponseControl) c);
454      }
455      else if (c.getOID().equals(GET_BACKEND_SET_ID_RESPONSE_OID))
456      {
457        decodedControls.add(new GetBackendSetIDResponseControl(c.getOID(),
458             c.isCritical(), c.getValue()));
459      }
460    }
461
462    return Collections.unmodifiableList(decodedControls);
463  }
464
465
466
467  /**
468   * {@inheritDoc}
469   */
470  @Override()
471  @NotNull()
472  public String getControlName()
473  {
474    return INFO_CONTROL_NAME_GET_BACKEND_SET_ID_RESPONSE.get();
475  }
476
477
478
479  /**
480   * {@inheritDoc}
481   */
482  @Override()
483  public void toString(@NotNull final StringBuilder buffer)
484  {
485    buffer.append("GetBackendSetIDResponseControl(" +
486         "entryBalancingRequestProcessorID='");
487    buffer.append(entryBalancingRequestProcessorID);
488    buffer.append("', backendSetIDs={");
489
490    final Iterator<String> iterator = backendSetIDs.iterator();
491    while (iterator.hasNext())
492    {
493      buffer.append('\'');
494      buffer.append(iterator.next());
495      buffer.append('\'');
496
497      if (iterator.hasNext())
498      {
499        buffer.append(", ");
500      }
501    }
502
503    buffer.append("})");
504  }
505}