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}