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