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