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