Java tutorial
/* * Copyright (c) 2005-2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. 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 org.wso2.carbon.context.internal; import org.apache.axiom.om.OMElement; import org.apache.axis2.util.XMLUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.wso2.carbon.CarbonConstants; import org.wso2.carbon.base.DiscoveryService; import org.wso2.carbon.base.UnloadTenantTask; import org.wso2.carbon.queuing.CarbonQueue; import org.wso2.carbon.queuing.CarbonQueueManager; import org.wso2.carbon.queuing.QueuingException; import org.wso2.carbon.registry.api.Registry; import org.wso2.carbon.user.api.UserRealm; import org.wso2.carbon.user.api.UserRealmService; import org.wso2.carbon.user.api.UserStoreException; import org.wso2.carbon.utils.CarbonUtils; import org.wso2.carbon.utils.ServerConstants; import org.wso2.carbon.utils.multitenancy.MultitenantCarbonQueueManager; import org.wso2.carbon.utils.multitenancy.MultitenantConstants; 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.NoInitialContextException; 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.event.EventContext; import javax.naming.event.EventDirContext; import javax.naming.event.NamingListener; import javax.naming.ldap.Control; import javax.naming.ldap.ExtendedRequest; import javax.naming.ldap.ExtendedResponse; import javax.naming.ldap.LdapContext; import javax.naming.spi.InitialContextFactory; import javax.naming.spi.InitialContextFactoryBuilder; import javax.naming.spi.NamingManager; import javax.xml.namespace.QName; import java.lang.ref.WeakReference; import java.net.Authenticator; import java.net.PasswordAuthentication; import java.net.URL; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Hashtable; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Stack; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicReference; import java.util.regex.Pattern; /** * This class will preserve an instance the current CarbonContext as a thread local variable. If a * CarbonContext is available on a global-scope (i.e. HTTP Session or AxisConfiguration) this class * will do the required lookup and obtain the corresponding instance. * <p/> * The CarbonContext provides the API for sub-tenant/super-tenant programming around <a * href="http://wso2.com/products/carbon">WSO2 Carbon</a> and <a href="http://wso2.com/cloud/stratos">WSO2 * Stratos</a>. */ @SuppressWarnings("unused") public final class CarbonContextDataHolder { private static final Log log = LogFactory.getLog(CarbonContextDataHolder.class); /** * The name of the property that stores a reference to the local repository instance of the * current tenant. */ protected Registry localRepository; /** * The name of the property that stores a reference to the configuration registry instance of * the current tenant, as visible to the system. */ protected Registry configSystemRegistry; /** * The name of the property that stores a reference to the governance registry instance of the * current tenant, as visible to the system. */ protected Registry governanceSystemRegistry; /** * The name of the property that stores a reference to the configuration registry instance of * the current tenant, as visible to a user. */ protected Registry configUserRegistry; /** * The name of the property that stores a reference to the governance registry instance of the * current tenant, as visible to a user. */ protected Registry governanceUserRegistry; /** * The name of the property that stores a reference to the UserRealm instance of the current * tenant, as visible to a user. */ protected UserRealm userRealm; // The CarbonContextHolder parameter is restricted. We have used the hardcoded string value in // TenantAxisConfiguration and CarbonHttpSession and ThriftSession. Any change to this constant, should also be // propagated to the three classes mentioned above. private static final String CARBON_CONTEXT_HOLDER = "carbonContextHolder"; private int tenantId = org.wso2.carbon.base.MultitenantConstants.INVALID_TENANT_ID; private static final int CARBON_AUTHENTICATION_UTIL_INDEX = 5; private static final String CARBON_AUTHENTICATION_UTIL_CLASS = "org.wso2.carbon.core.services.util.CarbonAuthenticationUtil"; private static final int CARBON_AUTHENTICATION_HANDLER_INDEX = 5; private static final String CARBON_AUTHENTICATION_HANDLER_CLASS = "org.wso2.carbon.server.admin.module.handler.AuthenticationHandler"; private String username; private String tenantDomain; private String applicationName; private Map<String, Object> properties; private static List<UnloadTenantTask> unloadTenantTasks = null; private static final AtomicReference<DiscoveryService> discoveryServiceProvider = new AtomicReference<DiscoveryService>(); // stores the current CarbonContext local to the running thread. private static ThreadLocal<CarbonContextDataHolder> currentContextHolder = new ThreadLocal<CarbonContextDataHolder>() { protected CarbonContextDataHolder initialValue() { return new CarbonContextDataHolder(); } }; // stores references to the existing CarbonContexts when starting tenant // flows. These // references will be popped back, when a tenant flow is ended. private static ThreadLocal<Stack<CarbonContextDataHolder>> parentContextHolderStack = new ThreadLocal<Stack<CarbonContextDataHolder>>(); // contains static initializations for multi-tenant caching, queuing and JNDI support available // via the CarbonContext. static { try { log.debug("Started Setting up Authenticator Configuration"); CarbonAuthenticator authenticator = new CarbonAuthenticator(); try { setupAuthenticator(authenticator); } finally { String username = System.getProperty("http.proxyUser"); String password = System.getProperty("http.proxyPassword"); if (username != null && password != null) { authenticator.addAuthenticator("proxy", ".*", username, password); } Authenticator.setDefault(authenticator); } log.debug("Completed Setting up Authenticator Configuration"); } catch (NoClassDefFoundError ignore) { // There can be situations where the CarbonContext is accessed, when there is no Axis2 // library on the classpath. if (log.isDebugEnabled()) { log.debug("There can be situations where the CarbonContext is accessed, when there is no Axis2" + "library on the classpath.", ignore); } } catch (Exception e) { String msg = "Unable to read Server Configuration"; log.error(msg, e); } unloadTenantTasks = new LinkedList<UnloadTenantTask>(); registerUnloadTenantTask(new CarbonContextCleanupTask()); try { CarbonQueueManager.setInstance(new InternalCarbonQueueManager()); } catch (RuntimeException ignore) { // We don't mind an exception being thrown in here. Since there can be a possibility of // the same class loading twice and then trying to reset the queue manager. if (log.isDebugEnabled()) { log.debug("there can be a possibility of the same class loading twice and then trying " + "to reset the initial context factory builder", ignore); } } try { NamingManager.setInitialContextFactoryBuilder(new CarbonInitialJNDIContextFactoryBuilder()); } catch (NamingException ignore) { // We don't mind an exception being thrown in here. Since there can be a possibility of // the same class loading twice and then trying to reset the initial context factory // builder. if (log.isDebugEnabled()) { log.debug("there can be a possibility of the same class loading twice and then trying " + "to reset the initial context factory builder", ignore); } } catch (RuntimeException ignore) { // We don't mind an exception being thrown in here. Since there can be a possibility of // the same class loading twice and then trying to reset the initial context factory // builder. We are also catching Runtime exceptions here, since some JDKs do throw them // instead of the expected NamingException. if (log.isDebugEnabled()) { log.debug("there can be a possibility of the same class loading twice and then trying " + "to reset the initial context factory builder", ignore); } } } public Registry getLocalRepository() { return localRepository; } public void setLocalRepository(Registry localRepository) { this.localRepository = localRepository; } public Registry getConfigSystemRegistry() { return configSystemRegistry; } public void setConfigSystemRegistry(Registry configSystemRegistry) { this.configSystemRegistry = configSystemRegistry; } public Registry getGovernanceSystemRegistry() { return governanceSystemRegistry; } public void setGovernanceSystemRegistry(Registry governanceSystemRegistry) { this.governanceSystemRegistry = governanceSystemRegistry; } public Registry getConfigUserRegistry() { return configUserRegistry; } public void setConfigUserRegistry(Registry configUserRegistry) { this.configUserRegistry = configUserRegistry; } public Registry getGovernanceUserRegistry() { return governanceUserRegistry; } public void setGovernanceUserRegistry(Registry governanceUserRegistry) { this.governanceUserRegistry = governanceUserRegistry; } public UserRealm getUserRealm() { if (userRealm == null) { UserRealmService userRealmService = OSGiDataHolder.getInstance().getUserRealmService(); if (userRealmService != null) { try { userRealm = userRealmService.getTenantUserRealm(tenantId); } catch (UserStoreException e) { log.error("Cannot retrieve UserRealm for tenant " + tenantId, e); } } } return userRealm; } public void setUserRealm(UserRealm userRealm) { this.userRealm = userRealm; } public String getApplicationName() { return applicationName; } public void setApplicationName(String applicationName) { this.applicationName = applicationName; } private static void setupAuthenticator(CarbonAuthenticator authenticator) throws Exception { OMElement documentElement = XMLUtils.toOM(CarbonUtils.getServerConfiguration().getDocumentElement()); OMElement authenticators = documentElement .getFirstChildWithName(new QName(ServerConstants.CARBON_SERVER_XML_NAMESPACE, "Security")) .getFirstChildWithName( new QName(ServerConstants.CARBON_SERVER_XML_NAMESPACE, "NetworkAuthenticatorConfig")); if (authenticators == null) { return; } for (Iterator iterator = authenticators.getChildElements(); iterator.hasNext();) { OMElement authenticatorElement = (OMElement) iterator.next(); if (!authenticatorElement.getLocalName().equalsIgnoreCase("Credential")) { continue; } String pattern = authenticatorElement .getFirstChildWithName(new QName(ServerConstants.CARBON_SERVER_XML_NAMESPACE, "Pattern")) .getText(); String type = authenticatorElement .getFirstChildWithName(new QName(ServerConstants.CARBON_SERVER_XML_NAMESPACE, "Type")) .getText(); String username = authenticatorElement .getFirstChildWithName(new QName(ServerConstants.CARBON_SERVER_XML_NAMESPACE, "Username")) .getText(); String password = authenticatorElement .getFirstChildWithName(new QName(ServerConstants.CARBON_SERVER_XML_NAMESPACE, "Password")) .getText(); authenticator.addAuthenticator(type, pattern, username, password); } } /** * Method to be called when this tenant is unloaded. This will clear all the resources created * by this tenant on the carbon contexts. */ public void unloadTenant() { // The security checks are done at the CarbonContextHolderBase level. int tenantId = getTenantId(); unloadTenant(tenantId); } /** * This method will always attempt to obtain an instance of the current CarbonContext from the * thread-local copy. * * @return the CarbonContext holder. */ public static CarbonContextDataHolder getThreadLocalCarbonContextHolder() { return currentContextHolder.get(); } /** * This method will set the current multi-tenant queue manager instance. * * @param queueManager the multi-tenant queue manager. * @throws org.wso2.carbon.queuing.QueuingException * if the operation failed. */ public void setQueueManager(MultitenantCarbonQueueManager queueManager) throws QueuingException { CarbonQueueManager manager = CarbonQueueManager.getInstance(); if (manager instanceof InternalCarbonQueueManager) { ((InternalCarbonQueueManager) manager).setQueueManager(queueManager); } log.debug("Successfully set the Queue Manager"); } /** * This method will remove the current multi-tenant queue manager instance. * * @throws QueuingException if the operation failed. */ public void removeQueueManager() throws QueuingException { CarbonQueueManager manager = CarbonQueueManager.getInstance(); if (manager instanceof InternalCarbonQueueManager) { ((InternalCarbonQueueManager) manager).removeQueueManager(); } log.debug("Successfully removed the Queue Manager"); } // Checks whether the given tenant is a sub-tenant or not. private static boolean isSubTenant(int tenantId) { return (tenantId != MultitenantConstants.SUPER_TENANT_ID && tenantId != MultitenantConstants.INVALID_TENANT_ID); } // A tenant-aware queue manager implementation. This will internally hold an instance of the // {@link MultitenantCarbonQueueManager}. private static class InternalCarbonQueueManager extends CarbonQueueManager { private AtomicReference<MultitenantCarbonQueueManager> queueManager = new AtomicReference<MultitenantCarbonQueueManager>(); public CarbonQueue<?> getQueue(String name) { int tenantId = getThreadLocalCarbonContextHolder().getTenantId(); if (queueManager.get() != null) { if (log.isDebugEnabled()) { log.debug("Retrieving named queue: " + name); } return queueManager.get().getQueue(name, isSubTenant(tenantId) ? tenantId : MultitenantConstants.SUPER_TENANT_ID); } return null; } public synchronized void setQueueManager(MultitenantCarbonQueueManager queueManager) throws QueuingException { CarbonUtils.checkSecurity(); if (getThreadLocalCarbonContextHolder().getTenantId() != MultitenantConstants.SUPER_TENANT_ID) { throw new QueuingException("Only the super-tenant can set the queue manager."); } InternalCarbonQueueManager carbonQueueManager = null; if (this.queueManager.get() != null) { throw new QueuingException("The queue manager has already been set."); } this.queueManager.set(queueManager); } public synchronized void removeQueueManager() throws QueuingException { CarbonUtils.checkSecurity(); if (getThreadLocalCarbonContextHolder().getTenantId() != MultitenantConstants.SUPER_TENANT_ID) { throw new QueuingException("Only the super-tenant can remove the queue manager."); } this.queueManager.set(null); } } // A tenant-aware JNDI Initial Context Factory Builder implementation. private static class CarbonInitialJNDIContextFactoryBuilder implements InitialContextFactoryBuilder { private static final String defaultInitialContextFactory = CarbonUtils.getServerConfiguration() .getFirstProperty("JNDI.DefaultInitialContextFactory"); public InitialContextFactory createInitialContextFactory(Hashtable<?, ?> h) throws NamingException { try { // get factory class name String factoryClassName = (String) h.get(Context.INITIAL_CONTEXT_FACTORY); // if the factory class has not been provided use the default initial context // factory defined in carbon.xml. if (factoryClassName == null) { factoryClassName = defaultInitialContextFactory; } else { Class<?> factoryClass = classForName(factoryClassName); return (InitialContextFactory) factoryClass.newInstance(); } if (factoryClassName == null) { throw new NoInitialContextException( "Failed to create " + "InitialContext. No factory specified in hash table."); } // new factory instance if (log.isDebugEnabled()) { log.debug("Loading JNDI Initial Context Factory: " + factoryClassName); } Class<?> factoryClass = classForName(factoryClassName); InitialContextFactory initialContextFactory = null; String defaultInitialContextFactoryClassName = CarbonUtils.getServerConfiguration() .getFirstProperty("JNDI.CarbonInitialJNDIContextFactory"); if (defaultInitialContextFactoryClassName != null) { Class<?> defaultInitialContextFactoryBuilderClass; try { defaultInitialContextFactoryBuilderClass = classForName( defaultInitialContextFactoryClassName); initialContextFactory = (InitialContextFactory) defaultInitialContextFactoryBuilderClass .getConstructor(InitialContextFactory.class) .newInstance(factoryClass.newInstance()); } catch (ClassNotFoundException e) { log.warn("The specified InitialContextFactoryBuilder " + defaultInitialContextFactoryClassName + " is not there in the class " + "path.Using the default InitialContextFactoryBuilder ", e); } catch (InstantiationException e) { log.warn("The specified InitialContextFactoryBuilder " + "" + defaultInitialContextFactoryClassName + " could not be " + "instantiated.Using the default InitialContextFactoryBuilder ", e); } catch (IllegalAccessException e) { log.warn("The specified InitialContextFactoryBuilder " + defaultInitialContextFactoryClassName + " could not be " + "accessed.Using the default InitialContextFactoryBuilder ", e); } } if (initialContextFactory != null) { return initialContextFactory; } //InitialContextFactory is not defined in carbon.xml, loading the default implementation. return new CarbonInitialJNDIContextFactory((InitialContextFactory) factoryClass.newInstance()); } catch (Exception e) { NamingException nex = new NoInitialContextException( "Failed to create " + "InitialContext using factory specified in hash table."); nex.setRootCause(e); throw nex; } } } // A tenant-aware JNDI Initial Context Factory implementation. private static class CarbonInitialJNDIContextFactory implements InitialContextFactory { private InitialContextFactory factory; public CarbonInitialJNDIContextFactory(InitialContextFactory factory) { this.factory = factory; } public Context getInitialContext(Hashtable<?, ?> h) throws NamingException { return new CarbonInitialJNDIContext(factory.getInitialContext(h)); } } // A tenant-aware JNDI Initial Context implementation. private static class CarbonInitialJNDIContext implements EventDirContext, LdapContext { private Context initialContext; private Map<String, Context> contextCache = Collections.synchronizedMap(new HashMap<String, Context>()); private static ContextCleanupTask contextCleanupTask; private static List<String> superTenantOnlyUrlContextSchemes; private static List<String> allTenantUrlContextSchemes; static { contextCleanupTask = new ContextCleanupTask(); registerUnloadTenantTask(contextCleanupTask); superTenantOnlyUrlContextSchemes = Arrays.asList(CarbonUtils.getServerConfiguration() .getProperties("JNDI.Restrictions.SuperTenantOnly.UrlContexts.UrlContext.Scheme")); allTenantUrlContextSchemes = Arrays.asList(CarbonUtils.getServerConfiguration() .getProperties("JNDI.Restrictions.AllTenants.UrlContexts.UrlContext.Scheme")); } public CarbonInitialJNDIContext(Context initialContext) throws NamingException { this.initialContext = initialContext; } private static String getScheme(String url) { if (null == url) { return null; } int colPos = url.indexOf(':'); if (colPos < 0) { return null; } String scheme = url.substring(0, colPos); char c; boolean inCharSet; for (int i = 0; i < scheme.length(); i++) { c = scheme.charAt(i); inCharSet = (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '+' || c == '.' || c == '-' || c == '_'; if (!inCharSet) { return null; } } return scheme; } private Context getInitialContext(Name name) { return getInitialContext(name.get(0)); } private Context getInitialContext() { return getInitialContext((String) null); } private boolean isBaseContextRequested() { try { String baseContextRequested = (String) this.initialContext.getEnvironment() .get(CarbonConstants.REQUEST_BASE_CONTEXT); if (baseContextRequested != null && baseContextRequested.equals("true")) { return true; } } catch (NamingException e) { log.warn("An error occurred while retrieving environment properties from initial context.", e); } return false; } private Context getInitialContext(String name) { /** * If environment is requesting a base context return the * base context. */ if (isBaseContextRequested()) { return initialContext; } Context base = null; String scheme = null; if (name != null) { // If the name has components scheme = getScheme(name); if (scheme != null) { if (contextCache.containsKey(scheme)) { base = contextCache.get(scheme); } else { try { Context urlContext = NamingManager.getURLContext(scheme, initialContext.getEnvironment()); if (urlContext != null) { contextCache.put(scheme, urlContext); base = urlContext; } } catch (NamingException ignored) { // If we are unable to find the context, we use the default context. if (log.isDebugEnabled()) { log.debug("If we are unable to find the context, we use the default context.", ignored); } } } } } if (base == null) { base = initialContext; scheme = null; } int tenantId = getThreadLocalCarbonContextHolder().getTenantId(); if (!isSubTenant(tenantId)) { return base; } else if (scheme != null) { if (allTenantUrlContextSchemes.contains(scheme)) { return base; } else if (superTenantOnlyUrlContextSchemes.contains(scheme)) { throw new SecurityException( "Tenants are not allowed to use JNDI contexts " + "with scheme: " + scheme); } } String tenantContextName = Integer.toString(tenantId); Context subContext; try { subContext = (Context) base.lookup(tenantContextName); if (subContext != null) { return subContext; } } catch (NamingException ignored) { // Depending on the JNDI Initial Context factory, the above operation may or may not // throw an exception. But, since we don't mind the exception, we can ignore it. if (log.isDebugEnabled()) { log.debug(ignored); } } try { log.debug("Creating Sub-Context: " + tenantContextName); subContext = base.createSubcontext(tenantContextName); contextCleanupTask.register(tenantId, subContext); if (subContext == null) { throw new RuntimeException("Initial context was not created for tenant: " + tenantId); } return subContext; } catch (NamingException e) { throw new RuntimeException( "An error occurred while creating the initial context " + "for tenant: " + tenantId, e); } } private static class ContextCleanupTask implements UnloadTenantTask<Context> { private Map<Integer, ArrayList<Context>> contexts = new ConcurrentHashMap<Integer, ArrayList<Context>>(); public void register(int tenantId, Context context) { ArrayList<Context> list = contexts.get(tenantId); if (list == null) { list = new ArrayList<Context>(); list.add(context); contexts.put(tenantId, list); } else if (!list.contains(context)) { list.add(context); } } public void cleanup(int tenantId) { ArrayList<Context> list = contexts.remove(tenantId); // We need to close the context in a LIFO fashion. if (list != null) { Collections.reverse(list); for (Context context : list) { try { context.close(); } catch (NamingException ignore) { // We are not worried about the exception thrown here, as we are simply // doing a routine cleanup. if (log.isDebugEnabled()) { log.debug("Exception while outine cleanup", ignore); } } } list.clear(); } } } public Object lookup(String s) throws NamingException { return getInitialContext(s).lookup(s); } public Object lookup(Name name) throws NamingException { return getInitialContext(name).lookup(name); } public void bind(String s, Object o) throws NamingException { getInitialContext(s).bind(s, o); } public void bind(Name name, Object o) throws NamingException { getInitialContext(name).bind(name, o); } public void rebind(String s, Object o) throws NamingException { getInitialContext(s).rebind(s, o); } public void rebind(Name name, Object o) throws NamingException { getInitialContext(name).rebind(name, o); } public void unbind(String s) throws NamingException { getInitialContext(s).unbind(s); } public void unbind(Name name) throws NamingException { getInitialContext(name).unbind(name); } public void rename(String s, String s1) throws NamingException { getInitialContext(s).rename(s, s1); } public void rename(Name name, Name name1) throws NamingException { getInitialContext(name).rename(name, name1); } public NamingEnumeration<NameClassPair> list(String s) throws NamingException { return getInitialContext(s).list(s); } public NamingEnumeration<NameClassPair> list(Name name) throws NamingException { return getInitialContext(name).list(name); } public NamingEnumeration<Binding> listBindings(String s) throws NamingException { return getInitialContext(s).listBindings(s); } public NamingEnumeration<Binding> listBindings(Name name) throws NamingException { return getInitialContext(name).listBindings(name); } public void destroySubcontext(String s) throws NamingException { getInitialContext(s).destroySubcontext(s); } public void destroySubcontext(Name name) throws NamingException { getInitialContext(name).destroySubcontext(name); } public Context createSubcontext(String s) throws NamingException { return getInitialContext(s).createSubcontext(s); } public Context createSubcontext(Name name) throws NamingException { return getInitialContext(name).createSubcontext(name); } public Object lookupLink(String s) throws NamingException { return getInitialContext(s).lookupLink(s); } public Object lookupLink(Name name) throws NamingException { return getInitialContext(name).lookupLink(name); } public NameParser getNameParser(String s) throws NamingException { return getInitialContext(s).getNameParser(s); } public NameParser getNameParser(Name name) throws NamingException { return getInitialContext(name).getNameParser(name); } public String composeName(String s, String s1) throws NamingException { return getInitialContext(s).composeName(s, s1); } public Name composeName(Name name, Name name1) throws NamingException { return getInitialContext(name).composeName(name, name1); } public Object addToEnvironment(String s, Object o) throws NamingException { return getInitialContext().addToEnvironment(s, o); } public Object removeFromEnvironment(String s) throws NamingException { return getInitialContext().removeFromEnvironment(s); } public Hashtable<?, ?> getEnvironment() throws NamingException { if (isSubTenant(getThreadLocalCarbonContextHolder().getTenantId())) { throw new NamingException("Tenants cannot retrieve the environment."); } return getInitialContext().getEnvironment(); } public void close() throws NamingException { if (isSubTenant(getThreadLocalCarbonContextHolder().getTenantId()) && !isBaseContextRequested()) { CarbonUtils.checkSecurity(); } Context ctx = this.getInitialContext(); /* the below condition is there, because of a bug in Tomcat JNDI context close method, * see org.apache.naming.NamingContext#close() */ if (!ctx.getClass().getName().equals("org.apache.naming.SelectorContext")) { ctx.close(); } } public String getNameInNamespace() throws NamingException { return getInitialContext().getNameInNamespace(); } public int hashCode() { return initialContext.hashCode(); } public boolean equals(Object o) { return (o instanceof CarbonInitialJNDIContext) && initialContext.equals(o); } //////////////////////////////////////////////////////// // Methods Required by a DirContext //////////////////////////////////////////////////////// private DirContext getDirectoryContext(Name name) throws NamingException { return getDirectoryContext(name.get(0)); } private DirContext getDirectoryContext() throws NamingException { return getDirectoryContext((String) null); } private DirContext getDirectoryContext(String name) throws NamingException { Context initialContext = getInitialContext(name); if (initialContext instanceof DirContext) { return (DirContext) initialContext; } throw new NamingException("The given Context is not an instance of " + DirContext.class.getName()); } public Attributes getAttributes(Name name) throws NamingException { return getDirectoryContext(name).getAttributes(name); } public Attributes getAttributes(String s) throws NamingException { return getDirectoryContext(s).getAttributes(s); } public Attributes getAttributes(Name name, String[] strings) throws NamingException { return getDirectoryContext(name).getAttributes(name, strings); } public Attributes getAttributes(String s, String[] strings) throws NamingException { return getDirectoryContext(s).getAttributes(s, strings); } public void modifyAttributes(Name name, int i, Attributes attributes) throws NamingException { getDirectoryContext(name).modifyAttributes(name, i, attributes); } public void modifyAttributes(String s, int i, Attributes attributes) throws NamingException { getDirectoryContext(s).modifyAttributes(s, i, attributes); } public void modifyAttributes(Name name, ModificationItem[] modificationItems) throws NamingException { getDirectoryContext(name).modifyAttributes(name, modificationItems); } public void modifyAttributes(String s, ModificationItem[] modificationItems) throws NamingException { getDirectoryContext(s).modifyAttributes(s, modificationItems); } public void bind(Name name, Object o, Attributes attributes) throws NamingException { getDirectoryContext(name).bind(name, o, attributes); } public void bind(String s, Object o, Attributes attributes) throws NamingException { getDirectoryContext(s).bind(s, o, attributes); } public void rebind(Name name, Object o, Attributes attributes) throws NamingException { getDirectoryContext(name).rebind(name, o, attributes); } public void rebind(String s, Object o, Attributes attributes) throws NamingException { getDirectoryContext(s).rebind(s, o, attributes); } public DirContext createSubcontext(Name name, Attributes attributes) throws NamingException { return getDirectoryContext(name).createSubcontext(name, attributes); } public DirContext createSubcontext(String s, Attributes attributes) throws NamingException { return getDirectoryContext(s).createSubcontext(s, attributes); } public DirContext getSchema(Name name) throws NamingException { return getDirectoryContext(name).getSchema(name); } public DirContext getSchema(String s) throws NamingException { return getDirectoryContext(s).getSchema(s); } public DirContext getSchemaClassDefinition(Name name) throws NamingException { return getDirectoryContext(name).getSchemaClassDefinition(name); } public DirContext getSchemaClassDefinition(String s) throws NamingException { return getDirectoryContext(s).getSchemaClassDefinition(s); } public NamingEnumeration<SearchResult> search(Name name, Attributes attributes, String[] strings) throws NamingException { return getDirectoryContext(name).search(name, attributes, strings); } public NamingEnumeration<SearchResult> search(String s, Attributes attributes, String[] strings) throws NamingException { return getDirectoryContext(s).search(s, attributes, strings); } public NamingEnumeration<SearchResult> search(Name name, Attributes attributes) throws NamingException { return getDirectoryContext(name).search(name, attributes); } public NamingEnumeration<SearchResult> search(String s, Attributes attributes) throws NamingException { return getDirectoryContext(s).search(s, attributes); } public NamingEnumeration<SearchResult> search(Name name, String filter, SearchControls searchControls) throws NamingException { return getDirectoryContext(name).search(name, filter, searchControls); } public NamingEnumeration<SearchResult> search(String s, String filter, SearchControls searchControls) throws NamingException { return getDirectoryContext(s).search(s, filter, searchControls); } public NamingEnumeration<SearchResult> search(Name name, String filter, Object[] objects, SearchControls searchControls) throws NamingException { return getDirectoryContext(name).search(name, filter, objects, searchControls); } public NamingEnumeration<SearchResult> search(String s, String filter, Object[] objects, SearchControls searchControls) throws NamingException { return getDirectoryContext(s).search(s, filter, objects, searchControls); } //////////////////////////////////////////////////////// // Methods Required by a LdapContext //////////////////////////////////////////////////////// private LdapContext getLdapContext() throws NamingException { DirContext dirContext = getDirectoryContext(); if (dirContext instanceof EventContext) { return (LdapContext) dirContext; } throw new NamingException("The given Context is not an instance of " + LdapContext.class.getName()); } public ExtendedResponse extendedOperation(ExtendedRequest extendedRequest) throws NamingException { return getLdapContext().extendedOperation(extendedRequest); } public LdapContext newInstance(Control[] controls) throws NamingException { return getLdapContext().newInstance(controls); } public void reconnect(Control[] controls) throws NamingException { getLdapContext().reconnect(controls); } public Control[] getConnectControls() throws NamingException { return getLdapContext().getConnectControls(); } public void setRequestControls(Control[] controls) throws NamingException { getLdapContext().setRequestControls(controls); } public Control[] getRequestControls() throws NamingException { return getLdapContext().getRequestControls(); } public Control[] getResponseControls() throws NamingException { return getLdapContext().getResponseControls(); } //////////////////////////////////////////////////////// // Methods Required by a EventContext //////////////////////////////////////////////////////// private EventContext getEventContext(Name name) throws NamingException { return getEventContext(name.get(0)); } private EventContext getEventContext() throws NamingException { return getEventContext((String) null); } private EventContext getEventContext(String name) throws NamingException { Context initialContext = getInitialContext(name); if (initialContext instanceof EventContext) { return (EventContext) initialContext; } throw new NamingException("The given Context is not an instance of " + EventContext.class.getName()); } public void addNamingListener(Name name, int i, NamingListener namingListener) throws NamingException { CarbonUtils.checkSecurity(); getEventContext(name).addNamingListener(name, i, namingListener); } public void addNamingListener(String s, int i, NamingListener namingListener) throws NamingException { CarbonUtils.checkSecurity(); getEventContext(s).addNamingListener(s, i, namingListener); } public void removeNamingListener(NamingListener namingListener) throws NamingException { CarbonUtils.checkSecurity(); getEventContext().removeNamingListener(namingListener); } public boolean targetMustExist() throws NamingException { return getEventContext().targetMustExist(); } //////////////////////////////////////////////////////// // Methods Required by a EventDirContext //////////////////////////////////////////////////////// private EventDirContext getEventDirContext(Name name) throws NamingException { return getEventDirContext(name.get(0)); } private EventDirContext getEventDirContext(String name) throws NamingException { EventContext eventContext = getEventContext(name); if (eventContext instanceof EventDirContext) { return (EventDirContext) eventContext; } throw new NamingException("The given Context is not an instance of " + EventDirContext.class.getName()); } public void addNamingListener(Name name, String filter, SearchControls searchControls, NamingListener namingListener) throws NamingException { CarbonUtils.checkSecurity(); getEventDirContext(name).addNamingListener(name, filter, searchControls, namingListener); } public void addNamingListener(String s, String filter, SearchControls searchControls, NamingListener namingListener) throws NamingException { CarbonUtils.checkSecurity(); getEventDirContext(s).addNamingListener(s, filter, searchControls, namingListener); } public void addNamingListener(Name name, String filter, Object[] objects, SearchControls searchControls, NamingListener namingListener) throws NamingException { CarbonUtils.checkSecurity(); getEventDirContext(name).addNamingListener(name, filter, objects, searchControls, namingListener); } public void addNamingListener(String s, String filter, Object[] objects, SearchControls searchControls, NamingListener namingListener) throws NamingException { CarbonUtils.checkSecurity(); getEventDirContext(s).addNamingListener(s, filter, objects, searchControls, namingListener); } } private static class CarbonAuthenticator extends Authenticator { private static class AuthenticatorBean { private Pattern pattern; private PasswordAuthentication credential; public AuthenticatorBean(String regEx, String username, String password) { this.pattern = Pattern.compile(regEx); credential = new PasswordAuthentication(username, password.toCharArray()); } public PasswordAuthentication getPasswordAuthentication() { return credential; } public boolean matches(String protocol, String host, int port) { return pattern.matcher((protocol + "://" + host + ':' + port).toLowerCase()).matches(); } public boolean matches(URL url) { return pattern.matcher(url.toString()).matches(); } } private List<AuthenticatorBean> proxyAuthenticators = new LinkedList<AuthenticatorBean>(); private List<AuthenticatorBean> serverAuthenticators = new LinkedList<AuthenticatorBean>(); public void addAuthenticator(String type, String regEx, String username, String password) { if (type.equalsIgnoreCase("proxy")) { proxyAuthenticators.add(new AuthenticatorBean(regEx, username, password)); } else if (type.equalsIgnoreCase("server")) { serverAuthenticators.add(new AuthenticatorBean(regEx, username, password)); } } protected PasswordAuthentication getPasswordAuthentication() { if (Authenticator.RequestorType.PROXY.equals(getRequestorType())) { for (AuthenticatorBean authenticator : proxyAuthenticators) { if (authenticator.matches(getRequestingProtocol(), getRequestingHost(), getRequestingPort()) || authenticator.matches(getRequestingURL())) { return authenticator.getPasswordAuthentication(); } } } else if (Authenticator.RequestorType.SERVER.equals(getRequestorType())) { for (AuthenticatorBean authenticator : serverAuthenticators) { if (authenticator.matches(getRequestingScheme(), getRequestingHost(), getRequestingPort()) || authenticator.matches(getRequestingURL())) { return authenticator.getPasswordAuthentication(); } } } return super.getPasswordAuthentication(); } } // loads a class. 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 ignored) { if (log.isDebugEnabled()) { log.debug(ignored); } } // try system class loader second try { return Class.forName(className, true, ClassLoader.getSystemClassLoader()); } catch (ClassNotFoundException ignored) { if (log.isDebugEnabled()) { log.debug(ignored); } } // return null, if fail to load class return null; } }); if (cls == null) { throw new ClassNotFoundException("class " + className + " not found"); } return cls; } /** * Method to obtain an instance to the Discovery Service. * * @return instance of the Discovery Service */ public static DiscoveryService getDiscoveryServiceProvider() { return discoveryServiceProvider.get(); } /** * Method to define the instance of the Discovery Service. * * @param discoveryServiceProvider the Discovery Service instance. */ public static void setDiscoveryServiceProvider(DiscoveryService discoveryServiceProvider) { CarbonContextDataHolder.discoveryServiceProvider.set(discoveryServiceProvider); } /** * Method to obtain the current carbon context holder's base. * * @return the current carbon context holder's base. */ public static CarbonContextDataHolder getCurrentCarbonContextHolderBase() { return currentContextHolder.get(); } /** * Method to register a task that will be executed when a tenant is * unloaded. * * @param unloadTenantTask the task to run. * @see UnloadTenantTask */ public static synchronized void registerUnloadTenantTask(UnloadTenantTask unloadTenantTask) { if (log.isDebugEnabled()) { log.debug("Unload Tenant Task: " + unloadTenantTask.getClass().getName() + " was " + "registered."); } unloadTenantTasks.add(unloadTenantTask); } /** * Method that will be called when a tenant is unloaded. This will run all * the corresponding tasks that have been registered to be called when a * tenant is unloaded. * * @param tenantId the tenant's identifier. */ public static void unloadTenant(int tenantId) { log.debug("Started unloading tenant"); for (UnloadTenantTask unloadTenantTask : unloadTenantTasks) { unloadTenantTask.cleanup(tenantId); } log.info("Completed unloading tenant"); } /** * Starts a tenant flow. This will stack the current CarbonContext and begin * a new nested flow which can have an entirely different context. This is * ideal for scenarios where multiple super-tenant and sub-tenant phases are * required within as a single block of execution. */ public void startTenantFlow() { Stack<CarbonContextDataHolder> carbonContextDataHolders = parentContextHolderStack.get(); if (carbonContextDataHolders == null) { carbonContextDataHolders = new Stack<CarbonContextDataHolder>(); parentContextHolderStack.set(carbonContextDataHolders); } carbonContextDataHolders.push(currentContextHolder.get()); currentContextHolder.remove(); } /** * This will end the tenant flow and restore the previous CarbonContext. */ public void endTenantFlow() { Stack<CarbonContextDataHolder> carbonContextDataHolders = parentContextHolderStack.get(); if (carbonContextDataHolders != null) { currentContextHolder.set(carbonContextDataHolders.pop()); } } /** * Default constructor to disallow creation of the CarbonContext. */ private CarbonContextDataHolder() { this.tenantId = org.wso2.carbon.base.MultitenantConstants.INVALID_TENANT_ID; this.username = null; this.tenantDomain = null; } /** * Constructor that can be used to create clones. * * @param carbonContextHolder the CarbonContext holder instance of which the clone will be * created from. */ public CarbonContextDataHolder(CarbonContextDataHolder carbonContextHolder) { this.tenantId = carbonContextHolder.tenantId; this.username = carbonContextHolder.username; this.tenantDomain = carbonContextHolder.tenantDomain; if (carbonContextHolder.properties != null) { this.properties = new HashMap<String, Object>(carbonContextHolder.properties); } } /** * Method to obtain the tenant id on this CarbonContext instance. * * @return the tenant id. */ public int getTenantId() { return tenantId; } /** * Method to set the tenant id on this CarbonContext instance. * * @param tenantId the tenant id. */ public void setTenantId(int tenantId) { try { if (this.tenantId == org.wso2.carbon.base.MultitenantConstants.INVALID_TENANT_ID || this.tenantId == org.wso2.carbon.base.MultitenantConstants.SUPER_TENANT_ID) { this.tenantId = tenantId; } else if (this.tenantId != tenantId) { StackTraceElement[] traces = Thread.currentThread().getStackTrace(); if (!isAllowedToChangeTenantDomain(traces)) { throw new IllegalStateException( "Trying to set the domain from " + this.tenantId + " to " + tenantId); } } } catch (IllegalStateException e) { log.error(e.getMessage(), e); } } /** * Method to obtain the username on this CarbonContext instance. * * @return the username. */ public String getUsername() { return username; } /** * Method to set the username on this CarbonContext instance. * * @param username the username. */ public void setUsername(String username) { CarbonUtils.checkSecurity(); this.username = username; } /** * Method to obtain the tenant domain on this CarbonContext instance. * * @return the tenant domain. */ public String getTenantDomain() { return tenantDomain; } /** * Method to set the tenant domain on this CarbonContext instance. * * @param domain the tenant domain. */ public void setTenantDomain(String domain) { try { if (this.tenantDomain == null || this.tenantDomain .equals(org.wso2.carbon.base.MultitenantConstants.SUPER_TENANT_DOMAIN_NAME)) { this.tenantDomain = domain; } else if (!tenantDomain.equals(domain)) { StackTraceElement[] traces = Thread.currentThread().getStackTrace(); if (!isAllowedToChangeTenantDomain(traces)) { throw new IllegalStateException( "Trying to set the domain from " + this.tenantDomain + " to " + domain); } } } catch (IllegalStateException e) { log.error(e.getMessage(), e); } } /** * Method to obtain a property on this CarbonContext instance. * * @param name the property name. * @return the value of the property by the given name. */ public Object getProperty(String name) { if (properties == null) { return null; } return properties.get(name); } /** * Method to set a property on this CarbonContext instance. * * @param name the property name. * @param value the value to be set to the property by the given name. */ public void setProperty(String name, Object value) { if (properties == null) { properties = new HashMap<String, Object>(); } properties.put(name, value); } // Method to cleanup all properties. private void cleanupProperties() { // This method would be called to reclaim memory. Therefore, this might // be called on an // object which has been partially garbage collected. Even unlikely, it // might be possible // that the object exists without any field-references, until all // WeakReferences are // cleaned-up. if (properties != null) { properties.clear(); } } private boolean isAllowedToChangeTenantDomain(StackTraceElement[] traces) { boolean allowChange = false; if ((traces.length > CARBON_AUTHENTICATION_UTIL_INDEX) && (traces[CARBON_AUTHENTICATION_UTIL_INDEX] .getClassName().equals(CARBON_AUTHENTICATION_UTIL_CLASS))) { allowChange = true; } else if ((traces.length > CARBON_AUTHENTICATION_HANDLER_INDEX) && traces[CARBON_AUTHENTICATION_HANDLER_INDEX].getClassName() .equals(CARBON_AUTHENTICATION_HANDLER_CLASS)) { allowChange = true; } return allowChange; } /** * This method will destroy the current CarbonContext holder. */ public static void destroyCurrentCarbonContextHolder() { currentContextHolder.remove(); parentContextHolderStack.remove(); } private static class CarbonContextCleanupTask implements UnloadTenantTask<CarbonContextDataHolder> { private Map<Integer, ArrayList<WeakReference<CarbonContextDataHolder>>> contextHolderList = new ConcurrentHashMap<Integer, ArrayList<WeakReference<CarbonContextDataHolder>>>(); public void register(int tenantId, CarbonContextDataHolder contextHolderBase) { ArrayList<WeakReference<CarbonContextDataHolder>> list = contextHolderList.get(tenantId); if (list == null) { list = new ArrayList<WeakReference<CarbonContextDataHolder>>(); list.add(new WeakReference<CarbonContextDataHolder>(contextHolderBase)); contextHolderList.put(tenantId, list); } else { list.add(new WeakReference<CarbonContextDataHolder>(contextHolderBase)); } } public void cleanup(int tenantId) { ArrayList<WeakReference<CarbonContextDataHolder>> list = contextHolderList.remove(tenantId); if (list != null) { for (WeakReference<CarbonContextDataHolder> carbonContextHolderBaseRef : list) { CarbonContextDataHolder carbonContextHolderBase = carbonContextHolderBaseRef.get(); if (carbonContextHolderBase != null) { carbonContextHolderBase.cleanupProperties(); } } list.clear(); } } } }