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 package javax.naming;
19
20 import java.util.HashMap;
21 import java.util.Hashtable;
22 import javax.naming.spi.NamingManager;
23
24 import org.apache.harmony.jndi.internal.UrlParser;
25 import org.apache.harmony.jndi.internal.EnvironmentReader;
26 import org.apache.harmony.jndi.internal.nls.Messages;
27
28 /**
29 * An <code>InitialContext</code> object is required as the starting context
30 * for any naming operations. Other contexts and subcontexts may be created
31 * later. Contexts may consist of different implementations according to the
32 * needs of the application. All naming operations are performed relative to a
33 * context and names are resolved beginning with the initial context.
34 * <p>
35 * When constructing an initial context, environment properties from a range of
36 * sources may be used to initialize the environment. See the specification of
37 * the {@link Context} interface for further details of environment properties.
38 * </p>
39 * <p>
40 * The environment at runtime determines the initial context implementation. By
41 * default, the naming frameworks look for the initial context factory class
42 * name in the property <code>Context.INITIAL_CONTEXT_FACTORY</code>. When
43 * URL strings must be resolved, a different policy is used which is described
44 * below.
45 * </p>
46 * <p>
47 * A <code>NoInitialContextException</code> is thrown when it cannot create an
48 * initial context. The exception may occur not only during constructor
49 * invocation, but may occur later. For example, when a subclass of <code>
50 * InitialContext</code>
51 * uses the lazy initialization option, <code>
52 * InitialContext</code> methods
53 * may be invoked later which require the initialization to be completed at that
54 * time using the <code>init</code> protected method. In these circumstances,
55 * <code>NoInitialContextException
56 * </code> may be thrown some time after the
57 * constructor was invoked. JNDI applications should be written to be
58 * independent of when initial context is actually initialized.
59 * </p>
60 * <p>
61 * If environment property <code>Context.INITIAL_CONTEXT_FACTORY</code> has a
62 * non-null value, then the specified initial context factory may experience a
63 * problem trying to instantiate an initial context and so throw an exception.
64 * It is a responsibility of the service provider implementation as to when an
65 * exception is thrown to report the problem to the JNDI application.
66 * </p>
67 * <p>
68 * URL names comprising a String format described by RFC1738 may be components
69 * of names passed to naming operations. Typically, the URL is composed of the
70 * "scheme" - such as one of http, ldap, dns - followed by additional text. If
71 * the JNDI can identify the URL scheme from the specified name, then it is used
72 * to construct a classname suffix in the following form:<br>
73 *
74 * <pre>
75 * <package_prefix> . <scheme> . <scheme>URLContextFactory
76 * </pre>
77 *
78 * Several variants of the classname are constructed using each element of the
79 * <code>Context.URL_PACKAGE_PREFIXES</code> environment property. Note that
80 * an additional package prefix - "com.sun.jndi.url" - is always considered to
81 * be at the end of those already present in the value of that environment
82 * property. Although a service provider may also provide a URL context
83 * implementation as well as a context implementation, it is not required to do
84 * so, and so an arbitrary service provider might not provide for creating URL
85 * contexts.
86 * </p>
87 * <p>
88 * If a URL context is successfully created for a specified URL scheme, the
89 * factory can create contexts for arbitrary URLs of the same scheme.
90 * <code>NamingManager.setInitialContextFactoryBuilder</code> may be used to
91 * specify an alternate policy for locating factories for initial contexts and
92 * URL contexts.
93 * </p>
94 * <p>
95 * On successful completion of <code>InitialContext</code> initialization, the
96 * service provider implementation will have returned an appropriate <code>
97 * Context</code>
98 * object which can be used for looking up and manipulating names which may or
99 * may not be URL names. <code>InitialContext</code> methods other than those
100 * dealing with environments should delegate context operations to that
101 * <code>Context</code> object.
102 * </p>
103 *
104 * @see Context
105 */
106 public class InitialContext implements Context {
107
108 /**
109 * Set to the result of the first successful invocation of <code>
110 * NamingManager.getInitialContext</code>
111 * by <code>getDefaultInitCtx
112 * </code>. Initially null.
113 */
114 protected Context defaultInitCtx;
115
116 /**
117 * Set to true when <code>NamingManager.getInitialContext</code> has been
118 * invoked to obtain an initial context. Initially false.
119 */
120 protected boolean gotDefault;
121
122 /**
123 * Contains all those JNDI environment properties that were found in any of
124 * the the sources of JNDI environment properties. Initially null.
125 */
126 protected Hashtable<Object, Object> myProps;
127
128 /**
129 * A shortcut method for retrieving the named object by <code>Name</code>.
130 * It is equivalent to
131 *
132 * <pre>
133 * InitialContext icxt = new InitialContext();
134 * T obj = icxt.lookup();
135 * </pre>
136 *
137 * <p>
138 * Returns a new instance of this context when <code>name</code> is empty.
139 * The new instance represents the same naming context as this context, but
140 * may be accessed/modified independently and concurrently.
141 * </p>
142 *
143 * @param name
144 * the name to be looked up
145 * @return the object bound to <code>name</code>
146 * @throws NamingException
147 * if a naming exception is encountered
148 *
149 * @see #doLookup(String)
150 * @see #lookup(Name)
151 * @since 1.6
152 */
153 public static <T> T doLookup(Name name) throws NamingException {
154 return (T) new InitialContext().lookup(name);
155 }
156
157 /**
158 * A static method that retrieves the named object by <code>String</code>.
159 *
160 * @param name
161 * the name of the object being looked up
162 * @return the object bound to <code>name</code>
163 * @throws NamingException
164 * if a naming exception is encountered
165 *
166 * @see #doLookup(Name)
167 * @since 1.6
168 */
169 public static <T> T doLookup(String name) throws NamingException {
170 return (T) new InitialContext().lookup(name);
171 }
172
173 /**
174 * Contains loaded properties for each classloader
175 */
176 private static Hashtable<ClassLoader, Hashtable<Object, Object>> propsCache = new Hashtable<ClassLoader, Hashtable<Object, Object>>();
177
178 /**
179 * Contians properties load from java.home/lib/jndi.properties
180 */
181 private static Hashtable<Object, Object> libProperties = null;
182
183 /**
184 * Constructs an <code>InitialContext</code> instance without using any
185 * environment properties. This constructor is effectively the same as using
186 * constructor <code>InitialContext((Hashtable)null)</code>.
187 *
188 * @throws NamingException
189 * If failed to create an <code>InitialContext</code>.
190 */
191 public InitialContext() throws NamingException {
192 this(null);
193 }
194
195 /**
196 * Constructs an <code>InitialContext</code> instance using environment
197 * properties in the supplied parameter which may be null.
198 *
199 * @param environment
200 * the JNDI environment properties used to create the context
201 * @throws NamingException
202 * If failed to create an <code>InitialContext</code>.
203 */
204 public InitialContext(Hashtable<?, ?> environment) throws NamingException {
205 internalInit(environment);
206 }
207
208 /**
209 * Constructs an <code>InitialContext</code> instance by indicating
210 * whether a lazy initialization is desired. Effectively, this is the same
211 * as using constructor <code>InitialContext()
212 * </code> if lazy
213 * initialization is not indicated.
214 * <p>
215 * This constructor may be invoked with a parameter value of true and the
216 * implementation will defer initialization of the instance. This may be
217 * used in an <code>InitialContext</code> subclass constructor in which
218 * later action will set up a <code>Hashtable</code> object with
219 * appropriate environment properties and pass that to the <code>init</code>
220 * method to complete initialization of the <code>InitialContext</code>
221 * object.
222 * </p>
223 *
224 * @param doNotInit
225 * Specifies whether to initialize the new instance.
226 * @throws NamingException
227 * If failed to create an <code>InitialContext</code>.
228 */
229 protected InitialContext(boolean doNotInit) throws NamingException {
230 if (!doNotInit) {
231 internalInit(null);
232 }
233 }
234
235 /**
236 * Does private initialization.
237 *
238 * @param env
239 * the JNDI environment properties used to create the context
240 * @throws NamingException
241 * If failed to create an InitialContext.
242 */
243 @SuppressWarnings("unchecked")
244 private void internalInit(Hashtable<?, ?> env) throws NamingException {
245
246 // 1. Read the environment parameter used to create this Context
247 if (null == env) {
248 myProps = new Hashtable<Object, Object>();
249 } else {
250 myProps = (Hashtable<Object, Object>) env.clone();
251 }
252
253 // 2. Read Applet parameters
254 EnvironmentReader.readAppletParameters(myProps.get(Context.APPLET),
255 myProps);
256
257 // 3. Read System properties
258 EnvironmentReader.readSystemProperties(myProps);
259
260 // 4.1 Read application/applet resource files
261 ClassLoader cl = Thread.currentThread().getContextClassLoader();
262 if (propsCache.containsKey(cl)) {
263 EnvironmentReader.mergeEnvironment(propsCache.get(cl), myProps,
264 true);
265 } else {
266 Hashtable<Object, Object> appProps = new Hashtable<Object, Object>();
267 EnvironmentReader.readApplicationResourceFiles(appProps);
268 propsCache.put(cl, appProps);
269 EnvironmentReader.mergeEnvironment(appProps, myProps, true);
270 }
271
272 // 4.2 Read "java.home"/lib/jndi.properties
273 if (libProperties == null) {
274 Hashtable<Object, Object> props = new Hashtable<Object, Object>();
275 EnvironmentReader.readLibraryResourceFile(props);
276 libProperties = props;
277 }
278
279 EnvironmentReader.mergeEnvironment(libProperties, myProps, true);
280
281
282 // 5. No need to read service provider resource files
283
284 // if JNDI standard property "java.naming.factory.initial" has a
285 // non-null value
286 if (myProps.containsKey(INITIAL_CONTEXT_FACTORY)) {
287 // call getDefaultInitCtx() to initialize gotDefault and
288 // defaultInitCtx
289 getDefaultInitCtx();
290 }
291
292 }
293
294 /**
295 * Uses the specified environment parameter together with other JNDI
296 * properties to initialize this <code>InitialContext</code> object. The
297 * <code>myProps</code> field will be filled with found JNDI properties.
298 * If JNDI standard property "java.naming.factory.initial" has a non-null
299 * value, then <code>getDefaultInitCtx</code> is invoked to try to
300 * initialize fields <code>gotDefault</code> and
301 * <code>defaultInitCtx</code> of the <code>InitialContext</code>
302 * object.
303 *
304 * @param env
305 * the JNDI environment properties supplied to create the context
306 * @throws NamingException
307 * If naming problems are encountered during initialization of
308 * these fields.
309 */
310 protected void init(Hashtable<?, ?> env) throws NamingException {
311 this.internalInit(env);
312 }
313
314 /*
315 * Initializes the default initial context.
316 *
317 * @throws NamingException If failed to initialize this InitialContext.
318 */
319 private void initializeDefaultInitCtx() throws NamingException {
320 if (!this.gotDefault) {
321 this.defaultInitCtx = NamingManager.getInitialContext(myProps);
322 if (null == this.defaultInitCtx) {
323 throw new NoInitialContextException(
324 "Failed to create an initial context."); //$NON-NLS-1$
325 }
326 this.gotDefault = true;
327 }
328 }
329
330 /**
331 * Gets the default underlying <code>Context</code> implementation. If
332 * <code>gotDefault</code> is true, returns the value of <code>
333 * defaultInitCtx</code>.
334 * Otherwise, calls <code>NamingManager.getInitialContext
335 * </code> to return
336 * an initial context for the current environment into
337 * <code>defaultInitCtx</code>, then <code>gotDefault</code> is set
338 * true. If the resulting context object is null, a
339 * <code>NoInitialContextException
340 * </code> is thrown, otherwise the value
341 * of <code>defaultInitCtx</code> is returned.
342 *
343 * @return the default context
344 * @throws NoInitialContextException
345 * If <code>NamingManager.getInitialContext</code> returns
346 * null.
347 * @throws NamingException
348 * If failed to create the default context.
349 */
350 protected Context getDefaultInitCtx() throws NamingException {
351 initializeDefaultInitCtx();
352 return this.defaultInitCtx;
353 }
354
355 /**
356 * Returns a non-null context for the specified name of Name representation.
357 * <p>
358 * If an initial context factory builder has been defined, then the
359 * specified <code>Name</code> parameter is ignored and the result of
360 * <code>
361 * getDefaultInitCtx</code> is returned. Otherwise, if the first
362 * component of the name is not a URL string, then it returns the result of
363 * invoking <code>getDefaultInitCtx</code>. Otherwise, it attempts to
364 * return a URL context
365 * {@link javax.naming.spi.NamingManager#getURLContext(String, Hashtable)},
366 * but if unsuccessful, returns the result of invoking
367 * <code>getDefaultInitCtx</code>.
368 * </p>
369 *
370 * @param name
371 * a name used in a naming operation which may not be null
372 * @return a context which may be a URL context
373 * @throws NamingException
374 * If failed to get the desired context.
375 */
376 protected Context getURLOrDefaultInitCtx(Name name) throws NamingException {
377 // If the name has components
378 if (0 < name.size()) {
379 return getURLOrDefaultInitCtx(name.get(0));
380 }
381 return getDefaultInitCtx();
382 }
383
384 /**
385 * Returns a non-null context for the specified name of string
386 * representation.
387 * <p>
388 * If an initial context factory builder has been defined, then the
389 * specified name parameter is ignored and the result of <code>
390 * getDefaultInitCtx</code>
391 * is returned. Otherwise, if the name is not a URL string, then it returns
392 * the result of invoking <code>getDefaultInitCtx
393 * </code>. Otherwise, it
394 * attempts to return a URL context
395 * {@link javax.naming.spi.NamingManager#getURLContext(String, Hashtable)},
396 * but if unsuccessful, returns the result of invoking <code>
397 * getDefaultInitCtx</code>.
398 * </p>
399 *
400 * @param name
401 * a name used in a naming operation which may not be null
402 * @return a context which may be a URL context
403 * @throws NamingException
404 * If failed to get the desired context.
405 */
406 protected Context getURLOrDefaultInitCtx(String name)
407 throws NamingException {
408
409 /*
410 * If an initial context factory builder has been defined, then the
411 * specified name parameter is ignored and the result of
412 * getDefaultInitCtx() is returned.
413 */
414 if (NamingManager.hasInitialContextFactoryBuilder()) {
415 return getDefaultInitCtx();
416 }
417
418 if (null == name) {
419 // jndi.00=name must not be null
420 throw new NullPointerException(Messages.getString("jndi.00")); //$NON-NLS-1$
421 }
422
423 // If the name has components
424 String scheme = UrlParser.getScheme(name);
425 Context ctx = null;
426 if (null != scheme) {
427 synchronized (contextCache) {
428 if (contextCache.containsKey(scheme)) {
429 return contextCache.get(scheme);
430 }
431
432 // So the first component is a valid URL
433 ctx = NamingManager.getURLContext(scheme, myProps);
434 if (null == ctx) {
435 ctx = getDefaultInitCtx();
436 }
437 contextCache.put(scheme, ctx);
438 }
439 return ctx;
440 }
441 return getDefaultInitCtx();
442 }
443
444 public Object lookup(Name name) throws NamingException {
445 return getURLOrDefaultInitCtx(name).lookup(name);
446 }
447
448 public Object lookup(String name) throws NamingException {
449 return getURLOrDefaultInitCtx(name).lookup(name);
450 }
451
452 public void bind(Name name, Object obj) throws NamingException {
453 getURLOrDefaultInitCtx(name).bind(name, obj);
454 }
455
456 public void bind(String name, Object obj) throws NamingException {
457 getURLOrDefaultInitCtx(name).bind(name, obj);
458 }
459
460 public void rebind(Name name, Object obj) throws NamingException {
461 getURLOrDefaultInitCtx(name).rebind(name, obj);
462 }
463
464 public void rebind(String name, Object obj) throws NamingException {
465 getURLOrDefaultInitCtx(name).rebind(name, obj);
466 }
467
468 public void unbind(Name name) throws NamingException {
469 getURLOrDefaultInitCtx(name).unbind(name);
470 }
471
472 public void unbind(String name) throws NamingException {
473 getURLOrDefaultInitCtx(name).unbind(name);
474 }
475
476 public void rename(Name oldName, Name newName) throws NamingException {
477 getURLOrDefaultInitCtx(oldName).rename(oldName, newName);
478 }
479
480 public void rename(String oldName, String newName) throws NamingException {
481 getURLOrDefaultInitCtx(oldName).rename(oldName, newName);
482 }
483
484 public NamingEnumeration<NameClassPair> list(Name name)
485 throws NamingException {
486 return getURLOrDefaultInitCtx(name).list(name);
487 }
488
489 public NamingEnumeration<NameClassPair> list(String name)
490 throws NamingException {
491 return getURLOrDefaultInitCtx(name).list(name);
492 }
493
494 public NamingEnumeration<Binding> listBindings(Name name)
495 throws NamingException {
496 return getURLOrDefaultInitCtx(name).listBindings(name);
497 }
498
499 public NamingEnumeration<Binding> listBindings(String name)
500 throws NamingException {
501 return getURLOrDefaultInitCtx(name).listBindings(name);
502 }
503
504 public void destroySubcontext(Name name) throws NamingException {
505 getURLOrDefaultInitCtx(name).destroySubcontext(name);
506 }
507
508 public void destroySubcontext(String name) throws NamingException {
509 getURLOrDefaultInitCtx(name).destroySubcontext(name);
510 }
511
512 public Context createSubcontext(Name name) throws NamingException {
513 return getURLOrDefaultInitCtx(name).createSubcontext(name);
514 }
515
516 public Context createSubcontext(String name) throws NamingException {
517 return getURLOrDefaultInitCtx(name).createSubcontext(name);
518 }
519
520 public Object lookupLink(Name name) throws NamingException {
521 return getURLOrDefaultInitCtx(name).lookupLink(name);
522 }
523
524 public Object lookupLink(String name) throws NamingException {
525 return getURLOrDefaultInitCtx(name).lookupLink(name);
526 }
527
528 public NameParser getNameParser(Name name) throws NamingException {
529 return getURLOrDefaultInitCtx(name).getNameParser(name);
530 }
531
532 public NameParser getNameParser(String name) throws NamingException {
533 return getURLOrDefaultInitCtx(name).getNameParser(name);
534 }
535
536 /**
537 * Combines two names into a composite name according to the syntax for this
538 * context. The name <code>prefix</code> is expected to be the name of one
539 * or more of the immediate parent contexts of this context, so should be an
540 * empty name for an <code>InitialContext</code>. <code>name</code> is
541 * a name relative to this context. Neither <code>prefix</code> nor
542 * <code>name</code> may be null.
543 *
544 * @param name
545 * a <code>Name</code>, may not be null
546 * @param prefix
547 * a <code>Name</code> serves as prefix, may not be null
548 * @return the combined name
549 * @throws NamingException
550 * if an error occurs.
551 */
552 public Name composeName(Name name, Name prefix) throws NamingException {
553 if (null == name) {
554 throw new NullPointerException();
555 }
556 return (Name) name.clone();
557 }
558
559 /**
560 * Combines two names into a composite name according to the syntax for this
561 * context. The name <code>prefix</code> is expected to be the name of one
562 * or more of the immediate parent contexts of this context, so should be an
563 * empty string for an <code>InitialContext</code>. <code>name</code>
564 * is a name relative to this context.
565 *
566 * @param name
567 * a <code>Name</code>, may not be null
568 * @param prefix
569 * a <code>Name</code> serves as prefix, may not be null
570 * @return the combined name
571 * @throws NamingException
572 * if an error occurs.
573 */
574 public String composeName(String name, String prefix)
575 throws NamingException {
576 return name;
577 }
578
579 public Object addToEnvironment(String propName, Object propVal)
580 throws NamingException {
581 synchronized (contextCache) {
582 myProps.put(propName, propVal);
583 contextCache.clear();
584 }
585 return getDefaultInitCtx().addToEnvironment(propName, propVal);
586 }
587
588 public Object removeFromEnvironment(String propName) throws NamingException {
589 synchronized (contextCache) {
590 myProps.remove(propName);
591 contextCache.clear();
592 }
593 return getDefaultInitCtx().removeFromEnvironment(propName);
594 }
595
596 public Hashtable<?, ?> getEnvironment() throws NamingException {
597 return getDefaultInitCtx().getEnvironment();
598 }
599
600 public void close() throws NamingException {
601 if (this.gotDefault) {
602 getDefaultInitCtx().close();
603 }
604 }
605
606 public String getNameInNamespace() throws NamingException {
607 return getDefaultInitCtx().getNameInNamespace();
608 }
609
610 private HashMap<String, Context> contextCache = new HashMap<String, Context>();
611 }