org.wso2.carbon.appmgt.impl.APIProviderImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.wso2.carbon.appmgt.impl.APIProviderImpl.java

Source

/*
*  Copyright (c) 2005-2013, 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.appmgt.impl;

import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMFactory;
import org.apache.axiom.om.util.AXIOMUtil;
import org.apache.axis2.Constants;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.StringUtils;
import org.json.simple.JSONObject;
import org.wso2.carbon.appmgt.api.APIProvider;
import org.wso2.carbon.appmgt.api.AppManagementException;
import org.wso2.carbon.appmgt.api.EntitlementService;
import org.wso2.carbon.appmgt.api.dto.UserApplicationAPIUsage;
import org.wso2.carbon.appmgt.api.model.APIIdentifier;
import org.wso2.carbon.appmgt.api.model.APIStatus;
import org.wso2.carbon.appmgt.api.model.APPLifecycleActions;
import org.wso2.carbon.appmgt.api.model.App;
import org.wso2.carbon.appmgt.api.model.AppDefaultVersion;
import org.wso2.carbon.appmgt.api.model.AppStore;
import org.wso2.carbon.appmgt.api.model.BusinessOwner;
import org.wso2.carbon.appmgt.api.model.Documentation;
import org.wso2.carbon.appmgt.api.model.EntitlementPolicyGroup;
import org.wso2.carbon.appmgt.api.model.ExternalAppStorePublisher;
import org.wso2.carbon.appmgt.api.model.FileContent;
import org.wso2.carbon.appmgt.api.model.JavaPolicy;
import org.wso2.carbon.appmgt.api.model.LifeCycleEvent;
import org.wso2.carbon.appmgt.api.model.MobileApp;
import org.wso2.carbon.appmgt.api.model.OneTimeDownloadLink;
import org.wso2.carbon.appmgt.api.model.Provider;
import org.wso2.carbon.appmgt.api.model.SSOProvider;
import org.wso2.carbon.appmgt.api.model.Subscriber;
import org.wso2.carbon.appmgt.api.model.Tag;
import org.wso2.carbon.appmgt.api.model.Tier;
import org.wso2.carbon.appmgt.api.model.Usage;
import org.wso2.carbon.appmgt.api.model.WebApp;
import org.wso2.carbon.appmgt.api.model.entitlement.EntitlementPolicy;
import org.wso2.carbon.appmgt.api.model.entitlement.EntitlementPolicyPartial;
import org.wso2.carbon.appmgt.api.model.entitlement.EntitlementPolicyValidationResult;
import org.wso2.carbon.appmgt.api.model.entitlement.XACMLPolicyTemplateContext;
import org.wso2.carbon.appmgt.impl.dao.AppMDAO;
import org.wso2.carbon.appmgt.impl.dto.Environment;
import org.wso2.carbon.appmgt.impl.dto.TierPermissionDTO;
import org.wso2.carbon.appmgt.impl.entitlement.EntitlementServiceFactory;
import org.wso2.carbon.appmgt.impl.idp.sso.SSOConfiguratorUtil;
import org.wso2.carbon.appmgt.impl.observers.APIStatusObserverList;
import org.wso2.carbon.appmgt.impl.service.ServiceReferenceHolder;
import org.wso2.carbon.appmgt.impl.template.APITemplateBuilder;
import org.wso2.carbon.appmgt.impl.template.APITemplateBuilderImpl;
import org.wso2.carbon.appmgt.impl.utils.APINameComparator;
import org.wso2.carbon.appmgt.impl.utils.AppManagerUtil;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.governance.api.common.dataobjects.GovernanceArtifact;
import org.wso2.carbon.governance.api.exception.GovernanceException;
import org.wso2.carbon.governance.api.generic.GenericArtifactManager;
import org.wso2.carbon.governance.api.generic.dataobjects.GenericArtifact;
import org.wso2.carbon.governance.api.util.GovernanceUtils;
import org.wso2.carbon.registry.common.CommonConstants;
import org.wso2.carbon.registry.core.ActionConstants;
import org.wso2.carbon.registry.core.Association;
import org.wso2.carbon.registry.core.CollectionImpl;
import org.wso2.carbon.registry.core.Registry;
import org.wso2.carbon.registry.core.RegistryConstants;
import org.wso2.carbon.registry.core.Resource;
import org.wso2.carbon.registry.core.config.RegistryContext;
import org.wso2.carbon.registry.core.exceptions.RegistryException;
import org.wso2.carbon.registry.core.jdbc.realm.RegistryAuthorizationManager;
import org.wso2.carbon.registry.core.session.UserRegistry;
import org.wso2.carbon.registry.core.utils.RegistryUtils;
import org.wso2.carbon.user.api.AuthorizationManager;
import org.wso2.carbon.user.api.UserStoreException;
import org.wso2.carbon.utils.multitenancy.MultitenantConstants;
import org.wso2.carbon.utils.multitenancy.MultitenantUtils;

import javax.cache.Cache;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * This class provides the core WebApp provider functionality. It is implemented in a very
 * self-contained and 'pure' manner, without taking requirements like security into account,
 * which are subject to frequent change. Due to this 'pure' nature and the significance of
 * the class to the overall WebApp management functionality, the visibility of the class has
 * been reduced to package level. This means we can still use it for internal purposes and
 * possibly even extend it, but it's totally off the limits of the users. Users wishing to
 * programmatically access this functionality should use one of the extensions of this
 * class which is visible to them. These extensions may add additional features like
 * security to this class.
 */
class APIProviderImpl extends AbstractAPIManager implements APIProvider {

    public APIProviderImpl(String username) throws AppManagementException {
        super(username);
    }

    /**
     * Delete a given business owner.
     *
     * @param businessOwnerId Id of the business owner
     * @return Whether business owner was deleted or not
     * @throws AppManagementException on error while trying to delete business owner
     */
    @Override
    public boolean deleteBusinessOwner(int businessOwnerId) throws AppManagementException {
        boolean isBusinessOwnerAssociatedWithApps = appMDAO.isBusinessOwnerAssociatedWithApps(businessOwnerId,
                registry, tenantDomain);
        if (!isBusinessOwnerAssociatedWithApps) {
            appMDAO.deleteBusinessOwner(businessOwnerId);
            // Return true if business owner is successfully deleted.
            return true;
        }
        // Return false if business owner is associated with one or more web apps.
        return false;
    }

    /**
     * Update a given business owner.
     *
     * @param businessOwner {@link BusinessOwner} object
     * @return Whether business owner was updated or not
     * @throws AppManagementException on error while trying to update business owner
     */
    @Override
    public boolean updateBusinessOwner(BusinessOwner businessOwner) throws AppManagementException {
        boolean isUpdated = false;
        if (appMDAO.getBusinessOwner(businessOwner.getBusinessOwnerId(), tenantId) != null) {
            appMDAO.updateBusinessOwner(businessOwner);
            isUpdated = true;
        }
        return isUpdated;
    }

    /**
     * Get all business owners.
     *
     * @return List of {@link BusinessOwner} objects
     * @throws AppManagementException on error while trying to get business owners
     */
    @Override
    public List<BusinessOwner> getBusinessOwners() throws AppManagementException {
        return appMDAO.getBusinessOwners(tenantId);
    }

    /**
     * Retrieve business owner by given id.
     *
     * @param businessOwnerId Business owner Id.
     * @return {@link BusinessOwner} object
     * @throws AppManagementException on error while trying to get business owner
     */
    @Override
    public BusinessOwner getBusinessOwner(int businessOwnerId) throws AppManagementException {
        return appMDAO.getBusinessOwner(businessOwnerId, tenantId);
    }

    /**
     * Search business owners with pagination.
     *
     * @param startIndex  Start index
     * @param pageSize    Page size
     * @param searchKey Search key
     * @return List of {@link BusinessOwner} objects
     * @throws AppManagementException on error while trying to search business owners
     */
    @Override
    public List<BusinessOwner> searchBusinessOwners(int startIndex, int pageSize, String searchKey)
            throws AppManagementException {
        return appMDAO.searchBusinessOwners(startIndex, pageSize, searchKey, tenantId);
    }

    /**
     * Get the count of business owners.
     *
     * @return Number of business owners.
     * @throws AppManagementException on error while trying to get business owners count
     */
    @Override
    public int getBusinessOwnersCount() throws AppManagementException {
        return appMDAO.getBusinessOwnersCount(tenantId);
    }

    /**
     * Save business owner.
     *
     * @param businessOwner {@link BusinessOwner} object
     * @return Saved business owner id
     * @throws AppManagementException on error while trying to save business owner
     */
    @Override
    public int saveBusinessOwner(BusinessOwner businessOwner) throws AppManagementException {
        return appMDAO.saveBusinessOwner(businessOwner, tenantId);
    }

    /**
     * Get Business owner id by business owner name and email.
     *
     * @param businessOwnerName  Business owner name
     * @param businessOwnerEmail Business owner email
     * @return Business owner id
     * @throws AppManagementException on error while trying to get business owner id
     */
    @Override
    public int getBusinessOwnerId(String businessOwnerName, String businessOwnerEmail)
            throws AppManagementException {
        return appMDAO.getBusinessOwnerId(businessOwnerName, businessOwnerEmail, tenantId);
    }

    /**
     * Returns a list of all #{@link org.wso2.carbon.apimgt.api.model.Provider} available on the system.
     *
     * @return Set<Provider>
     * @throws org.wso2.carbon.apimgt.api.APIManagementException
     *          if failed to get Providers
     */
    public Set<Provider> getAllProviders() throws AppManagementException {
        Set<Provider> providerSet = new HashSet<Provider>();
        GenericArtifactManager artifactManager = AppManagerUtil.getArtifactManager(registry,
                AppMConstants.PROVIDER_KEY);
        try {
            GenericArtifact[] genericArtifact = artifactManager.getAllGenericArtifacts();
            if (genericArtifact == null || genericArtifact.length == 0) {
                return providerSet;
            }
            for (GenericArtifact artifact : genericArtifact) {
                Provider provider = new Provider(artifact.getAttribute(AppMConstants.PROVIDER_OVERVIEW_NAME));
                provider.setDescription(AppMConstants.PROVIDER_OVERVIEW_DESCRIPTION);
                provider.setEmail(AppMConstants.PROVIDER_OVERVIEW_EMAIL);
                providerSet.add(provider);
            }
        } catch (GovernanceException e) {
            handleException("Failed to get all providers", e);
        }
        return providerSet;
    }

    /**
     * Get a list of APIs published by the given provider. If a given WebApp has multiple APIs,
     * only the latest version will
     * be included in this list.
     *
     * @param providerId , provider id
     * @return set of WebApp
     * @throws org.wso2.carbon.apimgt.api.APIManagementException
     *          if failed to get set of WebApp
     */
    public List<WebApp> getAPIsByProvider(String providerId) throws AppManagementException {

        List<WebApp> apiSortedList = new ArrayList<WebApp>();

        try {
            providerId = AppManagerUtil.replaceEmailDomain(providerId);
            String providerPath = AppMConstants.API_ROOT_LOCATION + RegistryConstants.PATH_SEPARATOR + providerId;
            GenericArtifactManager artifactManager = AppManagerUtil.getArtifactManager(registry,
                    AppMConstants.API_KEY);
            Association[] associations = registry.getAssociations(providerPath, AppMConstants.PROVIDER_ASSOCIATION);
            for (Association association : associations) {
                String apiPath = association.getDestinationPath();
                Resource resource = registry.get(apiPath);
                String apiArtifactId = resource.getUUID();
                if (apiArtifactId != null) {
                    GenericArtifact apiArtifact = artifactManager.getGenericArtifact(apiArtifactId);
                    apiSortedList.add(AppManagerUtil.getAPI(apiArtifact, registry));
                } else {
                    throw new GovernanceException("artifact id is null of " + apiPath);
                }
            }

        } catch (RegistryException e) {
            handleException("Failed to get APIs for provider : " + providerId, e);
        }
        Collections.sort(apiSortedList, new APINameComparator());

        return apiSortedList;

    }

    /**
     * Get a list of all the consumers for all APIs
     *
     * @param providerId if of the provider
     * @return Set<Subscriber>
     * @throws org.wso2.carbon.apimgt.api.APIManagementException
     *          if failed to get subscribed APIs of given provider
     */
    public Set<Subscriber> getSubscribersOfProvider(String providerId) throws AppManagementException {

        Set<Subscriber> subscriberSet = null;
        try {
            subscriberSet = appMDAO.getSubscribersOfProvider(providerId);
        } catch (AppManagementException e) {
            handleException("Failed to get Subscribers for : " + providerId, e);
        }
        return subscriberSet;
    }

    /**
     * get details of provider
     *
     * @param providerName name of the provider
     * @return Provider
     * @throws org.wso2.carbon.apimgt.api.APIManagementException
     *          if failed to get Provider
     */
    public Provider getProvider(String providerName) throws AppManagementException {
        Provider provider = null;
        String providerPath = RegistryConstants.GOVERNANCE_REGISTRY_BASE_PATH + AppMConstants.PROVIDERS_PATH
                + RegistryConstants.PATH_SEPARATOR + providerName;
        try {
            GenericArtifactManager artifactManager = AppManagerUtil.getArtifactManager(registry,
                    AppMConstants.PROVIDER_KEY);
            Resource providerResource = registry.get(providerPath);
            String artifactId = providerResource.getUUID();
            if (artifactId == null) {
                throw new AppManagementException("artifact it is null");
            }
            GenericArtifact providerArtifact = artifactManager.getGenericArtifact(artifactId);
            provider = AppManagerUtil.getProvider(providerArtifact);

        } catch (RegistryException e) {
            handleException("Failed to get Provider form : " + providerName, e);
        }
        return provider;
    }

    /**
     * Return Usage of given APIIdentifier
     *
     * @param apiIdentifier APIIdentifier
     * @return Usage
     */
    public Usage getUsageByAPI(APIIdentifier apiIdentifier) {
        return null;
    }

    /**
     * Return Usage of given provider and WebApp
     *
     * @param providerId if of the provider
     * @param apiName    name of the WebApp
     * @return Usage
     */
    public Usage getAPIUsageByUsers(String providerId, String apiName) {
        return null;
    }

    /**
     * Returns usage details of all APIs published by a provider
     *
     * @param providerName Provider Id
     * @return UserApplicationAPIUsages for given provider
     * @throws org.wso2.carbon.apimgt.api.APIManagementException
     *          If failed to get UserApplicationAPIUsage
     */
    public UserApplicationAPIUsage[] getAllAPIUsageByProvider(String providerName) throws AppManagementException {
        return appMDAO.getAllAPIUsageByProvider(providerName);
    }

    /**
     * Shows how a given consumer uses the given WebApp.
     *
     * @param apiIdentifier APIIdentifier
     * @param consumerEmail E-mal Address of consumer
     * @return Usage
     */
    public Usage getAPIUsageBySubscriber(APIIdentifier apiIdentifier, String consumerEmail) {
        return null;
    }

    /**
     * Returns full list of Subscribers of an WebApp
     *
     * @param identifier APIIdentifier
     * @return Set<Subscriber>
     * @throws org.wso2.carbon.apimgt.api.APIManagementException
     *          if failed to get Subscribers
     */
    public Set<Subscriber> getSubscribersOfAPI(APIIdentifier identifier) throws AppManagementException {

        Set<Subscriber> subscriberSet = null;
        try {
            subscriberSet = appMDAO.getSubscribersOfAPI(identifier);
        } catch (AppManagementException e) {
            handleException("Failed to get subscribers for WebApp : " + identifier.getApiName(), e);
        }
        return subscriberSet;
    }

    /**
     * this method returns the Set<APISubscriptionCount> for given provider and api
     *
     * @param identifier APIIdentifier
     * @return Set<APISubscriptionCount>
     * @throws org.wso2.carbon.apimgt.api.APIManagementException
     *          if failed to get APISubscriptionCountByAPI
     */
    public long getAPISubscriptionCountByAPI(APIIdentifier identifier) throws AppManagementException {
        long count = 0L;
        try {
            count = appMDAO.getAPISubscriptionCountByAPI(identifier);
        } catch (AppManagementException e) {
            handleException("Failed to get APISubscriptionCount for: " + identifier.getApiName(), e);
        }
        return count;
    }

    public Map<String, List> getSubscribedAPPsByUsers(String fromDate, String toDate)
            throws AppManagementException {
        Map<String, List> users = new HashMap<String, List>();
        try {
            users = appMDAO.getSubscribedAPPsByUsers(fromDate, toDate, tenantId);
        } catch (AppManagementException e) {
            handleException("Failed to get subscribed apps by users for the period " + fromDate + "to " + toDate,
                    e);
        }
        return users;
    }

    public void addTier(Tier tier) throws AppManagementException {
        addOrUpdateTier(tier, false);
    }

    public void updateTier(Tier tier) throws AppManagementException {
        addOrUpdateTier(tier, true);
    }

    private void addOrUpdateTier(Tier tier, boolean update) throws AppManagementException {
        if (AppMConstants.UNLIMITED_TIER.equals(tier.getName())) {
            throw new AppManagementException(
                    "Changes on the '" + AppMConstants.UNLIMITED_TIER + "' " + "tier are not allowed");
        }

        Set<Tier> tiers = getTiers();
        if (update && !tiers.contains(tier)) {
            throw new AppManagementException("No tier exists by the name: " + tier.getName());
        }

        Set<Tier> finalTiers = new HashSet<Tier>();
        for (Tier tet : tiers) {
            if (!tet.getName().equals(tier.getName())) {
                finalTiers.add(tet);
            }
        }
        finalTiers.add(tier);
        saveTiers(finalTiers);
    }

    private void saveTiers(Collection<Tier> tiers) throws AppManagementException {
        OMFactory fac = OMAbstractFactory.getOMFactory();
        OMElement root = fac.createOMElement(AppMConstants.POLICY_ELEMENT);
        OMElement assertion = fac.createOMElement(AppMConstants.ASSERTION_ELEMENT);
        try {
            Resource resource = registry.newResource();
            for (Tier tier : tiers) {
                String policy = new String(tier.getPolicyContent());
                assertion.addChild(AXIOMUtil.stringToOM(policy));
                // if (tier.getDescription() != null && !"".equals(tier.getDescription())) {
                //     resource.setProperty(AppMConstants.TIER_DESCRIPTION_PREFIX + tier.getName(),
                //              tier.getDescription());
                //  }
            }
            //resource.setProperty(AppMConstants.TIER_DESCRIPTION_PREFIX + AppMConstants.UNLIMITED_TIER,
            //        AppMConstants.UNLIMITED_TIER_DESC);
            root.addChild(assertion);
            resource.setContent(root.toString());
            registry.put(AppMConstants.API_TIER_LOCATION, resource);
        } catch (XMLStreamException e) {
            handleException("Error while constructing tier policy file", e);
        } catch (RegistryException e) {
            handleException("Error while saving tier configurations to the registry", e);
        }
    }

    public void removeTier(Tier tier) throws AppManagementException {
        if (AppMConstants.UNLIMITED_TIER.equals(tier.getName())) {
            throw new AppManagementException(
                    "Changes on the '" + AppMConstants.UNLIMITED_TIER + "' " + "tier are not allowed");
        }

        Set<Tier> tiers = getTiers();
        if (tiers.remove(tier)) {
            saveTiers(tiers);
        } else {
            throw new AppManagementException("No tier exists by the name: " + tier.getName());
        }
    }

    /**
     * Adds a new WebApp to the Store
     *
     * @param app WebApp
     * @throws org.wso2.carbon.appmgt.api.AppManagementException
     *          if failed to add WebApp
     */
    public void addWebApp(WebApp app) throws AppManagementException {
        try {
            createAPI(app);
            //            appMDAO.addWebApp(app);
            //            if (AppManagerUtil.isAPIManagementEnabled()) {
            //               Cache contextCache = AppManagerUtil.getAPIContextCache();
            //               Boolean apiContext = null;
            //               if (contextCache.get(app.getContext()) != null) {
            //                  apiContext = Boolean.parseBoolean(contextCache.get(app.getContext()).toString());
            //               }
            //               if (apiContext == null) {
            //                    contextCache.put(app.getContext(), true);
            //                }
            //            }
        } catch (AppManagementException e) {
            throw new AppManagementException("Error in adding WebApp :" + app.getId().getApiName(), e);
        }
    }

    /**
     * Create a new mobile applcation artifact
     *
     * @param mobileApp Mobile App
     * @throws org.wso2.carbon.appmgt.api.AppManagementException
     */
    public String createMobileApp(MobileApp mobileApp) throws AppManagementException {
        String artifactId = null;
        try {
            GenericArtifactManager artifactManager = AppManagerUtil.getArtifactManager(registry,
                    AppMConstants.MOBILE_ASSET_TYPE);
            final String appName = mobileApp.getAppName();

            Map<String, List<String>> attributeListMap = new HashMap<String, List<String>>();
            attributeListMap.put(AppMConstants.API_OVERVIEW_NAME, new ArrayList<String>() {
                {
                    add(appName);
                }
            });
            GenericArtifact[] existingArtifacts = artifactManager.findGenericArtifacts(attributeListMap);
            if (existingArtifacts != null && existingArtifacts.length > 0) {
                handleResourceAlreadyExistsException(
                        "A duplicate mobile application already exists for name : " + mobileApp.getAppName());
            }
            registry.beginTransaction();
            GenericArtifact genericArtifact = artifactManager
                    .newGovernanceArtifact(new QName(mobileApp.getAppName()));
            GenericArtifact artifact = AppManagerUtil.createMobileAppArtifactContent(genericArtifact, mobileApp);
            artifactManager.addGenericArtifact(artifact);
            artifactId = artifact.getId();
            changeLifeCycleStatus(AppMConstants.MOBILE_ASSET_TYPE, artifactId,
                    APPLifecycleActions.CREATE.getStatus());
            String artifactPath = GovernanceUtils.getArtifactPath(registry, artifact.getId());
            Set<String> tagSet = mobileApp.getTags();
            if (tagSet != null) {
                for (String tag : tagSet) {
                    registry.applyTag(artifactPath, tag);
                }
            }

            if (mobileApp.getAppVisibility() != null) {
                AppManagerUtil.setResourcePermissions(mobileApp.getAppProvider(),
                        AppMConstants.API_RESTRICTED_VISIBILITY, mobileApp.getAppVisibility(), artifactPath);
            }
            registry.commitTransaction();
        } catch (RegistryException e) {
            try {
                registry.rollbackTransaction();
            } catch (RegistryException re) {
                handleException("Error while rolling back the transaction for mobile application: "
                        + mobileApp.getAppName(), re);
            }
            handleException("Error occurred while creating the mobile application : " + mobileApp.getAppName(), e);
        }
        return artifactId;
    }

    @Override
    public String createWebApp(WebApp webApp) throws AppManagementException {

        final String appName = webApp.getId().getApiName();
        try {
            GenericArtifactManager artifactManager = AppManagerUtil.getArtifactManager(registry,
                    AppMConstants.WEBAPP_ASSET_TYPE);
            Map<String, List<String>> attributeListMap = new HashMap<String, List<String>>();
            attributeListMap.put(AppMConstants.API_OVERVIEW_NAME, new ArrayList<String>() {
                {
                    add(appName);
                }
            });
            GenericArtifact[] existingArtifacts = artifactManager.findGenericArtifacts(attributeListMap);

            if (existingArtifacts != null && existingArtifacts.length > 0) {
                handleResourceAlreadyExistsException("A duplicate webapp already exists with name : " + appName);
            }
        } catch (GovernanceException e) {
            handleException("Error occurred while checking existence for webapp with name '" + appName);
        }
        AppRepository appRepository = new DefaultAppRepository(registry);
        String appId = appRepository.saveApp(webApp);
        return appId;

    }

    /**
     * Create new version of the application
     * @param app applictaion
     * @return app UUID
     * @throws AppManagementException
     */
    @Override
    public String createNewVersion(App app) throws AppManagementException {
        AppRepository appRepository = new DefaultAppRepository(registry);
        String uuid = appRepository.createNewVersion(app);
        return uuid;
    }

    /**
     * Retrieve webapp for the given uuid
     * @param uuid uuid of the Application
     * @return Webapp
     * @throws AppManagementException
     */
    @Override
    public WebApp getWebApp(String uuid) throws AppManagementException {
        GenericArtifact artifact = null;
        WebApp webApp = null;

        try {
            GenericArtifactManager artifactManager = AppManagerUtil.getArtifactManager(registry,
                    AppMConstants.WEBAPP_ASSET_TYPE);
            artifact = artifactManager.getGenericArtifact(uuid);
            if (artifact == null) {
                handleResourceNotFoundException("Webapp does not exist with app id :" + uuid);
            }
            webApp = AppManagerUtil.getAPI(artifact, registry);

        } catch (GovernanceException e) {
            handleException("Error occurred while retrieving webapp registry artifact with uuid " + uuid);
        }
        return webApp;
    }

    /**
     * Retrieve webapp for the given uuid
     * @param uuid uuid of the Application
     * @return Webapp
     * @throws AppManagementException
     */
    @Override
    public MobileApp getMobileApp(String uuid) throws AppManagementException {
        GenericArtifact artifact = null;
        MobileApp mobileApp = null;

        try {
            GenericArtifactManager artifactManager = AppManagerUtil.getArtifactManager(registry,
                    AppMConstants.MOBILE_ASSET_TYPE);
            artifact = artifactManager.getGenericArtifact(uuid);
            if (artifact != null) {
                mobileApp = AppManagerUtil.getMobileApp(artifact);
            }

        } catch (GovernanceException e) {
            handleException("Error occurred while retrieving webapp registry artifact with uuid " + uuid);
        }
        return mobileApp;
    }

    private String createWebAppArtifact(WebApp webApp) throws AppManagementException {
        String artifactId = null;

        GenericArtifactManager artifactManager = null;
        try {
            final String webAppName = webApp.getId().getApiName();
            Map<String, List<String>> attributeListMap = new HashMap<String, List<String>>();

            artifactManager = AppManagerUtil.getArtifactManager(registry, AppMConstants.WEBAPP_ASSET_TYPE);

            attributeListMap.put(AppMConstants.API_OVERVIEW_NAME, new ArrayList<String>() {
                {
                    add(webAppName);
                }
            });
            GenericArtifact[] existingArtifacts = artifactManager.findGenericArtifacts(attributeListMap);
            if (existingArtifacts != null && existingArtifacts.length > 0) {
                handleResourceAlreadyExistsException(
                        "A duplicate web application already exists for name : " + webAppName);
            }
            registry.beginTransaction();
            GenericArtifact genericArtifact = artifactManager
                    .newGovernanceArtifact(new QName(webApp.getId().getApiName()));
            GenericArtifact artifact = AppManagerUtil.createWebAppArtifactContent(genericArtifact, webApp);
            artifactManager.addGenericArtifact(artifact);
            artifactId = artifact.getId();
            changeLifeCycleStatus(AppMConstants.WEBAPP_LIFE_CYCLE, artifactId,
                    APPLifecycleActions.CREATE.getStatus());
            String artifactPath = GovernanceUtils.getArtifactPath(registry, artifact.getId());

            Set<String> tagSet = webApp.getTags();
            if (tagSet != null) {
                for (String tag : tagSet) {
                    registry.applyTag(artifactPath, tag);
                }
            }
            if (webApp.getAppVisibility() != null) {
                AppManagerUtil.setResourcePermissions(webApp.getId().getProviderName(),
                        AppMConstants.API_RESTRICTED_VISIBILITY, webApp.getAppVisibility(), artifactPath);
            }
            String providerPath = AppManagerUtil.getAPIProviderPath(webApp.getId());
            //provider ------provides----> WebApp
            registry.addAssociation(providerPath, artifactPath, AppMConstants.PROVIDER_ASSOCIATION);
            registry.commitTransaction();
        } catch (RegistryException e) {
            try {
                registry.rollbackTransaction();
            } catch (RegistryException re) {
                handleException("Error while rolling back the transaction for web application: "
                        + webApp.getId().getApiName(), re);
            }
            handleException("Error occurred while creating the web application : " + webApp.getId().getApiName(),
                    e);
        }
        return artifactId;
    }

    /**
     * Generates entitlement policies for the given app.
     *
     * @param apiIdentifier@throws AppManagementException
     * @param authorizedAdminCookie      Authorized cookie to access IDP admin services
     */
    @Override
    public void generateEntitlementPolicies(APIIdentifier apiIdentifier, String authorizedAdminCookie)
            throws AppManagementException {

        AppManagerConfiguration config = ServiceReferenceHolder.getInstance().getAPIManagerConfigurationService()
                .getAPIManagerConfiguration();

        List<XACMLPolicyTemplateContext> xacmlPolicyTemplateContexts = appMDAO
                .getEntitlementPolicyTemplateContexts(apiIdentifier);

        if (xacmlPolicyTemplateContexts != null && !xacmlPolicyTemplateContexts.isEmpty()) {
            EntitlementService entitlementService = EntitlementServiceFactory.getEntitlementService(config,
                    authorizedAdminCookie);

            entitlementService.generateAndSaveEntitlementPolicies(xacmlPolicyTemplateContexts);

            // Update URL mapping => XACML partial mapping with the generated policy IDs.
            appMDAO.updateURLEntitlementPolicyPartialMappings(xacmlPolicyTemplateContexts);
        }
    }

    /**
     * Updates given entitlement policies.
     *
     * @param policies        Entitlement policies to be updated.
     * @param authorizedAdminCookie Authorized cookie to access IDP admin services
     * @throws org.wso2.carbon.appmgt.api.AppManagementException
     */
    @Override
    public void updateEntitlementPolicies(List<EntitlementPolicy> policies, String authorizedAdminCookie)
            throws AppManagementException {

        if (policies == null || policies.isEmpty()) {
            return;
        }

        AppManagerConfiguration config = ServiceReferenceHolder.getInstance().getAPIManagerConfigurationService()
                .getAPIManagerConfiguration();
        EntitlementService entitlementService = EntitlementServiceFactory.getEntitlementService(config,
                authorizedAdminCookie);

        for (EntitlementPolicy policy : policies) {
            entitlementService.updatePolicy(policy);
        }
    }

    /**
     * Get entitlement policy content from policy id.
     *
     * @param policyId        Entitlement policy id
     * @param authorizedAdminCookie Authorized cookie to access IDP admin services
     * @return entitlement policy content
     * @throws AppManagementException on error while trying to get entitlement policy
     */
    @Override
    public String getEntitlementPolicy(String policyId, String authorizedAdminCookie)
            throws AppManagementException {
        if (policyId == null) {
            return null;
        }
        AppManagerConfiguration config = ServiceReferenceHolder.getInstance().getAPIManagerConfigurationService()
                .getAPIManagerConfiguration();

        EntitlementService entitlementService = EntitlementServiceFactory.getEntitlementService(config,
                authorizedAdminCookie);
        return entitlementService.getPolicyContent(policyId);
    }

    @Override
    public int getWebAppId(String uuid) throws AppManagementException {
        return appMDAO.getWebAppId(uuid);
    }

    @Override
    public int saveEntitlementPolicyPartial(String policyPartialName, String policyPartial, boolean isSharedPartial,
            String policyAuthor, String policyPartialDesc) throws AppManagementException {
        return appMDAO.saveEntitlementPolicyPartial(policyPartialName, policyPartial, isSharedPartial, policyAuthor,
                policyPartialDesc, tenantId);
    }

    @Override
    public boolean updateEntitlementPolicyPartial(int policyPartialId, String policyPartial, String author,
            boolean isShared, String policyPartialDesc, String authorizedAdminCookie)
            throws AppManagementException {
        appMDAO.updateEntitlementPolicyPartial(policyPartialId, policyPartial, author, isShared, policyPartialDesc);

        // Regenerate XACML policies of the apps which are using the updated policy partial.
        List<APIIdentifier> associatedApps = getAssociatedApps(policyPartialId);

        for (APIIdentifier associatedApp : associatedApps) {
            generateEntitlementPolicies(associatedApp, authorizedAdminCookie);
        }

        return true;
    }

    @Override
    public EntitlementPolicyPartial getPolicyPartial(int policyPartialId) throws AppManagementException {
        return appMDAO.getPolicyPartial(policyPartialId);
    }

    @Override
    public List<APIIdentifier> getAssociatedApps(int policyPartialId) throws AppManagementException {
        return appMDAO.getAssociatedApps(policyPartialId);
    }

    @Override
    public boolean deleteEntitlementPolicyPartial(int policyPartialId, String author)
            throws AppManagementException {
        return appMDAO.deletePolicyPartial(policyPartialId, author);
    }

    @Override
    public List<EntitlementPolicyPartial> getSharedPolicyPartialsList() throws AppManagementException {
        return appMDAO.getSharedEntitlementPolicyPartialsList(tenantId);
    }

    /**
     * Get Policy Groups Application wise
     *
     * @param appId Application Id
     * @return List of policy groups
     * @throws AppManagementException
     */
    @Override
    public List<EntitlementPolicyGroup> getPolicyGroupListByApplication(int appId) throws AppManagementException {
        return appMDAO.getPolicyGroupListByApplication(appId);
    }

    /**
     * Retrieves TRACKING_CODE sequences from APM_APP Table
     *@param uuid : Application UUID
     *@return TRACKING_CODE
     *@throws org.wso2.carbon.appmgt.api.AppManagementException
     */
    @Override
    public String getTrackingID(String uuid) throws AppManagementException {
        return appMDAO.getTrackingID(uuid);
    }

    @Override
    public EntitlementPolicyValidationResult validateEntitlementPolicyPartial(String policyPartial)
            throws AppManagementException {

        AppManagerConfiguration config = ServiceReferenceHolder.getInstance().getAPIManagerConfigurationService()
                .getAPIManagerConfiguration();

        EntitlementService entitlementService = EntitlementServiceFactory.getEntitlementService(config);
        return entitlementService.validatePolicyPartial(policyPartial);
    }

    /**
     * Persist WebApp Status into a property of WebApp Registry resource
     *
     * @param artifactId WebApp artifact ID
     * @param apiStatus Current status of the WebApp
     * @throws org.wso2.carbon.appmgt.api.AppManagementException on error
     */
    private void saveAPIStatus(String artifactId, String apiStatus) throws AppManagementException {
        try {
            Resource resource = registry.get(artifactId);
            if (resource != null) {
                String propValue = resource.getProperty(AppMConstants.API_STATUS);
                if (propValue == null) {
                    resource.addProperty(AppMConstants.API_STATUS, apiStatus);
                } else {
                    resource.setProperty(AppMConstants.API_STATUS, apiStatus);
                }
                registry.put(artifactId, resource);
            }
        } catch (RegistryException e) {
            handleException("Error while adding WebApp", e);
        }
    }

    /**
     * Updates an existing WebApp
     *
     * @param api             WebApp
     * @param authorizedAdminCookie Authorized cookie to access IDP admin services
     * @throws AppManagementException if failed to update WebApp
     */
    public void updateAPI(WebApp api, String authorizedAdminCookie) throws AppManagementException {
        WebApp oldApi = getAPI(api.getId());
        if (oldApi.getStatus().equals(api.getStatus())) {
            try {

                //boolean updatePermissions = false;
                /*if(!oldApi.getVisibility().equals(api.getVisibility()) || (oldApi.getVisibility().equals(AppMConstants.API_RESTRICTED_VISIBILITY) && !api.getVisibleRoles().equals(oldApi.getVisibleRoles()))){
                updatePermissions = true;
                }*/
                //updateApiArtifact(api, true,updatePermissions);
                if (!oldApi.getContext().equals(api.getContext())) {
                    api.setApiHeaderChanged(true);
                }

                appMDAO.updateAPI(api, authorizedAdminCookie);

                AppManagerConfiguration config = ServiceReferenceHolder.getInstance()
                        .getAPIManagerConfigurationService().getAPIManagerConfiguration();
                boolean gatewayExists = config.getApiGatewayEnvironments().size() > 0;
                String gatewayType = config.getFirstProperty(AppMConstants.API_GATEWAY_TYPE);
                boolean isAPIPublished = false;

                isAPIPublished = isAPIPublished(api);
                if (gatewayExists) {
                    if (isAPIPublished) {
                        WebApp apiPublished = getAPI(api.getId());
                        apiPublished.setOldInSequence(oldApi.getInSequence());
                        apiPublished.setOldOutSequence(oldApi.getOutSequence());

                        //update version
                        if (api.isDefaultVersion() || oldApi.isDefaultVersion()) {
                            //remove both versioned/non versioned apis
                            WebApp webApp = new WebApp(api.getId());
                            webApp.setDefaultVersion(true);
                            removeFromGateway(webApp);
                        }

                        //publish to gateway if skipGateway is disabled only
                        if (!api.getSkipGateway()) {
                            publishToGateway(apiPublished);
                        }
                    }
                } else {
                    log.debug("Gateway is not existed for the current WebApp Provider");
                }

                /*Boolean gatewayKeyCacheEnabled=false;
                String gatewayKeyCacheEnabledString = config.getFirstProperty(AppMConstants.API_GATEWAY_KEY_CACHE_ENABLED);
                //If gateway key cache enabled
                if (gatewayKeyCacheEnabledString != null) {
                gatewayKeyCacheEnabled = Boolean.parseBoolean(gatewayKeyCacheEnabledString);
                }
                //If resource paths being saved are on permission cache, remove them.
                if (gatewayExists && gatewayKeyCacheEnabled) {
                if (isAPIPublished && !oldApi.getUriTemplates().equals(api.getUriTemplates())) {
                    Set<URITemplate> resourceVerbs = api.getUriTemplates();
                    
                    List<Environment> gatewayEnvs = config.getApiGatewayEnvironments();
                    for(Environment environment : gatewayEnvs){
                        APIAuthenticationAdminClient client =
                                new APIAuthenticationAdminClient(environment);
                        if(resourceVerbs != null){
                            for(URITemplate resourceVerb : resourceVerbs){
                                String resourceURLContext = resourceVerb.getUriTemplate();
                                //If url context ends with the '*' character.
                                if(resourceURLContext.endsWith("*")){
                                    //Remove the ending '*'
                                    resourceURLContext = resourceURLContext.substring(0, resourceURLContext.length() - 1);
                                }
                                client.invalidateResourceCache(api.getContext(),api.getId().getVersion(),resourceURLContext,resourceVerb.getHTTPVerb());
                                if (log.isDebugEnabled()) {
                                    log.debug("Calling invalidation cache");
                                }
                            }
                        }
                    }
                    
                }
                }*/
                /* Update WebApp Definition for Swagger
                createUpdateAPIDefinition(api);*/

                //update apiContext cache
                if (AppManagerUtil.isAPIManagementEnabled()) {
                    Cache contextCache = AppManagerUtil.getAPIContextCache();
                    contextCache.remove(oldApi.getContext());
                    contextCache.put(api.getContext(), true);
                }

            } catch (AppManagementException e) {
                handleException("Error while updating the WebApp :" + api.getId().getApiName(), e);
            }

        } else {
            // We don't allow WebApp status updates via this method.
            // Use changeAPIStatus for that kind of updates.
            throw new AppManagementException("Invalid WebApp update operation involving WebApp status changes");
        }
    }

    @Override
    public void updateApp(App app) throws AppManagementException {
        AppRepository appRepository = new DefaultAppRepository(registry);
        appRepository.updateApp(app);
    }

    /**
     * Updates an existing WebApp
     *
     * @param api WebApp
     * @throws org.wso2.carbon.apimgt.api.APIManagementException
     *          if failed to update WebApp
     */
    public void updateMobileApp(MobileApp mobileApp) throws AppManagementException {

        try {

            updateMobileAppArtifact(mobileApp, true);

        } catch (AppManagementException e) {
            handleException("Error while updating the WebApp :" + mobileApp.getAppName(), e);
        }

    }

    private void updateApiArtifact(WebApp api, boolean updateMetadata, boolean updatePermissions)
            throws AppManagementException {

        //Validate Transports
        validateAndSetTransports(api);

        try {
            registry.beginTransaction();
            String apiArtifactId = registry.get(AppManagerUtil.getAPIPath(api.getId())).getUUID();
            GenericArtifactManager artifactManager = AppManagerUtil.getArtifactManager(registry,
                    AppMConstants.API_KEY);
            GenericArtifact artifact = artifactManager.getGenericArtifact(apiArtifactId);
            GenericArtifact updateApiArtifact = AppManagerUtil.createAPIArtifactContent(artifact, api);
            String artifactPath = GovernanceUtils.getArtifactPath(registry, updateApiArtifact.getId());
            org.wso2.carbon.registry.core.Tag[] oldTags = registry.getTags(artifactPath);
            if (oldTags != null) {
                for (org.wso2.carbon.registry.core.Tag tag : oldTags) {
                    registry.removeTag(artifactPath, tag.getTagName());
                }
            }

            Set<String> tagSet = api.getTags();
            if (tagSet != null) {
                for (String tag : tagSet) {
                    registry.applyTag(artifactPath, tag);
                }
            }

            if (updateMetadata) {

                if (api.getWsdlUrl() != null && !"".equals(api.getWsdlUrl())) {
                    String path = AppManagerUtil.createWSDL(registry, api);
                    if (path != null) {
                        registry.addAssociation(artifactPath, path, CommonConstants.ASSOCIATION_TYPE01);
                        updateApiArtifact.setAttribute(AppMConstants.API_OVERVIEW_WSDL, api.getWsdlUrl()); //reset the wsdl path to permlink
                    }
                }

                if (api.getUrl() != null && !"".equals(api.getUrl())) {
                    String path = AppManagerUtil.createEndpoint(api.getUrl(), registry);
                    if (path != null) {
                        registry.addAssociation(artifactPath, path, CommonConstants.ASSOCIATION_TYPE01);
                    }
                }
            }

            artifactManager.updateGenericArtifact(updateApiArtifact);

            //write WebApp Status to a separate property. This is done to support querying APIs using custom query (SQL)
            //to gain performance
            String apiStatus = api.getStatus().getStatus();
            saveAPIStatus(artifactPath, apiStatus);
            if (updatePermissions) {
                clearResourcePermissions(artifactPath, api.getId());
                String visibleRolesList = api.getVisibleRoles();
                String[] visibleRoles = new String[0];
                if (visibleRolesList != null) {
                    visibleRoles = visibleRolesList.split(",");
                }
                AppManagerUtil.setResourcePermissions(api.getId().getProviderName(), api.getVisibility(),
                        visibleRoles, artifactPath);
            }
            registry.commitTransaction();
        } catch (Exception e) {
            try {
                registry.rollbackTransaction();
            } catch (RegistryException re) {
                handleException("Error while rolling back the transaction for WebApp: " + api.getId().getApiName(),
                        re);
            }
            handleException("Error while performing registry transaction operation", e);

        }
    }

    private void updateMobileAppArtifact(MobileApp mobileApp, boolean updatePermissions)
            throws AppManagementException {

        try {
            registry.beginTransaction();
            GenericArtifactManager artifactManager = AppManagerUtil.getArtifactManager(registry,
                    AppMConstants.MOBILE_ASSET_TYPE);
            GenericArtifact artifact = artifactManager.getGenericArtifact(mobileApp.getAppId());
            if (artifact != null) {

                GenericArtifact updateApiArtifact = AppManagerUtil.createMobileAppArtifactContent(artifact,
                        mobileApp);
                String artifactPath = GovernanceUtils.getArtifactPath(registry, updateApiArtifact.getId());
                artifactManager.updateGenericArtifact(updateApiArtifact);
            } else {
                handleResourceNotFoundException(
                        "Failed to get Mobile App. The artifact corresponding to artifactId " + mobileApp.getAppId()
                                + " does not exist");
            }
            //            org.wso2.carbon.registry.core.Tag[] oldTags = registry.getTags(artifactPath);
            //            if (oldTags != null) {
            //                for (org.wso2.carbon.registry.core.Tag tag : oldTags) {
            //                    registry.removeTag(artifactPath, tag.getTagName());
            //                }
            //            }

            //            Set<String> tagSet = api.getTags();
            //            if (tagSet != null) {
            //                for (String tag : tagSet) {
            //                    registry.applyTag(artifactPath, tag);
            //                }
            //            }

            registry.commitTransaction();
        } catch (Exception e) {
            try {
                registry.rollbackTransaction();
            } catch (RegistryException re) {
                handleException("Error while rolling back the transaction for WebApp: " + mobileApp.getAppName(),
                        re);
            }
            handleException("Error while performing registry transaction operation", e);

        }
    }

    /**
     * Create WebApp Definition in JSON and save in the registry
     *
     * @param api WebApp
     * @throws org.wso2.carbon.apimgt.api.APIManagementException
     *          if failed to generate the content and save
     */
    private void createUpdateAPIDefinition(WebApp api) throws AppManagementException {
        APIIdentifier identifier = api.getId();

        try {
            String jsonText = AppManagerUtil.createSwaggerJSONContent(api);

            String resourcePath = AppManagerUtil.getAPIDefinitionFilePath(identifier.getApiName(),
                    identifier.getVersion());

            Resource resource = registry.newResource();

            resource.setContent(jsonText);
            resource.setMediaType("application/json");
            registry.put(resourcePath, resource);

            /*Set permissions to anonymous role */
            AppManagerUtil.setResourcePermissions(api.getId().getProviderName(), null, null, resourcePath);

        } catch (RegistryException e) {
            handleException("Error while adding WebApp Definition for " + identifier.getApiName() + "-"
                    + identifier.getVersion(), e);
        } catch (AppManagementException e) {
            handleException("Error while adding WebApp Definition for " + identifier.getApiName() + "-"
                    + identifier.getVersion(), e);
        }
    }

    @Override
    public void changeAPIStatus(WebApp api, APIStatus status, String userId, boolean updateGatewayConfig)
            throws AppManagementException {
        APIStatus currentStatus = api.getStatus();
        if (!currentStatus.equals(status)) {
            api.setStatus(status);
            try {
                //                updateApiArtifact(api, false,false);
                //                appMDAO.recordAPILifeCycleEvent(api.getId(), currentStatus, status, userId);

                APIStatusObserverList observerList = APIStatusObserverList.getInstance();
                observerList.notifyObservers(currentStatus, status, api);
                AppManagerConfiguration config = ServiceReferenceHolder.getInstance()
                        .getAPIManagerConfigurationService().getAPIManagerConfiguration();
                String gatewayType = config.getFirstProperty(AppMConstants.API_GATEWAY_TYPE);
                if (!api.isAdvertiseOnly()) { // no need to publish to gateway if webb is only for advertising
                    if (updateGatewayConfig) {

                        if (api.isDefaultVersion()) {
                            if (status.equals(APIStatus.UNPUBLISHED)) {
                                //when un-publishing default version, reset the default published version as null in table
                                APIIdentifier identifier = new APIIdentifier(api.getId().getProviderName(),
                                        api.getId().getApiName(), null);
                                WebApp webApp = new WebApp(identifier);
                                appMDAO.updatePublishedDefaultVersion(webApp);
                            }
                        }

                        if (status.equals(APIStatus.PUBLISHED) || status.equals(APIStatus.DEPRECATED)
                                || status.equals(APIStatus.BLOCKED)) {

                            //update version
                            if (status.equals(APIStatus.PUBLISHED)) {
                                if (api.isDefaultVersion()) {
                                    appMDAO.updateDefaultVersionDetails(api);
                                }
                            }

                            //publish to gateway if skipGateway is disabled only
                            if (!api.getSkipGateway()) {
                                publishToGateway(api);
                            }
                        } else if (status.equals(APIStatus.UNPUBLISHED) || status.equals(APIStatus.RETIRED)) {
                            removeFromGateway(api);
                        }
                    }
                }

            } catch (AppManagementException e) {
                handleException("Error occured in the status change : " + api.getId().getApiName(), e);
            }
        }
    }

    public void updateWebAppSynapse(WebApp api) throws AppManagementException {
        removeFromGateway(api);
    }

    private void publishToGateway(WebApp api) throws AppManagementException {
        APITemplateBuilder builder = null;
        String tenantDomain = null;
        //        if (api.getId().getProviderName().contains("AT")) {
        String provider = api.getId().getProviderName().replace("-AT-", "@");
        tenantDomain = MultitenantUtils.getTenantDomain(provider);
        //        }

        try {
            builder = getAPITemplateBuilder(api);
        } catch (Exception e) {
            handleException("Error while publishing to Gateway ", e);
        }

        APIGatewayManager gatewayManager = APIGatewayManager.getInstance();
        try {
            gatewayManager.publishToGateway(api, builder, tenantDomain);
        } catch (Exception e) {
            handleException("Error while publishing to Gateway ", e);
        }
    }

    private void validateAndSetTransports(WebApp api) throws AppManagementException {
        String transports = api.getTransports();
        if (transports != null && !("null".equalsIgnoreCase(transports))) {
            if (transports.contains(",")) {
                StringTokenizer st = new StringTokenizer(transports, ",");
                while (st.hasMoreTokens()) {
                    checkIfValidTransport(st.nextToken());
                }
            } else {
                checkIfValidTransport(transports);
            }
        } else {
            api.setTransports(Constants.TRANSPORT_HTTP + "," + Constants.TRANSPORT_HTTPS);
            return;
        }
    }

    private void checkIfValidTransport(String transport) throws AppManagementException {
        if (!Constants.TRANSPORT_HTTP.equalsIgnoreCase(transport)
                && !Constants.TRANSPORT_HTTPS.equalsIgnoreCase(transport)) {
            handleException("Unsupported Transport [" + transport + "]");
        }
    }

    private void removeFromGateway(WebApp api) throws AppManagementException {
        String tenantDomain = null;
        if (api.getId().getProviderName().contains("@")) {
            tenantDomain = MultitenantUtils.getTenantDomain(api.getId().getProviderName());
        }

        APIGatewayManager gatewayManager = APIGatewayManager.getInstance();
        try {
            gatewayManager.removeFromGateway(api, tenantDomain);
        } catch (Exception e) {
            handleException("Error while removing WebApp from Gateway ", e);
        }
    }

    private boolean isAPIPublished(WebApp api) throws AppManagementException {
        try {
            String tenantDomain = null;
            if (api.getId().getProviderName().contains("AT")) {
                String provider = api.getId().getProviderName().replace("-AT-", "@");
                tenantDomain = MultitenantUtils.getTenantDomain(provider);
            }
            APIGatewayManager gatewayManager = APIGatewayManager.getInstance();
            return gatewayManager.isAPIPublished(api, tenantDomain);
        } catch (Exception e) {
            handleException("Error while checking WebApp status", e);
        }
        return false;
    }

    /**
     * This method dynamically returns the mandatory and selected java policy handlers list for given app
     *
     * @param api :WebApp class which contains details about web applications
     * @return :handlers list with properties to be applied
     * @throws AppManagementException on error
     */
    private APITemplateBuilder getAPITemplateBuilder(WebApp api) throws AppManagementException {
        APITemplateBuilderImpl velocityTemplateBuilder = new APITemplateBuilderImpl(api);

        //List of JavaPolicy class which contains policy related details
        List<JavaPolicy> policies = new ArrayList<JavaPolicy>();
        //contains properties related to relevant policy and will be used to generate the synapse api config file
        Map<String, String> properties;
        int counterPolicies; //counter :policies

        try {
            //fetch all the java policy handlers details which need to be included to synapse api config file
            policies = appMDAO.getMappedJavaPolicyList(api.getUUID(), true);
            //loop through each policy
            for (counterPolicies = 0; counterPolicies < policies.size(); counterPolicies++) {
                if (policies.get(counterPolicies).getProperties() == null) {
                    //if policy doesn't contain any properties assign an empty map and add java policy as a handler
                    velocityTemplateBuilder.addHandler(policies.get(counterPolicies).getFullQualifiName(),
                            Collections.EMPTY_MAP);
                } else {
                    //contains properties related to all the policies
                    JSONObject objPolicyProperties;
                    properties = new HashMap<String, String>();

                    //get property JSON object related to current policy in the loop
                    objPolicyProperties = policies.get(counterPolicies).getProperties();

                    //if policy contains any properties, run a loop and assign them
                    Set<String> keys = objPolicyProperties.keySet();
                    for (String key : keys) {
                        properties.put(key, objPolicyProperties.get(key).toString());
                    }
                    //add policy as a handler and also the relevant properties
                    velocityTemplateBuilder.addHandler(policies.get(counterPolicies).getFullQualifiName(),
                            properties);
                }
            }

        } catch (AppManagementException e) {
            handleException(
                    "Error occurred while adding java policy handlers to Application : " + api.getId().toString(),
                    e);
        }
        return velocityTemplateBuilder;
    }

    /**
     * @param webapp     origin web application
     * @param newVersion The version of the new WebApp
     * @throws AppManagementException
     */
    @Override
    public void copyWebappDocumentations(WebApp webapp, String newVersion) throws AppManagementException {

        try {

            // Retain the docs
            List<Documentation> docs = getAllDocumentation(webapp.getId());
            APIIdentifier newId = new APIIdentifier(webapp.getId().getProviderName(), webapp.getId().getApiName(),
                    newVersion);
            WebApp newAPI = getAPI(newId, webapp.getId());

            if (log.isDebugEnabled()) {
                log.debug("Copying documenatation of the web application - " + webapp.getApiName()
                        + "with the new version - " + newVersion);
            }

            for (Documentation doc : docs) {

                /* copying the file in registry for new api */
                Documentation.DocumentSourceType sourceType = doc.getSourceType();
                if (sourceType == Documentation.DocumentSourceType.FILE) {
                    String absoluteSourceFilePath = doc.getFilePath();
                    // extract the prepend
                    // ->/registry/resource/_system/governance/ and for
                    // tenant
                    // /t/my.com/registry/resource/_system/governance/
                    int prependIndex = absoluteSourceFilePath.indexOf(AppMConstants.API_LOCATION);
                    String prependPath = absoluteSourceFilePath.substring(0, prependIndex);
                    // get the file name from absolute file path
                    int fileNameIndex = absoluteSourceFilePath.lastIndexOf(RegistryConstants.PATH_SEPARATOR);
                    String fileName = absoluteSourceFilePath.substring(fileNameIndex + 1);
                    // create relative file path of old location
                    String sourceFilePath = absoluteSourceFilePath.substring(prependIndex);
                    // create the relative file path where file should be
                    // copied
                    String targetFilePath = AppMConstants.API_LOCATION + RegistryConstants.PATH_SEPARATOR
                            + newId.getProviderName() + RegistryConstants.PATH_SEPARATOR + newId.getApiName()
                            + RegistryConstants.PATH_SEPARATOR + newId.getVersion()
                            + RegistryConstants.PATH_SEPARATOR + AppMConstants.DOC_DIR
                            + RegistryConstants.PATH_SEPARATOR + AppMConstants.DOCUMENT_FILE_DIR
                            + RegistryConstants.PATH_SEPARATOR + fileName;
                    // copy the file from old location to new location(for
                    // new api)

                    registry.copy(sourceFilePath, targetFilePath);

                    // update the filepath attribute in doc artifact to
                    // create new doc artifact for new version of api
                    doc.setFilePath(prependPath + targetFilePath);
                }

                createDocumentation(newAPI.getId(), doc);
                String content = getDocumentationContent(webapp.getId(), doc.getName());
                if (content != null) {
                    addDocumentationContent(newAPI.getId(), doc.getName(), content);
                }
            }
        } catch (RegistryException e) {
            handleException("Error occurred while copying web application : " + webapp.getApiName());
        }
    }

    /**
     * Removes a given documentation
     * @param apiId   APIIdentifier
     * @param docName name of the document
     * @param docType the type of the documentation
     * @throws AppManagementException
     */
    @Override
    public void removeDocumentation(APIIdentifier apiId, String docName, String docType)
            throws AppManagementException {
        String docPath = AppManagerUtil.getAPIDocPath(apiId) + docName;

        try {
            String apiArtifactId = registry.get(docPath).getUUID();
            GenericArtifactManager artifactManager = AppManagerUtil.getArtifactManager(registry,
                    AppMConstants.DOCUMENTATION_KEY);
            GenericArtifact artifact = artifactManager.getGenericArtifact(apiArtifactId);
            String docFilePath = artifact.getAttribute(AppMConstants.DOC_FILE_PATH);

            if (docFilePath != null) {
                File tempFile = new File(docFilePath);
                String fileName = tempFile.getName();
                docFilePath = AppManagerUtil.getDocumentationFilePath(apiId, fileName);
                if (registry.resourceExists(docFilePath)) {
                    registry.delete(docFilePath);
                }
            }

            Association[] associations = registry.getAssociations(docPath, AppMConstants.DOCUMENTATION_ASSOCIATION);
            for (Association association : associations) {
                registry.delete(association.getDestinationPath());
            }
            String docContentPath = AppManagerUtil.getAPIDocContentPath(apiId, docName);

            //Remove Inline-documentation contents
            if (registry.resourceExists(docContentPath)) {
                registry.delete(docContentPath);
            }

        } catch (RegistryException e) {
            handleException("Failed to delete documentation", e);
        }
    }

    /**
     *
     * @param apiId   APIIdentifier
     * @param docId UUID of the doc
     * @throws AppManagementException if failed to remove documentation
     */
    public void removeDocumentation(APIIdentifier apiId, String docId) throws AppManagementException {
        String docPath;

        try {
            GenericArtifactManager artifactManager = AppManagerUtil.getArtifactManager(registry,
                    AppMConstants.DOCUMENTATION_KEY);
            GenericArtifact artifact = artifactManager.getGenericArtifact(docId);
            docPath = artifact.getPath();
            String docFilePath = artifact.getAttribute(AppMConstants.DOC_FILE_PATH);

            if (docFilePath != null) {
                File tempFile = new File(docFilePath);
                String fileName = tempFile.getName();
                docFilePath = AppManagerUtil.getDocumentationFilePath(apiId, fileName);
                if (registry.resourceExists(docFilePath)) {
                    registry.delete(docFilePath);
                }
            }

            Association[] associations = registry.getAssociations(docPath, AppMConstants.DOCUMENTATION_ASSOCIATION);

            for (Association association : associations) {
                registry.delete(association.getDestinationPath());
            }
        } catch (RegistryException e) {
            handleException("Failed to delete documentation", e);
        }
    }

    /**
     * Adds Documentation to an WebApp
     *
     * @param apiId         APIIdentifier
     * @param documentation Documentation
     * @throws org.wso2.carbon.apimgt.api.APIManagementException
     *          if failed to add documentation
     */
    @Override
    public void addDocumentation(APIIdentifier apiId, Documentation documentation) throws AppManagementException {
        createDocumentation(apiId, documentation);
    }

    /**
     * This method used to save the documentation content
     *
     * @param identifier,        WebApp identifier
     * @param documentationName, name of the inline documentation
     * @param text,              content of the inline documentation
     * @throws org.wso2.carbon.apimgt.api.APIManagementException
     *          if failed to add the document as a resource to registry
     */
    public void addDocumentationContent(APIIdentifier identifier, String documentationName, String text)
            throws AppManagementException {

        String documentationPath = AppManagerUtil.getAPIDocPath(identifier) + documentationName;
        String contentPath = AppManagerUtil.getAPIDocPath(identifier) + AppMConstants.INLINE_DOCUMENT_CONTENT_DIR
                + RegistryConstants.PATH_SEPARATOR + documentationName;
        try {
            Resource docContent;
            if (!registry.resourceExists(contentPath)) {
                docContent = registry.newResource();
            } else {
                docContent = registry.get(contentPath);
            }

            /* This is a temporary fix for doc content replace issue. We need to add
             * separate methods to add inline content resource in document update */
            if (!AppMConstants.NO_CONTENT_UPDATE.equals(text)) {
                docContent.setContent(text);
            }

            docContent.setMediaType(AppMConstants.DOCUMENTATION_INLINE_CONTENT_TYPE);
            registry.put(contentPath, docContent);
            registry.addAssociation(documentationPath, contentPath,
                    AppMConstants.DOCUMENTATION_CONTENT_ASSOCIATION);
            String[] authorizedRoles = getAuthorizedRoles(documentationPath);
            String apiPath = AppManagerUtil.getAPIPath(identifier);
            AppManagerUtil.setResourcePermissions(getAPI(apiPath).getId().getProviderName(),
                    getAPI(apiPath).getVisibility(), authorizedRoles, contentPath);
        } catch (RegistryException e) {
            String msg = "Failed to add the documentation content of : " + documentationName + " of WebApp :"
                    + identifier.getApiName();
            handleException(msg, e);
        } catch (UserStoreException e) {
            String msg = "Failed to add the documentation content of : " + documentationName + " of WebApp :"
                    + identifier.getApiName();
            handleException(msg, e);
        }
    }

    /**
     * Add a file to a document of source type FILE
     *
     * @param webApp
     * @param documentation document
     * @param filename name of the file
     * @param content content of the file as an Input Stream
     * @param contentType content type of the file
     * @throws AppManagementException if failed to add the file
     */
    public void addFileToDocumentation(WebApp webApp, Documentation documentation, String filename,
            InputStream content, String contentType) throws AppManagementException {
        if (Documentation.DocumentSourceType.FILE.equals(documentation.getSourceType())) {
            FileContent documentContent = new FileContent();
            documentContent.setContent(content);
            documentContent.setContentType(contentType);

            String filePath = AppManagerUtil.getDocumentationFilePath(webApp.getId(), filename);

            try {
                String visibleRolesList = webApp.getVisibleRoles();
                String[] visibleRoles = new String[0];
                if (visibleRolesList != null) {
                    visibleRoles = visibleRolesList.split(",");
                }
                AppManagerUtil.setResourcePermissions(webApp.getId().getProviderName(), webApp.getVisibility(),
                        visibleRoles, filePath);
                documentation.setFilePath(addResourceFile(filePath, documentContent));
                AppManagerUtil.setFilePermission(filePath);
            } catch (AppManagementException e) {
                handleException("Failed to add file to document " + documentation.getName(), e);
            }
        } else {
            String errorMsg = "Cannot add file to the Document. Document " + documentation.getName()
                    + "'s Source type is not FILE.";
            handleException(errorMsg);
        }
    }

    /**
     * This method used to update the WebApp definition content - Swagger
     *
     * @param identifier,        WebApp identifier
     * @param documentationName, name of the inline documentation
     * @param text,              content of the inline documentation
     * @throws org.wso2.carbon.apimgt.api.APIManagementException
     *          if failed to add the document as a resource to registry
     */
    public void addAPIDefinitionContent(APIIdentifier identifier, String documentationName, String text)
            throws AppManagementException {
        String contentPath = AppManagerUtil.getAPIDefinitionFilePath(identifier.getApiName(),
                identifier.getVersion());

        try {
            Resource docContent = registry.newResource();
            docContent.setContent(text);
            docContent.setMediaType("text/plain");
            registry.put(contentPath, docContent);

            String apiPath = AppManagerUtil.getAPIPath(identifier);
            WebApp api = getAPI(apiPath);
            String visibleRolesList = api.getVisibleRoles();
            String[] visibleRoles = new String[0];
            if (visibleRolesList != null) {
                visibleRoles = visibleRolesList.split(",");
            }
            AppManagerUtil.setResourcePermissions(api.getId().getProviderName(), api.getVisibility(), visibleRoles,
                    contentPath);
        } catch (RegistryException e) {
            String msg = "Failed to add the WebApp Definition content of : " + documentationName + " of WebApp :"
                    + identifier.getApiName();
            handleException(msg, e);
        }
    }

    /**
     * Updates a given documentation
     *
     * @param apiId         APIIdentifier
     * @param documentation Documentation
     * @throws org.wso2.carbon.appmgt.api.AppManagementException
     *          if failed to update docs
     */
    public void updateDocumentation(APIIdentifier apiId, Documentation documentation)
            throws AppManagementException {

        String docPath = AppMConstants.API_ROOT_LOCATION + RegistryConstants.PATH_SEPARATOR
                + apiId.getProviderName() + RegistryConstants.PATH_SEPARATOR + apiId.getApiName()
                + RegistryConstants.PATH_SEPARATOR + apiId.getVersion() + RegistryConstants.PATH_SEPARATOR
                + AppMConstants.DOC_DIR + RegistryConstants.PATH_SEPARATOR + documentation.getName();
        try {
            String apiArtifactId = registry.get(docPath).getUUID();
            GenericArtifactManager artifactManager = AppManagerUtil.getArtifactManager(registry,
                    AppMConstants.DOCUMENTATION_KEY);
            GenericArtifact artifact = artifactManager.getGenericArtifact(apiArtifactId);
            String apiPath = AppManagerUtil.getAPIPath(apiId);
            GenericArtifact updateApiArtifact = AppManagerUtil.createDocArtifactContent(artifact, apiId,
                    documentation);
            artifactManager.updateGenericArtifact(updateApiArtifact);
            clearResourcePermissions(docPath, apiId);

            WebApp api = getAPI(apiPath);
            String visibleRolesList = api.getVisibleRoles();
            String[] visibleRoles = new String[0];
            if (visibleRolesList != null) {
                visibleRoles = visibleRolesList.split(",");
            }

            AppManagerUtil.setResourcePermissions(api.getId().getProviderName(), api.getVisibility(), visibleRoles,
                    artifact.getPath());

            String docFilePath = artifact.getAttribute(AppMConstants.DOC_FILE_PATH);
            if (docFilePath != null && !docFilePath.equals("")) {
                //The docFilePatch comes as /t/tenanatdoman/registry/resource/_system/governance/apimgt/applicationdata..
                //We need to remove the /t/tenanatdoman/registry/resource/_system/governance section to set permissions.
                int startIndex = docFilePath.indexOf("governance") + "governance".length();
                String filePath = docFilePath.substring(startIndex, docFilePath.length());
                AppManagerUtil.setResourcePermissions(getAPI(apiPath).getId().getProviderName(),
                        getAPI(apiPath).getVisibility(), visibleRoles, filePath);
            }

        } catch (RegistryException e) {
            handleException("Failed to update documentation", e);
        }

    }

    /**
     * Copies current Documentation into another version of the same WebApp.
     *
     * @param toVersion Version to which Documentation should be copied.
     * @param apiId     id of the APIIdentifier
     * @throws org.wso2.carbon.apimgt.api.APIManagementException
     *          if failed to copy docs
     */
    public void copyAllDocumentation(APIIdentifier apiId, String toVersion) throws AppManagementException {

        String oldVersion = AppManagerUtil.getAPIDocPath(apiId);
        String newVersion = AppMConstants.API_ROOT_LOCATION + RegistryConstants.PATH_SEPARATOR
                + apiId.getProviderName() + RegistryConstants.PATH_SEPARATOR + apiId.getApiName()
                + RegistryConstants.PATH_SEPARATOR + toVersion + RegistryConstants.PATH_SEPARATOR
                + AppMConstants.DOC_DIR;

        try {
            Resource resource = registry.get(oldVersion);
            if (resource instanceof org.wso2.carbon.registry.core.Collection) {
                String[] docsPaths = ((org.wso2.carbon.registry.core.Collection) resource).getChildren();
                for (String docPath : docsPaths) {
                    registry.copy(docPath, newVersion);
                }
            }
        } catch (RegistryException e) {
            handleException("Failed to copy docs to new version : " + newVersion, e);
        }
    }

    /**
     * Create an Api
     *
     * @param api WebApp
     * @throws org.wso2.carbon.appmgt.api.AppManagementException if failed to create WebApp
     */
    private void createAPI(WebApp api) throws AppManagementException {
        GenericArtifactManager artifactManager = AppManagerUtil.getArtifactManager(registry, AppMConstants.API_KEY);

        //Validate Transports
        validateAndSetTransports(api);
        try {
            registry.beginTransaction();
            GenericArtifact genericArtifact = artifactManager
                    .newGovernanceArtifact(new QName(api.getId().getApiName()));
            GenericArtifact artifact = AppManagerUtil.createAPIArtifactContent(genericArtifact, api);
            artifactManager.addGenericArtifact(artifact);
            String artifactPath = GovernanceUtils.getArtifactPath(registry, artifact.getId());
            String providerPath = AppManagerUtil.getAPIProviderPath(api.getId());
            //provider ------provides----> WebApp
            registry.addAssociation(providerPath, artifactPath, AppMConstants.PROVIDER_ASSOCIATION);
            Set<String> tagSet = api.getTags();
            if (tagSet != null && tagSet.size() > 0) {
                for (String tag : tagSet) {
                    registry.applyTag(artifactPath, tag);
                }
            }
            if (api.getWsdlUrl() != null && !"".equals(api.getWsdlUrl())) {
                String path = AppManagerUtil.createWSDL(registry, api);
                if (path != null) {
                    registry.addAssociation(artifactPath, path, CommonConstants.ASSOCIATION_TYPE01);
                    artifact.setAttribute(AppMConstants.API_OVERVIEW_WSDL, api.getWsdlUrl()); //reset the wsdl path to permlink
                    artifactManager.updateGenericArtifact(artifact); //update the  artifact
                }
            }

            if (api.getUrl() != null && !"".equals(api.getUrl())) {
                String path = AppManagerUtil.createEndpoint(api.getUrl(), registry);
                if (path != null) {
                    registry.addAssociation(artifactPath, path, CommonConstants.ASSOCIATION_TYPE01);
                }
            }
            //write WebApp Status to a separate property. This is done to support querying APIs using custom query (SQL)
            //to gain performance
            String apiStatus = api.getStatus().getStatus();
            saveAPIStatus(artifactPath, apiStatus);
            String visibleRolesList = api.getVisibleRoles();
            String[] visibleRoles = new String[0];
            if (visibleRolesList != null) {
                visibleRoles = visibleRolesList.split(",");
            }
            AppManagerUtil.setResourcePermissions(api.getId().getProviderName(), api.getVisibility(), visibleRoles,
                    artifactPath);
            registry.commitTransaction();

            /* Generate WebApp Definition for Swagger */
            createUpdateAPIDefinition(api);

        } catch (Exception e) {
            try {
                registry.rollbackTransaction();
            } catch (RegistryException re) {
                handleException("Error while rolling back the transaction for WebApp: " + api.getId().getApiName(),
                        re);
            }
            handleException("Error while performing registry transaction operation", e);
        }

    }

    /**
     * This function is to set resource permissions based on its visibility
     *
     * @param artifactPath WebApp resource path
     * @throws org.wso2.carbon.appmgt.api.AppManagementException Throwing exception
     */
    private void clearResourcePermissions(String artifactPath, APIIdentifier apiId) throws AppManagementException {
        try {
            String resourcePath = RegistryUtils.getAbsolutePath(RegistryContext.getBaseInstance(),
                    RegistryConstants.GOVERNANCE_REGISTRY_BASE_PATH + artifactPath);
            String tenantDomain = MultitenantUtils
                    .getTenantDomain(AppManagerUtil.replaceEmailDomainBack(apiId.getProviderName()));
            if (!tenantDomain
                    .equals(org.wso2.carbon.utils.multitenancy.MultitenantConstants.SUPER_TENANT_DOMAIN_NAME)) {
                AuthorizationManager authManager = ServiceReferenceHolder.getInstance().getRealmService()
                        .getTenantUserRealm(((UserRegistry) registry).getTenantId()).getAuthorizationManager();
                authManager.clearResourceAuthorizations(resourcePath);
            } else {
                RegistryAuthorizationManager authorizationManager = new RegistryAuthorizationManager(
                        ServiceReferenceHolder.getUserRealm());
                authorizationManager.clearResourceAuthorizations(resourcePath);
            }
        } catch (UserStoreException e) {
            handleException("Error while adding role permissions to WebApp", e);
        }
    }

    /**
     * Create a documentation
     *
     * @param apiId         APIIdentifier
     * @param documentation Documentation
     * @throws org.wso2.carbon.appmgt.api.AppManagementException if failed to add documentation
     */
    private void createDocumentation(APIIdentifier apiId, Documentation documentation)
            throws AppManagementException {
        try {
            GenericArtifactManager artifactManager = new GenericArtifactManager(registry,
                    AppMConstants.DOCUMENTATION_KEY);
            GenericArtifact artifact = artifactManager.newGovernanceArtifact(new QName(documentation.getName()));
            artifactManager
                    .addGenericArtifact(AppManagerUtil.createDocArtifactContent(artifact, apiId, documentation));
            documentation.setId(artifact.getId());
            String apiPath = AppManagerUtil.getAPIPath(apiId);
            //Adding association from api to documentation . (WebApp -----> doc)
            registry.addAssociation(apiPath, artifact.getPath(), AppMConstants.DOCUMENTATION_ASSOCIATION);
            String[] authorizedRoles = getAuthorizedRoles(apiPath);
            AppManagerUtil.setResourcePermissions(getAPI(apiPath).getId().getProviderName(),
                    getAPI(apiPath).getVisibility(), authorizedRoles, artifact.getPath());

            String docFilePath = artifact.getAttribute(AppMConstants.DOC_FILE_PATH);
            if (docFilePath != null && !docFilePath.equals("")) {
                //The docFilePatch comes as /t/tenanatdoman/registry/resource/_system/governance/apimgt/applicationdata..
                //We need to remove the /t/tenanatdoman/registry/resource/_system/governance section to set permissions.
                int startIndex = docFilePath.indexOf("governance") + "governance".length();
                String filePath = docFilePath.substring(startIndex, docFilePath.length());
                AppManagerUtil.setResourcePermissions(getAPI(apiPath).getId().getProviderName(),
                        getAPI(apiPath).getVisibility(), authorizedRoles, filePath);
            }
        } catch (RegistryException e) {
            handleException("Failed to add documentation", e);
        } catch (UserStoreException e) {
            handleException("Failed to add documentation", e);
        }
    }

    private String[] getAuthorizedRoles(String artifactPath) throws UserStoreException {
        String resourcePath = RegistryUtils.getAbsolutePath(RegistryContext.getBaseInstance(),
                RegistryConstants.GOVERNANCE_REGISTRY_BASE_PATH + artifactPath);
        RegistryAuthorizationManager authorizationManager = new RegistryAuthorizationManager(
                ServiceReferenceHolder.getUserRealm());
        return authorizationManager.getAllowedRolesForResource(resourcePath, ActionConstants.GET);
    }

    /**
     * Returns the details of all the life-cycle changes done per api
     *
     * @param apiId WebApp Identifier
     * @return List of lifecycle events per given api
     * @throws org.wso2.carbon.apimgt.api.APIManagementException
     *          If failed to get Lifecycle Events
     */
    public List<LifeCycleEvent> getLifeCycleEvents(APIIdentifier apiId) throws AppManagementException {
        return appMDAO.getLifeCycleEvents(apiId);
    }

    /**
     * Update the subscription status
     *
     * @param apiId WebApp Identifier
     * @param subStatus Subscription Status
     * @param appId Application Id              *
     * @return int value with subscription id
     * @throws org.wso2.carbon.apimgt.api.APIManagementException
     *          If failed to update subscription status
     */
    public void updateSubscription(APIIdentifier apiId, String subStatus, int appId) throws AppManagementException {
        appMDAO.updateSubscription(apiId, subStatus, appId);
    }

    /**
     * Moves subscriptions of one app to another app
     *
     * @param fromIdentifier subscriptions of this app
     * @param toIdentifier   will be moved into this app
     * @return number of subscriptions moved
     * @throws AppManagementException
     */
    @Override
    public int moveSubscriptions(APIIdentifier fromIdentifier, APIIdentifier toIdentifier)
            throws AppManagementException {
        return appMDAO.moveSubscriptions(fromIdentifier, toIdentifier);
    }

    /**
     * Delete applicatoion
     * @param identifier AppIdentifier
     * @param ssoProvider SSO provider
     * @param authorizedAdminCookie The cookie which was generated from the SAML assertion.
     * @throws org.wso2.carbon.appmgt.api.AppManagementException
     */
    public boolean deleteApp(APIIdentifier identifier, SSOProvider ssoProvider, String authorizedAdminCookie)
            throws AppManagementException {

        SSOConfiguratorUtil ssoConfiguratorUtil;
        String path = AppMConstants.API_ROOT_LOCATION + RegistryConstants.PATH_SEPARATOR
                + identifier.getProviderName() + RegistryConstants.PATH_SEPARATOR + identifier.getApiName()
                + RegistryConstants.PATH_SEPARATOR + identifier.getVersion();

        String appArtifactPath = AppManagerUtil.getAPIPath(identifier);
        boolean isAppDeleted = false;

        try {
            long subsCount = appMDAO.getAPISubscriptionCountByAPI(identifier);
            Resource appArtifactResource = registry.get(appArtifactPath);
            String applicationStatus = appArtifactResource.getProperty(AppMConstants.WEB_APP_LIFECYCLE_STATUS);
            if (subsCount > 0 && !applicationStatus.equals("Retired")) {
                //remove subscriptions per app
                appMDAO.removeAPISubscription(identifier);
            }

            //If SSOProvider exists, remove it
            if (ssoProvider != null) {
                if (log.isDebugEnabled()) {
                    log.debug("Removing the SSO Provider with name : " + ssoProvider.getProviderName());
                }
                ssoConfiguratorUtil = new SSOConfiguratorUtil();

                Map<String, String> serviceConfigs = new HashMap<String, String>();
                serviceConfigs.put(SSOConfiguratorUtil.SP_ADMIN_SERVICE_COOKIE_PROPERTY_KEY, authorizedAdminCookie);

                ssoConfiguratorUtil.deleteSSOProvider(ssoProvider, serviceConfigs);
            }

            GovernanceUtils.loadGovernanceArtifacts((UserRegistry) registry);
            GenericArtifactManager artifactManager = AppManagerUtil.getArtifactManager(registry,
                    AppMConstants.API_KEY);
            Resource appResource = registry.get(path);
            String artifactId = appResource.getUUID();

            String appArtifactResourceId = appArtifactResource.getUUID();
            if (artifactId == null) {
                throw new AppManagementException("artifact id is null for : " + path);
            }

            GenericArtifact appArtifact = artifactManager.getGenericArtifact(appArtifactResourceId);
            String inSequence = appArtifact.getAttribute(AppMConstants.API_OVERVIEW_INSEQUENCE);
            String outSequence = appArtifact.getAttribute(AppMConstants.API_OVERVIEW_OUTSEQUENCE);

            //Delete the dependencies associated  with the api artifact
            GovernanceArtifact[] dependenciesArray = appArtifact.getDependencies();

            if (dependenciesArray.length > 0) {
                for (int i = 0; i < dependenciesArray.length; i++) {
                    registry.delete(dependenciesArray[i].getPath());
                }
            }

            artifactManager.removeGenericArtifact(appArtifact);
            artifactManager.removeGenericArtifact(artifactId);

            String thumbPath = AppManagerUtil.getIconPath(identifier);
            if (registry.resourceExists(thumbPath)) {
                registry.delete(thumbPath);
            }

            AppManagerConfiguration config = ServiceReferenceHolder.getInstance()
                    .getAPIManagerConfigurationService().getAPIManagerConfiguration();
            boolean gatewayExists = config.getApiGatewayEnvironments().size() > 0;
            String gatewayType = config.getFirstProperty(AppMConstants.API_GATEWAY_TYPE);

            WebApp webapp = new WebApp(identifier);
            // gatewayType check is required when WebApp Management is deployed on other servers to avoid synapse
            if (gatewayExists && "Synapse".equals(gatewayType)) {
                webapp.setInSequence(inSequence); //need to remove the custom sequences
                webapp.setOutSequence(outSequence);
                removeFromGateway(webapp);
            } else {
                if (log.isDebugEnabled()) {
                    log.debug("Gateway is not existed for the current applications Provider");
                }
            }
            appMDAO.deleteAPI(identifier, authorizedAdminCookie);

            /*remove empty directories*/
            String appCollectionPath = AppMConstants.API_ROOT_LOCATION + RegistryConstants.PATH_SEPARATOR
                    + identifier.getProviderName() + RegistryConstants.PATH_SEPARATOR + identifier.getApiName();
            if (registry.resourceExists(appCollectionPath)) {
                Resource appCollection = registry.get(appCollectionPath);
                CollectionImpl collection = (CollectionImpl) appCollection;
                //if there is no other versions of applications delete the directory of the applications
                if (collection.getChildCount() == 0) {
                    if (log.isDebugEnabled()) {
                        log.debug(
                                "No more versions of the applications found, removing applications collection from registry");
                    }
                    registry.delete(appCollectionPath);
                }
            }

            String appProviderPath = AppMConstants.API_ROOT_LOCATION + RegistryConstants.PATH_SEPARATOR
                    + identifier.getProviderName();
            if (registry.resourceExists(appProviderPath)) {
                Resource providerCollection = registry.get(appProviderPath);
                CollectionImpl collection = (CollectionImpl) providerCollection;
                //if there is no applications for given provider delete the provider directory
                if (collection.getChildCount() == 0) {
                    if (log.isDebugEnabled()) {
                        log.debug("No more Applications from the provider " + identifier.getProviderName()
                                + " found. " + "Removing provider collection from registry");
                    }
                    registry.delete(appProviderPath);
                }
            }
            isAppDeleted = true;
        } catch (RegistryException e) {
            handleException("Failed to remove the WebApp from : " + path, e);
        }

        return isAppDeleted;
    }

    public List<WebApp> searchAPIs(String searchTerm, String searchType, String providerId)
            throws AppManagementException {
        List<WebApp> apiSortedList = new ArrayList<WebApp>();
        String regex = "(?i)[\\w.|-]*" + searchTerm.trim() + "[\\w.|-]*";

        Pattern pattern;
        Matcher matcher;
        try {
            List<WebApp> apiList;
            if (providerId != null) {
                apiList = getAPIsByProvider(providerId);
            } else {
                apiList = getAllAPIs();
            }
            if (apiList == null || apiList.size() == 0) {
                return apiSortedList;
            }
            pattern = Pattern.compile(regex);
            for (WebApp api : apiList) {

                if (searchType.equalsIgnoreCase("Name")) {
                    String api1 = api.getId().getApiName();
                    matcher = pattern.matcher(api1);
                } else if (searchType.equalsIgnoreCase("Provider")) {
                    String api1 = api.getId().getProviderName();
                    matcher = pattern.matcher(api1);
                } else if (searchType.equalsIgnoreCase("Version")) {
                    String api1 = api.getId().getVersion();
                    matcher = pattern.matcher(api1);
                } else if (searchType.equalsIgnoreCase("Context")) {
                    String api1 = api.getContext();
                    matcher = pattern.matcher(api1);
                } else {
                    String apiName = api.getId().getApiName();
                    matcher = pattern.matcher(apiName);
                }

                if (matcher.find()) {
                    apiSortedList.add(api);
                }

            }
        } catch (AppManagementException e) {
            handleException("Failed to search APIs with type", e);
        }
        Collections.sort(apiSortedList, new APINameComparator());
        return apiSortedList;
    }

    /**
     * Get a list of APIs published by the given provider. If a given WebApp has multiple APIs, only the latest version
     * will be included in this list.
     *
     * @param providerId , provider id
     * @param appType    Asset Type(either webapp/mobileapp)
     * @return set of WebApp
     * @throws org.wso2.carbon.appmgt.api.AppManagementException if failed to get set of WebApp
     */
    public List<WebApp> getAPIsByProvider(String providerId, String appType) throws AppManagementException {

        List<WebApp> apiSortedList = new ArrayList<WebApp>();

        try {
            providerId = AppManagerUtil.replaceEmailDomain(providerId);
            String providerPath = AppMConstants.API_ROOT_LOCATION + RegistryConstants.PATH_SEPARATOR + providerId;
            GenericArtifactManager artifactManager = AppManagerUtil.getArtifactManager(registry, appType);
            Association[] associations = registry.getAssociations(providerPath, AppMConstants.PROVIDER_ASSOCIATION);
            for (Association association : associations) {
                String apiPath = association.getDestinationPath();
                Resource resource = registry.get(apiPath);
                String apiArtifactId = resource.getUUID();
                if (apiArtifactId != null) {
                    GenericArtifact apiArtifact = artifactManager.getGenericArtifact(apiArtifactId);
                    apiSortedList.add(AppManagerUtil.getGenericApp(apiArtifact, registry));
                } else {
                    throw new GovernanceException("artifact id is null of " + apiPath);
                }
            }

        } catch (RegistryException e) {
            handleException("Failed to get APIs for provider : " + providerId, e);
        }
        Collections.sort(apiSortedList, new APINameComparator());

        return apiSortedList;

    }

    @Override
    public List<App> searchApps(String appType, Map<String, String> searchTerms) throws AppManagementException {

        // If the app type is 'webapp' use the App Repository implementation path.
        if (AppMConstants.WEBAPP_ASSET_TYPE.equalsIgnoreCase(appType)) {
            return new DefaultAppRepository(registry).searchApps(appType, searchTerms);
        } else {
            List<App> apps = new ArrayList<App>();
            List<GenericArtifact> appArtifacts = getAppArtifacts(appType);

            for (GenericArtifact artifact : appArtifacts) {
                if (isSearchHit(artifact, searchTerms)) {
                    apps.add(createApp(artifact, appType));
                }
            }
            return apps;
        }
    }

    /**
     *
     * Search and return the published apps for the given search terms.
     *
     * @param appType App type
     * @param searchTerms Search terms
     * @return List of {@link App}
     * @throws AppManagementException on errors while trying to search published apps.
     */
    @Override
    public List<App> searchPublishedApps(String appType, Map<String, String> searchTerms)
            throws AppManagementException {
        // If the app type is 'webapp' use the App Repository implementation path.
        if (AppMConstants.WEBAPP_ASSET_TYPE.equalsIgnoreCase(appType)) {
            return new DefaultAppRepository(registry).searchApps(appType, searchTerms);
        } else {
            List<App> apps = new ArrayList<App>();
            List<GenericArtifact> appArtifacts = getPublishedAppArtifacts(appType);

            for (GenericArtifact artifact : appArtifacts) {
                if (isSearchHit(artifact, searchTerms)) {
                    apps.add(createApp(artifact, appType));
                }
            }
            return apps;
        }
    }

    /**
     * Update the Tier Permissions
     *
     * @param tierName Tier Name
     * @param permissionType Permission Type
     * @param roles Roles
     * @throws org.wso2.carbon.apimgt.api.APIManagementException
     *          If failed to update subscription status
     */
    public void updateTierPermissions(String tierName, String permissionType, String roles)
            throws AppManagementException {
        appMDAO.updateTierPermissions(tierName, permissionType, roles, tenantId);
    }

    @Override
    public Set<TierPermissionDTO> getTierPermissions() throws AppManagementException {
        Set<TierPermissionDTO> tierPermissions = appMDAO.getTierPermissions(tenantId);
        return tierPermissions;
    }

    /**
     * Get stored custom inSequences from governanceSystem registry
     *
     * @throws org.wso2.carbon.appmgt.api.AppManagementException
     */

    public List<String> getCustomInSequences() throws AppManagementException {

        List<String> sequenceList = new ArrayList<String>();
        try {
            UserRegistry registry = ServiceReferenceHolder.getInstance().getRegistryService()
                    .getGovernanceSystemRegistry(tenantId);
            if (registry.resourceExists(AppMConstants.API_CUSTOM_INSEQUENCE_LOCATION)) {
                org.wso2.carbon.registry.api.Collection inSeqCollection = (org.wso2.carbon.registry.api.Collection) registry
                        .get(AppMConstants.API_CUSTOM_INSEQUENCE_LOCATION);
                if (inSeqCollection != null) {
                    //   SequenceMediatorFactory factory = new SequenceMediatorFactory();
                    String[] inSeqChildPaths = inSeqCollection.getChildren();
                    for (int i = 0; i < inSeqChildPaths.length; i++) {
                        Resource inSequence = registry.get(inSeqChildPaths[i]);
                        OMElement seqElment = AppManagerUtil.buildOMElement(inSequence.getContentStream());
                        sequenceList.add(seqElment.getAttributeValue(new QName("name")));
                    }
                }
            }

        } catch (Exception e) {
            handleException("Issue is in getting custom InSequences from the Registry", e);
        }
        return sequenceList;
    }

    /**
     * Get stored custom outSequences from governanceSystem registry
     *
     * @throws org.wso2.carbon.appmgt.api.AppManagementException
     */

    public List<String> getCustomOutSequences() throws AppManagementException {

        List<String> sequenceList = new ArrayList<String>();
        try {
            UserRegistry registry = ServiceReferenceHolder.getInstance().getRegistryService()
                    .getGovernanceSystemRegistry(tenantId);
            if (registry.resourceExists(AppMConstants.API_CUSTOM_OUTSEQUENCE_LOCATION)) {
                org.wso2.carbon.registry.api.Collection outSeqCollection = (org.wso2.carbon.registry.api.Collection) registry
                        .get(AppMConstants.API_CUSTOM_OUTSEQUENCE_LOCATION);
                if (outSeqCollection != null) {
                    String[] outSeqChildPaths = outSeqCollection.getChildren();
                    for (int i = 0; i < outSeqChildPaths.length; i++) {
                        Resource outSequence = registry.get(outSeqChildPaths[i]);
                        OMElement seqElment = AppManagerUtil.buildOMElement(outSequence.getContentStream());

                        sequenceList.add(seqElment.getAttributeValue(new QName("name")));
                    }
                }
            }

        } catch (Exception e) {
            handleException("Issue is in getting custom OutSequences from the Registry", e);
        }
        return sequenceList;
    }

    @Override
    public List<WebApp> getAllWebApps() throws AppManagementException {
        return appMDAO.getAllWebApps();
    }

    @Override
    public List<WebApp> getAllWebApps(String tenantDomain) throws AppManagementException {
        return appMDAO.getAllWebApps(tenantDomain);
    }

    @Override
    public Map<String, Long> getSubscriptionCountByAPPs(String provider, String fromDate, String toDate,
            boolean isSubscriptionOn) throws AppManagementException {
        Map<String, Long> subscriptions = null;
        try {
            subscriptions = appMDAO.getSubscriptionCountByApp(provider, fromDate, toDate, tenantId,
                    isSubscriptionOn);
        } catch (AppManagementException e) {
            handleException("Failed to get subscriptionCount by apps for provider :" + provider + "for the period "
                    + fromDate + "to" + toDate, e);
        }
        return subscriptions;
    }

    public List<WebApp> getAppsWithEndpoint(String tenantDomain) throws AppManagementException {
        List<WebApp> appSortedList = appMDAO.getAllWebApps(tenantDomain);
        Collections.sort(appSortedList, new APINameComparator());
        return appSortedList;
    }

    @Override
    public Set<AppStore> getExternalAppStores(APIIdentifier identifier) throws AppManagementException {
        // get all stores from configuration
        Set<AppStore> storesFromConfig = AppManagerUtil.getExternalStores(tenantId);
        if (storesFromConfig != null && storesFromConfig.size() > 0) {
            AppManagerUtil.validateStoreName(storesFromConfig);
            //get already published stores from db
            Set<AppStore> publishedStores = appMDAO.getExternalAppStoresDetails(identifier);
            if (publishedStores != null && publishedStores.size() > 0) {
                //Retains only the stores that contained in configuration
                publishedStores.retainAll(storesFromConfig);

                for (AppStore publishedStore : publishedStores) {
                    for (AppStore configuredStore : storesFromConfig) {
                        if (publishedStore.getName().equals(configuredStore.getName())) { //If the configured appstore
                            // already stored in db, change the published state to true
                            configuredStore.setPublished(true);
                        }
                    }
                }
            }
        }
        return storesFromConfig;
    }

    @Override
    public void updateAppsInExternalAppStores(WebApp webApp, Set<AppStore> appStores)
            throws AppManagementException {
        // get the stores where given app is already published
        Set<AppStore> publishedStores = getPublishedExternalAppStores(webApp.getId());
        Set<AppStore> notPublishedAppStores = new HashSet<AppStore>();
        Set<AppStore> removedAppStores = new HashSet<AppStore>();

        if (publishedStores != null && publishedStores.size() > 0) {
            removedAppStores.addAll(publishedStores);
            removedAppStores.removeAll(appStores);

            notPublishedAppStores.addAll(appStores);
            notPublishedAppStores.removeAll(publishedStores);
        } else {
            notPublishedAppStores.addAll(appStores);
        }

        //Publish app to external app Store which are not yet published
        try {
            publishToExternalAppStores(webApp, notPublishedAppStores);
        } catch (AppManagementException e) {
            handleException("Failed to publish app to external store.App -> " + webApp.getApiName(), e);
        }
        //Delete app from external app Store
        try {
            if (removedAppStores.size() > 0) {
                deleteFromExternalAppStores(webApp, removedAppStores);
            }
        } catch (AppManagementException e) {
            handleException("Failed to delete app from external store.App -> " + webApp.getApiName(), e);
        }
    }

    /**
     * Get the stores where given app is already published.
     * @param identifier WebApp Identifier
     * @return
     * @throws AppManagementException
     */
    private Set<AppStore> getPublishedExternalAppStores(APIIdentifier identifier) throws AppManagementException {
        Set<AppStore> configuredAppStores = new HashSet<AppStore>();
        configuredAppStores.addAll(AppManagerUtil.getExternalStores(tenantId));
        if (configuredAppStores.size() != 0) {
            Set<AppStore> storesSet = appMDAO.getExternalAppStoresDetails(identifier);
            //Retains only the stores that contained in configuration
            configuredAppStores.retainAll(storesSet);
            return configuredAppStores;

        } else {
            return null;
        }
    }

    /**
     * Publish the APP to external App Stores and add the published
     * external store details to DB.
     *
     * @param webApp    The APP which need to published
     * @param appStores The APPStores set, to which need to publish APP
     * @throws AppManagementException If failed to publish to any external store
     */
    private void publishToExternalAppStores(WebApp webApp, Set<AppStore> appStores) throws AppManagementException {
        if (log.isDebugEnabled()) {
            String msg = String.format("Publish the web app -> %s to external stores ", webApp.getApiName());
            log.debug(msg);
        }
        Set<AppStore> publishedStores = new HashSet<AppStore>();
        StringBuilder errorStatus = new StringBuilder("Failure to publish to External Stores : ");
        List<String> failedAppStores = new ArrayList<String>();
        boolean failure = false;
        if (appStores.size() > 0) {
            for (AppStore store : appStores) {
                try {
                    String publisherClassName = store.getPublisherClassName();
                    if (publisherClassName == null) {
                        throw new AppManagementException("Publisher class name is not defined in the external "
                                + "store configuration for store with id" + store.getName());
                    }
                    ExternalAppStorePublisher publisher = AppManagerUtil
                            .getExternalStorePublisher(publisherClassName);
                    // First  publish the APP to external APP Store
                    publisher.publishToStore(webApp, store);
                    //collect the published store to add to DB
                    publishedStores.add(store);
                } catch (AppManagementException e) {
                    failure = true;
                    String msg = "Could not publish app :" + webApp.getApiName() + " to external store :"
                            + store.getDisplayName();
                    log.error(msg, e);
                    failedAppStores.add(store.getDisplayName());
                }
            }
            if (publishedStores.size() > 0) {
                //Save the detail of published app stores in DB
                addExternalAppStoresDetails(webApp.getId(), publishedStores);
            }
        }

        if (failure) {
            String failedStores = StringUtils.join(failedAppStores, ',');
            errorStatus.append(failedStores);
            throw new AppManagementException(errorStatus.toString());
        }

    }

    /**
     * Delete the given web app from given external stores and remove the related
     * records from DB.
     *
     * @param webApp           Web App
     * @param removedAppStores App Stores
     * @throws AppManagementException
     */
    private void deleteFromExternalAppStores(WebApp webApp, Set<AppStore> removedAppStores)
            throws AppManagementException {
        Set<AppStore> removalCompletedStores = new HashSet<AppStore>();
        StringBuilder errorStatus = new StringBuilder("Failed to delete from External Stores : ");
        List<String> failedAppStores = new ArrayList<String>();
        boolean failure = false;
        if (removedAppStores.size() > 0) {
            for (AppStore store : removedAppStores) {

                try {
                    String publisherClassName = store.getPublisherClassName();
                    ExternalAppStorePublisher publisher = AppManagerUtil
                            .getExternalStorePublisher(publisherClassName);
                    //delete from external store
                    publisher.deleteFromStore(webApp, store);
                    removalCompletedStores.add(store);
                } catch (AppManagementException e) {
                    failure = true;
                    String msg = "Could not delete app :" + webApp.getApiName() + " from external store :"
                            + store.getDisplayName();
                    log.error(msg, e);
                    failedAppStores.add(store.getDisplayName());
                }

            }
            if (removalCompletedStores.size() != 0) {
                //remove records from database
                removeExternalAppStoreDetails(webApp.getId(), removalCompletedStores);
            }

            if (failure) {
                String failedStores = StringUtils.join(failedAppStores, ',');
                errorStatus.append(failedStores);
            }
        }
    }

    /**
     * Store the published external stores details in DB.
     * @param apiId       WebApp Identifier
     * @param apiStoreSet stores
     * @return
     * @throws AppManagementException
     */
    private void addExternalAppStoresDetails(APIIdentifier apiId, Set<AppStore> apiStoreSet)
            throws AppManagementException {
        if (log.isDebugEnabled()) {
            String msg = String.format("Save published external app store details to DB " + "for web app %s ",
                    apiId.getApiName());
            log.debug(msg);
        }
        appMDAO.addExternalAppStoresDetails(apiId, apiStoreSet);
    }

    /**
     * Remove the records of unpublished external store details from DB.
     *
     * @param identifier    WebApp Identifier
     * @param removalCompletedStores stores
     * @throws AppManagementException
     */
    private void removeExternalAppStoreDetails(APIIdentifier identifier, Set<AppStore> removalCompletedStores)
            throws AppManagementException {
        if (log.isDebugEnabled()) {
            String msg = String.format("Delete  external app store details from DB " + "for web app %s ",
                    identifier.getApiName());
            log.debug(msg);
        }
        appMDAO.deleteExternalAppStoresDetails(identifier, removalCompletedStores);
    }

    /**
     * Get web app default version.
     *
     * @param appName
     * @param providerName
     * @param appStatus
     * @return
     * @throws AppManagementException
     */
    @Override
    public String getDefaultVersion(String appName, String providerName, AppDefaultVersion appStatus)
            throws AppManagementException {
        return AppMDAO.getDefaultVersion(appName, providerName, appStatus);
    }

    /**
     * Check if the given app is the default version.
     *
     * @param identifier
     * @return true if given app is the default version
     * @throws AppManagementException
     */
    @Override
    public boolean isDefaultVersion(APIIdentifier identifier) throws AppManagementException {
        return appMDAO.isDefaultVersion(identifier);
    }

    /**
     * Check if the given app has any other versions in any state.
     *
     * @param identifier
     * @return true if given app has more version
     * @throws AppManagementException
     */
    @Override
    public boolean hasMoreVersions(APIIdentifier identifier) throws AppManagementException {
        return appMDAO.hasMoreVersions(identifier);
    }

    /**
     * Get WebApp basic details by app uuid.
     *
     * @param uuid
     * @return Asset details
     * @throws AppManagementException
     */
    @Override
    public WebApp getAppDetailsFromUUID(String uuid) throws AppManagementException {
        WebApp webApp = appMDAO.getAppDetailsFromUUID(uuid);
        if (webApp == null) {
            handleResourceNotFoundException("Webapp does not exist with requested appID : " + uuid);
        }
        return webApp;
    }

    /**
     * Change the lifecycle state of a given application
     *
     * @param appType         application type ie: webapp, mobileapp
     * @param appId           application uuid
     * @param lifecycleAction lifecycle action perform on the application
     * @throws AppManagementException
     */
    public void changeLifeCycleStatus(String appType, String appId, String lifecycleAction)
            throws AppManagementException {

        try {
            String requiredPermission = null;

            if (AppMConstants.LifecycleActions.SUBMIT_FOR_REVIEW.equals(lifecycleAction)) {
                if (AppMConstants.MOBILE_ASSET_TYPE.equals(appType)) {
                    requiredPermission = AppMConstants.Permissions.MOBILE_APP_CREATE;
                } else if (AppMConstants.WEBAPP_ASSET_TYPE.equals(appType)) {
                    requiredPermission = AppMConstants.Permissions.WEB_APP_CREATE;
                }
            } else {
                if (AppMConstants.MOBILE_ASSET_TYPE.equals(appType)) {
                    requiredPermission = AppMConstants.Permissions.MOBILE_APP_PUBLISH;
                } else if (AppMConstants.WEBAPP_ASSET_TYPE.equals(appType)) {
                    requiredPermission = AppMConstants.Permissions.WEB_APP_PUBLISH;
                }
            }

            if (!AppManagerUtil.checkPermissionQuietly(this.username, requiredPermission)) {
                handleResourceAuthorizationException(
                        "The user " + this.username + " is not authorized to perform lifecycle action "
                                + lifecycleAction + " on " + appType + " with uuid " + appId);
            }
            //Check whether the user has enough permissions to change lifecycle
            PrivilegedCarbonContext.startTenantFlow();
            PrivilegedCarbonContext.getThreadLocalCarbonContext().setUsername(this.username);
            PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(this.tenantDomain, true);

            int tenantId = ServiceReferenceHolder.getInstance().getRealmService().getTenantManager()
                    .getTenantId(this.tenantDomain);

            AuthorizationManager authManager = ServiceReferenceHolder.getInstance().getRealmService()
                    .getTenantUserRealm(tenantId).getAuthorizationManager();

            //Get system registry for logged in tenant domain
            Registry systemRegistry = ServiceReferenceHolder.getInstance().getRegistryService()
                    .getGovernanceSystemRegistry(tenantId);
            GenericArtifactManager artifactManager = AppManagerUtil.getArtifactManager(systemRegistry, appType);
            GenericArtifact appArtifact = artifactManager.getGenericArtifact(appId);
            String resourcePath = RegistryUtils.getAbsolutePath(RegistryContext.getBaseInstance(),
                    RegistryConstants.GOVERNANCE_REGISTRY_BASE_PATH + appArtifact.getPath());

            if (appArtifact != null) {
                if (!authManager.isUserAuthorized(username, resourcePath, "authorize")) {
                    //Throws resource authorization exception
                    handleResourceAuthorizationException("The user " + this.username + " is not authorized to"
                            + appType + " with uuid " + appId);
                }
                //Change lifecycle status
                if (AppMConstants.MOBILE_ASSET_TYPE.equals(appType)) {
                    appArtifact.invokeAction(lifecycleAction, AppMConstants.MOBILE_LIFE_CYCLE);
                } else if (AppMConstants.WEBAPP_ASSET_TYPE.equals(appType)) {
                    appArtifact.invokeAction(lifecycleAction, AppMConstants.WEBAPP_LIFE_CYCLE);
                }

                //If application is role restricted, deny read rights for Internal/everyone and system/wso2.anonymous.role roles
                if ((AppMConstants.LifecycleActions.PUBLISH.equals(lifecycleAction)
                        || AppMConstants.LifecycleActions.RE_PUBLISH.equals(lifecycleAction))
                        && !StringUtils.isBlank(appArtifact.getAttribute("overview_visibleRoles"))) {

                    authManager.denyRole(AppMConstants.EVERYONE_ROLE, resourcePath, ActionConstants.GET);
                    authManager.denyRole(AppMConstants.ANONYMOUS_ROLE, resourcePath, ActionConstants.GET);
                }

                if (log.isDebugEnabled()) {
                    String logMessage = "Lifecycle action " + lifecycleAction
                            + " has been successfully performed on " + appType + " with id" + appId;
                    log.debug(logMessage);
                }
            } else {
                handleResourceNotFoundException("Failed to get " + appType
                        + " artifact corresponding to artifactId " + appId + ". Artifact does not exist");
            }
        } catch (UserStoreException e) {
            handleException("Error occurred while performing lifecycle action : " + lifecycleAction + " on "
                    + appType + " with id : " + appId + ". Failed to retrieve tenant id for user : ", e);
        } catch (RegistryException e) {
            handleException("Error occurred while performing lifecycle action : " + lifecycleAction + " on "
                    + appType + " with id : " + appId, e);
        } finally {
            PrivilegedCarbonContext.endTenantFlow();
        }
    }

    /**
     * Get the available next lifecycle actions of a given application
     *
     * @param appId   application type
     * @param appType application type
     * @return
     */
    public String[] getAllowedLifecycleActions(String appId, String appType) throws AppManagementException {

        String[] actions = null;
        try {
            PrivilegedCarbonContext.startTenantFlow();
            PrivilegedCarbonContext.getThreadLocalCarbonContext().setUsername(this.username);
            PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(this.tenantDomain, true);

            GenericArtifactManager artifactManager = AppManagerUtil.getArtifactManager(registry, appType);
            GenericArtifact appArtifact = artifactManager.getGenericArtifact(appId);
            if (appArtifact != null) {
                if (AppMConstants.MOBILE_ASSET_TYPE.equals(appType)) {
                    //Get all the actions corresponding to current state of the api artifact
                    actions = appArtifact.getAllLifecycleActions(AppMConstants.MOBILE_LIFE_CYCLE);
                } else if (AppMConstants.WEBAPP_ASSET_TYPE.equals(appType)) {
                    actions = appArtifact.getAllLifecycleActions(AppMConstants.WEBAPP_LIFE_CYCLE);
                } else {
                    handleException("Unsupported application type : " + appType + " provided");
                }
            } else {
                handleResourceNotFoundException("Failed to get " + appType
                        + " artifact corresponding to artifactId " + appId + ". Artifact does not exist");
            }
        } catch (GovernanceException e) {
            handleException("Error occurred while retrieving allowed lifecycle actions to perform on " + appType
                    + " with id : " + appId, e);
        } finally {
            PrivilegedCarbonContext.endTenantFlow();
        }
        return actions;
    }

    public boolean subscribeMobileApp(String userId, String appId) throws AppManagementException {

        String path = "users/" + userId + "/subscriptions/mobileapp/" + appId;
        Resource resource = null;
        boolean isSubscribed = false;
        try {
            UserRegistry sysRegistry = ServiceReferenceHolder.getInstance().getRegistryService()
                    .getGovernanceSystemRegistry(tenantId);
            if (!sysRegistry.resourceExists(path)) {
                resource = sysRegistry.newResource();
                resource.setContent("");
                sysRegistry.put(path, resource);
                isSubscribed = true;
            }
        } catch (org.wso2.carbon.registry.api.RegistryException e) {
            handleException(
                    "Error occurred while adding subscription registry resource for mobileapp with id :" + appId,
                    e);
        }
        return isSubscribed;
    }

    public boolean unSubscribeMobileApp(String userId, String appId) throws AppManagementException {
        String path = "users/" + userId + "/subscriptions/mobileapp/" + appId;
        boolean isUnSubscribed = false;
        try {
            if (registry.resourceExists(path)) {
                registry.delete(path);
                isUnSubscribed = true;
            }
        } catch (org.wso2.carbon.registry.api.RegistryException e) {
            handleException(
                    "Error occurred while removing subscription registry resource for mobileapp with id :" + appId,
                    e);
        }
        return isUnSubscribed;
    }

    /**
     *
     * Returns the 'app' (e.g. webapp, mobileapp) registry artifacts.
     *
     * @param appType
     * @return
     * @throws AppManagementException
     */
    private List<GenericArtifact> getAppArtifacts(String appType) throws AppManagementException {

        List<GenericArtifact> appArtifacts = new ArrayList<GenericArtifact>();

        boolean isTenantFlowStarted = false;
        try {
            if (tenantDomain != null && !MultitenantConstants.SUPER_TENANT_DOMAIN_NAME.equals(tenantDomain)) {
                isTenantFlowStarted = true;
                PrivilegedCarbonContext.startTenantFlow();
                PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(tenantDomain, true);
            }
            GenericArtifactManager artifactManager = AppManagerUtil.getArtifactManager(registry, appType);
            GenericArtifact[] artifacts = artifactManager.getAllGenericArtifacts();
            for (GenericArtifact artifact : artifacts) {
                appArtifacts.add(artifact);
            }

        } catch (RegistryException e) {
            handleException("Failed to get APIs from the registry", e);
        } finally {
            if (isTenantFlowStarted) {
                PrivilegedCarbonContext.endTenantFlow();
            }
        }

        return appArtifacts;
    }

    /**
     *
     * Return the published 'app' (e.g. webapp, mobileapp) registry artifacts.
     *
     * @param appType App type
     * @return List of {@link GenericArtifact}
     * @throws AppManagementException on errors while trying to get published app artifacts
     */
    private List<GenericArtifact> getPublishedAppArtifacts(String appType) throws AppManagementException {
        List<GenericArtifact> appArtifacts = new ArrayList<GenericArtifact>();

        boolean isTenantFlowStarted = false;
        try {
            if (tenantDomain != null && !MultitenantConstants.SUPER_TENANT_DOMAIN_NAME.equals(tenantDomain)) {
                isTenantFlowStarted = true;
                PrivilegedCarbonContext.startTenantFlow();
                PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(tenantDomain, true);
            }
            GenericArtifactManager artifactManager = AppManagerUtil.getArtifactManager(registry, appType);
            GenericArtifact[] artifacts = artifactManager.getAllGenericArtifacts();
            for (GenericArtifact artifact : artifacts) {
                if (artifact.getLifecycleState().toUpperCase().equals(AppMConstants.PUBLISHED)) {
                    appArtifacts.add(artifact);
                }
            }
        } catch (RegistryException e) {
            handleException("Failed to get APIs from the registry", e);
        } finally {
            if (isTenantFlowStarted) {
                PrivilegedCarbonContext.endTenantFlow();
            }
        }
        return appArtifacts;
    }

    private App createApp(GenericArtifact artifact, String appType) throws AppManagementException {

        AppFactory appFactory = null;

        if (AppMConstants.WEBAPP_ASSET_TYPE.equals(appType)) {
            appFactory = new WebAppFactory();
        } else if (AppMConstants.MOBILE_ASSET_TYPE.equals(appType)) {
            appFactory = new MobileAppFactory();
        }

        return appFactory.createApp(artifact, registry);
    }

    private boolean isSearchHit(GenericArtifact artifact, Map<String, String> searchTerms)
            throws AppManagementException {

        boolean isSearchHit = true;

        for (Map.Entry<String, String> term : searchTerms.entrySet()) {
            try {
                if ("ID".equalsIgnoreCase(term.getKey())) {
                    if (!artifact.getId().equals(term.getValue())) {
                        isSearchHit = false;
                        break;
                    }
                } else if (!term.getValue().equals(artifact.getAttribute(getRxtAttributeName(term.getKey())))) {
                    isSearchHit = false;
                    break;
                }
            } catch (GovernanceException e) {
                String errorMessage = String
                        .format("Error while determining whether artifact '%s' is a search hit.", artifact.getId());
                throw new AppManagementException(errorMessage, e);
            }
        }

        return isSearchHit;
    }

    private String getRxtAttributeName(String searchKey) {

        String rxtAttributeName = null;

        if (searchKey.equalsIgnoreCase("NAME")) {
            rxtAttributeName = AppMConstants.API_OVERVIEW_NAME;
        } else if (searchKey.equalsIgnoreCase("PROVIDER")) {
            rxtAttributeName = AppMConstants.API_OVERVIEW_PROVIDER;
        } else if (searchKey.equalsIgnoreCase("VERSION")) {
            rxtAttributeName = AppMConstants.API_OVERVIEW_VERSION;
        }

        return rxtAttributeName;
    }

    @Override
    public void addTags(String appType, String appId, List<String> tags) throws AppManagementException {
        try {
            PrivilegedCarbonContext.startTenantFlow();
            PrivilegedCarbonContext.getThreadLocalCarbonContext().setUsername(this.username);
            PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(this.tenantDomain, true);

            GenericArtifactManager artifactManager = AppManagerUtil.getArtifactManager(registry, appType);
            GenericArtifact appArtifact = artifactManager.getGenericArtifact(appId);
            if (appArtifact != null) {
                for (String tag : tags) {
                    registry.applyTag(appArtifact.getPath(), tag);
                }
            } else {
                handleResourceNotFoundException("Failed to get " + appType
                        + " artifact corresponding to artifactId " + appId + ". Artifact does not exist");
            }
        } catch (RegistryException e) {
            handleException("Error occurred while adding tags" + StringUtils.join(tags, ",") + " to " + appType
                    + " with id : " + appId, e);
        } finally {
            PrivilegedCarbonContext.endTenantFlow();
        }
    }

    @Override
    public void removeTag(String appType, String appId, List<String> tags) throws AppManagementException {
        try {
            PrivilegedCarbonContext.startTenantFlow();
            PrivilegedCarbonContext.getThreadLocalCarbonContext().setUsername(this.username);
            PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(this.tenantDomain, true);

            GenericArtifactManager artifactManager = AppManagerUtil.getArtifactManager(registry, appType);
            GenericArtifact appArtifact = artifactManager.getGenericArtifact(appId);
            if (appArtifact != null) {
                for (String tag : tags) {
                    registry.removeTag(appArtifact.getPath(), tag);
                }
            } else {
                handleResourceNotFoundException("Failed to retrieve " + appType
                        + " artifact corresponding to artifactId " + appId + ". Artifact does not exist");
            }
        } catch (RegistryException e) {
            handleException("Error occurred while removing tags '" + StringUtils.join(tags, ",") + "' from "
                    + appType + " with id : " + appId, e);
        } finally {
            PrivilegedCarbonContext.endTenantFlow();
        }
    }

    @Override
    public Set<Tag> getAllTags(String appType) throws AppManagementException {
        Set<Tag> tagSet = new HashSet<>();
        try {
            PrivilegedCarbonContext.startTenantFlow();
            PrivilegedCarbonContext.getThreadLocalCarbonContext().setUsername(this.username);
            PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(this.tenantDomain, true);

            Map<String, String> params = new HashMap<String, String>();
            if (AppMConstants.WEBAPP_ASSET_TYPE.equals(appType)) {
                params.put("1", AppMConstants.MediaType.WEB_APP);
                params.put("2", AppMConstants.WEB_APP_LIFECYCLE_STATUS);
                params.put("3", "%");
            } else if (AppMConstants.MOBILE_ASSET_TYPE.equals(appType)) {
                params.put("1", AppMConstants.MediaType.MOBILE_APP);
                params.put("2", AppMConstants.MOBILE_APP_LIFECYCLE_STATUS);
                params.put("3", "%");
            } else {
                handleException("Could not retrieve tags. Unsupported applictaion type :" + appType + " provided");
            }

            String tagsQueryPath = RegistryConstants.QUERIES_COLLECTION_PATH + "/tag-summary-appmgt";

            org.wso2.carbon.registry.core.Collection collection = registry.executeQuery(tagsQueryPath, params);
            for (String fullTag : collection.getChildren()) {

                String tagName = fullTag.substring(fullTag.indexOf(";") + 1, fullTag.indexOf(":"));
                int numberOfOccurrence = Integer.parseInt(fullTag.substring(fullTag.indexOf(":") + 1));
                tagSet.add(new Tag(tagName, numberOfOccurrence));
            }

        } catch (RegistryException e) {
            handleException("Error occurred while retrieving tags for " + appType + "s", e);
        } finally {
            PrivilegedCarbonContext.endTenantFlow();
        }
        return tagSet;
    }

    @Override
    public Set<Tag> getAllTags(String appType, String appId) throws AppManagementException {
        Set<Tag> tagSet = new HashSet<>();
        try {
            org.wso2.carbon.registry.core.Tag[] tags = null;
            PrivilegedCarbonContext.startTenantFlow();
            PrivilegedCarbonContext.getThreadLocalCarbonContext().setUsername(this.username);
            PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(this.tenantDomain, true);

            GenericArtifactManager artifactManager = AppManagerUtil.getArtifactManager(registry, appType);
            GenericArtifact appArtifact = artifactManager.getGenericArtifact(appId);
            if (appArtifact != null) {
                String artifactPath = appArtifact.getPath();
                tags = registry.getTags(artifactPath);
                for (org.wso2.carbon.registry.core.Tag tag : tags) {
                    tagSet.add(new Tag(tag.getTagName()));
                }
            } else {
                handleResourceNotFoundException("Failed to get " + appType
                        + " artifact corresponding to artifactId " + appId + ". Artifact does not exist");
            }
        } catch (RegistryException e) {
            handleException("Error occurred while retrieving tags from " + appType + " with id : " + appId, e);
        } finally {
            PrivilegedCarbonContext.endTenantFlow();
        }
        return tagSet;
    }

    public String addResourceFile(String resourcePath, FileContent resourceFile) throws AppManagementException {
        try {
            Resource thumb = registry.newResource();
            thumb.setContentStream(resourceFile.getContent());
            thumb.setMediaType(resourceFile.getContentType());
            registry.put(resourcePath, thumb);
            if (MultitenantConstants.SUPER_TENANT_DOMAIN_NAME.equalsIgnoreCase(tenantDomain)) {
                return RegistryConstants.PATH_SEPARATOR + "registry" + RegistryConstants.PATH_SEPARATOR + "resource"
                        + RegistryConstants.PATH_SEPARATOR + "_system" + RegistryConstants.PATH_SEPARATOR
                        + "governance" + resourcePath;
            } else {
                return "/t/" + tenantDomain + RegistryConstants.PATH_SEPARATOR + "registry"
                        + RegistryConstants.PATH_SEPARATOR + "resource" + RegistryConstants.PATH_SEPARATOR
                        + "_system" + RegistryConstants.PATH_SEPARATOR + "governance" + resourcePath;
            }
        } catch (RegistryException e) {
            handleException("Error while adding the resource to the registry", e);
        }
        return null;
    }

    /**
     * Remove mobile applications binary files from storage
     * @param filePath file path of the banner image, thumbnail, screenshots and app binary
     * @throws AppManagementException
     */
    public void removeBinaryFromStorage(String filePath) throws AppManagementException {
        if (StringUtils.isEmpty(filePath)) {
            handleException("Mobile Application BinaryFileStorage Configuration cannot be found."
                    + " Pleas check the configuration in app-management.xml ");
        }

        File binaryFile = new File(filePath);
        if (!binaryFile.exists()) {
            handleException("Binary file " + filePath + " does not exist");
        }

        boolean isDeleted = binaryFile.delete();
        if (!isDeleted) {
            handleException("Error occurred while deleting file " + filePath);
        }
    }

    /**
     * Generate one-time download link content in Database
     * @param appId mobile application id that the one-time download link generated for
     * @return UUID of the download link
     * @throws AppManagementException
     */
    public String generateOneTimeDownloadLink(String appId) throws AppManagementException {

        String downloadLinkUUID = null;
        try {
            GenericArtifactManager artifactManager = AppManagerUtil.getArtifactManager(registry,
                    AppMConstants.MOBILE_ASSET_TYPE);
            GenericArtifact mobileAppArtifact = artifactManager.getGenericArtifact(appId);
            if (mobileAppArtifact == null) {
                handleResourceNotFoundException(
                        "Failed to generate one-time download link for Mobile App. The artifact corresponding to artifactId "
                                + appId + " does not exist");
            }

            if (!AppMConstants.MOBILE_APP_TYPE_PUBLIC
                    .equals(mobileAppArtifact.getAttribute(AppMConstants.MOBILE_APP_OVERVIEW_TYPE))) {
                OneTimeDownloadLink oneTimeDownloadLink = new OneTimeDownloadLink();
                UUID contentUUID = UUID.randomUUID();
                downloadLinkUUID = contentUUID.toString();
                oneTimeDownloadLink.setUUID(downloadLinkUUID);
                oneTimeDownloadLink
                        .setFileName(mobileAppArtifact.getAttribute(AppMConstants.MOBILE_APP_OVERVIEW_URL));
                oneTimeDownloadLink.setDownloaded(false);
                appRepository.persistOneTimeDownloadLink(oneTimeDownloadLink);
            }
        } catch (RegistryException e) {
            handleException(
                    "Error occurred while generating one-time download link for mobile application : " + appId, e);
        }
        return downloadLinkUUID;
    }

    /**
     * Retrieve one-time download link details from database
     * @param UUID UUID of the one-time download link
     * @return
     * @throws AppManagementException
     */
    public OneTimeDownloadLink getOneTimeDownloadLinkDetails(String UUID) throws AppManagementException {
        return appRepository.getOneTimeDownloadLinkDetails(UUID);
    }

    /**
     * Update one-time download link details in database
     * @param oneTimeDownloadLink OneTimeDownloadLink content
     * @throws AppManagementException
     */
    public void updateOneTimeDownloadLinkStatus(OneTimeDownloadLink oneTimeDownloadLink)
            throws AppManagementException {
        appRepository.updateOneTimeDownloadLinkStatus(oneTimeDownloadLink);
    }

    public String getGatewayEndpoint() {
        Environment gatewayEnvironment = ServiceReferenceHolder.getInstance().getAPIManagerConfigurationService()
                .getAPIManagerConfiguration().getApiGatewayEnvironments().get(0);

        String gatewayUrl = gatewayEnvironment.getApiGatewayEndpoint().split(",")[0];
        return gatewayUrl;
    }

    public String getAppUUIDbyName(String appName, String appVersion, int tenantId) throws AppManagementException {
        return appRepository.getAppUUIDbyName(appName, appVersion, tenantId);
    }

    public String uploadImage(FileContent fileContent) throws AppManagementException {
        UUID contentUUID = UUID.randomUUID();
        String fileExtension = FilenameUtils.getExtension(fileContent.getFileName());
        String filename = generateBinaryUUID() + "." + fileExtension;
        fileContent.setFileName(filename);
        fileContent.setContentType("image/" + fileExtension);
        fileContent.setUuid(contentUUID.toString());
        try {
            fileContent.setContentLength(fileContent.getContent().available());
        } catch (IOException e) {
            handleException("Error occurred while uploading static content", e);
        }
        appRepository.persistStaticContents(fileContent);
        return contentUUID.toString() + File.separator + fileContent.getFileName();
    }

    private static String generateBinaryUUID() {
        SecureRandom secRandom = new SecureRandom();
        byte[] result = new byte[8];
        secRandom.nextBytes(result);
        String uuid = String.valueOf(Hex.encodeHex(result));
        return uuid;
    }
}