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 010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE 011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE. 012 * See the License for the specific language governing permissions 013 * and limitations under the License. 014 * 015 * When distributing Covered Code, include this CDDL HEADER in each 016 * file and include the License file at 017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, 018 * add the following below this CDDL HEADER, with the fields enclosed 019 * by brackets "[]" replaced with your own identifying information: 020 * Portions Copyright [yyyy] [name of copyright owner] 021 * 022 * CDDL HEADER END 023 * 024 * 025 * Copyright 2008 Sun Microsystems, Inc. 026 */ 027 package org.opends.server.controls; 028 import org.opends.messages.Message; 029 030 031 032 import java.util.ArrayList; 033 034 import org.opends.server.protocols.asn1.ASN1Element; 035 import org.opends.server.protocols.asn1.ASN1Integer; 036 import org.opends.server.protocols.asn1.ASN1OctetString; 037 import org.opends.server.protocols.asn1.ASN1Sequence; 038 import org.opends.server.protocols.ldap.LDAPResultCode; 039 import org.opends.server.types.ByteString; 040 import org.opends.server.types.Control; 041 import org.opends.server.types.LDAPException; 042 043 import static org.opends.messages.ProtocolMessages.*; 044 import static org.opends.server.util.ServerConstants.*; 045 import static org.opends.server.util.StaticUtils.*; 046 047 048 049 /** 050 * This class implements the virtual list view request controls as defined in 051 * draft-ietf-ldapext-ldapv3-vlv. The ASN.1 description for the control value 052 * is: 053 * <BR><BR> 054 * <PRE> 055 * VirtualListViewRequest ::= SEQUENCE { 056 * beforeCount INTEGER (0..maxInt), 057 * afterCount INTEGER (0..maxInt), 058 * target CHOICE { 059 * byOffset [0] SEQUENCE { 060 * offset INTEGER (1 .. maxInt), 061 * contentCount INTEGER (0 .. maxInt) }, 062 * greaterThanOrEqual [1] AssertionValue }, 063 * contextID OCTET STRING OPTIONAL } 064 * </PRE> 065 */ 066 public class VLVRequestControl 067 extends Control 068 { 069 /** 070 * The BER type to use when encoding the byOffset target element. 071 */ 072 public static final byte TYPE_TARGET_BYOFFSET = (byte) 0xA0; 073 074 075 076 /** 077 * The BER type to use when encoding the greaterThanOrEqual target element. 078 */ 079 public static final byte TYPE_TARGET_GREATERTHANOREQUAL = (byte) 0x81; 080 081 082 083 // The target type for this VLV request control. 084 private byte targetType; 085 086 // The context ID for this VLV request control. 087 private ByteString contextID; 088 089 // The greaterThanOrEqual target assertion value for this VLV request control. 090 private ByteString greaterThanOrEqual; 091 092 // The after count for this VLV request control. 093 private int afterCount; 094 095 // The before count for this VLV request control. 096 private int beforeCount; 097 098 // The content count for the byOffset target of this VLV request control. 099 private int contentCount; 100 101 // The offset for the byOffset target of this VLV request control. 102 private int offset; 103 104 105 106 /** 107 * Creates a new VLV request control with the provided information. 108 * 109 * @param beforeCount The number of entries before the target offset to 110 * retrieve in the results page. 111 * @param afterCount The number of entries after the target offset to 112 * retrieve in the results page. 113 * @param offset The offset in the result set to target for the 114 * beginning of the page of results. 115 * @param contentCount The content count returned by the server in the last 116 * phase of the VLV request, or zero for a new VLV 117 * request session. 118 */ 119 public VLVRequestControl(int beforeCount, int afterCount, int offset, 120 int contentCount) 121 { 122 this(beforeCount, afterCount, offset, contentCount, null); 123 } 124 125 126 127 /** 128 * Creates a new VLV request control with the provided information. 129 * 130 * @param beforeCount The number of entries before the target offset to 131 * retrieve in the results page. 132 * @param afterCount The number of entries after the target offset to 133 * retrieve in the results page. 134 * @param offset The offset in the result set to target for the 135 * beginning of the page of results. 136 * @param contentCount The content count returned by the server in the last 137 * phase of the VLV request, or zero for a new VLV 138 * request session. 139 * @param contextID The context ID provided by the server in the last 140 * VLV response for the same set of criteria, or 141 * {@code null} if there was no previous VLV response or 142 * the server did not include a context ID in the 143 * last response. 144 */ 145 public VLVRequestControl(int beforeCount, int afterCount, int offset, 146 int contentCount, ByteString contextID) 147 { 148 super(OID_VLV_REQUEST_CONTROL, false, 149 encodeControlValue(beforeCount, afterCount, offset, contentCount, 150 contextID)); 151 152 this.beforeCount = beforeCount; 153 this.afterCount = afterCount; 154 this.offset = offset; 155 this.contentCount = contentCount; 156 this.contextID = contextID; 157 158 targetType = TYPE_TARGET_BYOFFSET; 159 } 160 161 162 163 /** 164 * Creates a new VLV request control with the provided information. 165 * 166 * @param beforeCount The number of entries before the target offset 167 * to retrieve in the results page. 168 * @param afterCount The number of entries after the target offset 169 * to retrieve in the results page. 170 * @param greaterThanOrEqual The greaterThanOrEqual target assertion value 171 * that indicates where to start the page of 172 * results. 173 */ 174 public VLVRequestControl(int beforeCount, int afterCount, 175 ByteString greaterThanOrEqual) 176 { 177 this(beforeCount, afterCount, greaterThanOrEqual, null); 178 } 179 180 181 182 /** 183 * Creates a new VLV request control with the provided information. 184 * 185 * @param beforeCount The number of entries before the target 186 * assertion value. 187 * @param afterCount The number of entries after the target 188 * assertion value. 189 * @param greaterThanOrEqual The greaterThanOrEqual target assertion value 190 * that indicates where to start the page of 191 * results. 192 * @param contextID The context ID provided by the server in the 193 * last VLV response for the same set of criteria, 194 * or {@code null} if there was no previous VLV 195 * response or the server did not include a 196 * context ID in the last response. 197 */ 198 public VLVRequestControl(int beforeCount, int afterCount, 199 ByteString greaterThanOrEqual, 200 ByteString contextID) 201 { 202 super(OID_VLV_REQUEST_CONTROL, false, 203 encodeControlValue(beforeCount, afterCount, greaterThanOrEqual, 204 contextID)); 205 206 this.beforeCount = beforeCount; 207 this.afterCount = afterCount; 208 this.greaterThanOrEqual = greaterThanOrEqual; 209 this.contextID = contextID; 210 211 targetType = TYPE_TARGET_GREATERTHANOREQUAL; 212 } 213 214 215 216 /** 217 * Creates a new VLV request control with the provided information. 218 * 219 * @param oid The OID for the control. 220 * @param isCritical Indicates whether the control should be 221 * considered critical. 222 * @param controlValue The pre-encoded value for the control. 223 * @param beforeCount The number of entries before the target 224 * assertion value. 225 * @param afterCount The number of entries after the target 226 * assertion value. 227 * @param greaterThanOrEqual The greaterThanOrEqual target assertion value 228 * that indicates where to start the page of 229 * results. 230 * @param contextID The context ID provided by the server in the 231 * last VLV response for the same set of criteria, 232 * or {@code null} if there was no previous VLV 233 * response or the server did not include a 234 * context ID in the last response. 235 */ 236 private VLVRequestControl(String oid, boolean isCritical, 237 ASN1OctetString controlValue, int beforeCount, 238 int afterCount, byte targetType, 239 int offset, int contentCount, 240 ByteString greaterThanOrEqual, 241 ByteString contextID) 242 { 243 super(oid, isCritical, controlValue); 244 245 this.beforeCount = beforeCount; 246 this.afterCount = afterCount; 247 this.targetType = targetType; 248 this.offset = offset; 249 this.contentCount = contentCount; 250 this.greaterThanOrEqual = greaterThanOrEqual; 251 this.contextID = contextID; 252 } 253 254 255 256 /** 257 * Retrieves the number of entries before the target offset or assertion value 258 * to include in the results page. 259 * 260 * @return The number of entries before the target offset to include in the 261 * results page. 262 */ 263 public int getBeforeCount() 264 { 265 return beforeCount; 266 } 267 268 269 270 /** 271 * Retrieves the number of entries after the target offset or assertion value 272 * to include in the results page. 273 * 274 * @return The number of entries after the target offset to include in the 275 * results page. 276 */ 277 public int getAfterCount() 278 { 279 return afterCount; 280 } 281 282 283 284 /** 285 * Retrieves the BER type for the target that specifies the beginning of the 286 * results page. 287 * 288 * @return {@code TYPE_TARGET_BYOFFSET} if the beginning of the results page 289 * should be specified as a nuemric offset, or 290 * {@code TYPE_TARGET_GREATERTHANOREQUAL} if it should be specified 291 * by an assertion value. 292 */ 293 public byte getTargetType() 294 { 295 return targetType; 296 } 297 298 299 300 /** 301 * Retrieves the offset that indicates the beginning of the results page. The 302 * return value will only be applicable if the {@code getTargetType} method 303 * returns {@code TYPE_TARGET_BYOFFSET}. 304 * 305 * @return The offset that indicates the beginning of the results page. 306 */ 307 public int getOffset() 308 { 309 return offset; 310 } 311 312 313 314 /** 315 * Retrieves the content count indicating the estimated number of entries in 316 * the complete result set. The return value will only be applicable if the 317 * {@code getTargetType} method returns {@code TYPE_TARGET_BYOFFSET}. 318 * 319 * @return The content count indicating the estimated number of entries in 320 * the complete result set. 321 */ 322 public int getContentCount() 323 { 324 return contentCount; 325 } 326 327 328 329 /** 330 * Retrieves the assertion value that will be used to locate the beginning of 331 * the results page. This will only be applicable if the 332 * {@code getTargetType} method returns 333 * {@code TYPE_TARGET_GREATERTHANOREQUAL}. 334 * 335 * @return The assertion value that will be used to locate the beginning of 336 * the results page, or {@code null} if the beginning of the results 337 * page is to be specified using an offset. 338 */ 339 public ByteString getGreaterThanOrEqualAssertion() 340 { 341 return greaterThanOrEqual; 342 } 343 344 345 346 /** 347 * Retrieves a context ID value that should be used to resume a previous VLV 348 * results session. 349 * 350 * @return A context ID value that should be used to resume a previous VLV 351 * results session, or {@code null} if none is available. 352 */ 353 public ByteString getContextID() 354 { 355 return contextID; 356 } 357 358 359 360 /** 361 * Encodes the provided information in a manner suitable for use as the value 362 * of this control. 363 * 364 * @param beforeCount The number of entries before the target offset to 365 * retrieve in the results page. 366 * @param afterCount The number of entries after the target offset to 367 * retrieve in the results page. 368 * @param offset The offset in the result set to target for the 369 * beginning of the page of results. 370 * @param contentCount The content count returned by the server in the last 371 * phase of the VLV request, or zero for a new VLV 372 * request session. 373 * @param contextID The context ID provided by the server in the last 374 * VLV response for the same set of criteria, or 375 * {@code null} if there was no previous VLV response or 376 * the server did not include a context ID in the 377 * last response. 378 * 379 * @return The ASN.1 octet string containing the encoded sort order. 380 */ 381 private static ASN1OctetString encodeControlValue(int beforeCount, 382 int afterCount, int offset, 383 int contentCount, ByteString contextID) 384 { 385 ArrayList<ASN1Element> vlvElements = new ArrayList<ASN1Element>(4); 386 vlvElements.add(new ASN1Integer(beforeCount)); 387 vlvElements.add(new ASN1Integer(afterCount)); 388 389 ArrayList<ASN1Element> targetElements = new ArrayList<ASN1Element>(2); 390 targetElements.add(new ASN1Integer(offset)); 391 targetElements.add(new ASN1Integer(contentCount)); 392 vlvElements.add(new ASN1Sequence(TYPE_TARGET_BYOFFSET, targetElements)); 393 394 if (contextID != null) 395 { 396 vlvElements.add(contextID.toASN1OctetString()); 397 } 398 399 return new ASN1OctetString(new ASN1Sequence(vlvElements).encode()); 400 } 401 402 403 404 /** 405 * Encodes the provided information in a manner suitable for use as the value 406 * of this control. 407 * 408 * @param beforeCount The number of entries before the target 409 * assertion value. 410 * @param afterCount The number of entries after the target 411 * assertion value. 412 * @param greaterThanOrEqual The greaterThanOrEqual target assertion value 413 * that indicates where to start the page of 414 * results. 415 * @param contextID The context ID provided by the server in the 416 * last VLV response for the same set of criteria, 417 * or {@code null} if there was no previous VLV 418 * response or the server did not include a 419 * context ID in the last response. 420 * 421 * @return The ASN.1 octet string containing the encoded sort order. 422 */ 423 private static ASN1OctetString encodeControlValue(int beforeCount, 424 int afterCount, 425 ByteString greaterThanOrEqual, 426 ByteString contextID) 427 { 428 ArrayList<ASN1Element> vlvElements = new ArrayList<ASN1Element>(4); 429 vlvElements.add(new ASN1Integer(beforeCount)); 430 vlvElements.add(new ASN1Integer(afterCount)); 431 432 vlvElements.add(new ASN1OctetString(TYPE_TARGET_GREATERTHANOREQUAL, 433 greaterThanOrEqual.value())); 434 435 if (contextID != null) 436 { 437 vlvElements.add(contextID.toASN1OctetString()); 438 } 439 440 return new ASN1OctetString(new ASN1Sequence(vlvElements).encode()); 441 } 442 443 444 445 /** 446 * Creates a new VLV request control from the contents of the provided 447 * control. 448 * 449 * @param control The generic control containing the information to use to 450 * create this VLV request control. It must not be 451 * {@code null}. 452 * 453 * @return The VLV request control decoded from the provided control. 454 * 455 * @throws LDAPException If this control cannot be decoded as a valid VLV 456 * request control. 457 */ 458 public static VLVRequestControl decodeControl(Control control) 459 throws LDAPException 460 { 461 ASN1OctetString controlValue = control.getValue(); 462 if (controlValue == null) 463 { 464 Message message = INFO_VLVREQ_CONTROL_NO_VALUE.get(); 465 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 466 } 467 468 try 469 { 470 ASN1Sequence vlvSequence = 471 ASN1Sequence.decodeAsSequence(controlValue.value()); 472 ArrayList<ASN1Element> elements = vlvSequence.elements(); 473 474 if ((elements.size() < 3) || (elements.size() > 4)) 475 { 476 Message message = 477 INFO_VLVREQ_CONTROL_INVALID_ELEMENT_COUNT.get(elements.size()); 478 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 479 } 480 481 int beforeCount = elements.get(0).decodeAsInteger().intValue(); 482 int afterCount = elements.get(1).decodeAsInteger().intValue(); 483 484 ASN1Element targetElement = elements.get(2); 485 int offset = 0; 486 int contentCount = 0; 487 ASN1OctetString greaterThanOrEqual = null; 488 byte targetType = targetElement.getType(); 489 switch (targetType) 490 { 491 case TYPE_TARGET_BYOFFSET: 492 ArrayList<ASN1Element> targetElements = 493 targetElement.decodeAsSequence().elements(); 494 offset = targetElements.get(0).decodeAsInteger().intValue(); 495 contentCount = targetElements.get(1).decodeAsInteger().intValue(); 496 break; 497 498 case TYPE_TARGET_GREATERTHANOREQUAL: 499 greaterThanOrEqual = targetElement.decodeAsOctetString(); 500 break; 501 502 default: 503 Message message = INFO_VLVREQ_CONTROL_INVALID_TARGET_TYPE.get( 504 byteToHex(targetType)); 505 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 506 } 507 508 ASN1OctetString contextID = null; 509 if (elements.size() == 4) 510 { 511 contextID = elements.get(3).decodeAsOctetString(); 512 } 513 514 return new VLVRequestControl(control.getOID(), control.isCritical(), 515 controlValue, beforeCount, afterCount, 516 targetType, offset, contentCount, 517 greaterThanOrEqual, contextID); 518 } 519 catch (LDAPException le) 520 { 521 throw le; 522 } 523 catch (Exception e) 524 { 525 Message message = 526 INFO_VLVREQ_CONTROL_CANNOT_DECODE_VALUE.get(getExceptionMessage(e)); 527 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message, e); 528 } 529 } 530 531 532 533 /** 534 * Retrieves a string representation of this VLV request control. 535 * 536 * @return A string representation of this VLV request control. 537 */ 538 public String toString() 539 { 540 StringBuilder buffer = new StringBuilder(); 541 toString(buffer); 542 return buffer.toString(); 543 } 544 545 546 547 /** 548 * Appends a string representation of this VLV request control to the provided 549 * buffer. 550 * 551 * @param buffer The buffer to which the information should be appended. 552 */ 553 public void toString(StringBuilder buffer) 554 { 555 buffer.append("VLVRequestControl(beforeCount="); 556 buffer.append(beforeCount); 557 buffer.append(", afterCount="); 558 buffer.append(afterCount); 559 560 if (targetType == TYPE_TARGET_BYOFFSET) 561 { 562 buffer.append(", offset="); 563 buffer.append(offset); 564 buffer.append(", contentCount="); 565 buffer.append(contentCount); 566 } 567 else 568 { 569 buffer.append(", greaterThanOrEqual="); 570 buffer.append(greaterThanOrEqual); 571 } 572 573 if (contextID != null) 574 { 575 buffer.append(", contextID="); 576 buffer.append(contextID); 577 } 578 579 buffer.append(")"); 580 } 581 } 582