package org.jboss.as.naming; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Hashtable; import java.util.concurrent.atomic.AtomicInteger; import javax.naming.Binding; 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.spi.ObjectFactory; import org.jboss.invocation.proxy.ProxyConfiguration; import org.jboss.invocation.proxy.ProxyFactory; import org.jboss.modules.Module; import org.jboss.modules.ModuleIdentifier; import org.jboss.modules.ModuleLoadException; import org.wildfly.security.manager.WildFlySecurityManager; /** * An ObjectFactory that binds an arbitrary InitialContext into JNDI. */ public class ExternalContextObjectFactory implements ObjectFactory { private static final AtomicInteger PROXY_ID = new AtomicInteger(); public static final String CACHE_CONTEXT = "cache-context"; public static final String INITIAL_CONTEXT_CLASS = "initial-context-class"; public static final String INITIAL_CONTEXT_MODULE = "initial-context-module"; /** * If this property is set to {@code true} in the {@code Context} environment, objects will be looked up * by calling its {@link javax.naming.Context#lookup(String)} instead of {@link javax.naming.Context#lookup(javax.naming.Name)}. */ private static final String LOOKUP_BY_STRING = "org.jboss.as.naming.lookup.by.string"; private volatile Context cachedObject; @Override public Object getObjectInstance(final Object obj, final Name name, final Context nameCtx, final Hashtable<?, ?> environment) throws Exception { String cacheString = (String) environment.get(CACHE_CONTEXT); boolean cache = cacheString != null && cacheString.toLowerCase().equals("true"); if (cache) { if (cachedObject == null) { synchronized (this) { if (cachedObject == null) { cachedObject = createContext(environment, true); } } } return cachedObject; } else { return createContext(environment, false); } } private Context createContext(final Hashtable<?, ?> environment, boolean useProxy) throws NamingException, ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException, ModuleLoadException { String initialContextClassName = (String) environment.get(INITIAL_CONTEXT_CLASS); String initialContextModule = (String) environment.get(INITIAL_CONTEXT_MODULE); final boolean useStringLokup = useStringLookup(environment); final Hashtable<?, ?> newEnvironment = new Hashtable<>(environment); newEnvironment.remove(CACHE_CONTEXT); newEnvironment.remove(INITIAL_CONTEXT_CLASS); newEnvironment.remove(INITIAL_CONTEXT_MODULE); newEnvironment.remove(LOOKUP_BY_STRING); ClassLoader loader; if (! WildFlySecurityManager.isChecking()) { loader = getClass().getClassLoader(); } else { loader = AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() { @Override public ClassLoader run() { return getClass().getClassLoader(); } }); } Class initialContextClass = null; final Context loadedContext; if (initialContextModule == null) { initialContextClass = Class.forName(initialContextClassName); Constructor ctor = initialContextClass.getConstructor(Hashtable.class); loadedContext = (Context) ctor.newInstance(newEnvironment); } else { Module module = Module.getBootModuleLoader().loadModule(ModuleIdentifier.fromString(initialContextModule)); loader = module.getClassLoader(); final ClassLoader currentClassLoader = WildFlySecurityManager.getCurrentContextClassLoaderPrivileged(); try { WildFlySecurityManager.setCurrentContextClassLoaderPrivileged(loader); initialContextClass = Class.forName(initialContextClassName, true, loader); Constructor ctor = initialContextClass.getConstructor(Hashtable.class); loadedContext = (Context) ctor.newInstance(newEnvironment); } finally { WildFlySecurityManager.setCurrentContextClassLoaderPrivileged(currentClassLoader); } } final Context context; if (useStringLokup) { context = new LookupByStringContext(loadedContext); } else { context = loadedContext; } if (!useProxy) { return context; } ProxyConfiguration config = new ProxyConfiguration(); config.setClassLoader(loader); config.setSuperClass(initialContextClass); config.setProxyName(initialContextClassName + "$$$$Proxy" + PROXY_ID.incrementAndGet()); config.setProtectionDomain(context.getClass().getProtectionDomain()); ProxyFactory<?> factory = new ProxyFactory<Object>(config); return (Context) factory.newInstance(new CachedContext(context)); } /** * A proxy implementation of Context that simply intercepts the * close() method and ignores it since the underlying Context * object is being maintained in memory. */ static class CachedContext implements InvocationHandler { Context externalCtx; CachedContext(Context externalCtx) { this.externalCtx = externalCtx; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object value = null; if (method.getName().equals("close")) { // We just ignore the close method } else { try { value = method.invoke(externalCtx, args); } catch (InvocationTargetException e) { throw e.getTargetException(); } } return value; } } /** * @return {@code true} if the environment contains a {@code LOOKUP_BY_STRING} property with a value corresponding to a {@code true} boolean, or {@code false} in any other case. * @param environment */ private static boolean useStringLookup(Hashtable<?, ?> environment) { Object val = environment.get(LOOKUP_BY_STRING); if (val instanceof String) { return Boolean.valueOf((String) val); } return false; } /** * A wrapper around a {@code Context} that delegates {@link javax.naming.Name}-based lookup to * their corresponding {@code String}-based method. */ private static class LookupByStringContext implements Context { private final Context context; public LookupByStringContext(Context context) { this.context = context; } @Override public Object lookup(Name name) throws NamingException { return context.lookup(name.toString()); } @Override public Object lookup(String name) throws NamingException { return context.lookup(name); } @Override public void bind(Name name, Object obj) throws NamingException { context.bind(name, obj); } @Override public void bind(String name, Object obj) throws NamingException { context.bind(name, obj); } @Override public void rebind(Name name, Object obj) throws NamingException { context.rebind(name, obj); } @Override public void rebind(String name, Object obj) throws NamingException { context.rebind(name, obj); } @Override public void unbind(Name name) throws NamingException { context.unbind(name); } @Override public void unbind(String name) throws NamingException { context.unbind(name); } @Override public void rename(Name oldName, Name newName) throws NamingException { context.rename(oldName, newName); } @Override public void rename(String oldName, String newName) throws NamingException { context.rename(oldName, newName); } @Override public NamingEnumeration<NameClassPair> list(Name name) throws NamingException { return context.list(name); } @Override public NamingEnumeration<NameClassPair> list(String name) throws NamingException { return context.list(name); } @Override public NamingEnumeration<Binding> listBindings(Name name) throws NamingException { return context.listBindings(name); } @Override public NamingEnumeration<Binding> listBindings(String name) throws NamingException { return context.listBindings(name); } @Override public void destroySubcontext(Name name) throws NamingException { context.destroySubcontext(name); } @Override public void destroySubcontext(String name) throws NamingException { context.destroySubcontext(name); } @Override public Context createSubcontext(Name name) throws NamingException { return context.createSubcontext(name); } @Override public Context createSubcontext(String name) throws NamingException { return context.createSubcontext(name); } @Override public Object lookupLink(Name name) throws NamingException { return context.lookupLink(name.toString()); } @Override public Object lookupLink(String name) throws NamingException { return context.lookupLink(name); } @Override public NameParser getNameParser(Name name) throws NamingException { return context.getNameParser(name.toString()); } @Override public NameParser getNameParser(String name) throws NamingException { return context.getNameParser(name); } @Override public Name composeName(Name name, Name prefix) throws NamingException { return context.composeName(name, prefix); } @Override public String composeName(String name, String prefix) throws NamingException { return context.composeName(name, prefix); } @Override public Object addToEnvironment(String propName, Object propVal) throws NamingException { return context.addToEnvironment(propName, propVal); } @Override public Object removeFromEnvironment(String propName) throws NamingException { return context.removeFromEnvironment(propName); } @Override public Hashtable<?, ?> getEnvironment() throws NamingException { return context.getEnvironment(); } @Override public void close() throws NamingException { context.close(); } @Override public String getNameInNamespace() throws NamingException { return context.getNameInNamespace(); } } }