/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package javax.naming.spi; import java.net.URL; import java.net.URLClassLoader; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Enumeration; import java.util.Hashtable; import java.util.StringTokenizer; import javax.naming.Binding; import javax.naming.CannotProceedException; import javax.naming.Context; import javax.naming.Name; import javax.naming.NameClassPair; import javax.naming.NameParser; import javax.naming.NamingEnumeration; import javax.naming.NamingException; import javax.naming.RefAddr; import javax.naming.Reference; import javax.naming.Referenceable; import javax.naming.StringRefAddr; import javax.naming.directory.Attributes; import javax.naming.directory.DirContext; import javax.naming.directory.ModificationItem; import javax.naming.directory.SearchControls; import javax.naming.directory.SearchResult; import javax.naming.spi.DirStateFactory.Result; import org.apache.harmony.jndi.internal.EnvironmentReader; import org.apache.harmony.jndi.internal.UrlParser; import org.apache.harmony.jndi.internal.nls.Messages; /** * The <code>DirectoryManager</code> class cannot be instantiated. All its * methods are static. The methods are used by service providers for accessing * object and state factories and for determining continuation contexts. * <p> * The <code>Name</code> and <code>Hashtable</code> arguments passed to the * <code>DirectoryManager</code> methods remain owned purely by the calling * method. They must not be changed or referenced. * </p> * <p> * Multithreaded access to this class must be safe. * </p> */ public class DirectoryManager extends NamingManager { // Private to prevent it being instanced private DirectoryManager() { } /** * Create an object using an object factory. Returns a new * <code>Object</code> or the supplied <code>Object o</code> if one * cannot be created. * * The behaviour is like that for the <code>getObjectInstance</code> * method of <code>NamingManager</code> however it should be noted that * the intermediate object factory may be either of type * <code>DirObjectFactory</code> or of type <code>ObjectFactory</code>; * in the former case, the supplied <code>Attributes</code> must be passed * when getting the object, otherwise the supplied <code>Attributes</code> * are ignored. * * @param o * An object which may provide reference or location information. * May be null. * @param n * The name of the <code>Object</code> relative to the default * initial context (or relative to the Context c if it is * supplied) * @param c * The <code>Context</code> to which the Name is relative * @param h * a <code>Hashtable</code> containing environment properties * and values - may be null * @param a * <code>Attributes</code> - if some or all of the attributes * of <code>Object o</code> are already known they can be * supplied so that the factory does not have to do the work of * looking them up. * @return the created object * @throws NamingException * if one is encountered * @throws Exception * if any other exception is encountered */ public static Object getObjectInstance(Object o, Name n, Context c, Hashtable<?, ?> h, Attributes a) throws Exception { // 1. try ObjectFactoryBuilder, if it is set if (null != ofb) { // use the builder to create an object factory ObjectFactory factory = ofb.createObjectFactory(o, h); // get object instance using the factory and return return getObjectInstanceFromGivenFactory(factory, o, n, c, h, a); } // 2. see whether o is a Referenceable or a Reference Reference ref = null; if (o instanceof Referenceable) { ref = ((Referenceable) o).getReference(); } if (o instanceof Reference) { ref = (Reference) o; } // if o is a Referenceable or a Reference if (null != ref) { // if a factory class name is supplied by the reference, use it to // create if (null != ref.getFactoryClassName()) { return getObjectInstanceByFactoryInReference(ref, o, n, c, h, a); } // see if ref has any StringRefAddrs of address type URL, Object result = getObjectInstanceByUrlRefAddr(n, c, h, ref); // if success, return it if (null != result) { return result; } } // 3. try Context.OBJECT_FACTORIES Object result = getObjectInstanceByObjectFactory(o, n, c, h, a); if (null != result) { return result; } // all failed, just return o return o; } /** * Check the type of factory, DirObjectFactory or ObjectFactory, and call * getObjectInstance() on the property type. */ private static Object getObjectInstanceFromGivenFactory( ObjectFactory factory, Object o, Name n, Context c, Hashtable<?, ?> h, Attributes a) throws Exception { if (factory instanceof DirObjectFactory) { return ((DirObjectFactory) factory) .getObjectInstance(o, n, c, h, a); } return factory.getObjectInstance(o, n, c, h); } private static Object getObjectInstanceByObjectFactory(Object o, Name n, Context c, Hashtable<?, ?> h, Attributes a) throws NamingException, Exception { // obtain object factories from hashtable and service provider resource // file String fnames[] = EnvironmentReader .getFactoryNamesFromEnvironmentAndProviderResource(h, c, Context.OBJECT_FACTORIES); for (String element : fnames) { // new factory instance by its class name ObjectFactory factory = null; try { factory = (ObjectFactory) classForName(element).newInstance(); } catch (Exception e) { continue; } // create object using factory Object obj = getObjectInstanceFromGivenFactory(factory, o, n, c, h, a); if (null != obj) { return obj; } } // no object factory succeeded, return null return null; } private static Object getObjectInstanceByUrlRefAddr(Name n, Context c, Hashtable<?, ?> h, Reference ref) throws NamingException { // obtain pkg prefixes from hashtable and service provider resource file String pkgPrefixes[] = EnvironmentReader .getFactoryNamesFromEnvironmentAndProviderResource(h, c, Context.URL_PKG_PREFIXES); // for each RefAddr Enumeration<RefAddr> enumeration = ref.getAll(); while (enumeration.hasMoreElements()) { RefAddr addr = enumeration.nextElement(); // if it is StringRefAddr and type is URL if (addr instanceof StringRefAddr && addr.getType().equalsIgnoreCase("URL")) { //$NON-NLS-1$ // get the url address String url = (String) ((StringRefAddr) addr).getContent(); // try create using url context factory Object obj = getObjectInstanceByUrlContextFactory(url, n, c, h, pkgPrefixes, UrlParser.getScheme(url)); // if success, return the created obj if (null != obj) { return obj; } } } // failed to create using any StringRefAddr of address type URL, return // null return null; } private static Object getObjectInstanceByUrlContextFactory(String url, Name n, Context c, Hashtable<?, ?> h, String pkgPrefixes[], String schema) throws NamingException { // if schema is empty or null, fail, return null if (null == schema || 0 == schema.length()) { return null; } for (String element : pkgPrefixes) { ObjectFactory factory = null; try { // create url context factory instance String clsName = element + "." + schema + "." + schema + "URLContextFactory"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ factory = (ObjectFactory) classForName(clsName).newInstance(); } catch (Exception e) { // failed to create factory, continue trying continue; } try { // create obj using url context factory /* * Unit test shows it is ObjectFactory here, not * DirObjectFactory // Object obj = * factory.getObjectInstance(url, n, c, h, a); */ Object obj = factory.getObjectInstance(url, n, c, h); // if create success, return it if (null != obj) { return obj; } } catch (Exception e) { // throw NamingException, if factory fails if (e instanceof NamingException) { throw (NamingException) e; } // jndi.21=Failed to create object instance NamingException nex = new NamingException(Messages .getString("jndi.21")); //$NON-NLS-1$ nex.setRootCause(e); throw nex; } } // fail to create using url context factory, return null return null; } private static Object getObjectInstanceByFactoryInReference(Reference ref, Object o, Name n, Context c, Hashtable<?, ?> h, Attributes a) throws Exception { ObjectFactory factory = null; // try load the factory by its class name try { factory = (ObjectFactory) classForName(ref.getFactoryClassName()) .newInstance(); } catch (ClassNotFoundException e) { // Ignored. } // try load the factory from its class location if (null == factory && null != ref.getFactoryClassLocation()) { factory = (ObjectFactory) loadFactoryFromLocation(ref .getFactoryClassName(), ref.getFactoryClassLocation()); } // if factory cannot be loaded if (null == factory) { // return o return o; } // get object instance using the factory and return it return getObjectInstanceFromGivenFactory(factory, ref, n, c, h, a); } /* * If cannot load class, return null. Throws any exceptions except * ClassNotFoundException */ private static Object loadFactoryFromLocation(String clsName, String location) throws Exception { // convert location into an array of URL, separated by ' ' StringTokenizer st = new StringTokenizer(location, " "); //$NON-NLS-1$ URL urls[] = new URL[st.countTokens()]; for (int i = 0; i < urls.length; i++) { urls[i] = new URL(st.nextToken()); } // new a URLClassLoader from the URLs URLClassLoader l = new URLClassLoader(urls); // try load factory by URLClassLoader try { // return the new instance return l.loadClass(clsName).newInstance(); } catch (ClassNotFoundException e) { // return null if class loading failed return null; } } /** * Get the state of an Object. Returns a <code>DirStateFactory</code>. * Result which cannot be null. It contains the attributes and object to be * bound, either of which may be null. Once returned the caller is the owner * of it. The behaviour is like that for the <code>getStateToBind</code> * method of <code>NamingManager</code> however it should be noted that * the intermediate state factory may be of type * <code>DirStateFactory</code> rather than just <code>StateFactory</code> * in which case it should also use the supplied <code>Attributes</code> * when getting the state. * * @param o * An object which may provide reference or location information. * May be null. * @param n * The name of the <code>Object</code> relative to the default * initial context (or relative to the Context c if it is * supplied) * @param c * The <code>Context</code> to which the <code>Name</code> is * relative * @param h * a <code>Hashtable</code> containing environment properties * and values - may be null * @param a * <code>Attributes</code> - if some or all of the attributes * of <code>Object o</code> are already known they can be * supplied so that the factory does not have to do the work of * looking them up. * @return the state of the object * @throws NamingException * if one is encountered */ public static Result getStateToBind(Object o, Name n, Context c, Hashtable<?, ?> h, Attributes a) throws NamingException { // obtain state factories from hashtable and service provider resource // file String fnames[] = EnvironmentReader .getFactoryNamesFromEnvironmentAndProviderResource(h, c, Context.STATE_FACTORIES); for (String element : fnames) { // new factory instance by its class name StateFactory factory = null; try { factory = (StateFactory) classForName(element).newInstance(); } catch (Exception e) { continue; } if (factory instanceof DirStateFactory) { // try obtain state using the DirStateFactory Result r = ((DirStateFactory) factory).getStateToBind(o, n, c, h, a); // if the result is not null, return it if (null != r) { return r; } } else { // try obtain state using the StateFactory Object state = factory.getStateToBind(o, n, c, h); // if a state obtained successfully, return it if (null != state) { return new Result(state, a); } } } // all factories failed, return the input argument o return new Result(o, a); } /** * Create the next <code>DirContext</code> when using federation so that * the <code>DirContext</code> operation can be reinvoked. This should * work similarly to <code>NamingManager.getContinuationContext</code> * except that a reference to a <code>DirContext</code> is returned. * <p> * This method is also responsible for setting the property denoted by the * <code>CPE</code> string to be the supplied * <code>CannotProceedException</code> for that environment. * </p> * * @param cpe * the <code>CannotProceedException</code> generated by the * <code>DirContext</code> of the previous naming system when * it can proceed no further. * @return the next <code>DirContext</code> when using federation * @throws NamingException * if the resolved object is not set or if a * <code>DirContext</code> cannot be obtained from it either * directly or indirectly. */ public static DirContext getContinuationDirContext( CannotProceedException cpe) throws NamingException { // obtain next context using NamingManager Context nextContext = null; try { nextContext = NamingManager.getContinuationContext(cpe); } catch (CannotProceedException e) { // tolerate CannotProceedException here } // if it is a DirContext if (nextContext instanceof DirContext) { // return as DirContext return (DirContext) nextContext; } // in case it's Context but not DirContext, wrap it as DirContext // and return return new Context2DirContextWrapper(nextContext, cpe); } private static Class<?> classForName(final String className) throws ClassNotFoundException { Class<?> cls = AccessController .doPrivileged(new PrivilegedAction<Class<?>>() { public Class<?> run() { // try thread context class loader first try { return Class.forName(className, true, Thread .currentThread().getContextClassLoader()); } catch (ClassNotFoundException e) { // Could happen. } // try system class loader second try { return Class.forName(className, true, ClassLoader .getSystemClassLoader()); } catch (ClassNotFoundException e1) { // Not found here either. } // return null, if fail to load class return null; } }); if (cls == null) { // jndi.1C=class {0} not found throw new ClassNotFoundException(Messages.getString( "jndi.1C", className)); //$NON-NLS-1$ } return cls; } /** * An inner class that transforms a Context instance into DirContext. */ private static class Context2DirContextWrapper implements DirContext { private Context ctx; private CannotProceedException cpe; public Context2DirContextWrapper(Context ctx, CannotProceedException cpe) { this.ctx = ctx; this.cpe = cpe; } private Context getContext() throws CannotProceedException { if (ctx != null) { return ctx; } cpe.fillInStackTrace(); throw cpe; } private DirContext getDirContext() throws CannotProceedException { if (ctx instanceof DirContext) { return (DirContext) ctx; } cpe.fillInStackTrace(); throw cpe; } public NamingEnumeration<Binding> listBindings(String s) throws NamingException { return getContext().listBindings(s); } public Object removeFromEnvironment(String s) throws NamingException { return getContext().removeFromEnvironment(s); } public Object lookupLink(String s) throws NamingException { return getContext().lookupLink(s); } public NamingEnumeration<NameClassPair> list(Name n) throws NamingException { return getContext().list(n); } public Object lookup(String s) throws NamingException { return getContext().lookup(s); } public Object addToEnvironment(String s, Object o) throws NamingException { return getContext().addToEnvironment(s, o); } @Override public String toString() { try { return getContext().toString(); } catch (CannotProceedException e) { return super.toString(); } } public Context createSubcontext(String s) throws NamingException { return getContext().createSubcontext(s); } public void rename(Name nOld, Name nNew) throws NamingException { getContext().rename(nOld, nNew); } @Override public int hashCode() { try { return getContext().hashCode(); } catch (CannotProceedException e) { return super.hashCode(); } } public void rebind(Name n, Object o) throws NamingException { getContext().rebind(n, o); } public void rename(String sOld, String sNew) throws NamingException { getContext().rename(sOld, sNew); } public Context createSubcontext(Name n) throws NamingException { return getContext().createSubcontext(n); } public NameParser getNameParser(String s) throws NamingException { return getContext().getNameParser(s); } public void rebind(String s, Object o) throws NamingException { getContext().rebind(s, o); } public NamingEnumeration<Binding> listBindings(Name n) throws NamingException { return getContext().listBindings(n); } public NameParser getNameParser(Name n) throws NamingException { return getContext().getNameParser(n); } public NamingEnumeration<NameClassPair> list(String s) throws NamingException { return getContext().list(s); } public String getNameInNamespace() throws NamingException { return getContext().getNameInNamespace(); } public void unbind(Name n) throws NamingException { getContext().unbind(n); } public Name composeName(Name n, Name pfx) throws NamingException { return getContext().composeName(n, pfx); } public void bind(Name n, Object o) throws NamingException { getContext().bind(n, o); } public void unbind(String s) throws NamingException { getContext().unbind(s); } public void close() throws NamingException { getContext().close(); } public Object lookupLink(Name n) throws NamingException { return getContext().lookupLink(n); } public void destroySubcontext(Name n) throws NamingException { getContext().destroySubcontext(n); } public String composeName(String s, String pfx) throws NamingException { return getContext().composeName(s, pfx); } public void bind(String s, Object o) throws NamingException { getContext().bind(s, o); } public Object lookup(Name n) throws NamingException { return getContext().lookup(n); } @Override public boolean equals(Object arg0) { try { return getContext().equals(arg0); } catch (CannotProceedException e) { return super.equals(arg0); } } public void destroySubcontext(String s) throws NamingException { getContext().destroySubcontext(s); } public Hashtable<?, ?> getEnvironment() throws NamingException { return getContext().getEnvironment(); } public void bind(Name name, Object obj, Attributes attributes) throws NamingException { getDirContext().bind(name, obj, attributes); } public void bind(String s, Object obj, Attributes attributes) throws NamingException { getDirContext().bind(s, obj, attributes); } public DirContext createSubcontext(Name name, Attributes attributes) throws NamingException { return getDirContext().createSubcontext(name, attributes); } public DirContext createSubcontext(String s, Attributes attributes) throws NamingException { return getDirContext().createSubcontext(s, attributes); } public Attributes getAttributes(Name name) throws NamingException { return getDirContext().getAttributes(name); } public Attributes getAttributes(Name name, String[] as) throws NamingException { return getDirContext().getAttributes(name, as); } public Attributes getAttributes(String s) throws NamingException { return getDirContext().getAttributes(s); } public Attributes getAttributes(String s, String[] as) throws NamingException { return getDirContext().getAttributes(s, as); } public DirContext getSchema(Name name) throws NamingException { return getDirContext().getSchema(name); } public DirContext getSchema(String s) throws NamingException { return getDirContext().getSchema(s); } public DirContext getSchemaClassDefinition(Name name) throws NamingException { return getDirContext().getSchemaClassDefinition(name); } public DirContext getSchemaClassDefinition(String s) throws NamingException { return getDirContext().getSchemaClassDefinition(s); } public void modifyAttributes(Name name, int i, Attributes attributes) throws NamingException { getDirContext().modifyAttributes(name, i, attributes); } public void modifyAttributes(Name name, ModificationItem[] modificationItems) throws NamingException { getDirContext().modifyAttributes(name, modificationItems); } public void modifyAttributes(String s, int i, Attributes attributes) throws NamingException { getDirContext().modifyAttributes(s, i, attributes); } public void modifyAttributes(String s, ModificationItem[] modificationItems) throws NamingException { getDirContext().modifyAttributes(s, modificationItems); } public void rebind(Name name, Object obj, Attributes attributes) throws NamingException { getDirContext().rebind(name, obj, attributes); } public void rebind(String s, Object obj, Attributes attributes) throws NamingException { getDirContext().rebind(s, obj, attributes); } public NamingEnumeration<SearchResult> search(Name name, Attributes attributes) throws NamingException { return getDirContext().search(name, attributes); } public NamingEnumeration<SearchResult> search(Name name, Attributes attributes, String[] as) throws NamingException { return getDirContext().search(name, attributes, as); } public NamingEnumeration<SearchResult> search(Name name, String filter, Object[] objs, SearchControls searchControls) throws NamingException { return getDirContext().search(name, filter, objs, searchControls); } public NamingEnumeration<SearchResult> search(Name name, String filter, SearchControls searchControls) throws NamingException { return getDirContext().search(name, filter, searchControls); } public NamingEnumeration<SearchResult> search(String name, Attributes attributes) throws NamingException { return getDirContext().search(name, attributes); } public NamingEnumeration<SearchResult> search(String name, Attributes attributes, String[] as) throws NamingException { return getDirContext().search(name, attributes, as); } public NamingEnumeration<SearchResult> search(String name, String filter, Object[] objs, SearchControls searchControls) throws NamingException { return getDirContext().search(name, filter, objs, searchControls); } public NamingEnumeration<SearchResult> search(String name, String filter, SearchControls searchControls) throws NamingException { return getDirContext().search(name, filter, searchControls); } } }