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 }