View Javadoc

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