1 /*
   2  * Copyright (c) 1999, 2020, 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.spi;
  27 
  28 import java.net.MalformedURLException;
  29 import java.security.AccessController;
  30 import java.security.PrivilegedAction;
  31 import java.util.*;
  32 
  33 import javax.naming.*;
  34 import com.sun.naming.internal.VersionHelper;
  35 import com.sun.naming.internal.ResourceManager;
  36 import com.sun.naming.internal.FactoryEnumeration;
  37 import jdk.internal.loader.ClassLoaderValue;
  38 
  39 /**
  40  * This class contains methods for creating context objects
  41  * and objects referred to by location information in the naming
  42  * or directory service.
  43  *<p>
  44  * This class cannot be instantiated.  It has only static methods.
  45  *<p>
  46  * The mention of URL in the documentation for this class refers to
  47  * a URL string as defined by RFC 1738 and its related RFCs. It is
  48  * any string that conforms to the syntax described therein, and
  49  * may not always have corresponding support in the java.net.URL
  50  * class or Web browsers.
  51  *<p>
  52  * NamingManager is safe for concurrent access by multiple threads.
  53  *<p>
  54  * Except as otherwise noted,
  55  * a {@code Name} or environment parameter
  56  * passed to any method is owned by the caller.
  57  * The implementation will not modify the object or keep a reference
  58  * to it, although it may keep a reference to a clone or copy.
  59  *
  60  * @author Rosanna Lee
  61  * @author Scott Seligman
  62  * @since 1.3
  63  */
  64 
  65 public class NamingManager {
  66 
  67     /*
  68      * Disallow anyone from creating one of these.
  69      * Made package private so that DirectoryManager can subclass.
  70      */
  71 
  72     NamingManager() {}
  73 
  74     // should be protected and package private
  75     static final VersionHelper helper = VersionHelper.getVersionHelper();
  76 
  77 // --------- object factory stuff
  78 
  79     /**
  80      * Package-private; used by DirectoryManager and NamingManager.
  81      */
  82     private static ObjectFactoryBuilder object_factory_builder = null;
  83 
  84     private static final ClassLoaderValue<InitialContextFactory> FACTORIES_CACHE =
  85             new ClassLoaderValue<>();
  86 
  87     /**
  88      * The ObjectFactoryBuilder determines the policy used when
  89      * trying to load object factories.
  90      * See getObjectInstance() and class ObjectFactory for a description
  91      * of the default policy.
  92      * setObjectFactoryBuilder() overrides this default policy by installing
  93      * an ObjectFactoryBuilder. Subsequent object factories will
  94      * be loaded and created using the installed builder.
  95      *<p>
  96      * The builder can only be installed if the executing thread is allowed
  97      * (by the security manager's checkSetFactory() method) to do so.
  98      * Once installed, the builder cannot be replaced.
  99      *
 100      * @param builder The factory builder to install. If null, no builder
 101      *                  is installed.
 102      * @exception SecurityException builder cannot be installed
 103      *          for security reasons.
 104      * @exception NamingException builder cannot be installed for
 105      *         a non-security-related reason.
 106      * @exception IllegalStateException If a factory has already been installed.
 107      * @see #getObjectInstance
 108      * @see ObjectFactory
 109      * @see ObjectFactoryBuilder
 110      * @see java.lang.SecurityManager#checkSetFactory
 111      */
 112     public static synchronized void setObjectFactoryBuilder(
 113             ObjectFactoryBuilder builder) throws NamingException {
 114         if (object_factory_builder != null)
 115             throw new IllegalStateException("ObjectFactoryBuilder already set");
 116 
 117         SecurityManager security = System.getSecurityManager();
 118         if (security != null) {
 119             security.checkSetFactory();
 120         }
 121         object_factory_builder = builder;
 122     }
 123 
 124     /**
 125      * Used for accessing object factory builder.
 126      */
 127     static synchronized ObjectFactoryBuilder getObjectFactoryBuilder() {
 128         return object_factory_builder;
 129     }
 130 
 131 
 132     /**
 133      * Retrieves the ObjectFactory for the object identified by a reference,
 134      * using the reference's factory class name and factory codebase
 135      * to load in the factory's class.
 136      * @param ref The non-null reference to use.
 137      * @param factoryName The non-null class name of the factory.
 138      * @return The object factory for the object identified by ref; null
 139      * if unable to load the factory.
 140      */
 141     static ObjectFactory getObjectFactoryFromReference(
 142         Reference ref, String factoryName)
 143         throws IllegalAccessException,
 144         InstantiationException,
 145         MalformedURLException {
 146         Class<?> clas = null;
 147 
 148         // Try to use current class loader
 149         try {
 150              clas = helper.loadClass(factoryName);
 151         } catch (ClassNotFoundException e) {
 152             // ignore and continue
 153             // e.printStackTrace();
 154         }
 155         // All other exceptions are passed up.
 156 
 157         // Not in class path; try to use codebase
 158         String codebase;
 159         if (clas == null &&
 160                 (codebase = ref.getFactoryClassLocation()) != null) {
 161             try {
 162                 clas = helper.loadClass(factoryName, codebase);
 163             } catch (ClassNotFoundException e) {
 164             }
 165         }
 166 
 167         @SuppressWarnings("deprecation") // Class.newInstance
 168         ObjectFactory result = (clas != null) ? (ObjectFactory) clas.newInstance() : null;
 169         return result;
 170     }
 171 
 172 
 173     /**
 174      * Creates an object using the factories specified in the
 175      * {@code Context.OBJECT_FACTORIES} property of the environment
 176      * or of the provider resource file associated with {@code nameCtx}.
 177      *
 178      * @return factory created; null if cannot create
 179      */
 180     private static Object createObjectFromFactories(Object obj, Name name,
 181             Context nameCtx, Hashtable<?,?> environment) throws Exception {
 182 
 183         FactoryEnumeration factories = ResourceManager.getFactories(
 184             Context.OBJECT_FACTORIES, environment, nameCtx);
 185 
 186         if (factories == null)
 187             return null;
 188 
 189         // Try each factory until one succeeds
 190         ObjectFactory factory;
 191         Object answer = null;
 192         while (answer == null && factories.hasMore()) {
 193             factory = (ObjectFactory)factories.next();
 194             answer = factory.getObjectInstance(obj, name, nameCtx, environment);
 195         }
 196         return answer;
 197     }
 198 
 199     private static String getURLScheme(String str) {
 200         int colon_posn = str.indexOf(':');
 201         int slash_posn = str.indexOf('/');
 202 
 203         if (colon_posn > 0 && (slash_posn == -1 || colon_posn < slash_posn))
 204             return str.substring(0, colon_posn);
 205         return null;
 206     }
 207 
 208     /**
 209      * Creates an instance of an object for the specified object
 210      * and environment.
 211      * <p>
 212      * If an object factory builder has been installed, it is used to
 213      * create a factory for creating the object.
 214      * Otherwise, the following rules are used to create the object:
 215      *<ol>
 216      * <li>If {@code refInfo} is a {@code Reference}
 217      *    or {@code Referenceable} containing a factory class name,
 218      *    use the named factory to create the object.
 219      *    Return {@code refInfo} if the factory cannot be created.
 220      *    Under JDK 1.1, if the factory class must be loaded from a location
 221      *    specified in the reference, a {@code SecurityManager} must have
 222      *    been installed or the factory creation will fail.
 223      *    If an exception is encountered while creating the factory,
 224      *    it is passed up to the caller.
 225      * <li>If {@code refInfo} is a {@code Reference} or
 226      *    {@code Referenceable} with no factory class name,
 227      *    and the address or addresses are {@code StringRefAddr}s with
 228      *    address type "URL",
 229      *    try the URL context factory corresponding to each URL's scheme id
 230      *    to create the object (see {@code getURLContext()}).
 231      *    If that fails, continue to the next step.
 232      * <li> Use the object factories specified in
 233      *    the {@code Context.OBJECT_FACTORIES} property of the environment,
 234      *    and of the provider resource file associated with
 235      *    {@code nameCtx}, in that order.
 236      *    The value of this property is a colon-separated list of factory
 237      *    class names that are tried in order, and the first one that succeeds
 238      *    in creating an object is the one used.
 239      *    If none of the factories can be loaded,
 240      *    return {@code refInfo}.
 241      *    If an exception is encountered while creating the object, the
 242      *    exception is passed up to the caller.
 243      *</ol>
 244      *<p>
 245      * Service providers that implement the {@code DirContext}
 246      * interface should use
 247      * {@code DirectoryManager.getObjectInstance()}, not this method.
 248      * Service providers that implement only the {@code Context}
 249      * interface should use this method.
 250      * <p>
 251      * Note that an object factory (an object that implements the ObjectFactory
 252      * interface) must be public and must have a public constructor that
 253      * accepts no arguments.
 254      * In cases where the factory is in a named module then it must be in a
 255      * package which is exported by that module to the {@code java.naming}
 256      * module.
 257      * <p>
 258      * The {@code name} and {@code nameCtx} parameters may
 259      * optionally be used to specify the name of the object being created.
 260      * {@code name} is the name of the object, relative to context
 261      * {@code nameCtx}.  This information could be useful to the object
 262      * factory or to the object implementation.
 263      *  If there are several possible contexts from which the object
 264      *  could be named -- as will often be the case -- it is up to
 265      *  the caller to select one.  A good rule of thumb is to select the
 266      * "deepest" context available.
 267      * If {@code nameCtx} is null, {@code name} is relative
 268      * to the default initial context.  If no name is being specified, the
 269      * {@code name} parameter should be null.
 270      *
 271      * @param refInfo The possibly null object for which to create an object.
 272      * @param name The name of this object relative to {@code nameCtx}.
 273      *          Specifying a name is optional; if it is
 274      *          omitted, {@code name} should be null.
 275      * @param nameCtx The context relative to which the {@code name}
 276      *          parameter is specified.  If null, {@code name} is
 277      *          relative to the default initial context.
 278      * @param environment The possibly null environment to
 279      *          be used in the creation of the object factory and the object.
 280      * @return An object created using {@code refInfo}; or
 281      *          {@code refInfo} if an object cannot be created using
 282      *          the algorithm described above.
 283      * @exception NamingException if a naming exception was encountered
 284      *  while attempting to get a URL context, or if one of the
 285      *          factories accessed throws a NamingException.
 286      * @exception Exception if one of the factories accessed throws an
 287      *          exception, or if an error was encountered while loading
 288      *          and instantiating the factory and object classes.
 289      *          A factory should only throw an exception if it does not want
 290      *          other factories to be used in an attempt to create an object.
 291      *  See ObjectFactory.getObjectInstance().
 292      * @see #getURLContext
 293      * @see ObjectFactory
 294      * @see ObjectFactory#getObjectInstance
 295      */
 296     public static Object
 297         getObjectInstance(Object refInfo, Name name, Context nameCtx,
 298                           Hashtable<?,?> environment)
 299         throws Exception
 300     {
 301 
 302         ObjectFactory factory;
 303 
 304         // Use builder if installed
 305         ObjectFactoryBuilder builder = getObjectFactoryBuilder();
 306         if (builder != null) {
 307             // builder must return non-null factory
 308             factory = builder.createObjectFactory(refInfo, environment);
 309             return factory.getObjectInstance(refInfo, name, nameCtx,
 310                 environment);
 311         }
 312 
 313         // Use reference if possible
 314         Reference ref = null;
 315         if (refInfo instanceof Reference) {
 316             ref = (Reference) refInfo;
 317         } else if (refInfo instanceof Referenceable) {
 318             ref = ((Referenceable)(refInfo)).getReference();
 319         }
 320 
 321         Object answer;
 322 
 323         if (ref != null) {
 324             String f = ref.getFactoryClassName();
 325             if (f != null) {
 326                 // if reference identifies a factory, use exclusively
 327 
 328                 factory = getObjectFactoryFromReference(ref, f);
 329                 if (factory != null) {
 330                     return factory.getObjectInstance(ref, name, nameCtx,
 331                                                      environment);
 332                 }
 333                 // No factory found, so return original refInfo.
 334                 // Will reach this point if factory class is not in
 335                 // class path and reference does not contain a URL for it
 336                 return refInfo;
 337 
 338             } else {
 339                 // if reference has no factory, check for addresses
 340                 // containing URLs
 341 
 342                 answer = processURLAddrs(ref, name, nameCtx, environment);
 343                 if (answer != null) {
 344                     return answer;
 345                 }
 346             }
 347         }
 348 
 349         // try using any specified factories
 350         answer =
 351             createObjectFromFactories(refInfo, name, nameCtx, environment);
 352         return (answer != null) ? answer : refInfo;
 353     }
 354 
 355     /*
 356      * Ref has no factory.  For each address of type "URL", try its URL
 357      * context factory.  Returns null if unsuccessful in creating and
 358      * invoking a factory.
 359      */
 360     static Object processURLAddrs(Reference ref, Name name, Context nameCtx,
 361                                   Hashtable<?,?> environment)
 362             throws NamingException {
 363 
 364         for (int i = 0; i < ref.size(); i++) {
 365             RefAddr addr = ref.get(i);
 366             if (addr instanceof StringRefAddr &&
 367                 addr.getType().equalsIgnoreCase("URL")) {
 368 
 369                 String url = (String)addr.getContent();
 370                 Object answer = processURL(url, name, nameCtx, environment);
 371                 if (answer != null) {
 372                     return answer;
 373                 }
 374             }
 375         }
 376         return null;
 377     }
 378 
 379     private static Object processURL(Object refInfo, Name name,
 380                                      Context nameCtx, Hashtable<?,?> environment)
 381             throws NamingException {
 382         Object answer;
 383 
 384         // If refInfo is a URL string, try to use its URL context factory
 385         // If no context found, continue to try object factories.
 386         if (refInfo instanceof String) {
 387             String url = (String)refInfo;
 388             String scheme = getURLScheme(url);
 389             if (scheme != null) {
 390                 answer = getURLObject(scheme, refInfo, name, nameCtx,
 391                                       environment);
 392                 if (answer != null) {
 393                     return answer;
 394                 }
 395             }
 396         }
 397 
 398         // If refInfo is an array of URL strings,
 399         // try to find a context factory for any one of its URLs.
 400         // If no context found, continue to try object factories.
 401         if (refInfo instanceof String[]) {
 402             String[] urls = (String[])refInfo;
 403             for (int i = 0; i <urls.length; i++) {
 404                 String scheme = getURLScheme(urls[i]);
 405                 if (scheme != null) {
 406                     answer = getURLObject(scheme, refInfo, name, nameCtx,
 407                                           environment);
 408                     if (answer != null)
 409                         return answer;
 410                 }
 411             }
 412         }
 413         return null;
 414     }
 415 
 416 
 417     /**
 418      * Retrieves a context identified by {@code obj}, using the specified
 419      * environment.
 420      * Used by ContinuationContext.
 421      *
 422      * @param obj       The object identifying the context.
 423      * @param name      The name of the context being returned, relative to
 424      *                  {@code nameCtx}, or null if no name is being
 425      *                  specified.
 426      *                  See the {@code getObjectInstance} method for
 427      *                  details.
 428      * @param nameCtx   The context relative to which {@code name} is
 429      *                  specified, or null for the default initial context.
 430      *                  See the {@code getObjectInstance} method for
 431      *                  details.
 432      * @param environment Environment specifying characteristics of the
 433      *                  resulting context.
 434      * @return A context identified by {@code obj}.
 435      *
 436      * @see #getObjectInstance
 437      */
 438     static Context getContext(Object obj, Name name, Context nameCtx,
 439                               Hashtable<?,?> environment) throws NamingException {
 440         Object answer;
 441 
 442         if (obj instanceof Context) {
 443             // %%% Ignore environment for now.  OK since method not public.
 444             return (Context)obj;
 445         }
 446 
 447         try {
 448             answer = getObjectInstance(obj, name, nameCtx, environment);
 449         } catch (NamingException e) {
 450             throw e;
 451         } catch (Exception e) {
 452             NamingException ne = new NamingException();
 453             ne.setRootCause(e);
 454             throw ne;
 455         }
 456 
 457         return (answer instanceof Context)
 458             ? (Context)answer
 459             : null;
 460     }
 461 
 462     // Used by ContinuationContext
 463     static Resolver getResolver(Object obj, Name name, Context nameCtx,
 464                                 Hashtable<?,?> environment) throws NamingException {
 465         Object answer;
 466 
 467         if (obj instanceof Resolver) {
 468             // %%% Ignore environment for now.  OK since method not public.
 469             return (Resolver)obj;
 470         }
 471 
 472         try {
 473             answer = getObjectInstance(obj, name, nameCtx, environment);
 474         } catch (NamingException e) {
 475             throw e;
 476         } catch (Exception e) {
 477             NamingException ne = new NamingException();
 478             ne.setRootCause(e);
 479             throw ne;
 480         }
 481 
 482         return (answer instanceof Resolver)
 483             ? (Resolver)answer
 484             : null;
 485     }
 486 
 487 
 488     /***************** URL Context implementations ***************/
 489 
 490     /**
 491      * Creates a context for the given URL scheme id.
 492      * <p>
 493      * The resulting context is for resolving URLs of the
 494      * scheme {@code scheme}. The resulting context is not tied
 495      * to a specific URL. It is able to handle arbitrary URLs with
 496      * the specified scheme.
 497      *<p>
 498      * The class name of the factory that creates the resulting context
 499      * has the naming convention <i>scheme-id</i>URLContextFactory
 500      * (e.g. "ftpURLContextFactory" for the "ftp" scheme-id),
 501      * in the package specified as follows.
 502      * The {@code Context.URL_PKG_PREFIXES} environment property (which
 503      * may contain values taken from system properties,
 504      * or application resource files)
 505      * contains a colon-separated list of package prefixes.
 506      * Each package prefix in
 507      * the property is tried in the order specified to load the factory class.
 508      * The default package prefix is "com.sun.jndi.url" (if none of the
 509      * specified packages work, this default is tried).
 510      * The complete package name is constructed using the package prefix,
 511      * concatenated with the scheme id.
 512      *<p>
 513      * For example, if the scheme id is "ldap", and the
 514      * {@code Context.URL_PKG_PREFIXES} property
 515      * contains "com.widget:com.wiz.jndi",
 516      * the naming manager would attempt to load the following classes
 517      * until one is successfully instantiated:
 518      *<ul>
 519      * <li>com.widget.ldap.ldapURLContextFactory
 520      *  <li>com.wiz.jndi.ldap.ldapURLContextFactory
 521      *  <li>com.sun.jndi.url.ldap.ldapURLContextFactory
 522      *</ul>
 523      * If none of the package prefixes work, null is returned.
 524      *<p>
 525      * If a factory is instantiated, it is invoked with the following
 526      * parameters to produce the resulting context.
 527      * <p>
 528      * {@code factory.getObjectInstance(null, environment);}
 529      * <p>
 530      * For example, invoking getObjectInstance() as shown above
 531      * on a LDAP URL context factory would return a
 532      * context that can resolve LDAP urls
 533      * (e.g. "ldap://ldap.wiz.com/o=wiz,c=us",
 534      * "ldap://ldap.umich.edu/o=umich,c=us", ...).
 535      *<p>
 536      * Note that an object factory (an object that implements the ObjectFactory
 537      * interface) must be public and must have a public constructor that
 538      * accepts no arguments.
 539      * In cases where the factory is in a named module then it must be in a
 540      * package which is exported by that module to the {@code java.naming}
 541      * module.
 542      *
 543      * @param scheme    The non-null scheme-id of the URLs supported by the context.
 544      * @param environment The possibly null environment properties to be
 545      *           used in the creation of the object factory and the context.
 546      * @return A context for resolving URLs with the
 547      *         scheme id {@code scheme};
 548      *  {@code null} if the factory for creating the
 549      *         context is not found.
 550      * @exception NamingException If a naming exception occurs while creating
 551      *          the context.
 552      * @see #getObjectInstance
 553      * @see ObjectFactory#getObjectInstance
 554      */
 555     public static Context getURLContext(String scheme,
 556                                         Hashtable<?,?> environment)
 557         throws NamingException
 558     {
 559         // pass in 'null' to indicate creation of generic context for scheme
 560         // (i.e. not specific to a URL).
 561 
 562             Object answer = getURLObject(scheme, null, null, null, environment);
 563             if (answer instanceof Context) {
 564                 return (Context)answer;
 565             } else {
 566                 return null;
 567             }
 568     }
 569 
 570     private static final String defaultPkgPrefix = "com.sun.jndi.url";
 571 
 572     /**
 573      * Creates an object for the given URL scheme id using
 574      * the supplied urlInfo.
 575      * <p>
 576      * If urlInfo is null, the result is a context for resolving URLs
 577      * with the scheme id 'scheme'.
 578      * If urlInfo is a URL, the result is a context named by the URL.
 579      * Names passed to this context is assumed to be relative to this
 580      * context (i.e. not a URL). For example, if urlInfo is
 581      * "ldap://ldap.wiz.com/o=Wiz,c=us", the resulting context will
 582      * be that pointed to by "o=Wiz,c=us" on the server 'ldap.wiz.com'.
 583      * Subsequent names that can be passed to this context will be
 584      * LDAP names relative to this context (e.g. cn="Barbs Jensen").
 585      * If urlInfo is an array of URLs, the URLs are assumed
 586      * to be equivalent in terms of the context to which they refer.
 587      * The resulting context is like that of the single URL case.
 588      * If urlInfo is of any other type, that is handled by the
 589      * context factory for the URL scheme.
 590      * @param scheme the URL scheme id for the context
 591      * @param urlInfo information used to create the context
 592      * @param name name of this object relative to {@code nameCtx}
 593      * @param nameCtx Context whose provider resource file will be searched
 594      *          for package prefix values (or null if none)
 595      * @param environment Environment properties for creating the context
 596      * @see javax.naming.InitialContext
 597      */
 598     private static Object getURLObject(String scheme, Object urlInfo,
 599                                        Name name, Context nameCtx,
 600                                        Hashtable<?,?> environment)
 601             throws NamingException {
 602 
 603         // e.g. "ftpURLContextFactory"
 604         ObjectFactory factory = (ObjectFactory)ResourceManager.getFactory(
 605             Context.URL_PKG_PREFIXES, environment, nameCtx,
 606             "." + scheme + "." + scheme + "URLContextFactory", defaultPkgPrefix);
 607 
 608         if (factory == null)
 609           return null;
 610 
 611         // Found object factory
 612         try {
 613             return factory.getObjectInstance(urlInfo, name, nameCtx, environment);
 614         } catch (NamingException e) {
 615             throw e;
 616         } catch (Exception e) {
 617             NamingException ne = new NamingException();
 618             ne.setRootCause(e);
 619             throw ne;
 620         }
 621 
 622     }
 623 
 624 
 625 // ------------ Initial Context Factory Stuff
 626     private static InitialContextFactoryBuilder initctx_factory_builder = null;
 627 
 628     /**
 629      * Use this method for accessing initctx_factory_builder while
 630      * inside an unsynchronized method.
 631      */
 632     private static synchronized InitialContextFactoryBuilder
 633     getInitialContextFactoryBuilder() {
 634         return initctx_factory_builder;
 635     }
 636 
 637     /**
 638      * Creates an initial context using the specified environment
 639      * properties.
 640      * <p>
 641      * This is done as follows:
 642      * <ul>
 643      * <li>If an InitialContextFactoryBuilder has been installed,
 644      *     it is used to create the factory for creating the initial
 645      *     context</li>
 646      * <li>Otherwise, the class specified in the
 647      *     {@code Context.INITIAL_CONTEXT_FACTORY} environment property
 648      *     is used
 649      *     <ul>
 650      *     <li>First, the {@linkplain java.util.ServiceLoader ServiceLoader}
 651      *         mechanism tries to locate an {@code InitialContextFactory}
 652      *         provider using the current thread's context class loader</li>
 653      *     <li>Failing that, this implementation tries to locate a suitable
 654      *         {@code InitialContextFactory} using a built-in mechanism
 655      *         <br>
 656      *         (Note that an initial context factory (an object that implements
 657      *         the InitialContextFactory interface) must be public and must have
 658      *         a public constructor that accepts no arguments.
 659      *         In cases where the factory is in a named module then it must
 660      *         be in a package which is exported by that module to the
 661      *         {@code java.naming} module.)</li>
 662      *     </ul>
 663      * </li>
 664      * </ul>
 665      * @param env The possibly null environment properties used when
 666      *                  creating the context.
 667      * @return A non-null initial context.
 668      * @exception NoInitialContextException If the
 669      *          {@code Context.INITIAL_CONTEXT_FACTORY} property
 670      *         is not found or names a nonexistent
 671      *         class or a class that cannot be instantiated,
 672      *          or if the initial context could not be created for some other
 673      *          reason.
 674      * @exception NamingException If some other naming exception was encountered.
 675      * @see javax.naming.InitialContext
 676      * @see javax.naming.directory.InitialDirContext
 677      */
 678     public static Context getInitialContext(Hashtable<?,?> env)
 679         throws NamingException {
 680         ClassLoader loader;
 681         InitialContextFactory factory = null;
 682 
 683         InitialContextFactoryBuilder builder = getInitialContextFactoryBuilder();
 684         if (builder == null) {
 685             // No builder installed, use property
 686             // Get initial context factory class name
 687 
 688             String className = env != null ?
 689                 (String)env.get(Context.INITIAL_CONTEXT_FACTORY) : null;
 690             if (className == null) {
 691                 NoInitialContextException ne = new NoInitialContextException(
 692                     "Need to specify class name in environment or system " +
 693                     "property, or in an application resource file: " +
 694                     Context.INITIAL_CONTEXT_FACTORY);
 695                 throw ne;
 696             }
 697 
 698             if (System.getSecurityManager() == null) {
 699                 loader = Thread.currentThread().getContextClassLoader();
 700                 if (loader == null) loader = ClassLoader.getSystemClassLoader();
 701             } else {
 702                 PrivilegedAction<ClassLoader> pa = () -> {
 703                     ClassLoader cl = Thread.currentThread().getContextClassLoader();
 704                     return (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
 705                 };
 706                 loader = AccessController.doPrivileged(pa);
 707             }
 708 
 709             var key = FACTORIES_CACHE.sub(className);
 710             try {
 711                 factory = key.computeIfAbsent(loader, (ld, ky) -> getFactory(ky.key()));
 712             } catch (FactoryInitializationError e) {
 713                 throw e.getCause();
 714             }
 715         } else {
 716             factory = builder.createInitialContextFactory(env);
 717         }
 718 
 719         return factory.getInitialContext(env);
 720     }
 721 
 722     private static InitialContextFactory getFactory(String className) {
 723         InitialContextFactory factory;
 724         try {
 725             ServiceLoader<InitialContextFactory> loader =
 726                     ServiceLoader.load(InitialContextFactory.class);
 727 
 728             factory = loader
 729                     .stream()
 730                     .filter(p -> p.type().getName().equals(className))
 731                     .findFirst()
 732                     .map(ServiceLoader.Provider::get)
 733                     .orElse(null);
 734         } catch (ServiceConfigurationError e) {
 735             NoInitialContextException ne =
 736                     new NoInitialContextException(
 737                             "Cannot load initial context factory "
 738                                     + "'" + className + "'");
 739             ne.setRootCause(e);
 740             throw new FactoryInitializationError(ne);
 741         }
 742 
 743         if (factory == null) {
 744             try {
 745                 @SuppressWarnings("deprecation")
 746                 Object o = helper.loadClass(className).newInstance();
 747                 factory = (InitialContextFactory) o;
 748             } catch (Exception e) {
 749                 NoInitialContextException ne =
 750                         new NoInitialContextException(
 751                                 "Cannot instantiate class: " + className);
 752                 ne.setRootCause(e);
 753                 throw new FactoryInitializationError(ne);
 754             }
 755         }
 756         return factory;
 757     }
 758 
 759 
 760     /**
 761      * Sets the InitialContextFactory builder to be builder.
 762      *
 763      *<p>
 764      * The builder can only be installed if the executing thread is allowed by
 765      * the security manager to do so. Once installed, the builder cannot
 766      * be replaced.
 767      * @param builder The initial context factory builder to install. If null,
 768      *                no builder is set.
 769      * @exception SecurityException builder cannot be installed for security
 770      *                  reasons.
 771      * @exception NamingException builder cannot be installed for
 772      *         a non-security-related reason.
 773      * @exception IllegalStateException If a builder was previous installed.
 774      * @see #hasInitialContextFactoryBuilder
 775      * @see java.lang.SecurityManager#checkSetFactory
 776      */
 777     public static synchronized void setInitialContextFactoryBuilder(
 778         InitialContextFactoryBuilder builder)
 779         throws NamingException {
 780             if (initctx_factory_builder != null)
 781                 throw new IllegalStateException(
 782                     "InitialContextFactoryBuilder already set");
 783 
 784             SecurityManager security = System.getSecurityManager();
 785             if (security != null) {
 786                 security.checkSetFactory();
 787             }
 788             initctx_factory_builder = builder;
 789     }
 790 
 791     /**
 792      * Determines whether an initial context factory builder has
 793      * been set.
 794      * @return true if an initial context factory builder has
 795      *           been set; false otherwise.
 796      * @see #setInitialContextFactoryBuilder
 797      */
 798     public static boolean hasInitialContextFactoryBuilder() {
 799         return (getInitialContextFactoryBuilder() != null);
 800     }
 801 
 802 // -----  Continuation Context Stuff
 803 
 804     /**
 805      * Constant that holds the name of the environment property into
 806      * which {@code getContinuationContext()} stores the value of its
 807      * {@code CannotProceedException} parameter.
 808      * This property is inherited by the continuation context, and may
 809      * be used by that context's service provider to inspect the
 810      * fields of the exception.
 811      *<p>
 812      * The value of this constant is "java.naming.spi.CannotProceedException".
 813      *
 814      * @see #getContinuationContext
 815      * @since 1.3
 816      */
 817     public static final String CPE = "java.naming.spi.CannotProceedException";
 818 
 819     /**
 820      * Creates a context in which to continue a context operation.
 821      *<p>
 822      * In performing an operation on a name that spans multiple
 823      * namespaces, a context from one naming system may need to pass
 824      * the operation on to the next naming system.  The context
 825      * implementation does this by first constructing a
 826      * {@code CannotProceedException} containing information
 827      * pinpointing how far it has proceeded.  It then obtains a
 828      * continuation context from JNDI by calling
 829      * {@code getContinuationContext}.  The context
 830      * implementation should then resume the context operation by
 831      * invoking the same operation on the continuation context, using
 832      * the remainder of the name that has not yet been resolved.
 833      *<p>
 834      * Before making use of the {@code cpe} parameter, this method
 835      * updates the environment associated with that object by setting
 836      * the value of the property <a href="#CPE">{@code CPE}</a>
 837      * to {@code cpe}.  This property will be inherited by the
 838      * continuation context, and may be used by that context's
 839      * service provider to inspect the fields of this exception.
 840      *
 841      * @param cpe
 842      *          The non-null exception that triggered this continuation.
 843      * @return A non-null Context object for continuing the operation.
 844      * @exception NamingException If a naming exception occurred.
 845      */
 846     @SuppressWarnings("unchecked")
 847     public static Context getContinuationContext(CannotProceedException cpe)
 848             throws NamingException {
 849 
 850         Hashtable<Object,Object> env = (Hashtable<Object,Object>)cpe.getEnvironment();
 851         if (env == null) {
 852             env = new Hashtable<>(7);
 853         } else {
 854             // Make a (shallow) copy of the environment.
 855             env = (Hashtable<Object,Object>)env.clone();
 856         }
 857         env.put(CPE, cpe);
 858 
 859         ContinuationContext cctx = new ContinuationContext(cpe, env);
 860         return cctx.getTargetContext();
 861     }
 862 
 863 // ------------ State Factory Stuff
 864 
 865     /**
 866      * Retrieves the state of an object for binding.
 867      * <p>
 868      * Service providers that implement the {@code DirContext} interface
 869      * should use {@code DirectoryManager.getStateToBind()}, not this method.
 870      * Service providers that implement only the {@code Context} interface
 871      * should use this method.
 872      *<p>
 873      * This method uses the specified state factories in
 874      * the {@code Context.STATE_FACTORIES} property from the environment
 875      * properties, and from the provider resource file associated with
 876      * {@code nameCtx}, in that order.
 877      *    The value of this property is a colon-separated list of factory
 878      *    class names that are tried in order, and the first one that succeeds
 879      *    in returning the object's state is the one used.
 880      * If no object's state can be retrieved in this way, return the
 881      * object itself.
 882      *    If an exception is encountered while retrieving the state, the
 883      *    exception is passed up to the caller.
 884      * <p>
 885      * Note that a state factory
 886      * (an object that implements the StateFactory
 887      * interface) must be public and must have a public constructor that
 888      * accepts no arguments.
 889      * In cases where the factory is in a named module then it must be in a
 890      * package which is exported by that module to the {@code java.naming}
 891      * module.
 892      * <p>
 893      * The {@code name} and {@code nameCtx} parameters may
 894      * optionally be used to specify the name of the object being created.
 895      * See the description of "Name and Context Parameters" in
 896      * {@link ObjectFactory#getObjectInstance
 897      *          ObjectFactory.getObjectInstance()}
 898      * for details.
 899      * <p>
 900      * This method may return a {@code Referenceable} object.  The
 901      * service provider obtaining this object may choose to store it
 902      * directly, or to extract its reference (using
 903      * {@code Referenceable.getReference()}) and store that instead.
 904      *
 905      * @param obj The non-null object for which to get state to bind.
 906      * @param name The name of this object relative to {@code nameCtx},
 907      *          or null if no name is specified.
 908      * @param nameCtx The context relative to which the {@code name}
 909      *          parameter is specified, or null if {@code name} is
 910      *          relative to the default initial context.
 911      *  @param environment The possibly null environment to
 912      *          be used in the creation of the state factory and
 913      *  the object's state.
 914      * @return The non-null object representing {@code obj}'s state for
 915      *  binding.  It could be the object ({@code obj}) itself.
 916      * @exception NamingException If one of the factories accessed throws an
 917      *          exception, or if an error was encountered while loading
 918      *          and instantiating the factory and object classes.
 919      *          A factory should only throw an exception if it does not want
 920      *          other factories to be used in an attempt to create an object.
 921      *  See {@code StateFactory.getStateToBind()}.
 922      * @see StateFactory
 923      * @see StateFactory#getStateToBind
 924      * @see DirectoryManager#getStateToBind
 925      * @since 1.3
 926      */
 927     public static Object
 928         getStateToBind(Object obj, Name name, Context nameCtx,
 929                        Hashtable<?,?> environment)
 930         throws NamingException
 931     {
 932 
 933         FactoryEnumeration factories = ResourceManager.getFactories(
 934             Context.STATE_FACTORIES, environment, nameCtx);
 935 
 936         if (factories == null) {
 937             return obj;
 938         }
 939 
 940         // Try each factory until one succeeds
 941         StateFactory factory;
 942         Object answer = null;
 943         while (answer == null && factories.hasMore()) {
 944             factory = (StateFactory)factories.next();
 945             answer = factory.getStateToBind(obj, name, nameCtx, environment);
 946         }
 947 
 948         return (answer != null) ? answer : obj;
 949     }
 950 
 951     private static class FactoryInitializationError extends Error {
 952         static final long serialVersionUID = -5805552256848841560L;
 953 
 954         private FactoryInitializationError(NoInitialContextException cause) {
 955             super(cause);
 956         }
 957 
 958         @Override
 959         public NoInitialContextException getCause() {
 960             return (NoInitialContextException) super.getCause();
 961         }
 962     }
 963 }