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<>(); 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 @Override 099 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}