1 /******************************************************************************
3 * Copyright (c) 2003 Novell Inc. www.novell.com
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the Software), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 *******************************************************************************/
24 // Novell.Directory.Ldap.LdapSearchResults.cs
27 // Sunil Kumar (Sunilk@novell.com)
29 // (C) 2003 Novell, Inc (http://www.novell.com)
33 using Novell
.Directory
.Ldap
.Utilclass
;
35 namespace Novell
.Directory
.Ldap
38 /// <summary> An LdapSearchResults object is returned from a synchronous search
39 /// operation. It provides access to all results received during the
40 /// operation (entries and exceptions).
43 /// <seealso cref="LdapConnection.Search">
45 public class LdapSearchResults
47 /// <summary> Returns a count of the items in the search result.
49 /// Returns a count of the entries and exceptions remaining in the object.
50 /// If the search was submitted with a batch size greater than zero,
51 /// getCount reports the number of results received so far but not enumerated
52 /// with next(). If batch size equals zero, getCount reports the number of
53 /// items received, since the application thread blocks until all results are
57 /// <returns> The number of items received but not retrieved by the application
59 virtual public int Count
63 int qCount
= queue
.MessageAgent
.Count
;
64 return entryCount
- entryIndex
+ referenceCount
- referenceIndex
+ qCount
;
68 /// <summary> Returns the latest server controls returned by the server
69 /// in the context of this search request, or null
70 /// if no server controls were returned.
73 /// <returns> The server controls returned with the search request, or null
74 /// if none were returned.
76 virtual public LdapControl
[] ResponseControls
84 /// <summary> Collects batchSize elements from an LdapSearchQueue message
85 /// queue and places them in a Vector.
87 /// If the last message from the server,
88 /// the result message, contains an error, it will be stored in the Vector
89 /// for nextElement to process. (although it does not increment the search
90 /// result count) All search result entries will be placed in the Vector.
91 /// If a null is returned from getResponse(), it is likely that the search
95 /// <returns> true if all search results have been placed in the vector.
97 private bool BatchOfResults
103 // <=batchSize so that we can pick up the result-done message
104 for (int i
= 0; i
< batchSize
; )
108 if ((msg
= queue
.getResponse()) != null)
110 // Only save controls if there are some
111 LdapControl
[] ctls
= msg
.Controls
;
118 if (msg
is LdapSearchResult
)
121 System
.Object entry
= ((LdapSearchResult
) msg
).Entry
;
126 else if (msg
is LdapSearchResultReference
)
129 System
.String
[] refs
= ((LdapSearchResultReference
) msg
).Referrals
;
131 if (cons
.ReferralFollowing
)
133 // referralConn = conn.chaseReferral(queue, cons, msg, refs, 0, true, referralConn);
137 references
.Add(refs
);
144 LdapResponse resp
= (LdapResponse
) msg
;
145 int resultCode
= resp
.ResultCode
;
146 // Check for an embedded exception
147 if (resp
.hasException())
149 // Fake it, results in an exception when msg read
150 resultCode
= LdapException
.CONNECT_ERROR
;
153 if ((resultCode
== LdapException
.REFERRAL
) && cons
.ReferralFollowing
)
155 // Following referrals
156 // referralConn = conn.chaseReferral(queue, cons, resp, resp.Referrals, 0, false, referralConn);
158 else if (resultCode
!= LdapException
.SUCCESS
)
160 // Results in an exception when message read
164 // We are done only when we have read all messages
165 // including those received from following referrals
166 int[] msgIDs
= queue
.MessageIDs
;
167 if (msgIDs
.Length
== 0)
169 // Release referral exceptions
170 // conn.releaseReferralConnections(referralConn);
171 return true; // search completed
181 // We get here if the connection timed out
182 // we have no responses, no message IDs and no exceptions
183 LdapException e
= new LdapException(null, LdapException
.Ldap_TIMEOUT
, (System
.String
) null);
188 catch (LdapException e
)
190 // Hand exception off to user
195 return false; // search not completed
200 private System
.Collections
.ArrayList entries
; // Search entries
201 private int entryCount
; // # Search entries in vector
202 private int entryIndex
; // Current position in vector
203 private System
.Collections
.ArrayList references
; // Search Result References
204 private int referenceCount
; // # Search Result Reference in vector
205 private int referenceIndex
; // Current position in vector
206 private int batchSize
; // Application specified batch size
207 private bool completed
= false; // All entries received
208 private LdapControl
[] controls
= null; // Last set of controls
209 private LdapSearchQueue queue
;
210 private static System
.Object nameLock
; // protect resultsNum
211 private static int resultsNum
= 0; // used for debug
212 private System
.String name
; // used for debug
213 private LdapConnection conn
; // LdapConnection which started search
214 private LdapSearchConstraints cons
; // LdapSearchConstraints for search
215 private System
.Collections
.ArrayList referralConn
= null; // Referral Connections
217 /// <summary> Constructs a queue object for search results.
220 /// <param name="conn">The LdapConnection which initiated the search
223 /// <param name="queue">The queue for the search results.
226 /// <param name="cons">The LdapSearchConstraints associated with this search
229 internal LdapSearchResults(LdapConnection conn
, LdapSearchQueue queue
, LdapSearchConstraints cons
)
231 // setup entry Vector
234 int batchSize
= cons
.BatchSize
;
235 int vectorIncr
= (batchSize
== 0)?64:0;
236 entries
= new System
.Collections
.ArrayList((batchSize
== 0)?64:batchSize
);
240 // setup search reference Vector
241 references
= new System
.Collections
.ArrayList(5);
246 this.batchSize
= (batchSize
== 0)?System
.Int32
.MaxValue
:batchSize
;
251 /// <summary> Reports if there are more search results.
254 /// <returns> true if there are more search results.
256 public virtual bool hasMore()
259 if ((entryIndex
< entryCount
) || (referenceIndex
< referenceCount
))
264 else if (completed
== false)
266 // reload the Vector by getting more results
268 ret
= (entryIndex
< entryCount
) || (referenceIndex
< referenceCount
);
274 * If both of the vectors are empty, get more data for them.
276 private void resetVectors()
278 // If we're done, no further checking needed
283 // Checks if we have run out of references
284 if ((referenceIndex
!= 0) && (referenceIndex
>= referenceCount
))
286 SupportClass
.SetSize(references
, 0);
290 // Checks if we have run out of entries
291 if ((entryIndex
!= 0) && (entryIndex
>= entryCount
))
293 SupportClass
.SetSize(entries
, 0);
297 // If no data at all, must reload enumeration
298 if ((referenceIndex
== 0) && (referenceCount
== 0) && (entryIndex
== 0) && (entryCount
== 0))
300 completed
= BatchOfResults
;
304 /// <summary> Returns the next result as an LdapEntry.
306 /// If automatic referral following is disabled or if a referral
307 /// was not followed, next() will throw an LdapReferralException
308 /// when the referral is received.
311 /// <returns> The next search result as an LdapEntry.
314 /// <exception> LdapException A general exception which includes an error
315 /// message and an Ldap error code.
317 /// <exception> LdapReferralException A referral was received and not
320 public virtual LdapEntry
next()
322 if (completed
&& (entryIndex
>= entryCount
) && (referenceIndex
>= referenceCount
))
324 throw new System
.ArgumentOutOfRangeException("LdapSearchResults.next() no more results");
326 // Check if the enumeration is empty and must be reloaded
329 System
.Object element
= null;
330 // Check for Search References & deliver to app as they come in
331 // We only get here if not following referrals/references
332 if (referenceIndex
< referenceCount
)
334 System
.String
[] refs
= (System
.String
[]) (references
[referenceIndex
++]);
335 LdapReferralException rex
= new LdapReferralException(ExceptionMessages
.REFERENCE_NOFOLLOW
);
336 rex
.setReferrals(refs
);
339 else if (entryIndex
< entryCount
)
341 // Check for Search Entries and the Search Result
342 element
= entries
[entryIndex
++];
343 if (element
is LdapResponse
)
345 // Search done w/bad status
346 if (((LdapResponse
) element
).hasException())
349 LdapResponse lr
= (LdapResponse
) element
;
350 ReferralInfo ri
= lr
.ActiveReferral
;
354 // Error attempting to follow a search continuation reference
355 LdapReferralException rex
= new LdapReferralException(ExceptionMessages
.REFERENCE_ERROR
, lr
.Exception
);
356 rex
.setReferrals(ri
.ReferralList
);
357 rex
.FailedReferral
= ri
.ReferralUrl
.ToString();
361 // Throw an exception if not success
362 ((LdapResponse
) element
).chkResultCode();
364 else if (element
is LdapException
)
366 throw (LdapException
) element
;
371 // If not a Search Entry, Search Result, or search continuation
372 // we are very confused.
373 // LdapSearchResults.next(): No entry found & request is not complete
374 throw new LdapException(ExceptionMessages
.REFERRAL_LOCAL
, new System
.Object
[]{"next"}
, LdapException
.LOCAL_ERROR
, (System
.String
) null);
376 return (LdapEntry
) element
;
379 /// <summary> Cancels the search request and clears the message and enumeration.</summary>
381 internal virtual void Abandon()
383 // first, remove message ID and timer and any responses in the queue
384 queue
.MessageAgent
.AbandonAll();
386 // next, clear out enumeration
390 static LdapSearchResults()
392 nameLock
= new System
.Object();