001 /*
002 * Copyright 2007-2013 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2008-2013 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.controls;
022
023
024
025 import com.unboundid.asn1.ASN1Element;
026 import com.unboundid.asn1.ASN1OctetString;
027 import com.unboundid.asn1.ASN1Sequence;
028 import com.unboundid.ldap.sdk.Control;
029 import com.unboundid.ldap.sdk.LDAPException;
030 import com.unboundid.ldap.sdk.ResultCode;
031 import com.unboundid.util.NotMutable;
032 import com.unboundid.util.ThreadSafety;
033 import com.unboundid.util.ThreadSafetyLevel;
034
035 import static com.unboundid.ldap.sdk.controls.ControlMessages.*;
036 import static com.unboundid.util.Debug.*;
037 import 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 all users in the Sales department, sorted by last name and then
061 * by first name:
062 * <PRE>
063 * SearchRequest searchRequest =
064 * new SearchRequest("dc=example,dc=com", SearchScope.SUB, "(ou=Sales)");
065 * searchRequest.addControl(new ServerSideSortRequestControl(
066 * new SortKey("sn"), new SortKey("givenName")));
067 * SearchResult searchResult = connection.search(searchRequest);
068 * </PRE>
069 * <BR><BR>
070 * <H2>Client-Side Sorting</H2>
071 * The UnboundID LDAP SDK for Java provides support for client-side sorting as
072 * an alternative to server-side sorting. Client-side sorting may be useful in
073 * cases in which the target server does not support the use of the server-side
074 * sort control, or when it is desirable to perform the sort processing on the
075 * client systems rather than on the directory server systems. See the
076 * {@link com.unboundid.ldap.sdk.EntrySorter} class for details on performing
077 * client-side sorting in the LDAP SDK.
078 */
079 @NotMutable()
080 @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
081 public final class ServerSideSortRequestControl
082 extends Control
083 {
084 /**
085 * The OID (1.2.840.113556.1.4.473) for the server-side sort request control.
086 */
087 public static final String SERVER_SIDE_SORT_REQUEST_OID =
088 "1.2.840.113556.1.4.473";
089
090
091
092 /**
093 * The serial version UID for this serializable class.
094 */
095 private static final long serialVersionUID = -3021901578330574772L;
096
097
098
099 // The set of sort keys to use with this control.
100 private final SortKey[] sortKeys;
101
102
103
104 /**
105 * Creates a new server-side sort control that will sort the results based on
106 * the provided set of sort keys.
107 *
108 * @param sortKeys The set of sort keys to define the desired order in which
109 * the results should be returned. It must not be
110 * {@code null} or empty.
111 */
112 public ServerSideSortRequestControl(final SortKey... sortKeys)
113 {
114 super(SERVER_SIDE_SORT_REQUEST_OID, false, encodeValue(sortKeys));
115
116 this.sortKeys = sortKeys;
117 }
118
119
120
121 /**
122 * Creates a new server-side sort control that will sort the results based on
123 * the provided set of sort keys.
124 *
125 * @param isCritical Indicates whether this control should be marked
126 * critical.
127 * @param sortKeys The set of sort keys to define the desired order in
128 * which the results should be returned. It must not be
129 * {@code null} or empty.
130 */
131 public ServerSideSortRequestControl(final boolean isCritical,
132 final SortKey... sortKeys)
133 {
134 super(SERVER_SIDE_SORT_REQUEST_OID, isCritical, encodeValue(sortKeys));
135
136 this.sortKeys = sortKeys;
137 }
138
139
140
141 /**
142 * Creates a new server-side sort request control which is decoded from the
143 * provided generic control.
144 *
145 * @param control The generic control to be decoded as a server-side sort
146 * request control.
147 *
148 * @throws LDAPException If the provided control cannot be decoded as a
149 * server-side sort request control.
150 */
151 public ServerSideSortRequestControl(final Control control)
152 throws LDAPException
153 {
154 super(control);
155
156 final ASN1OctetString value = control.getValue();
157 if (value == null)
158 {
159 throw new LDAPException(ResultCode.DECODING_ERROR,
160 ERR_SORT_REQUEST_NO_VALUE.get());
161 }
162
163 try
164 {
165 final ASN1Element valueElement = ASN1Element.decode(value.getValue());
166 final ASN1Element[] elements =
167 ASN1Sequence.decodeAsSequence(valueElement).elements();
168 sortKeys = new SortKey[elements.length];
169 for (int i=0; i < elements.length; i++)
170 {
171 sortKeys[i] = SortKey.decode(elements[i]);
172 }
173 }
174 catch (Exception e)
175 {
176 debugException(e);
177 throw new LDAPException(ResultCode.DECODING_ERROR,
178 ERR_SORT_REQUEST_CANNOT_DECODE.get(e), e);
179 }
180 }
181
182
183
184 /**
185 * Encodes the provided information into an octet string that can be used as
186 * the value for this control.
187 *
188 * @param sortKeys The set of sort keys to define the desired order in which
189 * the results should be returned. It must not be
190 * {@code null} or empty.
191 *
192 * @return An ASN.1 octet string that can be used as the value for this
193 * control.
194 */
195 private static ASN1OctetString encodeValue(final SortKey[] sortKeys)
196 {
197 ensureNotNull(sortKeys);
198 ensureTrue(sortKeys.length > 0,
199 "ServerSideSortRequestControl.sortKeys must not be empty.");
200
201 final ASN1Element[] valueElements = new ASN1Element[sortKeys.length];
202 for (int i=0; i < sortKeys.length; i++)
203 {
204 valueElements[i] = sortKeys[i].encode();
205 }
206
207 return new ASN1OctetString(new ASN1Sequence(valueElements).encode());
208 }
209
210
211
212 /**
213 * Retrieves the set of sort keys that define the desired order in which the
214 * results should be returned.
215 *
216 * @return The set of sort keys that define the desired order in which the
217 * results should be returned.
218 */
219 public SortKey[] getSortKeys()
220 {
221 return sortKeys;
222 }
223
224
225
226 /**
227 * {@inheritDoc}
228 */
229 @Override()
230 public String getControlName()
231 {
232 return INFO_CONTROL_NAME_SORT_REQUEST.get();
233 }
234
235
236
237 /**
238 * {@inheritDoc}
239 */
240 @Override()
241 public void toString(final StringBuilder buffer)
242 {
243 buffer.append("ServerSideSortRequestControl(sortKeys={");
244
245 for (int i=0; i < sortKeys.length; i++)
246 {
247 if (i > 0)
248 {
249 buffer.append(", ");
250 }
251
252 buffer.append('\'');
253 sortKeys[i].toString(buffer);
254 buffer.append('\'');
255 }
256
257 buffer.append("})");
258 }
259 }