org.wso2.carbon.core.multitenancy.utils.TenantAxisUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.wso2.carbon.core.multitenancy.utils.TenantAxisUtils.java

Source

/*
 *  Copyright (c) 2005-2009, 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.core.multitenancy.utils;

import org.apache.axis2.AxisFault;
import org.apache.axis2.context.ConfigurationContext;
import org.apache.axis2.context.ConfigurationContextFactory;
import org.apache.axis2.description.AxisService;
import org.apache.axis2.description.TransportOutDescription;
import org.apache.axis2.engine.AxisConfiguration;
import org.apache.axis2.transport.TransportListener;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.osgi.framework.BundleContext;
import org.osgi.util.tracker.ServiceTracker;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.core.internal.CarbonCoreDataHolder;
import org.wso2.carbon.core.multitenancy.TenantAxisConfigurator;
import org.wso2.carbon.core.multitenancy.eager.TenantEagerLoader;
import org.wso2.carbon.core.multitenancy.transports.DummyTransportListener;
import org.wso2.carbon.core.multitenancy.transports.TenantTransportInDescription;
import org.wso2.carbon.core.multitenancy.transports.TenantTransportSender;
import org.wso2.carbon.core.transports.TransportPersistenceManager;
import org.wso2.carbon.core.util.Utils;
import org.wso2.carbon.registry.core.session.UserRegistry;
import org.wso2.carbon.user.core.tenant.Tenant;
import org.wso2.carbon.user.core.tenant.TenantManager;
import org.wso2.carbon.utils.Axis2ConfigurationContextObserver;
import org.wso2.carbon.utils.CarbonUtils;
import org.wso2.carbon.utils.ServerConstants;
import org.wso2.carbon.utils.multitenancy.MultitenantConstants;
import org.wso2.carbon.utils.multitenancy.MultitenantUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * Utility methods for Tenant Operations at Axis2-level.
 */
@SuppressWarnings("unused")
public final class TenantAxisUtils {

    private static final Log log = LogFactory.getLog(TenantAxisUtils.class);
    private static final String TENANT_CONFIGURATION_CONTEXTS = "tenant.config.contexts";
    private static final String TENANT_CONFIGURATION_CONTEXTS_CREATED = "tenant.config.contexts.created";
    private static CarbonCoreDataHolder dataHolder = CarbonCoreDataHolder.getInstance();
    private static Map<String, ReentrantReadWriteLock> tenantReadWriteLocks = new ConcurrentHashMap<String, ReentrantReadWriteLock>();

    private TenantAxisUtils() {
    }

    /**
     * Get tenant ID from config context
     *
     * @param configCtx The config context
     * @return The tenant ID
     * @deprecated use {@link MultitenantUtils#getTenantId(ConfigurationContext)}
     */
    public static int getTenantId(ConfigurationContext configCtx) {
        return MultitenantUtils.getTenantId(configCtx);
    }

    public static AxisConfiguration getTenantAxisConfiguration(String tenant, ConfigurationContext mainConfigCtx) {
        ConfigurationContext tenantConfigCtx = getTenantConfigurationContext(tenant, mainConfigCtx);
        if (tenantConfigCtx != null) {
            return tenantConfigCtx.getAxisConfiguration();
        }
        return null;
    }

    public static ConfigurationContext getTenantConfigurationContextFromUrl(String url,
            ConfigurationContext mainConfigCtx) {
        String tenantDomain = MultitenantUtils.getTenantDomainFromUrl(url);
        return getTenantConfigurationContext(tenantDomain, mainConfigCtx);
    }

    public static ConfigurationContext getTenantConfigurationContext(String tenantDomain,
            ConfigurationContext mainConfigCtx) {
        ConfigurationContext tenantConfigCtx;

        Boolean isTenantActive;
        try {
            isTenantActive = CarbonCoreDataHolder.getInstance().getRealmService().getTenantManager()
                    .isTenantActive(getTenantId(tenantDomain));
        } catch (Exception e) {
            throw new RuntimeException("Error while getting tenant activation status.", e);
        }

        if (!isTenantActive) {
            throw new RuntimeException("Trying to access inactive tenant domain : " + tenantDomain);
        }

        if (tenantReadWriteLocks.get(tenantDomain) == null) {
            synchronized (tenantDomain.intern()) {
                if (tenantReadWriteLocks.get(tenantDomain) == null) {
                    tenantReadWriteLocks.put(tenantDomain, new ReentrantReadWriteLock());
                }
            }
        }
        Lock tenantReadLock = tenantReadWriteLocks.get(tenantDomain).readLock();
        try {
            tenantReadLock.lock();
            Map<String, ConfigurationContext> tenantConfigContexts = getTenantConfigurationContexts(mainConfigCtx);
            tenantConfigCtx = tenantConfigContexts.get(tenantDomain);
            if (tenantConfigCtx == null) {
                try {
                    tenantConfigCtx = createTenantConfigurationContext(mainConfigCtx, tenantDomain);
                } catch (Exception e) {
                    throw new RuntimeException(
                            "Cannot create tenant ConfigurationContext for tenant " + tenantDomain, e);
                }
            }
            tenantConfigCtx.setProperty(MultitenantConstants.LAST_ACCESSED, System.currentTimeMillis());
        } finally {
            tenantReadLock.unlock();
        }
        return tenantConfigCtx;
    }

    public static long getLastAccessed(String tenantDomain, ConfigurationContext mainConfigCtx) {
        Map<String, ConfigurationContext> tenantConfigContexts = getTenantConfigurationContexts(mainConfigCtx);
        ConfigurationContext tenantConfigCtx = tenantConfigContexts.get(tenantDomain);
        if (tenantConfigCtx != null) {
            Long lastAccessed = (Long) tenantConfigCtx.getProperty(MultitenantConstants.LAST_ACCESSED);
            return lastAccessed == null ? -1 : lastAccessed;
        }
        return -1;
    }

    public static void setTenantAccessed(String tenantDomain, ConfigurationContext mainConfigCtx) {
        getTenantConfigurationContext(tenantDomain, mainConfigCtx);
    }

    /**
     * @param url               will have pattern <some-string>/t/<tenant>/<service>?<some-params>
     * @param mainConfigContext The main ConfigurationContext from the server
     * @return The tenant's AxisService
     * @throws org.apache.axis2.AxisFault If an error occurs while retrieving the AxisService
     */
    public static AxisService getAxisService(String url, ConfigurationContext mainConfigContext) throws AxisFault {
        String[] strings = url.split("/");
        boolean foundTenantDelimiter = false;
        String tenant = null;
        String service = null;
        for (String str : strings) {
            if (!foundTenantDelimiter && str.equals("t")) {
                foundTenantDelimiter = true;
                continue;
            }
            if (foundTenantDelimiter & tenant == null) {
                tenant = str;
                continue;
            }
            if (tenant != null) {
                if (service == null) {
                    service = str;
                } else {
                    service += "/" + str;
                }
            }
        }
        if (service != null) {
            service = service.split("\\?")[0];
            AxisConfiguration tenantAxisConfig = getTenantAxisConfiguration(tenant, mainConfigContext);
            if (tenantAxisConfig != null) {
                return tenantAxisConfig.getServiceForActivation(service);
            }
        }
        return null;
    }

    /**
     * Get all the tenant ConfigurationContexts
     *
     * @param mainConfigCtx Super-tenant Axis2 ConfigurationContext
     * @return the tenant ConfigurationContexts as a Map of "tenant domain -> ConfigurationContext"
     */
    @SuppressWarnings("unchecked")
    public static Map<String, ConfigurationContext> getTenantConfigurationContexts(
            ConfigurationContext mainConfigCtx) {
        Map<String, ConfigurationContext> tenantConfigContexts = (Map<String, ConfigurationContext>) mainConfigCtx
                .getProperty(TENANT_CONFIGURATION_CONTEXTS);
        if (tenantConfigContexts == null) {
            tenantConfigContexts = new ConcurrentHashMap<String, ConfigurationContext>();
            mainConfigCtx.setProperty(TENANT_CONFIGURATION_CONTEXTS, tenantConfigContexts);
        }
        return tenantConfigContexts;
    }

    /**
     * Set the transports for the tenants
     *
     * @param mainConfigCtx The main config context
     * @throws AxisFault If an error occurs while initializing tenant transports
     */
    @SuppressWarnings("unchecked")
    public static void initializeTenantTransports(ConfigurationContext mainConfigCtx) throws AxisFault {
        AxisConfiguration mainAxisConfig = mainConfigCtx.getAxisConfiguration();
        Map<String, ConfigurationContext> tenantConfigContexts = getTenantConfigurationContexts(mainConfigCtx);
        if (tenantConfigContexts != null) {
            for (Map.Entry<String, ConfigurationContext> entry : tenantConfigContexts.entrySet()) {
                String tenantDomain = entry.getKey();
                ConfigurationContext tenantConfigCtx = entry.getValue();
                AxisConfiguration tenantAxisConfig = tenantConfigCtx.getAxisConfiguration();
                // Add the transports that are made available in the main axis2.xml file
                setTenantTransports(mainAxisConfig, tenantDomain, tenantAxisConfig);
            }
        }
    }

    public static void setTenantTransports(AxisConfiguration mainAxisConfig, String tenantDomain,
            AxisConfiguration tenantAxisConfig) throws AxisFault {
        for (String transport : mainAxisConfig.getTransportsIn().keySet()) {
            TenantTransportInDescription tenantTransportIn = new TenantTransportInDescription(transport);
            tenantTransportIn.setMainTransportInDescription(mainAxisConfig.getTransportsIn().get(transport));
            TransportListener mainTransportListener = mainAxisConfig.getTransportIn(transport).getReceiver();
            tenantTransportIn.setReceiver(new DummyTransportListener(mainTransportListener, tenantDomain));
            tenantAxisConfig.addTransportIn(tenantTransportIn);
        }
    }

    /**
     * Create Tenant Axis2 ConfigurationContexts & add them to the main Axis2 ConfigurationContext
     *
     * @param mainConfigCtx Super-tenant Axis2 ConfigurationContext
     * @param tenantDomain  Tenant domain (e.g. foo.com)
     * @return The newly created Tenant ConfigurationContext
     * @throws Exception If an error occurs while creating tenant ConfigurationContext
     */
    private static ConfigurationContext createTenantConfigurationContext(ConfigurationContext mainConfigCtx,
            String tenantDomain) throws Exception {
        synchronized (tenantDomain.intern()) { // lock based on tenant domain
            Map<String, ConfigurationContext> tenantConfigContexts = getTenantConfigurationContexts(mainConfigCtx);
            ConfigurationContext tenantConfigCtx = tenantConfigContexts.get(tenantDomain);
            if (tenantConfigCtx != null) {
                return tenantConfigCtx;
            }
            long tenantLoadingStartTime = System.currentTimeMillis();
            int tenantId = getTenantId(tenantDomain);
            if (tenantId == MultitenantConstants.SUPER_TENANT_ID
                    || tenantId == MultitenantConstants.INVALID_TENANT_ID) {
                throw new Exception("Tenant " + tenantDomain + " does not exist");
            }
            PrivilegedCarbonContext carbonContext = PrivilegedCarbonContext.getThreadLocalCarbonContext();
            carbonContext.setTenantId(tenantId);
            carbonContext.setTenantDomain(tenantDomain);

            tenantConfigCtx = tenantConfigContexts.get(tenantDomain);
            if (tenantConfigCtx != null) {
                return tenantConfigCtx;
            }

            AxisConfiguration mainAxisConfig = mainConfigCtx.getAxisConfiguration();

            dataHolder.getTenantRegistryLoader().loadTenantRegistry(tenantId);

            try {
                UserRegistry tenantConfigRegistry = dataHolder.getRegistryService()
                        .getConfigSystemRegistry(tenantId);
                UserRegistry tenantLocalUserRegistry = dataHolder.getRegistryService().getLocalRepository(tenantId);
                TenantAxisConfigurator tenantAxisConfigurator = new TenantAxisConfigurator(mainAxisConfig,
                        tenantDomain, tenantId, tenantConfigRegistry, tenantLocalUserRegistry);
                doPreConfigContextCreation(tenantId);
                tenantConfigCtx = ConfigurationContextFactory.createConfigurationContext(tenantAxisConfigurator);

                AxisConfiguration tenantAxisConfig = tenantConfigCtx.getAxisConfiguration();

                tenantConfigCtx.setServicePath(CarbonUtils.getAxis2ServicesDir(tenantAxisConfig));
                tenantConfigCtx.setContextRoot("local:/");

                TenantTransportSender transportSender = new TenantTransportSender(mainConfigCtx);
                // Adding transport senders
                HashMap<String, TransportOutDescription> transportSenders = mainAxisConfig.getTransportsOut();
                if (transportSenders != null && !transportSenders.isEmpty()) {
                    for (String strTransport : transportSenders.keySet()) {
                        TransportOutDescription outDescription = new TransportOutDescription(strTransport);
                        outDescription.setSender(transportSender);
                        tenantAxisConfig.addTransportOut(outDescription);
                    }
                }

                // Set the work directory
                tenantConfigCtx.setProperty(ServerConstants.WORK_DIR,
                        mainConfigCtx.getProperty(ServerConstants.WORK_DIR));
                PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantId(tenantId);
                new TransportPersistenceManager(tenantAxisConfig).updateEnabledTransports(
                        tenantAxisConfig.getTransportsIn().values(), tenantAxisConfig.getTransportsOut().values());

                // Notify all observers
                BundleContext bundleContext = dataHolder.getBundleContext();
                if (bundleContext != null) {
                    ServiceTracker tracker = new ServiceTracker(bundleContext,
                            Axis2ConfigurationContextObserver.class.getName(), null);
                    tracker.open();
                    Object[] services = tracker.getServices();
                    if (services != null) {
                        for (Object service : services) {
                            ((Axis2ConfigurationContextObserver) service)
                                    .createdConfigurationContext(tenantConfigCtx);
                        }
                    }
                    tracker.close();
                }
                tenantConfigCtx.setProperty(MultitenantConstants.LAST_ACCESSED, System.currentTimeMillis());

                // Register Capp deployer for this tenant
                Utils.addCAppDeployer(tenantAxisConfig);

                //deploy the services since all the deployers are initialized by now.
                tenantAxisConfigurator.deployServices();

                //tenant config context must only be made after the tenant is fully loaded, and all its artifacts
                //are deployed.
                // -- THIS SHOULD BE THE LAST OPERATION OF THIS METHOD --
                tenantConfigContexts.put(tenantDomain, tenantConfigCtx);

                log.info("Loaded tenant " + tenantDomain + " in "
                        + (System.currentTimeMillis() - tenantLoadingStartTime) + " ms");

                return tenantConfigCtx;
            } catch (Exception e) {
                String msg = "Error occurred while running deployment for tenant ";
                log.error(msg + tenantDomain, e);
                throw new Exception(msg, e);
            }
        }
    }

    /**
     * Get the list of all active tenants in the system
     *
     * @param mainConfigCtx The main super-tenant ConfigurationContext
     * @return The list of active tenants
     * @throws Exception If an error occurs while retrieving tenants
     */
    public static List<Tenant> getActiveTenants(ConfigurationContext mainConfigCtx) throws Exception {
        Map<String, ConfigurationContext> tenantConfigContexts = getTenantConfigurationContexts(mainConfigCtx);
        List<Tenant> tenants = new ArrayList<Tenant>();
        try {
            TenantManager tenantManager = dataHolder.getRealmService().getTenantManager();
            for (ConfigurationContext tenantCfgCtx : tenantConfigContexts.values()) {
                Tenant tenant = (Tenant) tenantManager.getTenant(MultitenantUtils.getTenantId(tenantCfgCtx));
                tenants.add(tenant);
            }
        } catch (Exception e) {
            String msg = "Error occurred while getting active tenant list";
            log.error(msg, e);
            throw new Exception(msg, e);
        }
        return tenants;
    }

    /**
     * Get the tenantID given the domain
     *
     * @param tenantDomain The tenant domain
     * @return The tenant ID
     * @throws Exception If an error occurs while retrieving tenant ID
     */
    private static int getTenantId(String tenantDomain) throws Exception {
        return dataHolder.getRealmService().getTenantManager().getTenantId(tenantDomain);
    }

    /**
     * Traverse the list of tenants and cleanup tenants which have been idling for longer than
     * <code>tenantIdleTimeMillis</code>
     *
     * @param tenantIdleTimeMillis The maximum tenant idle time in milliseconds
     */
    public static void cleanupTenants(long tenantIdleTimeMillis) {
        ConfigurationContext mainServerConfigContext = CarbonCoreDataHolder.getInstance()
                .getMainServerConfigContext();
        if (mainServerConfigContext == null) {
            return;
        }
        Map<String, ConfigurationContext> tenantConfigContexts = getTenantConfigurationContexts(
                mainServerConfigContext);
        ArrayList<String> eagerLoadedTenants = TenantEagerLoader.getEagerLoadingTenantList();
        for (Map.Entry<String, ConfigurationContext> entry : tenantConfigContexts.entrySet()) {
            String tenantDomain = entry.getKey();
            // Only unload the tenant if it's not in EagerLoading tenant list
            if (!eagerLoadedTenants.contains(tenantDomain)) {
                synchronized (tenantDomain.intern()) {
                    ConfigurationContext tenantCfgCtx = entry.getValue();
                    Long lastAccessed = (Long) tenantCfgCtx.getProperty(MultitenantConstants.LAST_ACCESSED);
                    if (System.currentTimeMillis() - lastAccessed >= tenantIdleTimeMillis) {
                        // Get the write lock.
                        Lock tenantWriteLock = tenantReadWriteLocks.get(tenantDomain).writeLock();
                        tenantWriteLock.lock();
                        try {
                            lastAccessed = (Long) tenantCfgCtx.getProperty(MultitenantConstants.LAST_ACCESSED);
                            if (System.currentTimeMillis() - lastAccessed >= tenantIdleTimeMillis) {
                                try {
                                    PrivilegedCarbonContext.startTenantFlow();
                                    // Creating CarbonContext object for these threads.
                                    PrivilegedCarbonContext carbonContext = PrivilegedCarbonContext
                                            .getThreadLocalCarbonContext();
                                    carbonContext.setTenantDomain(tenantDomain, true);

                                    // Terminating idle tenant configuration contexts.
                                    terminateTenantConfigContext(tenantCfgCtx);
                                    tenantConfigContexts.remove(tenantDomain);
                                } finally {
                                    PrivilegedCarbonContext.endTenantFlow();
                                }
                            }
                        } finally {
                            tenantWriteLock.unlock();
                        }
                    }
                }
            }
        }
    }

    /**
     * Calculate the tenant domain from the complete URL
     *
     * @param url - incoming URL
     * @return - Tenant domain
     */
    public static String getTenantDomain(String url) {
        String[] strings = url.split("/");
        boolean foundTenantDelimiter = false;
        String tenant = null;
        for (String str : strings) {
            if (!foundTenantDelimiter && str.equals("t")) {
                foundTenantDelimiter = true;
                continue;
            }
            if (foundTenantDelimiter) {
                tenant = str;
                break;
            }
        }
        return tenant;
    }

    /**
     * Terminate the provided Tenant ConfigurationContext
     *
     * @param tenantCfgCtx The tenant ConfigurationContext which needs to be terminated
     */
    public static void terminateTenantConfigContext(ConfigurationContext tenantCfgCtx) {
        ConfigurationContext mainServerConfigContext = CarbonCoreDataHolder.getInstance()
                .getMainServerConfigContext();
        Map<String, ConfigurationContext> tenantConfigContexts = getTenantConfigurationContexts(
                mainServerConfigContext);
        String tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain();
        log.info("Starting to clean tenant : " + tenantDomain);
        tenantCfgCtx.getAxisConfiguration().getConfigurator().cleanup();
        try {
            doPreConfigContextTermination(tenantCfgCtx);
            tenantCfgCtx.terminate();
            doPostConfigContextTermination(tenantCfgCtx);
            tenantConfigContexts.remove(tenantDomain);
            log.info("Cleaned up tenant " + tenantDomain);
        } catch (AxisFault e) {
            log.error("Cannot cleanup ConfigurationContext of tenant " + tenantDomain, e);
        }
    }

    private static void doPreConfigContextCreation(int tenantId) {
        BundleContext bundleContext = CarbonCoreDataHolder.getInstance().getBundleContext();
        if (bundleContext != null) {
            ServiceTracker tracker = new ServiceTracker(bundleContext,
                    Axis2ConfigurationContextObserver.class.getName(), null);
            tracker.open();
            Object[] services = tracker.getServices();
            if (services != null) {
                for (Object service : services) {
                    ((Axis2ConfigurationContextObserver) service).creatingConfigurationContext(tenantId);
                }
            }
            tracker.close();
        }
    }

    private static void doPreConfigContextTermination(ConfigurationContext tenantCfgCtx) {
        BundleContext bundleContext = CarbonCoreDataHolder.getInstance().getBundleContext();
        if (bundleContext != null) {
            ServiceTracker tracker = new ServiceTracker(bundleContext,
                    Axis2ConfigurationContextObserver.class.getName(), null);
            tracker.open();
            Object[] services = tracker.getServices();
            if (services != null) {
                for (Object service : services) {
                    ((Axis2ConfigurationContextObserver) service).terminatingConfigurationContext(tenantCfgCtx);
                }
            }
            tracker.close();
        }
    }

    private static void doPostConfigContextTermination(ConfigurationContext tenantCfgCtx) {
        BundleContext bundleContext = CarbonCoreDataHolder.getInstance().getBundleContext();
        if (bundleContext != null) {
            ServiceTracker tracker = new ServiceTracker(bundleContext,
                    Axis2ConfigurationContextObserver.class.getName(), null);
            tracker.open();
            Object[] services = tracker.getServices();
            if (services != null) {
                for (Object service : services) {
                    ((Axis2ConfigurationContextObserver) service).terminatedConfigurationContext(tenantCfgCtx);
                }
            }
            tracker.close();
        }
    }
}