View Javadoc

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