View Javadoc
1   /*
2    *   Licensed to the Apache Software Foundation (ASF) under one
3    *   or more contributor license agreements.  See the NOTICE file
4    *   distributed with this work for additional information
5    *   regarding copyright ownership.  The ASF licenses this file
6    *   to you under the Apache License, Version 2.0 (the
7    *   "License"); you may not use this file except in compliance
8    *   with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *   Unless required by applicable law or agreed to in writing,
13   *   software distributed under the License is distributed on an
14   *   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *   KIND, either express or implied.  See the License for the
16   *   specific language governing permissions and limitations
17   *   under the License.
18   *
19   */
20  package org.apache.directory.server.ldap.handlers.controls;
21  
22  
23  import java.util.HashSet;
24  import java.util.Set;
25  import java.util.concurrent.atomic.AtomicInteger;
26  
27  import org.apache.directory.api.asn1.ber.tlv.BerValue;
28  import org.apache.directory.api.ldap.model.constants.SchemaConstants;
29  import org.apache.directory.api.ldap.model.cursor.Cursor;
30  import org.apache.directory.api.ldap.model.entry.Entry;
31  import org.apache.directory.api.ldap.model.exception.LdapException;
32  import org.apache.directory.api.ldap.model.message.SearchRequest;
33  import org.apache.directory.api.ldap.model.name.Dn;
34  import org.apache.directory.api.ldap.model.schema.AttributeType;
35  import org.apache.directory.api.ldap.model.schema.SchemaManager;
36  import org.apache.directory.api.util.Strings;
37  import org.apache.directory.server.ldap.LdapSession;
38  
39  
40  /**
41   * The structure which stores the informations relative to the pagedSearch control.
42   * They are associated to a cookie, stored into the session and associated to an
43   * instance of this class.
44   *
45   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
46   */
47  public class PagedSearchContext
48  {
49      /** The previous search request */
50      private SearchRequest previousSearchRequest;
51  
52      /** The current position in the cursor */
53      private int currentPosition;
54  
55      /** The cookie key */
56      private byte[] cookie;
57  
58      /** The integer value for the cookie */
59      private AtomicInteger cookieValue;
60  
61      /** The associated cursor for the current search request */
62      private Cursor<Entry> cursor;
63  
64  
65      /**
66       * Creates a new instance of this class, storing the SearchRequest into it.
67       * 
68       * @param searchRequest The SearchRequest
69       */
70      public PagedSearchContext( SearchRequest searchRequest )
71      {
72          previousSearchRequest = searchRequest;
73          currentPosition = 0;
74  
75          // We compute a key for this cookie. It combines the search request
76          // and some time seed, in order to avoid possible collisions, as
77          // a user may send more than one PagedSearch on the same session.
78          cookieValue = new AtomicInteger( searchRequest.getMessageId() << 16 );
79  
80          cookie = BerValue.getBytes( cookieValue.get() );
81      }
82  
83  
84      /**
85       * Compute a new key for this cookie, based on the current searchRequest
86       * hashCode and the current position. This value will be stored into the
87       * session, and will permit the retrieval of this instance.
88       *
89       * @return The new cookie's key
90       */
91      public byte[] getCookie()
92      {
93          return cookie;
94      }
95  
96  
97      public int getCookieValue()
98      {
99          return cookieValue.get();
100     }
101 
102 
103     /**
104      * Compute a new cookie, if the previous one already exists. This
105      * is unlikely, as we are based on some time seed, but just in case,
106      * this method will generate a new one.
107      * @return The new cookie
108      */
109     public byte[] getNewCookie()
110     {
111         cookie = BerValue.getBytes( cookieValue.incrementAndGet() );
112 
113         return cookie;
114     }
115 
116 
117     /**
118      * Build a set of OIDs from the list of attributes we have in the search request
119      */
120     private Set<String> buildAttributeSet( SearchRequest request,
121         SchemaManager schemaManager )
122     {
123         Set<String> requestSet = new HashSet<>();
124 
125         // Build the set of attributeType from the attributes
126         for ( String attribute : request.getAttributes() )
127         {
128             try
129             {
130                 AttributeType at = schemaManager.lookupAttributeTypeRegistry( attribute );
131                 requestSet.add( at.getOid() );
132             }
133             catch ( LdapException le )
134             {
135                 // Deal with special attributes : '*', '+' and '1.1'
136                 if ( attribute.equals( SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES )
137                     || attribute.equals( SchemaConstants.ALL_USER_ATTRIBUTES )
138                     || attribute.equals( SchemaConstants.NO_ATTRIBUTE ) )
139                 {
140                     requestSet.add( attribute );
141                 }
142 
143                 // Otherwise, don't add the attribute to the set
144             }
145         }
146 
147         return requestSet;
148     }
149 
150 
151     /**
152      * Compare the previous search request and the new one, and return
153      * true if they are equal. We compare every field but the MessageID.
154      *
155      * @param request The new SearchRequest
156      * @param session The LdapSession in use
157      * @return true if both request are equal.
158      */
159     public boolean hasSameRequest( SearchRequest request, LdapSession session )
160     {
161         // Compares the scope
162         if ( request.getScope() != previousSearchRequest.getScope() )
163         {
164             return false;
165         }
166 
167         // Compares the sizeLimit
168         if ( request.getSizeLimit() != previousSearchRequest.getSizeLimit() )
169         {
170             return false;
171         }
172 
173         // Compares the timeLimit
174         if ( request.getTimeLimit() != previousSearchRequest.getTimeLimit() )
175         {
176             return false;
177         }
178 
179         // Compares the TypesOnly
180         if ( request.getTypesOnly() != previousSearchRequest.getTypesOnly() )
181         {
182             return false;
183         }
184 
185         // Compares the deref aliases mode
186         if ( request.getDerefAliases() != previousSearchRequest.getDerefAliases() )
187         {
188             return false;
189         }
190 
191         SchemaManager schemaManager =
192             session.getLdapServer().getDirectoryService().getSchemaManager();
193 
194         // Compares the attributes
195         if ( request.getAttributes() == null )
196         {
197             if ( previousSearchRequest.getAttributes() != null )
198             {
199                 return false;
200             }
201         }
202         else
203         {
204             if ( previousSearchRequest.getAttributes() == null )
205             {
206                 return false;
207             }
208             else
209             {
210                 // We have to normalize the attributes in order to compare them
211                 if ( request.getAttributes().size() != previousSearchRequest.getAttributes().size() )
212                 {
213                     return false;
214                 }
215 
216                 // Build the set of attributeType from both requests
217                 Set<String> requestSet = buildAttributeSet( request, schemaManager );
218                 Set<String> previousRequestSet = buildAttributeSet( previousSearchRequest, schemaManager );
219 
220                 // Check that both sets have the same size again after having converted
221                 // the attributes to OID
222                 if ( requestSet.size() != previousRequestSet.size() )
223                 {
224                     return false;
225                 }
226 
227                 for ( String attribute : requestSet )
228                 {
229                     previousRequestSet.remove( attribute );
230                 }
231 
232                 // The other set must be empty
233                 if ( !previousRequestSet.isEmpty() )
234                 {
235                     return false;
236                 }
237             }
238         }
239 
240         // Compare the baseDN
241         try
242         {
243             if ( !request.getBase().isSchemaAware() )
244             {
245                 request.setBase( new Dn( schemaManager, request.getBase() ) );
246             }
247 
248             if ( !previousSearchRequest.getBase().isSchemaAware() )
249             {
250                 previousSearchRequest.setBase( new Dn( schemaManager, previousSearchRequest.getBase() ) );
251             }
252 
253             if ( !request.getBase().equals( previousSearchRequest.getBase() ) )
254             {
255                 return false;
256             }
257         }
258         catch ( LdapException le )
259         {
260             return false;
261         }
262 
263         // Compare the filters
264         // Here, we assume the user hasn't changed the filter's order or content,
265         // as the filter is not normalized. This is a real problem, as the normalization
266         // phase is done in the interceptor chain, which is a bad decision wrt what we
267         // do here.
268         return true; //request.getFilter().equals( previousSearchRequest.getFilter() );
269     }
270 
271 
272     /**
273      * @return The current position in the cursor. This value is updated
274      * after each successful search request.
275      */
276     public int getCurrentPosition()
277     {
278         return currentPosition;
279     }
280 
281 
282     /**
283      * Set the new current position, incrementing it with the
284      * number of returned entries.
285      *
286      * @param returnedEntries The number of returned entries
287      */
288     public void incrementCurrentPosition( int returnedEntries )
289     {
290         this.currentPosition += returnedEntries;
291     }
292 
293 
294     /**
295      * @return The previous search request
296      */
297     public SearchRequest getPreviousSearchRequest()
298     {
299         return previousSearchRequest;
300     }
301 
302 
303     /**
304      * @return The associated cursor
305      */
306     public Cursor<Entry> getCursor()
307     {
308         return cursor;
309     }
310 
311 
312     /**
313      * Set the new cursor for this search request
314      * @param cursor The associated cursor
315      */
316     public void setCursor( Cursor<Entry> cursor )
317     {
318         this.cursor = cursor;
319     }
320 
321 
322     /**
323      * @see Object#toString()
324      */
325     public String toString()
326     {
327         StringBuilder sb = new StringBuilder();
328 
329         sb.append( "PagedSearch context : <" );
330         sb.append( Strings.dumpBytes( cookie ) );
331         sb.append( ", " );
332         sb.append( currentPosition );
333         sb.append( ">" );
334 
335         return sb.toString();
336     }
337 }