1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17
18
19 package org.apache.naming;
20
21 import java.util.Enumeration;
22 import java.util.HashMap;
23 import java.util.Hashtable;
24
25 import javax.naming.Binding;
26 import javax.naming.CompositeName;
27 import javax.naming.Context;
28 import javax.naming.InitialContext;
29 import javax.naming.LinkRef;
30 import javax.naming.Name;
31 import javax.naming.NameAlreadyBoundException;
32 import javax.naming.NameClassPair;
33 import javax.naming.NameNotFoundException;
34 import javax.naming.NameParser;
35 import javax.naming.NamingEnumeration;
36 import javax.naming.NamingException;
37 import javax.naming.NotContextException;
38 import javax.naming.OperationNotSupportedException;
39 import javax.naming.Reference;
40 import javax.naming.Referenceable;
41 import javax.naming.spi.NamingManager;
42 import javax.naming.spi.ObjectFactory;
43
44 import org.apache.juli.logging.Log;
45 import org.apache.juli.logging.LogFactory;
46
47 /**
48  * Catalina JNDI Context implementation.
49  *
50  * @author Remy Maucherat
51  */

52 public class NamingContext implements Context {
53
54
55     // -------------------------------------------------------------- Constants
56
57
58     /**
59      * Name parser for this context.
60      */

61     protected static final NameParser nameParser = new NameParserImpl();
62
63
64     private static final Log log = LogFactory.getLog(NamingContext.class);
65
66
67     // ----------------------------------------------------------- Constructors
68
69
70     /**
71      * Builds a naming context.
72      *
73      * @param env The environment to use to construct the naming context
74      * @param name The name of the associated Catalina Context
75      */

76     public NamingContext(Hashtable<String,Object> env, String name) {
77         this(env, name, new HashMap<String,NamingEntry>());
78     }
79
80
81     /**
82      * Builds a naming context.
83      *
84      * @param env The environment to use to construct the naming context
85      * @param name The name of the associated Catalina Context
86      * @param bindings The initial bindings for the naming context
87      */

88     public NamingContext(Hashtable<String,Object> env, String name,
89             HashMap<String,NamingEntry> bindings) {
90
91         this.env = new Hashtable<>();
92         this.name = name;
93         // Populating the environment hashtable
94         if (env != null ) {
95             Enumeration<String> envEntries = env.keys();
96             while (envEntries.hasMoreElements()) {
97                 String entryName = envEntries.nextElement();
98                 addToEnvironment(entryName, env.get(entryName));
99             }
100         }
101         this.bindings = bindings;
102     }
103
104
105     // ----------------------------------------------------- Instance Variables
106
107
108     /**
109      * Environment.
110      */

111     protected final Hashtable<String,Object> env;
112
113
114     /**
115      * The string manager for this package.
116      */

117     protected static final StringManager sm = StringManager.getManager(NamingContext.class);
118
119
120     /**
121      * Bindings in this Context.
122      */

123     protected final HashMap<String,NamingEntry> bindings;
124
125
126     /**
127      * Name of the associated Catalina Context.
128      */

129     protected final String name;
130
131
132     /**
133      * Determines if an attempt to write to a read-only context results in an
134      * exception or if the request is ignored.
135      */

136     private boolean exceptionOnFailedWrite = true;
137     public boolean getExceptionOnFailedWrite() {
138         return exceptionOnFailedWrite;
139     }
140     public void setExceptionOnFailedWrite(boolean exceptionOnFailedWrite) {
141         this.exceptionOnFailedWrite = exceptionOnFailedWrite;
142     }
143
144
145     // -------------------------------------------------------- Context Methods
146
147     /**
148      * Retrieves the named object. If name is empty, returns a new instance
149      * of this context (which represents the same naming context as this
150      * context, but its environment may be modified independently and it may
151      * be accessed concurrently).
152      *
153      * @param name the name of the object to look up
154      * @return the object bound to name
155      * @exception NamingException if a naming exception is encountered
156      */

157     @Override
158     public Object lookup(Name name)
159         throws NamingException {
160         return lookup(name, true);
161     }
162
163
164     /**
165      * Retrieves the named object.
166      *
167      * @param name the name of the object to look up
168      * @return the object bound to name
169      * @exception NamingException if a naming exception is encountered
170      */

171     @Override
172     public Object lookup(String name)
173         throws NamingException {
174         return lookup(new CompositeName(name), true);
175     }
176
177
178     /**
179      * Binds a name to an object. All intermediate contexts and the target
180      * context (that named by all but terminal atomic component of the name)
181      * must already exist.
182      *
183      * @param name the name to bind; may not be empty
184      * @param obj the object to bind; possibly null
185      * @exception NameAlreadyBoundException if name is already bound
186      * @exception javax.naming.directory.InvalidAttributesException if object
187      * did not supply all mandatory attributes
188      * @exception NamingException if a naming exception is encountered
189      */

190     @Override
191     public void bind(Name name, Object obj)
192         throws NamingException {
193         bind(name, obj, false);
194     }
195
196
197     /**
198      * Binds a name to an object.
199      *
200      * @param name the name to bind; may not be empty
201      * @param obj the object to bind; possibly null
202      * @exception NameAlreadyBoundException if name is already bound
203      * @exception javax.naming.directory.InvalidAttributesException if object
204      * did not supply all mandatory attributes
205      * @exception NamingException if a naming exception is encountered
206      */

207     @Override
208     public void bind(String name, Object obj)
209         throws NamingException {
210         bind(new CompositeName(name), obj);
211     }
212
213
214     /**
215      * Binds a name to an object, overwriting any existing binding. All
216      * intermediate contexts and the target context (that named by all but
217      * terminal atomic component of the name) must already exist.
218      * <p>
219      * If the object is a DirContext, any existing attributes associated with
220      * the name are replaced with those of the object. Otherwise, any
221      * existing attributes associated with the name remain unchanged.
222      *
223      * @param name the name to bind; may not be empty
224      * @param obj the object to bind; possibly null
225      * @exception javax.naming.directory.InvalidAttributesException if object
226      * did not supply all mandatory attributes
227      * @exception NamingException if a naming exception is encountered
228      */

229     @Override
230     public void rebind(Name name, Object obj)
231         throws NamingException {
232         bind(name, obj, true);
233     }
234
235
236     /**
237      * Binds a name to an object, overwriting any existing binding.
238      *
239      * @param name the name to bind; may not be empty
240      * @param obj the object to bind; possibly null
241      * @exception javax.naming.directory.InvalidAttributesException if object
242      * did not supply all mandatory attributes
243      * @exception NamingException if a naming exception is encountered
244      */

245     @Override
246     public void rebind(String name, Object obj)
247         throws NamingException {
248         rebind(new CompositeName(name), obj);
249     }
250
251
252     /**
253      * Unbinds the named object. Removes the terminal atomic name in name
254      * from the target context--that named by all but the terminal atomic
255      * part of name.
256      * <p>
257      * This method is idempotent. It succeeds even if the terminal atomic
258      * name is not bound in the target context, but throws
259      * NameNotFoundException if any of the intermediate contexts do not exist.
260      *
261      * @param name the name to bind; may not be empty
262      * @exception NameNotFoundException if an intermediate context does not
263      * exist
264      * @exception NamingException if a naming exception is encountered
265      */

266     @Override
267     public void unbind(Name name) throws NamingException {
268
269         if (!checkWritable()) {
270             return;
271         }
272
273         while ((!name.isEmpty()) && (name.get(0).length() == 0))
274             name = name.getSuffix(1);
275         if (name.isEmpty())
276             throw new NamingException
277                 (sm.getString("namingContext.invalidName"));
278
279         NamingEntry entry = bindings.get(name.get(0));
280
281         if (entry == null) {
282             throw new NameNotFoundException
283                 (sm.getString("namingContext.nameNotBound", name, name.get(0)));
284         }
285
286         if (name.size() > 1) {
287             if (entry.type == NamingEntry.CONTEXT) {
288                 ((Context) entry.value).unbind(name.getSuffix(1));
289             } else {
290                 throw new NamingException
291                     (sm.getString("namingContext.contextExpected"));
292             }
293         } else {
294             bindings.remove(name.get(0));
295         }
296
297     }
298
299
300     /**
301      * Unbinds the named object.
302      *
303      * @param name the name to bind; may not be empty
304      * @exception NameNotFoundException if an intermediate context does not
305      * exist
306      * @exception NamingException if a naming exception is encountered
307      */

308     @Override
309     public void unbind(String name)
310         throws NamingException {
311         unbind(new CompositeName(name));
312     }
313
314
315     /**
316      * Binds a new name to the object bound to an old name, and unbinds the
317      * old name. Both names are relative to this context. Any attributes
318      * associated with the old name become associated with the new name.
319      * Intermediate contexts of the old name are not changed.
320      *
321      * @param oldName the name of the existing binding; may not be empty
322      * @param newName the name of the new binding; may not be empty
323      * @exception NameAlreadyBoundException if newName is already bound
324      * @exception NamingException if a naming exception is encountered
325      */

326     @Override
327     public void rename(Name oldName, Name newName)
328         throws NamingException {
329         Object value = lookup(oldName);
330         bind(newName, value);
331         unbind(oldName);
332     }
333
334
335     /**
336      * Binds a new name to the object bound to an old name, and unbinds the
337      * old name.
338      *
339      * @param oldName the name of the existing binding; may not be empty
340      * @param newName the name of the new binding; may not be empty
341      * @exception NameAlreadyBoundException if newName is already bound
342      * @exception NamingException if a naming exception is encountered
343      */

344     @Override
345     public void rename(String oldName, String newName)
346         throws NamingException {
347         rename(new CompositeName(oldName), new CompositeName(newName));
348     }
349
350
351     /**
352      * Enumerates the names bound in the named context, along with the class
353      * names of objects bound to them. The contents of any subcontexts are
354      * not included.
355      * <p>
356      * If a binding is added to or removed from this context, its effect on
357      * an enumeration previously returned is undefined.
358      *
359      * @param name the name of the context to list
360      * @return an enumeration of the names and class names of the bindings in
361      * this context. Each element of the enumeration is of type NameClassPair.
362      * @exception NamingException if a naming exception is encountered
363      */

364     @Override
365     public NamingEnumeration<NameClassPair> list(Name name)
366         throws NamingException {
367         // Removing empty parts
368         while ((!name.isEmpty()) && (name.get(0).length() == 0))
369             name = name.getSuffix(1);
370         if (name.isEmpty()) {
371             return new NamingContextEnumeration(bindings.values().iterator());
372         }
373
374         NamingEntry entry = bindings.get(name.get(0));
375
376         if (entry == null) {
377             throw new NameNotFoundException
378                 (sm.getString("namingContext.nameNotBound", name, name.get(0)));
379         }
380
381         if (entry.type != NamingEntry.CONTEXT) {
382             throw new NamingException
383                 (sm.getString("namingContext.contextExpected"));
384         }
385         return ((Context) entry.value).list(name.getSuffix(1));
386     }
387
388
389     /**
390      * Enumerates the names bound in the named context, along with the class
391      * names of objects bound to them.
392      *
393      * @param name the name of the context to list
394      * @return an enumeration of the names and class names of the bindings in
395      * this context. Each element of the enumeration is of type NameClassPair.
396      * @exception NamingException if a naming exception is encountered
397      */

398     @Override
399     public NamingEnumeration<NameClassPair> list(String name)
400         throws NamingException {
401         return list(new CompositeName(name));
402     }
403
404
405     /**
406      * Enumerates the names bound in the named context, along with the
407      * objects bound to them. The contents of any subcontexts are not
408      * included.
409      * <p>
410      * If a binding is added to or removed from this context, its effect on
411      * an enumeration previously returned is undefined.
412      *
413      * @param name the name of the context to list
414      * @return an enumeration of the bindings in this context.
415      * Each element of the enumeration is of type Binding.
416      * @exception NamingException if a naming exception is encountered
417      */

418     @Override
419     public NamingEnumeration<Binding> listBindings(Name name)
420         throws NamingException {
421         // Removing empty parts
422         while ((!name.isEmpty()) && (name.get(0).length() == 0))
423             name = name.getSuffix(1);
424         if (name.isEmpty()) {
425             return new NamingContextBindingsEnumeration(bindings.values().iterator(), this);
426         }
427
428         NamingEntry entry = bindings.get(name.get(0));
429
430         if (entry == null) {
431             throw new NameNotFoundException
432                 (sm.getString("namingContext.nameNotBound", name, name.get(0)));
433         }
434
435         if (entry.type != NamingEntry.CONTEXT) {
436             throw new NamingException
437                 (sm.getString("namingContext.contextExpected"));
438         }
439         return ((Context) entry.value).listBindings(name.getSuffix(1));
440     }
441
442
443     /**
444      * Enumerates the names bound in the named context, along with the
445      * objects bound to them.
446      *
447      * @param name the name of the context to list
448      * @return an enumeration of the bindings in this context.
449      * Each element of the enumeration is of type Binding.
450      * @exception NamingException if a naming exception is encountered
451      */

452     @Override
453     public NamingEnumeration<Binding> listBindings(String name)
454         throws NamingException {
455         return listBindings(new CompositeName(name));
456     }
457
458
459     /**
460      * Destroys the named context and removes it from the namespace. Any
461      * attributes associated with the name are also removed. Intermediate
462      * contexts are not destroyed.
463      * <p>
464      * This method is idempotent. It succeeds even if the terminal atomic
465      * name is not bound in the target context, but throws
466      * NameNotFoundException if any of the intermediate contexts do not exist.
467      *
468      * In a federated naming system, a context from one naming system may be
469      * bound to a name in another. One can subsequently look up and perform
470      * operations on the foreign context using a composite name. However, an
471      * attempt destroy the context using this composite name will fail with
472      * NotContextException, because the foreign context is not a "subcontext"
473      * of the context in which it is bound. Instead, use unbind() to remove
474      * the binding of the foreign context. Destroying the foreign context
475      * requires that the destroySubcontext() be performed on a context from
476      * the foreign context's "native" naming system.
477      *
478      * @param name the name of the context to be destroyed; may not be empty
479      * @exception NameNotFoundException if an intermediate context does not
480      * exist
481      * @exception NotContextException if the name is bound but does not name
482      * a context, or does not name a context of the appropriate type
483      */

484     @Override
485     public void destroySubcontext(Name name) throws NamingException {
486
487         if (!checkWritable()) {
488             return;
489         }
490
491         while ((!name.isEmpty()) && (name.get(0).length() == 0))
492             name = name.getSuffix(1);
493         if (name.isEmpty())
494             throw new NamingException
495                 (sm.getString("namingContext.invalidName"));
496
497         NamingEntry entry = bindings.get(name.get(0));
498
499         if (entry == null) {
500             throw new NameNotFoundException
501                 (sm.getString("namingContext.nameNotBound", name, name.get(0)));
502         }
503
504         if (name.size() > 1) {
505             if (entry.type == NamingEntry.CONTEXT) {
506                 ((Context) entry.value).destroySubcontext(name.getSuffix(1));
507             } else {
508                 throw new NamingException
509                     (sm.getString("namingContext.contextExpected"));
510             }
511         } else {
512             if (entry.type == NamingEntry.CONTEXT) {
513                 ((Context) entry.value).close();
514                 bindings.remove(name.get(0));
515             } else {
516                 throw new NotContextException
517                     (sm.getString("namingContext.contextExpected"));
518             }
519         }
520
521     }
522
523
524     /**
525      * Destroys the named context and removes it from the namespace.
526      *
527      * @param name the name of the context to be destroyed; may not be empty
528      * @exception NameNotFoundException if an intermediate context does not
529      * exist
530      * @exception NotContextException if the name is bound but does not name
531      * a context, or does not name a context of the appropriate type
532      */

533     @Override
534     public void destroySubcontext(String name)
535         throws NamingException {
536         destroySubcontext(new CompositeName(name));
537     }
538
539
540     /**
541      * Creates and binds a new context. Creates a new context with the given
542      * name and binds it in the target context (that named by all but
543      * terminal atomic component of the name). All intermediate contexts and
544      * the target context must already exist.
545      *
546      * @param name the name of the context to create; may not be empty
547      * @return the newly created context
548      * @exception NameAlreadyBoundException if name is already bound
549      * @exception javax.naming.directory.InvalidAttributesException if creation
550      * of the sub-context requires specification of mandatory attributes
551      * @exception NamingException if a naming exception is encountered
552      */

553     @Override
554     public Context createSubcontext(Name name) throws NamingException {
555         if (!checkWritable()) {
556             return null;
557         }
558
559         NamingContext newContext = new NamingContext(env, this.name);
560         bind(name, newContext);
561
562         newContext.setExceptionOnFailedWrite(getExceptionOnFailedWrite());
563
564         return newContext;
565     }
566
567
568     /**
569      * Creates and binds a new context.
570      *
571      * @param name the name of the context to create; may not be empty
572      * @return the newly created context
573      * @exception NameAlreadyBoundException if name is already bound
574      * @exception javax.naming.directory.InvalidAttributesException if creation
575      * of the sub-context requires specification of mandatory attributes
576      * @exception NamingException if a naming exception is encountered
577      */

578     @Override
579     public Context createSubcontext(String name)
580         throws NamingException {
581         return createSubcontext(new CompositeName(name));
582     }
583
584
585     /**
586      * Retrieves the named object, following links except for the terminal
587      * atomic component of the name. If the object bound to name is not a
588      * link, returns the object itself.
589      *
590      * @param name the name of the object to look up
591      * @return the object bound to name, not following the terminal link
592      * (if any).
593      * @exception NamingException if a naming exception is encountered
594      */

595     @Override
596     public Object lookupLink(Name name)
597         throws NamingException {
598         return lookup(name, false);
599     }
600
601
602     /**
603      * Retrieves the named object, following links except for the terminal
604      * atomic component of the name.
605      *
606      * @param name the name of the object to look up
607      * @return the object bound to name, not following the terminal link
608      * (if any).
609      * @exception NamingException if a naming exception is encountered
610      */

611     @Override
612     public Object lookupLink(String name)
613         throws NamingException {
614         return lookup(new CompositeName(name), false);
615     }
616
617
618     /**
619      * Retrieves the parser associated with the named context. In a
620      * federation of namespaces, different naming systems will parse names
621      * differently. This method allows an application to get a parser for
622      * parsing names into their atomic components using the naming convention
623      * of a particular naming system. Within any single naming system,
624      * NameParser objects returned by this method must be equal (using the
625      * equals() test).
626      *
627      * @param name the name of the context from which to get the parser
628      * @return a name parser that can parse compound names into their atomic
629      * components
630      * @exception NamingException if a naming exception is encountered
631      */

632     @Override
633     public NameParser getNameParser(Name name)
634         throws NamingException {
635
636         while ((!name.isEmpty()) && (name.get(0).length() == 0))
637             name = name.getSuffix(1);
638         if (name.isEmpty())
639             return nameParser;
640
641         if (name.size() > 1) {
642             Object obj = bindings.get(name.get(0));
643             if (obj instanceof Context) {
644                 return ((Context) obj).getNameParser(name.getSuffix(1));
645             } else {
646                 throw new NotContextException
647                     (sm.getString("namingContext.contextExpected"));
648             }
649         }
650
651         return nameParser;
652
653     }
654
655
656     /**
657      * Retrieves the parser associated with the named context.
658      *
659      * @param name the name of the context from which to get the parser
660      * @return a name parser that can parse compound names into their atomic
661      * components
662      * @exception NamingException if a naming exception is encountered
663      */

664     @Override
665     public NameParser getNameParser(String name)
666         throws NamingException {
667         return getNameParser(new CompositeName(name));
668     }
669
670
671     /**
672      * Composes the name of this context with a name relative to this context.
673      * <p>
674      * Given a name (name) relative to this context, and the name (prefix)
675      * of this context relative to one of its ancestors, this method returns
676      * the composition of the two names using the syntax appropriate for the
677      * naming system(s) involved. That is, if name names an object relative
678      * to this context, the result is the name of the same object, but
679      * relative to the ancestor context. None of the names may be null.
680      *
681      * @param name a name relative to this context
682      * @param prefix the name of this context relative to one of its ancestors
683      * @return the composition of prefix and name
684      * @exception NamingException if a naming exception is encountered
685      */

686     @Override
687     public Name composeName(Name name, Name prefix) throws NamingException {
688         prefix = (Name) prefix.clone();
689         return prefix.addAll(name);
690     }
691
692
693     /**
694      * Composes the name of this context with a name relative to this context.
695      *
696      * @param name a name relative to this context
697      * @param prefix the name of this context relative to one of its ancestors
698      * @return the composition of prefix and name
699      */

700     @Override
701     public String composeName(String name, String prefix) {
702         return prefix + "/" + name;
703     }
704
705
706     /**
707      * Adds a new environment property to the environment of this context. If
708      * the property already exists, its value is overwritten.
709      *
710      * @param propName the name of the environment property to add; may not
711      * be null
712      * @param propVal the value of the property to add; may not be null
713      */

714     @Override
715     public Object addToEnvironment(String propName, Object propVal) {
716         return env.put(propName, propVal);
717     }
718
719
720     /**
721      * Removes an environment property from the environment of this context.
722      *
723      * @param propName the name of the environment property to remove;
724      * may not be null
725      */

726     @Override
727     public Object removeFromEnvironment(String propName){
728         return env.remove(propName);
729     }
730
731
732     /**
733      * Retrieves the environment in effect for this context. See class
734      * description for more details on environment properties.
735      * The caller should not make any changes to the object returned: their
736      * effect on the context is undefined. The environment of this context
737      * may be changed using addToEnvironment() and removeFromEnvironment().
738      *
739      * @return the environment of this context; never null
740      */

741     @Override
742     public Hashtable<?,?> getEnvironment() {
743         return env;
744     }
745
746
747     /**
748      * Closes this context. This method releases this context's resources
749      * immediately, instead of waiting for them to be released automatically
750      * by the garbage collector.
751      * This method is idempotent: invoking it on a context that has already
752      * been closed has no effect. Invoking any other method on a closed
753      * context is not allowed, and results in undefined behaviour.
754      *
755      * @exception NamingException if a naming exception is encountered
756      */

757     @Override
758     public void close() throws NamingException {
759         if (!checkWritable()) {
760             return;
761         }
762         env.clear();
763     }
764
765
766     /**
767      * Retrieves the full name of this context within its own namespace.
768      * <p>
769      * Many naming services have a notion of a "full name" for objects in
770      * their respective namespaces. For example, an LDAP entry has a
771      * distinguished name, and a DNS record has a fully qualified name. This
772      * method allows the client application to retrieve this name. The string
773      * returned by this method is not a JNDI composite name and should not be
774      * passed directly to context methods. In naming systems for which the
775      * notion of full name does not make sense,
776      * OperationNotSupportedException is thrown.
777      *
778      * @return this context's name in its own namespace; never null
779      * @exception OperationNotSupportedException if the naming system does
780      * not have the notion of a full name
781      * @exception NamingException if a naming exception is encountered
782      */

783     @Override
784     public String getNameInNamespace()
785         throws NamingException {
786         throw  new OperationNotSupportedException
787             (sm.getString("namingContext.noAbsoluteName"));
788         //FIXME ?
789     }
790
791
792     // ------------------------------------------------------ Protected Methods
793
794
795     private static final boolean GRAAL;
796
797     static {
798         boolean result = false;
799         try {
800             Class<?> nativeImageClazz = Class.forName("org.graalvm.nativeimage.ImageInfo");
801             result = Boolean.TRUE.equals(nativeImageClazz.getMethod("inImageCode").invoke(null));
802         } catch (ClassNotFoundException e) {
803             // Must be Graal
804         } catch (ReflectiveOperationException | IllegalArgumentException e) {
805             // Should never happen
806         }
807         GRAAL = result;
808     }
809
810     /**
811      * Retrieves the named object.
812      *
813      * @param name the name of the object to look up
814      * @param resolveLinks If true, the links will be resolved
815      * @return the object bound to name
816      * @exception NamingException if a naming exception is encountered
817      */

818     protected Object lookup(Name name, boolean resolveLinks)
819         throws NamingException {
820
821         // Removing empty parts
822         while ((!name.isEmpty()) && (name.get(0).length() == 0))
823             name = name.getSuffix(1);
824         if (name.isEmpty()) {
825             // If name is empty, a newly allocated naming context is returned
826             return new NamingContext(env, this.name, bindings);
827         }
828
829         NamingEntry entry = bindings.get(name.get(0));
830
831         if (entry == null) {
832             throw new NameNotFoundException
833                 (sm.getString("namingContext.nameNotBound", name, name.get(0)));
834         }
835
836         if (name.size() > 1) {
837             // If the size of the name is greater that 1, then we go through a
838             // number of subcontexts.
839             if (entry.type != NamingEntry.CONTEXT) {
840                 throw new NamingException
841                     (sm.getString("namingContext.contextExpected"));
842             }
843             return ((Context) entry.value).lookup(name.getSuffix(1));
844         } else {
845             if ((resolveLinks) && (entry.type == NamingEntry.LINK_REF)) {
846                 String link = ((LinkRef) entry.value).getLinkName();
847                 if (link.startsWith(".")) {
848                     // Link relative to this context
849                     return lookup(link.substring(1));
850                 } else {
851                     return new InitialContext(env).lookup(link);
852                 }
853             } else if (entry.type == NamingEntry.REFERENCE) {
854                 try {
855                     Object obj = null;
856                     if (!GRAAL) {
857                         obj = NamingManager.getObjectInstance(entry.value, name, this, env);
858                     } else {
859                         // NamingManager.getObjectInstance would simply return the reference here
860                         // Use the configured object factory to resolve it directly if possible
861                         // Note: This may need manual constructor reflection configuration
862                         Reference reference = (Reference) entry.value;
863                         Class<?> factoryClass = getClass().getClassLoader().loadClass(reference.getFactoryClassName());
864                         ObjectFactory factory = (ObjectFactory) factoryClass.newInstance();
865                         obj = factory.getObjectInstance(entry.value, name, this, env);
866                     }
867                     if (entry.value instanceof ResourceRef) {
868                         boolean singleton = Boolean.parseBoolean(
869                                     (String) ((ResourceRef) entry.value).get(
870                                         "singleton").getContent());
871                         if (singleton) {
872                             entry.type = NamingEntry.ENTRY;
873                             entry.value = obj;
874                         }
875                     }
876                     return obj;
877                 } catch (NamingException e) {
878                     throw e;
879                 } catch (Exception e) {
880                     String msg = sm.getString("namingContext.failResolvingReference");
881                     log.warn(msg, e);
882                     NamingException ne = new NamingException(msg);
883                     ne.initCause(e);
884                     throw ne;
885                 }
886             } else {
887                 return entry.value;
888             }
889         }
890
891     }
892
893
894     /**
895      * Binds a name to an object. All intermediate contexts and the target
896      * context (that named by all but terminal atomic component of the name)
897      * must already exist.
898      *
899      * @param name the name to bind; may not be empty
900      * @param obj the object to bind; possibly null
901      * @param rebind if true, then perform a rebind (ie, overwrite)
902      * @exception NameAlreadyBoundException if name is already bound
903      * @exception javax.naming.directory.InvalidAttributesException if object
904      * did not supply all mandatory attributes
905      * @exception NamingException if a naming exception is encountered
906      */

907     protected void bind(Name name, Object obj, boolean rebind)
908         throws NamingException {
909
910         if (!checkWritable()) {
911             return;
912         }
913
914         while ((!name.isEmpty()) && (name.get(0).length() == 0))
915             name = name.getSuffix(1);
916         if (name.isEmpty())
917             throw new NamingException
918                 (sm.getString("namingContext.invalidName"));
919
920         NamingEntry entry = bindings.get(name.get(0));
921
922         if (name.size() > 1) {
923             if (entry == null) {
924                 throw new NameNotFoundException(sm.getString(
925                         "namingContext.nameNotBound", name, name.get(0)));
926             }
927             if (entry.type == NamingEntry.CONTEXT) {
928                 if (rebind) {
929                     ((Context) entry.value).rebind(name.getSuffix(1), obj);
930                 } else {
931                     ((Context) entry.value).bind(name.getSuffix(1), obj);
932                 }
933             } else {
934                 throw new NamingException
935                     (sm.getString("namingContext.contextExpected"));
936             }
937         } else {
938             if ((!rebind) && (entry != null)) {
939                 throw new NameAlreadyBoundException
940                     (sm.getString("namingContext.alreadyBound", name.get(0)));
941             } else {
942                 // Getting the type of the object and wrapping it within a new
943                 // NamingEntry
944                 Object toBind =
945                     NamingManager.getStateToBind(obj, name, this, env);
946                 if (toBind instanceof Context) {
947                     entry = new NamingEntry(name.get(0), toBind,
948                                             NamingEntry.CONTEXT);
949                 } else if (toBind instanceof LinkRef) {
950                     entry = new NamingEntry(name.get(0), toBind,
951                                             NamingEntry.LINK_REF);
952                 } else if (toBind instanceof Reference) {
953                     entry = new NamingEntry(name.get(0), toBind,
954                                             NamingEntry.REFERENCE);
955                 } else if (toBind instanceof Referenceable) {
956                     toBind = ((Referenceable) toBind).getReference();
957                     entry = new NamingEntry(name.get(0), toBind,
958                                             NamingEntry.REFERENCE);
959                 } else {
960                     entry = new NamingEntry(name.get(0), toBind,
961                                             NamingEntry.ENTRY);
962                 }
963                 bindings.put(name.get(0), entry);
964             }
965         }
966
967     }
968
969
970     /**
971      * @return <code>true</code> if writing is allowed on this context.
972      */

973     protected boolean isWritable() {
974         return ContextAccessController.isWritable(name);
975     }
976
977
978     /**
979      * Throws a naming exception is Context is not writable.
980      * @return <code>true</code> if the Context is writable
981      * @throws NamingException if the Context is not writable and
982      *  <code>exceptionOnFailedWrite</code> is <code>true</code>
983      */

984     protected boolean checkWritable() throws NamingException {
985         if (isWritable()) {
986             return true;
987         } else {
988             if (exceptionOnFailedWrite) {
989                 throw new javax.naming.OperationNotSupportedException(
990                         sm.getString("namingContext.readOnly"));
991             }
992         }
993         return false;
994     }
995 }
996
997