1/*
2 * Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package javax.naming;
27
28import java.util.Hashtable;
29import javax.naming.spi.NamingManager;
30import com.sun.naming.internal.ResourceManager;
31
32/**
33 * This class is the starting context for performing naming operations.
34 *<p>
35 * All naming operations are relative to a context.
36 * The initial context implements the Context interface and
37 * provides the starting point for resolution of names.
38 *<p>
39 * <a id=ENVIRONMENT></a>
40 * When the initial context is constructed, its environment
41 * is initialized with properties defined in the environment parameter
42 * passed to the constructor, and in any
43 * <a href=Context.html#RESOURCEFILES>application resource files</a>.
44 *<p>
45 * JNDI determines each property's value by merging
46 * the values from the following two sources, in order:
47 * <ol>
48 * <li>
49 * The first occurrence of the property from the constructor's
50 * environment parameter and system properties.
51 * <li>
52 * The application resource files ({@code jndi.properties}).
53 * </ol>
54 * For each property found in both of these two sources, or in
55 * more than one application resource file, the property's value
56 * is determined as follows.  If the property is
57 * one of the standard JNDI properties that specify a list of JNDI
58 * factories (see <a href=Context.html#LISTPROPS>{@code Context}</a>),
59 * all of the values are
60 * concatenated into a single colon-separated list.  For other
61 * properties, only the first value found is used.
62 *
63 *<p>
64 * The initial context implementation is determined at runtime.
65 * The default policy uses the environment property
66 * "{@link Context#INITIAL_CONTEXT_FACTORY java.naming.factory.initial}",
67 * which contains the class name of the initial context factory.
68 * An exception to this policy is made when resolving URL strings, as described
69 * below.
70 *<p>
71 * When a URL string (a {@code String} of the form
72 * <em>scheme_id:rest_of_name</em>) is passed as a name parameter to
73 * any method, a URL context factory for handling that scheme is
74 * located and used to resolve the URL.  If no such factory is found,
75 * the initial context specified by
76 * {@code "java.naming.factory.initial"} is used.  Similarly, when a
77 * {@code CompositeName} object whose first component is a URL string is
78 * passed as a name parameter to any method, a URL context factory is
79 * located and used to resolve the first name component.
80 * See {@link NamingManager#getURLContext
81 * NamingManager.getURLContext()} for a description of how URL
82 * context factories are located.
83 *<p>
84 * This default policy of locating the initial context and URL context
85 * factories may be overridden
86 * by calling
87 * {@code NamingManager.setInitialContextFactoryBuilder()}.
88 *<p>
89 * NoInitialContextException is thrown when an initial context cannot
90 * be instantiated. This exception can be thrown during any interaction
91 * with the InitialContext, not only when the InitialContext is constructed.
92 * For example, the implementation of the initial context might lazily
93 * retrieve the context only when actual methods are invoked on it.
94 * The application should not have any dependency on when the existence
95 * of an initial context is determined.
96 *<p>
97 * When the environment property "java.naming.factory.initial" is
98 * non-null, the InitialContext constructor will attempt to create the
99 * initial context specified therein. At that time, the initial context factory
100 * involved might throw an exception if a problem is encountered. However,
101 * it is provider implementation-dependent when it verifies and indicates
102 * to the users of the initial context any environment property- or
103 * connection- related problems. It can do so lazily--delaying until
104 * an operation is performed on the context, or eagerly, at the time
105 * the context is constructed.
106 *<p>
107 * An InitialContext instance is not synchronized against concurrent
108 * access by multiple threads. Multiple threads each manipulating a
109 * different InitialContext instance need not synchronize.
110 * Threads that need to access a single InitialContext instance
111 * concurrently should synchronize amongst themselves and provide the
112 * necessary locking.
113 *
114 * @author Rosanna Lee
115 * @author Scott Seligman
116 *
117 * @see Context
118 * @see NamingManager#setInitialContextFactoryBuilder
119 *      NamingManager.setInitialContextFactoryBuilder
120 * @since 1.3, JNDI 1.1
121 */
122
123public class InitialContext implements Context {
124
125    /**
126     * The environment associated with this InitialContext.
127     * It is initialized to null and is updated by the constructor
128     * that accepts an environment or by the {@code init()} method.
129     * @see #addToEnvironment
130     * @see #removeFromEnvironment
131     * @see #getEnvironment
132     */
133    protected Hashtable<Object,Object> myProps = null;
134
135    /**
136     * Field holding the result of calling NamingManager.getInitialContext().
137     * It is set by getDefaultInitCtx() the first time getDefaultInitCtx()
138     * is called. Subsequent invocations of getDefaultInitCtx() return
139     * the value of defaultInitCtx.
140     * @see #getDefaultInitCtx
141     */
142    protected Context defaultInitCtx = null;
143
144    /**
145     * Field indicating whether the initial context has been obtained
146     * by calling NamingManager.getInitialContext().
147     * If true, its result is in <code>defaultInitCtx</code>.
148     */
149    protected boolean gotDefault = false;
150
151    /**
152     * Constructs an initial context with the option of not
153     * initializing it.  This may be used by a constructor in
154     * a subclass when the value of the environment parameter
155     * is not yet known at the time the {@code InitialContext}
156     * constructor is called.  The subclass's constructor will
157     * call this constructor, compute the value of the environment,
158     * and then call {@code init()} before returning.
159     *
160     * @param lazy
161     *          true means do not initialize the initial context; false
162     *          is equivalent to calling {@code new InitialContext()}
163     * @throws  NamingException if a naming exception is encountered
164     *
165     * @see #init(Hashtable)
166     * @since 1.3
167     */
168    protected InitialContext(boolean lazy) throws NamingException {
169        if (!lazy) {
170            init(null);
171        }
172    }
173
174    /**
175     * Constructs an initial context.
176     * No environment properties are supplied.
177     * Equivalent to {@code new InitialContext(null)}.
178     *
179     * @throws  NamingException if a naming exception is encountered
180     *
181     * @see #InitialContext(Hashtable)
182     */
183    public InitialContext() throws NamingException {
184        init(null);
185    }
186
187    /**
188     * Constructs an initial context using the supplied environment.
189     * Environment properties are discussed in the class description.
190     *
191     * <p> This constructor will not modify {@code environment}
192     * or save a reference to it, but may save a clone.
193     * Caller should not modify mutable keys and values in
194     * {@code environment} after it has been passed to the constructor.
195     *
196     * @param environment
197     *          environment used to create the initial context.
198     *          Null indicates an empty environment.
199     *
200     * @throws  NamingException if a naming exception is encountered
201     */
202    public InitialContext(Hashtable<?,?> environment)
203        throws NamingException
204    {
205        if (environment != null) {
206            environment = (Hashtable)environment.clone();
207        }
208        init(environment);
209    }
210
211    /**
212     * Initializes the initial context using the supplied environment.
213     * Environment properties are discussed in the class description.
214     *
215     * <p> This method will modify {@code environment} and save
216     * a reference to it.  The caller may no longer modify it.
217     *
218     * @param environment
219     *          environment used to create the initial context.
220     *          Null indicates an empty environment.
221     *
222     * @throws  NamingException if a naming exception is encountered
223     *
224     * @see #InitialContext(boolean)
225     * @since 1.3
226     */
227    @SuppressWarnings("unchecked")
228    protected void init(Hashtable<?,?> environment)
229        throws NamingException
230    {
231        myProps = (Hashtable<Object,Object>)
232                ResourceManager.getInitialEnvironment(environment);
233
234        if (myProps.get(Context.INITIAL_CONTEXT_FACTORY) != null) {
235            // user has specified initial context factory; try to get it
236            getDefaultInitCtx();
237        }
238    }
239
240    /**
241     * A static method to retrieve the named object.
242     * This is a shortcut method equivalent to invoking:
243     * <p>
244     * <code>
245     *        InitialContext ic = new InitialContext();
246     *        Object obj = ic.lookup();
247     * </code>
248     * <p> If {@code name} is empty, returns a new instance of this context
249     * (which represents the same naming context as this context, but its
250     * environment may be modified independently and it may be accessed
251     * concurrently).
252     *
253     * @param <T> the type of the returned object
254     * @param name
255     *          the name of the object to look up
256     * @return  the object bound to {@code name}
257     * @throws  NamingException if a naming exception is encountered
258     *
259     * @see #doLookup(String)
260     * @see #lookup(Name)
261     * @since 1.6
262     */
263    @SuppressWarnings("unchecked")
264    public static <T> T doLookup(Name name)
265        throws NamingException {
266        return (T) (new InitialContext()).lookup(name);
267    }
268
269   /**
270     * A static method to retrieve the named object.
271     * See {@link #doLookup(Name)} for details.
272     * @param <T> the type of the returned object
273     * @param name
274     *          the name of the object to look up
275     * @return  the object bound to {@code name}
276     * @throws  NamingException if a naming exception is encountered
277     * @since 1.6
278     */
279    @SuppressWarnings("unchecked")
280    public static <T> T doLookup(String name)
281        throws NamingException {
282        return (T) (new InitialContext()).lookup(name);
283    }
284
285    private static String getURLScheme(String str) {
286        int colon_posn = str.indexOf(':');
287        int slash_posn = str.indexOf('/');
288
289        if (colon_posn > 0 && (slash_posn == -1 || colon_posn < slash_posn))
290            return str.substring(0, colon_posn);
291        return null;
292    }
293
294    /**
295     * Retrieves the initial context by calling
296     * <code>NamingManager.getInitialContext()</code>
297     * and cache it in defaultInitCtx.
298     * Set <code>gotDefault</code> so that we know we've tried this before.
299     * @return The non-null cached initial context.
300     * @exception NoInitialContextException If cannot find an initial context.
301     * @exception NamingException If a naming exception was encountered.
302     */
303    protected Context getDefaultInitCtx() throws NamingException{
304        if (!gotDefault) {
305            defaultInitCtx = NamingManager.getInitialContext(myProps);
306            gotDefault = true;
307        }
308        if (defaultInitCtx == null)
309            throw new NoInitialContextException();
310
311        return defaultInitCtx;
312    }
313
314    /**
315     * Retrieves a context for resolving the string name <code>name</code>.
316     * If <code>name</code> name is a URL string, then attempt
317     * to find a URL context for it. If none is found, or if
318     * <code>name</code> is not a URL string, then return
319     * <code>getDefaultInitCtx()</code>.
320     *<p>
321     * See getURLOrDefaultInitCtx(Name) for description
322     * of how a subclass should use this method.
323     * @param name The non-null name for which to get the context.
324     * @return A URL context for <code>name</code> or the cached
325     *         initial context. The result cannot be null.
326     * @exception NoInitialContextException If cannot find an initial context.
327     * @exception NamingException In a naming exception is encountered.
328     * @see javax.naming.spi.NamingManager#getURLContext
329     */
330    protected Context getURLOrDefaultInitCtx(String name)
331        throws NamingException {
332        if (NamingManager.hasInitialContextFactoryBuilder()) {
333            return getDefaultInitCtx();
334        }
335        String scheme = getURLScheme(name);
336        if (scheme != null) {
337            Context ctx = NamingManager.getURLContext(scheme, myProps);
338            if (ctx != null) {
339                return ctx;
340            }
341        }
342        return getDefaultInitCtx();
343    }
344
345    /**
346     * Retrieves a context for resolving <code>name</code>.
347     * If the first component of <code>name</code> name is a URL string,
348     * then attempt to find a URL context for it. If none is found, or if
349     * the first component of <code>name</code> is not a URL string,
350     * then return <code>getDefaultInitCtx()</code>.
351     *<p>
352     * When creating a subclass of InitialContext, use this method as
353     * follows.
354     * Define a new method that uses this method to get an initial
355     * context of the desired subclass.
356     * <blockquote><pre>
357     * protected XXXContext getURLOrDefaultInitXXXCtx(Name name)
358     * throws NamingException {
359     *  Context answer = getURLOrDefaultInitCtx(name);
360     *  if (!(answer instanceof XXXContext)) {
361     *    if (answer == null) {
362     *      throw new NoInitialContextException();
363     *    } else {
364     *      throw new NotContextException("Not an XXXContext");
365     *    }
366     *  }
367     *  return (XXXContext)answer;
368     * }
369     * </pre></blockquote>
370     * When providing implementations for the new methods in the subclass,
371     * use this newly defined method to get the initial context.
372     * <blockquote><pre>
373     * public Object XXXMethod1(Name name, ...) {
374     *  throws NamingException {
375     *    return getURLOrDefaultInitXXXCtx(name).XXXMethod1(name, ...);
376     * }
377     * </pre></blockquote>
378     *
379     * @param name The non-null name for which to get the context.
380     * @return A URL context for <code>name</code> or the cached
381     *         initial context. The result cannot be null.
382     * @exception NoInitialContextException If cannot find an initial context.
383     * @exception NamingException In a naming exception is encountered.
384     *
385     * @see javax.naming.spi.NamingManager#getURLContext
386     */
387    protected Context getURLOrDefaultInitCtx(Name name)
388        throws NamingException {
389        if (NamingManager.hasInitialContextFactoryBuilder()) {
390            return getDefaultInitCtx();
391        }
392        if (name.size() > 0) {
393            String first = name.get(0);
394            String scheme = getURLScheme(first);
395            if (scheme != null) {
396                Context ctx = NamingManager.getURLContext(scheme, myProps);
397                if (ctx != null) {
398                    return ctx;
399                }
400            }
401        }
402        return getDefaultInitCtx();
403    }
404
405// Context methods
406// Most Javadoc is deferred to the Context interface.
407
408    public Object lookup(String name) throws NamingException {
409        return getURLOrDefaultInitCtx(name).lookup(name);
410    }
411
412    public Object lookup(Name name) throws NamingException {
413        return getURLOrDefaultInitCtx(name).lookup(name);
414    }
415
416    public void bind(String name, Object obj) throws NamingException {
417        getURLOrDefaultInitCtx(name).bind(name, obj);
418    }
419
420    public void bind(Name name, Object obj) throws NamingException {
421        getURLOrDefaultInitCtx(name).bind(name, obj);
422    }
423
424    public void rebind(String name, Object obj) throws NamingException {
425        getURLOrDefaultInitCtx(name).rebind(name, obj);
426    }
427
428    public void rebind(Name name, Object obj) throws NamingException {
429        getURLOrDefaultInitCtx(name).rebind(name, obj);
430    }
431
432    public void unbind(String name) throws NamingException  {
433        getURLOrDefaultInitCtx(name).unbind(name);
434    }
435
436    public void unbind(Name name) throws NamingException  {
437        getURLOrDefaultInitCtx(name).unbind(name);
438    }
439
440    public void rename(String oldName, String newName) throws NamingException {
441        getURLOrDefaultInitCtx(oldName).rename(oldName, newName);
442    }
443
444    public void rename(Name oldName, Name newName)
445        throws NamingException
446    {
447        getURLOrDefaultInitCtx(oldName).rename(oldName, newName);
448    }
449
450    public NamingEnumeration<NameClassPair> list(String name)
451        throws NamingException
452    {
453        return (getURLOrDefaultInitCtx(name).list(name));
454    }
455
456    public NamingEnumeration<NameClassPair> list(Name name)
457        throws NamingException
458    {
459        return (getURLOrDefaultInitCtx(name).list(name));
460    }
461
462    public NamingEnumeration<Binding> listBindings(String name)
463            throws NamingException  {
464        return getURLOrDefaultInitCtx(name).listBindings(name);
465    }
466
467    public NamingEnumeration<Binding> listBindings(Name name)
468            throws NamingException  {
469        return getURLOrDefaultInitCtx(name).listBindings(name);
470    }
471
472    public void destroySubcontext(String name) throws NamingException  {
473        getURLOrDefaultInitCtx(name).destroySubcontext(name);
474    }
475
476    public void destroySubcontext(Name name) throws NamingException  {
477        getURLOrDefaultInitCtx(name).destroySubcontext(name);
478    }
479
480    public Context createSubcontext(String name) throws NamingException  {
481        return getURLOrDefaultInitCtx(name).createSubcontext(name);
482    }
483
484    public Context createSubcontext(Name name) throws NamingException  {
485        return getURLOrDefaultInitCtx(name).createSubcontext(name);
486    }
487
488    public Object lookupLink(String name) throws NamingException  {
489        return getURLOrDefaultInitCtx(name).lookupLink(name);
490    }
491
492    public Object lookupLink(Name name) throws NamingException {
493        return getURLOrDefaultInitCtx(name).lookupLink(name);
494    }
495
496    public NameParser getNameParser(String name) throws NamingException {
497        return getURLOrDefaultInitCtx(name).getNameParser(name);
498    }
499
500    public NameParser getNameParser(Name name) throws NamingException {
501        return getURLOrDefaultInitCtx(name).getNameParser(name);
502    }
503
504    /**
505     * Composes the name of this context with a name relative to
506     * this context.
507     * Since an initial context may never be named relative
508     * to any context other than itself, the value of the
509     * {@code prefix} parameter must be an empty name ({@code ""}).
510     */
511    public String composeName(String name, String prefix)
512            throws NamingException {
513        return name;
514    }
515
516    /**
517     * Composes the name of this context with a name relative to
518     * this context.
519     * Since an initial context may never be named relative
520     * to any context other than itself, the value of the
521     * {@code prefix} parameter must be an empty name.
522     */
523    public Name composeName(Name name, Name prefix)
524        throws NamingException
525    {
526        return (Name)name.clone();
527    }
528
529    public Object addToEnvironment(String propName, Object propVal)
530            throws NamingException {
531        myProps.put(propName, propVal);
532        return getDefaultInitCtx().addToEnvironment(propName, propVal);
533    }
534
535    public Object removeFromEnvironment(String propName)
536            throws NamingException {
537        myProps.remove(propName);
538        return getDefaultInitCtx().removeFromEnvironment(propName);
539    }
540
541    public Hashtable<?,?> getEnvironment() throws NamingException {
542        return getDefaultInitCtx().getEnvironment();
543    }
544
545    public void close() throws NamingException {
546        myProps = null;
547        if (defaultInitCtx != null) {
548            defaultInitCtx.close();
549            defaultInitCtx = null;
550        }
551        gotDefault = false;
552    }
553
554    public String getNameInNamespace() throws NamingException {
555        return getDefaultInitCtx().getNameInNamespace();
556    }
557};
558