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.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.NotMutable; 036import com.unboundid.util.ThreadSafety; 037import com.unboundid.util.ThreadSafetyLevel; 038 039import static com.unboundid.ldap.sdk.controls.ControlMessages.*; 040import static com.unboundid.util.Debug.*; 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 * @param isCritical Indicates whether this control should be marked 111 * critical. 112 */ 113 public ServerSideSortResponseControl(final ResultCode resultCode, 114 final String attributeName, 115 final boolean isCritical) 116 { 117 super(SERVER_SIDE_SORT_RESPONSE_OID, isCritical, 118 encodeValue(resultCode, attributeName)); 119 120 this.resultCode = resultCode; 121 this.attributeName = attributeName; 122 } 123 124 125 126 /** 127 * Creates a new server-side sort response control from the information 128 * contained in the provided control. 129 * 130 * @param oid The OID for the control. 131 * @param isCritical Indicates whether the control should be marked 132 * critical. 133 * @param value The encoded value for the control. This may be 134 * {@code null} if no value was provided. 135 * 136 * @throws LDAPException If a problem occurs while attempting to decode the 137 * provided control as a server-side sort response 138 * control. 139 */ 140 public ServerSideSortResponseControl(final String oid, 141 final boolean isCritical, 142 final ASN1OctetString value) 143 throws LDAPException 144 { 145 super(oid, isCritical, value); 146 147 if (value == null) 148 { 149 throw new LDAPException(ResultCode.DECODING_ERROR, 150 ERR_SORT_RESPONSE_NO_VALUE.get()); 151 } 152 153 final ASN1Sequence valueSequence; 154 try 155 { 156 final ASN1Element valueElement = 157 ASN1Element.decode(value.getValue()); 158 valueSequence = ASN1Sequence.decodeAsSequence(valueElement); 159 } 160 catch (final ASN1Exception ae) 161 { 162 debugException(ae); 163 throw new LDAPException(ResultCode.DECODING_ERROR, 164 ERR_SORT_RESPONSE_VALUE_NOT_SEQUENCE.get(ae), ae); 165 } 166 167 final ASN1Element[] valueElements = valueSequence.elements(); 168 if ((valueElements.length < 1) || (valueElements.length > 2)) 169 { 170 throw new LDAPException(ResultCode.DECODING_ERROR, 171 ERR_SORT_RESPONSE_INVALID_ELEMENT_COUNT.get( 172 valueElements.length)); 173 } 174 175 try 176 { 177 final int rc = 178 ASN1Enumerated.decodeAsEnumerated(valueElements[0]).intValue(); 179 resultCode = ResultCode.valueOf(rc); 180 } 181 catch (final ASN1Exception ae) 182 { 183 debugException(ae); 184 throw new LDAPException(ResultCode.DECODING_ERROR, 185 ERR_SORT_RESPONSE_FIRST_NOT_ENUM.get(ae), ae); 186 } 187 188 if (valueElements.length == 2) 189 { 190 attributeName = 191 ASN1OctetString.decodeAsOctetString(valueElements[1]).stringValue(); 192 } 193 else 194 { 195 attributeName = null; 196 } 197 } 198 199 200 201 /** 202 * {@inheritDoc} 203 */ 204 public ServerSideSortResponseControl 205 decodeControl(final String oid, final boolean isCritical, 206 final ASN1OctetString value) 207 throws LDAPException 208 { 209 return new ServerSideSortResponseControl(oid, isCritical, value); 210 } 211 212 213 214 /** 215 * Extracts a server-side sort response control from the provided result. 216 * 217 * @param result The result from which to retrieve the server-side sort 218 * response control. 219 * 220 * @return The server-side sort response control contained in the provided 221 * result, or {@code null} if the result did not contain a 222 * server-side sort response control. 223 * 224 * @throws LDAPException If a problem is encountered while attempting to 225 * decode the server-side sort response control 226 * contained in the provided result. 227 */ 228 public static ServerSideSortResponseControl get(final SearchResult result) 229 throws LDAPException 230 { 231 final Control c = result.getResponseControl(SERVER_SIDE_SORT_RESPONSE_OID); 232 if (c == null) 233 { 234 return null; 235 } 236 237 if (c instanceof ServerSideSortResponseControl) 238 { 239 return (ServerSideSortResponseControl) c; 240 } 241 else 242 { 243 return new ServerSideSortResponseControl(c.getOID(), c.isCritical(), 244 c.getValue()); 245 } 246 } 247 248 249 250 /** 251 * Encodes the provided information into an octet string that can be used as 252 * the value for this control. 253 * 254 * @param resultCode The result code for this server-side sort response 255 * control. 256 * @param attributeName The attribute name to include in the control, or 257 * {@code null} if it should not be provided. 258 * 259 * @return An ASN.1 octet string that can be used as the value for this 260 * control. 261 */ 262 private static ASN1OctetString encodeValue(final ResultCode resultCode, 263 final String attributeName) 264 { 265 final ASN1Element[] valueElements; 266 if (attributeName == null) 267 { 268 valueElements = new ASN1Element[] 269 { 270 new ASN1Enumerated(resultCode.intValue()) 271 }; 272 } 273 else 274 { 275 valueElements = new ASN1Element[] 276 { 277 new ASN1Enumerated(resultCode.intValue()), 278 new ASN1OctetString(TYPE_ATTRIBUTE_TYPE, attributeName) 279 }; 280 } 281 282 return new ASN1OctetString(new ASN1Sequence(valueElements).encode()); 283 } 284 285 286 287 /** 288 * Retrieves the result code for this server-side sort response control. 289 * 290 * @return The result code for this server-side sort response control. 291 */ 292 public ResultCode getResultCode() 293 { 294 return resultCode; 295 } 296 297 298 299 /** 300 * Retrieves the attribute name for this server-side sort response control, if 301 * available. 302 * 303 * @return The attribute name for this server-side sort response control, or 304 * {@code null} if none was provided. 305 */ 306 public String getAttributeName() 307 { 308 return attributeName; 309 } 310 311 312 313 /** 314 * {@inheritDoc} 315 */ 316 @Override() 317 public String getControlName() 318 { 319 return INFO_CONTROL_NAME_SORT_RESPONSE.get(); 320 } 321 322 323 324 /** 325 * {@inheritDoc} 326 */ 327 @Override() 328 public void toString(final StringBuilder buffer) 329 { 330 buffer.append("ServerSideSortResponseControl(resultCode="); 331 buffer.append(resultCode); 332 333 if (attributeName != null) 334 { 335 buffer.append(", attributeName='"); 336 buffer.append(attributeName); 337 buffer.append('\''); 338 } 339 340 buffer.append(')'); 341 } 342}