0N/A/*
2362N/A * Copyright (c) 1999, 2003, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage com.sun.jndi.ldap;
0N/A
0N/Aimport javax.naming.*;
0N/Aimport javax.naming.directory.*;
0N/Aimport javax.naming.spi.*;
0N/A
0N/Aimport com.sun.jndi.toolkit.ctx.Continuation;
0N/Aimport java.util.NoSuchElementException;
0N/Aimport java.util.Vector;
0N/Aimport javax.naming.ldap.LdapName;
0N/A
0N/A/**
0N/A * Basic enumeration for NameClassPair, Binding, and SearchResults.
0N/A */
0N/A
0N/Aclass LdapNamingEnumeration implements NamingEnumeration, ReferralEnumeration {
0N/A protected Name listArg;
0N/A
0N/A private boolean cleaned = false;
0N/A private LdapResult res;
0N/A private LdapClient enumClnt;
0N/A private Continuation cont; // used to fill in exceptions
0N/A private Vector entries = null;
0N/A private int limit = 0;
0N/A private int posn = 0;
0N/A protected LdapCtx homeCtx;
0N/A private LdapReferralException refEx = null;
0N/A private NamingException errEx = null;
0N/A
0N/A private static final String defaultClassName = DirContext.class.getName();
0N/A
0N/A /*
0N/A * Record the next set of entries and/or referrals.
0N/A */
0N/A LdapNamingEnumeration(LdapCtx homeCtx, LdapResult answer, Name listArg,
0N/A Continuation cont) throws NamingException {
0N/A
0N/A // These checks are to accommodate referrals and limit exceptions
0N/A // which will generate an enumeration and defer the exception
0N/A // to be thrown at the end of the enumeration.
0N/A // All other exceptions are thrown immediately.
0N/A // Exceptions shouldn't be thrown here anyhow because
0N/A // process_return_code() is called before the constructor
0N/A // is called, so these are just safety checks.
0N/A
0N/A if ((answer.status != LdapClient.LDAP_SUCCESS) &&
0N/A (answer.status != LdapClient.LDAP_SIZE_LIMIT_EXCEEDED) &&
0N/A (answer.status != LdapClient.LDAP_TIME_LIMIT_EXCEEDED) &&
0N/A (answer.status != LdapClient.LDAP_ADMIN_LIMIT_EXCEEDED) &&
0N/A (answer.status != LdapClient.LDAP_REFERRAL) &&
0N/A (answer.status != LdapClient.LDAP_PARTIAL_RESULTS)) {
0N/A
0N/A // %%% need to deal with referral
0N/A NamingException e = new NamingException(
0N/A LdapClient.getErrorMessage(
0N/A answer.status, answer.errorMessage));
0N/A
0N/A throw cont.fillInException(e);
0N/A }
0N/A
0N/A // otherwise continue
0N/A
0N/A res = answer;
0N/A entries = answer.entries;
0N/A limit = (entries == null) ? 0 : entries.size(); // handle empty set
0N/A this.listArg = listArg;
0N/A this.cont = cont;
0N/A
0N/A if (answer.refEx != null) {
0N/A refEx = answer.refEx;
0N/A }
0N/A
0N/A // Ensures that context won't get closed from underneath us
0N/A this.homeCtx = homeCtx;
0N/A homeCtx.incEnumCount();
0N/A enumClnt = homeCtx.clnt; // remember
0N/A }
0N/A
0N/A public Object nextElement() {
0N/A try {
0N/A return next();
0N/A } catch (NamingException e) {
0N/A // can't throw exception
0N/A cleanup();
0N/A return null;
0N/A }
0N/A }
0N/A
0N/A public boolean hasMoreElements() {
0N/A try {
0N/A return hasMore();
0N/A } catch (NamingException e) {
0N/A // can't throw exception
0N/A cleanup();
0N/A return false;
0N/A }
0N/A }
0N/A
0N/A /*
0N/A * Retrieve the next set of entries and/or referrals.
0N/A */
0N/A private void getNextBatch() throws NamingException {
0N/A
0N/A res = homeCtx.getSearchReply(enumClnt, res);
0N/A if (res == null) {
0N/A limit = posn = 0;
0N/A return;
0N/A }
0N/A
0N/A entries = res.entries;
0N/A limit = (entries == null) ? 0 : entries.size(); // handle empty set
0N/A posn = 0; // reset
0N/A
0N/A // mimimize the number of calls to processReturnCode()
0N/A // (expensive when batchSize is small and there are many results)
0N/A if ((res.status != LdapClient.LDAP_SUCCESS) ||
0N/A ((res.status == LdapClient.LDAP_SUCCESS) &&
0N/A (res.referrals != null))) {
0N/A
0N/A try {
0N/A // convert referrals into a chain of LdapReferralException
0N/A homeCtx.processReturnCode(res, listArg);
0N/A
0N/A } catch (LimitExceededException e) {
0N/A setNamingException(e);
0N/A
0N/A } catch (PartialResultException e) {
0N/A setNamingException(e);
0N/A }
0N/A }
0N/A
0N/A // merge any newly received referrals with any current referrals
0N/A if (res.refEx != null) {
0N/A if (refEx == null) {
0N/A refEx = res.refEx;
0N/A } else {
0N/A refEx = refEx.appendUnprocessedReferrals(res.refEx);
0N/A }
0N/A res.refEx = null; // reset
0N/A }
0N/A
0N/A if (res.resControls != null) {
0N/A homeCtx.respCtls = res.resControls;
0N/A }
0N/A }
0N/A
0N/A private boolean more = true; // assume we have something to start with
0N/A private boolean hasMoreCalled = false;
0N/A
0N/A /*
0N/A * Test if unprocessed entries or referrals exist.
0N/A */
0N/A public boolean hasMore() throws NamingException {
0N/A
0N/A if (hasMoreCalled) {
0N/A return more;
0N/A }
0N/A
0N/A hasMoreCalled = true;
0N/A
0N/A if (!more) {
0N/A return false;
0N/A } else {
0N/A return (more = hasMoreImpl());
0N/A }
0N/A }
0N/A
0N/A /*
0N/A * Retrieve the next entry.
0N/A */
0N/A public Object next() throws NamingException {
0N/A
0N/A if (!hasMoreCalled) {
0N/A hasMore();
0N/A }
0N/A hasMoreCalled = false;
0N/A return nextImpl();
0N/A }
0N/A
0N/A /*
0N/A * Test if unprocessed entries or referrals exist.
0N/A */
0N/A private boolean hasMoreImpl() throws NamingException {
0N/A // when page size is supported, this
0N/A // might generate an exception while attempting
0N/A // to fetch the next batch to determine
0N/A // whether there are any more elements
0N/A
0N/A // test if the current set of entries has been processed
0N/A if (posn == limit) {
0N/A getNextBatch();
0N/A }
0N/A
0N/A // test if any unprocessed entries exist
0N/A if (posn < limit) {
0N/A return true;
0N/A } else {
0N/A
0N/A try {
0N/A // try to process another referral
0N/A return hasMoreReferrals();
0N/A
0N/A } catch (LdapReferralException e) {
0N/A cleanup();
0N/A throw e;
0N/A
0N/A } catch (LimitExceededException e) {
0N/A cleanup();
0N/A throw e;
0N/A
0N/A } catch (PartialResultException e) {
0N/A cleanup();
0N/A throw e;
0N/A
0N/A } catch (NamingException e) {
0N/A cleanup();
0N/A PartialResultException pre = new PartialResultException();
0N/A pre.setRootCause(e);
0N/A throw pre;
0N/A }
0N/A }
0N/A }
0N/A
0N/A /*
0N/A * Retrieve the next entry.
0N/A */
0N/A private Object nextImpl() throws NamingException {
0N/A try {
0N/A return nextAux();
0N/A } catch (NamingException e) {
0N/A cleanup();
0N/A throw cont.fillInException(e);
0N/A }
0N/A }
0N/A
0N/A private Object nextAux() throws NamingException {
0N/A if (posn == limit) {
0N/A getNextBatch(); // updates posn and limit
0N/A }
0N/A
0N/A if (posn >= limit) {
0N/A cleanup();
0N/A throw new NoSuchElementException("invalid enumeration handle");
0N/A }
0N/A
0N/A LdapEntry result = (LdapEntry)entries.elementAt(posn++);
0N/A
0N/A // gets and outputs DN from the entry
0N/A return createItem(result.DN, result.attributes, result.respCtls);
0N/A }
0N/A
0N/A protected String getAtom(String dn) {
0N/A String atom;
0N/A // need to strip off all but lowest component of dn
0N/A // so that is relative to current context (currentDN)
0N/A try {
0N/A Name parsed = new LdapName(dn);
0N/A return parsed.get(parsed.size() - 1);
0N/A } catch (NamingException e) {
0N/A return dn;
0N/A }
0N/A }
0N/A
0N/A protected NameClassPair createItem(String dn, Attributes attrs,
0N/A Vector respCtls) throws NamingException {
0N/A
0N/A Attribute attr;
0N/A String className = null;
0N/A
0N/A // use the Java classname if present
0N/A if ((attr = attrs.get(Obj.JAVA_ATTRIBUTES[Obj.CLASSNAME])) != null) {
0N/A className = (String)attr.get();
0N/A } else {
0N/A className = defaultClassName;
0N/A }
0N/A CompositeName cn = new CompositeName();
0N/A cn.add(getAtom(dn));
0N/A
0N/A NameClassPair ncp;
0N/A if (respCtls != null) {
0N/A ncp = new NameClassPairWithControls(
0N/A cn.toString(), className,
0N/A homeCtx.convertControls(respCtls));
0N/A } else {
0N/A ncp = new NameClassPair(cn.toString(), className);
0N/A }
0N/A ncp.setNameInNamespace(dn);
0N/A return ncp;
0N/A }
0N/A
0N/A /*
0N/A * Append the supplied (chain of) referrals onto the
0N/A * end of the current (chain of) referrals.
0N/A */
0N/A public void appendUnprocessedReferrals(LdapReferralException ex) {
0N/A
0N/A if (refEx != null) {
0N/A refEx = refEx.appendUnprocessedReferrals(ex);
0N/A } else {
0N/A refEx = ex.appendUnprocessedReferrals(refEx);
0N/A }
0N/A }
0N/A
0N/A void setNamingException(NamingException e) {
0N/A errEx = e;
0N/A }
0N/A
0N/A protected LdapNamingEnumeration
0N/A getReferredResults(LdapReferralContext refCtx) throws NamingException {
0N/A // repeat the original operation at the new context
0N/A return (LdapNamingEnumeration)refCtx.list(listArg);
0N/A }
0N/A
0N/A /*
0N/A * Iterate through the URLs of a referral. If successful then perform
0N/A * a search operation and merge the received results with the current
0N/A * results.
0N/A */
0N/A protected boolean hasMoreReferrals() throws NamingException {
0N/A
0N/A if ((refEx != null) &&
0N/A (refEx.hasMoreReferrals() ||
0N/A refEx.hasMoreReferralExceptions())) {
0N/A
0N/A if (homeCtx.handleReferrals == LdapClient.LDAP_REF_THROW) {
0N/A throw (NamingException)(refEx.fillInStackTrace());
0N/A }
0N/A
0N/A // process the referrals sequentially
0N/A while (true) {
0N/A
0N/A LdapReferralContext refCtx =
0N/A (LdapReferralContext)refEx.getReferralContext(
0N/A homeCtx.envprops, homeCtx.reqCtls);
0N/A
0N/A try {
0N/A
0N/A update(getReferredResults(refCtx));
0N/A break;
0N/A
0N/A } catch (LdapReferralException re) {
0N/A
0N/A // record a previous exception
0N/A if (errEx == null) {
0N/A errEx = re.getNamingException();
0N/A }
0N/A refEx = re;
0N/A continue;
0N/A
0N/A } finally {
0N/A // Make sure we close referral context
0N/A refCtx.close();
0N/A }
0N/A }
0N/A return hasMoreImpl();
0N/A
0N/A } else {
0N/A cleanup();
0N/A
0N/A if (errEx != null) {
0N/A throw errEx;
0N/A }
0N/A return (false);
0N/A }
0N/A }
0N/A
0N/A /*
0N/A * Merge the entries and/or referrals from the supplied enumeration
0N/A * with those of the current enumeration.
0N/A */
0N/A protected void update(LdapNamingEnumeration ne) {
0N/A // Cleanup previous context first
0N/A homeCtx.decEnumCount();
0N/A
0N/A // New enum will have already incremented enum count and recorded clnt
0N/A homeCtx = ne.homeCtx;
0N/A enumClnt = ne.enumClnt;
0N/A
0N/A // Do this to prevent referral enumeration (ne) from decrementing
0N/A // enum count because we'll be doing that here from this
0N/A // enumeration.
0N/A ne.homeCtx = null;
0N/A
0N/A // Record rest of information from new enum
0N/A posn = ne.posn;
0N/A limit = ne.limit;
0N/A res = ne.res;
0N/A entries = ne.entries;
0N/A refEx = ne.refEx;
0N/A listArg = ne.listArg;
0N/A }
0N/A
0N/A protected void finalize() {
0N/A cleanup();
0N/A }
0N/A
0N/A protected void cleanup() {
0N/A if (cleaned) return; // been there; done that
0N/A
0N/A if(enumClnt != null) {
0N/A enumClnt.clearSearchReply(res, homeCtx.reqCtls);
0N/A }
0N/A
0N/A enumClnt = null;
0N/A cleaned = true;
0N/A if (homeCtx != null) {
0N/A homeCtx.decEnumCount();
0N/A homeCtx = null;
0N/A }
0N/A }
0N/A
0N/A public void close() {
0N/A cleanup();
0N/A }
0N/A}