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