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