/****************************************************************************** * The MIT License * Copyright (c) 2003 Novell Inc. www.novell.com * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the Software), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. *******************************************************************************/ // // Novell.Directory.Ldap.LdapSearchResults.cs // // Author: // Sunil Kumar (Sunilk@novell.com) // // (C) 2003 Novell, Inc (http://www.novell.com) // using System; using Novell.Directory.Ldap.Utilclass; namespace Novell.Directory.Ldap { ///

An LdapSearchResults object is returned from a synchronous search /// operation. It provides access to all results received during the /// operation (entries and exceptions).

/// ///
/// /// public class LdapSearchResults { /// Returns a count of the items in the search result. /// ///

Returns a count of the entries and exceptions remaining in the object. /// If the search was submitted with a batch size greater than zero, /// getCount reports the number of results received so far but not enumerated /// with next(). If batch size equals zero, getCount reports the number of /// items received, since the application thread blocks until all results are /// received.

/// ///
/// The number of items received but not retrieved by the application /// virtual public int Count { get { int qCount = queue.MessageAgent.Count; return entryCount - entryIndex + referenceCount - referenceIndex + qCount; } } /// Returns the latest server controls returned by the server /// in the context of this search request, or null /// if no server controls were returned. /// /// /// The server controls returned with the search request, or null /// if none were returned. /// virtual public LdapControl[] ResponseControls { get { return controls; } } /// Collects batchSize elements from an LdapSearchQueue message /// queue and places them in a Vector. /// ///

If the last message from the server, /// the result message, contains an error, it will be stored in the Vector /// for nextElement to process. (although it does not increment the search /// result count) All search result entries will be placed in the Vector. /// If a null is returned from getResponse(), it is likely that the search /// was abandoned.

/// ///
/// true if all search results have been placed in the vector. /// private bool BatchOfResults { get { LdapMessage msg; // <=batchSize so that we can pick up the result-done message for (int i = 0; i < batchSize; ) { try { if ((msg = queue.getResponse()) != null) { // Only save controls if there are some LdapControl[] ctls = msg.Controls; if (ctls != null) { controls = ctls; } if (msg is LdapSearchResult) { // Search Entry System.Object entry = ((LdapSearchResult) msg).Entry; entries.Add(entry); i++; entryCount++; } else if (msg is LdapSearchResultReference) { // Search Ref System.String[] refs = ((LdapSearchResultReference) msg).Referrals; if (cons.ReferralFollowing) { // referralConn = conn.chaseReferral(queue, cons, msg, refs, 0, true, referralConn); } else { references.Add(refs); referenceCount++; } } else { // LdapResponse LdapResponse resp = (LdapResponse) msg; int resultCode = resp.ResultCode; // Check for an embedded exception if (resp.hasException()) { // Fake it, results in an exception when msg read resultCode = LdapException.CONNECT_ERROR; } if ((resultCode == LdapException.REFERRAL) && cons.ReferralFollowing) { // Following referrals // referralConn = conn.chaseReferral(queue, cons, resp, resp.Referrals, 0, false, referralConn); } else if (resultCode != LdapException.SUCCESS) { // Results in an exception when message read entries.Add(resp); entryCount++; } // We are done only when we have read all messages // including those received from following referrals int[] msgIDs = queue.MessageIDs; if (msgIDs.Length == 0) { // Release referral exceptions // conn.releaseReferralConnections(referralConn); return true; // search completed } else { } continue; } } else { // We get here if the connection timed out // we have no responses, no message IDs and no exceptions LdapException e = new LdapException(null, LdapException.Ldap_TIMEOUT, (System.String) null); entries.Add(e); break; } } catch (LdapException e) { // Hand exception off to user entries.Add(e); } continue; } return false; // search not completed } } private System.Collections.ArrayList entries; // Search entries private int entryCount; // # Search entries in vector private int entryIndex; // Current position in vector private System.Collections.ArrayList references; // Search Result References private int referenceCount; // # Search Result Reference in vector private int referenceIndex; // Current position in vector private int batchSize; // Application specified batch size private bool completed = false; // All entries received private LdapControl[] controls = null; // Last set of controls private LdapSearchQueue queue; private static System.Object nameLock; // protect resultsNum private static int resultsNum = 0; // used for debug private System.String name; // used for debug private LdapConnection conn; // LdapConnection which started search private LdapSearchConstraints cons; // LdapSearchConstraints for search private System.Collections.ArrayList referralConn = null; // Referral Connections /// Constructs a queue object for search results. /// /// /// The LdapConnection which initiated the search ///

/// /// The queue for the search results. ///

/// /// The LdapSearchConstraints associated with this search /// /* package */ internal LdapSearchResults(LdapConnection conn, LdapSearchQueue queue, LdapSearchConstraints cons) { // setup entry Vector this.conn = conn; this.cons = cons; int batchSize = cons.BatchSize; int vectorIncr = (batchSize == 0)?64:0; entries = new System.Collections.ArrayList((batchSize == 0)?64:batchSize); entryCount = 0; entryIndex = 0; // setup search reference Vector references = new System.Collections.ArrayList(5); referenceCount = 0; referenceIndex = 0; this.queue = queue; this.batchSize = (batchSize == 0)?System.Int32.MaxValue:batchSize; return ; } /// Reports if there are more search results. /// /// /// true if there are more search results. /// public virtual bool hasMore() { bool ret = false; if ((entryIndex < entryCount) || (referenceIndex < referenceCount)) { // we have data ret = true; } else if (completed == false) { // reload the Vector by getting more results resetVectors(); ret = (entryIndex < entryCount) || (referenceIndex < referenceCount); } return ret; } /* * If both of the vectors are empty, get more data for them. */ private void resetVectors() { // If we're done, no further checking needed if (completed) { return ; } // Checks if we have run out of references if ((referenceIndex != 0) && (referenceIndex >= referenceCount)) { SupportClass.SetSize(references, 0); referenceCount = 0; referenceIndex = 0; } // Checks if we have run out of entries if ((entryIndex != 0) && (entryIndex >= entryCount)) { SupportClass.SetSize(entries, 0); entryCount = 0; entryIndex = 0; } // If no data at all, must reload enumeration if ((referenceIndex == 0) && (referenceCount == 0) && (entryIndex == 0) && (entryCount == 0)) { completed = BatchOfResults; } return ; } /// Returns the next result as an LdapEntry. /// ///

If automatic referral following is disabled or if a referral /// was not followed, next() will throw an LdapReferralException /// when the referral is received.

/// ///
/// The next search result as an LdapEntry. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// /// LdapReferralException A referral was received and not /// followed. /// public virtual LdapEntry next() { if (completed && (entryIndex >= entryCount) && (referenceIndex >= referenceCount)) { throw new System.ArgumentOutOfRangeException("LdapSearchResults.next() no more results"); } // Check if the enumeration is empty and must be reloaded resetVectors(); System.Object element = null; // Check for Search References & deliver to app as they come in // We only get here if not following referrals/references if (referenceIndex < referenceCount) { System.String[] refs = (System.String[]) (references[referenceIndex++]); LdapReferralException rex = new LdapReferralException(ExceptionMessages.REFERENCE_NOFOLLOW); rex.setReferrals(refs); throw rex; } else if (entryIndex < entryCount) { // Check for Search Entries and the Search Result element = entries[entryIndex++]; if (element is LdapResponse) { // Search done w/bad status if (((LdapResponse) element).hasException()) { LdapResponse lr = (LdapResponse) element; ReferralInfo ri = lr.ActiveReferral; if (ri != null) { // Error attempting to follow a search continuation reference LdapReferralException rex = new LdapReferralException(ExceptionMessages.REFERENCE_ERROR, lr.Exception); rex.setReferrals(ri.ReferralList); rex.FailedReferral = ri.ReferralUrl.ToString(); throw rex; } } // Throw an exception if not success ((LdapResponse) element).chkResultCode(); } else if (element is LdapException) { throw (LdapException) element; } } else { // If not a Search Entry, Search Result, or search continuation // we are very confused. // LdapSearchResults.next(): No entry found & request is not complete throw new LdapException(ExceptionMessages.REFERRAL_LOCAL, new System.Object[]{"next"}, LdapException.LOCAL_ERROR, (System.String) null); } return (LdapEntry) element; } /// Cancels the search request and clears the message and enumeration. /*package*/ internal virtual void Abandon() { // first, remove message ID and timer and any responses in the queue queue.MessageAgent.AbandonAll(); // next, clear out enumeration resetVectors(); completed = true; } static LdapSearchResults() { nameLock = new System.Object(); } } }