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 }