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 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}