/*
 * Copyright (c) 1999, 2003, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package com.sun.jndi.ldap;

import javax.naming.*;
import javax.naming.directory.*;
import javax.naming.spi.*;

import com.sun.jndi.toolkit.ctx.Continuation;
import java.util.NoSuchElementException;
import java.util.Vector;
import javax.naming.ldap.LdapName;

Basic enumeration for NameClassPair, Binding, and SearchResults.
/** * Basic enumeration for NameClassPair, Binding, and SearchResults. */
class LdapNamingEnumeration implements NamingEnumeration, ReferralEnumeration { protected Name listArg; private boolean cleaned = false; private LdapResult res; private LdapClient enumClnt; private Continuation cont; // used to fill in exceptions private Vector entries = null; private int limit = 0; private int posn = 0; protected LdapCtx homeCtx; private LdapReferralException refEx = null; private NamingException errEx = null; private static final String defaultClassName = DirContext.class.getName(); /* * Record the next set of entries and/or referrals. */ LdapNamingEnumeration(LdapCtx homeCtx, LdapResult answer, Name listArg, Continuation cont) throws NamingException { // These checks are to accommodate referrals and limit exceptions // which will generate an enumeration and defer the exception // to be thrown at the end of the enumeration. // All other exceptions are thrown immediately. // Exceptions shouldn't be thrown here anyhow because // process_return_code() is called before the constructor // is called, so these are just safety checks. if ((answer.status != LdapClient.LDAP_SUCCESS) && (answer.status != LdapClient.LDAP_SIZE_LIMIT_EXCEEDED) && (answer.status != LdapClient.LDAP_TIME_LIMIT_EXCEEDED) && (answer.status != LdapClient.LDAP_ADMIN_LIMIT_EXCEEDED) && (answer.status != LdapClient.LDAP_REFERRAL) && (answer.status != LdapClient.LDAP_PARTIAL_RESULTS)) { // %%% need to deal with referral NamingException e = new NamingException( LdapClient.getErrorMessage( answer.status, answer.errorMessage)); throw cont.fillInException(e); } // otherwise continue res = answer; entries = answer.entries; limit = (entries == null) ? 0 : entries.size(); // handle empty set this.listArg = listArg; this.cont = cont; if (answer.refEx != null) { refEx = answer.refEx; } // Ensures that context won't get closed from underneath us this.homeCtx = homeCtx; homeCtx.incEnumCount(); enumClnt = homeCtx.clnt; // remember } public Object nextElement() { try { return next(); } catch (NamingException e) { // can't throw exception cleanup(); return null; } } public boolean hasMoreElements() { try { return hasMore(); } catch (NamingException e) { // can't throw exception cleanup(); return false; } } /* * Retrieve the next set of entries and/or referrals. */ private void getNextBatch() throws NamingException { res = homeCtx.getSearchReply(enumClnt, res); if (res == null) { limit = posn = 0; return; } entries = res.entries; limit = (entries == null) ? 0 : entries.size(); // handle empty set posn = 0; // reset // mimimize the number of calls to processReturnCode() // (expensive when batchSize is small and there are many results) if ((res.status != LdapClient.LDAP_SUCCESS) || ((res.status == LdapClient.LDAP_SUCCESS) && (res.referrals != null))) { try { // convert referrals into a chain of LdapReferralException homeCtx.processReturnCode(res, listArg); } catch (LimitExceededException e) { setNamingException(e); } catch (PartialResultException e) { setNamingException(e); } } // merge any newly received referrals with any current referrals if (res.refEx != null) { if (refEx == null) { refEx = res.refEx; } else { refEx = refEx.appendUnprocessedReferrals(res.refEx); } res.refEx = null; // reset } if (res.resControls != null) { homeCtx.respCtls = res.resControls; } } private boolean more = true; // assume we have something to start with private boolean hasMoreCalled = false; /* * Test if unprocessed entries or referrals exist. */ public boolean hasMore() throws NamingException { if (hasMoreCalled) { return more; } hasMoreCalled = true; if (!more) { return false; } else { return (more = hasMoreImpl()); } } /* * Retrieve the next entry. */ public Object next() throws NamingException { if (!hasMoreCalled) { hasMore(); } hasMoreCalled = false; return nextImpl(); } /* * Test if unprocessed entries or referrals exist. */ private boolean hasMoreImpl() throws NamingException { // when page size is supported, this // might generate an exception while attempting // to fetch the next batch to determine // whether there are any more elements // test if the current set of entries has been processed if (posn == limit) { getNextBatch(); } // test if any unprocessed entries exist if (posn < limit) { return true; } else { try { // try to process another referral return hasMoreReferrals(); } catch (LdapReferralException e) { cleanup(); throw e; } catch (LimitExceededException e) { cleanup(); throw e; } catch (PartialResultException e) { cleanup(); throw e; } catch (NamingException e) { cleanup(); PartialResultException pre = new PartialResultException(); pre.setRootCause(e); throw pre; } } } /* * Retrieve the next entry. */ private Object nextImpl() throws NamingException { try { return nextAux(); } catch (NamingException e) { cleanup(); throw cont.fillInException(e); } } private Object nextAux() throws NamingException { if (posn == limit) { getNextBatch(); // updates posn and limit } if (posn >= limit) { cleanup(); throw new NoSuchElementException("invalid enumeration handle"); } LdapEntry result = (LdapEntry)entries.elementAt(posn++); // gets and outputs DN from the entry return createItem(result.DN, result.attributes, result.respCtls); } protected String getAtom(String dn) { String atom; // need to strip off all but lowest component of dn // so that is relative to current context (currentDN) try { Name parsed = new LdapName(dn); return parsed.get(parsed.size() - 1); } catch (NamingException e) { return dn; } } protected NameClassPair createItem(String dn, Attributes attrs, Vector respCtls) throws NamingException { Attribute attr; String className = null; // use the Java classname if present if ((attr = attrs.get(Obj.JAVA_ATTRIBUTES[Obj.CLASSNAME])) != null) { className = (String)attr.get(); } else { className = defaultClassName; } CompositeName cn = new CompositeName(); cn.add(getAtom(dn)); NameClassPair ncp; if (respCtls != null) { ncp = new NameClassPairWithControls( cn.toString(), className, homeCtx.convertControls(respCtls)); } else { ncp = new NameClassPair(cn.toString(), className); } ncp.setNameInNamespace(dn); return ncp; } /* * Append the supplied (chain of) referrals onto the * end of the current (chain of) referrals. */ public void appendUnprocessedReferrals(LdapReferralException ex) { if (refEx != null) { refEx = refEx.appendUnprocessedReferrals(ex); } else { refEx = ex.appendUnprocessedReferrals(refEx); } } void setNamingException(NamingException e) { errEx = e; } protected LdapNamingEnumeration getReferredResults(LdapReferralContext refCtx) throws NamingException { // repeat the original operation at the new context return (LdapNamingEnumeration)refCtx.list(listArg); } /* * Iterate through the URLs of a referral. If successful then perform * a search operation and merge the received results with the current * results. */ protected boolean hasMoreReferrals() throws NamingException { if ((refEx != null) && (refEx.hasMoreReferrals() || refEx.hasMoreReferralExceptions())) { if (homeCtx.handleReferrals == LdapClient.LDAP_REF_THROW) { throw (NamingException)(refEx.fillInStackTrace()); } // process the referrals sequentially while (true) { LdapReferralContext refCtx = (LdapReferralContext)refEx.getReferralContext( homeCtx.envprops, homeCtx.reqCtls); try { update(getReferredResults(refCtx)); break; } catch (LdapReferralException re) { // record a previous exception if (errEx == null) { errEx = re.getNamingException(); } refEx = re; continue; } finally { // Make sure we close referral context refCtx.close(); } } return hasMoreImpl(); } else { cleanup(); if (errEx != null) { throw errEx; } return (false); } } /* * Merge the entries and/or referrals from the supplied enumeration * with those of the current enumeration. */ protected void update(LdapNamingEnumeration ne) { // Cleanup previous context first homeCtx.decEnumCount(); // New enum will have already incremented enum count and recorded clnt homeCtx = ne.homeCtx; enumClnt = ne.enumClnt; // Do this to prevent referral enumeration (ne) from decrementing // enum count because we'll be doing that here from this // enumeration. ne.homeCtx = null; // Record rest of information from new enum posn = ne.posn; limit = ne.limit; res = ne.res; entries = ne.entries; refEx = ne.refEx; listArg = ne.listArg; } protected void finalize() { cleanup(); } protected void cleanup() { if (cleaned) return; // been there; done that if(enumClnt != null) { enumClnt.clearSearchReply(res, homeCtx.reqCtls); } enumClnt = null; cleaned = true; if (homeCtx != null) { homeCtx.decEnumCount(); homeCtx = null; } } public void close() { cleanup(); } }