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 }