001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package javax.naming;
019:
020: import java.util.HashMap;
021: import java.util.Hashtable;
022: import javax.naming.spi.NamingManager;
023:
024: import org.apache.harmony.jndi.internal.UrlParser;
025: import org.apache.harmony.jndi.internal.EnvironmentReader;
026: import org.apache.harmony.jndi.internal.nls.Messages;
027:
028: /**
029: * An <code>InitialContext</code> object is required as the starting context
030: * for any naming operations. Other contexts and subcontexts may be created
031: * later. Contexts may consist of different implementations according to the
032: * needs of the application. All naming operations are performed relative to a
033: * context and names are resolved beginning with the initial context.
034: * <p>
035: * When constructing an initial context, environment properties from a range of
036: * sources may be used to initialize the environment. See the specification of
037: * the {@link Context} interface for further details of environment properties.
038: * </p>
039: * <p>
040: * The environment at runtime determines the initial context implementation. By
041: * default, the naming frameworks look for the initial context factory class
042: * name in the property <code>Context.INITIAL_CONTEXT_FACTORY</code>. When
043: * URL strings must be resolved, a different policy is used which is described
044: * below.
045: * </p>
046: * <p>
047: * A <code>NoInitialContextException</code> is thrown when it cannot create an
048: * initial context. The exception may occur not only during constructor
049: * invocation, but may occur later. For example, when a subclass of <code>
050: * InitialContext</code>
051: * uses the lazy initialization option, <code>
052: * InitialContext</code> methods
053: * may be invoked later which require the initialization to be completed at that
054: * time using the <code>init</code> protected method. In these circumstances,
055: * <code>NoInitialContextException
056: * </code> may be thrown some time after the
057: * constructor was invoked. JNDI applications should be written to be
058: * independent of when initial context is actually initialized.
059: * </p>
060: * <p>
061: * If environment property <code>Context.INITIAL_CONTEXT_FACTORY</code> has a
062: * non-null value, then the specified initial context factory may experience a
063: * problem trying to instantiate an initial context and so throw an exception.
064: * It is a responsibility of the service provider implementation as to when an
065: * exception is thrown to report the problem to the JNDI application.
066: * </p>
067: * <p>
068: * URL names comprising a String format described by RFC1738 may be components
069: * of names passed to naming operations. Typically, the URL is composed of the
070: * "scheme" - such as one of http, ldap, dns - followed by additional text. If
071: * the JNDI can identify the URL scheme from the specified name, then it is used
072: * to construct a classname suffix in the following form:<br>
073: *
074: * <pre>
075: * <package_prefix> . <scheme> . <scheme>URLContextFactory
076: * </pre>
077: *
078: * Several variants of the classname are constructed using each element of the
079: * <code>Context.URL_PACKAGE_PREFIXES</code> environment property. Note that
080: * an additional package prefix - "com.sun.jndi.url" - is always considered to
081: * be at the end of those already present in the value of that environment
082: * property. Although a service provider may also provide a URL context
083: * implementation as well as a context implementation, it is not required to do
084: * so, and so an arbitrary service provider might not provide for creating URL
085: * contexts.
086: * </p>
087: * <p>
088: * If a URL context is successfully created for a specified URL scheme, the
089: * factory can create contexts for arbitrary URLs of the same scheme.
090: * <code>NamingManager.setInitialContextFactoryBuilder</code> may be used to
091: * specify an alternate policy for locating factories for initial contexts and
092: * URL contexts.
093: * </p>
094: * <p>
095: * On successful completion of <code>InitialContext</code> initialization, the
096: * service provider implementation will have returned an appropriate <code>
097: * Context</code>
098: * object which can be used for looking up and manipulating names which may or
099: * 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: * Contains loaded properties for each classloader
130: */
131: private static Hashtable<ClassLoader, Hashtable<Object, Object>> propsCache = new Hashtable<ClassLoader, Hashtable<Object, Object>>();
132:
133: /**
134: * Contians properties load from java.home/lib/jndi.properties
135: */
136: private static Hashtable<Object, Object> libProperties = null;
137:
138: /**
139: * Constructs an <code>InitialContext</code> instance without using any
140: * environment properties. This constructor is effectively the same as using
141: * constructor <code>InitialContext((Hashtable)null)</code>.
142: *
143: * @throws NamingException
144: * If failed to create an <code>InitialContext</code>.
145: */
146: public InitialContext() throws NamingException {
147: this (null);
148: }
149:
150: /**
151: * Constructs an <code>InitialContext</code> instance using environment
152: * properties in the supplied parameter which may be null.
153: *
154: * @param environment
155: * the JNDI environment properties used to create the context
156: * @throws NamingException
157: * If failed to create an <code>InitialContext</code>.
158: */
159: public InitialContext(Hashtable<?, ?> environment)
160: throws NamingException {
161: internalInit(environment);
162: }
163:
164: /**
165: * Constructs an <code>InitialContext</code> instance by indicating
166: * whether a lazy initialization is desired. Effectively, this is the same
167: * as using constructor <code>InitialContext()
168: * </code> if lazy
169: * initialization is not indicated.
170: * <p>
171: * This constructor may be invoked with a parameter value of true and the
172: * implementation will defer initialization of the instance. This may be
173: * used in an <code>InitialContext</code> subclass constructor in which
174: * later action will set up a <code>Hashtable</code> object with
175: * appropriate environment properties and pass that to the <code>init</code>
176: * method to complete initialization of the <code>InitialContext</code>
177: * object.
178: * </p>
179: *
180: * @param doNotInit
181: * Specifies whether to initialize the new instance.
182: * @throws NamingException
183: * If failed to create an <code>InitialContext</code>.
184: */
185: protected InitialContext(boolean doNotInit) throws NamingException {
186: if (!doNotInit) {
187: internalInit(null);
188: }
189: }
190:
191: /**
192: * Does private initialization.
193: *
194: * @param env
195: * the JNDI environment properties used to create the context
196: * @throws NamingException
197: * If failed to create an InitialContext.
198: */
199: @SuppressWarnings("unchecked")
200: private void internalInit(Hashtable<?, ?> env)
201: throws NamingException {
202:
203: // 1. Read the environment parameter used to create this Context
204: if (null == env) {
205: myProps = new Hashtable<Object, Object>();
206: } else {
207: myProps = (Hashtable<Object, Object>) env.clone();
208: }
209:
210: // 2. Read Applet parameters
211: EnvironmentReader.readAppletParameters(myProps
212: .get(Context.APPLET), myProps);
213:
214: // 3. Read System properties
215: EnvironmentReader.readSystemProperties(myProps);
216:
217: // 4.1 Read application/applet resource files
218: ClassLoader cl = Thread.currentThread().getContextClassLoader();
219: if (propsCache.containsKey(cl)) {
220: EnvironmentReader.mergeEnvironment(propsCache.get(cl),
221: myProps, true);
222: } else {
223: Hashtable<Object, Object> appProps = new Hashtable<Object, Object>();
224: EnvironmentReader.readApplicationResourceFiles(appProps);
225: propsCache.put(cl, appProps);
226: EnvironmentReader.mergeEnvironment(appProps, myProps, true);
227: }
228:
229: // 4.2 Read "java.home"/lib/jndi.properties
230: if (libProperties == null) {
231: libProperties = new Hashtable<Object, Object>();
232: EnvironmentReader.readLibraryResourceFile(libProperties);
233: }
234:
235: EnvironmentReader
236: .mergeEnvironment(libProperties, myProps, true);
237:
238: // 5. No need to read service provider resource files
239:
240: // if JNDI standard property "java.naming.factory.initial" has a
241: // non-null value
242: if (myProps.containsKey(INITIAL_CONTEXT_FACTORY)) {
243: // call getDefaultInitCtx() to initialize gotDefault and
244: // defaultInitCtx
245: getDefaultInitCtx();
246: }
247:
248: }
249:
250: /**
251: * Uses the specified environment parameter together with other JNDI
252: * properties to initialize this <code>InitialContext</code> object. The
253: * <code>myProps</code> field will be filled with found JNDI properties.
254: * If JNDI standard property "java.naming.factory.initial" has a non-null
255: * value, then <code>getDefaultInitCtx</code> is invoked to try to
256: * initialize fields <code>gotDefault</code> and
257: * <code>defaultInitCtx</code> of the <code>InitialContext</code>
258: * object.
259: *
260: * @param env
261: * the JNDI environment properties supplied to create the context
262: * @throws NamingException
263: * If naming problems are encountered during initialization of
264: * these fields.
265: */
266: protected void init(Hashtable<?, ?> env) throws NamingException {
267: this .internalInit(env);
268: }
269:
270: /*
271: * Initializes the default initial context.
272: *
273: * @throws NamingException If failed to initialize this InitialContext.
274: */
275: private void initializeDefaultInitCtx() throws NamingException {
276: if (!this .gotDefault) {
277: this .defaultInitCtx = NamingManager
278: .getInitialContext(myProps);
279: if (null == this .defaultInitCtx) {
280: throw new NoInitialContextException(
281: "Failed to create an initial context."); //$NON-NLS-1$
282: }
283: this .gotDefault = true;
284: }
285: }
286:
287: /**
288: * Gets the default underlying <code>Context</code> implementation. If
289: * <code>gotDefault</code> is true, returns the value of <code>
290: * defaultInitCtx</code>.
291: * Otherwise, calls <code>NamingManager.getInitialContext
292: * </code> to return
293: * an initial context for the current environment into
294: * <code>defaultInitCtx</code>, then <code>gotDefault</code> is set
295: * true. If the resulting context object is null, a
296: * <code>NoInitialContextException
297: * </code> is thrown, otherwise the value
298: * of <code>defaultInitCtx</code> is returned.
299: *
300: * @return the default context
301: * @throws NoInitialContextException
302: * If <code>NamingManager.getInitialContext</code> returns
303: * null.
304: * @throws NamingException
305: * If failed to create the default context.
306: */
307: protected Context getDefaultInitCtx() throws NamingException {
308: initializeDefaultInitCtx();
309: return this .defaultInitCtx;
310: }
311:
312: /**
313: * Returns a non-null context for the specified name of Name representation.
314: * <p>
315: * If an initial context factory builder has been defined, then the
316: * specified <code>Name</code> parameter is ignored and the result of
317: * <code>
318: * getDefaultInitCtx</code> is returned. Otherwise, if the first
319: * component of the name is not a URL string, then it returns the result of
320: * invoking <code>getDefaultInitCtx</code>. Otherwise, it attempts to
321: * return a URL context
322: * {@link javax.naming.spi.NamingManager#getURLContext(String, Hashtable)},
323: * but if unsuccessful, returns the result of invoking
324: * <code>getDefaultInitCtx</code>.
325: * </p>
326: *
327: * @param name
328: * a name used in a naming operation which may not be null
329: * @return a context which may be a URL context
330: * @throws NamingException
331: * If failed to get the desired context.
332: */
333: protected Context getURLOrDefaultInitCtx(Name name)
334: throws NamingException {
335: // If the name has components
336: if (0 < name.size()) {
337: return getURLOrDefaultInitCtx(name.get(0));
338: }
339: return getDefaultInitCtx();
340: }
341:
342: /**
343: * Returns a non-null context for the specified name of string
344: * representation.
345: * <p>
346: * If an initial context factory builder has been defined, then the
347: * specified name parameter is ignored and the result of <code>
348: * getDefaultInitCtx</code>
349: * is returned. Otherwise, if the name is not a URL string, then it returns
350: * the result of invoking <code>getDefaultInitCtx
351: * </code>. Otherwise, it
352: * attempts to return a URL context
353: * {@link javax.naming.spi.NamingManager#getURLContext(String, Hashtable)},
354: * but if unsuccessful, returns the result of invoking <code>
355: * getDefaultInitCtx</code>.
356: * </p>
357: *
358: * @param name
359: * a name used in a naming operation which may not be null
360: * @return a context which may be a URL context
361: * @throws NamingException
362: * If failed to get the desired context.
363: */
364: protected Context getURLOrDefaultInitCtx(String name)
365: throws NamingException {
366:
367: /*
368: * If an initial context factory builder has been defined, then the
369: * specified name parameter is ignored and the result of
370: * getDefaultInitCtx() is returned.
371: */
372: if (NamingManager.hasInitialContextFactoryBuilder()) {
373: return getDefaultInitCtx();
374: }
375:
376: if (null == name) {
377: // jndi.00=name must not be null
378: throw new NullPointerException(Messages
379: .getString("jndi.00")); //$NON-NLS-1$
380: }
381:
382: // If the name has components
383: String scheme = UrlParser.getScheme(name);
384: Context ctx = null;
385: if (null != scheme) {
386: synchronized (contextCache) {
387: if (contextCache.containsKey(scheme)) {
388: return contextCache.get(scheme);
389: }
390:
391: // So the first component is a valid URL
392: ctx = NamingManager.getURLContext(scheme, myProps);
393: if (null == ctx) {
394: ctx = getDefaultInitCtx();
395: }
396: contextCache.put(scheme, ctx);
397: }
398: return ctx;
399: }
400: return getDefaultInitCtx();
401: }
402:
403: public Object lookup(Name name) throws NamingException {
404: return getURLOrDefaultInitCtx(name).lookup(name);
405: }
406:
407: public Object lookup(String name) throws NamingException {
408: return getURLOrDefaultInitCtx(name).lookup(name);
409: }
410:
411: public void bind(Name name, Object obj) throws NamingException {
412: getURLOrDefaultInitCtx(name).bind(name, obj);
413: }
414:
415: public void bind(String name, Object obj) throws NamingException {
416: getURLOrDefaultInitCtx(name).bind(name, obj);
417: }
418:
419: public void rebind(Name name, Object obj) throws NamingException {
420: getURLOrDefaultInitCtx(name).rebind(name, obj);
421: }
422:
423: public void rebind(String name, Object obj) throws NamingException {
424: getURLOrDefaultInitCtx(name).rebind(name, obj);
425: }
426:
427: public void unbind(Name name) throws NamingException {
428: getURLOrDefaultInitCtx(name).unbind(name);
429: }
430:
431: public void unbind(String name) throws NamingException {
432: getURLOrDefaultInitCtx(name).unbind(name);
433: }
434:
435: public void rename(Name oldName, Name newName)
436: throws NamingException {
437: getURLOrDefaultInitCtx(oldName).rename(oldName, newName);
438: }
439:
440: public void rename(String oldName, String newName)
441: throws NamingException {
442: getURLOrDefaultInitCtx(oldName).rename(oldName, newName);
443: }
444:
445: public NamingEnumeration<NameClassPair> list(Name name)
446: throws NamingException {
447: return getURLOrDefaultInitCtx(name).list(name);
448: }
449:
450: public NamingEnumeration<NameClassPair> list(String name)
451: throws NamingException {
452: return getURLOrDefaultInitCtx(name).list(name);
453: }
454:
455: public NamingEnumeration<Binding> listBindings(Name name)
456: throws NamingException {
457: return getURLOrDefaultInitCtx(name).listBindings(name);
458: }
459:
460: public NamingEnumeration<Binding> listBindings(String name)
461: throws NamingException {
462: return getURLOrDefaultInitCtx(name).listBindings(name);
463: }
464:
465: public void destroySubcontext(Name name) throws NamingException {
466: getURLOrDefaultInitCtx(name).destroySubcontext(name);
467: }
468:
469: public void destroySubcontext(String name) throws NamingException {
470: getURLOrDefaultInitCtx(name).destroySubcontext(name);
471: }
472:
473: public Context createSubcontext(Name name) throws NamingException {
474: return getURLOrDefaultInitCtx(name).createSubcontext(name);
475: }
476:
477: public Context createSubcontext(String name) throws NamingException {
478: return getURLOrDefaultInitCtx(name).createSubcontext(name);
479: }
480:
481: public Object lookupLink(Name name) throws NamingException {
482: return getURLOrDefaultInitCtx(name).lookupLink(name);
483: }
484:
485: public Object lookupLink(String name) throws NamingException {
486: return getURLOrDefaultInitCtx(name).lookupLink(name);
487: }
488:
489: public NameParser getNameParser(Name name) throws NamingException {
490: return getURLOrDefaultInitCtx(name).getNameParser(name);
491: }
492:
493: public NameParser getNameParser(String name) throws NamingException {
494: return getURLOrDefaultInitCtx(name).getNameParser(name);
495: }
496:
497: /**
498: * Combines two names into a composite name according to the syntax for this
499: * context. The name <code>prefix</code> is expected to be the name of one
500: * or more of the immediate parent contexts of this context, so should be an
501: * empty name for an <code>InitialContext</code>. <code>name</code> is
502: * a name relative to this context. Neither <code>prefix</code> nor
503: * <code>name</code> may be null.
504: *
505: * @param name
506: * a <code>Name</code>, may not be null
507: * @param prefix
508: * a <code>Name</code> serves as prefix, may not be null
509: * @return the combined name
510: * @throws NamingException
511: * if an error occurs.
512: */
513: public Name composeName(Name name, Name prefix)
514: throws NamingException {
515: if (null == name) {
516: throw new NullPointerException();
517: }
518: return (Name) name.clone();
519: }
520:
521: /**
522: * Combines two names into a composite name according to the syntax for this
523: * context. The name <code>prefix</code> is expected to be the name of one
524: * or more of the immediate parent contexts of this context, so should be an
525: * empty string for an <code>InitialContext</code>. <code>name</code>
526: * is a name relative to this context.
527: *
528: * @param name
529: * a <code>Name</code>, may not be null
530: * @param prefix
531: * a <code>Name</code> serves as prefix, may not be null
532: * @return the combined name
533: * @throws NamingException
534: * if an error occurs.
535: */
536: public String composeName(String name, String prefix)
537: throws NamingException {
538: return name;
539: }
540:
541: public Object addToEnvironment(String propName, Object propVal)
542: throws NamingException {
543: synchronized (contextCache) {
544: myProps.put(propName, propVal);
545: contextCache.clear();
546: }
547: return getDefaultInitCtx().addToEnvironment(propName, propVal);
548: }
549:
550: public Object removeFromEnvironment(String propName)
551: throws NamingException {
552: synchronized (contextCache) {
553: myProps.remove(propName);
554: contextCache.clear();
555: }
556: return getDefaultInitCtx().removeFromEnvironment(propName);
557: }
558:
559: public Hashtable<?, ?> getEnvironment() throws NamingException {
560: return getDefaultInitCtx().getEnvironment();
561: }
562:
563: public void close() throws NamingException {
564: if (this .gotDefault) {
565: getDefaultInitCtx().close();
566: }
567: }
568:
569: public String getNameInNamespace() throws NamingException {
570: return getDefaultInitCtx().getNameInNamespace();
571: }
572:
573: private HashMap<String, Context> contextCache = new HashMap<String, Context>();
574: }
|