1 /* 2 * Copyright (c) 1999, 2003, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.jndi.ldap; 27 28 import javax.naming.*; 29 import javax.naming.directory.*; 30 import javax.naming.spi.*; 31 32 import com.sun.jndi.toolkit.ctx.Continuation; 33 import java.util.NoSuchElementException; 34 import java.util.Vector; 35 import javax.naming.ldap.LdapName; 36 37 /** 38 * Basic enumeration for NameClassPair, Binding, and SearchResults. 39 */ 40 41 class LdapNamingEnumeration implements NamingEnumeration, ReferralEnumeration { 42 protected Name listArg; 43 44 private boolean cleaned = false; 45 private LdapResult res; 46 private LdapClient enumClnt; 47 private Continuation cont; // used to fill in exceptions 48 private Vector entries = null; 49 private int limit = 0; 50 private int posn = 0; 51 protected LdapCtx homeCtx; 52 private LdapReferralException refEx = null; 53 private NamingException errEx = null; 54 55 private static final String defaultClassName = DirContext.class.getName(); 56 57 /* 58 * Record the next set of entries and/or referrals. 59 */ 60 LdapNamingEnumeration(LdapCtx homeCtx, LdapResult answer, Name listArg, 61 Continuation cont) throws NamingException { 62 63 // These checks are to accommodate referrals and limit exceptions 64 // which will generate an enumeration and defer the exception 65 // to be thrown at the end of the enumeration. 66 // All other exceptions are thrown immediately. 67 // Exceptions shouldn't be thrown here anyhow because 68 // process_return_code() is called before the constructor 69 // is called, so these are just safety checks. 70 71 if ((answer.status != LdapClient.LDAP_SUCCESS) && 72 (answer.status != LdapClient.LDAP_SIZE_LIMIT_EXCEEDED) && 73 (answer.status != LdapClient.LDAP_TIME_LIMIT_EXCEEDED) && 74 (answer.status != LdapClient.LDAP_ADMIN_LIMIT_EXCEEDED) && 75 (answer.status != LdapClient.LDAP_REFERRAL) && 76 (answer.status != LdapClient.LDAP_PARTIAL_RESULTS)) { 77 78 // %%% need to deal with referral 79 NamingException e = new NamingException( 80 LdapClient.getErrorMessage( 81 answer.status, answer.errorMessage)); 82 83 throw cont.fillInException(e); 84 } 85 86 // otherwise continue 87 88 res = answer; 89 entries = answer.entries; 90 limit = (entries == null) ? 0 : entries.size(); // handle empty set 91 this.listArg = listArg; 92 this.cont = cont; 93 94 if (answer.refEx != null) { 95 refEx = answer.refEx; 96 } 97 98 // Ensures that context won't get closed from underneath us 99 this.homeCtx = homeCtx; 100 homeCtx.incEnumCount(); 101 enumClnt = homeCtx.clnt; // remember 102 } 103 104 public Object nextElement() { 105 try { 106 return next(); 107 } catch (NamingException e) { 108 // can't throw exception 109 cleanup(); 110 return null; 111 } 112 } 113 114 public boolean hasMoreElements() { 115 try { 116 return hasMore(); 117 } catch (NamingException e) { 118 // can't throw exception 119 cleanup(); 120 return false; 121 } 122 } 123 124 /* 125 * Retrieve the next set of entries and/or referrals. 126 */ 127 private void getNextBatch() throws NamingException { 128 129 res = homeCtx.getSearchReply(enumClnt, res); 130 if (res == null) { 131 limit = posn = 0; 132 return; 133 } 134 135 entries = res.entries; 136 limit = (entries == null) ? 0 : entries.size(); // handle empty set 137 posn = 0; // reset 138 139 // mimimize the number of calls to processReturnCode() 140 // (expensive when batchSize is small and there are many results) 141 if ((res.status != LdapClient.LDAP_SUCCESS) || 142 ((res.status == LdapClient.LDAP_SUCCESS) && 143 (res.referrals != null))) { 144 145 try { 146 // convert referrals into a chain of LdapReferralException 147 homeCtx.processReturnCode(res, listArg); 148 149 } catch (LimitExceededException e) { 150 setNamingException(e); 151 152 } catch (PartialResultException e) { 153 setNamingException(e); 154 } 155 } 156 157 // merge any newly received referrals with any current referrals 158 if (res.refEx != null) { 159 if (refEx == null) { 160 refEx = res.refEx; 161 } else { 162 refEx = refEx.appendUnprocessedReferrals(res.refEx); 163 } 164 res.refEx = null; // reset 165 } 166 167 if (res.resControls != null) { 168 homeCtx.respCtls = res.resControls; 169 } 170 } 171 172 private boolean more = true; // assume we have something to start with 173 private boolean hasMoreCalled = false; 174 175 /* 176 * Test if unprocessed entries or referrals exist. 177 */ 178 public boolean hasMore() throws NamingException { 179 180 if (hasMoreCalled) { 181 return more; 182 } 183 184 hasMoreCalled = true; 185 186 if (!more) { 187 return false; 188 } else { 189 return (more = hasMoreImpl()); 190 } 191 } 192 193 /* 194 * Retrieve the next entry. 195 */ 196 public Object next() throws NamingException { 197 198 if (!hasMoreCalled) { 199 hasMore(); 200 } 201 hasMoreCalled = false; 202 return nextImpl(); 203 } 204 205 /* 206 * Test if unprocessed entries or referrals exist. 207 */ 208 private boolean hasMoreImpl() throws NamingException { 209 // when page size is supported, this 210 // might generate an exception while attempting 211 // to fetch the next batch to determine 212 // whether there are any more elements 213 214 // test if the current set of entries has been processed 215 if (posn == limit) { 216 getNextBatch(); 217 } 218 219 // test if any unprocessed entries exist 220 if (posn < limit) { 221 return true; 222 } else { 223 224 try { 225 // try to process another referral 226 return hasMoreReferrals(); 227 228 } catch (LdapReferralException e) { 229 cleanup(); 230 throw e; 231 232 } catch (LimitExceededException e) { 233 cleanup(); 234 throw e; 235 236 } catch (PartialResultException e) { 237 cleanup(); 238 throw e; 239 240 } catch (NamingException e) { 241 cleanup(); 242 PartialResultException pre = new PartialResultException(); 243 pre.setRootCause(e); 244 throw pre; 245 } 246 } 247 } 248 249 /* 250 * Retrieve the next entry. 251 */ 252 private Object nextImpl() throws NamingException { 253 try { 254 return nextAux(); 255 } catch (NamingException e) { 256 cleanup(); 257 throw cont.fillInException(e); 258 } 259 } 260 261 private Object nextAux() throws NamingException { 262 if (posn == limit) { 263 getNextBatch(); // updates posn and limit 264 } 265 266 if (posn >= limit) { 267 cleanup(); 268 throw new NoSuchElementException("invalid enumeration handle"); 269 } 270 271 LdapEntry result = (LdapEntry)entries.elementAt(posn++); 272 273 // gets and outputs DN from the entry 274 return createItem(result.DN, result.attributes, result.respCtls); 275 } 276 277 protected String getAtom(String dn) { 278 String atom; 279 // need to strip off all but lowest component of dn 280 // so that is relative to current context (currentDN) 281 try { 282 Name parsed = new LdapName(dn); 283 return parsed.get(parsed.size() - 1); 284 } catch (NamingException e) { 285 return dn; 286 } 287 } 288 289 protected NameClassPair createItem(String dn, Attributes attrs, 290 Vector respCtls) throws NamingException { 291 292 Attribute attr; 293 String className = null; 294 295 // use the Java classname if present 296 if ((attr = attrs.get(Obj.JAVA_ATTRIBUTES[Obj.CLASSNAME])) != null) { 297 className = (String)attr.get(); 298 } else { 299 className = defaultClassName; 300 } 301 CompositeName cn = new CompositeName(); 302 cn.add(getAtom(dn)); 303 304 NameClassPair ncp; 305 if (respCtls != null) { 306 ncp = new NameClassPairWithControls( 307 cn.toString(), className, 308 homeCtx.convertControls(respCtls)); 309 } else { 310 ncp = new NameClassPair(cn.toString(), className); 311 } 312 ncp.setNameInNamespace(dn); 313 return ncp; 314 } 315 316 /* 317 * Append the supplied (chain of) referrals onto the 318 * end of the current (chain of) referrals. 319 */ 320 public void appendUnprocessedReferrals(LdapReferralException ex) { 321 322 if (refEx != null) { 323 refEx = refEx.appendUnprocessedReferrals(ex); 324 } else { 325 refEx = ex.appendUnprocessedReferrals(refEx); 326 } 327 } 328 329 void setNamingException(NamingException e) { 330 errEx = e; 331 } 332 333 protected LdapNamingEnumeration 334 getReferredResults(LdapReferralContext refCtx) throws NamingException { 335 // repeat the original operation at the new context 336 return (LdapNamingEnumeration)refCtx.list(listArg); 337 } 338 339 /* 340 * Iterate through the URLs of a referral. If successful then perform 341 * a search operation and merge the received results with the current 342 * results. 343 */ 344 protected boolean hasMoreReferrals() throws NamingException { 345 346 if ((refEx != null) && 347 (refEx.hasMoreReferrals() || 348 refEx.hasMoreReferralExceptions())) { 349 350 if (homeCtx.handleReferrals == LdapClient.LDAP_REF_THROW) { 351 throw (NamingException)(refEx.fillInStackTrace()); 352 } 353 354 // process the referrals sequentially 355 while (true) { 356 357 LdapReferralContext refCtx = 358 (LdapReferralContext)refEx.getReferralContext( 359 homeCtx.envprops, homeCtx.reqCtls); 360 361 try { 362 363 update(getReferredResults(refCtx)); 364 break; 365 366 } catch (LdapReferralException re) { 367 368 // record a previous exception 369 if (errEx == null) { 370 errEx = re.getNamingException(); 371 } 372 refEx = re; 373 continue; 374 375 } finally { 376 // Make sure we close referral context 377 refCtx.close(); 378 } 379 } 380 return hasMoreImpl(); 381 382 } else { 383 cleanup(); 384 385 if (errEx != null) { 386 throw errEx; 387 } 388 return (false); 389 } 390 } 391 392 /* 393 * Merge the entries and/or referrals from the supplied enumeration 394 * with those of the current enumeration. 395 */ 396 protected void update(LdapNamingEnumeration ne) { 397 // Cleanup previous context first 398 homeCtx.decEnumCount(); 399 400 // New enum will have already incremented enum count and recorded clnt 401 homeCtx = ne.homeCtx; 402 enumClnt = ne.enumClnt; 403 404 // Do this to prevent referral enumeration (ne) from decrementing 405 // enum count because we'll be doing that here from this 406 // enumeration. 407 ne.homeCtx = null; 408 409 // Record rest of information from new enum 410 posn = ne.posn; 411 limit = ne.limit; 412 res = ne.res; 413 entries = ne.entries; 414 refEx = ne.refEx; 415 listArg = ne.listArg; 416 } 417 418 protected void finalize() { 419 cleanup(); 420 } 421 422 protected void cleanup() { 423 if (cleaned) return; // been there; done that 424 425 if(enumClnt != null) { 426 enumClnt.clearSearchReply(res, homeCtx.reqCtls); 427 } 428 429 enumClnt = null; 430 cleaned = true; 431 if (homeCtx != null) { 432 homeCtx.decEnumCount(); 433 homeCtx = null; 434 } 435 } 436 437 public void close() { 438 cleanup(); 439 } 440 }