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.Hashtable;
22
23 import javax.naming.Binding;
24 import javax.naming.Context;
25 import javax.naming.Name;
26 import javax.naming.NameClassPair;
27 import javax.naming.NameParser;
28 import javax.naming.NamingEnumeration;
29 import javax.naming.NamingException;
30
31 import org.apache.juli.logging.Log;
32 import org.apache.juli.logging.LogFactory;
33
34 /**
35 * Catalina JNDI Context implementation.
36 *
37 * @author Remy Maucherat
38 */
39 public class SelectorContext implements Context {
40
41
42 // -------------------------------------------------------------- Constants
43
44
45 /**
46 * Namespace URL.
47 */
48 public static final String prefix = "java:";
49
50
51 /**
52 * Namespace URL length.
53 */
54 public static final int prefixLength = prefix.length();
55
56
57 /**
58 * Initial context prefix.
59 */
60 public static final String IC_PREFIX = "IC_";
61
62
63 private static final Log log = LogFactory.getLog(SelectorContext.class);
64
65 // ----------------------------------------------------------- Constructors
66
67
68 /**
69 * Builds a Catalina selector context using the given environment.
70 * @param env The environment
71 */
72 public SelectorContext(Hashtable<String,Object> env) {
73 this.env = env;
74 this.initialContext = false;
75 }
76
77
78 /**
79 * Builds a Catalina selector context using the given environment.
80 * @param env The environment
81 * @param initialContext <code>true</code> if this is the main
82 * initial context
83 */
84 public SelectorContext(Hashtable<String,Object> env,
85 boolean initialContext) {
86 this.env = env;
87 this.initialContext = initialContext;
88 }
89
90
91 // ----------------------------------------------------- Instance Variables
92
93
94 /**
95 * Environment.
96 */
97 protected final Hashtable<String,Object> env;
98
99
100 /**
101 * The string manager for this package.
102 */
103 protected static final StringManager sm = StringManager.getManager(SelectorContext.class);
104
105
106 /**
107 * Request for an initial context.
108 */
109 protected final boolean initialContext;
110
111
112 // --------------------------------------------------------- Public Methods
113
114
115 // -------------------------------------------------------- Context Methods
116
117
118 /**
119 * Retrieves the named object. If name is empty, returns a new instance
120 * of this context (which represents the same naming context as this
121 * context, but its environment may be modified independently and it may
122 * be accessed concurrently).
123 *
124 * @param name the name of the object to look up
125 * @return the object bound to name
126 * @throws NamingException if a naming exception is encountered
127 */
128 @Override
129 public Object lookup(Name name)
130 throws NamingException {
131
132 if (log.isDebugEnabled()) {
133 log.debug(sm.getString("selectorContext.methodUsingName", "lookup",
134 name));
135 }
136
137 // Strip the URL header
138 // Find the appropriate NamingContext according to the current bindings
139 // Execute the lookup on that context
140 return getBoundContext().lookup(parseName(name));
141 }
142
143
144 /**
145 * Retrieves the named object.
146 *
147 * @param name the name of the object to look up
148 * @return the object bound to name
149 * @throws NamingException if a naming exception is encountered
150 */
151 @Override
152 public Object lookup(String name)
153 throws NamingException {
154
155 if (log.isDebugEnabled()) {
156 log.debug(sm.getString("selectorContext.methodUsingString", "lookup",
157 name));
158 }
159
160 // Strip the URL header
161 // Find the appropriate NamingContext according to the current bindings
162 // Execute the lookup on that context
163 return getBoundContext().lookup(parseName(name));
164 }
165
166
167 /**
168 * Binds a name to an object. All intermediate contexts and the target
169 * context (that named by all but terminal atomic component of the name)
170 * must already exist.
171 *
172 * @param name the name to bind; may not be empty
173 * @param obj the object to bind; possibly null
174 * @throws javax.naming.NameAlreadyBoundException if name is already
175 * bound
176 * @throws javax.naming.directory.InvalidAttributesException if object did not
177 * supply all mandatory attributes
178 * @throws NamingException if a naming exception is encountered
179 */
180 @Override
181 public void bind(Name name, Object obj)
182 throws NamingException {
183 getBoundContext().bind(parseName(name), obj);
184 }
185
186
187 /**
188 * Binds a name to an object.
189 *
190 * @param name the name to bind; may not be empty
191 * @param obj the object to bind; possibly null
192 * @throws javax.naming.NameAlreadyBoundException if name is already
193 * bound
194 * @throws javax.naming.directory.InvalidAttributesException if object did not
195 * supply all mandatory attributes
196 * @throws NamingException if a naming exception is encountered
197 */
198 @Override
199 public void bind(String name, Object obj)
200 throws NamingException {
201 getBoundContext().bind(parseName(name), obj);
202 }
203
204
205 /**
206 * Binds a name to an object, overwriting any existing binding. All
207 * intermediate contexts and the target context (that named by all but
208 * terminal atomic component of the name) must already exist.
209 * <p>
210 * If the object is a DirContext, any existing attributes associated with
211 * the name are replaced with those of the object. Otherwise, any
212 * existing attributes associated with the name remain unchanged.
213 *
214 * @param name the name to bind; may not be empty
215 * @param obj the object to bind; possibly null
216 * @throws javax.naming.directory.InvalidAttributesException if object did not
217 * supply all mandatory attributes
218 * @throws NamingException if a naming exception is encountered
219 */
220 @Override
221 public void rebind(Name name, Object obj)
222 throws NamingException {
223 getBoundContext().rebind(parseName(name), obj);
224 }
225
226
227 /**
228 * Binds a name to an object, overwriting any existing binding.
229 *
230 * @param name the name to bind; may not be empty
231 * @param obj the object to bind; possibly null
232 * @throws javax.naming.directory.InvalidAttributesException if object did not
233 * supply all mandatory attributes
234 * @throws NamingException if a naming exception is encountered
235 */
236 @Override
237 public void rebind(String name, Object obj)
238 throws NamingException {
239 getBoundContext().rebind(parseName(name), obj);
240 }
241
242
243 /**
244 * Unbinds the named object. Removes the terminal atomic name in name
245 * from the target context--that named by all but the terminal atomic
246 * part of name.
247 * <p>
248 * This method is idempotent. It succeeds even if the terminal atomic
249 * name is not bound in the target context, but throws
250 * NameNotFoundException if any of the intermediate contexts do not exist.
251 *
252 * @param name the name to bind; may not be empty
253 * @throws javax.naming.NameNotFoundException if an intermediate context
254 * does not exist
255 * @throws NamingException if a naming exception is encountered
256 */
257 @Override
258 public void unbind(Name name)
259 throws NamingException {
260 getBoundContext().unbind(parseName(name));
261 }
262
263
264 /**
265 * Unbinds the named object.
266 *
267 * @param name the name to bind; may not be empty
268 * @throws javax.naming.NameNotFoundException if an intermediate context
269 * does not exist
270 * @throws NamingException if a naming exception is encountered
271 */
272 @Override
273 public void unbind(String name)
274 throws NamingException {
275 getBoundContext().unbind(parseName(name));
276 }
277
278
279 /**
280 * Binds a new name to the object bound to an old name, and unbinds the
281 * old name. Both names are relative to this context. Any attributes
282 * associated with the old name become associated with the new name.
283 * Intermediate contexts of the old name are not changed.
284 *
285 * @param oldName the name of the existing binding; may not be empty
286 * @param newName the name of the new binding; may not be empty
287 * @throws javax.naming.NameAlreadyBoundException if name is already
288 * bound
289 * @throws NamingException if a naming exception is encountered
290 */
291 @Override
292 public void rename(Name oldName, Name newName)
293 throws NamingException {
294 getBoundContext().rename(parseName(oldName), parseName(newName));
295 }
296
297
298 /**
299 * Binds a new name to the object bound to an old name, and unbinds the
300 * old name.
301 *
302 * @param oldName the name of the existing binding; may not be empty
303 * @param newName the name of the new binding; may not be empty
304 * @throws javax.naming.NameAlreadyBoundException if name is already
305 * bound
306 * @throws NamingException if a naming exception is encountered
307 */
308 @Override
309 public void rename(String oldName, String newName)
310 throws NamingException {
311 getBoundContext().rename(parseName(oldName), parseName(newName));
312 }
313
314
315 /**
316 * Enumerates the names bound in the named context, along with the class
317 * names of objects bound to them. The contents of any subcontexts are
318 * not included.
319 * <p>
320 * If a binding is added to or removed from this context, its effect on
321 * an enumeration previously returned is undefined.
322 *
323 * @param name the name of the context to list
324 * @return an enumeration of the names and class names of the bindings in
325 * this context. Each element of the enumeration is of type NameClassPair.
326 * @throws NamingException if a naming exception is encountered
327 */
328 @Override
329 public NamingEnumeration<NameClassPair> list(Name name)
330 throws NamingException {
331
332 if (log.isDebugEnabled()) {
333 log.debug(sm.getString("selectorContext.methodUsingName", "list",
334 name));
335 }
336
337 return getBoundContext().list(parseName(name));
338 }
339
340
341 /**
342 * Enumerates the names bound in the named context, along with the class
343 * names of objects bound to them.
344 *
345 * @param name the name of the context to list
346 * @return an enumeration of the names and class names of the bindings in
347 * this context. Each element of the enumeration is of type NameClassPair.
348 * @throws NamingException if a naming exception is encountered
349 */
350 @Override
351 public NamingEnumeration<NameClassPair> list(String name)
352 throws NamingException {
353
354 if (log.isDebugEnabled()) {
355 log.debug(sm.getString("selectorContext.methodUsingString", "list",
356 name));
357 }
358
359 return getBoundContext().list(parseName(name));
360 }
361
362
363 /**
364 * Enumerates the names bound in the named context, along with the
365 * objects bound to them. The contents of any subcontexts are not
366 * included.
367 * <p>
368 * If a binding is added to or removed from this context, its effect on
369 * an enumeration previously returned is undefined.
370 *
371 * @param name the name of the context to list
372 * @return an enumeration of the bindings in this context.
373 * Each element of the enumeration is of type Binding.
374 * @throws NamingException if a naming exception is encountered
375 */
376 @Override
377 public NamingEnumeration<Binding> listBindings(Name name)
378 throws NamingException {
379
380 if (log.isDebugEnabled()) {
381 log.debug(sm.getString("selectorContext.methodUsingName",
382 "listBindings", name));
383 }
384
385 return getBoundContext().listBindings(parseName(name));
386 }
387
388
389 /**
390 * Enumerates the names bound in the named context, along with the
391 * objects bound to them.
392 *
393 * @param name the name of the context to list
394 * @return an enumeration of the bindings in this context.
395 * Each element of the enumeration is of type Binding.
396 * @throws NamingException if a naming exception is encountered
397 */
398 @Override
399 public NamingEnumeration<Binding> listBindings(String name)
400 throws NamingException {
401
402 if (log.isDebugEnabled()) {
403 log.debug(sm.getString("selectorContext.methodUsingString",
404 "listBindings", name));
405 }
406
407 return getBoundContext().listBindings(parseName(name));
408 }
409
410
411 /**
412 * Destroys the named context and removes it from the namespace. Any
413 * attributes associated with the name are also removed. Intermediate
414 * contexts are not destroyed.
415 * <p>
416 * This method is idempotent. It succeeds even if the terminal atomic
417 * name is not bound in the target context, but throws
418 * NameNotFoundException if any of the intermediate contexts do not exist.
419 *
420 * In a federated naming system, a context from one naming system may be
421 * bound to a name in another. One can subsequently look up and perform
422 * operations on the foreign context using a composite name. However, an
423 * attempt destroy the context using this composite name will fail with
424 * NotContextException, because the foreign context is not a "subcontext"
425 * of the context in which it is bound. Instead, use unbind() to remove
426 * the binding of the foreign context. Destroying the foreign context
427 * requires that the destroySubcontext() be performed on a context from
428 * the foreign context's "native" naming system.
429 *
430 * @param name the name of the context to be destroyed; may not be empty
431 * @throws javax.naming.NameNotFoundException if an intermediate context
432 * does not exist
433 * @throws javax.naming.NotContextException if the name is bound but does
434 * not name a context, or does not name a context of the appropriate type
435 */
436 @Override
437 public void destroySubcontext(Name name)
438 throws NamingException {
439 getBoundContext().destroySubcontext(parseName(name));
440 }
441
442
443 /**
444 * Destroys the named context and removes it from the namespace.
445 *
446 * @param name the name of the context to be destroyed; may not be empty
447 * @throws javax.naming.NameNotFoundException if an intermediate context
448 * does not exist
449 * @throws javax.naming.NotContextException if the name is bound but does
450 * not name a context, or does not name a context of the appropriate type
451 */
452 @Override
453 public void destroySubcontext(String name)
454 throws NamingException {
455 getBoundContext().destroySubcontext(parseName(name));
456 }
457
458
459 /**
460 * Creates and binds a new context. Creates a new context with the given
461 * name and binds it in the target context (that named by all but
462 * terminal atomic component of the name). All intermediate contexts and
463 * the target context must already exist.
464 *
465 * @param name the name of the context to create; may not be empty
466 * @return the newly created context
467 * @throws javax.naming.NameAlreadyBoundException if name is already
468 * bound
469 * @throws javax.naming.directory.InvalidAttributesException if creation of the
470 * sub-context requires specification of mandatory attributes
471 * @throws NamingException if a naming exception is encountered
472 */
473 @Override
474 public Context createSubcontext(Name name)
475 throws NamingException {
476 return getBoundContext().createSubcontext(parseName(name));
477 }
478
479
480 /**
481 * Creates and binds a new context.
482 *
483 * @param name the name of the context to create; may not be empty
484 * @return the newly created context
485 * @throws javax.naming.NameAlreadyBoundException if name is already
486 * bound
487 * @throws javax.naming.directory.InvalidAttributesException if creation of the
488 * sub-context requires specification of mandatory attributes
489 * @throws NamingException if a naming exception is encountered
490 */
491 @Override
492 public Context createSubcontext(String name)
493 throws NamingException {
494 return getBoundContext().createSubcontext(parseName(name));
495 }
496
497
498 /**
499 * Retrieves the named object, following links except for the terminal
500 * atomic component of the name. If the object bound to name is not a
501 * link, returns the object itself.
502 *
503 * @param name the name of the object to look up
504 * @return the object bound to name, not following the terminal link
505 * (if any).
506 * @throws NamingException if a naming exception is encountered
507 */
508 @Override
509 public Object lookupLink(Name name)
510 throws NamingException {
511
512 if (log.isDebugEnabled()) {
513 log.debug(sm.getString("selectorContext.methodUsingName",
514 "lookupLink", name));
515 }
516
517 return getBoundContext().lookupLink(parseName(name));
518 }
519
520
521 /**
522 * Retrieves the named object, following links except for the terminal
523 * atomic component of the name.
524 *
525 * @param name the name of the object to look up
526 * @return the object bound to name, not following the terminal link
527 * (if any).
528 * @throws NamingException if a naming exception is encountered
529 */
530 @Override
531 public Object lookupLink(String name)
532 throws NamingException {
533
534 if (log.isDebugEnabled()) {
535 log.debug(sm.getString("selectorContext.methodUsingString",
536 "lookupLink", name));
537 }
538
539 return getBoundContext().lookupLink(parseName(name));
540 }
541
542
543 /**
544 * Retrieves the parser associated with the named context. In a
545 * federation of namespaces, different naming systems will parse names
546 * differently. This method allows an application to get a parser for
547 * parsing names into their atomic components using the naming convention
548 * of a particular naming system. Within any single naming system,
549 * NameParser objects returned by this method must be equal (using the
550 * equals() test).
551 *
552 * @param name the name of the context from which to get the parser
553 * @return a name parser that can parse compound names into their atomic
554 * components
555 * @throws NamingException if a naming exception is encountered
556 */
557 @Override
558 public NameParser getNameParser(Name name)
559 throws NamingException {
560 return getBoundContext().getNameParser(parseName(name));
561 }
562
563
564 /**
565 * Retrieves the parser associated with the named context.
566 *
567 * @param name the name of the context from which to get the parser
568 * @return a name parser that can parse compound names into their atomic
569 * components
570 * @throws NamingException if a naming exception is encountered
571 */
572 @Override
573 public NameParser getNameParser(String name)
574 throws NamingException {
575 return getBoundContext().getNameParser(parseName(name));
576 }
577
578
579 /**
580 * Composes the name of this context with a name relative to this context.
581 * <p>
582 * Given a name (name) relative to this context, and the name (prefix)
583 * of this context relative to one of its ancestors, this method returns
584 * the composition of the two names using the syntax appropriate for the
585 * naming system(s) involved. That is, if name names an object relative
586 * to this context, the result is the name of the same object, but
587 * relative to the ancestor context. None of the names may be null.
588 *
589 * @param name a name relative to this context
590 * @param prefix the name of this context relative to one of its ancestors
591 * @return the composition of prefix and name
592 * @throws NamingException if a naming exception is encountered
593 */
594 @Override
595 public Name composeName(Name name, Name prefix)
596 throws NamingException {
597 Name prefixClone = (Name) prefix.clone();
598 return prefixClone.addAll(name);
599 }
600
601
602 /**
603 * Composes the name of this context with a name relative to this context.
604 *
605 * @param name a name relative to this context
606 * @param prefix the name of this context relative to one of its ancestors
607 * @return the composition of prefix and name
608 * @throws NamingException if a naming exception is encountered
609 */
610 @Override
611 public String composeName(String name, String prefix)
612 throws NamingException {
613 return prefix + "/" + name;
614 }
615
616
617 /**
618 * Adds a new environment property to the environment of this context. If
619 * the property already exists, its value is overwritten.
620 *
621 * @param propName the name of the environment property to add; may not
622 * be null
623 * @param propVal the value of the property to add; may not be null
624 * @throws NamingException if a naming exception is encountered
625 */
626 @Override
627 public Object addToEnvironment(String propName, Object propVal)
628 throws NamingException {
629 return getBoundContext().addToEnvironment(propName, propVal);
630 }
631
632
633 /**
634 * Removes an environment property from the environment of this context.
635 *
636 * @param propName the name of the environment property to remove;
637 * may not be null
638 * @throws NamingException if a naming exception is encountered
639 */
640 @Override
641 public Object removeFromEnvironment(String propName)
642 throws NamingException {
643 return getBoundContext().removeFromEnvironment(propName);
644 }
645
646
647 /**
648 * Retrieves the environment in effect for this context. See class
649 * description for more details on environment properties.
650 * The caller should not make any changes to the object returned: their
651 * effect on the context is undefined. The environment of this context
652 * may be changed using addToEnvironment() and removeFromEnvironment().
653 *
654 * @return the environment of this context; never null
655 * @throws NamingException if a naming exception is encountered
656 */
657 @Override
658 public Hashtable<?,?> getEnvironment()
659 throws NamingException {
660 return getBoundContext().getEnvironment();
661 }
662
663
664 /**
665 * Closes this context. This method releases this context's resources
666 * immediately, instead of waiting for them to be released automatically
667 * by the garbage collector.
668 * This method is idempotent: invoking it on a context that has already
669 * been closed has no effect. Invoking any other method on a closed
670 * context is not allowed, and results in undefined behaviour.
671 *
672 * @throws NamingException if a naming exception is encountered
673 */
674 @Override
675 public void close()
676 throws NamingException {
677 getBoundContext().close();
678 }
679
680
681 /**
682 * Retrieves the full name of this context within its own namespace.
683 * <p>
684 * Many naming services have a notion of a "full name" for objects in
685 * their respective namespaces. For example, an LDAP entry has a
686 * distinguished name, and a DNS record has a fully qualified name. This
687 * method allows the client application to retrieve this name. The string
688 * returned by this method is not a JNDI composite name and should not be
689 * passed directly to context methods. In naming systems for which the
690 * notion of full name does not make sense,
691 * OperationNotSupportedException is thrown.
692 *
693 * @return this context's name in its own namespace; never null
694 * @throws javax.naming.OperationNotSupportedException if the naming
695 * system does not have the notion of a full name
696 * @throws NamingException if a naming exception is encountered
697 */
698 @Override
699 public String getNameInNamespace()
700 throws NamingException {
701 return prefix;
702 }
703
704
705 // ------------------------------------------------------ Protected Methods
706
707
708 /**
709 * Get the bound context.
710 * @return the Context bound with either the current thread or
711 * the current classloader
712 * @throws NamingException Bindings exception
713 */
714 protected Context getBoundContext()
715 throws NamingException {
716
717 if (initialContext) {
718 String ICName = IC_PREFIX;
719 if (ContextBindings.isThreadBound()) {
720 ICName += ContextBindings.getThreadName();
721 } else if (ContextBindings.isClassLoaderBound()) {
722 ICName += ContextBindings.getClassLoaderName();
723 }
724 Context initialContext = ContextBindings.getContext(ICName);
725 if (initialContext == null) {
726 // Allocating a new context and binding it to the appropriate
727 // name
728 initialContext = new NamingContext(env, ICName);
729 ContextBindings.bindContext(ICName, initialContext);
730 }
731 return initialContext;
732 } else {
733 if (ContextBindings.isThreadBound()) {
734 return ContextBindings.getThread();
735 } else {
736 return ContextBindings.getClassLoader();
737 }
738 }
739
740 }
741
742
743 /**
744 * Strips the URL header.
745 * @param name The name
746 * @return the parsed name
747 * @throws NamingException if there is no "java:" header or if no
748 * naming context has been bound to this thread
749 */
750 protected String parseName(String name)
751 throws NamingException {
752
753 if ((!initialContext) && (name.startsWith(prefix))) {
754 return name.substring(prefixLength);
755 } else {
756 if (initialContext) {
757 return name;
758 } else {
759 throw new NamingException
760 (sm.getString("selectorContext.noJavaUrl"));
761 }
762 }
763
764 }
765
766
767 /**
768 * Strips the URL header.
769 * @param name The name
770 * @return the parsed name
771 * @throws NamingException if there is no "java:" header or if no
772 * naming context has been bound to this thread
773 */
774 protected Name parseName(Name name)
775 throws NamingException {
776
777 if (!initialContext && !name.isEmpty() &&
778 name.get(0).startsWith(prefix)) {
779 if (name.get(0).equals(prefix)) {
780 return name.getSuffix(1);
781 } else {
782 Name result = name.getSuffix(1);
783 result.add(0, name.get(0).substring(prefixLength));
784 return result;
785 }
786 } else {
787 if (initialContext) {
788 return name;
789 } else {
790 throw new NamingException(
791 sm.getString("selectorContext.noJavaUrl"));
792 }
793 }
794
795 }
796
797
798 }
799
800