View Javadoc

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