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.api.ldap.model.message;
21  
22  
23  import java.text.ParseException;
24  import java.util.ArrayList;
25  import java.util.Arrays;
26  import java.util.Collections;
27  import java.util.List;
28  
29  import org.apache.directory.api.ldap.model.exception.LdapException;
30  import org.apache.directory.api.ldap.model.exception.LdapProtocolErrorException;
31  import org.apache.directory.api.ldap.model.filter.BranchNormalizedVisitor;
32  import org.apache.directory.api.ldap.model.filter.ExprNode;
33  import org.apache.directory.api.ldap.model.filter.FilterParser;
34  import org.apache.directory.api.ldap.model.name.Dn;
35  import org.apache.directory.api.util.Strings;
36  
37  
38  /**
39   * SearchRequest implementation.
40   * 
41   * @author <a href="mailto:dev@directory.apache.org"> Apache Directory Project</a>
42   */
43  public class SearchRequestImpl extends AbstractAbandonableRequest implements SearchRequest
44  {
45      static final long serialVersionUID = -5655881944020886218L;
46  
47      /** Search base distinguished name */
48      private Dn baseDn;
49  
50      /** Search filter expression tree's root node */
51      private ExprNode filterNode;
52  
53      /** Search scope enumeration value */
54      private SearchScope scope;
55  
56      /** Types only return flag */
57      private boolean typesOnly;
58  
59      /** Max size in entries to return */
60      private long sizeLimit;
61  
62      /** Max seconds to wait for search to complete */
63      private int timeLimit;
64  
65      /** Alias dereferencing mode enumeration value (default to DEREF_ALWAYS) */
66      private AliasDerefMode aliasDerefMode = AliasDerefMode.DEREF_ALWAYS;
67  
68      /** Attributes to return */
69      private List<String> attributes = new ArrayList<String>();
70  
71      /** The final result containing SearchResponseDone response */
72      private SearchResultDone response;
73      
74      /** A flag set to tell the search what to do wth referrals */
75      private ReferralsPolicyEnum referralHandling = ReferralsPolicyEnum.THROW;
76  
77  
78      // -----------------------------------------------------------------------
79      // Constructors
80      // -----------------------------------------------------------------------
81      /**
82       * Creates a SearcRequest implementing object used to search the
83       * DIT.
84       */
85      public SearchRequestImpl()
86      {
87          super( -1, MessageTypeEnum.SEARCH_REQUEST );
88      }
89  
90  
91      // ------------------------------------------------------------------------
92      // SearchRequest Interface Method Implementations
93      // ------------------------------------------------------------------------
94  
95      /**
96       * {@inheritDoc}
97       */
98      public List<String> getAttributes()
99      {
100         return Collections.unmodifiableList( attributes );
101     }
102 
103 
104     /**
105      * Gets the search base as a distinguished name.
106      * 
107      * @return the search base
108      */
109     public Dn getBase()
110     {
111         return baseDn;
112     }
113 
114 
115     /**
116      * {@inheritDoc}
117      */
118     public SearchRequest setBase( Dn base )
119     {
120         baseDn = base;
121 
122         return this;
123     }
124 
125 
126     /**
127      * {@inheritDoc}
128      */
129     public AliasDerefMode getDerefAliases()
130     {
131         return aliasDerefMode;
132     }
133 
134 
135     /**
136      * {@inheritDoc}
137      */
138     public SearchRequest setDerefAliases( AliasDerefMode aliasDerefAliases )
139     {
140         this.aliasDerefMode = aliasDerefAliases;
141 
142         return this;
143     }
144 
145 
146     /**
147      * {@inheritDoc}
148      */
149     public ExprNode getFilter()
150     {
151         return filterNode;
152     }
153 
154 
155     /**
156      * {@inheritDoc}
157      */
158     public SearchRequest setFilter( ExprNode filter )
159     {
160         this.filterNode = filter;
161         return this;
162     }
163 
164 
165     /**
166      * {@inheritDoc}
167      */
168     public SearchRequest setFilter( String filter ) throws LdapException
169     {
170         try
171         {
172             filterNode = FilterParser.parse( Strings.getBytesUtf8( filter ) );
173         }
174         catch ( ParseException pe )
175         {
176             String msg = "The filter " + filter + " is invalid.";
177             throw new LdapProtocolErrorException( msg, pe );
178         }
179 
180         return this;
181     }
182 
183 
184     /**
185      * {@inheritDoc}
186      */
187     public MessageTypeEnum[] getResponseTypes()
188     {
189         return RESPONSE_TYPES.clone();
190     }
191 
192 
193     /**
194      * {@inheritDoc}
195      */
196     public SearchScope getScope()
197     {
198         return scope;
199     }
200 
201 
202     /**
203      * {@inheritDoc}
204      */
205     public SearchRequest setScope( SearchScope scope )
206     {
207         this.scope = scope;
208 
209         return this;
210     }
211 
212 
213     /**
214      * {@inheritDoc}
215      */
216     public long getSizeLimit()
217     {
218         return sizeLimit;
219     }
220 
221 
222     /**
223      * {@inheritDoc}
224      */
225     public SearchRequest setSizeLimit( long entriesMax )
226     {
227         sizeLimit = entriesMax;
228 
229         return this;
230     }
231 
232 
233     /**
234      * {@inheritDoc}
235      */
236     public int getTimeLimit()
237     {
238         return timeLimit;
239     }
240 
241 
242     /**
243      * {@inheritDoc}
244      */
245     public SearchRequest setTimeLimit( int secondsMax )
246     {
247         timeLimit = secondsMax;
248 
249         return this;
250     }
251 
252 
253     /**
254      * {@inheritDoc}
255      */
256     public boolean getTypesOnly()
257     {
258         return typesOnly;
259     }
260 
261 
262     /**
263      * {@inheritDoc}
264      */
265     public SearchRequest setTypesOnly( boolean typesOnly )
266     {
267         this.typesOnly = typesOnly;
268 
269         return this;
270     }
271 
272 
273     /**
274      * {@inheritDoc}
275      */
276     public SearchRequest addAttributes( String... attributesToAdd )
277     {
278         this.attributes.addAll( Arrays.asList( attributesToAdd ) );
279 
280         return this;
281     }
282 
283 
284     /**
285      * {@inheritDoc}
286      */
287     public SearchRequest removeAttribute( String attribute )
288     {
289         attributes.remove( attribute );
290 
291         return this;
292     }
293 
294 
295     /**
296      * {@inheritDoc}
297      */
298     public SearchResultDone getResultResponse()
299     {
300         if ( response == null )
301         {
302             response = new SearchResultDoneImpl( getMessageId() );
303         }
304 
305         return response;
306     }
307 
308 
309     /**
310      * {@inheritDoc}
311      */
312     public SearchRequest setMessageId( int messageId )
313     {
314         super.setMessageId( messageId );
315 
316         return this;
317     }
318 
319 
320     /**
321      * {@inheritDoc}
322      */
323     public SearchRequest addControl( Control control )
324     {
325         return ( SearchRequest ) super.addControl( control );
326     }
327 
328 
329     /**
330      * {@inheritDoc}
331      */
332     public SearchRequest addAllControls( Control[] controls )
333     {
334         return ( SearchRequest ) super.addAllControls( controls );
335     }
336 
337 
338     /**
339      * {@inheritDoc}
340      */
341     public SearchRequest removeControl( Control control )
342     {
343         return ( SearchRequest ) super.removeControl( control );
344     }
345 
346 
347     /**
348      * {@inheritDoc}
349      */
350     @Override
351     public int hashCode()
352     {
353         int hash = 37;
354 
355         if ( baseDn != null )
356         {
357             hash = hash * 17 + baseDn.hashCode();
358         }
359 
360         hash = hash * 17 + aliasDerefMode.hashCode();
361         hash = hash * 17 + scope.hashCode();
362         hash = hash * 17 + Long.valueOf( sizeLimit ).hashCode();
363         hash = hash * 17 + timeLimit;
364         hash = hash * 17 + ( typesOnly ? 0 : 1 );
365 
366         if ( attributes != null )
367         {
368             hash = hash * 17 + attributes.size();
369 
370             // Order doesn't matter, thus just add hashCode
371             for ( String attr : attributes )
372             {
373                 hash = hash + attr.hashCode();
374             }
375         }
376 
377         BranchNormalizedVisitor visitor = new BranchNormalizedVisitor();
378         filterNode.accept( visitor );
379         hash = hash * 17 + filterNode.toString().hashCode();
380         hash = hash * 17 + super.hashCode();
381 
382         return hash;
383     }
384 
385 
386     /**
387      * Checks to see if two search requests are equal. The Lockable properties
388      * and the get/set context specific parameters are not consulted to
389      * determine equality. The filter expression tree comparison will normalize
390      * the child order of filter branch nodes then generate a string
391      * representation which is comparable. For the time being this is a very
392      * costly operation.
393      * 
394      * @param obj the object to check for equality to this SearchRequest
395      * @return true if the obj is a SearchRequest and equals this SearchRequest,
396      *         false otherwise
397      */
398     public boolean equals( Object obj )
399     {
400         if ( obj == this )
401         {
402             return true;
403         }
404 
405         if ( !super.equals( obj ) )
406         {
407             return false;
408         }
409 
410         SearchRequest req = ( SearchRequest ) obj;
411 
412         if ( !req.getBase().equals( baseDn ) )
413         {
414             return false;
415         }
416 
417         if ( req.getDerefAliases() != aliasDerefMode )
418         {
419             return false;
420         }
421 
422         if ( req.getScope() != scope )
423         {
424             return false;
425         }
426 
427         if ( req.getSizeLimit() != sizeLimit )
428         {
429             return false;
430         }
431 
432         if ( req.getTimeLimit() != timeLimit )
433         {
434             return false;
435         }
436 
437         if ( req.getTypesOnly() != typesOnly )
438         {
439             return false;
440         }
441 
442         if ( req.getAttributes() == null && attributes != null && attributes.size() > 0 )
443         {
444             return false;
445         }
446 
447         if ( req.getAttributes() != null && attributes == null && req.getAttributes().size() > 0 )
448         {
449             return false;
450         }
451 
452         if ( req.getAttributes() != null && attributes != null )
453         {
454             if ( req.getAttributes().size() != attributes.size() )
455             {
456                 return false;
457             }
458 
459             for ( String attribute : attributes )
460             {
461                 if ( !req.getAttributes().contains( attribute ) )
462                 {
463                     return false;
464                 }
465             }
466         }
467 
468         BranchNormalizedVisitor visitor = new BranchNormalizedVisitor();
469         req.getFilter().accept( visitor );
470         filterNode.accept( visitor );
471 
472         String myFilterString = filterNode.toString();
473         String reqFilterString = req.getFilter().toString();
474 
475         return myFilterString.equals( reqFilterString );
476     }
477 
478 
479     /**
480      * Return a string the represent a SearchRequest
481      * {@inheritDoc}
482      */
483     public String toString()
484     {
485         StringBuilder sb = new StringBuilder();
486 
487         sb.append( "    SearchRequest\n" );
488         sb.append( "        baseDn : '" ).append( baseDn ).append( "'\n" );
489 
490         if ( filterNode != null )
491         {
492             sb.append( "        filter : '" );
493             sb.append( filterNode.toString() );
494             sb.append( "'\n" );
495         }
496 
497         sb.append( "        scope : " );
498 
499         switch ( scope )
500         {
501             case OBJECT:
502                 sb.append( "base object" );
503                 break;
504 
505             case ONELEVEL:
506                 sb.append( "single level" );
507                 break;
508 
509             case SUBTREE:
510                 sb.append( "whole subtree" );
511                 break;
512         }
513 
514         sb.append( '\n' );
515 
516         sb.append( "        typesOnly : " ).append( typesOnly ).append( '\n' );
517 
518         sb.append( "        Size Limit : " );
519 
520         if ( sizeLimit == 0L )
521         {
522             sb.append( "no limit" );
523         }
524         else
525         {
526             sb.append( sizeLimit );
527         }
528 
529         sb.append( '\n' );
530 
531         sb.append( "        Time Limit : " );
532 
533         if ( timeLimit == 0 )
534         {
535             sb.append( "no limit" );
536         }
537         else
538         {
539             sb.append( timeLimit );
540         }
541 
542         sb.append( '\n' );
543 
544         sb.append( "        Deref Aliases : " );
545 
546         switch ( aliasDerefMode )
547         {
548             case NEVER_DEREF_ALIASES:
549                 sb.append( "never Deref Aliases" );
550                 break;
551 
552             case DEREF_IN_SEARCHING:
553                 sb.append( "deref In Searching" );
554                 break;
555 
556             case DEREF_FINDING_BASE_OBJ:
557                 sb.append( "deref Finding Base Obj" );
558                 break;
559 
560             case DEREF_ALWAYS:
561                 sb.append( "deref Always" );
562                 break;
563         }
564 
565         sb.append( '\n' );
566         sb.append( "        attributes : " );
567 
568         boolean isFirst = true;
569 
570         if ( attributes != null )
571         {
572             for ( String attribute : attributes )
573             {
574                 if ( isFirst )
575                 {
576                     isFirst = false;
577                 }
578                 else
579                 {
580                     sb.append( ", " );
581                 }
582 
583                 sb.append( '\'' ).append( attribute ).append( '\'' );
584             }
585         }
586 
587         sb.append( '\n' );
588 
589         // The controls
590         sb.append( super.toString() );
591 
592         return super.toString( sb.toString() );
593     }
594     
595     
596     /**
597      * {@inheritDoc}
598      */
599     public boolean isFollowReferrals()
600     {
601         return referralHandling == ReferralsPolicyEnum.FOLLOW;
602     }
603 
604 
605     /**
606      * {@inheritDoc}
607      */
608     public SearchRequest followReferrals()
609     {
610         referralHandling = ReferralsPolicyEnum.FOLLOW;
611         
612         return this;
613     }
614     
615     
616     /**
617      * {@inheritDoc}
618      */
619     public boolean isIgnoreReferrals()
620     {
621         return referralHandling == ReferralsPolicyEnum.IGNORE;
622     }
623 
624 
625     /**
626      * {@inheritDoc}
627      */
628     public SearchRequest ignoreReferrals()
629     {
630         referralHandling = ReferralsPolicyEnum.IGNORE;
631         
632         return this;
633     }
634 }