View Javadoc

1   // ========================================================================
2   // $Id: NamingContext.java 887 2006-09-05 13:46:42Z janb $
3   // Copyright 1999-2006 Mort Bay Consulting Pty. Ltd.
4   // ------------------------------------------------------------------------
5   // Licensed under the Apache License, Version 2.0 (the "License");
6   // you may not use this file except in compliance with the License.
7   // You may obtain a copy of the License at 
8   // http://www.apache.org/licenses/LICENSE-2.0
9   // Unless required by applicable law or agreed to in writing, software
10  // distributed under the License is distributed on an "AS IS" BASIS,
11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  // See the License for the specific language governing permissions and
13  // limitations under the License.
14  // ========================================================================
15  
16  package org.mortbay.naming;
17  
18  
19  import java.util.Enumeration;
20  import java.util.Hashtable;
21  import java.util.NoSuchElementException;
22  
23  import javax.naming.Binding;
24  import javax.naming.CompoundName;
25  import javax.naming.Context;
26  import javax.naming.InitialContext;
27  import javax.naming.LinkRef;
28  import javax.naming.Name;
29  import javax.naming.NameAlreadyBoundException;
30  import javax.naming.NameClassPair;
31  import javax.naming.NameNotFoundException;
32  import javax.naming.NameParser;
33  import javax.naming.NamingEnumeration;
34  import javax.naming.NamingException;
35  import javax.naming.NotContextException;
36  import javax.naming.OperationNotSupportedException;
37  import javax.naming.Reference;
38  import javax.naming.Referenceable;
39  import javax.naming.spi.NamingManager;
40  
41  import org.mortbay.log.Log;
42  
43  
44  /*------------------------------------------------*/    
45  /** NamingContext
46   * <p>Implementation of Context interface.
47   *
48   * <p><h4>Notes</h4>
49   * <p>All Names are expected to be Compound, not Composite.
50   *
51   * <p><h4>Usage</h4>
52   * <pre>
53   */
54  /*
55  * </pre>
56  *
57  * @see
58  *
59  * @author <a href="mailto:janb@mortbay.com">Jan Bartel</a>
60  * @version 1.0
61  */
62  public class NamingContext implements Context, Cloneable
63  {
64  
65      public static final String LOCK_PROPERTY = "org.mortbay.jndi.lock";
66      public static final String UNLOCK_PROPERTY = "org.mortbay.jndi.unlock";
67      
68      public static final Enumeration EMPTY_ENUM = new Enumeration ()
69          {       
70              public boolean hasMoreElements()
71              {
72                  return false;
73              }
74              
75              public Object nextElement ()
76              {
77                  throw new NoSuchElementException();
78              }
79          };
80  
81      
82      protected Context _parent = null;
83      protected String _name = null;
84      protected Hashtable _env = null;
85      protected Hashtable _bindings = new Hashtable();
86      protected NameParser _parser = null;
87  
88  
89  
90      /*------------------------------------------------*/    
91      /** NameEnumeration
92       * <p>Implementation of NamingEnumeration interface.
93       *
94       * <p><h4>Notes</h4>
95       * <p>Used for returning results of Context.list();
96       *
97       * <p><h4>Usage</h4>
98       * <pre>
99       */
100     /*
101      * </pre>
102      *
103      * @see
104      *
105      */
106     public class NameEnumeration implements NamingEnumeration
107     {
108         Enumeration _delegate;
109 
110         public NameEnumeration (Enumeration e)
111         {
112             _delegate = e;
113         }
114 
115         public void close()
116             throws NamingException
117         {
118         }
119 
120         public boolean hasMore ()
121             throws NamingException
122         {
123             return _delegate.hasMoreElements();
124         }
125 
126         public Object next()
127             throws NamingException
128         {
129             Binding b = (Binding)_delegate.nextElement();
130             return new NameClassPair (b.getName(), b.getClassName(), true);
131         }
132 
133         public boolean hasMoreElements()
134         {
135             return _delegate.hasMoreElements();
136         }
137 
138         public Object nextElement()
139         {
140             Binding b = (Binding)_delegate.nextElement();
141             return new NameClassPair (b.getName(), b.getClassName(), true);
142         }
143     }
144 
145 
146 
147 
148 
149 
150     /*------------------------------------------------*/    
151     /** BindingEnumeration
152      * <p>Implementation of NamingEnumeration
153      *
154      * <p><h4>Notes</h4>
155      * <p>Used to return results of Context.listBindings();
156      *
157      * <p><h4>Usage</h4>
158      * <pre>
159      */
160     /*
161      * </pre>
162      *
163      * @see
164      *
165      */
166     public class BindingEnumeration implements NamingEnumeration
167     {       
168         Enumeration _delegate;
169 
170         public BindingEnumeration (Enumeration e)
171         {
172             _delegate = e;
173         }
174 
175         public void close()
176             throws NamingException
177         {
178         }
179 
180         public boolean hasMore ()
181             throws NamingException
182         {
183             return _delegate.hasMoreElements();
184         }
185 
186         public Object next()
187             throws NamingException
188         {
189             Binding b = (Binding)_delegate.nextElement();
190             return new Binding (b.getName(), b.getClassName(), b.getObject(), true);
191         }
192 
193         public boolean hasMoreElements()
194         {
195             return _delegate.hasMoreElements();
196         }
197 
198         public Object nextElement()
199         {
200             Binding b = (Binding)_delegate.nextElement();
201             return new Binding (b.getName(), b.getClassName(), b.getObject(),true);
202         }
203     }
204 
205 
206 
207     /*------------------------------------------------*/    
208     /**
209      * Constructor
210      *
211      * @param env environment properties
212      * @param name relative name of this context
213      * @param parent immediate ancestor Context (can be null)
214      * @param parser NameParser for this Context
215      */
216     public NamingContext(Hashtable env, 
217                          String name, 
218                          Context parent, 
219                          NameParser parser) 
220     {
221         if (env == null)
222             _env = new Hashtable();
223         else
224             _env = new Hashtable(env);
225         _name = name;
226         _parent = parent;
227         _parser = parser;
228     } 
229 
230 
231 
232     /*------------------------------------------------*/
233     /**
234      * Creates a new <code>NamingContext</code> instance.
235      *
236      * @param env a <code>Hashtable</code> value
237      */
238     public NamingContext (Hashtable env)
239     {
240         if (env == null)
241             _env = new Hashtable();
242         else
243             _env = new Hashtable(env);
244     }
245 
246 
247 
248 
249     /*------------------------------------------------*/
250     /**
251      * Constructor
252      *
253      */
254     public NamingContext ()
255     {
256         _env = new Hashtable();
257     }
258 
259 
260     /*------------------------------------------------*/
261     /**
262      * Clone this NamingContext
263      *
264      * @return copy of this NamingContext
265      * @exception CloneNotSupportedException if an error occurs
266      */
267     public Object clone ()
268         throws CloneNotSupportedException
269     {
270         NamingContext ctx = (NamingContext)super.clone();
271         ctx._env = (Hashtable)_env.clone();
272         ctx._bindings = (Hashtable)_bindings.clone();
273         return ctx;
274     }
275 
276 
277     /*------------------------------------------------*/
278     /**
279      * Getter for _name
280      *
281      * @return name of this Context (relative, not absolute)
282      */
283     public String getName ()
284     {
285         return _name;
286     }
287 
288     /*------------------------------------------------*/
289     /**
290      * Getter for _parent
291      *
292      * @return parent Context
293      */
294     public Context getParent()
295     {
296         return _parent;
297     }
298 
299     /*------------------------------------------------*/
300     /**
301      * Setter for _parser
302      *
303      * 
304      */
305     public void setNameParser (NameParser parser)
306     {
307         _parser = parser;
308     }
309 
310 
311 
312     /*------------------------------------------------*/
313     /**
314      * Bind a name to an object
315      *
316      * @param name Name of the object
317      * @param obj object to bind
318      * @exception NamingException if an error occurs
319      */
320     public void bind(Name name, Object obj) 
321         throws NamingException
322     {
323         if (isLocked())
324             throw new NamingException ("This context is immutable");
325 
326         Name cname = toCanonicalName(name);
327         
328         if (cname == null)
329             throw new NamingException ("Name is null");
330         
331         if (cname.size() == 0)
332             throw new NamingException ("Name is empty");
333 
334 
335         //if no subcontexts, just bind it
336         if (cname.size() == 1)
337         {
338             //get the object to be bound
339             Object objToBind = NamingManager.getStateToBind(obj, name,this, null);
340             // Check for Referenceable
341             if (objToBind instanceof Referenceable) 
342             {
343                 objToBind = ((Referenceable)objToBind).getReference();
344             }
345             //anything else we should be able to bind directly
346                            
347             Binding binding = getBinding (cname);
348             if (binding == null)
349                 addBinding (cname, objToBind);
350             else
351                 throw new NameAlreadyBoundException (cname.toString());
352         }
353         else
354         {
355             if(Log.isDebugEnabled())Log.debug("Checking for existing binding for name="+cname+" for first element of name="+cname.get(0));
356           
357             //walk down the subcontext hierarchy       
358             //need to ignore trailing empty "" name components
359                     
360             String firstComponent = cname.get(0);
361             Object ctx = null;
362 
363             if (firstComponent.equals(""))
364                 ctx = this;
365             else
366             {
367 
368                 Binding  binding = getBinding (firstComponent);
369                 if (binding == null)
370                     throw new NameNotFoundException (firstComponent+ " is not bound");
371                 
372                 ctx = binding.getObject();
373                 
374                 
375                 if (ctx instanceof Reference)
376                 {  
377                     //deference the object
378                     try
379                     {
380                         ctx = NamingManager.getObjectInstance(ctx, getNameParser("").parse(firstComponent), this, _env);
381                     }
382                     catch (NamingException e)
383                     {
384                         throw e;
385                     }
386                     catch (Exception e)
387                     {
388                         Log.warn("",e);
389                         throw new NamingException (e.getMessage());
390                     }
391                 }
392             }
393 
394 
395             if (ctx instanceof Context)
396             {
397                 ((Context)ctx).bind (cname.getSuffix(1), obj);
398             }
399             else
400                 throw new NotContextException ("Object bound at "+firstComponent +" is not a Context");
401         }
402     }
403 
404 
405 
406     /*------------------------------------------------*/
407     /**
408      * Bind a name (as a String) to an object
409      *
410      * @param name a <code>String</code> value
411      * @param obj an <code>Object</code> value
412      * @exception NamingException if an error occurs
413      */
414     public void bind(String name, Object obj) 
415         throws NamingException
416     {
417         bind (_parser.parse(name), obj);
418     }
419 
420 
421     /*------------------------------------------------*/
422     /**
423      * Create a context as a child of this one
424      *
425      * @param name a <code>Name</code> value
426      * @return a <code>Context</code> value
427      * @exception NamingException if an error occurs
428      */
429     public Context createSubcontext (Name name)
430         throws NamingException
431     {
432         if (isLocked())
433         {
434             NamingException ne = new NamingException ("This context is immutable"); 
435             ne.setRemainingName(name);
436             throw ne;
437         }
438            
439         
440         
441         Name cname = toCanonicalName (name);
442 
443         if (cname == null)
444             throw new NamingException ("Name is null");
445         if (cname.size() == 0)
446             throw new NamingException ("Name is empty");
447 
448         if (cname.size() == 1)
449         {
450             //not permitted to bind if something already bound at that name
451             Binding binding = getBinding (cname);
452             if (binding != null)
453                 throw new NameAlreadyBoundException (cname.toString());
454 
455             Context ctx = new NamingContext ((Hashtable)_env.clone(), cname.get(0), this, _parser);
456             addBinding (cname, ctx);
457             return ctx;
458         }
459         
460             
461         //If the name has multiple subcontexts, walk the hierarchy by
462         //fetching the first one. All intermediate subcontexts in the 
463         //name must already exist.
464         String firstComponent = cname.get(0);
465         Object ctx = null;
466 
467         if (firstComponent.equals(""))
468             ctx = this;
469         else
470         {
471             Binding binding = getBinding (firstComponent);
472             if (binding == null)
473                 throw new NameNotFoundException (firstComponent + " is not bound");
474             
475             ctx = binding.getObject();
476             
477             if (ctx instanceof Reference)
478             {  
479                 //deference the object
480                 if(Log.isDebugEnabled())Log.debug("Object bound at "+firstComponent +" is a Reference");
481                 try
482                 {
483                     ctx = NamingManager.getObjectInstance(ctx, getNameParser("").parse(firstComponent), this, _env);
484                 }
485                 catch (NamingException e)
486                 {
487                     throw e;
488                 }
489                 catch (Exception e)
490                 {
491                     Log.warn("",e);
492                     throw new NamingException (e.getMessage());
493                 }
494             }
495         }
496         
497         if (ctx instanceof Context)
498         {
499             return ((Context)ctx).createSubcontext (cname.getSuffix(1));
500         }
501         else
502             throw new NotContextException (firstComponent +" is not a Context");
503     }
504 
505 
506     /*------------------------------------------------*/
507     /**
508      * Create a Context as a child of this one
509      *
510      * @param name a <code>String</code> value
511      * @return a <code>Context</code> value
512      * @exception NamingException if an error occurs
513      */
514     public Context createSubcontext (String name)
515         throws NamingException
516     {
517         return createSubcontext(_parser.parse(name));
518     }
519 
520 
521 
522     /*------------------------------------------------*/
523     /**
524      * Not supported
525      *
526      * @param name name of subcontext to remove
527      * @exception NamingException if an error occurs
528      */
529     public void destroySubcontext (String name)
530         throws NamingException
531     {
532         removeBinding(_parser.parse(name));
533     }
534 
535 
536 
537     /*------------------------------------------------*/
538     /**
539      * Not supported
540      *
541      * @param name name of subcontext to remove
542      * @exception NamingException if an error occurs
543      */
544     public void destroySubcontext (Name name)
545         throws NamingException
546     {
547          removeBinding(name);
548     }
549 
550     /*------------------------------------------------*/
551     /**
552      * Lookup a binding by name
553      *
554      * @param name name of bound object
555      * @exception NamingException if an error occurs
556      */
557     public Object lookup(Name name)
558         throws NamingException
559     {
560         if(Log.isDebugEnabled())Log.debug("Looking up name=\""+name+"\"");
561         Name cname = toCanonicalName(name);
562 
563         if ((cname == null) || (cname.size() == 0))
564         {
565             Log.debug("Null or empty name, returning copy of this context");
566             NamingContext ctx = new NamingContext (_env, _name, _parent, _parser);
567             ctx._bindings = _bindings;
568             return ctx;
569         }
570 
571     
572       
573         if (cname.size() == 1)
574         {
575             Binding binding = getBinding (cname);
576             if (binding == null)
577             {
578                 NameNotFoundException nnfe = new NameNotFoundException();
579                 nnfe.setRemainingName(cname);
580                 throw nnfe;
581             }
582                 
583 
584             Object o = binding.getObject();
585 
586             //handle links by looking up the link
587             if (o instanceof LinkRef)
588             {
589                 //if link name starts with ./ it is relative to current context
590                 String linkName = ((LinkRef)o).getLinkName();
591                 if (linkName.startsWith("./"))
592                     return this.lookup (linkName.substring(2));
593                 else
594                 {
595                     //link name is absolute
596                     InitialContext ictx = new InitialContext();
597                     return ictx.lookup (linkName);
598                 }
599             }
600             else if (o instanceof Reference)
601             {
602                 //deference the object
603                 try
604                 {
605                     return NamingManager.getObjectInstance(o, cname, this, _env);
606                 }
607                 catch (NamingException e)
608                 {
609                     throw e;
610                 }
611                 catch (Exception e)
612                 {
613                     Log.warn("",e);
614                     throw new NamingException (e.getMessage());
615                 }
616             }
617             else
618                 return o;
619         }
620 
621         //it is a multipart name, recurse to the first subcontext
622    
623         String firstComponent = cname.get(0);
624         Object ctx = null;
625 
626         if (firstComponent.equals(""))
627             ctx = this;
628         else
629         {
630             
631             Binding binding = getBinding (firstComponent);
632             if (binding == null)
633             {
634                 NameNotFoundException nnfe = new NameNotFoundException();
635                 nnfe.setRemainingName(cname);
636                 throw nnfe;
637             }
638             
639             //as we have bound a reference to an object factory 
640             //for the component specific contexts
641             //at "comp" we need to resolve the reference
642             ctx = binding.getObject();
643             
644             if (ctx instanceof Reference)
645             {  
646                 //deference the object
647                 try
648                 {
649                     ctx = NamingManager.getObjectInstance(ctx, getNameParser("").parse(firstComponent), this, _env);
650                 }
651                 catch (NamingException e)
652                 {
653                     throw e;
654                 }
655                 catch (Exception e)
656                 {
657                     Log.warn("",e);
658                     throw new NamingException (e.getMessage());
659                 }
660             }
661         }
662         if (!(ctx instanceof Context))
663             throw new NotContextException();
664 
665         return ((Context)ctx).lookup (cname.getSuffix(1));
666     }
667 
668 
669     /*------------------------------------------------*/
670     /**
671      * Lookup binding of an object by name
672      *
673      * @param name name of bound object
674      * @return object bound to name
675      * @exception NamingException if an error occurs
676      */
677     public Object lookup (String name)
678         throws NamingException
679     {
680         return lookup (_parser.parse(name));
681     }
682 
683 
684 
685     /*------------------------------------------------*/
686     /**
687      * Lookup link bound to name
688      *
689      * @param name name of link binding
690      * @return LinkRef or plain object bound at name
691      * @exception NamingException if an error occurs
692      */
693     public Object lookupLink (Name name)
694         throws NamingException 
695     {      
696         Name cname = toCanonicalName(name);
697 
698         if (cname == null)
699         {
700             NamingContext ctx = new NamingContext (_env, _name, _parent, _parser);
701             ctx._bindings = _bindings;
702             return ctx;
703         }
704         if (cname.size() == 0)
705             throw new NamingException ("Name is empty");
706 
707         if (cname.size() == 1)
708         {
709             Binding binding = getBinding (cname);
710             if (binding == null)
711                 throw new NameNotFoundException();
712 
713             Object o = binding.getObject();
714 
715             //handle links by looking up the link
716             if (o instanceof Reference)
717             {
718                 //deference the object
719                 try
720                 {
721                     return NamingManager.getObjectInstance(o, cname.getPrefix(1), this, _env);
722                 }
723                 catch (NamingException e)
724                 {
725                     throw e;
726                 }
727                 catch (Exception e)
728                 {
729                     Log.warn("",e);
730                     throw new NamingException (e.getMessage());
731                 }
732             }
733             else
734             {
735                 //object is either a LinkRef which we don't dereference
736                 //or a plain object in which case spec says we return it
737                 return o;
738             }
739         }
740 
741 
742         //it is a multipart name, recurse to the first subcontext
743         String firstComponent = cname.get(0);
744         Object ctx = null;
745         
746         if (firstComponent.equals(""))
747             ctx = this;
748         else
749         {
750             Binding binding = getBinding (firstComponent);
751             if (binding == null)
752                 throw new NameNotFoundException ();
753             
754             ctx = binding.getObject();
755 
756             if (ctx instanceof Reference)
757             {  
758                 //deference the object
759                 try
760                 {
761                     ctx = NamingManager.getObjectInstance(ctx, getNameParser("").parse(firstComponent), this, _env);
762                 }
763                 catch (NamingException e)
764                 {
765                     throw e;
766                 }
767                 catch (Exception e)
768                 {
769                     Log.warn("",e);
770                     throw new NamingException (e.getMessage());
771                 }
772             }
773         }
774 
775         if (!(ctx instanceof Context))
776             throw new NotContextException();
777 
778         return ((Context)ctx).lookup (cname.getSuffix(1));
779     }
780 
781 
782     /*------------------------------------------------*/
783     /**
784      * Lookup link bound to name
785      *
786      * @param name name of link binding
787      * @return LinkRef or plain object bound at name
788      * @exception NamingException if an error occurs
789      */
790     public Object lookupLink (String name)
791         throws NamingException
792     {
793         return lookupLink (_parser.parse(name));
794     }
795 
796 
797     /*------------------------------------------------*/
798     /**
799      * List all names bound at Context named by Name
800      *
801      * @param name a <code>Name</code> value
802      * @return a <code>NamingEnumeration</code> value
803      * @exception NamingException if an error occurs
804      */
805     public NamingEnumeration list(Name name)
806         throws NamingException
807     {
808         if(Log.isDebugEnabled())Log.debug("list() on Context="+getName()+" for name="+name);
809         Name cname = toCanonicalName(name);
810 
811      
812 
813         if (cname == null)
814         {
815             return new NameEnumeration(EMPTY_ENUM);
816         }
817 
818         
819         if (cname.size() == 0)
820         {
821            return new NameEnumeration (_bindings.elements()); 
822         }
823 
824       
825 
826         //multipart name
827         String firstComponent = cname.get(0);
828         Object ctx = null;
829 
830         if (firstComponent.equals(""))
831             ctx = this;
832         else
833         {
834             Binding binding = getBinding (firstComponent);
835             if (binding == null)
836                 throw new NameNotFoundException ();
837             
838             ctx = binding.getObject();
839             
840             if (ctx instanceof Reference)
841             {  
842                 //deference the object
843                 if(Log.isDebugEnabled())Log.debug("Dereferencing Reference for "+name.get(0));
844                 try
845                 {
846                     ctx = NamingManager.getObjectInstance(ctx, getNameParser("").parse(firstComponent), this, _env);
847                 }
848                 catch (NamingException e)
849                 {
850                     throw e;
851                 }
852                 catch (Exception e)
853                 {
854                     Log.warn("",e);
855                     throw new NamingException (e.getMessage());
856                 }
857             }
858         }
859 
860         if (!(ctx instanceof Context))
861             throw new NotContextException();
862 
863         return ((Context)ctx).list (cname.getSuffix(1));       
864     }
865 
866 
867     /*------------------------------------------------*/
868     /**
869      * List all names bound at Context named by Name
870      *
871      * @param name a <code>Name</code> value
872      * @return a <code>NamingEnumeration</code> value
873      * @exception NamingException if an error occurs
874      */       
875     public NamingEnumeration list(String name)
876         throws NamingException
877     {
878         return list(_parser.parse(name));
879     }
880 
881 
882 
883     /*------------------------------------------------*/
884     /**
885      * List all Bindings present at Context named by Name
886      *
887      * @param name a <code>Name</code> value
888      * @return a <code>NamingEnumeration</code> value
889      * @exception NamingException if an error occurs
890      */
891     public NamingEnumeration listBindings(Name name)
892         throws NamingException
893     {  
894         Name cname = toCanonicalName (name);
895 
896         if (cname == null)
897         {
898             return new BindingEnumeration(EMPTY_ENUM);
899         }
900 
901         if (cname.size() == 0)
902         {
903            return new BindingEnumeration (_bindings.elements()); 
904         }
905 
906       
907         
908         //multipart name
909         String firstComponent = cname.get(0);
910         Object ctx = null;
911 
912         //if a name has a leading "/" it is parsed as "" so ignore it by staying
913         //at this level in the tree
914         if (firstComponent.equals(""))
915             ctx = this;
916         else
917         {
918             //it is a non-empty name component
919             Binding binding = getBinding (firstComponent);
920             if (binding == null)
921                 throw new NameNotFoundException ();
922         
923             ctx = binding.getObject();
924 
925             if (ctx instanceof Reference)
926             {  
927                 //deference the object
928                 try
929                 {
930                     ctx = NamingManager.getObjectInstance(ctx, getNameParser("").parse(firstComponent), this, _env);
931                 }
932                 catch (NamingException e)
933                 {
934                     throw e;
935                 }
936                 catch (Exception e)
937                 {
938                     Log.warn("",e);
939                     throw new NamingException (e.getMessage());
940                 }
941             }
942         }
943 
944         if (!(ctx instanceof Context))
945             throw new NotContextException();
946 
947         return ((Context)ctx).listBindings (cname.getSuffix(1));
948     }
949 
950 
951 
952     /*------------------------------------------------*/
953     /**
954      * List all Bindings at Name
955      *
956      * @param name a <code>String</code> value
957      * @return a <code>NamingEnumeration</code> value
958      * @exception NamingException if an error occurs
959      */
960     public NamingEnumeration listBindings(String name)
961         throws NamingException
962     {
963         return listBindings (_parser.parse(name));
964     }
965 
966 
967     /*------------------------------------------------*/
968     /**
969      * Overwrite or create a binding
970      *
971      * @param name a <code>Name</code> value
972      * @param obj an <code>Object</code> value
973      * @exception NamingException if an error occurs
974      */
975     public void rebind(Name name,
976                        Object obj)
977         throws NamingException
978     {    
979         if (isLocked())
980             throw new NamingException ("This context is immutable");
981 
982         Name cname = toCanonicalName(name);
983 
984         if (cname == null)
985             throw new NamingException ("Name is null");
986         
987         if (cname.size() == 0)
988             throw new NamingException ("Name is empty");
989 
990 
991         //if no subcontexts, just bind it
992         if (cname.size() == 1)
993         {         
994             //check if it is a Referenceable
995             Object objToBind = NamingManager.getStateToBind(obj, name, this, null);
996             if (objToBind instanceof Referenceable)
997             {
998                 objToBind = ((Referenceable)objToBind).getReference();
999             }
1000             addBinding (cname, objToBind);
1001         }
1002         else
1003         { 
1004             //walk down the subcontext hierarchy
1005             if(Log.isDebugEnabled())Log.debug("Checking for existing binding for name="+cname+" for first element of name="+cname.get(0));
1006                     
1007             String firstComponent = cname.get(0);
1008             Object ctx = null;
1009 
1010             
1011             if (firstComponent.equals(""))
1012                 ctx = this;
1013             else
1014             {
1015                 Binding  binding = getBinding (name.get(0));
1016                 if (binding == null)
1017                     throw new NameNotFoundException (name.get(0)+ " is not bound");
1018             
1019                 ctx = binding.getObject();
1020 
1021 
1022                 if (ctx instanceof Reference)
1023                 {  
1024                     //deference the object
1025                     try
1026                     {
1027                         ctx = NamingManager.getObjectInstance(ctx, getNameParser("").parse(firstComponent), this, _env);
1028                     }
1029                     catch (NamingException e)
1030                     {
1031                         throw e;
1032                     }
1033                     catch (Exception e)
1034                     {
1035                         Log.warn("",e);
1036                         throw new NamingException (e.getMessage());
1037                     }
1038                 }
1039             }
1040 
1041             if (ctx instanceof Context)
1042             {
1043                 ((Context)ctx).rebind (cname.getSuffix(1), obj);
1044             }
1045             else
1046                 throw new NotContextException ("Object bound at "+firstComponent +" is not a Context");
1047         }
1048     }
1049 
1050 
1051     /*------------------------------------------------*/
1052     /**
1053      * Overwrite or create a binding from Name to Object
1054      *
1055      * @param name a <code>String</code> value
1056      * @param obj an <code>Object</code> value
1057      * @exception NamingException if an error occurs
1058      */
1059     public void rebind (String name,
1060                         Object obj)
1061         throws NamingException
1062     {
1063         rebind (_parser.parse(name), obj);
1064     }
1065 
1066     /*------------------------------------------------*/
1067     /**
1068      * Not supported.
1069      *
1070      * @param name a <code>String</code> value
1071      * @exception NamingException if an error occurs
1072      */
1073     public void unbind (String name)
1074         throws NamingException
1075     {
1076         unbind(_parser.parse(name));
1077     }
1078 
1079     /*------------------------------------------------*/
1080     /**
1081      * Not supported.
1082      *
1083      * @param name a <code>String</code> value
1084      * @exception NamingException if an error occurs
1085      */
1086     public void unbind (Name name)
1087         throws NamingException
1088     {
1089         if (name.size() == 0)
1090             return;
1091         
1092         
1093         if (isLocked())
1094             throw new NamingException ("This context is immutable");
1095 
1096         Name cname = toCanonicalName(name);
1097 
1098         if (cname == null)
1099             throw new NamingException ("Name is null");
1100         
1101         if (cname.size() == 0)
1102             throw new NamingException ("Name is empty");
1103 
1104 
1105         //if no subcontexts, just unbind it
1106         if (cname.size() == 1)
1107         {         
1108             removeBinding (cname);
1109         }
1110         else
1111         { 
1112             //walk down the subcontext hierarchy
1113             if(Log.isDebugEnabled())Log.debug("Checking for existing binding for name="+cname+" for first element of name="+cname.get(0));
1114                     
1115             String firstComponent = cname.get(0);
1116             Object ctx = null;
1117 
1118             
1119             if (firstComponent.equals(""))
1120                 ctx = this;
1121             else
1122             {
1123                 Binding  binding = getBinding (name.get(0));
1124                 if (binding == null)
1125                     throw new NameNotFoundException (name.get(0)+ " is not bound");
1126             
1127                 ctx = binding.getObject();
1128 
1129 
1130                 if (ctx instanceof Reference)
1131                 {  
1132                     //deference the object
1133                     try
1134                     {
1135                         ctx = NamingManager.getObjectInstance(ctx, getNameParser("").parse(firstComponent), this, _env);
1136                     }
1137                     catch (NamingException e)
1138                     {
1139                         throw e;
1140                     }
1141                     catch (Exception e)
1142                     {
1143                         Log.warn("",e);
1144                         throw new NamingException (e.getMessage());
1145                     }
1146                 }
1147             }
1148 
1149             if (ctx instanceof Context)
1150             {
1151                 ((Context)ctx).unbind (cname.getSuffix(1));
1152             }
1153             else
1154                 throw new NotContextException ("Object bound at "+firstComponent +" is not a Context");
1155         }
1156         
1157     }
1158 
1159     /*------------------------------------------------*/
1160     /**
1161      * Not supported
1162      *
1163      * @param oldName a <code>Name</code> value
1164      * @param newName a <code>Name</code> value
1165      * @exception NamingException if an error occurs
1166      */
1167     public void rename(Name oldName,
1168                        Name newName)
1169         throws NamingException
1170     {
1171         throw new OperationNotSupportedException();
1172     }
1173 
1174     
1175     /*------------------------------------------------*/
1176     /**
1177      * Not supported
1178      *
1179      * @param oldName a <code>Name</code> value
1180      * @param newName a <code>Name</code> value
1181      * @exception NamingException if an error occurs
1182      */    public void rename(String oldName,
1183                        String newName)
1184         throws NamingException
1185     {
1186         throw new OperationNotSupportedException();
1187     }
1188 
1189 
1190 
1191     /*------------------------------------------------*/
1192     /** Join two names together. These are treated as
1193      * CompoundNames.
1194      *
1195      * @param name a <code>Name</code> value
1196      * @param prefix a <code>Name</code> value
1197      * @return a <code>Name</code> value
1198      * @exception NamingException if an error occurs
1199      */
1200     public Name composeName(Name name,
1201                             Name prefix)
1202         throws NamingException
1203     {
1204         if (name == null)
1205             throw new NamingException ("Name cannot be null");
1206         if (prefix == null)
1207             throw new NamingException ("Prefix cannot be null");
1208 
1209         Name compoundName = (CompoundName)prefix.clone();
1210         compoundName.addAll (name);
1211         return compoundName;
1212     }
1213 
1214 
1215 
1216     /*------------------------------------------------*/    
1217     /** Join two names together. These are treated as
1218      * CompoundNames.
1219      *
1220      * @param name a <code>Name</code> value
1221      * @param prefix a <code>Name</code> value
1222      * @return a <code>Name</code> value
1223      * @exception NamingException if an error occurs
1224      */
1225     public String composeName (String name,
1226                                String prefix)
1227         throws NamingException
1228     {       
1229         if (name == null)
1230             throw new NamingException ("Name cannot be null");
1231         if (prefix == null)
1232             throw new NamingException ("Prefix cannot be null");
1233 
1234         Name compoundName = _parser.parse(prefix);
1235         compoundName.add (name);
1236         return compoundName.toString();
1237     }
1238 
1239 
1240     /*------------------------------------------------*/    
1241     /**
1242      * Do nothing
1243      *
1244      * @exception NamingException if an error occurs
1245      */
1246     public void close ()
1247         throws NamingException
1248     {
1249         
1250         
1251     }
1252 
1253 
1254     /*------------------------------------------------*/    
1255     /**
1256      * Return a NameParser for this Context.
1257      *
1258      * @param name a <code>Name</code> value
1259      * @return a <code>NameParser</code> value
1260      */
1261     public NameParser getNameParser (Name name)
1262     {
1263         return _parser;
1264     }
1265 
1266     /*------------------------------------------------*/    
1267     /**
1268      * Return a NameParser for this Context.
1269      *
1270      * @param name a <code>Name</code> value
1271      * @return a <code>NameParser</code> value
1272      */    
1273     public NameParser getNameParser (String name)
1274     {
1275         return _parser;
1276     }
1277     
1278 
1279     /*------------------------------------------------*/    
1280     /**
1281      * Get the full name of this Context node
1282      * by visiting it's ancestors back to root.
1283      *
1284      * NOTE: if this Context has a URL namespace then
1285      * the URL prefix will be missing
1286      *
1287      * @return the full name of this Context
1288      * @exception NamingException if an error occurs
1289      */
1290     public String getNameInNamespace ()
1291         throws NamingException
1292     {
1293         Name name = _parser.parse("");
1294 
1295         NamingContext c = this;
1296         while (c != null)
1297         {
1298             String str = c.getName();
1299             if (str != null)
1300                 name.add(0, str);
1301             c = (NamingContext)c.getParent();
1302         }
1303         return name.toString();
1304     }
1305 
1306 
1307     /*------------------------------------------------*/    
1308     /**
1309      * Add an environment setting to this Context
1310      *
1311      * @param propName name of the property to add
1312      * @param propVal value of the property to add
1313      * @return propVal or previous value of the property
1314      * @exception NamingException if an error occurs
1315      */
1316     public Object addToEnvironment(String propName,
1317                                    Object propVal)
1318         throws NamingException
1319     {
1320         if (isLocked() && !(propName.equals(UNLOCK_PROPERTY)))
1321             throw new NamingException ("This context is immutable");
1322         
1323         return _env.put (propName, propVal);
1324     }
1325 
1326 
1327     /*------------------------------------------------*/    
1328     /**
1329      * Remove a property from this Context's environment.
1330      *
1331      * @param propName name of property to remove
1332      * @return value of property or null if it didn't exist
1333      * @exception NamingException if an error occurs
1334      */
1335     public Object removeFromEnvironment(String propName)
1336         throws NamingException
1337     {
1338         if (isLocked())
1339             throw new NamingException ("This context is immutable");
1340         
1341         return _env.remove (propName);
1342     }
1343 
1344 
1345     /*------------------------------------------------*/    
1346     /**
1347      * Get the environment of this Context.
1348      *
1349      * @return a copy of the environment of this Context.
1350      */
1351     public Hashtable getEnvironment ()
1352     {
1353         return (Hashtable)_env.clone();
1354     }
1355 
1356 
1357     /*------------------------------------------------*/    
1358     /**
1359      * Add a name to object binding to this Context.
1360      *
1361      * @param name a <code>Name</code> value
1362      * @param obj an <code>Object</code> value
1363      */
1364     protected void addBinding (Name name, Object obj)
1365     {
1366         String key = name.toString();
1367         if(Log.isDebugEnabled())Log.debug("Adding binding with key="+key+" obj="+obj+" for context="+_name);
1368         _bindings.put (key, new Binding (key, obj));
1369     }
1370 
1371     /*------------------------------------------------*/    
1372     /**
1373      * Get a name to object binding from this Context
1374      *
1375      * @param name a <code>Name</code> value
1376      * @return a <code>Binding</code> value
1377      */
1378     protected Binding getBinding (Name name)
1379     {
1380         if(Log.isDebugEnabled())Log.debug("Looking up binding for "+name.toString()+" for context="+_name);
1381         return (Binding) _bindings.get(name.toString());
1382     }
1383 
1384 
1385     /*------------------------------------------------*/    
1386     /**
1387      * Get a name to object binding from this Context
1388      *
1389      * @param name as a String
1390      * @return null or the Binding
1391      */
1392     protected Binding getBinding (String name)
1393     {
1394         if(Log.isDebugEnabled())Log.debug("Looking up binding for "+name+" for context="+_name);
1395         return (Binding) _bindings.get(name);
1396     }
1397 
1398 
1399     protected void removeBinding (Name name)
1400     {
1401         String key = name.toString();
1402         if (Log.isDebugEnabled()) Log.debug("Removing binding with key="+key);
1403         _bindings.remove(key);
1404     }
1405 
1406     /*------------------------------------------------*/    
1407     /**
1408      * Remove leading or trailing empty components from
1409      * name. Eg "/comp/env/" -> "comp/env"
1410      *
1411      * @param name the name to normalize
1412      * @return normalized name
1413      */
1414     public Name toCanonicalName (Name name)
1415     {
1416         Name canonicalName = name;
1417 
1418         if (name != null)
1419         {
1420             if (canonicalName.size() > 1)
1421             {
1422                 if (canonicalName.get(0).equals(""))
1423                     canonicalName = canonicalName.getSuffix(1);
1424  
1425                 
1426                 if (canonicalName.get(canonicalName.size()-1).equals(""))
1427                     canonicalName = canonicalName.getPrefix(canonicalName.size()-1);
1428                 
1429             }
1430         }
1431 
1432         return canonicalName;
1433     }
1434 
1435     private boolean isLocked()
1436     {
1437        if ((_env.get(LOCK_PROPERTY) == null) && (_env.get(UNLOCK_PROPERTY) == null))
1438            return false;
1439        
1440        Object lockKey = _env.get(LOCK_PROPERTY);
1441        Object unlockKey = _env.get(UNLOCK_PROPERTY);
1442        
1443        if ((lockKey != null) && (unlockKey != null) && (lockKey.equals(unlockKey)))
1444            return false;
1445        return true;
1446     }
1447    
1448 }