001/*
002 * Copyright 2007-2017 UnboundID Corp.
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2008-2017 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 */
021package com.unboundid.ldap.sdk.controls;
022
023
024
025import java.util.List;
026
027import com.unboundid.asn1.ASN1Element;
028import com.unboundid.asn1.ASN1OctetString;
029import com.unboundid.asn1.ASN1Sequence;
030import com.unboundid.ldap.sdk.Control;
031import com.unboundid.ldap.sdk.LDAPException;
032import com.unboundid.ldap.sdk.ResultCode;
033import com.unboundid.util.NotMutable;
034import com.unboundid.util.ThreadSafety;
035import com.unboundid.util.ThreadSafetyLevel;
036
037import static com.unboundid.ldap.sdk.controls.ControlMessages.*;
038import static com.unboundid.util.Debug.*;
039import static com.unboundid.util.Validator.*;
040
041
042
043/**
044 * This class provides an implementation of the server-side sort request
045 * control, as defined in
046 * <A HREF="http://www.ietf.org/rfc/rfc2891.txt">RFC 2891</A>.  It may be
047 * included in a search request to indicate that the server should sort the
048 * results before returning them to the client.
049 * <BR><BR>
050 * The order in which the entries are to be sorted is specified by one or more
051 * {@link SortKey} values.  Each sort key includes an attribute name and a flag
052 * that indicates whether to sort in ascending or descending order.  It may also
053 * specify a custom matching rule that should be used to specify which logic
054 * should be used to perform the sorting.
055 * <BR><BR>
056 * If the search is successful, then the search result done message may include
057 * the {@link ServerSideSortResponseControl} to provide information about the
058 * status of the sort processing.
059 * <BR><BR>
060 * <H2>Example</H2>
061 * The following example demonstrates the use of the server-side sort controls
062 * to retrieve users in different sort orders.
063 * <PRE>
064 * // Perform a search to get all user entries sorted by last name, then by
065 * // first name, both in ascending order.
066 * SearchRequest searchRequest = new SearchRequest(
067 *      "ou=People,dc=example,dc=com", SearchScope.SUB,
068 *      Filter.createEqualityFilter("objectClass", "person"));
069 * searchRequest.addControl(new ServerSideSortRequestControl(
070 *      new SortKey("sn"), new SortKey("givenName")));
071 * SearchResult lastNameAscendingResult;
072 * try
073 * {
074 *   lastNameAscendingResult = connection.search(searchRequest);
075 *   // If we got here, then the search was successful.
076 * }
077 * catch (LDAPSearchException lse)
078 * {
079 *   // The search failed for some reason.
080 *   lastNameAscendingResult = lse.getSearchResult();
081 *   ResultCode resultCode = lse.getResultCode();
082 *   String errorMessageFromServer = lse.getDiagnosticMessage();
083 * }
084 *
085 * // Get the response control and retrieve the result code for the sort
086 * // processing.
087 * LDAPTestUtils.assertHasControl(lastNameAscendingResult,
088 *      ServerSideSortResponseControl.SERVER_SIDE_SORT_RESPONSE_OID);
089 * ServerSideSortResponseControl lastNameAscendingResponseControl =
090 *      ServerSideSortResponseControl.get(lastNameAscendingResult);
091 * ResultCode lastNameSortResult =
092 *      lastNameAscendingResponseControl.getResultCode();
093 *
094 *
095 * // Perform the same search, but this time request the results to be sorted
096 * // in descending order by first name, then last name.
097 * searchRequest.setControls(new ServerSideSortRequestControl(
098 *      new SortKey("givenName", true), new SortKey("sn", true)));
099 * SearchResult firstNameDescendingResult;
100 * try
101 * {
102 *   firstNameDescendingResult = connection.search(searchRequest);
103 *   // If we got here, then the search was successful.
104 * }
105 * catch (LDAPSearchException lse)
106 * {
107 *   // The search failed for some reason.
108 *   firstNameDescendingResult = lse.getSearchResult();
109 *   ResultCode resultCode = lse.getResultCode();
110 *   String errorMessageFromServer = lse.getDiagnosticMessage();
111 * }
112 *
113 * // Get the response control and retrieve the result code for the sort
114 * // processing.
115 * LDAPTestUtils.assertHasControl(firstNameDescendingResult,
116 *      ServerSideSortResponseControl.SERVER_SIDE_SORT_RESPONSE_OID);
117 * ServerSideSortResponseControl firstNameDescendingResponseControl =
118 *      ServerSideSortResponseControl.get(firstNameDescendingResult);
119 * ResultCode firstNameSortResult =
120 *      firstNameDescendingResponseControl.getResultCode();
121 * </PRE>
122 * <BR><BR>
123 * <H2>Client-Side Sorting</H2>
124 * The UnboundID LDAP SDK for Java provides support for client-side sorting as
125 * an alternative to server-side sorting.  Client-side sorting may be useful in
126 * cases in which the target server does not support the use of the server-side
127 * sort control, or when it is desirable to perform the sort processing on the
128 * client systems rather than on the directory server systems.  See the
129 * {@link com.unboundid.ldap.sdk.EntrySorter} class for details on performing
130 * client-side sorting in the LDAP SDK.
131 */
132@NotMutable()
133@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
134public final class ServerSideSortRequestControl
135       extends Control
136{
137  /**
138   * The OID (1.2.840.113556.1.4.473) for the server-side sort request control.
139   */
140  public static final String SERVER_SIDE_SORT_REQUEST_OID =
141       "1.2.840.113556.1.4.473";
142
143
144
145  /**
146   * The serial version UID for this serializable class.
147   */
148  private static final long serialVersionUID = -3021901578330574772L;
149
150
151
152  // The set of sort keys to use with this control.
153  private final SortKey[] sortKeys;
154
155
156
157  /**
158   * Creates a new server-side sort control that will sort the results based on
159   * the provided set of sort keys.
160   *
161   * @param  sortKeys  The set of sort keys to define the desired order in which
162   *                   the results should be returned.  It must not be
163   *                   {@code null} or empty.
164   */
165  public ServerSideSortRequestControl(final SortKey... sortKeys)
166  {
167    this(false, sortKeys);
168  }
169
170
171
172  /**
173   * Creates a new server-side sort control that will sort the results based on
174   * the provided set of sort keys.
175   *
176   * @param  sortKeys  The set of sort keys to define the desired order in which
177   *                   the results should be returned.  It must not be
178   *                   {@code null} or empty.
179   */
180  public ServerSideSortRequestControl(final List<SortKey> sortKeys)
181  {
182    this(false, sortKeys);
183  }
184
185
186
187  /**
188   * Creates a new server-side sort control that will sort the results based on
189   * the provided set of sort keys.
190   *
191   * @param  isCritical  Indicates whether this control should be marked
192   *                     critical.
193   * @param  sortKeys    The set of sort keys to define the desired order in
194   *                     which the results should be returned.  It must not be
195   *                     {@code null} or empty.
196   */
197  public ServerSideSortRequestControl(final boolean isCritical,
198                                      final SortKey... sortKeys)
199  {
200    super(SERVER_SIDE_SORT_REQUEST_OID, isCritical, encodeValue(sortKeys));
201
202    this.sortKeys = sortKeys;
203  }
204
205
206
207  /**
208   * Creates a new server-side sort control that will sort the results based on
209   * the provided set of sort keys.
210   *
211   * @param  isCritical  Indicates whether this control should be marked
212   *                     critical.
213   * @param  sortKeys    The set of sort keys to define the desired order in
214   *                     which the results should be returned.  It must not be
215   *                     {@code null} or empty.
216   */
217  public ServerSideSortRequestControl(final boolean isCritical,
218                                      final List<SortKey> sortKeys)
219  {
220    this(isCritical, sortKeys.toArray(new SortKey[sortKeys.size()]));
221  }
222
223
224
225  /**
226   * Creates a new server-side sort request control which is decoded from the
227   * provided generic control.
228   *
229   * @param  control  The generic control to be decoded as a server-side sort
230   *                  request control.
231   *
232   * @throws  LDAPException  If the provided control cannot be decoded as a
233   *                         server-side sort request control.
234   */
235  public ServerSideSortRequestControl(final Control control)
236         throws LDAPException
237  {
238    super(control);
239
240    final ASN1OctetString value = control.getValue();
241    if (value == null)
242    {
243      throw new LDAPException(ResultCode.DECODING_ERROR,
244                              ERR_SORT_REQUEST_NO_VALUE.get());
245    }
246
247    try
248    {
249      final ASN1Element valueElement = ASN1Element.decode(value.getValue());
250      final ASN1Element[] elements =
251           ASN1Sequence.decodeAsSequence(valueElement).elements();
252      sortKeys = new SortKey[elements.length];
253      for (int i=0; i < elements.length; i++)
254      {
255        sortKeys[i] = SortKey.decode(elements[i]);
256      }
257    }
258    catch (Exception e)
259    {
260      debugException(e);
261      throw new LDAPException(ResultCode.DECODING_ERROR,
262                              ERR_SORT_REQUEST_CANNOT_DECODE.get(e), e);
263    }
264  }
265
266
267
268  /**
269   * Encodes the provided information into an octet string that can be used as
270   * the value for this control.
271   *
272   * @param  sortKeys  The set of sort keys to define the desired order in which
273   *                   the results should be returned.  It must not be
274   *                   {@code null} or empty.
275   *
276   * @return  An ASN.1 octet string that can be used as the value for this
277   *          control.
278   */
279  private static ASN1OctetString encodeValue(final SortKey[] sortKeys)
280  {
281    ensureNotNull(sortKeys);
282    ensureTrue(sortKeys.length > 0,
283               "ServerSideSortRequestControl.sortKeys must not be empty.");
284
285    final ASN1Element[] valueElements = new ASN1Element[sortKeys.length];
286    for (int i=0; i < sortKeys.length; i++)
287    {
288      valueElements[i] = sortKeys[i].encode();
289    }
290
291    return new ASN1OctetString(new ASN1Sequence(valueElements).encode());
292  }
293
294
295
296  /**
297   * Retrieves the set of sort keys that define the desired order in which the
298   * results should be returned.
299   *
300   * @return  The set of sort keys that define the desired order in which the
301   *          results should be returned.
302   */
303  public SortKey[] getSortKeys()
304  {
305    return sortKeys;
306  }
307
308
309
310  /**
311   * {@inheritDoc}
312   */
313  @Override()
314  public String getControlName()
315  {
316    return INFO_CONTROL_NAME_SORT_REQUEST.get();
317  }
318
319
320
321  /**
322   * {@inheritDoc}
323   */
324  @Override()
325  public void toString(final StringBuilder buffer)
326  {
327    buffer.append("ServerSideSortRequestControl(sortKeys={");
328
329    for (int i=0; i < sortKeys.length; i++)
330    {
331      if (i > 0)
332      {
333        buffer.append(", ");
334      }
335
336      buffer.append('\'');
337      sortKeys[i].toString(buffer);
338      buffer.append('\'');
339    }
340
341    buffer.append("})");
342  }
343}