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 */ 020 package org.apache.directory.shared.ldap.message; 021 022 023 import java.util.ArrayList; 024 import java.util.Collections; 025 import java.util.Iterator; 026 import java.util.List; 027 028 import org.apache.directory.shared.ldap.codec.LdapConstants; 029 import org.apache.directory.shared.ldap.codec.MessageTypeEnum; 030 import org.apache.directory.shared.ldap.filter.BranchNormalizedVisitor; 031 import org.apache.directory.shared.ldap.filter.ExprNode; 032 import org.apache.directory.shared.ldap.filter.SearchScope; 033 import org.apache.directory.shared.ldap.message.internal.InternalResultResponse; 034 import org.apache.directory.shared.ldap.message.internal.InternalSearchRequest; 035 import org.apache.directory.shared.ldap.message.internal.InternalSearchResponseDone; 036 import org.apache.directory.shared.ldap.name.DN; 037 038 039 /** 040 * Lockable SearchRequest implementation. 041 * 042 * @author <a href="mailto:dev@directory.apache.org"> Apache Directory Project</a> 043 * @version $Rev: 919009 $ 044 */ 045 public class SearchRequestImpl extends AbstractAbandonableRequest implements InternalSearchRequest 046 { 047 static final long serialVersionUID = -5655881944020886218L; 048 049 /** Search base distinguished name */ 050 private DN baseDn; 051 052 /** Search filter expression tree's root node */ 053 private ExprNode filter; 054 055 /** Search scope enumeration value */ 056 private SearchScope scope; 057 058 /** Types only return flag */ 059 private boolean typesOnly; 060 061 /** Max size in entries to return */ 062 private long sizeLimit; 063 064 /** Max seconds to wait for search to complete */ 065 private int timeLimit; 066 067 /** Alias dereferencing mode enumeration value */ 068 private AliasDerefMode aliasDerefMode; 069 070 /** Attributes to return */ 071 private List<String> attributes = new ArrayList<String>(); 072 073 /** The final result containing SearchResponseDone response */ 074 private InternalSearchResponseDone response; 075 076 077 // ------------------------------------------------------------------------ 078 // Constructors 079 // ------------------------------------------------------------------------ 080 081 /** 082 * Creates a Lockable SearcRequest implementing object used to search the 083 * DIT. 084 * 085 * @param id 086 * the sequential message identifier 087 */ 088 public SearchRequestImpl(final int id) 089 { 090 super( id, MessageTypeEnum.SEARCH_REQUEST ); 091 } 092 093 094 // ------------------------------------------------------------------------ 095 // SearchRequest Interface Method Implementations 096 // ------------------------------------------------------------------------ 097 098 /** 099 * Gets a list of the attributes to be returned from each entry which 100 * matches the search filter. There are two special values which may be 101 * used: an empty list with no attributes, and the attribute description 102 * string "*". Both of these signify that all user attributes are to be 103 * returned. (The "*" allows the client to request all user attributes in 104 * addition to specific operational attributes). Attributes MUST be named at 105 * most once in the list, and are returned at most once in an entry. If 106 * there are attribute descriptions in the list which are not recognized, 107 * they are ignored by the server. If the client does not want any 108 * attributes returned, it can specify a list containing only the attribute 109 * with OID "1.1". This OID was chosen arbitrarily and does not correspond 110 * to any attribute in use. Client implementors should note that even if all 111 * user attributes are requested, some attributes of the entry may not be 112 * included in search results due to access control or other restrictions. 113 * Furthermore, servers will not return operational attributes, such as 114 * objectClasses or attributeTypes, unless they are listed by name, since 115 * there may be extremely large number of values for certain operational 116 * attributes. 117 * 118 * @return the collection of attributes to return for each entry 119 */ 120 public List<String> getAttributes() 121 { 122 return Collections.unmodifiableList( attributes ); 123 } 124 125 126 /** 127 * Gets the search base as a distinguished name. 128 * 129 * @return the search base 130 */ 131 public DN getBase() 132 { 133 return baseDn; 134 } 135 136 137 /** 138 * Sets the search base as a distinguished name. 139 * 140 * @param base 141 * the search base 142 */ 143 public void setBase( DN base ) 144 { 145 baseDn = base; 146 } 147 148 149 /** 150 * Gets the alias handling parameter. 151 * 152 * @return the alias handling parameter enumeration. 153 */ 154 public AliasDerefMode getDerefAliases() 155 { 156 return aliasDerefMode; 157 } 158 159 160 /** 161 * Sets the alias handling parameter. 162 * 163 * @param aliasDerefAliases 164 * the alias handling parameter enumeration. 165 */ 166 public void setDerefAliases( AliasDerefMode aliasDerefAliases ) 167 { 168 this.aliasDerefMode = aliasDerefAliases; 169 } 170 171 172 /** 173 * Gets the search filter associated with this search request. 174 * 175 * @return the expression node for the root of the filter expression tree. 176 */ 177 public ExprNode getFilter() 178 { 179 return filter; 180 } 181 182 183 /** 184 * Sets the search filter associated with this search request. 185 * 186 * @param filter 187 * the expression node for the root of the filter expression 188 * tree. 189 */ 190 public void setFilter( ExprNode filter ) 191 { 192 this.filter = filter; 193 } 194 195 196 /** 197 * Gets the different response types generated by a search request. 198 * 199 * @return the RESPONSE_TYPES array 200 * @see #RESPONSE_TYPES 201 */ 202 public MessageTypeEnum[] getResponseTypes() 203 { 204 return RESPONSE_TYPES.clone(); 205 } 206 207 208 /** 209 * Gets the search scope parameter enumeration. 210 * 211 * @return the scope enumeration parameter. 212 */ 213 public SearchScope getScope() 214 { 215 return scope; 216 } 217 218 219 /** 220 * Sets the search scope parameter enumeration. 221 * 222 * @param scope 223 * the scope enumeration parameter. 224 */ 225 public void setScope( SearchScope scope ) 226 { 227 this.scope = scope; 228 } 229 230 231 /** 232 * A sizelimit that restricts the maximum number of entries to be returned 233 * as a result of the search. A value of 0 in this field indicates that no 234 * client-requested sizelimit restrictions are in effect for the search. 235 * Servers may enforce a maximum number of entries to return. 236 * 237 * @return search size limit. 238 */ 239 public long getSizeLimit() 240 { 241 return sizeLimit; 242 } 243 244 245 /** 246 * Sets sizelimit that restricts the maximum number of entries to be 247 * returned as a result of the search. A value of 0 in this field indicates 248 * that no client-requested sizelimit restrictions are in effect for the 249 * search. Servers may enforce a maximum number of entries to return. 250 * 251 * @param entriesMax 252 * maximum search result entries to return. 253 */ 254 public void setSizeLimit( long entriesMax ) 255 { 256 sizeLimit = entriesMax; 257 } 258 259 260 /** 261 * Gets the timelimit that restricts the maximum time (in seconds) allowed 262 * for a search. A value of 0 in this field indicates that no client- 263 * requested timelimit restrictions are in effect for the search. 264 * 265 * @return the search time limit in seconds. 266 */ 267 public int getTimeLimit() 268 { 269 return timeLimit; 270 } 271 272 273 /** 274 * Sets the timelimit that restricts the maximum time (in seconds) allowed 275 * for a search. A value of 0 in this field indicates that no client- 276 * requested timelimit restrictions are in effect for the search. 277 * 278 * @param secondsMax 279 * the search time limit in seconds. 280 */ 281 public void setTimeLimit( int secondsMax ) 282 { 283 timeLimit = secondsMax; 284 } 285 286 287 /** 288 * An indicator as to whether search results will contain both attribute 289 * types and values, or just attribute types. Setting this field to TRUE 290 * causes only attribute types (no values) to be returned. Setting this 291 * field to FALSE causes both attribute types and values to be returned. 292 * 293 * @return true for only types, false for types and values. 294 */ 295 public boolean getTypesOnly() 296 { 297 return typesOnly; 298 } 299 300 301 /** 302 * An indicator as to whether search results will contain both attribute 303 * types and values, or just attribute types. Setting this field to TRUE 304 * causes only attribute types (no values) to be returned. Setting this 305 * field to FALSE causes both attribute types and values to be returned. 306 * 307 * @param typesOnly 308 * true for only types, false for types and values. 309 */ 310 public void setTypesOnly( boolean typesOnly ) 311 { 312 this.typesOnly = typesOnly; 313 } 314 315 316 /** 317 * Adds an attribute to the set of entry attributes to return. 318 * 319 * @param attribute 320 * the attribute description or identifier. 321 */ 322 public void addAttribute( String attribute ) 323 { 324 attributes.add( attribute ); 325 } 326 327 328 /** 329 * Removes an attribute to the set of entry attributes to return. 330 * 331 * @param attribute 332 * the attribute description or identifier. 333 */ 334 public void removeAttribute( String attribute ) 335 { 336 attributes.remove( attribute ); 337 } 338 339 340 /** 341 * The result containing response for this request. 342 * 343 * @return the result containing response for this request 344 */ 345 public InternalResultResponse getResultResponse() 346 { 347 if ( response == null ) 348 { 349 response = new SearchResponseDoneImpl( getMessageId() ); 350 } 351 352 return response; 353 } 354 355 356 /** 357 * Checks to see if two search requests are equal. The Lockable properties 358 * and the get/set context specific parameters are not consulted to 359 * determine equality. The filter expression tree comparison will normalize 360 * the child order of filter branch nodes then generate a string 361 * representation which is comparable. For the time being this is a very 362 * costly operation. 363 * 364 * @param obj 365 * the object to check for equality to this SearchRequest 366 * @return true if the obj is a SearchRequest and equals this SearchRequest, 367 * false otherwise 368 */ 369 public boolean equals( Object obj ) 370 { 371 if ( obj == this ) 372 { 373 return true; 374 } 375 376 if ( !super.equals( obj ) ) 377 { 378 return false; 379 } 380 381 InternalSearchRequest req = ( InternalSearchRequest ) obj; 382 383 if ( !req.getBase().equals( baseDn ) ) 384 { 385 return false; 386 } 387 388 if ( req.getDerefAliases() != aliasDerefMode ) 389 { 390 return false; 391 } 392 393 if ( req.getScope() != scope ) 394 { 395 return false; 396 } 397 398 if ( req.getSizeLimit() != sizeLimit ) 399 { 400 return false; 401 } 402 403 if ( req.getTimeLimit() != timeLimit ) 404 { 405 return false; 406 } 407 408 if ( req.getTypesOnly() != typesOnly ) 409 { 410 return false; 411 } 412 413 if ( req.getAttributes() == null && attributes != null ) 414 { 415 if ( attributes.size() > 0 ) 416 { 417 return false; 418 } 419 } 420 421 if ( req.getAttributes() != null && attributes == null ) 422 { 423 if ( req.getAttributes().size() > 0 ) 424 { 425 return false; 426 } 427 } 428 429 if ( req.getAttributes() != null && attributes != null ) 430 { 431 if ( req.getAttributes().size() != attributes.size() ) 432 { 433 return false; 434 } 435 436 Iterator<String> list = attributes.iterator(); 437 438 while ( list.hasNext() ) 439 { 440 if ( !req.getAttributes().contains( list.next() ) ) 441 { 442 return false; 443 } 444 } 445 } 446 447 BranchNormalizedVisitor visitor = new BranchNormalizedVisitor(); 448 req.getFilter().accept( visitor ); 449 filter.accept( visitor ); 450 451 String myFilterString = filter.toString(); 452 String reqFilterString = req.getFilter().toString(); 453 454 return myFilterString.equals( reqFilterString ); 455 } 456 457 /** 458 * Return a string the represent a SearchRequest 459 */ 460 public String toString() 461 { 462 StringBuilder sb = new StringBuilder(); 463 464 sb.append( " SearchRequest\n" ); 465 sb.append( " baseDn : '" ).append( baseDn ).append( "'\n" ); 466 467 if ( filter != null ) 468 { 469 sb.append( " filter : '" ); 470 sb.append( filter.toString() ); 471 sb.append( "'\n" ); 472 } 473 474 sb.append( " scope : " ); 475 476 switch ( scope ) 477 { 478 case OBJECT: 479 sb.append( "base object" ); 480 break; 481 482 case ONELEVEL: 483 sb.append( "single level" ); 484 break; 485 486 case SUBTREE: 487 sb.append( "whole subtree" ); 488 break; 489 } 490 491 sb.append( '\n' ); 492 493 sb.append( " typesOnly : " ).append( typesOnly ).append( '\n' ); 494 495 sb.append( " Size Limit : " ); 496 497 if ( sizeLimit == 0L ) 498 { 499 sb.append( "no limit" ); 500 } 501 else 502 { 503 sb.append( sizeLimit ); 504 } 505 506 sb.append( '\n' ); 507 508 sb.append( " Time Limit : " ); 509 510 if ( timeLimit == 0 ) 511 { 512 sb.append( "no limit" ); 513 } 514 else 515 { 516 sb.append( timeLimit ); 517 } 518 519 sb.append( '\n' ); 520 521 sb.append( " Deref Aliases : " ); 522 523 switch ( aliasDerefMode.getValue() ) 524 { 525 case LdapConstants.NEVER_DEREF_ALIASES: 526 sb.append( "never Deref Aliases" ); 527 break; 528 529 case LdapConstants.DEREF_IN_SEARCHING: 530 sb.append( "deref In Searching" ); 531 break; 532 533 case LdapConstants.DEREF_FINDING_BASE_OBJ: 534 sb.append( "deref Finding Base Obj" ); 535 break; 536 537 case LdapConstants.DEREF_ALWAYS: 538 sb.append( "deref Always" ); 539 break; 540 } 541 542 sb.append( '\n' ); 543 sb.append( " attributes : " ); 544 545 boolean isFirst = true; 546 547 if ( attributes != null ) 548 { 549 Iterator<String> it = attributes.iterator(); 550 551 while ( it.hasNext() ) 552 { 553 if ( isFirst ) 554 { 555 isFirst = false; 556 } 557 else 558 { 559 sb.append( ", " ); 560 } 561 562 sb.append( '\'' ).append( it.next() ).append( '\'' ); 563 } 564 565 } 566 567 sb.append( '\n' ); 568 569 return sb.toString(); 570 } 571 }