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