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