Path: blob/master/src/java.naming/share/classes/com/sun/jndi/ldap/AbstractLdapNamingEnumeration.java
41161 views
/*1* Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package com.sun.jndi.ldap;2627import com.sun.jndi.toolkit.ctx.Continuation;28import java.util.NoSuchElementException;29import java.util.Vector;3031import javax.naming.*;32import javax.naming.directory.Attributes;33import javax.naming.ldap.Control;3435/**36* Basic enumeration for NameClassPair, Binding, and SearchResults.37*/3839abstract class AbstractLdapNamingEnumeration<T extends NameClassPair>40implements NamingEnumeration<T>, ReferralEnumeration<T> {4142protected Name listArg;4344private boolean cleaned = false;45private LdapResult res;46private LdapClient enumClnt;47private Continuation cont; // used to fill in exceptions48private Vector<LdapEntry> entries = null;49private int limit = 0;50private int posn = 0;51protected LdapCtx homeCtx;52private LdapReferralException refEx = null;53private NamingException errEx = null;5455/*56* Record the next set of entries and/or referrals.57*/58AbstractLdapNamingEnumeration(LdapCtx homeCtx, LdapResult answer, Name listArg,59Continuation cont) throws NamingException {6061// These checks are to accommodate referrals and limit exceptions62// which will generate an enumeration and defer the exception63// to be thrown at the end of the enumeration.64// All other exceptions are thrown immediately.65// Exceptions shouldn't be thrown here anyhow because66// process_return_code() is called before the constructor67// is called, so these are just safety checks.6869if ((answer.status != LdapClient.LDAP_SUCCESS) &&70(answer.status != LdapClient.LDAP_SIZE_LIMIT_EXCEEDED) &&71(answer.status != LdapClient.LDAP_TIME_LIMIT_EXCEEDED) &&72(answer.status != LdapClient.LDAP_ADMIN_LIMIT_EXCEEDED) &&73(answer.status != LdapClient.LDAP_REFERRAL) &&74(answer.status != LdapClient.LDAP_PARTIAL_RESULTS)) {7576// %%% need to deal with referral77NamingException e = new NamingException(78LdapClient.getErrorMessage(79answer.status, answer.errorMessage));8081throw cont.fillInException(e);82}8384// otherwise continue8586res = answer;87entries = answer.entries;88limit = (entries == null) ? 0 : entries.size(); // handle empty set89this.listArg = listArg;90this.cont = cont;9192if (answer.refEx != null) {93refEx = answer.refEx;94}9596// Ensures that context won't get closed from underneath us97this.homeCtx = homeCtx;98homeCtx.incEnumCount();99enumClnt = homeCtx.clnt; // remember100}101102@Override103public final T nextElement() {104try {105return next();106} catch (NamingException e) {107// can't throw exception108cleanup();109return null;110}111}112113@Override114public final boolean hasMoreElements() {115try {116return hasMore();117} catch (NamingException e) {118// can't throw exception119cleanup();120return false;121}122}123124/*125* Retrieve the next set of entries and/or referrals.126*/127private void getNextBatch() throws NamingException {128129res = homeCtx.getSearchReply(enumClnt, res);130if (res == null) {131limit = posn = 0;132return;133}134135entries = res.entries;136limit = (entries == null) ? 0 : entries.size(); // handle empty set137posn = 0; // reset138139// minimize the number of calls to processReturnCode()140// (expensive when batchSize is small and there are many results)141if ((res.status != LdapClient.LDAP_SUCCESS) ||142((res.status == LdapClient.LDAP_SUCCESS) &&143(res.referrals != null))) {144145try {146// convert referrals into a chain of LdapReferralException147homeCtx.processReturnCode(res, listArg);148149} catch (LimitExceededException | PartialResultException e) {150setNamingException(e);151152}153}154155// merge any newly received referrals with any current referrals156if (res.refEx != null) {157if (refEx == null) {158refEx = res.refEx;159} else {160refEx = refEx.appendUnprocessedReferrals(res.refEx);161}162res.refEx = null; // reset163}164165if (res.resControls != null) {166homeCtx.respCtls = res.resControls;167}168}169170private boolean more = true; // assume we have something to start with171private boolean hasMoreCalled = false;172173/*174* Test if unprocessed entries or referrals exist.175*/176@Override177public final boolean hasMore() throws NamingException {178179if (hasMoreCalled) {180return more;181}182183hasMoreCalled = true;184185if (!more) {186return false;187} else {188return (more = hasMoreImpl());189}190}191192/*193* Retrieve the next entry.194*/195@Override196public final T next() throws NamingException {197198if (!hasMoreCalled) {199hasMore();200}201hasMoreCalled = false;202return nextImpl();203}204205/*206* Test if unprocessed entries or referrals exist.207*/208private boolean hasMoreImpl() throws NamingException {209// when page size is supported, this210// might generate an exception while attempting211// to fetch the next batch to determine212// whether there are any more elements213214// test if the current set of entries has been processed215if (posn == limit) {216getNextBatch();217}218219// test if any unprocessed entries exist220if (posn < limit) {221return true;222} else {223224try {225// try to process another referral226return hasMoreReferrals();227228} catch (LdapReferralException |229LimitExceededException |230PartialResultException e) {231cleanup();232throw e;233234} catch (NamingException e) {235cleanup();236PartialResultException pre = new PartialResultException();237pre.setRootCause(e);238throw pre;239}240}241}242243/*244* Retrieve the next entry.245*/246private T nextImpl() throws NamingException {247try {248return nextAux();249} catch (NamingException e) {250cleanup();251throw cont.fillInException(e);252}253}254255private T nextAux() throws NamingException {256if (posn == limit) {257getNextBatch(); // updates posn and limit258}259260if (posn >= limit) {261cleanup();262throw new NoSuchElementException("invalid enumeration handle");263}264265LdapEntry result = entries.elementAt(posn++);266267// gets and outputs DN from the entry268return createItem(result.DN, result.attributes, result.respCtls);269}270271protected final String getAtom(String dn) {272// need to strip off all but lowest component of dn273// so that is relative to current context (currentDN)274try {275Name parsed = new LdapName(dn);276return parsed.get(parsed.size() - 1);277} catch (NamingException e) {278return dn;279}280}281282protected abstract T createItem(String dn, Attributes attrs,283Vector<Control> respCtls) throws NamingException;284285/*286* Append the supplied (chain of) referrals onto the287* end of the current (chain of) referrals.288*/289@Override290public void appendUnprocessedReferrals(LdapReferralException ex) {291if (refEx != null) {292refEx = refEx.appendUnprocessedReferrals(ex);293} else {294refEx = ex.appendUnprocessedReferrals(refEx);295}296}297298final void setNamingException(NamingException e) {299errEx = e;300}301302protected abstract AbstractLdapNamingEnumeration<? extends NameClassPair> getReferredResults(303LdapReferralContext refCtx) throws NamingException;304305/*306* Iterate through the URLs of a referral. If successful then perform307* a search operation and merge the received results with the current308* results.309*/310protected final boolean hasMoreReferrals() throws NamingException {311312if ((refEx != null) &&313(refEx.hasMoreReferrals() ||314refEx.hasMoreReferralExceptions()315&& !(errEx instanceof LimitExceededException))) {316317if (homeCtx.handleReferrals == LdapClient.LDAP_REF_THROW) {318throw (NamingException)(refEx.fillInStackTrace());319}320321// process the referrals sequentially322while (true) {323324LdapReferralContext refCtx =325(LdapReferralContext)refEx.getReferralContext(326homeCtx.envprops, homeCtx.reqCtls);327328try {329330update(getReferredResults(refCtx));331break;332333} catch (LdapReferralException re) {334335// record a previous exception336if (errEx == null) {337errEx = re.getNamingException();338}339refEx = re;340continue;341342} finally {343// Make sure we close referral context344refCtx.close();345}346}347return hasMoreImpl();348349} else {350cleanup();351352if (errEx != null) {353throw errEx;354}355return (false);356}357}358359/*360* Merge the entries and/or referrals from the supplied enumeration361* with those of the current enumeration.362*/363protected void update(AbstractLdapNamingEnumeration<? extends NameClassPair> ne) {364// Cleanup previous context first365homeCtx.decEnumCount();366367// New enum will have already incremented enum count and recorded clnt368homeCtx = ne.homeCtx;369enumClnt = ne.enumClnt;370371// Do this to prevent referral enumeration (ne) from decrementing372// enum count because we'll be doing that here from this373// enumeration.374ne.homeCtx = null;375376// Record rest of information from new enum377posn = ne.posn;378limit = ne.limit;379res = ne.res;380entries = ne.entries;381refEx = ne.refEx;382listArg = ne.listArg;383}384385@SuppressWarnings("deprecation")386protected final void finalize() {387cleanup();388}389390protected final void cleanup() {391if (cleaned) return; // been there; done that392393if(enumClnt != null) {394enumClnt.clearSearchReply(res, homeCtx.reqCtls);395}396397enumClnt = null;398cleaned = true;399if (homeCtx != null) {400homeCtx.decEnumCount();401homeCtx = null;402}403}404405@Override406public final void close() {407cleanup();408}409}410411412