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