001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020 package org.apache.directory.shared.ldap.codec.search.controls.pagedSearch; 021 022 023 import java.nio.ByteBuffer; 024 025 import org.apache.directory.shared.asn1.Asn1Object; 026 import org.apache.directory.shared.asn1.ber.tlv.TLV; 027 import org.apache.directory.shared.asn1.ber.tlv.UniversalTag; 028 import org.apache.directory.shared.asn1.ber.tlv.Value; 029 import org.apache.directory.shared.asn1.codec.EncoderException; 030 import org.apache.directory.shared.i18n.I18n; 031 import org.apache.directory.shared.ldap.codec.controls.AbstractControl; 032 import org.apache.directory.shared.ldap.util.StringTools; 033 034 035 /** 036 * A request/response control used to implement a simple paging of search 037 * results. This is an implementation of RFC 2696 : 038 * <a href="http://www.faqs.org/rfcs/rfc2696.html">LDAP Control Extension for Simple Paged Results Manipulation</a> 039 * <br/> 040 * <pre> 041 * This control is included in the searchRequest and searchResultDone 042 * messages as part of the controls field of the LDAPMessage, as defined 043 * in Section 4.1.12 of [LDAPv3]. The structure of this control is as 044 * follows: 045 * 046 * pagedResultsControl ::= SEQUENCE { 047 * controlType 1.2.840.113556.1.4.319, 048 * criticality BOOLEAN DEFAULT FALSE, 049 * controlValue searchControlValue 050 * } 051 * 052 * The searchControlValue is an OCTET STRING wrapping the BER-encoded 053 * version of the following SEQUENCE: 054 * 055 * realSearchControlValue ::= SEQUENCE { 056 * size INTEGER (0..maxInt), 057 * -- requested page size from client 058 * -- result set size estimate from server 059 * cookie OCTET STRING 060 * } 061 * 062 * </pre> 063 * 064 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 065 * @version $Rev: $ 066 */ 067 public class PagedResultsControl extends AbstractControl 068 { 069 /** The Paged Search Control OID */ 070 public static final String CONTROL_OID = "1.2.840.113556.1.4.319"; 071 072 /** The number of entries to return, or returned */ 073 private int size; 074 075 /** The exchanged cookie */ 076 private byte[] cookie; 077 078 /** The entry change global length */ 079 private int pscSeqLength; 080 081 /** 082 * @see Asn1Object#Asn1Object 083 */ 084 public PagedResultsControl() 085 { 086 super( CONTROL_OID ); 087 088 decoder = new PagedResultsControlDecoder(); 089 } 090 091 092 /** 093 * Compute the PagedSearchControl length, which is the sum 094 * of the control length and the value length. 095 * 096 * <pre> 097 * PagedSearchControl value length : 098 * 099 * 0x30 L1 100 * | 101 * +--> 0x02 0x0(1-4) [0..2^63-1] (size) 102 * +--> 0x04 L2 (cookie) 103 * </pre> 104 */ 105 public int computeLength() 106 { 107 int sizeLength = 1 + 1 + Value.getNbBytes( size ); 108 109 int cookieLength = 0; 110 111 if ( cookie != null ) 112 { 113 cookieLength = 1 + TLV.getNbBytes( cookie.length ) + cookie.length; 114 } 115 else 116 { 117 cookieLength = 1 + 1; 118 } 119 120 pscSeqLength = sizeLength + cookieLength; 121 valueLength = 1 + TLV.getNbBytes( pscSeqLength ) + pscSeqLength; 122 123 // Call the super class to compute the global control length 124 return super.computeLength( valueLength ); 125 } 126 127 128 /** 129 * Encodes the paged search control. 130 * 131 * @param buffer The encoded sink 132 * @return A ByteBuffer that contains the encoded PDU 133 * @throws EncoderException If anything goes wrong. 134 */ 135 public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException 136 { 137 if ( buffer == null ) 138 { 139 throw new EncoderException( I18n.err( I18n.ERR_04023 ) ); 140 } 141 142 // Encode the Control envelop 143 super.encode( buffer ); 144 145 // Encode the OCTET_STRING tag 146 buffer.put( UniversalTag.OCTET_STRING_TAG ); 147 buffer.put( TLV.getBytes( valueLength ) ); 148 149 // Now encode the PagedSearch specific part 150 buffer.put( UniversalTag.SEQUENCE_TAG ); 151 buffer.put( TLV.getBytes( pscSeqLength ) ); 152 153 Value.encode( buffer, size ); 154 Value.encode( buffer, cookie ); 155 156 return buffer; 157 } 158 159 160 /** 161 * {@inheritDoc} 162 */ 163 public byte[] getValue() 164 { 165 if ( value == null ) 166 { 167 try 168 { 169 computeLength(); 170 ByteBuffer buffer = ByteBuffer.allocate( valueLength ); 171 172 // Now encode the PagedSearch specific part 173 buffer.put( UniversalTag.SEQUENCE_TAG ); 174 buffer.put( TLV.getBytes( pscSeqLength ) ); 175 176 Value.encode( buffer, size ); 177 Value.encode( buffer, cookie ); 178 179 value = buffer.array(); 180 } 181 catch ( Exception e ) 182 { 183 return null; 184 } 185 } 186 187 return value; 188 } 189 190 191 /** 192 * @return The requested or returned number of entries 193 */ 194 public int getSize() 195 { 196 return size; 197 } 198 199 200 /** 201 * Set the number of entry requested or returned 202 * 203 * @param size The number of entries 204 */ 205 public void setSize( int size ) 206 { 207 this.size = size; 208 } 209 210 211 /** 212 * @return The stored cookie 213 */ 214 public byte[] getCookie() 215 { 216 return cookie; 217 } 218 219 220 /** 221 * Set the cookie 222 * 223 * @param cookie The cookie to store in this control 224 */ 225 public void setCookie( byte[] cookie ) 226 { 227 this.cookie = cookie; 228 } 229 230 231 /** 232 * @return The integer value for the current cookie 233 */ 234 public int getCookieValue() 235 { 236 int value = ((cookie[0]&0x00FF)<<24) + ((cookie[1]&0x00FF)<<16) + ((cookie[2]&0x00FF)<<8) + (cookie[3]&0x00FF); 237 238 return value; 239 } 240 241 242 /** 243 * Return a String representing this PagedSearchControl. 244 */ 245 public String toString() 246 { 247 StringBuffer sb = new StringBuffer(); 248 249 sb.append( " Paged Search Control\n" ); 250 sb.append( " oid : " ).append( getOid() ).append( '\n' ); 251 sb.append( " critical : " ).append( isCritical() ).append( '\n' ); 252 sb.append( " size : '" ).append( size ).append( "'\n" ); 253 sb.append( " cookie : '" ).append( StringTools.dumpBytes( cookie ) ).append( "'\n" ); 254 255 return sb.toString(); 256 } 257 }