001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.xbean.naming.context; 018 019 import javax.naming.CompositeName; 020 import javax.naming.Context; 021 import javax.naming.InvalidNameException; 022 import javax.naming.Name; 023 import javax.naming.NameAlreadyBoundException; 024 import javax.naming.NameParser; 025 import javax.naming.NamingEnumeration; 026 import javax.naming.NamingException; 027 import javax.naming.NotContextException; 028 import javax.naming.LinkRef; 029 import javax.naming.NameNotFoundException; 030 import javax.naming.InitialContext; 031 import javax.naming.OperationNotSupportedException; 032 import javax.naming.NameClassPair; 033 import javax.naming.Binding; 034 035 import java.io.Serializable; 036 import java.util.Collections; 037 import java.util.Hashtable; 038 import java.util.Map; 039 040 public abstract class AbstractContext implements Context, NestedContextFactory, Serializable { 041 private static final long serialVersionUID = 6481918425692261483L; 042 private final String nameInNamespace; 043 private final Name parsedNameInNamespace; 044 private final ContextAccess contextAccess; 045 private final boolean modifiable; 046 private final ThreadLocal<Name> inCall = new ThreadLocal<Name>(); 047 048 protected AbstractContext(String nameInNamespace) { 049 this(nameInNamespace, ContextAccess.MODIFIABLE); 050 } 051 052 public AbstractContext(String nameInNamespace, ContextAccess contextAccess) { 053 this.nameInNamespace = nameInNamespace; 054 try { 055 this.parsedNameInNamespace = getNameParser().parse(nameInNamespace); 056 } catch (NamingException e) { 057 throw new RuntimeException(e); 058 } 059 this.contextAccess = contextAccess; 060 this.modifiable = contextAccess.isModifiable(getParsedNameInNamespace()); 061 } 062 063 public void close() throws NamingException { 064 //Ignore. Explicitly do not close the context 065 } 066 067 protected ContextAccess getContextAccess() { 068 return contextAccess; 069 } 070 071 // 072 // Lookup Binding 073 // 074 075 /** 076 * Gets the object bound to the name. The name may contain slashes. 077 * @param name the name 078 * @return the object bound to the name, or null if not found 079 */ 080 protected Object getDeepBinding(String name) { 081 return null; 082 } 083 084 /** 085 * Gets the object bound to the name. The name will not contain slashes. 086 * @param name the name 087 * @return the object bound to the name, or null if not found 088 * @throws javax.naming.NamingException on error 089 */ 090 protected Object getBinding(String name) throws NamingException { 091 Map<String, Object> bindings = getBindings(); 092 return bindings.get(name); 093 } 094 095 /** 096 * Finds the specified entry. Normally there is no need to override this method; instead you should 097 * simply implement the getDeepBindings(String) and getBindings(String) method. 098 * 099 * This method will follow links except for the final element which is always just returned without 100 * inspection. This means this method can be used to implement lookupLink. 101 * 102 * @param stringName the string version of the name; maybe null 103 * @param parsedName the parsed name; may be null 104 * @return the value bound to the name 105 * @throws NamingException if no value is bound to that name or if a problem occurs during the lookup 106 */ 107 protected Object lookup(String stringName, Name parsedName) throws NamingException { 108 if (stringName == null && parsedName == null) { 109 throw new IllegalArgumentException("Both stringName and parsedName are null"); 110 } 111 if (stringName == null) stringName = parsedName.toString(); 112 113 // try to look up the name directly (this is the fastest path) 114 Object directLookup = getDeepBinding(stringName); 115 if (directLookup != null) { 116 return ContextUtil.resolve(directLookup, stringName, parsedName, this); 117 } 118 119 // if the parsed name has no parts, they are asking for the current context 120 if (parsedName == null) parsedName = getNameParser().parse(stringName); 121 if (parsedName.isEmpty()) { 122 return this; 123 } 124 125 // we didn't find an entry, pop the first element off the parsed name and attempt to 126 // get a context from the bindings and delegate to that context 127 Object localValue; 128 String firstNameElement = parsedName.get(0); 129 if (firstNameElement.length() == 0) { 130 // the element is null... this is normally caused by looking up with a trailing '/' character 131 localValue = this; 132 } else { 133 localValue = getBinding(firstNameElement); 134 } 135 136 if (localValue != null) { 137 138 // if the name only had one part, we've looked up everything 139 if (parsedName.size() == 1) { 140 localValue = ContextUtil.resolve(localValue, stringName, parsedName, this); 141 return localValue; 142 } 143 144 // if we have a link ref, follow it 145 if (localValue instanceof LinkRef) { 146 LinkRef linkRef = (LinkRef) localValue; 147 localValue = lookup(linkRef.getLinkName()); 148 } 149 150 // we have more to lookup so we better have a context object 151 if (!(localValue instanceof Context)) { 152 throw new NameNotFoundException(stringName); 153 } 154 155 // delegate to the sub-context 156 return ((Context) localValue).lookup(parsedName.getSuffix(1)); 157 } 158 159 // if we didn't find an entry, it may be an absolute name 160 Object value = faultLookup(stringName, parsedName); 161 if (value != null) { 162 return value; 163 } 164 if (parsedName.size() > 1) { 165 throw new NotContextException(stringName); 166 } else { 167 throw new NameNotFoundException(stringName); 168 } 169 } 170 171 /** 172 * When a value can not be found within this context, this method is called as a last ditch effort befrore 173 * thowing a null pointer exception. 174 * @param stringName the string version of the name; will not be null 175 * @param parsedName the parsed name; will not be null 176 * @return the value or null if no fault value could be found 177 */ 178 protected Object faultLookup(String stringName, Name parsedName) { 179 if (!stringName.startsWith(nameInNamespace) && stringName.indexOf(':') > 0 && inCall.get() == null) { 180 inCall.set(parsedName); 181 try { 182 Context ctx = new InitialContext(); 183 return ctx.lookup(parsedName); 184 } catch (NamingException ignored) { 185 // thrown below 186 } finally { 187 inCall.set(null); 188 } 189 } 190 return null; 191 } 192 193 protected Context lookupFinalContext(Name name) throws NamingException { 194 Object value; 195 try { 196 value = lookup(name.getPrefix(name.size() - 1)); 197 } catch (NamingException e) { 198 throw new NotContextException("The intermediate context " + name.get(name.size() - 1) + " does not exist"); 199 } 200 201 if (value == null) { 202 throw new NotContextException("The intermediate context " + name.get(name.size() - 1) + " does not exist"); 203 } else if (!(value instanceof Context)) { 204 throw new NotContextException("The intermediate context " + name.get(name.size() - 1) + " does is not a context"); 205 } else { 206 return (Context) value; 207 } 208 } 209 210 // 211 // List Bindings 212 // 213 214 /** 215 * Gets a map of the bindings for the current node (i.e., no names with slashes). 216 * This method must not return null. 217 * 218 * @return a Map from binding name to binding value 219 * @throws NamingException if a problem occurs while getting the bindigns 220 */ 221 protected abstract Map<String, Object> getBindings() throws NamingException; 222 223 // 224 // Add Binding 225 // 226 227 protected void addDeepBinding(Name name, Object value, boolean rebind, boolean createIntermediateContexts) throws NamingException { 228 if (name == null) throw new NullPointerException("name is null"); 229 if (value == null) throw new NullPointerException("value is null"); 230 231 if (name.isEmpty()) { 232 throw new InvalidNameException("Name is empty"); 233 } 234 235 if (name.size() == 1) { 236 addBinding(name.get(0), value, rebind); 237 return; 238 } 239 240 if (!createIntermediateContexts) { 241 Context context = lookupFinalContext(name); 242 243 String lastSegment = name.get(name.size() - 1); 244 addBinding(context, lastSegment, value, rebind); 245 } else { 246 Context currentContext = this; 247 for (int i = 0; i < name.size(); i++) { 248 String part = name.get(i); 249 250 // empty path parts are not allowed 251 if (part.length() == 0) { 252 // this could be supported but it would be tricky 253 throw new InvalidNameException("Name part " + i + " is empty: " + name); 254 } 255 256 // Is this the last element in the name? 257 if (i == name.size() - 1) { 258 // we're at the end... (re)bind the value into the parent context 259 addBinding(currentContext, part, value, rebind); 260 261 // all done... this is redundant but makes the code more readable 262 break; 263 } else { 264 Object currentValue = getBinding(currentContext, part); 265 if (currentValue == null) { 266 // the next step in the tree is not present, so create everything down 267 // and add it to the current bindings 268 Context subcontext = createSubcontextTree(name.getPrefix(i).toString(), name.getSuffix(i), value); 269 addBinding(currentContext, part, subcontext, rebind); 270 271 // all done 272 break; 273 } else { 274 // the current value must be a nested subcontext 275 if (!(currentValue instanceof Context)) { 276 throw new NotContextException("Expected an instance of context to be bound at " + 277 part + " but found an instance of " + currentValue.getClass().getName()); 278 } 279 currentContext = (Context) currentValue; 280 // now we recurse into the current context 281 } 282 } 283 } 284 } 285 } 286 287 /** 288 * Gets the value bound to the specified name within the specified context. If the specified context is an 289 * AbstractContext this method will call the faster getBinding method, otherwise it will call lookup. 290 * 291 * @param context the context to get the binding from 292 * @param name the binding name 293 * @return the bound value or null if no value was bound 294 */ 295 private static Object getBinding(Context context, String name) { 296 try { 297 if (context instanceof AbstractContext) { 298 AbstractContext abstractContext = (AbstractContext) context; 299 return abstractContext.getBinding(name); 300 } else { 301 return context.lookup(name); 302 } 303 } catch (NamingException e) { 304 return null; 305 } 306 } 307 308 /** 309 * Binds the specified value to the specified name within the specified context. If the specified context is an 310 * AbstractContext and is a nested subcontext, this method will call the direct addBinding method, otherwise it 311 * will call public (re)bind method. 312 * 313 * @param context the context to add the binding to 314 * @param name the binding name 315 * @param value the value to bind 316 * @param rebind if true, this method will replace any exsiting binding, otherwise a NamingException will be thrown 317 * @throws NamingException if a problem occurs while (re)binding 318 */ 319 protected void addBinding(Context context, String name, Object value, boolean rebind) throws NamingException { 320 if (context == this || (context instanceof AbstractContext && isNestedSubcontext(context))) { 321 AbstractContext abstractContext = (AbstractContext) context; 322 abstractContext.addBinding(name, value, rebind); 323 } else { 324 if (rebind) { 325 context.rebind(name, value); 326 } else { 327 context.bind(name, value); 328 } 329 } 330 } 331 332 protected abstract boolean addBinding(String name, Object value, boolean rebind) throws NamingException; 333 334 /** 335 * Creates a context tree which will be rooted at the specified path and contain a single entry located down 336 * a path specified by the name. All necessary intermediate contexts will be created using the createContext method. 337 * @param path the path to the context that will contains this context 338 * @param name the name under which the value should be bound 339 * @param value the value 340 * @return a context with the value bound at the specified name 341 * @throws NamingException if a problem occurs while creating the subcontext tree 342 */ 343 protected Context createSubcontextTree(String path, Name name, Object value) throws NamingException { 344 if (path == null) throw new NullPointerException("path is null"); 345 if (name == null) throw new NullPointerException("name is null"); 346 if (name.size() < 2) throw new InvalidNameException("name must have at least 2 parts " + name); 347 348 if (path.length() > 0 && !path.endsWith("/")) path += "/"; 349 350 for (int i = name.size() - 1; i > 0; i--) { 351 String fullPath = path + name.getPrefix(i); 352 String key = name.get(i); 353 value = createNestedSubcontext(fullPath, Collections.singletonMap(key, value)); 354 } 355 return (Context) value; 356 } 357 358 359 // 360 // Remove Binding 361 // 362 363 /** 364 * Removes the binding from the context. The name will not contain a path and the value will not 365 * be a nested context although it may be a foreign context. 366 * @param name name under which the value should be bound 367 * @param removeNotEmptyContext ??? TODO figure this out 368 * @return whether removal was successful 369 * @throws NamingException if a problem occurs during the bind such as a value already being bound 370 */ 371 protected abstract boolean removeBinding(String name, boolean removeNotEmptyContext) throws NamingException; 372 373 protected void removeDeepBinding(Name name, boolean pruneEmptyContexts) throws NamingException { 374 removeDeepBinding(name, pruneEmptyContexts, false); 375 } 376 377 protected void removeDeepBinding(Name name, boolean pruneEmptyContexts, boolean removeNotEmptyContext) throws NamingException { 378 if (name == null) throw new NullPointerException("name is null"); 379 if (name.isEmpty()) { 380 throw new InvalidNameException("Name is empty"); 381 } 382 383 if (name.size() == 1) { 384 removeBinding(name.get(0), removeNotEmptyContext); 385 return; 386 } 387 388 if (!pruneEmptyContexts) { 389 Context context = lookupFinalContext(name); 390 context.unbind(name.getSuffix(name.size() - 1)); 391 } else { 392 // we serch the tree for a target context and name to remove 393 // this is normally the last context in the tree and the final name part, but 394 // it may be farther up the path if the intervening nodes are empty 395 Context targetContext = this; 396 String targetName = name.get(0); 397 398 Context currentContext = this; 399 for (int i = 0; i < name.size(); i++) { 400 String part = name.get(i); 401 402 // empty path parts are not allowed 403 if (part.length() == 0) { 404 throw new InvalidNameException("Name part " + i + " is empty: " + name); 405 } 406 407 // update targets 408 if (getSize(currentContext) > 1) { 409 targetContext = currentContext; 410 targetName = part; 411 } 412 413 414 // Is this the last element in the name? 415 if (i == name.size() - 1) { 416 // we're at the end... unbind value 417 unbind(targetContext, targetName, true); 418 419 // all done... this is redundant but makes the code more readable 420 break; 421 } else { 422 Object currentValue = getBinding(currentContext, part); 423 if (currentValue == null) { 424 // path not found we are done, but first prune the empty contexts 425 if (targetContext != currentContext) { 426 unbind(targetContext, targetName, false); 427 } 428 break; 429 } else { 430 // the current value must be a context 431 if (!(currentValue instanceof Context)) { 432 throw new NotContextException("Expected an instance of context to be bound at " + 433 part + " but found an instance of " + currentValue.getClass().getName()); 434 } 435 currentContext = (Context) currentValue; 436 // now we recurse into the current context 437 } 438 } 439 } 440 } 441 } 442 443 protected static boolean isEmpty(Context context) throws NamingException { 444 if (context instanceof AbstractContext) { 445 AbstractContext abstractContext = (AbstractContext) context; 446 Map<String, Object> currentBindings = abstractContext.getBindings(); 447 return currentBindings.isEmpty(); 448 } else { 449 NamingEnumeration namingEnumeration = context.list(""); 450 return namingEnumeration.hasMore(); 451 } 452 } 453 454 protected static int getSize(Context context) throws NamingException { 455 if (context instanceof AbstractContext) { 456 AbstractContext abstractContext = (AbstractContext) context; 457 Map<String, Object> currentBindings = abstractContext.getBindings(); 458 return currentBindings.size(); 459 } else { 460 NamingEnumeration namingEnumeration = context.list(""); 461 int size = 0; 462 while (namingEnumeration.hasMore()) size++; 463 return size; 464 } 465 } 466 467 /** 468 * Unbinds any value bound to the specified name within the specified context. If the specified context is an 469 * AbstractContext and is a nested context, this method will call the direct removeBinding method, otherwise it 470 * will call public unbind. 471 * 472 * @param context the context to remove the binding from 473 * @param name the binding name 474 * @param removeNotEmptyContext ??? TODO figure this out 475 * @throws NamingException if a problem occurs while unbinding 476 */ 477 private void unbind(Context context, String name, boolean removeNotEmptyContext) throws NamingException { 478 if (context == this || (context instanceof AbstractContext && isNestedSubcontext(context))) { 479 AbstractContext abstractContext = (AbstractContext) context; 480 abstractContext.removeBinding(name, removeNotEmptyContext); 481 } else { 482 context.unbind(name); 483 } 484 } 485 486 // 487 // Environment 488 // 489 490 /** 491 * Always returns a new (empty) Hashtable. 492 * @return a new (empty) Hashtable 493 */ 494 public Hashtable getEnvironment() { 495 return new Hashtable(); 496 } 497 498 public Object addToEnvironment(String propName, Object propVal) throws NamingException { 499 if (propName == null) throw new NullPointerException("propName is null"); 500 if (propVal == null) throw new NullPointerException("propVal is null"); 501 502 Map env = getEnvironment(); 503 return env.put(propName, propVal); 504 } 505 506 public Object removeFromEnvironment(String propName) throws NamingException { 507 if (propName == null) throw new NullPointerException("propName is null"); 508 509 Map env = getEnvironment(); 510 return env.remove(propName); 511 } 512 513 // 514 // Name handling 515 // 516 517 /** 518 * Gets the name of this context withing the global namespace. This method may return null 519 * if the location of the node in the global namespace is not known 520 * @return the name of this context within the global namespace or null if unknown. 521 */ 522 public String getNameInNamespace() { 523 return nameInNamespace; 524 } 525 526 /** 527 * Gets the name of this context withing the global namespace. This method may return null 528 * if the location of the node in the global namespace is not known 529 * @return the name of this context within the global namespace or null if unknown. 530 */ 531 protected Name getParsedNameInNamespace() { 532 return parsedNameInNamespace; 533 } 534 535 /** 536 * Gets the name of a path withing the global namespace context. 537 * @param path path to extend 538 * @return full path in namespace 539 */ 540 protected String getNameInNamespace(String path) { 541 String nameInNamespace = getNameInNamespace(); 542 if (nameInNamespace == null || nameInNamespace.length() == 0) { 543 return path; 544 } else { 545 return nameInNamespace + "/" + path; 546 } 547 } 548 549 /** 550 * Gets the name of a path withing the global namespace context. 551 * @param path path to extend 552 * @return full path in namespace 553 * @throws javax.naming.NamingException on error 554 */ 555 protected Name getNameInNamespace(Name path) throws NamingException { 556 Name nameInNamespace = getParsedNameInNamespace(); 557 if (nameInNamespace == null || nameInNamespace.size() == 0) { 558 return path; 559 } else { 560 return composeName(nameInNamespace, path); 561 } 562 } 563 564 /** 565 * A parser that can turn Strings into javax.naming.Name objects. 566 * @return ContextUtil.NAME_PARSER 567 */ 568 protected NameParser getNameParser() { 569 return ContextUtil.NAME_PARSER; 570 } 571 572 public NameParser getNameParser(Name name) { 573 return getNameParser(); 574 } 575 576 public NameParser getNameParser(String name) { 577 return getNameParser(); 578 } 579 580 public Name composeName(Name name, Name prefix) throws NamingException { 581 if (name == null) throw new NullPointerException("name is null"); 582 if (prefix == null) throw new NullPointerException("prefix is null"); 583 584 Name result = (Name) prefix.clone(); 585 result.addAll(name); 586 return result; 587 } 588 589 public String composeName(String name, String prefix) throws NamingException { 590 if (name == null) throw new NullPointerException("name is null"); 591 if (prefix == null) throw new NullPointerException("prefix is null"); 592 593 CompositeName result = new CompositeName(prefix); 594 result.addAll(new CompositeName(name)); 595 return result.toString(); 596 } 597 598 // 599 // Lookup 600 // 601 602 public Object lookup(String name) throws NamingException { 603 if (name == null) throw new NullPointerException("name is null"); 604 605 Object value = lookup(name, null); 606 607 // if we got a link back we need to resolve it 608 if (value instanceof LinkRef) { 609 LinkRef linkRef = (LinkRef) value; 610 value = lookup(linkRef.getLinkName()); 611 } 612 613 return value; 614 } 615 616 public Object lookup(Name name) throws NamingException { 617 if (name == null) throw new NullPointerException("name is null"); 618 619 Object value = lookup(null, name); 620 621 622 // if we got a link back we need to resolve it 623 if (value instanceof LinkRef) { 624 LinkRef linkRef = (LinkRef) value; 625 value = lookup(linkRef.getLinkName()); 626 } 627 628 return value; 629 } 630 631 public Object lookupLink(String name) throws NamingException { 632 if (name == null) throw new NullPointerException("name is null"); 633 return lookup(name, null); 634 } 635 636 public Object lookupLink(Name name) throws NamingException { 637 if (name == null) throw new NullPointerException("name is null"); 638 return lookup(null, name); 639 } 640 641 // 642 // Bind, rebind, rename and unbind 643 // 644 645 public void bind(String name, Object obj) throws NamingException { 646 if (!modifiable) throw new OperationNotSupportedException("Context is read only"); 647 if (name == null) throw new NullPointerException("name is null"); 648 if (name.length() == 0) { 649 throw new NameAlreadyBoundException("Cannot bind to an empty name (this context)"); 650 } 651 bind(new CompositeName(name), obj); 652 } 653 654 public void bind(Name name, Object obj) throws NamingException { 655 if (!modifiable) throw new OperationNotSupportedException("Context is read only"); 656 if (name == null) throw new NullPointerException("name is null"); 657 if (name.isEmpty()) { 658 throw new NameAlreadyBoundException("Cannot bind to an empty name (this context)"); 659 } 660 addDeepBinding(name, obj, false, false); 661 } 662 663 public void rebind(String name, Object obj) throws NamingException { 664 if (!modifiable) throw new OperationNotSupportedException("Context is read only"); 665 if (name == null) throw new NullPointerException("name is null"); 666 rebind(new CompositeName(name), obj); 667 } 668 669 public void rebind(Name name, Object obj) throws NamingException { 670 if (!modifiable) throw new OperationNotSupportedException("Context is read only"); 671 if (name == null) throw new NullPointerException("name is null"); 672 if (name.isEmpty()) { 673 throw new NameAlreadyBoundException("Cannot rebind an empty name (this context)"); 674 } 675 addDeepBinding(name, obj, true, false); 676 } 677 678 public void rename(String oldName, String newName) throws NamingException { 679 if (!modifiable) throw new OperationNotSupportedException("Context is read only"); 680 if (oldName == null) throw new NullPointerException("oldName is null"); 681 if (newName == null) throw new NullPointerException("newName is null"); 682 rename(new CompositeName(oldName), new CompositeName(newName)); 683 } 684 685 public void rename(Name oldName, Name newName) throws NamingException { 686 if (!modifiable) throw new OperationNotSupportedException("Context is read only"); 687 if (oldName == null || newName == null) { 688 throw new NullPointerException("name is null"); 689 } else if (oldName.isEmpty() || newName.isEmpty()) { 690 throw new NameAlreadyBoundException("Name cannot be empty"); 691 } 692 this.bind(newName, this.lookup(oldName)); 693 this.unbind(oldName); 694 } 695 696 public void unbind(String name) throws NamingException { 697 if (!modifiable) throw new OperationNotSupportedException("Context is read only"); 698 if (name == null) throw new NullPointerException("name is null"); 699 unbind(new CompositeName(name)); 700 } 701 702 public void unbind(Name name) throws NamingException { 703 if (!modifiable) throw new OperationNotSupportedException("Context is read only"); 704 if (name == null) throw new NullPointerException("name is null"); 705 if (name.isEmpty()) { 706 throw new InvalidNameException("Cannot unbind empty name"); 707 } 708 removeDeepBinding(name, false); 709 } 710 711 // 712 // List 713 // 714 715 protected NamingEnumeration<NameClassPair> list() throws NamingException { 716 Map<String, Object> bindings = getBindings(); 717 return new ContextUtil.ListEnumeration(bindings); 718 } 719 720 protected NamingEnumeration<Binding> listBindings() throws NamingException { 721 Map<String, Object> bindings = getBindings(); 722 return new ContextUtil.ListBindingEnumeration(bindings, this); 723 } 724 725 public NamingEnumeration<NameClassPair> list(String name) throws NamingException { 726 if (name == null) throw new NullPointerException("name is null"); 727 728 // if the name is empty, list the current context 729 if (name.length() == 0) { 730 return list(); 731 } 732 733 // lookup the target context 734 Object target; 735 try { 736 target = lookup(name); 737 } catch (NamingException e) { 738 throw new NotContextException(name); 739 } 740 741 if (target == this) { 742 return list(); 743 } else if (target instanceof Context) { 744 return ((Context) target).list(""); 745 } else { 746 throw new NotContextException("The name " + name + " cannot be listed"); 747 } 748 } 749 750 public NamingEnumeration<NameClassPair> list(Name name) throws NamingException { 751 if (name == null) throw new NullPointerException("name is null"); 752 753 // if the name is empty, list the current context 754 if (name.isEmpty()) { 755 return list(); 756 } 757 758 // lookup the target context 759 Object target; 760 try { 761 target = lookup(name); 762 } catch (NamingException e) { 763 throw new NotContextException(name.toString()); 764 } 765 766 if (target == this) { 767 return list(); 768 } else if (target instanceof Context) { 769 return ((Context) target).list(""); 770 } else { 771 throw new NotContextException("The name " + name + " cannot be listed"); 772 } 773 } 774 775 public NamingEnumeration<Binding> listBindings(String name) throws NamingException { 776 if (name == null) throw new NullPointerException("name is null"); 777 778 // if the name is empty, list the current context 779 if (name.length() == 0) { 780 return listBindings(); 781 } 782 783 // lookup the target context 784 Object target; 785 try { 786 target = lookup(name); 787 } catch (NamingException e) { 788 throw new NotContextException(name); 789 } 790 791 if (target == this) { 792 return listBindings(); 793 } else if (target instanceof Context) { 794 return ((Context) target).listBindings(""); 795 } else { 796 throw new NotContextException("The name " + name + " cannot be listed"); 797 } 798 } 799 800 public NamingEnumeration<Binding> listBindings(Name name) throws NamingException { 801 if (name == null) throw new NullPointerException("name is null"); 802 803 // if the name is empty, list the current context 804 if (name.isEmpty()) { 805 return listBindings(); 806 } 807 808 // lookup the target context 809 Object target; 810 try { 811 target = lookup(name); 812 } catch (NamingException e) { 813 throw new NotContextException(name.toString()); 814 } 815 816 if (target == this) { 817 return listBindings(); 818 } else if (target instanceof Context) { 819 return ((Context) target).listBindings(""); 820 } else { 821 throw new NotContextException("The name " + name + " cannot be listed"); 822 } 823 } 824 825 // 826 // Subcontexts 827 // 828 829 public Context createSubcontext(String name) throws NamingException { 830 if (!modifiable) throw new OperationNotSupportedException("Context is read only"); 831 if (name == null) throw new NullPointerException("name is null"); 832 return createSubcontext(new CompositeName(name)); 833 } 834 835 public Context createSubcontext(Name name) throws NamingException { 836 if (!modifiable) throw new OperationNotSupportedException("Context is read only"); 837 if (name == null) throw new NullPointerException("name is null"); 838 if (name.isEmpty()) { 839 throw new NameAlreadyBoundException("Cannot create a subcontext if the name is empty"); 840 } 841 Context abstractContext = createNestedSubcontext(name.toString(), Collections.EMPTY_MAP); 842 addDeepBinding(name, abstractContext, false, false); 843 return abstractContext; 844 } 845 846 public void destroySubcontext(String name) throws NamingException { 847 if (!modifiable) throw new OperationNotSupportedException("Context is read only"); 848 if (name == null) throw new NullPointerException("name is null"); 849 destroySubcontext(new CompositeName(name)); 850 } 851 852 public void destroySubcontext(Name name) throws NamingException { 853 if (!modifiable) throw new OperationNotSupportedException("Context is read only"); 854 if (name == null) throw new NullPointerException("name is null"); 855 if (name.isEmpty()) { 856 throw new InvalidNameException("Cannot destroy subcontext with empty name"); 857 } 858 unbind(name); 859 } 860 }