001: package org.mockejb.jndi;
002:
003: import java.util.*;
004: import javax.naming.*;
005:
006: import org.apache.commons.logging.*;
007: import org.mockejb.MethodNotImplementedException;
008:
009: /**
010: * Provides implementation of <code>javax.naming.Context</code> interface for
011: * hierarchical in memory single-namespace naming system.
012: * A name in the <code>MockContext</code> namespace is a sequence of one or more
013: * atomic names, relative to a root initial context.
014: * When a name consist of more than one atomic names it is a <code>CompoundName</code>
015: * where atomic names are separated with separator character - '/' or '.'.
016: * It is possible to use both separator characters in the same name. In such cases
017: * any occurrences of '.' are replaced with '/' before parsing.
018: * <p>
019: * Leading and terminal components of a <code>CompoundName</code> can not be empty -
020: * for example "name1/name2/name3" is a valid name, while the following names are
021: * not valid - "/name1/name2/name3", "name1/name2/name3/", "/name1/name2/name3/".
022: * If such name is passed, all empty leading/terminal components will be removed
023: * before the name is actually used (this will not affect the original value) -
024: * from the above three examples the actual name will be "name1/name2/name3".
025: * If a name contains intermediate empty components (for example "a//b") then
026: * <code>InvalidNameException</code> will be thrown.
027: * <p>
028: * Composite names (instances of <code>CompositeName</code>) must contain zero or one
029: * component from the <code>MockContext</code> namespace.
030: * <p>
031: * The namespace of <code>MockContext</code> can be represented as a tree of atomic names.
032: * Each name is bound to an instance of MockContext (subcontext) or to an arbitrary object.
033: * Each subcontext has collection of names bound to other subcontexts or arbitrary objects.
034: * <p>
035: * When instance of <code>Name</code> is used as parameter to any of the
036: * MockContext methods, if the object is not <code>CompositeName</code> then
037: * it is assumed that it is <code>CompoundName</code>
038: * <p>
039: * Example:
040: * <pre><code>
041: * myContext = initialContext.lookup("foo");
042: * myObject = myContext.lookup("bar");
043: * </code>
044: * is equivalent to
045: * <code>myObject = initialContext.lookup("foo/bar");</code>
046: * </pre>
047: * <p>
048: * Instances of <code>MockContext</code> are created only through
049: * <code>MockContextFactory</code>, when <code>InitialContext</code> is instantiated.
050: * <p>
051: * If a remote context is provided, this class will search in that remote context if the
052: * object is not found locally.
053: * <p>
054: * For overloaded methods that accept name as <code>String</code> or
055: * <code>Name</code> only the version for <code>Name</code> is documented.
056: * The <code>String</code> version creates <code>CompoundName</code>, from
057: * the string name passed as parameter, and calls the <code>Name</code> version of
058: * the same method.
059: *
060: * @author Alexander Ananiev
061: * @author Dimitar Gospodinov
062: */
063: public class MockContext implements Context {
064:
065: private static final String ROOT_CONTEXT_NAME = "ROOT";
066: // MockContext supports single naming scheme and all instances use the same parser.
067: private static final NameParser nameParser = new MockContextNameParser();
068:
069: // logger for this class
070: private static Log logger = LogFactory.getLog(MockContext.class
071: .getName());
072: /**
073: * Map of objects registered for this context representing the local context
074: */
075: private final Map objects = Collections
076: .synchronizedMap(new HashMap());
077: // Parent Context of this Context
078: private MockContext parent;
079: // Atomic name of this Context
080: private String contextName;
081: /**
082: * Container context used for delegated lookups
083: */
084: private Context remoteContext;
085: // Shows if this context has been destroyed
086: private boolean isDestroyed;
087:
088: /**
089: * Creates a new instance of the context.
090: * This class can only be instantiated by its factory.
091: * @param remoteContext remote context that MockContext will
092: * delegate to if it fails to lookup an object locally
093: */
094: protected MockContext(Context remoteContext) {
095:
096: this (remoteContext, null, ROOT_CONTEXT_NAME);
097: }
098:
099: /**
100: * Creates new instance of <code>MockContext</code>.
101: * @param remoteContext remote context that MockContext will
102: * delegate to if it fails to lookup an object locally
103: * @param parent parent context of this context. <code>null</code> if
104: * this is the root context.
105: * @param name atomic name for this context
106: */
107: private MockContext(Context remoteContext, MockContext parent,
108: String name) {
109:
110: this .remoteContext = remoteContext;
111: this .parent = parent;
112: this .contextName = name;
113: this .isDestroyed = false;
114: }
115:
116: /**
117: * Not implemented
118: * @see javax.naming.Context#addToEnvironment(java.lang.String, java.lang.Object)
119: */
120: public Object addToEnvironment(String arg0, Object arg1)
121: throws NamingException {
122:
123: throwMethodNotImplemented("addToEnvironment( String, Object)");
124: return null;
125: }
126:
127: /**
128: * Binds object <code>obj</code> to name <code>name</code> in this context.
129: * Intermediate contexts that do not exist will be created.
130: *
131: * @param name name of the object to bind
132: * @param obj object to bind. Can be <code>null</code>.
133: *
134: * @throws NoPermissionException if this context has been destroyed
135: * @throws InvalidNameException if <code>name</code> is empty or is
136: * <code>CompositeName</code> that spans more than one naming system
137: * @throws NotContextException if <code>name</code> has more than one
138: * atomic name and intermediate atomic name is bound to object that is
139: * not context.
140: *
141: * @see javax.naming.Context#bind(javax.naming.Name, java.lang.Object)
142: */
143: public void bind(Name name, Object obj) throws NamingException {
144:
145: checkIsDestroyed();
146: // Do not check for already bound name. Simply replace the existing value.
147: rebind(name, obj);
148: }
149:
150: /**
151: * Binds object <code>obj</code> to name <code>name</code> in this context.
152: * @param name name of the object to add
153: * @param obj object to bind
154: * @throws NamingException if naming error occurs
155: * @see #bind(Name, Object)
156: */
157: public void bind(String name, Object obj) throws NamingException {
158:
159: bind(nameParser.parse(name), obj);
160: }
161:
162: /**
163: * Does nothing.
164: */
165: public void close() throws NamingException {
166: }
167:
168: /**
169: * Returns composition of <code>prefix</code> and <code>name</code>.
170: * @param name name relative to this context
171: * @param prefix name of this context
172: * @see javax.naming.Context#composeName(javax.naming.Name, javax.naming.Name)
173: */
174: public Name composeName(Name name, Name prefix)
175: throws NamingException {
176:
177: checkIsDestroyed();
178: /*
179: * We do not want to modify any of the parameters (JNDI requirement).
180: * Clone <code>prefix</code> to satisfy the requirement.
181: */
182: Name parsedPrefix = getParsedName((Name) prefix.clone());
183: Name parsedName = getParsedName(name);
184:
185: return parsedPrefix.addAll(parsedName);
186: }
187:
188: /**
189: * Composes the name of this context with a name relative to this context.
190: * Given a name (name) relative to this context, and the name (prefix)
191: * of this context relative to one of its ancestors, this method returns
192: * the composition of the two names using the syntax appropriate for
193: * the naming system(s) involved.
194: * Example:
195: * composeName("a","b") b/a
196: * composeName("a","") a
197: * @param name name relative to this context
198: * @param prefix name of this context
199: * @see javax.naming.Context#composeName(java.lang.String, java.lang.String)
200: */
201: public String composeName(String name, String prefix)
202: throws NamingException {
203:
204: checkIsDestroyed();
205: return composeName(nameParser.parse(name),
206: nameParser.parse(prefix)).toString();
207: }
208:
209: /**
210: * Creates subcontext with name <code>name</code>, relative to this Context.
211: * @param name subcontext name.
212: * @return new subcontext named <code>name</code> relative to this context
213: * @throws NoPermissionException if this context has been destroyed
214: * @throws InvalidNameException if <code>name</code> is empty or is
215: * <code>CompositeName</code> that spans more than one naming system
216: * @throws NameAlreadyBoundException if <code>name</code> is already bound in this Context
217: * @throws NotContextException if any intermediate name from <code>name</code>
218: * is not bound to instance of <code>javax.naming.Context</code>
219: * @see javax.naming.Context#createSubcontext(javax.naming.Name)
220: */
221: public Context createSubcontext(Name name) throws NamingException {
222:
223: checkIsDestroyed();
224: Name parsedName = getParsedName(name);
225: if (parsedName.size() == 0 || parsedName.get(0).length() == 0) {
226: throw new InvalidNameException("Name can not be empty!");
227: }
228: String subContextName = parsedName.get(0);
229: Object boundObject = objects.get(parsedName.get(0));
230:
231: if (parsedName.size() == 1) {
232: // Check if <code>name</code> is already in use
233: if (boundObject == null) {
234: Context subContext = new MockContext(remoteContext,
235: this , subContextName);
236: objects.put(subContextName, subContext);
237: return subContext;
238: } else {
239: throw new NameAlreadyBoundException("Name "
240: + subContextName + " is already bound!");
241: }
242: } else {
243: if (boundObject instanceof Context) {
244: // Let the subcontext create new subcontext
245: return ((Context) boundObject)
246: .createSubcontext(parsedName.getSuffix(1));
247: } else {
248: throw new NotContextException(
249: "Expected Context but found " + boundObject);
250: }
251: }
252: }
253:
254: /**
255: * Creates subcontext with name <code>name</code>, relative to this Context.
256: * @param name subcontext name
257: * @return new subcontext named <code>name</code> relative to this context
258: * @throws NamingException if naming error occurs
259: * @see #createSubcontext(javax.naming.Name)
260: */
261: public Context createSubcontext(String name) throws NamingException {
262:
263: return createSubcontext(nameParser.parse(name));
264: }
265:
266: /**
267: * Destroys subcontext with name <code>name</code>
268: * The subcontext must be empty otherwise <code>ContextNotEmptyException</code>
269: * is thrown.
270: * <p>
271: * Once a context is destroyed, the instance should not be used.
272: * @param name subcontext to destroy
273: * @throws NoPermissionException if this context has been destroyed
274: * @throws InvalidNameException if <code>name</code> is empty or is
275: * <code>CompositeName</code> that spans more than one naming system
276: * @throws ContextNotEmptyException if Context <code>name</code> is not empty
277: * @throws NameNotFoundException if subcontext with name <code>name</code> can not be found
278: * @throws NotContextException if <code>name</code> is not bound to instance of
279: * <code>MockContext</code>
280: * @see javax.naming.Context#destroySubcontext(javax.naming.Name)
281: */
282: public void destroySubcontext(Name name) throws NamingException {
283:
284: checkIsDestroyed();
285: Name parsedName = getParsedName(name);
286: if (parsedName.size() == 0 || parsedName.get(0).length() == 0) {
287: throw new InvalidNameException("Name can not be empty!");
288: }
289: String subContextName = parsedName.get(0);
290: Object boundObject = objects.get(subContextName);
291: if (boundObject == null) {
292: throw new NameNotFoundException("Name " + subContextName
293: + "not found in the context!");
294: }
295: if (!(boundObject instanceof MockContext)) {
296: throw new NotContextException();
297: }
298: MockContext contextToDestroy = (MockContext) boundObject;
299: if (parsedName.size() == 1) {
300: /*
301: * Check if the Context to be destroyed is empty.
302: * Can not destroy non-empty Context.
303: */
304: if (contextToDestroy.objects.size() == 0) {
305: objects.remove(subContextName);
306: contextToDestroy.destroyInternal();
307: } else {
308: throw new ContextNotEmptyException(
309: "Can not destroy non-empty Context!");
310: }
311: } else {
312: // Let the subcontext destroy the context
313: ((MockContext) boundObject).destroySubcontext(parsedName
314: .getSuffix(1));
315: }
316: }
317:
318: /**
319: * Destroys subcontext with name <code>name</code>
320: * @param name name of subcontext to destroy
321: * @throws NamingException if naming error occurs
322: * @see #destroySubcontext(javax.naming.Name)
323: */
324: public void destroySubcontext(String name) throws NamingException {
325:
326: destroySubcontext(nameParser.parse(name));
327: }
328:
329: /**
330: * Not implemented
331: * @see javax.naming.Context#getEnvironment()
332: */
333: public Hashtable getEnvironment() throws NamingException {
334:
335: throwMethodNotImplemented("getEnvironment()");
336: return null;
337: }
338:
339: /**
340: * Not implemented
341: * @see javax.naming.Context#getNameInNamespace()
342: */
343: public String getNameInNamespace() throws NamingException {
344:
345: throwMethodNotImplemented("getNameInNamespace()");
346: return null;
347: }
348:
349: /**
350: * Retrieves name parser used to parse context with name <code>name</code>.
351: * @param name context name
352: * @return <code>NameParser</code>
353: * @throws NoPermissionException if this context has been destroyed
354: * @throws NamingException if any other naming error occurs
355: * @see javax.naming.Context#getNameParser(javax.naming.Name)
356: */
357: public NameParser getNameParser(Name name) throws NamingException {
358:
359: checkIsDestroyed();
360: return nameParser;
361: }
362:
363: /**
364: * Retrieves name parser used to parse context with name <code>name</code>.
365: * @param name context name
366: * @return <code>NameParser</code>
367: * @throws NamingException if naming error occurs
368: * @see #getNameParser(javax.naming.Name)
369: */
370: public NameParser getNameParser(String name) throws NamingException {
371:
372: checkIsDestroyed();
373: return nameParser;
374: }
375:
376: /**
377: * The same as <code>listBindings(String)</code>
378: * @param name name of Context, relative to this Context
379: * @return <code>NamingEnumeration</code> of all name-class pairs. Each
380: * element from the enumeration is instance of <code>NameClassPair</code>
381: * @throws NamingException if naming error occurs
382: * @see #listBindings(javax.naming.Name)
383: */
384: public NamingEnumeration list(Name name) throws NamingException {
385:
386: return listBindings(name);
387: }
388:
389: /**
390: * The same as <code>listBindings(String)</code>
391: * @param name name of Context, relative to this Context
392: * @return <code>NamingEnumeration</code> of all name-class pairs. Each
393: * element from the enumeration is instance of <code>NameClassPair</code>
394: * @throws NamingException if naming error occurs
395: * @see #listBindings(java.lang.String)
396: */
397: public NamingEnumeration list(String name) throws NamingException {
398:
399: return list(nameParser.parse(name));
400: }
401:
402: /**
403: * Lists all bindings for Context with name <code>name</code>.
404: * If <code>name</code> is empty then this Context is assumed.
405: * @param name name of Context, relative to this Context
406: * @return <code>NamingEnumeration</code> of all name-object pairs. Each
407: * element from the enumeration is instance of <code>Binding</code>
408: * @throws NoPermissionException if this context has been destroyed
409: * @throws InvalidNameException if <code>name</code> is <code>CompositeName</code>
410: * that spans more than one naming system
411: * @throws NameNotFoundException if <code>name</code> can not be found
412: * @throws NotContextException component of <code>name</code> is not bound to instance of
413: * <code>MockContext</code>, when <code>name</code> is not an atomic name
414: * @throws NamingException if any other naming error occurs
415: * @see javax.naming.Context#listBindings(javax.naming.Name)
416: */
417: public NamingEnumeration listBindings(Name name)
418: throws NamingException {
419:
420: checkIsDestroyed();
421: Name parsedName = getParsedName(name);
422: if (parsedName.size() == 0) {
423: Vector bindings = new Vector();
424: Iterator iterat = objects.keySet().iterator();
425: while (iterat.hasNext()) {
426: String bindingName = (String) iterat.next();
427: bindings.addElement(new Binding(bindingName, objects
428: .get(bindingName)));
429: }
430: return new NamingEnumerationImpl(bindings);
431: } else {
432: Object subContext = objects.get(parsedName.get(0));
433: if (subContext instanceof Context) {
434: return ((Context) subContext).list(parsedName
435: .getSuffix(1));
436: }
437: if (subContext == null
438: && !objects.containsKey(parsedName.get(0))) {
439: throw new NameNotFoundException("Name " + name
440: + " not found");
441: } else {
442: throw new NotContextException(
443: "Expected Context but found " + subContext);
444: }
445: }
446: }
447:
448: /**
449: * Lists all bindings for Context with name <code>name</code>.
450: * If <code>name</code> is empty then this Context is assumed.
451: * @param name name of Context, relative to this Context
452: * @return <code>NamingEnumeration</code> of all name-object pairs. Each
453: * element from the enumeration is instance of <code>Binding</code>
454: * @throws NamingException if naming error occurs
455: * @see #listBindings(javax.naming.Name)
456: */
457: public NamingEnumeration listBindings(String name)
458: throws NamingException {
459:
460: return listBindings(nameParser.parse(name));
461: }
462:
463: /**
464: * Looks up object with name <code>name</code> in this context. If the object is not
465: * found and the remote context was provided, calls the remote
466: * context to lookup the object.
467: * @param name name to look up
468: * @return object reference bound to name <code>name</code>
469: * @throws NoPermissionException if this context has been destroyed
470: * @throws InvalidNameException if <code>name</code> is <code>CompositeName</code>
471: * that spans more than one naming system
472: * @throws NameNotFoundException if <code>name</code> can not be found
473: * @throws NotContextException component of <code>name</code> is not bound to instance of
474: * <code>MockContext</code>, when <code>name</code> is not atomic name.
475: * @throws NamingException if any other naming error occurs
476: * @see javax.naming.Context#lookup(javax.naming.Name)
477: */
478: public Object lookup(Name name) throws NamingException {
479:
480: checkIsDestroyed();
481: try {
482: return lookupInternal(name);
483: } catch (NameNotFoundException ex) {
484: // Shall we delegate?
485: if (remoteContext != null) {
486: logger
487: .debug("Was not able to find name "
488: + name
489: + " in the local context. Will try remote context.");
490:
491: return remoteContext.lookup(name);
492: } else {
493: throw new NameNotFoundException("Name " + name
494: + " not found. ");
495: }
496: }
497: }
498:
499: private Object lookupInternal(Name name) throws NamingException {
500:
501: Name parsedName = getParsedName(name);
502: String nameComponent = parsedName.get(0);
503:
504: Object res = objects.get(nameComponent);
505:
506: // if not found
507: if (!objects.containsKey(nameComponent)) {
508: throw new NameNotFoundException("Name " + name
509: + " not found.");
510: }
511: // if this is a compound name
512: else if (parsedName.size() > 1) {
513:
514: if (res instanceof MockContext) {
515: res = ((MockContext) res).lookupInternal(parsedName
516: .getSuffix(1));
517: } else {
518: throw new NotContextException(
519: "Expected MockContext but found " + res);
520: }
521: }
522:
523: return res;
524: }
525:
526: /**
527: * Looks up the object in this context. If the object is not
528: * found and the remote context was provided, calls the remote
529: * context to lookup the object.
530: * @param name object to search
531: * @return object reference bound to name <code>name</code>
532: * @throws NamingException if naming error occurs
533: * @see #lookup(javax.naming.Name)
534: */
535: public Object lookup(String name) throws NamingException {
536: checkIsDestroyed();
537: try {
538: return lookupInternal(name);
539: } catch (NameNotFoundException ex) {
540: // Shall we delegate?
541: if (remoteContext != null) {
542: logger
543: .debug("Was not able to find name "
544: + name
545: + " in the local context. Will try remote context.");
546:
547: return remoteContext.lookup(name);
548: } else {
549: throw new NameNotFoundException("Name " + name
550: + " not found. ");
551: }
552: }
553: }
554:
555: private Object lookupInternal(String name) throws NamingException {
556: return lookupInternal(nameParser.parse(name));
557: }
558:
559: /**
560: * Not implemented
561: * @see javax.naming.Context#lookupLink(javax.naming.Name)
562: */
563: public Object lookupLink(Name arg0) throws NamingException {
564:
565: throwMethodNotImplemented("lookupLink(Name)");
566: return null;
567: }
568:
569: /**
570: * Not implemented
571: * @see javax.naming.Context#lookupLink(java.lang.String)
572: */
573: public Object lookupLink(String arg0) throws NamingException {
574:
575: throwMethodNotImplemented("lookupLink(String)");
576: return null;
577: }
578:
579: /**
580: * Rebinds object <code>obj</code> to name <code>name</code>.
581: * If there is existing binding it will be overwritten.
582: * @param name name of the object to rebind
583: * @param obj object to add. Can be <code>null</code>
584: * @throws NoPermissionException if this context has been destroyed
585: * @throws InvalidNameException if <code>name</code> is empty or is
586: * <code>CompositeName</code> that spans more than one naming system
587: * @throws NotContextException if <code>name</code> has more than one
588: * atomic name and intermediate context is not found
589: * @throws NamingException if any other naming error occurs
590: * @see javax.naming.Context#rebind(javax.naming.Name, java.lang.Object)
591: */
592: public void rebind(Name name, Object obj) throws NamingException {
593:
594: checkIsDestroyed();
595: Name parsedName = getParsedName(name);
596: if (parsedName.size() == 0 || parsedName.get(0).length() == 0) {
597: throw new InvalidNameException("Name can not be empty!");
598: }
599: String nameToBind = parsedName.get(0);
600:
601: if (parsedName.size() == 1) {
602: objects.put(nameToBind, obj);
603: logger.debug("Bound object " + obj + " to the name "
604: + nameToBind);
605: } else {
606: Object boundObject = objects.get(nameToBind);
607: if (boundObject instanceof Context) {
608: /*
609: * Let the subcontext bind the object.
610: */
611: ((Context) boundObject).bind(parsedName.getSuffix(1),
612: obj);
613: } else {
614: if (boundObject == null) {
615: // Create new subcontext and let it do the binding
616: Context sub = createSubcontext(nameToBind);
617: sub.bind(parsedName.getSuffix(1), obj);
618: } else {
619: throw new NotContextException(
620: "Expected Context but found " + boundObject);
621: }
622: }
623: }
624: }
625:
626: /**
627: * Same as bind except that if <code>name</code> is already bound in
628: * the context, it will be re-bound to object <code>obj</code>
629: * @param name name of the object to rebind
630: * @param obj object to add. Can be <code>null</code>
631: * @throws NamingException if naming error occurs
632: * @see #rebind(javax.naming.Name, Object)
633: */
634: public void rebind(String name, Object obj) throws NamingException {
635:
636: rebind(nameParser.parse(name), obj);
637: }
638:
639: /**
640: * Not implemented
641: * @see javax.naming.Context#removeFromEnvironment(java.lang.String)
642: */
643: public Object removeFromEnvironment(String arg0)
644: throws NamingException {
645:
646: throwMethodNotImplemented("removeFromEnvironment(String)");
647: return null;
648: }
649:
650: /**
651: * Not implemented
652: * @see javax.naming.Context#rename(javax.naming.Name, javax.naming.Name)
653: */
654: public void rename(Name arg0, Name arg1) throws NamingException {
655:
656: throwMethodNotImplemented("rename(Name arg0, Name arg1)");
657:
658: }
659:
660: /**
661: * Not implemented
662: * @see javax.naming.Context#rename(java.lang.String, java.lang.String)
663: */
664: public void rename(String arg0, String arg1) throws NamingException {
665:
666: throwMethodNotImplemented("rename(String arg0, String arg1)");
667:
668: }
669:
670: /**
671: * Removes <code>name</code> and its associated object from the context.
672: * @param name name to remove
673: * @throws NoPermissionException if this context has been destroyed
674: * @throws InvalidNameException if <code>name</code> is empty or is
675: * <code>CompositeName</code> that spans more than one naming system
676: * @throws NameNotFoundException if intermediate context can not be found
677: * @throws NotContextException if <code>name</code> has more than one
678: * atomic name and intermediate context is not found
679: * @throws NamingException if any other naming exception occurs
680: * @see javax.naming.Context#unbind(javax.naming.Name)
681: */
682: public void unbind(Name name) throws NamingException {
683:
684: checkIsDestroyed();
685: Name parsedName = getParsedName(name);
686: if (parsedName.size() == 0 || parsedName.get(0).length() == 0) {
687: throw new InvalidNameException("Name can not be empty!");
688: }
689: String nameToRemove = parsedName.get(0);
690:
691: if (parsedName.size() == 1) {
692: objects.remove(nameToRemove);
693: logger.debug("Unbound name " + name);
694: } else {
695: Object boundObject = objects.get(nameToRemove);
696: if (boundObject instanceof Context) {
697: /*
698: * Let the subcontext do the unbind
699: */
700: ((Context) boundObject).unbind(parsedName.getSuffix(1));
701: } else {
702: if (!objects.containsKey(nameToRemove)) {
703: throw new NameNotFoundException("Can not find "
704: + name);
705: }
706: throw new NotContextException(
707: "Expected Context but found " + boundObject);
708: }
709: }
710: }
711:
712: /**
713: * Removes object from the object map
714: * @param name object to remove
715: * @throws NamingException if naming error occurs
716: * @see #unbind(javax.naming.Name)
717: */
718: public void unbind(String name) throws NamingException {
719:
720: unbind(nameParser.parse(name));
721: }
722:
723: // ** Non-standard methods
724:
725: /**
726: * Checks if this context has been destroyed.
727: * <code>isDestroyed</code> is set to <code>true</code> when a context is
728: * destroyed by calling <code>destroySubcontext</code> method.
729: * @throws NoPermissionException if this context has been destroyed
730: */
731: private void checkIsDestroyed() throws NamingException {
732:
733: if (isDestroyed) {
734: throw new NoPermissionException(
735: "Can not invoke operations on destroyed context!");
736: }
737: }
738:
739: /**
740: * Marks this context as destroyed.
741: * Method called only by <code>destroySubcontext</code>.
742: */
743: private void destroyInternal() {
744:
745: isDestroyed = true;
746: }
747:
748: /**
749: * Parses <code>name</code> which is <code>CompositeName</code> or <code>CompoundName</code>.
750: * If <code>name</code> is not <code>CompositeName</code> then it is
751: * assumed to be <code>CompoundName</code>.
752: * <p>
753: * If the name contains leading and/or terminal empty components, they will not
754: * be included in the result.
755: * @param name <code>Name</code> to parse
756: * @return parsed name as instance of <code>CompoundName</code>
757: * @throws InvalidNameException if <code>name</code> is
758: * <code>CompositeName</code> and spans more than one name space
759: * @throws NamingException if any other naming exception occurs
760: */
761: private Name getParsedName(Name name) throws NamingException {
762: Name result = null;
763:
764: if (name instanceof CompositeName) {
765: if (name.size() == 0) {
766: // Return empty CompositeName
767: result = nameParser.parse("");
768: } else if (name.size() > 1) {
769: throw new InvalidNameException(
770: "Multiple name systems are not supported!");
771: }
772: result = nameParser.parse(name.get(0));
773: } else {
774: result = (Name) name.clone();
775: }
776:
777: while (!result.isEmpty()) {
778: if (result.get(0).length() == 0) {
779: result.remove(0);
780: continue;
781: }
782: if (result.get(result.size() - 1).length() == 0) {
783: result.remove(result.size() - 1);
784: continue;
785: }
786: break;
787: }
788: // Validate that there are not intermediate empty components.
789: // Skip first and last element, they are valid
790: for (int i = 1; i < result.size() - 1; i++) {
791: if (result.get(i).length() == 0) {
792: throw new InvalidNameException(
793: "Empty intermediate components are not supported!");
794: }
795: }
796: return result;
797: }
798:
799: /**
800: * Returns the compound string name of this context.
801: * Suppose a/b/c/d is the full name and this context is "c".
802: * It's compound string name is a/b/c
803: *
804: * @return compound string name of the context
805: */
806: String getCompoundStringName() throws NamingException {
807:
808: //StringBuffer compositeName = new StringBuffer();
809: String compositeName = "";
810: MockContext curCtx = this ;
811: while (!curCtx.isRootContext()) {
812: compositeName = composeName(compositeName, curCtx
813: .getAtomicName());
814: curCtx = curCtx.getParentContext();
815: }
816:
817: return compositeName;
818:
819: }
820:
821: /**
822: * Returns parent context of this context
823: */
824: MockContext getParentContext() {
825: return parent;
826: }
827:
828: /**
829: * Returns the "atomic" (as opposed to "composite") name of the context.
830: * @return name of the context
831: */
832: String getAtomicName() {
833: return contextName;
834: }
835:
836: /**
837: * Returns true if this context is the root context
838: * @return true if the context is the root context
839: */
840: boolean isRootContext() {
841: return getParentContext() == null;
842: }
843:
844: private void throwMethodNotImplemented(String methodName) {
845:
846: throw new MethodNotImplementedException(methodName, this
847: .getClass().getName());
848:
849: }
850:
851: private class NamingEnumerationImpl implements NamingEnumeration {
852:
853: private Vector elements;
854: private int currentElement;
855:
856: NamingEnumerationImpl(Vector elements) {
857:
858: this .elements = elements;
859: this .currentElement = 0;
860: }
861:
862: public void close() {
863:
864: currentElement = 0;
865: elements.clear();
866: }
867:
868: public boolean hasMore() {
869:
870: return hasMoreElements();
871: }
872:
873: public boolean hasMoreElements() {
874:
875: if (currentElement < elements.size()) {
876: return true;
877: }
878: close();
879: return false;
880: }
881:
882: public Object next() {
883:
884: return nextElement();
885: }
886:
887: public Object nextElement() {
888:
889: if (hasMoreElements()) {
890: return elements.get(currentElement++);
891: }
892: throw new NoSuchElementException();
893: }
894: }
895: }
|