001/*
002 * Copyright 2007-2020 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2007-2020 Ping Identity Corporation
007 *
008 * Licensed under the Apache License, Version 2.0 (the "License");
009 * you may not use this file except in compliance with the License.
010 * You may obtain a copy of the License at
011 *
012 *    http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing, software
015 * distributed under the License is distributed on an "AS IS" BASIS,
016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017 * See the License for the specific language governing permissions and
018 * limitations under the License.
019 */
020/*
021 * Copyright (C) 2008-2020 Ping Identity Corporation
022 *
023 * This program is free software; you can redistribute it and/or modify
024 * it under the terms of the GNU General Public License (GPLv2 only)
025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
026 * as published by the Free Software Foundation.
027 *
028 * This program is distributed in the hope that it will be useful,
029 * but WITHOUT ANY WARRANTY; without even the implied warranty of
030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
031 * GNU General Public License for more details.
032 *
033 * You should have received a copy of the GNU General Public License
034 * along with this program; if not, see <http://www.gnu.org/licenses>.
035 */
036package com.unboundid.ldap.sdk.controls;
037
038
039
040import com.unboundid.asn1.ASN1Element;
041import com.unboundid.asn1.ASN1Enumerated;
042import com.unboundid.asn1.ASN1Exception;
043import com.unboundid.asn1.ASN1OctetString;
044import com.unboundid.asn1.ASN1Sequence;
045import com.unboundid.ldap.sdk.Control;
046import com.unboundid.ldap.sdk.DecodeableControl;
047import com.unboundid.ldap.sdk.LDAPException;
048import com.unboundid.ldap.sdk.ResultCode;
049import com.unboundid.ldap.sdk.SearchResult;
050import com.unboundid.util.Debug;
051import com.unboundid.util.NotMutable;
052import com.unboundid.util.ThreadSafety;
053import com.unboundid.util.ThreadSafetyLevel;
054
055import static com.unboundid.ldap.sdk.controls.ControlMessages.*;
056
057
058
059/**
060 * This class provides an implementation of the server-side sort response
061 * control, as defined in
062 * <A HREF="http://www.ietf.org/rfc/rfc2891.txt">RFC 2891</A>.  It may be used
063 * to provide information about the result of server-side sort processing.  If
064 * the corresponding search request included the
065 * {@link ServerSideSortRequestControl}, then the search result done message
066 * may include this response control to provide information about the state of
067 * the sorting.
068 */
069@NotMutable()
070@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
071public final class ServerSideSortResponseControl
072       extends Control
073       implements DecodeableControl
074{
075  /**
076   * The OID (1.2.840.113556.1.4.474) for the server-side sort response control.
077   */
078  public static final String SERVER_SIDE_SORT_RESPONSE_OID =
079       "1.2.840.113556.1.4.474";
080
081
082
083  /**
084   * The BER type to use for the element that holds the attribute type.
085   */
086  private static final byte TYPE_ATTRIBUTE_TYPE = (byte) 0x80;
087
088
089
090  /**
091   * The serial version UID for this serializable class.
092   */
093  private static final long serialVersionUID = -8707533262822875822L;
094
095
096
097  // The result code for this server-side sort response control.
098  private final ResultCode resultCode;
099
100  // The name of the attribute associated with this result, if available.
101  private final String attributeName;
102
103
104
105  /**
106   * Creates a new empty control instance that is intended to be used only for
107   * decoding controls via the {@code DecodeableControl} interface.
108   */
109  ServerSideSortResponseControl()
110  {
111    resultCode    = null;
112    attributeName = null;
113  }
114
115
116
117  /**
118   * Creates a new server-side sort response control with the provided
119   * information.
120   *
121   * @param  resultCode     The result code for this server-side sort response.
122   * @param  attributeName  The name of the attribute associated with this
123   *                        result.  It may be {@code null} if there is no
124   *                        associated attribute name.
125   */
126  public ServerSideSortResponseControl(final ResultCode resultCode,
127                                       final String attributeName)
128  {
129    this(resultCode, attributeName, false);
130  }
131
132
133
134  /**
135   * Creates a new server-side sort response control with the provided
136   * information.
137   *
138   * @param  resultCode     The result code for this server-side sort response.
139   * @param  attributeName  The name of the attribute associated with this
140   *                        result.  It may be {@code null} if there is no
141   *                        associated attribute name.
142   * @param  isCritical     Indicates whether this control should be marked
143   *                        critical.  Response controls should generally not be
144   *                        critical.
145   */
146  public ServerSideSortResponseControl(final ResultCode resultCode,
147                                       final String attributeName,
148                                       final boolean isCritical)
149  {
150    super(SERVER_SIDE_SORT_RESPONSE_OID, isCritical,
151          encodeValue(resultCode, attributeName));
152
153    this.resultCode    = resultCode;
154    this.attributeName = attributeName;
155  }
156
157
158
159  /**
160   * Creates a new server-side sort response control from the information
161   * contained in the provided control.
162   *
163   * @param  oid         The OID for the control.
164   * @param  isCritical  Indicates whether the control should be marked
165   *                     critical.
166   * @param  value       The encoded value for the control.  This may be
167   *                     {@code null} if no value was provided.
168   *
169   * @throws  LDAPException  If a problem occurs while attempting to decode the
170   *                         provided control as a server-side sort response
171   *                         control.
172   */
173  public ServerSideSortResponseControl(final String oid,
174                                       final boolean isCritical,
175                                       final ASN1OctetString value)
176         throws LDAPException
177  {
178    super(oid, isCritical, value);
179
180    if (value == null)
181    {
182      throw new LDAPException(ResultCode.DECODING_ERROR,
183                              ERR_SORT_RESPONSE_NO_VALUE.get());
184    }
185
186    final ASN1Sequence valueSequence;
187    try
188    {
189      final ASN1Element valueElement =
190           ASN1Element.decode(value.getValue());
191      valueSequence = ASN1Sequence.decodeAsSequence(valueElement);
192    }
193    catch (final ASN1Exception ae)
194    {
195      Debug.debugException(ae);
196      throw new LDAPException(ResultCode.DECODING_ERROR,
197                              ERR_SORT_RESPONSE_VALUE_NOT_SEQUENCE.get(ae), ae);
198    }
199
200    final ASN1Element[] valueElements = valueSequence.elements();
201    if ((valueElements.length < 1) || (valueElements.length > 2))
202    {
203      throw new LDAPException(ResultCode.DECODING_ERROR,
204                              ERR_SORT_RESPONSE_INVALID_ELEMENT_COUNT.get(
205                                   valueElements.length));
206    }
207
208    try
209    {
210      final int rc =
211           ASN1Enumerated.decodeAsEnumerated(valueElements[0]).intValue();
212      resultCode = ResultCode.valueOf(rc);
213    }
214    catch (final ASN1Exception ae)
215    {
216      Debug.debugException(ae);
217      throw new LDAPException(ResultCode.DECODING_ERROR,
218                              ERR_SORT_RESPONSE_FIRST_NOT_ENUM.get(ae), ae);
219    }
220
221    if (valueElements.length == 2)
222    {
223      attributeName =
224           ASN1OctetString.decodeAsOctetString(valueElements[1]).stringValue();
225    }
226    else
227    {
228      attributeName = null;
229    }
230  }
231
232
233
234  /**
235   * {@inheritDoc}
236   */
237  @Override()
238  public ServerSideSortResponseControl
239              decodeControl(final String oid, final boolean isCritical,
240                            final ASN1OctetString value)
241         throws LDAPException
242  {
243    return new ServerSideSortResponseControl(oid, isCritical, value);
244  }
245
246
247
248  /**
249   * Extracts a server-side sort response control from the provided result.
250   *
251   * @param  result  The result from which to retrieve the server-side sort
252   *                 response control.
253   *
254   * @return  The server-side sort response control contained in the provided
255   *          result, or {@code null} if the result did not contain a
256   *          server-side sort response control.
257   *
258   * @throws  LDAPException  If a problem is encountered while attempting to
259   *                         decode the server-side sort response control
260   *                         contained in the provided result.
261   */
262  public static ServerSideSortResponseControl get(final SearchResult result)
263         throws LDAPException
264  {
265    final Control c = result.getResponseControl(SERVER_SIDE_SORT_RESPONSE_OID);
266    if (c == null)
267    {
268      return null;
269    }
270
271    if (c instanceof ServerSideSortResponseControl)
272    {
273      return (ServerSideSortResponseControl) c;
274    }
275    else
276    {
277      return new ServerSideSortResponseControl(c.getOID(), c.isCritical(),
278           c.getValue());
279    }
280  }
281
282
283
284  /**
285   * Encodes the provided information into an octet string that can be used as
286   * the value for this control.
287   *
288   * @param  resultCode     The result code for this server-side sort response
289   *                        control.
290   * @param  attributeName  The attribute name to include in the control, or
291   *                        {@code null} if it should not be provided.
292   *
293   * @return  An ASN.1 octet string that can be used as the value for this
294   *          control.
295   */
296  private static ASN1OctetString encodeValue(final ResultCode resultCode,
297                                             final String attributeName)
298  {
299    final ASN1Element[] valueElements;
300    if (attributeName == null)
301    {
302      valueElements = new ASN1Element[]
303      {
304        new ASN1Enumerated(resultCode.intValue())
305      };
306    }
307    else
308    {
309      valueElements = new ASN1Element[]
310      {
311        new ASN1Enumerated(resultCode.intValue()),
312        new ASN1OctetString(TYPE_ATTRIBUTE_TYPE, attributeName)
313      };
314    }
315
316    return new ASN1OctetString(new ASN1Sequence(valueElements).encode());
317  }
318
319
320
321  /**
322   * Retrieves the result code for this server-side sort response control.
323   *
324   * @return  The result code for this server-side sort response control.
325   */
326  public ResultCode getResultCode()
327  {
328    return resultCode;
329  }
330
331
332
333  /**
334   * Retrieves the attribute name for this server-side sort response control, if
335   * available.
336   *
337   * @return  The attribute name for this server-side sort response control, or
338   *          {@code null} if none was provided.
339   */
340  public String getAttributeName()
341  {
342    return attributeName;
343  }
344
345
346
347  /**
348   * {@inheritDoc}
349   */
350  @Override()
351  public String getControlName()
352  {
353    return INFO_CONTROL_NAME_SORT_RESPONSE.get();
354  }
355
356
357
358  /**
359   * {@inheritDoc}
360   */
361  @Override()
362  public void toString(final StringBuilder buffer)
363  {
364    buffer.append("ServerSideSortResponseControl(resultCode=");
365    buffer.append(resultCode);
366
367    if (attributeName != null)
368    {
369      buffer.append(", attributeName='");
370      buffer.append(attributeName);
371      buffer.append('\'');
372    }
373
374    buffer.append(')');
375  }
376}