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 */ 020package org.apache.directory.api.ldap.model.message; 021 022 023import java.text.ParseException; 024import java.util.ArrayList; 025import java.util.Arrays; 026import java.util.Collections; 027import java.util.List; 028 029import org.apache.directory.api.ldap.model.exception.LdapException; 030import org.apache.directory.api.ldap.model.exception.LdapProtocolErrorException; 031import org.apache.directory.api.ldap.model.filter.BranchNormalizedVisitor; 032import org.apache.directory.api.ldap.model.filter.ExprNode; 033import org.apache.directory.api.ldap.model.filter.FilterParser; 034import org.apache.directory.api.ldap.model.name.Dn; 035import org.apache.directory.api.util.Strings; 036 037 038/** 039 * SearchRequest implementation. 040 * 041 * @author <a href="mailto:dev@directory.apache.org"> Apache Directory Project</a> 042 */ 043public class SearchRequestImpl extends AbstractAbandonableRequest implements SearchRequest 044{ 045 static final long serialVersionUID = -5655881944020886218L; 046 047 /** Search base distinguished name */ 048 private Dn baseDn; 049 050 /** Search filter expression tree's root node */ 051 private ExprNode filterNode; 052 053 /** Search scope enumeration value */ 054 private SearchScope scope; 055 056 /** Types only return flag */ 057 private boolean typesOnly; 058 059 /** Max size in entries to return */ 060 private long sizeLimit; 061 062 /** Max seconds to wait for search to complete */ 063 private int timeLimit; 064 065 /** Alias dereferencing mode enumeration value (default to DEREF_ALWAYS) */ 066 private AliasDerefMode aliasDerefMode = AliasDerefMode.DEREF_ALWAYS; 067 068 /** Attributes to return */ 069 private List<String> attributes = new ArrayList<String>(); 070 071 /** The final result containing SearchResponseDone response */ 072 private SearchResultDone response; 073 074 /** A flag set to tell the search what to do wth referrals */ 075 private ReferralsPolicyEnum referralHandling = ReferralsPolicyEnum.THROW; 076 077 078 // ----------------------------------------------------------------------- 079 // Constructors 080 // ----------------------------------------------------------------------- 081 /** 082 * Creates a SearcRequest implementing object used to search the 083 * DIT. 084 */ 085 public SearchRequestImpl() 086 { 087 super( -1, MessageTypeEnum.SEARCH_REQUEST ); 088 } 089 090 091 // ------------------------------------------------------------------------ 092 // SearchRequest Interface Method Implementations 093 // ------------------------------------------------------------------------ 094 095 /** 096 * {@inheritDoc} 097 */ 098 public List<String> getAttributes() 099 { 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 if ( attr != null ) 374 { 375 hash = hash + attr.hashCode(); 376 } 377 } 378 } 379 380 BranchNormalizedVisitor visitor = new BranchNormalizedVisitor(); 381 filterNode.accept( visitor ); 382 hash = hash * 17 + filterNode.toString().hashCode(); 383 hash = hash * 17 + super.hashCode(); 384 385 return hash; 386 } 387 388 389 /** 390 * Checks to see if two search requests are equal. The Lockable properties 391 * and the get/set context specific parameters are not consulted to 392 * determine equality. The filter expression tree comparison will normalize 393 * the child order of filter branch nodes then generate a string 394 * representation which is comparable. For the time being this is a very 395 * costly operation. 396 * 397 * @param obj the object to check for equality to this SearchRequest 398 * @return true if the obj is a SearchRequest and equals this SearchRequest, 399 * false otherwise 400 */ 401 public boolean equals( Object obj ) 402 { 403 if ( obj == this ) 404 { 405 return true; 406 } 407 408 if ( !super.equals( obj ) ) 409 { 410 return false; 411 } 412 413 SearchRequest req = ( SearchRequest ) obj; 414 415 if ( !req.getBase().equals( baseDn ) ) 416 { 417 return false; 418 } 419 420 if ( req.getDerefAliases() != aliasDerefMode ) 421 { 422 return false; 423 } 424 425 if ( req.getScope() != scope ) 426 { 427 return false; 428 } 429 430 if ( req.getSizeLimit() != sizeLimit ) 431 { 432 return false; 433 } 434 435 if ( req.getTimeLimit() != timeLimit ) 436 { 437 return false; 438 } 439 440 if ( req.getTypesOnly() != typesOnly ) 441 { 442 return false; 443 } 444 445 if ( req.getAttributes() == null && attributes != null && attributes.size() > 0 ) 446 { 447 return false; 448 } 449 450 if ( req.getAttributes() != null && attributes == null && req.getAttributes().size() > 0 ) 451 { 452 return false; 453 } 454 455 if ( req.getAttributes() != null && attributes != null ) 456 { 457 if ( req.getAttributes().size() != attributes.size() ) 458 { 459 return false; 460 } 461 462 for ( String attribute : attributes ) 463 { 464 if ( !req.getAttributes().contains( attribute ) ) 465 { 466 return false; 467 } 468 } 469 } 470 471 BranchNormalizedVisitor visitor = new BranchNormalizedVisitor(); 472 req.getFilter().accept( visitor ); 473 filterNode.accept( visitor ); 474 475 String myFilterString = filterNode.toString(); 476 String reqFilterString = req.getFilter().toString(); 477 478 return myFilterString.equals( reqFilterString ); 479 } 480 481 482 /** 483 * Return a string the represent a SearchRequest 484 * {@inheritDoc} 485 */ 486 public String toString() 487 { 488 StringBuilder sb = new StringBuilder(); 489 490 sb.append( " SearchRequest\n" ); 491 sb.append( " baseDn : '" ).append( baseDn ).append( "'\n" ); 492 493 if ( filterNode != null ) 494 { 495 sb.append( " filter : '" ); 496 sb.append( filterNode.toString() ); 497 sb.append( "'\n" ); 498 } 499 500 sb.append( " scope : " ); 501 502 switch ( scope ) 503 { 504 case OBJECT: 505 sb.append( "base object" ); 506 break; 507 508 case ONELEVEL: 509 sb.append( "single level" ); 510 break; 511 512 case SUBTREE: 513 sb.append( "whole subtree" ); 514 break; 515 } 516 517 sb.append( '\n' ); 518 519 sb.append( " typesOnly : " ).append( typesOnly ).append( '\n' ); 520 521 sb.append( " Size Limit : " ); 522 523 if ( sizeLimit == 0L ) 524 { 525 sb.append( "no limit" ); 526 } 527 else 528 { 529 sb.append( sizeLimit ); 530 } 531 532 sb.append( '\n' ); 533 534 sb.append( " Time Limit : " ); 535 536 if ( timeLimit == 0 ) 537 { 538 sb.append( "no limit" ); 539 } 540 else 541 { 542 sb.append( timeLimit ); 543 } 544 545 sb.append( '\n' ); 546 547 sb.append( " Deref Aliases : " ); 548 549 switch ( aliasDerefMode ) 550 { 551 case NEVER_DEREF_ALIASES: 552 sb.append( "never Deref Aliases" ); 553 break; 554 555 case DEREF_IN_SEARCHING: 556 sb.append( "deref In Searching" ); 557 break; 558 559 case DEREF_FINDING_BASE_OBJ: 560 sb.append( "deref Finding Base Obj" ); 561 break; 562 563 case DEREF_ALWAYS: 564 sb.append( "deref Always" ); 565 break; 566 } 567 568 sb.append( '\n' ); 569 sb.append( " attributes : " ); 570 571 boolean isFirst = true; 572 573 if ( attributes != null ) 574 { 575 for ( String attribute : attributes ) 576 { 577 if ( isFirst ) 578 { 579 isFirst = false; 580 } 581 else 582 { 583 sb.append( ", " ); 584 } 585 586 sb.append( '\'' ).append( attribute ).append( '\'' ); 587 } 588 } 589 590 sb.append( '\n' ); 591 592 // The controls 593 sb.append( super.toString() ); 594 595 return super.toString( sb.toString() ); 596 } 597 598 599 /** 600 * {@inheritDoc} 601 */ 602 public boolean isFollowReferrals() 603 { 604 return referralHandling == ReferralsPolicyEnum.FOLLOW; 605 } 606 607 608 /** 609 * {@inheritDoc} 610 */ 611 public SearchRequest followReferrals() 612 { 613 referralHandling = ReferralsPolicyEnum.FOLLOW; 614 615 return this; 616 } 617 618 619 /** 620 * {@inheritDoc} 621 */ 622 public boolean isIgnoreReferrals() 623 { 624 return referralHandling == ReferralsPolicyEnum.IGNORE; 625 } 626 627 628 /** 629 * {@inheritDoc} 630 */ 631 public SearchRequest ignoreReferrals() 632 { 633 referralHandling = ReferralsPolicyEnum.IGNORE; 634 635 return this; 636 } 637}