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