001: /*
002: * Copyright 1999-2003 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package com.sun.jndi.ldap;
027:
028: import javax.naming.*;
029: import javax.naming.directory.*;
030: import javax.naming.spi.*;
031:
032: import com.sun.jndi.toolkit.ctx.Continuation;
033: import java.util.NoSuchElementException;
034: import java.util.Vector;
035: import javax.naming.ldap.LdapName;
036:
037: /**
038: * Basic enumeration for NameClassPair, Binding, and SearchResults.
039: */
040:
041: class LdapNamingEnumeration implements NamingEnumeration,
042: ReferralEnumeration {
043: protected Name listArg;
044:
045: private boolean cleaned = false;
046: private LdapResult res;
047: private LdapClient enumClnt;
048: private Continuation cont; // used to fill in exceptions
049: private Vector entries = null;
050: private int limit = 0;
051: private int posn = 0;
052: protected LdapCtx homeCtx;
053: private LdapReferralException refEx = null;
054: private NamingException errEx = null;
055:
056: private static final String defaultClassName = DirContext.class
057: .getName();
058:
059: /*
060: * Record the next set of entries and/or referrals.
061: */
062: LdapNamingEnumeration(LdapCtx homeCtx, LdapResult answer,
063: Name listArg, Continuation cont) throws NamingException {
064:
065: // These checks are to accommodate referrals and limit exceptions
066: // which will generate an enumeration and defer the exception
067: // to be thrown at the end of the enumeration.
068: // All other exceptions are thrown immediately.
069: // Exceptions shouldn't be thrown here anyhow because
070: // process_return_code() is called before the constructor
071: // is called, so these are just safety checks.
072:
073: if ((answer.status != LdapClient.LDAP_SUCCESS)
074: && (answer.status != LdapClient.LDAP_SIZE_LIMIT_EXCEEDED)
075: && (answer.status != LdapClient.LDAP_TIME_LIMIT_EXCEEDED)
076: && (answer.status != LdapClient.LDAP_ADMIN_LIMIT_EXCEEDED)
077: && (answer.status != LdapClient.LDAP_REFERRAL)
078: && (answer.status != LdapClient.LDAP_PARTIAL_RESULTS)) {
079:
080: // %%% need to deal with referral
081: NamingException e = new NamingException(
082: LdapClient.getErrorMessage(answer.status,
083: answer.errorMessage));
084:
085: throw cont.fillInException(e);
086: }
087:
088: // otherwise continue
089:
090: res = answer;
091: entries = answer.entries;
092: limit = (entries == null) ? 0 : entries.size(); // handle empty set
093: this .listArg = listArg;
094: this .cont = cont;
095:
096: if (answer.refEx != null) {
097: refEx = answer.refEx;
098: }
099:
100: // Ensures that context won't get closed from underneath us
101: this .homeCtx = homeCtx;
102: homeCtx.incEnumCount();
103: enumClnt = homeCtx.clnt; // remember
104: }
105:
106: public Object nextElement() {
107: try {
108: return next();
109: } catch (NamingException e) {
110: // can't throw exception
111: cleanup();
112: return null;
113: }
114: }
115:
116: public boolean hasMoreElements() {
117: try {
118: return hasMore();
119: } catch (NamingException e) {
120: // can't throw exception
121: cleanup();
122: return false;
123: }
124: }
125:
126: /*
127: * Retrieve the next set of entries and/or referrals.
128: */
129: private void getNextBatch() throws NamingException {
130:
131: res = homeCtx.getSearchReply(enumClnt, res);
132: if (res == null) {
133: limit = posn = 0;
134: return;
135: }
136:
137: entries = res.entries;
138: limit = (entries == null) ? 0 : entries.size(); // handle empty set
139: posn = 0; // reset
140:
141: // mimimize the number of calls to processReturnCode()
142: // (expensive when batchSize is small and there are many results)
143: if ((res.status != LdapClient.LDAP_SUCCESS)
144: || ((res.status == LdapClient.LDAP_SUCCESS) && (res.referrals != null))) {
145:
146: try {
147: // convert referrals into a chain of LdapReferralException
148: homeCtx.processReturnCode(res, listArg);
149:
150: } catch (LimitExceededException e) {
151: setNamingException(e);
152:
153: } catch (PartialResultException e) {
154: setNamingException(e);
155: }
156: }
157:
158: // merge any newly received referrals with any current referrals
159: if (res.refEx != null) {
160: if (refEx == null) {
161: refEx = res.refEx;
162: } else {
163: refEx = refEx.appendUnprocessedReferrals(res.refEx);
164: }
165: res.refEx = null; // reset
166: }
167:
168: if (res.resControls != null) {
169: homeCtx.respCtls = res.resControls;
170: }
171: }
172:
173: private boolean more = true; // assume we have something to start with
174: private boolean hasMoreCalled = false;
175:
176: /*
177: * Test if unprocessed entries or referrals exist.
178: */
179: public boolean hasMore() throws NamingException {
180:
181: if (hasMoreCalled) {
182: return more;
183: }
184:
185: hasMoreCalled = true;
186:
187: if (!more) {
188: return false;
189: } else {
190: return (more = hasMoreImpl());
191: }
192: }
193:
194: /*
195: * Retrieve the next entry.
196: */
197: public Object next() throws NamingException {
198:
199: if (!hasMoreCalled) {
200: hasMore();
201: }
202: hasMoreCalled = false;
203: return nextImpl();
204: }
205:
206: /*
207: * Test if unprocessed entries or referrals exist.
208: */
209: private boolean hasMoreImpl() throws NamingException {
210: // when page size is supported, this
211: // might generate an exception while attempting
212: // to fetch the next batch to determine
213: // whether there are any more elements
214:
215: // test if the current set of entries has been processed
216: if (posn == limit) {
217: getNextBatch();
218: }
219:
220: // test if any unprocessed entries exist
221: if (posn < limit) {
222: return true;
223: } else {
224:
225: try {
226: // try to process another referral
227: return hasMoreReferrals();
228:
229: } catch (LdapReferralException e) {
230: cleanup();
231: throw e;
232:
233: } catch (LimitExceededException e) {
234: cleanup();
235: throw e;
236:
237: } catch (PartialResultException e) {
238: cleanup();
239: throw e;
240:
241: } catch (NamingException e) {
242: cleanup();
243: PartialResultException pre = new PartialResultException();
244: pre.setRootCause(e);
245: throw pre;
246: }
247: }
248: }
249:
250: /*
251: * Retrieve the next entry.
252: */
253: private Object nextImpl() throws NamingException {
254: try {
255: return nextAux();
256: } catch (NamingException e) {
257: cleanup();
258: throw cont.fillInException(e);
259: }
260: }
261:
262: private Object nextAux() throws NamingException {
263: if (posn == limit) {
264: getNextBatch(); // updates posn and limit
265: }
266:
267: if (posn >= limit) {
268: cleanup();
269: throw new NoSuchElementException(
270: "invalid enumeration handle");
271: }
272:
273: LdapEntry result = (LdapEntry) entries.elementAt(posn++);
274:
275: // gets and outputs DN from the entry
276: return createItem(result.DN, result.attributes, result.respCtls);
277: }
278:
279: protected String getAtom(String dn) {
280: String atom;
281: // need to strip off all but lowest component of dn
282: // so that is relative to current context (currentDN)
283: try {
284: Name parsed = new LdapName(dn);
285: return parsed.get(parsed.size() - 1);
286: } catch (NamingException e) {
287: return dn;
288: }
289: }
290:
291: protected NameClassPair createItem(String dn, Attributes attrs,
292: Vector respCtls) throws NamingException {
293:
294: Attribute attr;
295: String className = null;
296:
297: // use the Java classname if present
298: if ((attr = attrs.get(Obj.JAVA_ATTRIBUTES[Obj.CLASSNAME])) != null) {
299: className = (String) attr.get();
300: } else {
301: className = defaultClassName;
302: }
303: CompositeName cn = new CompositeName();
304: cn.add(getAtom(dn));
305:
306: NameClassPair ncp;
307: if (respCtls != null) {
308: ncp = new NameClassPairWithControls(cn.toString(),
309: className, homeCtx.convertControls(respCtls));
310: } else {
311: ncp = new NameClassPair(cn.toString(), className);
312: }
313: ncp.setNameInNamespace(dn);
314: return ncp;
315: }
316:
317: /*
318: * Append the supplied (chain of) referrals onto the
319: * end of the current (chain of) referrals.
320: */
321: public void appendUnprocessedReferrals(LdapReferralException ex) {
322:
323: if (refEx != null) {
324: refEx = refEx.appendUnprocessedReferrals(ex);
325: } else {
326: refEx = ex.appendUnprocessedReferrals(refEx);
327: }
328: }
329:
330: void setNamingException(NamingException e) {
331: errEx = e;
332: }
333:
334: protected LdapNamingEnumeration getReferredResults(
335: LdapReferralContext refCtx) throws NamingException {
336: // repeat the original operation at the new context
337: return (LdapNamingEnumeration) refCtx.list(listArg);
338: }
339:
340: /*
341: * Iterate through the URLs of a referral. If successful then perform
342: * a search operation and merge the received results with the current
343: * results.
344: */
345: protected boolean hasMoreReferrals() throws NamingException {
346:
347: if ((refEx != null)
348: && (refEx.hasMoreReferrals() || refEx
349: .hasMoreReferralExceptions())) {
350:
351: if (homeCtx.handleReferrals == LdapClient.LDAP_REF_THROW) {
352: throw (NamingException) (refEx.fillInStackTrace());
353: }
354:
355: // process the referrals sequentially
356: while (true) {
357:
358: LdapReferralContext refCtx = (LdapReferralContext) refEx
359: .getReferralContext(homeCtx.envprops,
360: homeCtx.reqCtls);
361:
362: try {
363:
364: update(getReferredResults(refCtx));
365: break;
366:
367: } catch (LdapReferralException re) {
368:
369: // record a previous exception
370: if (errEx == null) {
371: errEx = re.getNamingException();
372: }
373: refEx = re;
374: continue;
375:
376: } finally {
377: // Make sure we close referral context
378: refCtx.close();
379: }
380: }
381: return hasMoreImpl();
382:
383: } else {
384: cleanup();
385:
386: if (errEx != null) {
387: throw errEx;
388: }
389: return (false);
390: }
391: }
392:
393: /*
394: * Merge the entries and/or referrals from the supplied enumeration
395: * with those of the current enumeration.
396: */
397: protected void update(LdapNamingEnumeration ne) {
398: // Cleanup previous context first
399: homeCtx.decEnumCount();
400:
401: // New enum will have already incremented enum count and recorded clnt
402: homeCtx = ne.homeCtx;
403: enumClnt = ne.enumClnt;
404:
405: // Do this to prevent referral enumeration (ne) from decrementing
406: // enum count because we'll be doing that here from this
407: // enumeration.
408: ne.homeCtx = null;
409:
410: // Record rest of information from new enum
411: posn = ne.posn;
412: limit = ne.limit;
413: res = ne.res;
414: entries = ne.entries;
415: refEx = ne.refEx;
416: listArg = ne.listArg;
417: }
418:
419: protected void finalize() {
420: cleanup();
421: }
422:
423: protected void cleanup() {
424: if (cleaned)
425: return; // been there; done that
426:
427: if (enumClnt != null) {
428: enumClnt.clearSearchReply(res, homeCtx.reqCtls);
429: }
430:
431: enumClnt = null;
432: cleaned = true;
433: if (homeCtx != null) {
434: homeCtx.decEnumCount();
435: homeCtx = null;
436: }
437: }
438:
439: public void close() {
440: cleanup();
441: }
442: }
|