diff -r 4ebc2e2fb97c -r 71c04702a3d5 src/java.naming/share/classes/javax/naming/spi/NamingManager.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.naming/share/classes/javax/naming/spi/NamingManager.java Tue Sep 12 19:03:39 2017 +0200 @@ -0,0 +1,924 @@ +/* + * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package javax.naming.spi; + +import java.net.MalformedURLException; +import java.util.*; + + +import javax.naming.*; +import com.sun.naming.internal.VersionHelper; +import com.sun.naming.internal.ResourceManager; +import com.sun.naming.internal.FactoryEnumeration; + +/** + * This class contains methods for creating context objects + * and objects referred to by location information in the naming + * or directory service. + *

+ * This class cannot be instantiated. It has only static methods. + *

+ * The mention of URL in the documentation for this class refers to + * a URL string as defined by RFC 1738 and its related RFCs. It is + * any string that conforms to the syntax described therein, and + * may not always have corresponding support in the java.net.URL + * class or Web browsers. + *

+ * NamingManager is safe for concurrent access by multiple threads. + *

+ * Except as otherwise noted, + * a {@code Name} or environment parameter + * passed to any method is owned by the caller. + * The implementation will not modify the object or keep a reference + * to it, although it may keep a reference to a clone or copy. + * + * @author Rosanna Lee + * @author Scott Seligman + * @since 1.3 + */ + +public class NamingManager { + + /* + * Disallow anyone from creating one of these. + * Made package private so that DirectoryManager can subclass. + */ + + NamingManager() {} + + // should be protected and package private + static final VersionHelper helper = VersionHelper.getVersionHelper(); + +// --------- object factory stuff + + /** + * Package-private; used by DirectoryManager and NamingManager. + */ + private static ObjectFactoryBuilder object_factory_builder = null; + + /** + * The ObjectFactoryBuilder determines the policy used when + * trying to load object factories. + * See getObjectInstance() and class ObjectFactory for a description + * of the default policy. + * setObjectFactoryBuilder() overrides this default policy by installing + * an ObjectFactoryBuilder. Subsequent object factories will + * be loaded and created using the installed builder. + *

+ * The builder can only be installed if the executing thread is allowed + * (by the security manager's checkSetFactory() method) to do so. + * Once installed, the builder cannot be replaced. + * + * @param builder The factory builder to install. If null, no builder + * is installed. + * @exception SecurityException builder cannot be installed + * for security reasons. + * @exception NamingException builder cannot be installed for + * a non-security-related reason. + * @exception IllegalStateException If a factory has already been installed. + * @see #getObjectInstance + * @see ObjectFactory + * @see ObjectFactoryBuilder + * @see java.lang.SecurityManager#checkSetFactory + */ + public static synchronized void setObjectFactoryBuilder( + ObjectFactoryBuilder builder) throws NamingException { + if (object_factory_builder != null) + throw new IllegalStateException("ObjectFactoryBuilder already set"); + + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkSetFactory(); + } + object_factory_builder = builder; + } + + /** + * Used for accessing object factory builder. + */ + static synchronized ObjectFactoryBuilder getObjectFactoryBuilder() { + return object_factory_builder; + } + + + /** + * Retrieves the ObjectFactory for the object identified by a reference, + * using the reference's factory class name and factory codebase + * to load in the factory's class. + * @param ref The non-null reference to use. + * @param factoryName The non-null class name of the factory. + * @return The object factory for the object identified by ref; null + * if unable to load the factory. + */ + static ObjectFactory getObjectFactoryFromReference( + Reference ref, String factoryName) + throws IllegalAccessException, + InstantiationException, + MalformedURLException { + Class clas = null; + + // Try to use current class loader + try { + clas = helper.loadClass(factoryName); + } catch (ClassNotFoundException e) { + // ignore and continue + // e.printStackTrace(); + } + // All other exceptions are passed up. + + // Not in class path; try to use codebase + String codebase; + if (clas == null && + (codebase = ref.getFactoryClassLocation()) != null) { + try { + clas = helper.loadClass(factoryName, codebase); + } catch (ClassNotFoundException e) { + } + } + + @SuppressWarnings("deprecation") // Class.newInstance + ObjectFactory result = (clas != null) ? (ObjectFactory) clas.newInstance() : null; + return result; + } + + + /** + * Creates an object using the factories specified in the + * {@code Context.OBJECT_FACTORIES} property of the environment + * or of the provider resource file associated with {@code nameCtx}. + * + * @return factory created; null if cannot create + */ + private static Object createObjectFromFactories(Object obj, Name name, + Context nameCtx, Hashtable environment) throws Exception { + + FactoryEnumeration factories = ResourceManager.getFactories( + Context.OBJECT_FACTORIES, environment, nameCtx); + + if (factories == null) + return null; + + // Try each factory until one succeeds + ObjectFactory factory; + Object answer = null; + while (answer == null && factories.hasMore()) { + factory = (ObjectFactory)factories.next(); + answer = factory.getObjectInstance(obj, name, nameCtx, environment); + } + return answer; + } + + private static String getURLScheme(String str) { + int colon_posn = str.indexOf(':'); + int slash_posn = str.indexOf('/'); + + if (colon_posn > 0 && (slash_posn == -1 || colon_posn < slash_posn)) + return str.substring(0, colon_posn); + return null; + } + + /** + * Creates an instance of an object for the specified object + * and environment. + *

+ * If an object factory builder has been installed, it is used to + * create a factory for creating the object. + * Otherwise, the following rules are used to create the object: + *

    + *
  1. If {@code refInfo} is a {@code Reference} + * or {@code Referenceable} containing a factory class name, + * use the named factory to create the object. + * Return {@code refInfo} if the factory cannot be created. + * Under JDK 1.1, if the factory class must be loaded from a location + * specified in the reference, a {@code SecurityManager} must have + * been installed or the factory creation will fail. + * If an exception is encountered while creating the factory, + * it is passed up to the caller. + *
  2. If {@code refInfo} is a {@code Reference} or + * {@code Referenceable} with no factory class name, + * and the address or addresses are {@code StringRefAddr}s with + * address type "URL", + * try the URL context factory corresponding to each URL's scheme id + * to create the object (see {@code getURLContext()}). + * If that fails, continue to the next step. + *
  3. Use the object factories specified in + * the {@code Context.OBJECT_FACTORIES} property of the environment, + * and of the provider resource file associated with + * {@code nameCtx}, in that order. + * The value of this property is a colon-separated list of factory + * class names that are tried in order, and the first one that succeeds + * in creating an object is the one used. + * If none of the factories can be loaded, + * return {@code refInfo}. + * If an exception is encountered while creating the object, the + * exception is passed up to the caller. + *
+ *

+ * Service providers that implement the {@code DirContext} + * interface should use + * {@code DirectoryManager.getObjectInstance()}, not this method. + * Service providers that implement only the {@code Context} + * interface should use this method. + *

+ * Note that an object factory (an object that implements the ObjectFactory + * interface) must be public and must have a public constructor that + * accepts no arguments. + * In cases where the factory is in a named module then it must be in a + * package which is exported by that module to the {@code java.naming} + * module. + *

+ * The {@code name} and {@code nameCtx} parameters may + * optionally be used to specify the name of the object being created. + * {@code name} is the name of the object, relative to context + * {@code nameCtx}. This information could be useful to the object + * factory or to the object implementation. + * If there are several possible contexts from which the object + * could be named -- as will often be the case -- it is up to + * the caller to select one. A good rule of thumb is to select the + * "deepest" context available. + * If {@code nameCtx} is null, {@code name} is relative + * to the default initial context. If no name is being specified, the + * {@code name} parameter should be null. + * + * @param refInfo The possibly null object for which to create an object. + * @param name The name of this object relative to {@code nameCtx}. + * Specifying a name is optional; if it is + * omitted, {@code name} should be null. + * @param nameCtx The context relative to which the {@code name} + * parameter is specified. If null, {@code name} is + * relative to the default initial context. + * @param environment The possibly null environment to + * be used in the creation of the object factory and the object. + * @return An object created using {@code refInfo}; or + * {@code refInfo} if an object cannot be created using + * the algorithm described above. + * @exception NamingException if a naming exception was encountered + * while attempting to get a URL context, or if one of the + * factories accessed throws a NamingException. + * @exception Exception if one of the factories accessed throws an + * exception, or if an error was encountered while loading + * and instantiating the factory and object classes. + * A factory should only throw an exception if it does not want + * other factories to be used in an attempt to create an object. + * See ObjectFactory.getObjectInstance(). + * @see #getURLContext + * @see ObjectFactory + * @see ObjectFactory#getObjectInstance + */ + public static Object + getObjectInstance(Object refInfo, Name name, Context nameCtx, + Hashtable environment) + throws Exception + { + + ObjectFactory factory; + + // Use builder if installed + ObjectFactoryBuilder builder = getObjectFactoryBuilder(); + if (builder != null) { + // builder must return non-null factory + factory = builder.createObjectFactory(refInfo, environment); + return factory.getObjectInstance(refInfo, name, nameCtx, + environment); + } + + // Use reference if possible + Reference ref = null; + if (refInfo instanceof Reference) { + ref = (Reference) refInfo; + } else if (refInfo instanceof Referenceable) { + ref = ((Referenceable)(refInfo)).getReference(); + } + + Object answer; + + if (ref != null) { + String f = ref.getFactoryClassName(); + if (f != null) { + // if reference identifies a factory, use exclusively + + factory = getObjectFactoryFromReference(ref, f); + if (factory != null) { + return factory.getObjectInstance(ref, name, nameCtx, + environment); + } + // No factory found, so return original refInfo. + // Will reach this point if factory class is not in + // class path and reference does not contain a URL for it + return refInfo; + + } else { + // if reference has no factory, check for addresses + // containing URLs + + answer = processURLAddrs(ref, name, nameCtx, environment); + if (answer != null) { + return answer; + } + } + } + + // try using any specified factories + answer = + createObjectFromFactories(refInfo, name, nameCtx, environment); + return (answer != null) ? answer : refInfo; + } + + /* + * Ref has no factory. For each address of type "URL", try its URL + * context factory. Returns null if unsuccessful in creating and + * invoking a factory. + */ + static Object processURLAddrs(Reference ref, Name name, Context nameCtx, + Hashtable environment) + throws NamingException { + + for (int i = 0; i < ref.size(); i++) { + RefAddr addr = ref.get(i); + if (addr instanceof StringRefAddr && + addr.getType().equalsIgnoreCase("URL")) { + + String url = (String)addr.getContent(); + Object answer = processURL(url, name, nameCtx, environment); + if (answer != null) { + return answer; + } + } + } + return null; + } + + private static Object processURL(Object refInfo, Name name, + Context nameCtx, Hashtable environment) + throws NamingException { + Object answer; + + // If refInfo is a URL string, try to use its URL context factory + // If no context found, continue to try object factories. + if (refInfo instanceof String) { + String url = (String)refInfo; + String scheme = getURLScheme(url); + if (scheme != null) { + answer = getURLObject(scheme, refInfo, name, nameCtx, + environment); + if (answer != null) { + return answer; + } + } + } + + // If refInfo is an array of URL strings, + // try to find a context factory for any one of its URLs. + // If no context found, continue to try object factories. + if (refInfo instanceof String[]) { + String[] urls = (String[])refInfo; + for (int i = 0; i environment) throws NamingException { + Object answer; + + if (obj instanceof Context) { + // %%% Ignore environment for now. OK since method not public. + return (Context)obj; + } + + try { + answer = getObjectInstance(obj, name, nameCtx, environment); + } catch (NamingException e) { + throw e; + } catch (Exception e) { + NamingException ne = new NamingException(); + ne.setRootCause(e); + throw ne; + } + + return (answer instanceof Context) + ? (Context)answer + : null; + } + + // Used by ContinuationContext + static Resolver getResolver(Object obj, Name name, Context nameCtx, + Hashtable environment) throws NamingException { + Object answer; + + if (obj instanceof Resolver) { + // %%% Ignore environment for now. OK since method not public. + return (Resolver)obj; + } + + try { + answer = getObjectInstance(obj, name, nameCtx, environment); + } catch (NamingException e) { + throw e; + } catch (Exception e) { + NamingException ne = new NamingException(); + ne.setRootCause(e); + throw ne; + } + + return (answer instanceof Resolver) + ? (Resolver)answer + : null; + } + + + /***************** URL Context implementations ***************/ + + /** + * Creates a context for the given URL scheme id. + *

+ * The resulting context is for resolving URLs of the + * scheme {@code scheme}. The resulting context is not tied + * to a specific URL. It is able to handle arbitrary URLs with + * the specified scheme. + *

+ * The class name of the factory that creates the resulting context + * has the naming convention scheme-idURLContextFactory + * (e.g. "ftpURLContextFactory" for the "ftp" scheme-id), + * in the package specified as follows. + * The {@code Context.URL_PKG_PREFIXES} environment property (which + * may contain values taken from system properties, + * or application resource files) + * contains a colon-separated list of package prefixes. + * Each package prefix in + * the property is tried in the order specified to load the factory class. + * The default package prefix is "com.sun.jndi.url" (if none of the + * specified packages work, this default is tried). + * The complete package name is constructed using the package prefix, + * concatenated with the scheme id. + *

+ * For example, if the scheme id is "ldap", and the + * {@code Context.URL_PKG_PREFIXES} property + * contains "com.widget:com.wiz.jndi", + * the naming manager would attempt to load the following classes + * until one is successfully instantiated: + *

+ * If none of the package prefixes work, null is returned. + *

+ * If a factory is instantiated, it is invoked with the following + * parameters to produce the resulting context. + *

+ * {@code factory.getObjectInstance(null, environment);} + *

+ * For example, invoking getObjectInstance() as shown above + * on a LDAP URL context factory would return a + * context that can resolve LDAP urls + * (e.g. "ldap://ldap.wiz.com/o=wiz,c=us", + * "ldap://ldap.umich.edu/o=umich,c=us", ...). + *

+ * Note that an object factory (an object that implements the ObjectFactory + * interface) must be public and must have a public constructor that + * accepts no arguments. + * In cases where the factory is in a named module then it must be in a + * package which is exported by that module to the {@code java.naming} + * module. + * + * @param scheme The non-null scheme-id of the URLs supported by the context. + * @param environment The possibly null environment properties to be + * used in the creation of the object factory and the context. + * @return A context for resolving URLs with the + * scheme id {@code scheme}; + * {@code null} if the factory for creating the + * context is not found. + * @exception NamingException If a naming exception occurs while creating + * the context. + * @see #getObjectInstance + * @see ObjectFactory#getObjectInstance + */ + public static Context getURLContext(String scheme, + Hashtable environment) + throws NamingException + { + // pass in 'null' to indicate creation of generic context for scheme + // (i.e. not specific to a URL). + + Object answer = getURLObject(scheme, null, null, null, environment); + if (answer instanceof Context) { + return (Context)answer; + } else { + return null; + } + } + + private static final String defaultPkgPrefix = "com.sun.jndi.url"; + + /** + * Creates an object for the given URL scheme id using + * the supplied urlInfo. + *

+ * If urlInfo is null, the result is a context for resolving URLs + * with the scheme id 'scheme'. + * If urlInfo is a URL, the result is a context named by the URL. + * Names passed to this context is assumed to be relative to this + * context (i.e. not a URL). For example, if urlInfo is + * "ldap://ldap.wiz.com/o=Wiz,c=us", the resulting context will + * be that pointed to by "o=Wiz,c=us" on the server 'ldap.wiz.com'. + * Subsequent names that can be passed to this context will be + * LDAP names relative to this context (e.g. cn="Barbs Jensen"). + * If urlInfo is an array of URLs, the URLs are assumed + * to be equivalent in terms of the context to which they refer. + * The resulting context is like that of the single URL case. + * If urlInfo is of any other type, that is handled by the + * context factory for the URL scheme. + * @param scheme the URL scheme id for the context + * @param urlInfo information used to create the context + * @param name name of this object relative to {@code nameCtx} + * @param nameCtx Context whose provider resource file will be searched + * for package prefix values (or null if none) + * @param environment Environment properties for creating the context + * @see javax.naming.InitialContext + */ + private static Object getURLObject(String scheme, Object urlInfo, + Name name, Context nameCtx, + Hashtable environment) + throws NamingException { + + // e.g. "ftpURLContextFactory" + ObjectFactory factory = (ObjectFactory)ResourceManager.getFactory( + Context.URL_PKG_PREFIXES, environment, nameCtx, + "." + scheme + "." + scheme + "URLContextFactory", defaultPkgPrefix); + + if (factory == null) + return null; + + // Found object factory + try { + return factory.getObjectInstance(urlInfo, name, nameCtx, environment); + } catch (NamingException e) { + throw e; + } catch (Exception e) { + NamingException ne = new NamingException(); + ne.setRootCause(e); + throw ne; + } + + } + + +// ------------ Initial Context Factory Stuff + private static InitialContextFactoryBuilder initctx_factory_builder = null; + + /** + * Use this method for accessing initctx_factory_builder while + * inside an unsynchronized method. + */ + private static synchronized InitialContextFactoryBuilder + getInitialContextFactoryBuilder() { + return initctx_factory_builder; + } + + /** + * Creates an initial context using the specified environment + * properties. + *

+ * This is done as follows: + *

+ * @param env The possibly null environment properties used when + * creating the context. + * @return A non-null initial context. + * @exception NoInitialContextException If the + * {@code Context.INITIAL_CONTEXT_FACTORY} property + * is not found or names a nonexistent + * class or a class that cannot be instantiated, + * or if the initial context could not be created for some other + * reason. + * @exception NamingException If some other naming exception was encountered. + * @see javax.naming.InitialContext + * @see javax.naming.directory.InitialDirContext + */ + public static Context getInitialContext(Hashtable env) + throws NamingException { + InitialContextFactory factory = null; + + InitialContextFactoryBuilder builder = getInitialContextFactoryBuilder(); + if (builder == null) { + // No builder installed, use property + // Get initial context factory class name + + String className = env != null ? + (String)env.get(Context.INITIAL_CONTEXT_FACTORY) : null; + if (className == null) { + NoInitialContextException ne = new NoInitialContextException( + "Need to specify class name in environment or system " + + "property, or in an application resource file: " + + Context.INITIAL_CONTEXT_FACTORY); + throw ne; + } + + ServiceLoader loader = + ServiceLoader.load(InitialContextFactory.class); + + Iterator iterator = loader.iterator(); + try { + while (iterator.hasNext()) { + InitialContextFactory f = iterator.next(); + if (f.getClass().getName().equals(className)) { + factory = f; + break; + } + } + } catch (ServiceConfigurationError e) { + NoInitialContextException ne = + new NoInitialContextException( + "Cannot load initial context factory " + + "'" + className + "'"); + ne.setRootCause(e); + throw ne; + } + + if (factory == null) { + try { + @SuppressWarnings("deprecation") + Object o = helper.loadClass(className).newInstance(); + factory = (InitialContextFactory) o; + } catch (Exception e) { + NoInitialContextException ne = + new NoInitialContextException( + "Cannot instantiate class: " + className); + ne.setRootCause(e); + throw ne; + } + } + } else { + factory = builder.createInitialContextFactory(env); + } + + return factory.getInitialContext(env); + } + + + /** + * Sets the InitialContextFactory builder to be builder. + * + *

+ * The builder can only be installed if the executing thread is allowed by + * the security manager to do so. Once installed, the builder cannot + * be replaced. + * @param builder The initial context factory builder to install. If null, + * no builder is set. + * @exception SecurityException builder cannot be installed for security + * reasons. + * @exception NamingException builder cannot be installed for + * a non-security-related reason. + * @exception IllegalStateException If a builder was previous installed. + * @see #hasInitialContextFactoryBuilder + * @see java.lang.SecurityManager#checkSetFactory + */ + public static synchronized void setInitialContextFactoryBuilder( + InitialContextFactoryBuilder builder) + throws NamingException { + if (initctx_factory_builder != null) + throw new IllegalStateException( + "InitialContextFactoryBuilder already set"); + + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkSetFactory(); + } + initctx_factory_builder = builder; + } + + /** + * Determines whether an initial context factory builder has + * been set. + * @return true if an initial context factory builder has + * been set; false otherwise. + * @see #setInitialContextFactoryBuilder + */ + public static boolean hasInitialContextFactoryBuilder() { + return (getInitialContextFactoryBuilder() != null); + } + +// ----- Continuation Context Stuff + + /** + * Constant that holds the name of the environment property into + * which {@code getContinuationContext()} stores the value of its + * {@code CannotProceedException} parameter. + * This property is inherited by the continuation context, and may + * be used by that context's service provider to inspect the + * fields of the exception. + *

+ * The value of this constant is "java.naming.spi.CannotProceedException". + * + * @see #getContinuationContext + * @since 1.3 + */ + public static final String CPE = "java.naming.spi.CannotProceedException"; + + /** + * Creates a context in which to continue a context operation. + *

+ * In performing an operation on a name that spans multiple + * namespaces, a context from one naming system may need to pass + * the operation on to the next naming system. The context + * implementation does this by first constructing a + * {@code CannotProceedException} containing information + * pinpointing how far it has proceeded. It then obtains a + * continuation context from JNDI by calling + * {@code getContinuationContext}. The context + * implementation should then resume the context operation by + * invoking the same operation on the continuation context, using + * the remainder of the name that has not yet been resolved. + *

+ * Before making use of the {@code cpe} parameter, this method + * updates the environment associated with that object by setting + * the value of the property {@code CPE} + * to {@code cpe}. This property will be inherited by the + * continuation context, and may be used by that context's + * service provider to inspect the fields of this exception. + * + * @param cpe + * The non-null exception that triggered this continuation. + * @return A non-null Context object for continuing the operation. + * @exception NamingException If a naming exception occurred. + */ + @SuppressWarnings("unchecked") + public static Context getContinuationContext(CannotProceedException cpe) + throws NamingException { + + Hashtable env = (Hashtable)cpe.getEnvironment(); + if (env == null) { + env = new Hashtable<>(7); + } else { + // Make a (shallow) copy of the environment. + env = (Hashtable)env.clone(); + } + env.put(CPE, cpe); + + ContinuationContext cctx = new ContinuationContext(cpe, env); + return cctx.getTargetContext(); + } + +// ------------ State Factory Stuff + + /** + * Retrieves the state of an object for binding. + *

+ * Service providers that implement the {@code DirContext} interface + * should use {@code DirectoryManager.getStateToBind()}, not this method. + * Service providers that implement only the {@code Context} interface + * should use this method. + *

+ * This method uses the specified state factories in + * the {@code Context.STATE_FACTORIES} property from the environment + * properties, and from the provider resource file associated with + * {@code nameCtx}, in that order. + * The value of this property is a colon-separated list of factory + * class names that are tried in order, and the first one that succeeds + * in returning the object's state is the one used. + * If no object's state can be retrieved in this way, return the + * object itself. + * If an exception is encountered while retrieving the state, the + * exception is passed up to the caller. + *

+ * Note that a state factory + * (an object that implements the StateFactory + * interface) must be public and must have a public constructor that + * accepts no arguments. + * In cases where the factory is in a named module then it must be in a + * package which is exported by that module to the {@code java.naming} + * module. + *

+ * The {@code name} and {@code nameCtx} parameters may + * optionally be used to specify the name of the object being created. + * See the description of "Name and Context Parameters" in + * {@link ObjectFactory#getObjectInstance + * ObjectFactory.getObjectInstance()} + * for details. + *

+ * This method may return a {@code Referenceable} object. The + * service provider obtaining this object may choose to store it + * directly, or to extract its reference (using + * {@code Referenceable.getReference()}) and store that instead. + * + * @param obj The non-null object for which to get state to bind. + * @param name The name of this object relative to {@code nameCtx}, + * or null if no name is specified. + * @param nameCtx The context relative to which the {@code name} + * parameter is specified, or null if {@code name} is + * relative to the default initial context. + * @param environment The possibly null environment to + * be used in the creation of the state factory and + * the object's state. + * @return The non-null object representing {@code obj}'s state for + * binding. It could be the object ({@code obj}) itself. + * @exception NamingException If one of the factories accessed throws an + * exception, or if an error was encountered while loading + * and instantiating the factory and object classes. + * A factory should only throw an exception if it does not want + * other factories to be used in an attempt to create an object. + * See {@code StateFactory.getStateToBind()}. + * @see StateFactory + * @see StateFactory#getStateToBind + * @see DirectoryManager#getStateToBind + * @since 1.3 + */ + public static Object + getStateToBind(Object obj, Name name, Context nameCtx, + Hashtable environment) + throws NamingException + { + + FactoryEnumeration factories = ResourceManager.getFactories( + Context.STATE_FACTORIES, environment, nameCtx); + + if (factories == null) { + return obj; + } + + // Try each factory until one succeeds + StateFactory factory; + Object answer = null; + while (answer == null && factories.hasMore()) { + factory = (StateFactory)factories.next(); + answer = factory.getStateToBind(obj, name, nameCtx, environment); + } + + return (answer != null) ? answer : obj; + } +}