001/* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2008 Sun Microsystems, Inc. 025 * Portions Copyright 2014-2015 ForgeRock AS 026 */ 027package org.opends.server.controls; 028import org.forgerock.i18n.LocalizableMessage; 029 030 031import java.io.IOException; 032 033import org.forgerock.opendj.io.*; 034import org.opends.server.types.Control; 035import org.opends.server.types.DirectoryException; 036import org.forgerock.opendj.ldap.ByteString; 037import org.forgerock.opendj.ldap.ResultCode; 038 039import static org.opends.messages.ProtocolMessages.*; 040import static org.opends.server.util.ServerConstants.*; 041import static org.opends.server.util.StaticUtils.*; 042 043 044 045/** 046 * This class implements the virtual list view request controls as defined in 047 * draft-ietf-ldapext-ldapv3-vlv. The ASN.1 description for the control value 048 * is: 049 * <BR><BR> 050 * <PRE> 051 * VirtualListViewRequest ::= SEQUENCE { 052 * beforeCount INTEGER (0..maxInt), 053 * afterCount INTEGER (0..maxInt), 054 * target CHOICE { 055 * byOffset [0] SEQUENCE { 056 * offset INTEGER (1 .. maxInt), 057 * contentCount INTEGER (0 .. maxInt) }, 058 * greaterThanOrEqual [1] AssertionValue }, 059 * contextID OCTET STRING OPTIONAL } 060 * </PRE> 061 */ 062public class VLVRequestControl 063 extends Control 064{ 065 /** 066 * ControlDecoder implementation to decode this control from a ByteString. 067 */ 068 private static final class Decoder 069 implements ControlDecoder<VLVRequestControl> 070 { 071 /** {@inheritDoc} */ 072 public VLVRequestControl decode(boolean isCritical, ByteString value) 073 throws DirectoryException 074 { 075 if (value == null) 076 { 077 LocalizableMessage message = INFO_VLVREQ_CONTROL_NO_VALUE.get(); 078 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message); 079 } 080 081 ASN1Reader reader = ASN1.getReader(value); 082 try 083 { 084 reader.readStartSequence(); 085 086 int beforeCount = (int)reader.readInteger(); 087 int afterCount = (int)reader.readInteger(); 088 089 int offset = 0; 090 int contentCount = 0; 091 ByteString greaterThanOrEqual = null; 092 byte targetType = reader.peekType(); 093 switch (targetType) 094 { 095 case TYPE_TARGET_BYOFFSET: 096 reader.readStartSequence(); 097 offset = (int)reader.readInteger(); 098 contentCount = (int)reader.readInteger(); 099 reader.readEndSequence(); 100 break; 101 102 case TYPE_TARGET_GREATERTHANOREQUAL: 103 greaterThanOrEqual = reader.readOctetString(); 104 break; 105 106 default: 107 LocalizableMessage message = INFO_VLVREQ_CONTROL_INVALID_TARGET_TYPE.get( 108 byteToHex(targetType)); 109 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message); 110 } 111 112 ByteString contextID = null; 113 if (reader.hasNextElement()) 114 { 115 contextID = reader.readOctetString(); 116 } 117 118 if(targetType == TYPE_TARGET_BYOFFSET) 119 { 120 return new VLVRequestControl(isCritical, beforeCount, 121 afterCount, offset, contentCount, contextID); 122 } 123 124 return new VLVRequestControl(isCritical, beforeCount, 125 afterCount, greaterThanOrEqual, contextID); 126 } 127 catch (DirectoryException de) 128 { 129 throw de; 130 } 131 catch (Exception e) 132 { 133 LocalizableMessage message = 134 INFO_VLVREQ_CONTROL_CANNOT_DECODE_VALUE.get(getExceptionMessage(e)); 135 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message, e); 136 } 137 } 138 139 public String getOID() 140 { 141 return OID_VLV_REQUEST_CONTROL; 142 } 143 144 } 145 146 /** 147 * The Control Decoder that can be used to decode this control. 148 */ 149 public static final ControlDecoder<VLVRequestControl> DECODER = 150 new Decoder(); 151 152 /** 153 * The BER type to use when encoding the byOffset target element. 154 */ 155 public static final byte TYPE_TARGET_BYOFFSET = (byte) 0xA0; 156 157 158 159 /** 160 * The BER type to use when encoding the greaterThanOrEqual target element. 161 */ 162 public static final byte TYPE_TARGET_GREATERTHANOREQUAL = (byte) 0x81; 163 164 165 166 /** The target type for this VLV request control. */ 167 private byte targetType; 168 169 /** The context ID for this VLV request control. */ 170 private ByteString contextID; 171 172 /** The greaterThanOrEqual target assertion value for this VLV request control. */ 173 private ByteString greaterThanOrEqual; 174 175 /** The after count for this VLV request control. */ 176 private int afterCount; 177 178 /** The before count for this VLV request control. */ 179 private int beforeCount; 180 181 /** The content count for the byOffset target of this VLV request control. */ 182 private int contentCount; 183 184 /** The offset for the byOffset target of this VLV request control. */ 185 private int offset; 186 187 188 189 /** 190 * Creates a new VLV request control with the provided information. 191 * 192 * @param beforeCount The number of entries before the target offset to 193 * retrieve in the results page. 194 * @param afterCount The number of entries after the target offset to 195 * retrieve in the results page. 196 * @param offset The offset in the result set to target for the 197 * beginning of the page of results. 198 * @param contentCount The content count returned by the server in the last 199 * phase of the VLV request, or zero for a new VLV 200 * request session. 201 */ 202 public VLVRequestControl(int beforeCount, int afterCount, int offset, 203 int contentCount) 204 { 205 this(false, beforeCount, afterCount, offset, contentCount, null); 206 } 207 208 209 210 /** 211 * Creates a new VLV request control with the provided information. 212 * 213 * @param isCritical Indicates whether or not the control is critical. 214 * @param beforeCount The number of entries before the target offset to 215 * retrieve in the results page. 216 * @param afterCount The number of entries after the target offset to 217 * retrieve in the results page. 218 * @param offset The offset in the result set to target for the 219 * beginning of the page of results. 220 * @param contentCount The content count returned by the server in the last 221 * phase of the VLV request, or zero for a new VLV 222 * request session. 223 * @param contextID The context ID provided by the server in the last 224 * VLV response for the same set of criteria, or 225 * {@code null} if there was no previous VLV response or 226 * the server did not include a context ID in the 227 * last response. 228 */ 229 public VLVRequestControl(boolean isCritical, int beforeCount, int afterCount, 230 int offset, int contentCount, ByteString contextID) 231 { 232 super(OID_VLV_REQUEST_CONTROL, isCritical); 233 234 this.beforeCount = beforeCount; 235 this.afterCount = afterCount; 236 this.offset = offset; 237 this.contentCount = contentCount; 238 this.contextID = contextID; 239 240 targetType = TYPE_TARGET_BYOFFSET; 241 } 242 243 244 245 /** 246 * Creates a new VLV request control with the provided information. 247 * 248 * @param beforeCount The number of entries before the target offset 249 * to retrieve in the results page. 250 * @param afterCount The number of entries after the target offset 251 * to retrieve in the results page. 252 * @param greaterThanOrEqual The greaterThanOrEqual target assertion value 253 * that indicates where to start the page of 254 * results. 255 */ 256 public VLVRequestControl(int beforeCount, int afterCount, 257 ByteString greaterThanOrEqual) 258 { 259 this(false, beforeCount, afterCount, greaterThanOrEqual, null); 260 } 261 262 263 264 /** 265 * Creates a new VLV request control with the provided information. 266 * 267 * @param isCritical Indicates whether the control should be 268 * considered critical. 269 * @param beforeCount The number of entries before the target 270 * assertion value. 271 * @param afterCount The number of entries after the target 272 * assertion value. 273 * @param greaterThanOrEqual The greaterThanOrEqual target assertion value 274 * that indicates where to start the page of 275 * results. 276 * @param contextID The context ID provided by the server in the 277 * last VLV response for the same set of criteria, 278 * or {@code null} if there was no previous VLV 279 * response or the server did not include a 280 * context ID in the last response. 281 */ 282 public VLVRequestControl(boolean isCritical, int beforeCount, int afterCount, 283 ByteString greaterThanOrEqual, 284 ByteString contextID) 285 { 286 super(OID_VLV_REQUEST_CONTROL, isCritical); 287 288 this.beforeCount = beforeCount; 289 this.afterCount = afterCount; 290 this.greaterThanOrEqual = greaterThanOrEqual; 291 this.contextID = contextID; 292 293 targetType = TYPE_TARGET_GREATERTHANOREQUAL; 294 } 295 296 297 298 /** 299 * Retrieves the number of entries before the target offset or assertion value 300 * to include in the results page. 301 * 302 * @return The number of entries before the target offset to include in the 303 * results page. 304 */ 305 public int getBeforeCount() 306 { 307 return beforeCount; 308 } 309 310 311 312 /** 313 * Retrieves the number of entries after the target offset or assertion value 314 * to include in the results page. 315 * 316 * @return The number of entries after the target offset to include in the 317 * results page. 318 */ 319 public int getAfterCount() 320 { 321 return afterCount; 322 } 323 324 325 326 /** 327 * Retrieves the BER type for the target that specifies the beginning of the 328 * results page. 329 * 330 * @return {@code TYPE_TARGET_BYOFFSET} if the beginning of the results page 331 * should be specified as a nuemric offset, or 332 * {@code TYPE_TARGET_GREATERTHANOREQUAL} if it should be specified 333 * by an assertion value. 334 */ 335 public byte getTargetType() 336 { 337 return targetType; 338 } 339 340 341 342 /** 343 * Retrieves the offset that indicates the beginning of the results page. The 344 * return value will only be applicable if the {@code getTargetType} method 345 * returns {@code TYPE_TARGET_BYOFFSET}. 346 * 347 * @return The offset that indicates the beginning of the results page. 348 */ 349 public int getOffset() 350 { 351 return offset; 352 } 353 354 355 356 /** 357 * Retrieves the content count indicating the estimated number of entries in 358 * the complete result set. The return value will only be applicable if the 359 * {@code getTargetType} method returns {@code TYPE_TARGET_BYOFFSET}. 360 * 361 * @return The content count indicating the estimated number of entries in 362 * the complete result set. 363 */ 364 public int getContentCount() 365 { 366 return contentCount; 367 } 368 369 370 371 /** 372 * Retrieves the assertion value that will be used to locate the beginning of 373 * the results page. This will only be applicable if the 374 * {@code getTargetType} method returns 375 * {@code TYPE_TARGET_GREATERTHANOREQUAL}. 376 * 377 * @return The assertion value that will be used to locate the beginning of 378 * the results page, or {@code null} if the beginning of the results 379 * page is to be specified using an offset. 380 */ 381 public ByteString getGreaterThanOrEqualAssertion() 382 { 383 return greaterThanOrEqual; 384 } 385 386 387 388 /** 389 * Retrieves a context ID value that should be used to resume a previous VLV 390 * results session. 391 * 392 * @return A context ID value that should be used to resume a previous VLV 393 * results session, or {@code null} if none is available. 394 */ 395 public ByteString getContextID() 396 { 397 return contextID; 398 } 399 400 401 402 /** 403 * Writes this control's value to an ASN.1 writer. The value (if any) must be 404 * written as an ASN1OctetString. 405 * 406 * @param writer The ASN.1 writer to use. 407 * @throws IOException If a problem occurs while writing to the stream. 408 */ 409 @Override 410 protected void writeValue(ASN1Writer writer) throws IOException { 411 writer.writeStartSequence(ASN1.UNIVERSAL_OCTET_STRING_TYPE); 412 413 writer.writeStartSequence(); 414 writer.writeInteger(beforeCount); 415 writer.writeInteger(afterCount); 416 if(targetType == TYPE_TARGET_BYOFFSET) 417 { 418 writer.writeStartSequence(TYPE_TARGET_BYOFFSET); 419 writer.writeInteger(offset); 420 writer.writeInteger(contentCount); 421 writer.writeEndSequence(); 422 } 423 else 424 { 425 writer.writeOctetString(TYPE_TARGET_GREATERTHANOREQUAL, 426 greaterThanOrEqual); 427 } 428 if (contextID != null) 429 { 430 writer.writeOctetString(contextID); 431 } 432 writer.writeEndSequence(); 433 434 writer.writeEndSequence(); 435 } 436 437 438 439 /** 440 * Appends a string representation of this VLV request control to the provided 441 * buffer. 442 * 443 * @param buffer The buffer to which the information should be appended. 444 */ 445 @Override 446 public void toString(StringBuilder buffer) 447 { 448 buffer.append("VLVRequestControl(beforeCount="); 449 buffer.append(beforeCount); 450 buffer.append(", afterCount="); 451 buffer.append(afterCount); 452 453 if (targetType == TYPE_TARGET_BYOFFSET) 454 { 455 buffer.append(", offset="); 456 buffer.append(offset); 457 buffer.append(", contentCount="); 458 buffer.append(contentCount); 459 } 460 else 461 { 462 buffer.append(", greaterThanOrEqual="); 463 buffer.append(greaterThanOrEqual); 464 } 465 466 if (contextID != null) 467 { 468 buffer.append(", contextID="); 469 buffer.append(contextID); 470 } 471 472 buffer.append(")"); 473 } 474} 475