View Javadoc

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