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