View Javadoc

1   /*
2    * Copyright (c) 2003 - 2005, Henri Yandell, Robert M. Zigweid
3    * All rights reserved.
4    * 
5    * Redistribution and use in source and binary forms, with or 
6    * without modification, are permitted provided that the 
7    * following conditions are met:
8    * 
9    * + Redistributions of source code must retain the above copyright notice, 
10   *   this list of conditions and the following disclaimer.
11   * 
12   * + Redistributions in binary form must reproduce the above copyright notice, 
13   *   this list of conditions and the following disclaimer in the documentation 
14   *   and/or other materials provided with the distribution.
15   * 
16   * + Neither the name of Simple-JNDI nor the names of its contributors 
17   *   may be used to endorse or promote products derived from this software 
18   *   without specific prior written permission.
19   * 
20   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
21   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
22   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
23   * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
24   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
25   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
26   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
27   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
28   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
29   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
30   * POSSIBILITY OF SUCH DAMAGE.
31   */
32  
33  package org.osjava.sj.jndi;
34  
35  import javax.naming.Context;
36  import javax.naming.ContextNotEmptyException;
37  import javax.naming.NamingException;
38  import javax.naming.NameParser;
39  import javax.naming.InvalidNameException;
40  import javax.naming.NameAlreadyBoundException;
41  import javax.naming.NotContextException;
42  import javax.naming.NameNotFoundException;
43  import javax.naming.NamingEnumeration;
44  import javax.naming.Name;
45  
46  import java.util.HashMap;
47  import java.util.Hashtable;
48  import java.util.Iterator;
49  import java.util.Map;
50  
51  import java.util.List;
52  import java.util.LinkedList;
53  
54  /***
55   * The heart of the system, the abstract implementation of context for 
56   * simple-jndi.  There are no abstract methods in this class, but it is
57   * not meant to be instantiated, but extended instead.
58   *
59   * @author Robert M. Zigweid
60   * @since Simple-JNDI 0.11
61   * @version $Rev: 2684 $ $Date: 2008-05-24 22:19:06 -0700 (Sat, 24 May 2008) $
62   */
63  public abstract class AbstractContext 
64          implements Cloneable, Context  {
65  
66      // used for the shared feature
67      private static final Hashtable TABLE = new Hashtable();
68      private static final Hashtable SUB_CONTEXTS = new Hashtable();
69  
70      // table is used as a read-write cache which sits 
71      // above the file-store
72      private Hashtable table = new Hashtable();
73      private Hashtable subContexts = new Hashtable();
74      private Hashtable env = new Hashtable();
75      private NameParser nameParser;
76      /* 
77       * The name full name of this context. 
78       */
79      private Name nameInNamespace = null;
80      private boolean nameLock = false;
81      private boolean closing;
82  
83      /* **********************************************************************
84       * Constructors.                                                        *
85       * Even though this class cannot be instantiated, it provides default   *
86       * implemenation for doing so in hopes of making Contexts that extend   *
87       * this class easier.                                                   *
88       * **********************************************************************/
89      /***
90       * Creates a AbstractContext.
91       */
92      protected AbstractContext() {
93          this((Hashtable)null);
94      }
95      
96      /***
97       * Creates a AbstractContext.
98       * 
99       * @param env a Hashtable containing the Context's environemnt.
100      */
101     protected AbstractContext(Hashtable env) {
102         /* By default allow system properties to override. */
103         this(env, true, null);
104     }
105     
106     /***
107      * Creates a AbstractContext.
108      * 
109      * @param env a Hashtable containing the Context's environment.
110      * @param systemOverride allow System Parameters to override the
111      *        environment that is passed in.
112      */
113     protected AbstractContext(Hashtable env, boolean systemOverride) {
114         this(env, systemOverride, null);
115     }
116 
117     /***
118      * Creates a AbstractContext.
119      * 
120      * @param env a Hashtable containing the Context's environment.
121      * @param parser the NameParser being used by the Context.
122      */
123     protected AbstractContext(Hashtable env, NameParser parser) {
124         this(env, true, parser);
125     }
126 
127     /***
128      * Creates a AbstractContext.
129      * 
130      * @param systemOverride allow System Parameters to override the
131      *        environment that is passed in.
132      */
133     protected AbstractContext(boolean systemOverride) {
134         this(null, systemOverride, null);
135     }
136 
137     /***
138      * Creates a AbstractContext.
139      * 
140      * @param systemOverride allow System Parameters to override the
141      *        environment that is passed in.
142      * @param parser the NameParser being used by the Context.
143      */
144     protected AbstractContext(boolean systemOverride, NameParser parser) {
145         this(null, systemOverride, parser);
146     }
147 
148     /***
149      * Creates a AbstractContext.
150      * 
151      * @param parser the NameParser being used by the Context.
152      */
153     protected AbstractContext(NameParser parser) {
154         this(null, true, parser);
155     }
156 
157     /***
158      * Creates a AbstractContext.
159      * 
160      * @param env a Hashtable containing the Context's environment.
161      * @param systemOverride allow System Parameters to override the
162      *        environment that is passed in.
163      * @param parser the NameParser being used by the Context.
164      */
165     protected AbstractContext(Hashtable env, boolean systemOverride, NameParser parser) {
166         String shared = null;
167         if(env != null) {
168             this.env = (Hashtable)env.clone();
169             shared = (String)this.env.get("org.osjava.sj.jndi.shared");
170         }
171 
172         /* let System properties override the jndi.properties file, if
173          * systemOverride is true */
174         if(systemOverride) {
175             if(System.getProperty("org.osjava.sj.jndi.shared") != null) {
176                 shared = System.getProperty("org.osjava.sj.jndi.shared");
177             }
178         }
179         
180         if("true".equals(shared)) {
181             this.table = new StaticHashtable(TABLE);
182             this.subContexts = new StaticHashtable(SUB_CONTEXTS);
183         }
184 
185         if(parser == null) {
186             try {
187                 nameParser = new SimpleNameParser(this);
188             } catch (NamingException e) {
189                 /* 
190                  * XXX: This should never really occur.  If it does, there is 
191                  * a severe problem.  I also don't want to throw the exception
192                  * right now because that would break compatability, even 
193                  * though it is probably the right thing to do.  This might
194                  * get upgraded to a fixme.
195                  */
196                 e.printStackTrace();
197             }
198         }
199         try {
200             nameInNamespace = nameParser.parse("");
201         } catch (NamingException e) {
202             // TODO Auto-generated catch block
203             /* This shouldn't be an issue at this point */
204             e.printStackTrace();
205         }
206     }
207 
208     /***
209      * Create a new context based upon the environment of the passed context.
210      * @param that
211      */
212     protected AbstractContext(AbstractContext that) {
213         this(that.env);
214     }
215 
216     
217     /* **********************************************************************
218      * Implementation of methods specified by java.lang.naming.Context      *
219      * **********************************************************************/
220     /*** 
221      * Return the named object.  
222      * This implementation looks for things in the following order:<br/>
223      * <ol>
224      * <li>The empty element to duplicate the context.</li>
225      * <li>A named object in the environment.</li>
226      * <li>A named object in the Context's store</li>
227      * <li>A named sub-Context of this Context</li>
228      * </ol>
229      * Unlike many implementations of the JNDI specification, this 
230      * implementation returns null when a name is not found, rather than 
231      * throwing an exception. The specification does not appear to state 
232      * which way it should be. 
233      *
234      * @see javax.naming.Context#lookup(javax.naming.Name)
235      */
236     public Object lookup(Name name) throws NamingException {
237         /* 
238          * The string form of the name will be used in several places below 
239          * if not matched in the hashtable.
240          */
241         String stringName = name.toString();
242         /*
243          * If name is empty then this context is to be cloned.  This is 
244          * required based upon the javadoc of Context.  UGH!
245          */
246         if(name.size() == 0) {
247             Object ret = null;
248             try {
249                 ret = (AbstractContext)this.clone();
250             } catch(CloneNotSupportedException e) {
251                 /* 
252                  * TODO: Improve error handling.  I'm not quite sure yet what 
253                  *       should be done, but this almost certainly isn't it.
254                  */
255                 e.printStackTrace();
256             }
257             if(ret != null) {
258                 return ret;
259             }
260         }
261 
262         /* Lookup system properties */
263         if(System.getProperty(stringName) != null) {
264             return System.getProperty(stringName);
265         }
266 
267         Name objName = name.getPrefix(1);
268         if(name.size() > 1) {
269             /* Look in a subcontext. */
270             if(subContexts.containsKey(objName)) {
271                 return ((Context)subContexts.get(objName)).lookup(name.getSuffix(1));
272             }
273             throw new NamingException("Invalid subcontext '" + objName.toString() + "' in context '" 
274                             + getNameInNamespace() + "'");
275         }
276         
277         /* Lookup the object in this context */
278         if(table.containsKey(name)) {
279             return table.get(objName);
280         }
281         
282         /* 
283          * Lookup the object from the subcontexts table and return it if found.
284          */
285         if(subContexts.containsKey(name)) {
286             return subContexts.get(name);
287         }
288         
289         /* Look it up in the environment. */
290         if(env.containsKey(name)) {
291             return env.get(name.toString());
292         }
293         /* Nothing could be found.  Return null. */
294         /*
295          * XXX: Is this right?  Should a NamingException be thrown here 
296          *      instead because nothing could be found?
297          */
298         return null;
299     }
300 
301     /***
302      * @see javax.naming.Context#lookup(java.lang.String)
303      */
304     public Object lookup(String name) throws NamingException {
305         return lookup(nameParser.parse(name));
306     }
307 
308     /***
309      * @see javax.naming.Context#bind(javax.naming.Name, java.lang.Object)
310      */
311     public void bind(Name name, Object object) throws NamingException {
312         /* 
313          * If the name of obj doesn't start with the name of this context, 
314          * it is an error, throw a NamingException
315          */
316         if(name.size() > 1) {
317             Name prefix = name.getPrefix(1);
318             if(subContexts.containsKey(prefix)) {
319                 ((Context)subContexts.get(prefix)).bind(name.getSuffix(1), object);
320                 return;
321             }
322         }
323         if(name.size() == 0) {
324             throw new InvalidNameException("Cannot bind to an empty name");
325         }
326         /* Determine if the name is already bound */
327         if(table.containsKey(name) ||
328            subContexts.containsKey(name) ||
329            env.containsKey(name.toString())) {
330             throw new NameAlreadyBoundException("Name " + name.toString()
331                 + " already bound.  Use rebind() to override");
332         }
333         
334         if(object instanceof Context) {
335             subContexts.put(name, object);
336         } else {
337             table.put(name, object);
338         }
339     }
340 
341     /***
342      * @see javax.naming.Context#bind(java.lang.String, java.lang.Object)
343      */
344     public void bind(String name, Object object) throws NamingException {
345         bind(nameParser.parse(name), object);
346     }
347 
348     /***
349      * @see javax.naming.Context#rebind(javax.naming.Name, java.lang.Object)
350      */
351     public void rebind(Name name, Object object) throws NamingException {
352         if(name.isEmpty()) {
353             throw new InvalidNameException("Cannot bind to empty name");
354         }
355         /* Look up the target context first. */
356         Object targetContext = lookup(name.getPrefix(name.size() - 1));
357         if(targetContext == null || !(targetContext instanceof Context)) {
358             throw new NamingException("Cannot bind object.  Target context does not exist.");
359         }
360         unbind(name);
361         bind(name, object);
362     }
363 
364     /***
365      * @see javax.naming.Context#rebind(java.lang.String, java.lang.Object)
366      */
367     public void rebind(String name, Object object) throws NamingException {
368         rebind(nameParser.parse(name), object);
369     }
370 
371     /***
372      * @see javax.naming.Context#unbind(javax.naming.Name)
373      */
374     public void unbind(Name name) throws NamingException {
375         if(name.isEmpty()) {
376             throw new InvalidNameException("Cannot unbind to empty name");
377         }
378 
379         if(name.size() == 1) {
380             if(table.containsKey(name)) {
381                 table.remove(name);
382             }
383             return;
384         }
385         
386         /* Look up the target context first. */
387         Object targetContext = lookup(name.getPrefix(name.size() - 1));
388         if(targetContext == null || !(targetContext instanceof Context)) {
389             throw new NamingException("Cannot unbind object.  Target context does not exist.");
390         }
391         ((Context)targetContext).unbind(name.getSuffix(name.size() - 1));
392     }
393 
394     /***
395      * @see javax.naming.Context#unbind(java.lang.String)
396      */
397     public void unbind(String name) throws NamingException {
398         unbind(nameParser.parse(name));
399     }
400 
401     /***
402      * @see javax.naming.Context#rename(javax.naming.Name, javax.naming.Name)
403      */
404     public void rename(Name oldName, Name newName) throws NamingException {
405         /* Confirm that this works.  We might have to catch the exception */
406         Object old = lookup(oldName);
407         if(newName.isEmpty()) {
408             throw new InvalidNameException("Cannot bind to empty name");
409         }
410         
411         if(old == null) {
412             throw new NamingException("Name '" + oldName + "' not found.");
413         }
414 
415         /* If the new name is bound throw a NameAlreadyBoundException */
416         if(lookup(newName) != null) {
417             throw new NameAlreadyBoundException("Name '" + newName + "' already bound");
418         }
419 
420         unbind(oldName);
421         unbind(newName);
422         bind(newName, old);
423         /* 
424          * If the object is a Thread, or a ThreadContext, give it the new 
425          * name.
426          */
427         if(old instanceof Thread) {
428             ((Thread)old).setName(newName.toString());
429         }
430     }
431 
432     /***
433      * @see javax.naming.Context#rename(java.lang.String, java.lang.String)
434      */
435     public void rename(String oldName, String newName) throws NamingException {
436         rename(nameParser.parse(oldName), nameParser.parse(newName));
437     }
438     /* End of Write-functionality */
439 
440     /* Start of List functionality */
441     /***
442      * @see javax.naming.Context#list(javax.naming.Name)
443      */
444     public NamingEnumeration list(Name name) throws NamingException {
445 //      if name is a directory, we should do the same as we do above
446 //      if name is a properties file, we should return the keys (?)
447 //      issues: default.properties ?
448         if(name == null || name.isEmpty()) {
449             /* 
450              * Because there are two mappings that need to be used here, 
451              * create a new mapping and add the two maps to it.  This also 
452              * adds the safety of cloning the two maps so the original is
453              * unharmed.
454              */
455             Map enumStore = new HashMap();
456             enumStore.putAll(table);
457             enumStore.putAll(subContexts);
458             NamingEnumeration enumerator = new ContextNames(enumStore);
459             return enumerator;
460         }
461         /* Look for a subcontext */
462         Name subName = name.getPrefix(1);
463         if(table.containsKey(subName)) {
464             /* Nope, actual object */
465             throw new NotContextException(name + " cannot be listed");
466         }
467         if(subContexts.containsKey(subName)) {
468             return ((Context)subContexts.get(subName)).list(name.getSuffix(1));
469         }
470         /* 
471          * Couldn't find the subcontext and it wasn't pointing at us, throw
472          * an exception.
473          */
474         /* TODO: Give this a better message */
475         throw new NamingException();
476     }
477 
478 
479     /***
480      * @see javax.naming.Context#list(java.lang.String)
481      */
482     public NamingEnumeration list(String name) throws NamingException {
483         return list(nameParser.parse(name));
484     }
485 
486     /***
487      * @see javax.naming.Context#listBindings(javax.naming.Name)
488      */
489     public NamingEnumeration listBindings(Name name) throws NamingException {
490         if(name == null || name.isEmpty()) {
491             /* 
492              * Because there are two mappings that need to be used here, 
493              * create a new mapping and add the two maps to it.  This also 
494              * adds the safety of cloning the two maps so the original is
495              * unharmed.
496              */
497             Map enumStore = new HashMap();
498             enumStore.putAll(table);
499             enumStore.putAll(subContexts);
500             return new ContextBindings(enumStore);
501         }
502         /* Look for a subcontext */
503         Name subName = name.getPrefix(1);
504         if(table.containsKey(subName)) {
505             /* Nope, actual object */
506             throw new NotContextException(name + " cannot be listed");
507         }
508         if(subContexts.containsKey(subName)) {
509             return ((Context)subContexts.get(subName)).listBindings(name.getSuffix(1));
510         }
511         /* 
512          * Couldn't find the subcontext and it wasn't pointing at us, throw
513          * an exception.
514          */
515         throw new NamingException();
516     }
517 
518     /***
519      * @see javax.naming.Context#listBindings(java.lang.String)
520      */
521     public NamingEnumeration listBindings(String name) throws NamingException {
522         return listBindings(nameParser.parse(name));
523     }
524     /* End of List functionality */
525 
526     /***
527      * @see javax.naming.Context#destroySubcontext(javax.naming.Name)
528      */
529     public void destroySubcontext(Name name) throws NamingException {
530         if(name.size() > 1) {
531             if(subContexts.containsKey(name.getPrefix(1))) {
532                 Context subContext = (Context)subContexts.get(name.getPrefix(1));
533                 subContext.destroySubcontext(name.getSuffix(1));
534                 return;
535             } 
536             /* TODO: Better message might be necessary */
537             throw new NameNotFoundException();
538         }
539         /* Look at the contextStore to see if the name is bound there */
540         if(table.containsKey(name)) {
541             throw new NotContextException();
542         }
543         /* Look for the subcontext */
544         if(!subContexts.containsKey(name)) {
545             throw new NameNotFoundException();
546         }
547         Context subContext = (Context)subContexts.get(name); 
548         /* Look to see if the context is empty */
549         NamingEnumeration names = subContext.list("");
550         if(names.hasMore()) {
551             throw new ContextNotEmptyException();
552         }
553         ((Context)subContexts.get(name)).close();
554         subContexts.remove(name);
555     }
556 
557     /***
558      * @see javax.naming.Context#destroySubcontext(java.lang.String)
559      */
560     public void destroySubcontext(String name) throws NamingException {
561         destroySubcontext(nameParser.parse(name));
562     }
563 
564     /***
565      * @see javax.naming.Context#createSubcontext(javax.naming.Name)
566      */
567     public abstract Context createSubcontext(Name name) throws NamingException;
568     /* TODO: Put this example implemenation into the javadoc.
569     /* Example implementation 
570     public Context createSubcontext(Name name) throws NamingException {
571         Context newContext;
572         Hashtable subContexts = getSubContexts();
573 
574         if(name.size() > 1) {
575             if(subContexts.containsKey(name.getPrefix(1))) {
576                 Context subContext = (Context)subContexts.get(name.getPrefix(1));
577                 newContext = subContext.createSubcontext(name.getSuffix(1));
578                 return newContext;
579             } 
580             throw new NameNotFoundException("The subcontext " + name.getPrefix(1) + " was not found.");
581         }
582         
583         if(lookup(name) != null) {
584             throw new NameAlreadyBoundException();
585         }
586 
587         Name contextName = getNameParser(getNameInNamespace())
588             .parse(getNameInNamespace());
589         contextName.addAll(name);
590         newContext = new GenericContext(this);
591         ((AbstractContext)newContext).setName(contextName);
592         subContexts.put(name, newContext);
593         return newContext;
594     }
595 
596     */
597     
598     /***
599      * @see javax.naming.Context#createSubcontext(java.lang.String)
600      */
601     public Context createSubcontext(String name) throws NamingException {
602         return createSubcontext(nameParser.parse(name));
603     }
604     
605 
606     /***
607      * @see javax.naming.Context#lookupLink(javax.naming.Name)
608      */
609     public Object lookupLink(Name name) throws NamingException {
610         return lookup(name);
611     }
612 
613     /***
614      * @see javax.naming.Context#lookupLink(java.lang.String)
615      */
616     public Object lookupLink(String name) throws NamingException {
617         return lookup(nameParser.parse(name));
618     }
619 
620     /***
621      * @see javax.naming.Context#getNameParser(javax.naming.Name)
622      */
623     public NameParser getNameParser(Name name) throws NamingException {
624         /* 
625          * XXX: Not sure this conditional is adequate.  It might still cause
626          *      problems with foo.foo name structures.
627          */
628         if(name == null ||
629            name.isEmpty() || 
630            (name.size() == 1 && name.toString().equals(getNameInNamespace()))) {
631             return nameParser;
632         }
633         Name subName = name.getPrefix(1); 
634         if(subContexts.containsKey(subName)) {
635             return ((Context)subContexts.get(subName)).getNameParser(name.getSuffix(1));
636         }
637         throw new NotContextException();
638     }
639 
640     /***
641      * @see javax.naming.Context#getNameParser(java.lang.String)
642      */
643     public NameParser getNameParser(String name) throws NamingException {
644         return getNameParser(nameParser.parse(name));
645     }
646 
647     /***
648      * @see javax.naming.Context#composeName(javax.naming.Name, javax.naming.Name)
649      */
650     public Name composeName(Name name, Name prefix) throws NamingException {
651         // XXX: NO IDEA IF THIS IS RIGHT
652         if(name == null || prefix == null) {
653             throw new NamingException("Arguments must not be null");
654         }
655         Name retName = (Name)prefix.clone();
656         retName.addAll(name);
657         return retName;
658     }
659 
660     /***
661      * @see javax.naming.Context#composeName(java.lang.String, java.lang.String)
662      */
663     public String composeName(String name, String prefix) throws NamingException {
664         Name retName = composeName(nameParser.parse(name), nameParser.parse(prefix));
665         /* toString pretty much is guaranteed to exist */
666         return retName.toString();
667     }
668 
669     /***
670      * @see javax.naming.Context#addToEnvironment(java.lang.String, java.lang.Object)
671      */
672     public Object addToEnvironment(String name, Object object) throws NamingException {
673         if(this.env == null) {
674             return null;
675         }
676         return this.env.put(name, object);
677     }
678 
679     /***
680      * @see javax.naming.Context#removeFromEnvironment(java.lang.String)
681      */
682     public Object removeFromEnvironment(String name) throws NamingException {
683         if(this.env == null) {
684             return null;
685         }
686         return this.env.remove(name);
687     }
688 
689     /***
690      * @see javax.naming.Context#getEnvironment()
691      */
692     public Hashtable getEnvironment() throws NamingException {
693         if(this.env == null) {
694             return new Hashtable();
695         }
696         return (Hashtable)this.env.clone();
697     }
698 
699     /***
700      * @see javax.naming.Context#close()
701      */
702     public void close() throws NamingException {
703         /* Don't try anything if we're already in the process of closing */
704         // BUG: closing is never actually set
705         if(closing) {
706             return;
707         }
708         Iterator it = subContexts.keySet().iterator();
709         while(it.hasNext()) {
710             destroySubcontext((Name)it.next());
711         }
712         
713         while(table.size() > 0 || subContexts.size() > 0) {
714             it = table.keySet().iterator();
715             List toRemove = new LinkedList();
716             while(it.hasNext()) {
717                 Name name = (Name)it.next();
718 
719                 Object entry = table.get(name);
720 
721                 if(entry instanceof Thread) {
722                     Thread thread = (Thread) entry;
723                     if(thread.isAlive()) {
724                         toRemove.add(name);
725                     }
726                 } else {
727                     toRemove.add(name);
728                 }
729             }
730             for(it = toRemove.iterator(); it.hasNext();) {
731                 table.remove(it.next());
732             }
733 
734             toRemove.clear();
735             it = subContexts.keySet().iterator();
736             while(it.hasNext()) {
737                 Name name = (Name)it.next();
738                 AbstractContext context = (AbstractContext)subContexts.get(name);
739                 if(context.isEmpty()) {
740                     toRemove.add(name);
741                 }
742             }
743             for(it = toRemove.iterator(); it.hasNext();) {
744                 subContexts.remove(it.next());
745             }
746         }
747         this.env = null;
748         this.table = null;
749     }
750 
751     /***
752      * @see javax.naming.Context#getNameInNamespace()
753      */
754     public String getNameInNamespace() throws NamingException {
755         return nameInNamespace.toString();
756     }
757 
758     /* **********************************************************************
759      * Implementation other methods used by the Context.                    *
760      * **********************************************************************/
761     /***
762      * Determine whether or not the context is empty.  Objects bound directly
763      * to the context or subcontexts are all that is considered.  The 
764      * environment of the context is not considered.
765      *    
766      * @return true of the context is empty, else false.
767      */
768     public boolean isEmpty() {
769         return (table.size() > 0 || subContexts.size() > 0);
770     }
771 
772     /***
773      * Set the name of the Context.  This is only used from createSubcontext. 
774      * It might get replaced by adding more constructors, but there is really
775      * no reason to expose it publicly anyway.
776      * 
777      * @param name the Name of the context.
778      * @throws NamingException if the subContext already has a name.
779      */
780     public void setNameInNamespace(Name name) throws NamingException {
781         if(nameLock) {
782             if(nameInNamespace != null || !nameInNamespace.isEmpty()) {
783                 throw new NamingException("Name already set.");
784             }
785         }
786         nameInNamespace = name;
787         nameLock = true;
788     }
789     
790     /***
791      * Set the name of the Context.  This is only used from createSubcontext. 
792      * It might get replaced by adding more constructors, but there is really
793      * no reason to expose it publicly anyway.
794      * 
795      * @param name a String representation of the Name of the context.
796      * @throws NamingException if the subContext already has a name.
797      */
798     protected void setNameInNamespace(String name) throws NamingException {
799         setNameInNamespace(nameParser.parse(name));
800     }
801 
802     /***
803      * Convenience method returning the subcontexts that this context parents.
804      * @return a Hashtable of context objects that are parented by this 
805      *         context.
806      */
807     protected Hashtable getSubContexts() {
808         return (Hashtable)subContexts.clone();
809     }
810 
811     /***
812      * Whether this context is running in shared mode AND has already 
813      * been loaded with data. 
814      */
815     public static boolean isSharedAndLoaded() {
816         return (TABLE.size() != 0 || SUB_CONTEXTS.size() != 0);
817     }
818 
819 }
820 
821