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