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.ASN1Integer; 029import com.unboundid.asn1.ASN1OctetString; 030import com.unboundid.asn1.ASN1Sequence; 031import com.unboundid.ldap.sdk.Control; 032import com.unboundid.ldap.sdk.DecodeableControl; 033import com.unboundid.ldap.sdk.LDAPException; 034import com.unboundid.ldap.sdk.ResultCode; 035import com.unboundid.ldap.sdk.SearchResult; 036import com.unboundid.util.NotMutable; 037import com.unboundid.util.ThreadSafety; 038import com.unboundid.util.ThreadSafetyLevel; 039 040import static com.unboundid.ldap.sdk.controls.ControlMessages.*; 041import static com.unboundid.util.Debug.*; 042 043 044 045/** 046 * This class provides an implementation of the virtual list view (VLV) response 047 * control, as defined in draft-ietf-ldapext-ldapv3-vlv. It may be used to 048 * provide information about the result of virtual list view processing for a 049 * search containing the {@link VirtualListViewRequestControl}. 050 * <BR><BR> 051 * The virtual list view response control may include the following elements: 052 * <UL> 053 * <LI>{@code resultCode} -- A result code that indicates the result of the 054 * virtual list view processing. It may be the same as or different from 055 * the result code contained in the search result done message.</LI> 056 * <LI>{@code targetPosition} -- The offset of the target entry specified by 057 * the client in the result set.</LI> 058 * <LI>{@code contentCount} -- The estimated total number of entries in the 059 * entire result set.</LI> 060 * <LI>{@code contextID} -- An optional cookie that the client should include 061 * in the next request as part of the virtual list view sequence.</LI> 062 * </UL> 063 */ 064@NotMutable() 065@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 066public final class VirtualListViewResponseControl 067 extends Control 068 implements DecodeableControl 069{ 070 /** 071 * The OID (2.16.840.1.113730.3.4.10) for the virtual list view response 072 * control. 073 */ 074 public static final String VIRTUAL_LIST_VIEW_RESPONSE_OID = 075 "2.16.840.1.113730.3.4.10"; 076 077 078 079 /** 080 * The serial version UID for this serializable class. 081 */ 082 private static final long serialVersionUID = -534656674756287217L; 083 084 085 086 // The context ID for this VLV response control, if available. 087 private final ASN1OctetString contextID; 088 089 // The estimated total number of entries in the result set. 090 private final int contentCount; 091 092 // The result code for this VLV response control. 093 private final ResultCode resultCode; 094 095 // The offset of the target entry for this VLV response control. 096 private final int targetPosition; 097 098 099 100 /** 101 * Creates a new empty control instance that is intended to be used only for 102 * decoding controls via the {@code DecodeableControl} interface. 103 */ 104 VirtualListViewResponseControl() 105 { 106 targetPosition = -1; 107 contentCount = -1; 108 resultCode = null; 109 contextID = null; 110 } 111 112 113 114 /** 115 * Creates a new virtual list view response control with the provided 116 * information. It will not be marked critical. 117 * 118 * @param targetPosition The offset of the target entry for this VLV 119 * response control. 120 * @param contentCount The estimated total number of entries in the 121 * result set. 122 * @param resultCode The result code for this VLV response control. 123 * @param contextID The context ID for this VLV response control. It 124 * may be {@code null} if no context ID is available. 125 */ 126 public VirtualListViewResponseControl(final int targetPosition, 127 final int contentCount, final ResultCode resultCode, 128 final ASN1OctetString contextID) 129 { 130 super(VIRTUAL_LIST_VIEW_RESPONSE_OID, false, 131 encodeValue(targetPosition, contentCount, resultCode, contextID)); 132 133 this.targetPosition = targetPosition; 134 this.contentCount = contentCount; 135 this.resultCode = resultCode; 136 this.contextID = contextID; 137 } 138 139 140 141 /** 142 * Creates a new virtual list view response control from the information 143 * contained in the provided control. 144 * 145 * @param oid The OID for the control. 146 * @param isCritical Indicates whether the control should be marked 147 * critical. 148 * @param value The encoded value for the control. This may be 149 * {@code null} if no value was provided. 150 * 151 * @throws LDAPException If a problem occurs while attempting to decode the 152 * provided control as a virtual list view response 153 * control. 154 */ 155 public VirtualListViewResponseControl(final String oid, 156 final boolean isCritical, 157 final ASN1OctetString value) 158 throws LDAPException 159 { 160 super(oid, isCritical, value); 161 162 if (value == null) 163 { 164 throw new LDAPException(ResultCode.DECODING_ERROR, 165 ERR_VLV_RESPONSE_NO_VALUE.get()); 166 } 167 168 final ASN1Sequence valueSequence; 169 try 170 { 171 final ASN1Element valueElement = 172 ASN1Element.decode(value.getValue()); 173 valueSequence = ASN1Sequence.decodeAsSequence(valueElement); 174 } 175 catch (final ASN1Exception ae) 176 { 177 debugException(ae); 178 throw new LDAPException(ResultCode.DECODING_ERROR, 179 ERR_VLV_RESPONSE_VALUE_NOT_SEQUENCE.get(ae), ae); 180 } 181 182 final ASN1Element[] valueElements = valueSequence.elements(); 183 if ((valueElements.length < 3) || (valueElements.length > 4)) 184 { 185 throw new LDAPException(ResultCode.DECODING_ERROR, 186 ERR_VLV_RESPONSE_INVALID_ELEMENT_COUNT.get( 187 valueElements.length)); 188 } 189 190 try 191 { 192 targetPosition = ASN1Integer.decodeAsInteger(valueElements[0]).intValue(); 193 } 194 catch (final ASN1Exception ae) 195 { 196 debugException(ae); 197 throw new LDAPException(ResultCode.DECODING_ERROR, 198 ERR_VLV_RESPONSE_FIRST_NOT_INTEGER.get(ae), ae); 199 } 200 201 try 202 { 203 contentCount = ASN1Integer.decodeAsInteger(valueElements[1]).intValue(); 204 } 205 catch (final ASN1Exception ae) 206 { 207 debugException(ae); 208 throw new LDAPException(ResultCode.DECODING_ERROR, 209 ERR_VLV_RESPONSE_SECOND_NOT_INTEGER.get(ae), ae); 210 } 211 212 try 213 { 214 final int rc = 215 ASN1Enumerated.decodeAsEnumerated(valueElements[2]).intValue(); 216 resultCode = ResultCode.valueOf(rc); 217 } 218 catch (final ASN1Exception ae) 219 { 220 debugException(ae); 221 throw new LDAPException(ResultCode.DECODING_ERROR, 222 ERR_VLV_RESPONSE_THIRD_NOT_ENUM.get(ae), ae); 223 } 224 225 if (valueElements.length == 4) 226 { 227 contextID = ASN1OctetString.decodeAsOctetString(valueElements[3]); 228 } 229 else 230 { 231 contextID = null; 232 } 233 } 234 235 236 237 /** 238 * {@inheritDoc} 239 */ 240 public VirtualListViewResponseControl 241 decodeControl(final String oid, final boolean isCritical, 242 final ASN1OctetString value) 243 throws LDAPException 244 { 245 return new VirtualListViewResponseControl(oid, isCritical, value); 246 } 247 248 249 250 /** 251 * Extracts a virtual list view response control from the provided result. 252 * 253 * @param result The result from which to retrieve the virtual list view 254 * response control. 255 * 256 * @return The virtual list view response control contained in the provided 257 * result, or {@code null} if the result did not contain a virtual 258 * list view response control. 259 * 260 * @throws LDAPException If a problem is encountered while attempting to 261 * decode the virtual list view response control 262 * contained in the provided result. 263 */ 264 public static VirtualListViewResponseControl get(final SearchResult result) 265 throws LDAPException 266 { 267 final Control c = result.getResponseControl(VIRTUAL_LIST_VIEW_RESPONSE_OID); 268 if (c == null) 269 { 270 return null; 271 } 272 273 if (c instanceof VirtualListViewResponseControl) 274 { 275 return (VirtualListViewResponseControl) c; 276 } 277 else 278 { 279 return new VirtualListViewResponseControl(c.getOID(), c.isCritical(), 280 c.getValue()); 281 } 282 } 283 284 285 286 /** 287 * Encodes the provided information into an octet string that can be used as 288 * the value for this control. 289 * 290 * @param targetPosition The offset of the target entry for this VLV 291 * response control. 292 * @param contentCount The estimated total number of entries in the 293 * result set. 294 * @param resultCode The result code for this VLV response control. 295 * @param contextID The context ID for this VLV response control. It 296 * may be {@code null} if no context ID is available. 297 * 298 * @return An ASN.1 octet string that can be used as the value for this 299 * control. 300 */ 301 private static ASN1OctetString encodeValue(final int targetPosition, 302 final int contentCount, 303 final ResultCode resultCode, 304 final ASN1OctetString contextID) 305 { 306 final ASN1Element[] vlvElements; 307 if (contextID == null) 308 { 309 vlvElements = new ASN1Element[] 310 { 311 new ASN1Integer(targetPosition), 312 new ASN1Integer(contentCount), 313 new ASN1Enumerated(resultCode.intValue()) 314 }; 315 } 316 else 317 { 318 vlvElements = new ASN1Element[] 319 { 320 new ASN1Integer(targetPosition), 321 new ASN1Integer(contentCount), 322 new ASN1Enumerated(resultCode.intValue()), 323 contextID 324 }; 325 } 326 327 return new ASN1OctetString(new ASN1Sequence(vlvElements).encode()); 328 } 329 330 331 332 /** 333 * Retrieves the offset of the target entry for this virtual list view 334 * response control. 335 * 336 * @return The offset of the target entry for this virtual list view response 337 * control. 338 */ 339 public int getTargetPosition() 340 { 341 return targetPosition; 342 } 343 344 345 346 /** 347 * Retrieves the estimated total number of entries in the result set. 348 * 349 * @return The estimated total number of entries in the result set. 350 */ 351 public int getContentCount() 352 { 353 return contentCount; 354 } 355 356 357 358 /** 359 * Retrieves the result code for this virtual list view response control. 360 * 361 * @return The result code for this virtual list view response control. 362 */ 363 public ResultCode getResultCode() 364 { 365 return resultCode; 366 } 367 368 369 370 /** 371 * Retrieves the context ID for this virtual list view response control, if 372 * available. 373 * 374 * @return The context ID for this virtual list view response control, or 375 * {@code null} if none was provided. 376 */ 377 public ASN1OctetString getContextID() 378 { 379 return contextID; 380 } 381 382 383 384 /** 385 * {@inheritDoc} 386 */ 387 @Override() 388 public String getControlName() 389 { 390 return INFO_CONTROL_NAME_VLV_RESPONSE.get(); 391 } 392 393 394 395 /** 396 * {@inheritDoc} 397 */ 398 @Override() 399 public void toString(final StringBuilder buffer) 400 { 401 buffer.append("VirtualListViewResponseControl(targetPosition="); 402 buffer.append(targetPosition); 403 buffer.append(", contentCount="); 404 buffer.append(contentCount); 405 buffer.append(", resultCode="); 406 buffer.append(resultCode); 407 buffer.append(')'); 408 } 409}