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.Iterator;
24  import java.util.List;
25  import java.util.Map;
26  
27  import javax.naming.Binding;
28  import javax.naming.CompoundName;
29  import javax.naming.Context;
30  import javax.naming.InitialContext;
31  import javax.naming.LinkRef;
32  import javax.naming.Name;
33  import javax.naming.NameAlreadyBoundException;
34  import javax.naming.NameClassPair;
35  import javax.naming.NameNotFoundException;
36  import javax.naming.NameParser;
37  import javax.naming.NamingEnumeration;
38  import javax.naming.NamingException;
39  import javax.naming.NotContextException;
40  import javax.naming.OperationNotSupportedException;
41  import javax.naming.Reference;
42  import javax.naming.Referenceable;
43  import javax.naming.spi.NamingManager;
44  
45  import org.eclipse.jetty.util.component.Dumpable;
46  import org.eclipse.jetty.util.log.Logger;
47  
48  
49  /*------------------------------------------------*/    
50  /** NamingContext
51   * <p>Implementation of Context interface.
52   *
53   * <p><h4>Notes</h4>
54   * <p>All Names are expected to be Compound, not Composite.
55   *
56   * <p><h4>Usage</h4>
57   * <pre>
58   */
59  /*
60  * </pre>
61  *
62  * @see
63  *
64  * 
65  * @version 1.0
66  */
67  public class NamingContext implements Context, Cloneable, Dumpable
68  {
69      private final static Logger __log=NamingUtil.__log;
70      private final static List<Binding> __empty = Collections.emptyList();
71      public static final String LOCK_PROPERTY = "org.eclipse.jndi.lock";
72      public static final String UNLOCK_PROPERTY = "org.eclipse.jndi.unlock";
73      
74      protected final Hashtable<String,Object> _env = new Hashtable<String,Object>();
75      protected Map<String,Binding> _bindings = new HashMap<String,Binding>();
76  
77      protected NamingContext _parent = null;
78      protected String _name = null;
79      protected NameParser _parser = null;
80      private Collection<Listener> _listeners; 
81  
82      /*------------------------------------------------*/    
83      /**
84       * Naming Context Listener.
85       */
86      public interface Listener
87      {
88          /**
89           * Called by {@link NamingContext#addBinding(Name, Object)} when adding
90           * a binding.
91           * @param ctx The context to add to.
92           * @param binding The binding to add.
93           * @return The binding to bind, or null if the binding should be ignored.
94           */
95          Binding bind(NamingContext ctx, Binding binding);
96          
97          /**
98           * @param ctx The context to unbind from
99           * @param binding The binding that was unbound.
100          */
101         void unbind(NamingContext ctx, Binding binding);
102     }
103 
104     /*------------------------------------------------*/    
105     /** NameEnumeration
106      * <p>Implementation of NamingEnumeration interface.
107      *
108      * <p><h4>Notes</h4>
109      * <p>Used for returning results of Context.list();
110      *
111      * <p><h4>Usage</h4>
112      * <pre>
113      */
114     /*
115      * </pre>
116      *
117      * @see
118      *
119      */
120     public class NameEnumeration implements NamingEnumeration<NameClassPair>
121     {
122         Iterator<Binding> _delegate;
123 
124         public NameEnumeration (Iterator<Binding> e)
125         {
126             _delegate = e;
127         }
128 
129         public void close()
130             throws NamingException
131         {
132         }
133 
134         public boolean hasMore ()
135             throws NamingException
136         {
137             return _delegate.hasNext();
138         }
139 
140         public NameClassPair next()
141             throws NamingException
142         {
143             Binding b = _delegate.next();
144             return new NameClassPair(b.getName(),b.getClassName(),true);
145         }
146 
147         public boolean hasMoreElements()
148         {
149             return _delegate.hasNext();
150         }
151 
152         public NameClassPair nextElement()
153         {
154             Binding b = _delegate.next();
155             return new NameClassPair(b.getName(),b.getClassName(),true);
156         }
157     }
158 
159 
160 
161 
162 
163 
164     /*------------------------------------------------*/    
165     /** BindingEnumeration
166      * <p>Implementation of NamingEnumeration
167      *
168      * <p><h4>Notes</h4>
169      * <p>Used to return results of Context.listBindings();
170      *
171      * <p><h4>Usage</h4>
172      * <pre>
173      */
174     /*
175      * </pre>
176      *
177      * @see
178      *
179      */
180     public class BindingEnumeration implements NamingEnumeration<Binding>
181     {       
182         Iterator<Binding> _delegate;
183 
184         public BindingEnumeration (Iterator<Binding> e)
185         {
186             _delegate = e;
187         }
188 
189         public void close()
190             throws NamingException
191         {
192         }
193 
194         public boolean hasMore ()
195             throws NamingException
196         {
197             return _delegate.hasNext();
198         }
199 
200         public Binding next()
201             throws NamingException
202         {
203             Binding b = (Binding)_delegate.next();
204             return new Binding (b.getName(), b.getClassName(), b.getObject(), true);
205         }
206 
207         public boolean hasMoreElements()
208         {
209             return _delegate.hasNext();
210         }
211 
212         public Binding nextElement()
213         {
214             Binding b = (Binding)_delegate.next();
215             return new Binding (b.getName(), b.getClassName(), b.getObject(),true);
216         }
217     }
218 
219 
220 
221     /*------------------------------------------------*/    
222     /**
223      * Constructor
224      *
225      * @param env environment properties
226      * @param name relative name of this context
227      * @param parent immediate ancestor Context (can be null)
228      * @param parser NameParser for this Context
229      */
230     public NamingContext(Hashtable<String,Object> env, 
231                          String name, 
232                          NamingContext parent, 
233                          NameParser parser) 
234     {
235         if (env != null)
236             _env.putAll(env);
237         _name = name;
238         _parent = parent;
239         _parser = parser;
240     } 
241 
242 
243     /*------------------------------------------------*/
244     /**
245      * Creates a new <code>NamingContext</code> instance.
246      *
247      * @param env a <code>Hashtable</code> value
248      */
249     public NamingContext (Hashtable<String,Object> env)
250     {
251         if (env != null)
252             _env.putAll(env);
253     }
254 
255     /*------------------------------------------------*/
256     /**
257      * Constructor
258      *
259      */
260     public NamingContext ()
261     {
262     }
263 
264 
265     /*------------------------------------------------*/
266     /**
267      * Clone this NamingContext
268      *
269      * @return copy of this NamingContext
270      * @exception CloneNotSupportedException if an error occurs
271      */
272     public Object clone ()
273         throws CloneNotSupportedException
274     {
275         NamingContext ctx = (NamingContext)super.clone();
276         ctx._env.putAll(_env);
277         ctx._bindings.putAll(_bindings);
278         return ctx;
279     }
280 
281 
282     /*------------------------------------------------*/
283     /**
284      * Getter for _name
285      *
286      * @return name of this Context (relative, not absolute)
287      */
288     public String getName ()
289     {
290         return _name;
291     }
292 
293     /*------------------------------------------------*/
294     /**
295      * Getter for _parent
296      *
297      * @return parent Context
298      */
299     public Context getParent()
300     {
301         return _parent;
302     }
303 
304     /*------------------------------------------------*/
305     /**
306      * Setter for _parser
307      *
308      * 
309      */
310     public void setNameParser (NameParser parser)
311     {
312         _parser = parser;
313     }
314 
315 
316 
317     /*------------------------------------------------*/
318     /**
319      * Bind a name to an object
320      *
321      * @param name Name of the object
322      * @param obj object to bind
323      * @exception NamingException if an error occurs
324      */
325     public void bind(Name name, Object obj) 
326         throws NamingException
327     {
328         if (isLocked())
329             throw new NamingException ("This context is immutable");
330 
331         Name cname = toCanonicalName(name);
332         
333         if (cname == null)
334             throw new NamingException ("Name is null");
335         
336         if (cname.size() == 0)
337             throw new NamingException ("Name is empty");
338 
339 
340         //if no subcontexts, just bind it
341         if (cname.size() == 1)
342         {
343             //get the object to be bound
344             Object objToBind = NamingManager.getStateToBind(obj, name,this, _env);
345             // Check for Referenceable
346             if (objToBind instanceof Referenceable) 
347             {
348                 objToBind = ((Referenceable)objToBind).getReference();
349             }
350             
351             //anything else we should be able to bind directly    
352             addBinding (cname, objToBind);
353         }
354         else
355         {
356             if(__log.isDebugEnabled())__log.debug("Checking for existing binding for name="+cname+" for first element of name="+cname.get(0));
357           
358             //walk down the subcontext hierarchy       
359             //need to ignore trailing empty "" name components
360                     
361             String firstComponent = cname.get(0);
362             Object ctx = null;
363 
364             if (firstComponent.equals(""))
365                 ctx = this;
366             else
367             {
368 
369                 Binding  binding = getBinding (firstComponent);
370                 if (binding == null)
371                     throw new NameNotFoundException (firstComponent+ " is not bound");
372                 
373                 ctx = binding.getObject();
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.iterator());
816         }
817 
818         
819         if (cname.size() == 0)
820         {
821            return new NameEnumeration (_bindings.values().iterator()); 
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.iterator());
899         }
900 
901         if (cname.size() == 0)
902         {
903            return new BindingEnumeration (_bindings.values().iterator()); 
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, _env);
996             
997             if (objToBind instanceof Referenceable)
998             {
999                 objToBind = ((Referenceable)objToBind).getReference();
1000             }
1001             removeBinding(cname);
1002             addBinding (cname, objToBind);
1003         }
1004         else
1005         { 
1006             //walk down the subcontext hierarchy
1007             if(__log.isDebugEnabled())__log.debug("Checking for existing binding for name="+cname+" for first element of name="+cname.get(0));
1008                     
1009             String firstComponent = cname.get(0);
1010             Object ctx = null;
1011 
1012             
1013             if (firstComponent.equals(""))
1014                 ctx = this;
1015             else
1016             {
1017                 Binding  binding = getBinding (name.get(0));
1018                 if (binding == null)
1019                     throw new NameNotFoundException (name.get(0)+ " is not bound");
1020             
1021                 ctx = binding.getObject();
1022 
1023 
1024                 if (ctx instanceof Reference)
1025                 {  
1026                     //deference the object
1027                     try
1028                     {
1029                         ctx = NamingManager.getObjectInstance(ctx, getNameParser("").parse(firstComponent), this, _env);
1030                     }
1031                     catch (NamingException e)
1032                     {
1033                         throw e;
1034                     }
1035                     catch (Exception e)
1036                     {
1037                         __log.warn("",e);
1038                         throw new NamingException (e.getMessage());
1039                     }
1040                 }
1041             }
1042 
1043             if (ctx instanceof Context)
1044             {
1045                 ((Context)ctx).rebind (cname.getSuffix(1), obj);
1046             }
1047             else
1048                 throw new NotContextException ("Object bound at "+firstComponent +" is not a Context");
1049         }
1050     }
1051 
1052 
1053     /*------------------------------------------------*/
1054     /**
1055      * Overwrite or create a binding from Name to Object
1056      *
1057      * @param name a <code>String</code> value
1058      * @param obj an <code>Object</code> value
1059      * @exception NamingException if an error occurs
1060      */
1061     public void rebind (String name,
1062                         Object obj)
1063         throws NamingException
1064     {
1065         rebind (_parser.parse(name), obj);
1066     }
1067 
1068     /*------------------------------------------------*/
1069     /**
1070      * Not supported.
1071      *
1072      * @param name a <code>String</code> value
1073      * @exception NamingException if an error occurs
1074      */
1075     public void unbind (String name)
1076         throws NamingException
1077     {
1078         unbind(_parser.parse(name));
1079     }
1080 
1081     /*------------------------------------------------*/
1082     /**
1083      * Not supported.
1084      *
1085      * @param name a <code>String</code> value
1086      * @exception NamingException if an error occurs
1087      */
1088     public void unbind (Name name)
1089         throws NamingException
1090     {
1091         if (name.size() == 0)
1092             return;
1093         
1094         
1095         if (isLocked())
1096             throw new NamingException ("This context is immutable");
1097 
1098         Name cname = toCanonicalName(name);
1099 
1100         if (cname == null)
1101             throw new NamingException ("Name is null");
1102         
1103         if (cname.size() == 0)
1104             throw new NamingException ("Name is empty");
1105 
1106 
1107         //if no subcontexts, just unbind it
1108         if (cname.size() == 1)
1109         {         
1110             removeBinding (cname);
1111         }
1112         else
1113         { 
1114             //walk down the subcontext hierarchy
1115             if(__log.isDebugEnabled())__log.debug("Checking for existing binding for name="+cname+" for first element of name="+cname.get(0));
1116                     
1117             String firstComponent = cname.get(0);
1118             Object ctx = null;
1119 
1120             
1121             if (firstComponent.equals(""))
1122                 ctx = this;
1123             else
1124             {
1125                 Binding  binding = getBinding (name.get(0));
1126                 if (binding == null)
1127                     throw new NameNotFoundException (name.get(0)+ " is not bound");
1128             
1129                 ctx = binding.getObject();
1130 
1131 
1132                 if (ctx instanceof Reference)
1133                 {  
1134                     //deference the object
1135                     try
1136                     {
1137                         ctx = NamingManager.getObjectInstance(ctx, getNameParser("").parse(firstComponent), this, _env);
1138                     }
1139                     catch (NamingException e)
1140                     {
1141                         throw e;
1142                     }
1143                     catch (Exception e)
1144                     {
1145                         __log.warn("",e);
1146                         throw new NamingException (e.getMessage());
1147                     }
1148                 }
1149             }
1150 
1151             if (ctx instanceof Context)
1152             {
1153                 ((Context)ctx).unbind (cname.getSuffix(1));
1154             }
1155             else
1156                 throw new NotContextException ("Object bound at "+firstComponent +" is not a Context");
1157         }
1158         
1159     }
1160 
1161     /*------------------------------------------------*/
1162     /**
1163      * Not supported
1164      *
1165      * @param oldName a <code>Name</code> value
1166      * @param newName a <code>Name</code> value
1167      * @exception NamingException if an error occurs
1168      */
1169     public void rename(Name oldName,
1170                        Name newName)
1171         throws NamingException
1172     {
1173         throw new OperationNotSupportedException();
1174     }
1175 
1176     
1177     /*------------------------------------------------*/
1178     /**
1179      * Not supported
1180      *
1181      * @param oldName a <code>Name</code> value
1182      * @param newName a <code>Name</code> value
1183      * @exception NamingException if an error occurs
1184      */    public void rename(String oldName,
1185                        String newName)
1186         throws NamingException
1187     {
1188         throw new OperationNotSupportedException();
1189     }
1190 
1191 
1192 
1193     /*------------------------------------------------*/
1194     /** Join two names together. These are treated as
1195      * CompoundNames.
1196      *
1197      * @param name a <code>Name</code> value
1198      * @param prefix a <code>Name</code> value
1199      * @return a <code>Name</code> value
1200      * @exception NamingException if an error occurs
1201      */
1202     public Name composeName(Name name,
1203                             Name prefix)
1204         throws NamingException
1205     {
1206         if (name == null)
1207             throw new NamingException ("Name cannot be null");
1208         if (prefix == null)
1209             throw new NamingException ("Prefix cannot be null");
1210 
1211         Name compoundName = (CompoundName)prefix.clone();
1212         compoundName.addAll (name);
1213         return compoundName;
1214     }
1215 
1216 
1217 
1218     /*------------------------------------------------*/    
1219     /** Join two names together. These are treated as
1220      * CompoundNames.
1221      *
1222      * @param name a <code>Name</code> value
1223      * @param prefix a <code>Name</code> value
1224      * @return a <code>Name</code> value
1225      * @exception NamingException if an error occurs
1226      */
1227     public String composeName (String name,
1228                                String prefix)
1229         throws NamingException
1230     {       
1231         if (name == null)
1232             throw new NamingException ("Name cannot be null");
1233         if (prefix == null)
1234             throw new NamingException ("Prefix cannot be null");
1235 
1236         Name compoundName = _parser.parse(prefix);
1237         compoundName.add (name);
1238         return compoundName.toString();
1239     }
1240 
1241 
1242     /*------------------------------------------------*/    
1243     /**
1244      * Do nothing
1245      *
1246      * @exception NamingException if an error occurs
1247      */
1248     public void close ()
1249         throws NamingException
1250     {
1251         
1252         
1253     }
1254 
1255 
1256     /*------------------------------------------------*/    
1257     /**
1258      * Return a NameParser for this Context.
1259      *
1260      * @param name a <code>Name</code> value
1261      * @return a <code>NameParser</code> value
1262      */
1263     public NameParser getNameParser (Name name)
1264     {
1265         return _parser;
1266     }
1267 
1268     /*------------------------------------------------*/    
1269     /**
1270      * Return a NameParser for this Context.
1271      *
1272      * @param name a <code>Name</code> value
1273      * @return a <code>NameParser</code> value
1274      */    
1275     public NameParser getNameParser (String name)
1276     {
1277         return _parser;
1278     }
1279     
1280 
1281     /*------------------------------------------------*/    
1282     /**
1283      * Get the full name of this Context node
1284      * by visiting it's ancestors back to root.
1285      *
1286      * NOTE: if this Context has a URL namespace then
1287      * the URL prefix will be missing
1288      *
1289      * @return the full name of this Context
1290      * @exception NamingException if an error occurs
1291      */
1292     public String getNameInNamespace ()
1293         throws NamingException
1294     {
1295         Name name = _parser.parse("");
1296 
1297         NamingContext c = this;
1298         while (c != null)
1299         {
1300             String str = c.getName();
1301             if (str != null)
1302                 name.add(0, str);
1303             c = (NamingContext)c.getParent();
1304         }
1305         return name.toString();
1306     }
1307 
1308 
1309     /*------------------------------------------------*/    
1310     /**
1311      * Add an environment setting to this Context
1312      *
1313      * @param propName name of the property to add
1314      * @param propVal value of the property to add
1315      * @return propVal or previous value of the property
1316      * @exception NamingException if an error occurs
1317      */
1318     public Object addToEnvironment(String propName,
1319                                    Object propVal)
1320         throws NamingException
1321     {
1322         if (isLocked() && !(propName.equals(UNLOCK_PROPERTY)))
1323             throw new NamingException ("This context is immutable");
1324         
1325         return _env.put (propName, propVal);
1326     }
1327 
1328 
1329     /*------------------------------------------------*/    
1330     /**
1331      * Remove a property from this Context's environment.
1332      *
1333      * @param propName name of property to remove
1334      * @return value of property or null if it didn't exist
1335      * @exception NamingException if an error occurs
1336      */
1337     public Object removeFromEnvironment(String propName)
1338         throws NamingException
1339     {
1340         if (isLocked())
1341             throw new NamingException ("This context is immutable");
1342         
1343         return _env.remove (propName);
1344     }
1345 
1346 
1347     /*------------------------------------------------*/    
1348     /**
1349      * Get the environment of this Context.
1350      *
1351      * @return a copy of the environment of this Context.
1352      */
1353     public Hashtable getEnvironment ()
1354     {
1355         return (Hashtable)_env.clone();
1356     }
1357 
1358     /*------------------------------------------------*/    
1359     /**
1360      * Add a name to object binding to this Context.
1361      *
1362      * @param name a <code>Name</code> value
1363      * @param obj an <code>Object</code> value
1364      */
1365     protected void addBinding (Name name, Object obj) throws NameAlreadyBoundException
1366     {
1367         String key = name.toString();
1368         Binding binding=new Binding (key, obj);
1369         
1370         Collection<Listener> list = findListeners();
1371         
1372         for (Listener listener : list)
1373         {
1374             binding=listener.bind(this,binding);
1375             if (binding==null)
1376                 break;
1377         }
1378 
1379         if(__log.isDebugEnabled())
1380             __log.debug("Adding binding with key="+key+" obj="+obj+" for context="+_name+" as "+binding);
1381         
1382         if (binding!=null)
1383         {
1384             if (_bindings.containsKey(key))
1385                 throw new NameAlreadyBoundException(name.toString());
1386             _bindings.put(key,binding);  
1387         }
1388     }
1389 
1390     /*------------------------------------------------*/    
1391     /**
1392      * Get a name to object binding from this Context
1393      *
1394      * @param name a <code>Name</code> value
1395      * @return a <code>Binding</code> value
1396      */
1397     protected Binding getBinding (Name name)
1398     {
1399         return (Binding) _bindings.get(name.toString());
1400     }
1401 
1402 
1403     /*------------------------------------------------*/    
1404     /**
1405      * Get a name to object binding from this Context
1406      *
1407      * @param name as a String
1408      * @return null or the Binding
1409      */
1410     protected Binding getBinding (String name)
1411     {
1412         return (Binding) _bindings.get(name);
1413     }
1414 
1415     /*------------------------------------------------*/    
1416     protected void removeBinding (Name name)
1417     {
1418         String key = name.toString();
1419         if (__log.isDebugEnabled()) 
1420             __log.debug("Removing binding with key="+key);
1421         Binding binding = _bindings.remove(key);
1422         if (binding!=null)
1423         {
1424             Collection<Listener> list = findListeners();
1425             for (Listener listener : list)
1426                 listener.unbind(this,binding);
1427         }
1428     }
1429 
1430     /*------------------------------------------------*/    
1431     /**
1432      * Remove leading or trailing empty components from
1433      * name. Eg "/comp/env/" -> "comp/env"
1434      *
1435      * @param name the name to normalize
1436      * @return normalized name
1437      */
1438     public Name toCanonicalName (Name name)
1439     {
1440         Name canonicalName = name;
1441 
1442         if (name != null)
1443         {
1444             if (canonicalName.size() > 1)
1445             {
1446                 if (canonicalName.get(0).equals(""))
1447                     canonicalName = canonicalName.getSuffix(1);
1448  
1449                 if (canonicalName.get(canonicalName.size()-1).equals(""))
1450                     canonicalName = canonicalName.getPrefix(canonicalName.size()-1);               
1451             }
1452         }
1453 
1454         return canonicalName;
1455     }
1456     
1457     /* ------------------------------------------------------------ */
1458     private boolean isLocked()
1459     {
1460        if ((_env.get(LOCK_PROPERTY) == null) && (_env.get(UNLOCK_PROPERTY) == null))
1461            return false;
1462        
1463        Object lockKey = _env.get(LOCK_PROPERTY);
1464        Object unlockKey = _env.get(UNLOCK_PROPERTY);
1465        
1466        if ((lockKey != null) && (unlockKey != null) && (lockKey.equals(unlockKey)))
1467            return false;
1468        return true;
1469     }
1470     
1471     
1472     /* ------------------------------------------------------------ */
1473     public String dump()
1474     {
1475         StringBuilder buf = new StringBuilder();
1476         try
1477         {
1478             dump(buf,"");
1479         }
1480         catch(Exception e)
1481         {
1482             __log.warn(e);
1483         }
1484         return buf.toString();
1485     }
1486     
1487 
1488     /* ------------------------------------------------------------ */
1489     public void dump(Appendable out,String indent) throws IOException
1490     {
1491         out.append(this.getClass().getSimpleName()).append("@").append(Long.toHexString(this.hashCode())).append("\n");
1492         int size=_bindings.size();
1493         int i=0;
1494         for (Map.Entry<String,Binding> entry : ((Map<String,Binding>)_bindings).entrySet())
1495         {
1496             boolean last=++i==size;
1497             out.append(indent).append(" +- ").append(entry.getKey()).append(": ");
1498             
1499             Binding binding = entry.getValue();
1500             Object value = binding.getObject();
1501             
1502             if ("comp".equals(entry.getKey()) && value instanceof Reference && "org.eclipse.jetty.jndi.ContextFactory".equals(((Reference)value).getFactoryClassName()))
1503             {
1504                 ContextFactory.dump(out,indent+(last?"    ":" |  "));
1505             }
1506             else if (value instanceof Dumpable)
1507             {
1508                 ((Dumpable)value).dump(out,indent+(last?"    ":" |  "));
1509             }
1510             else
1511             {
1512                 out.append(value.getClass().getSimpleName()).append("=");
1513                 out.append(String.valueOf(value).replace('\n','|').replace('\r','|'));
1514                 out.append("\n");
1515             }
1516         }
1517     }
1518   
1519     private Collection<Listener> findListeners()
1520     {
1521         Collection<Listener> list = new ArrayList<Listener>();
1522         NamingContext ctx=this;
1523         while (ctx!=null)
1524         {
1525             if (ctx._listeners!=null)
1526                 list.addAll(ctx._listeners);
1527             ctx=(NamingContext)ctx.getParent();
1528         }
1529         return list;
1530     }
1531     
1532     public void addListener(Listener listener)
1533     {
1534         if (_listeners==null)
1535             _listeners=new ArrayList<Listener>();
1536         _listeners.add(listener);
1537     }
1538     
1539     public boolean removeListener(Listener listener)
1540     {   
1541         return _listeners.remove(listener);
1542     }
1543 }