1 /*
   2  * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javax.naming.ldap;
  27 
  28 import java.io.IOException;
  29 import com.sun.jndi.ldap.Ber;
  30 import com.sun.jndi.ldap.BerEncoder;
  31 
  32 /**
  33  * Requests that the results of a search operation be returned by the LDAP
  34  * server in batches of a specified size.
  35  * The requestor controls the rate at which batches are returned by the rate
  36  * at which it invokes search operations.
  37  * <p>
  38  * The following code sample shows how the class may be used:
  39  * <pre>{@code
  40  *
  41  *     // Open an LDAP association
  42  *     LdapContext ctx = new InitialLdapContext();
  43  *
  44  *     // Activate paged results
  45  *     int pageSize = 20; // 20 entries per page
  46  *     byte[] cookie = null;
  47  *     int total;
  48  *     ctx.setRequestControls(new Control[]{
  49  *         new PagedResultsControl(pageSize, Control.CRITICAL) });
  50  *
  51  *     do {
  52  *         // Perform the search
  53  *         NamingEnumeration results =
  54  *             ctx.search("", "(objectclass=*)", new SearchControls());
  55  *
  56  *         // Iterate over a batch of search results
  57  *         while (results != null && results.hasMore()) {
  58  *             // Display an entry
  59  *             SearchResult entry = (SearchResult)results.next();
  60  *             System.out.println(entry.getName());
  61  *             System.out.println(entry.getAttributes());
  62  *
  63  *             // Handle the entry's response controls (if any)
  64  *             if (entry instanceof HasControls) {
  65  *                 // ((HasControls)entry).getControls();
  66  *             }
  67  *         }
  68  *         // Examine the paged results control response
  69  *         Control[] controls = ctx.getResponseControls();
  70  *         if (controls != null) {
  71  *             for (int i = 0; i < controls.length; i++) {
  72  *                 if (controls[i] instanceof PagedResultsResponseControl) {
  73  *                     PagedResultsResponseControl prrc =
  74  *                         (PagedResultsResponseControl)controls[i];
  75  *                     total = prrc.getResultSize();
  76  *                     cookie = prrc.getCookie();
  77  *                 } else {
  78  *                     // Handle other response controls (if any)
  79  *                 }
  80  *             }
  81  *         }
  82  *
  83  *         // Re-activate paged results
  84  *         ctx.setRequestControls(new Control[]{
  85  *             new PagedResultsControl(pageSize, cookie, Control.CRITICAL) });
  86  *     } while (cookie != null);
  87  *
  88  *     // Close the LDAP association
  89  *     ctx.close();
  90  *     ...
  91  *
  92  * } </pre>
  93  * <p>
  94  * This class implements the LDAPv3 Control for paged-results as defined in
  95  * <a href="http://www.ietf.org/rfc/rfc2696.txt">RFC 2696</a>.
  96  *
  97  * The control's value has the following ASN.1 definition:
  98  * <pre>{@code
  99  *
 100  *     realSearchControlValue ::= SEQUENCE {
 101  *         size      INTEGER (0..maxInt),
 102  *                           -- requested page size from client
 103  *                           -- result set size estimate from server
 104  *         cookie    OCTET STRING
 105  *     }
 106  *
 107  * }</pre>
 108  *
 109  * @since 1.5
 110  * @see PagedResultsResponseControl
 111  * @author Vincent Ryan
 112  */
 113 final public class PagedResultsControl extends BasicControl {
 114 
 115     /**
 116      * The paged-results control's assigned object identifier
 117      * is 1.2.840.113556.1.4.319.
 118      */
 119     public static final String OID = "1.2.840.113556.1.4.319";
 120 
 121     private static final byte[] EMPTY_COOKIE = new byte[0];
 122 
 123     private static final long serialVersionUID = 6684806685736844298L;
 124 
 125     /**
 126      * Constructs a control to set the number of entries to be returned per
 127      * page of results.
 128      *
 129      * @param   pageSize        The number of entries to return in a page.
 130      * @param   criticality     If true then the server must honor the control
 131      *                          and return search results as indicated by
 132      *                          pageSize or refuse to perform the search.
 133      *                          If false, then the server need not honor the
 134      *                          control.
 135      * @exception IOException   If an error was encountered while encoding the
 136      *                          supplied arguments into a control.
 137      */
 138     public PagedResultsControl(int pageSize, boolean criticality)
 139             throws IOException {
 140 
 141         super(OID, criticality, null);
 142         value = setEncodedValue(pageSize, EMPTY_COOKIE);
 143     }
 144 
 145     /**
 146      * Constructs a control to set the number of entries to be returned per
 147      * page of results. The cookie is provided by the server and may be
 148      * obtained from the paged-results response control.
 149      * <p>
 150      * A sequence of paged-results can be abandoned by setting the pageSize
 151      * to zero and setting the cookie to the last cookie received from the
 152      * server.
 153      *
 154      * @param   pageSize        The number of entries to return in a page.
 155      * @param   cookie          A possibly null server-generated cookie.
 156      * @param   criticality     If true then the server must honor the control
 157      *                          and return search results as indicated by
 158      *                          pageSize or refuse to perform the search.
 159      *                          If false, then the server need not honor the
 160      *                          control.
 161      * @exception IOException   If an error was encountered while encoding the
 162      *                          supplied arguments into a control.
 163      */
 164     public PagedResultsControl(int pageSize, byte[] cookie,
 165         boolean criticality) throws IOException {
 166 
 167         super(OID, criticality, null);
 168         if (cookie == null) {
 169             cookie = EMPTY_COOKIE;
 170         }
 171         value = setEncodedValue(pageSize, cookie);
 172     }
 173 
 174     /*
 175      * Encodes the paged-results control's value using ASN.1 BER.
 176      * The result includes the BER tag and length for the control's value but
 177      * does not include the control's object identifier and criticality setting.
 178      *
 179      * @param   pageSize        The number of entries to return in a page.
 180      * @param   cookie          A non-null server-generated cookie.
 181      * @return A possibly null byte array representing the ASN.1 BER encoded
 182      *         value of the LDAP paged-results control.
 183      * @exception IOException If a BER encoding error occurs.
 184      */
 185     private byte[] setEncodedValue(int pageSize, byte[] cookie)
 186         throws IOException {
 187 
 188         // build the ASN.1 encoding
 189         BerEncoder ber = new BerEncoder(10 + cookie.length);
 190 
 191         ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
 192             ber.encodeInt(pageSize);
 193             ber.encodeOctetString(cookie, Ber.ASN_OCTET_STR);
 194         ber.endSeq();
 195 
 196         return ber.getTrimmedBuf();
 197     }
 198 }