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