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 }