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.server.ldap.handlers.controls;
021
022 import java.util.HashSet;
023 import java.util.Set;
024
025 import org.apache.directory.server.core.filtering.EntryFilteringCursor;
026 import org.apache.directory.server.ldap.LdapSession;
027 import org.apache.directory.shared.asn1.ber.tlv.Value;
028 import org.apache.directory.shared.ldap.constants.SchemaConstants;
029 import org.apache.directory.shared.ldap.exception.LdapException;
030 import org.apache.directory.shared.ldap.message.internal.InternalSearchRequest;
031 import org.apache.directory.shared.ldap.schema.AttributeType;
032 import org.apache.directory.shared.ldap.schema.SchemaManager;
033 import org.apache.directory.shared.ldap.util.StringTools;
034
035 /**
036 * The structure which stores the informations relative to the pagedSearch control.
037 * They are associated to a cookie, stored into the session and associated to an
038 * instance of this class.
039 *
040 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
041 * @version $Rev: $
042 */
043 public class PagedSearchContext
044 {
045 /** The previous search request */
046 private InternalSearchRequest previousSearchRequest;
047
048 /** The current position in the cursor */
049 private int currentPosition;
050
051 /** The cookie key */
052 private byte[] cookie;
053
054 /** The integer value for the cookie */
055 private int cookieValue;
056
057 /** The associated cursor for the current search request */
058 private EntryFilteringCursor cursor;
059
060 /**
061 * Creates a new instance of this class, storing the Searchrequest into it.
062 */
063 public PagedSearchContext( InternalSearchRequest searchRequest )
064 {
065 previousSearchRequest = searchRequest;
066 currentPosition = 0;
067
068 // We compute a key for this cookie. It combines the search request
069 // and some time seed, in order to avoid possible collisions, as
070 // a user may send more than one PagedSearch on the same session.
071 cookieValue = (int)(System.nanoTime()*17) + searchRequest.getMessageId();
072
073 cookie = Value.getBytes( cookieValue );
074 }
075
076
077 /**
078 * Compute a new key for this cookie, based on the current searchRequest
079 * hashCode and the current position. This value will be stored into the
080 * session, and will permit the retrieval of this instance.
081 *
082 * @return The new cookie's key
083 */
084 public byte[] getCookie()
085 {
086 return cookie;
087 }
088
089
090 public int getCookieValue()
091 {
092 return cookieValue;
093 }
094
095
096 /**
097 * Compute a new cookie, if the previous one already exists. This
098 * is unlikely, as we are based on some time seed, but just in case,
099 * this method will generate a new one.
100 * @return The new cookie
101 */
102 public byte[] getNewCookie()
103 {
104 cookieValue = cookieValue + (int)(System.nanoTime()*17);
105 cookie = Value.getBytes( cookieValue );
106
107 return cookie;
108 }
109
110
111 /**
112 * Build a set of OIDs from the list of attributes we have in the search request
113 */
114 private Set<String> buildAttributeSet( InternalSearchRequest request, LdapSession session,
115 SchemaManager schemaManager )
116 {
117 Set<String> requestSet = new HashSet<String>();
118
119 // Build the set of attributeType from the attributes
120 for ( String attribute:request.getAttributes() )
121 {
122 try
123 {
124 AttributeType at = schemaManager.lookupAttributeTypeRegistry( attribute );
125 requestSet.add( at.getOid() );
126 }
127 catch ( LdapException le )
128 {
129 // Deal with special attributes : '*', '+' and '1.1'
130 if ( attribute.equals( SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES ) ||
131 attribute.equals( SchemaConstants.ALL_USER_ATTRIBUTES ) ||
132 attribute.equals( SchemaConstants.NO_ATTRIBUTE ) )
133 {
134 requestSet.add( attribute );
135 }
136
137 // Otherwise, don't add the attribute to the set
138 }
139 }
140
141 return requestSet;
142 }
143
144 /**
145 * Compare the previous search request and the new one, and return
146 * true if they are equal. We compare every field but the MessageID.
147 *
148 * @param request The new SearchRequest
149 * @return true if both request are equal.
150 */
151 public boolean hasSameRequest( InternalSearchRequest request, LdapSession session )
152 {
153 // Compares the scope
154 if ( request.getScope() != previousSearchRequest.getScope() )
155 {
156 return false;
157 }
158
159 // Compares the sizeLimit
160 if ( request.getSizeLimit() != previousSearchRequest.getSizeLimit() )
161 {
162 return false;
163 }
164
165 // Compares the timeLimit
166 if ( request.getTimeLimit() != previousSearchRequest.getTimeLimit() )
167 {
168 return false;
169 }
170
171 // Compares the TypesOnly
172 if ( request.getTypesOnly() != previousSearchRequest.getTypesOnly() )
173 {
174 return false;
175 }
176
177 // Compares the deref aliases mode
178 if ( request.getDerefAliases() != previousSearchRequest.getDerefAliases() )
179 {
180 return false;
181 }
182
183 SchemaManager schemaManager =
184 session.getLdapServer().getDirectoryService().getSchemaManager();
185
186 // Compares the attributes
187 if ( request.getAttributes() == null )
188 {
189 if ( previousSearchRequest.getAttributes() != null )
190 {
191 return false;
192 }
193 }
194 else
195 {
196 if ( previousSearchRequest.getAttributes() == null )
197 {
198 return false;
199 }
200 else
201 {
202 // We have to normalize the attributes in order to compare them
203 if ( request.getAttributes().size() != previousSearchRequest.getAttributes().size() )
204 {
205 return false;
206 }
207
208 // Build the set of attributeType from both requests
209 Set<String> requestSet = buildAttributeSet( request, session, schemaManager );
210 Set<String> previousRequestSet = buildAttributeSet( previousSearchRequest, session, schemaManager );
211
212 // Check that both sets have the same size again after having converted
213 // the attributes to OID
214 if ( requestSet.size() != previousRequestSet.size() )
215 {
216 return false;
217 }
218
219 for ( String attribute:requestSet )
220 {
221 previousRequestSet.remove( attribute );
222 }
223
224 // The other set must be empty
225 if ( !previousRequestSet.isEmpty() )
226 {
227 return false;
228 }
229 }
230 }
231
232 // Compare the baseDN
233 try
234 {
235 request.getBase().normalize( schemaManager.getNormalizerMapping() );
236
237 if ( !previousSearchRequest.getBase().isNormalized() )
238 {
239 previousSearchRequest.getBase().normalize( schemaManager.getNormalizerMapping() );
240 }
241
242 if ( !request.getBase().equals( previousSearchRequest.getBase() ) )
243 {
244 return false;
245 }
246 }
247 catch ( LdapException le )
248 {
249 return false;
250 }
251
252 // Compare the filters
253 // Here, we assume the user hasn't changed the filter's order or content,
254 // as the filter is not normalized. This is a real problem, as the normalization
255 // phase is done in the interceptor chain, which is a bad decision wrt what we
256 // do here.
257 return true; //request.getFilter().equals( previousSearchRequest.getFilter() );
258 }
259
260
261 /**
262 * @return The current position in the cursor. This value is updated
263 * after each successful search request.
264 */
265 public int getCurrentPosition()
266 {
267 return currentPosition;
268 }
269
270
271 /**
272 * Set the new current position, incrementing it with the
273 * number of returned entries.
274 *
275 * @param returnedEntries The number of returned entries
276 */
277 public void incrementCurrentPosition( int returnedEntries )
278 {
279 this.currentPosition += returnedEntries;
280 }
281
282
283 /**
284 * @return The previous search request
285 */
286 public InternalSearchRequest getPreviousSearchRequest()
287 {
288 return previousSearchRequest;
289 }
290
291
292 /**
293 * @return The associated cursor
294 */
295 public EntryFilteringCursor getCursor()
296 {
297 return cursor;
298 }
299
300
301 /**
302 * Set the new cursor for this search request
303 * @param cursor The associated cursor
304 */
305 public void setCursor( EntryFilteringCursor cursor )
306 {
307 this.cursor = cursor;
308 }
309
310
311 /**
312 * @see Object#toString()
313 */
314 public String toString()
315 {
316 StringBuilder sb = new StringBuilder();
317
318 sb.append( "PagedSearch context : <" );
319 sb.append( StringTools.dumpBytes( cookie ) );
320 sb.append( ", " );
321 sb.append( currentPosition );
322 sb.append( ">" );
323
324 return sb.toString();
325 }
326 }