Return-Path: X-Original-To: apmail-directory-dev-archive@www.apache.org Delivered-To: apmail-directory-dev-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 441FD3F2D for ; Thu, 28 Apr 2011 13:24:09 +0000 (UTC) Received: (qmail 2850 invoked by uid 500); 28 Apr 2011 13:24:09 -0000 Delivered-To: apmail-directory-dev-archive@directory.apache.org Received: (qmail 2793 invoked by uid 500); 28 Apr 2011 13:24:09 -0000 Mailing-List: contact dev-help@directory.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: "Apache Directory Developers List" Delivered-To: mailing list dev@directory.apache.org Received: (qmail 2786 invoked by uid 99); 28 Apr 2011 13:24:09 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 28 Apr 2011 13:24:09 +0000 X-ASF-Spam-Status: No, hits=-0.7 required=5.0 tests=FREEMAIL_FROM,RCVD_IN_DNSWL_LOW,RFC_ABUSE_POST,SPF_PASS X-Spam-Check-By: apache.org Received-SPF: pass (nike.apache.org: domain of elecharny@gmail.com designates 74.125.82.44 as permitted sender) Received: from [74.125.82.44] (HELO mail-ww0-f44.google.com) (74.125.82.44) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 28 Apr 2011 13:24:01 +0000 Received: by wwa36 with SMTP id 36so3107102wwa.1 for ; Thu, 28 Apr 2011 06:23:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:message-id:date:from:reply-to:user-agent :mime-version:to:subject:references:in-reply-to:content-type :content-transfer-encoding; bh=EDBY0OguSUaAg4/9fUMPfSRBrDwfUNafmX8UUK6/gSs=; b=Pjm1pf20IXo0X/ZyJF1RnCFIqFslHhk+maQ7UdwkW0BrsuunpkmFauk4EB65/uoOQ+ cvY4SlWNiWiBKqdEOtCYjm3EA3Y+PKyMLYU3lIGbD9ouBRk67GbRCAn2wtPxgBd13kDF Phdn3stOnIBRa4RquYk5WCvuObJTLyX/Rj0n4= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=message-id:date:from:reply-to:user-agent:mime-version:to:subject :references:in-reply-to:content-type:content-transfer-encoding; b=ks6Q+mzZCZYxP3QcQFCFES8cCFNXcaGIqG7r82S/Mkr3/hisZPd1jwKh42Ss1VcndI JZUEZY0PnK/tjUFkMhvwu2Uh6Xn2MSoiBl9AWtkQOJosdECU7ndlXPzT4YaDqUr+doCr RsVlQ4L+aJofzYTJVYhmEu6nlkJIQyZjz9vLg= Received: by 10.227.148.144 with SMTP id p16mr3765938wbv.223.1303997021381; Thu, 28 Apr 2011 06:23:41 -0700 (PDT) Received: from emmanuel-lecharnys-MacBook-Pro.local (ran75-1-78-192-106-184.fbxo.proxad.net [78.192.106.184]) by mx.google.com with ESMTPS id w25sm1093225wbd.22.2011.04.28.06.23.39 (version=TLSv1/SSLv3 cipher=OTHER); Thu, 28 Apr 2011 06:23:40 -0700 (PDT) Message-ID: <4DB96A5A.2070507@gmail.com> Date: Thu, 28 Apr 2011 15:23:38 +0200 From: Emmanuel Lecharny Reply-To: elecharny@apache.org User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.2.15) Gecko/20110303 Thunderbird/3.1.9 MIME-Version: 1.0 To: Apache Directory Developers List Subject: Re: Search result in LDAP API References: <4DB94AA0.5030302@gmail.com> <4F5C26AF-2E6A-442C-A694-097C3851A62B@marcelot.net> In-Reply-To: Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit X-Virus-Checked: Checked by ClamAV on apache.org FYI, UnboundiD SDK has another approach : http://www.unboundid.com/products/ldap-sdk/docs/javadoc/index.html OpenDS/DJ does things this way : final ConnectionEntryReader reader = connection.search("dc=example,dc=com", SearchScope.WHOLE_SUBTREE,"(uid=bjensen)","*"); while (reader.hasNext()) { if (!reader.isReference()) { // Got an entry. final SearchResultEntry entry = reader.readEntry(); writer.writeComment("Search result entry:" + entry.getName().toString()); writer.writeEntry(entry); } else { // Got a continuation reference. final SearchResultReference ref = reader.readReference(); writer.writeComment("Search result reference:" + ref.getURIs().toString()); } } writer.flush(); } Food for thoughts... On 4/28/11 2:56 PM, Kiran Ayyagari wrote: > On Thu, Apr 28, 2011 at 6:18 PM, Pierre-Arnaud Marcelot wrote: >> On 28 avr. 2011, at 14:31, Kiran Ayyagari wrote: >> >>> On Thu, Apr 28, 2011 at 5:50 PM, Pierre-Arnaud Marcelot wrote: >>>> On 28 avr. 2011, at 13:39, Kiran Ayyagari wrote: >>>> >>>>> On Thu, Apr 28, 2011 at 4:38 PM, Emmanuel Lecharny wrote: >>>>>> Hi guys, >>>>>> >>>>>> yesterday, as I started to write the doc about the Search Operation, I faced >>>>>> some issue. Let me explain. >>>>>> >>>>>> When you do a simple search, you get back a cursor : >>>>>> >>>>>> SearchCursor cursor = connection.search( "ou=system", >>>>>> "(objectclass=*)", SearchScope.ONELEVEL ); >>>>>> >>>>>> The SearchCursor extends Cursor. >>>>>> >>>>>> That means you get some Response when you do a search, so you have to write >>>>>> such code to get back the entries : >>>>>> >>>>>> while ( cursor.next() ) >>>>>> { >>>>>> Entry entry = ((SearchResultEntry)(cursor.get())).getEntry(); >>>>>> >>>>>> which is just horrible. >>>> I completely agree. >>>> We are creating this API to make the user's life easier and not embarrass him with such constructions. >>>> I'm pretty sure 90% of the users of the API won't know there are multiple types of responses to search. >>>> Most of them only expect entries. >>>> >>>> On the other hand, LDAP experts know everything about the spec and the API should also ease their work and allow them to access everything. >>>> >>>>>> The reason is that the search can return three kinds of responses : >>>>>> - normal entries >>>>>> - search result done >>>>>> - and referrals >>>>>> >>>>> there is one more, the IntermediateResponse >>>>> >>>>>> We can deal with the searchResultDone (and we do, you can call a >>>>>> cursor.getSearchResultDone() when you quit the loop), but the referral >>>>>> handling is a bit more special. >>>>>> >>>>>> We have many options to clean up the API here : >>>>>> - first, we can consider that a cursor.get() will always return an entry (or >>>>>> a SearchResultEntry). In this case, if the next result is a referral, we >>>>>> have two cases. The first one, if the users have asked the API to chase >>>>>> referrals, he will get back an entry, and we are fine. The other case is >>>>>> when we don't chase referrals, and then we can just throw a >>>>>> ReferralException, up to the client to catch this exception >>>>> IMHO a cursor, should never assume about the type of elements it is >>>>> serving beyond a certain level of abstraction >>>>> (sadly in LDAP, as we know, the result set contains different types >>>>> of objects for the same operation, >>>>> But we have already made some changes in this way, like getting the >>>>> SearchResultDone) >>>>>> - second, we can expect the user to check the result type before grabbing >>>>>> it. If it's a SearchResultEntry, then do a cursor.getEntry(), otherwise do a >>>>>> cursor.getReferral(). >>>>> this is exactly the way we wanted it to work, not just for referrals >>>>> but any sort of search response, >>>>> i.e the cursor returns the ResponseS (the super type) and it is upto >>>>> the user to deal based on the specific subtype >>>>> infact the above shown code snippet casts directly to a >>>>> SearchResultEntry cause we know first hand that this search >>>>> (perhaps done in a test case) will *only* return search entries and >>>>> nothing else. >>>>> >>>>>> - third, we can cumulate all the referrals and ask the user to look on the >>>>>> cursor a second time to deal with referral. We would have to store those >>>>>> referrals internally to the cursor, and add a nextReferral() method. >>>>>> >>>>> IMO, this should be avoided by all means, cause in the worst case it >>>>> will lead to a OOM >>>>> >>>>>> I personally find the first solution the least painful for our users. In any >>>>>> case, we have to do two things : >>>>>> - change the current API >>>>>> - implement the Referral chasing in the API >>>>>> >>>>>> So, now, wdyt ? >>>>> my preference would be to keep the get() method as it is and add new >>>>> method(s) like getEntry() , getReferral() >>>>> and user can call the respective method based on his requirement, and >>>>> these method can throw >>>>> an exception when the expected type is not compatible >>>>> e.x NotAnEntryException when getEntry() is called but the current >>>>> result is a referral >>>> This would be my preference as well (with another method to access the IntermediateResponse too). >>>> >>>> But I would add other convenient methods like: >>>> - boolean nextEntry(); >>>> - boolean nextReferral(); >>>> - boolean nextIntermediate(); >>> hmm, we can just use next() method alone, cause all that we need is to >>> read the next response >>> (OTOH nextEntry/Referral() might confuse users to think that the >>> cursor will skip to next avalable entry in the responses) >> These methods would guaranty that the current object in the cursor is of the requested type and avoid either getting a null reference or an exception when getting the object via the getXXX() method. >> >> In the case you're requesting the next entry via nextEntry(), the cursor will advance until it finds the next SearchResultEntry and discard any other message type in between. >> > in the case of skipping the responses, yes, makes sense >> This is only for convenience and to avoid having to test the current object's type in the loop test like this, for a loop that's only interested in getting entries: >>> while ( cursor.next()&& cursor.isCurrentEntry() ) >>> { >>> Entry entry = cursor.getEntry(); >>> } > I like this approach rather than having a nextEntry() method, though > not against it >>>> - boolean previousEntry(); >>>> - boolean previousReferral(); >>>> - boolean previousIntermediate(); >>> we cannot support these methods unless we store them in the cursor, >>> but as we know, maintaining them in memory brings up >>> other issues (GC, OOM) >> I don't know how it's done ATM and if it works, but if the previous() method of the cursor work, it shouldn't be that difficult to implement as well. >> > we don't support the previous() methods atm >>>> This methods would allow the user to jump to the next/previous object of their choice easily. >>>> >>>> For example, this snippet would allow the user to get all entries from a search cursor in a much nicer way than today: >>>>> while ( cursor.nextEntry() ) >>> we can just use - while ( cursor.next() ) >>>>> { >>>>> Entry entry = cursor.getEntry(); >>>>> } >>>> Other methods to know what is the type of the current object in the cursor would also be useful like: >>>> - boolean isCurrentEntry(); >>>> - boolean isCurrentReferral(); >>>> - boolean isCurrentIntermediate(); >>> +1, makes it more convenient >>>> Lastly, I'm wondering if the getXXX() methods should return an Entry/Referral or the associated SearchResult instead (SearchResultEntry/SearchResultReference). >>>> Thoughts? >>>> >>> just the plain Entry/Referral/IntermediateResponse objects directly, >>> otherwise caller has to make another call like >>> SearchResultEntry.getEntry() >> +1, I was thinking the same thing. >> It's much more convenient. >> Just didn't want to influence by indicating it... ;) >> >> Regards, >> Pierre-Arnaud > > -- Regards, Cordialement, Emmanuel Lécharny www.iktek.com