org.wso2.carbon.apimgt.core.impl.APIPublisherImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.wso2.carbon.apimgt.core.impl.APIPublisherImpl.java

Source

/*
 *
 *   Copyright (c) 2016, 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.apimgt.core.impl;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wso2.carbon.apimgt.core.api.APIGateway;
import org.wso2.carbon.apimgt.core.api.APILifecycleManager;
import org.wso2.carbon.apimgt.core.api.APIMObservable;
import org.wso2.carbon.apimgt.core.api.APIMgtAdminService;
import org.wso2.carbon.apimgt.core.api.APIPublisher;
import org.wso2.carbon.apimgt.core.api.EventObserver;
import org.wso2.carbon.apimgt.core.api.GatewaySourceGenerator;
import org.wso2.carbon.apimgt.core.api.IdentityProvider;
import org.wso2.carbon.apimgt.core.api.KeyManager;
import org.wso2.carbon.apimgt.core.api.WSDLProcessor;
import org.wso2.carbon.apimgt.core.api.WorkflowExecutor;
import org.wso2.carbon.apimgt.core.api.WorkflowResponse;
import org.wso2.carbon.apimgt.core.dao.APISubscriptionDAO;
import org.wso2.carbon.apimgt.core.dao.ApiDAO;
import org.wso2.carbon.apimgt.core.dao.ApplicationDAO;
import org.wso2.carbon.apimgt.core.dao.LabelDAO;
import org.wso2.carbon.apimgt.core.dao.PolicyDAO;
import org.wso2.carbon.apimgt.core.dao.TagDAO;
import org.wso2.carbon.apimgt.core.dao.WorkflowDAO;
import org.wso2.carbon.apimgt.core.exception.APIManagementException;
import org.wso2.carbon.apimgt.core.exception.APIMgtDAOException;
import org.wso2.carbon.apimgt.core.exception.APIMgtResourceNotFoundException;
import org.wso2.carbon.apimgt.core.exception.APIMgtWSDLException;
import org.wso2.carbon.apimgt.core.exception.ApiDeleteFailureException;
import org.wso2.carbon.apimgt.core.exception.ExceptionCodes;
import org.wso2.carbon.apimgt.core.exception.GatewayException;
import org.wso2.carbon.apimgt.core.exception.IdentityProviderException;
import org.wso2.carbon.apimgt.core.exception.LabelException;
import org.wso2.carbon.apimgt.core.exception.WorkflowException;
import org.wso2.carbon.apimgt.core.models.API;
import org.wso2.carbon.apimgt.core.models.APIResource;
import org.wso2.carbon.apimgt.core.models.CorsConfiguration;
import org.wso2.carbon.apimgt.core.models.DocumentInfo;
import org.wso2.carbon.apimgt.core.models.Endpoint;
import org.wso2.carbon.apimgt.core.models.Event;
import org.wso2.carbon.apimgt.core.models.Label;
import org.wso2.carbon.apimgt.core.models.LifeCycleEvent;
import org.wso2.carbon.apimgt.core.models.Provider;
import org.wso2.carbon.apimgt.core.models.Subscription;
import org.wso2.carbon.apimgt.core.models.SubscriptionValidationData;
import org.wso2.carbon.apimgt.core.models.UriTemplate;
import org.wso2.carbon.apimgt.core.models.WSDLArchiveInfo;
import org.wso2.carbon.apimgt.core.models.WorkflowStatus;
import org.wso2.carbon.apimgt.core.models.policy.Policy;
import org.wso2.carbon.apimgt.core.template.APIConfigContext;
import org.wso2.carbon.apimgt.core.template.APITemplateException;
import org.wso2.carbon.apimgt.core.template.dto.TemplateBuilderDTO;
import org.wso2.carbon.apimgt.core.util.APIFileUtils;
import org.wso2.carbon.apimgt.core.util.APIMWSDLUtils;
import org.wso2.carbon.apimgt.core.util.APIMgtConstants;
import org.wso2.carbon.apimgt.core.util.APIMgtConstants.APILCWorkflowStatus;
import org.wso2.carbon.apimgt.core.util.APIMgtConstants.WorkflowConstants;
import org.wso2.carbon.apimgt.core.util.APIUtils;
import org.wso2.carbon.apimgt.core.workflow.APIStateChangeWorkflow;
import org.wso2.carbon.apimgt.core.workflow.WorkflowExecutorFactory;
import org.wso2.carbon.lcm.core.exception.LifecycleException;
import org.wso2.carbon.lcm.core.impl.LifecycleEventManager;
import org.wso2.carbon.lcm.core.impl.LifecycleState;
import org.wso2.carbon.lcm.sql.beans.LifecycleHistoryBean;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.ProtocolException;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

/**
 * Implementation of API Publisher operations
 */
public class APIPublisherImpl extends AbstractAPIManager implements APIPublisher, APIMObservable {

    private static final Logger log = LoggerFactory.getLogger(APIPublisherImpl.class);

    // Map to store observers, which observe APIPublisher events
    private Map<String, EventObserver> eventObservers = new HashMap<>();

    public APIPublisherImpl(String username, IdentityProvider idp, KeyManager keyManager, ApiDAO apiDAO,
            ApplicationDAO applicationDAO, APISubscriptionDAO apiSubscriptionDAO, PolicyDAO policyDAO,
            APILifecycleManager apiLifecycleManager, LabelDAO labelDAO, WorkflowDAO workflowDAO, TagDAO tagDAO,
            GatewaySourceGenerator gatewaySourceGenerator, APIGateway apiGatewayPublisher) {
        super(username, idp, keyManager, apiDAO, applicationDAO, apiSubscriptionDAO, policyDAO, apiLifecycleManager,
                labelDAO, workflowDAO, tagDAO, gatewaySourceGenerator, apiGatewayPublisher);
    }

    /**
     * Returns a list of all #{@code org.wso2.carbon.apimgt.core.models.Provider} available on the system.
     *
     * @return {@code Set<Provider>}
     * @throws APIManagementException if failed to get Providers
     */
    @Override
    public Set<Provider> getAllProviders() throws APIManagementException {
        return null;
    }

    /**
     * Get a list of subscriptions for provider's APIs
     *
     * @param offset       Starting index of the search results
     * @param limit        Number of search results returned
     * @param providerName if of the provider
     * @return {@code List<Subscriber>} List of subscriptions for provider's APIs
     * @throws APIManagementException if failed to get subscriptions
     */
    @Override
    public List<Subscription> getSubscribersOfProvider(int offset, int limit, String providerName)
            throws APIManagementException {
        try {
            return getApiSubscriptionDAO().getAPISubscriptionsForUser(offset, limit, providerName);
        } catch (APIMgtDAOException e) {
            String errorMsg = "Unable to fetch subscriptions APIs of provider " + providerName;
            log.error(errorMsg, e);
            throw new APIManagementException(errorMsg, e, e.getErrorHandler());
        }
    }

    /**
     * get details of provider
     *
     * @param providerName name of the provider
     * @return Provider
     * @throws APIManagementException if failed to get Provider
     */
    @Override
    public Provider getProvider(String providerName) throws APIManagementException {
        return null;
    }

    /**
     * Returns full list of Subscribers of an API
     *
     * @param identifier String
     * @return {@code Set<String>}
     * @throws APIManagementException if failed to get Subscribers
     */
    @Override
    public Set<String> getSubscribersOfAPI(API identifier) throws APIManagementException {
        return null;
    }

    /**
     * this method returns the {@code Set<APISubscriptionCount>} for given provider and api
     *
     * @param id String
     * @return {@code Set<APISubscriptionCount>}
     * @throws APIManagementException if failed to get APISubscriptionCountByAPI
     */
    @Override
    public long getAPISubscriptionCountByAPI(String id) throws APIManagementException {
        long subscriptionCount;
        try {
            subscriptionCount = getApiSubscriptionDAO().getSubscriptionCountByAPI(id);
        } catch (APIMgtDAOException e) {
            log.error("Couldn't retrieve Subscriptions for API " + id, e, log);
            throw new APIManagementException("Couldn't retrieve Subscriptions for API " + id, e,
                    ExceptionCodes.SUBSCRIPTION_NOT_FOUND);
        }
        return subscriptionCount;
    }

    @Override
    public String getDefaultVersion(String apiid) throws APIManagementException {
        return null;
    }

    @Override
    public API getAPIbyUUID(String uuid) throws APIManagementException {
        API api = null;
        try {
            api = super.getAPIbyUUID(uuid);
            if (api != null) {
                api.setUserSpecificApiPermissions(getAPIPermissionsOfLoggedInUser(getUsername(), api));
                String permissionString = api.getApiPermission();
                if (!StringUtils.isEmpty(permissionString)) {
                    api.setApiPermission(replaceGroupIdWithName(permissionString));
                }
            }
        } catch (ParseException e) {
            String errorMsg = "Error occurred while parsing the permission json string for API " + api.getName();
            log.error(errorMsg, e);
            throw new APIManagementException(errorMsg, e, ExceptionCodes.JSON_PARSE_ERROR);
        }
        return api;
    }

    /**
     * Adds a new API to the system
     *
     * @param apiBuilder API model object
     * @return UUID of the added API.
     * @throws APIManagementException if failed to add API
     */
    @Override
    public String addAPI(API.APIBuilder apiBuilder) throws APIManagementException {

        API createdAPI;
        APIGateway gateway = getApiGateway();

        apiBuilder.provider(getUsername());
        if (StringUtils.isEmpty(apiBuilder.getId())) {
            apiBuilder.id(UUID.randomUUID().toString());
        }

        LocalDateTime localDateTime = LocalDateTime.now();
        apiBuilder.createdTime(localDateTime);
        apiBuilder.lastUpdatedTime(localDateTime);
        apiBuilder.createdBy(getUsername());
        apiBuilder.updatedBy(getUsername());
        if (apiBuilder.getLabels().isEmpty()) {
            Set<String> labelSet = new HashSet<>();
            labelSet.add(APIMgtConstants.DEFAULT_LABEL_NAME);
            apiBuilder.labels(labelSet);
        }
        Map<String, Endpoint> apiEndpointMap = apiBuilder.getEndpoint();
        validateEndpoints(apiEndpointMap, false);
        try {
            if (!isApiNameExist(apiBuilder.getName()) && !isContextExist(apiBuilder.getContext())) {
                LifecycleState lifecycleState = getApiLifecycleManager().addLifecycle(APIMgtConstants.API_LIFECYCLE,
                        getUsername());
                apiBuilder.associateLifecycle(lifecycleState);

                createUriTemplateList(apiBuilder, false);

                List<UriTemplate> list = new ArrayList<>(apiBuilder.getUriTemplates().values());
                List<TemplateBuilderDTO> resourceList = new ArrayList<>();

                validateApiPolicy(apiBuilder.getApiPolicy());
                validateSubscriptionPolicies(apiBuilder);
                for (UriTemplate uriTemplate : list) {
                    TemplateBuilderDTO dto = new TemplateBuilderDTO();
                    dto.setTemplateId(uriTemplate.getTemplateId());
                    dto.setUriTemplate(uriTemplate.getUriTemplate());
                    dto.setHttpVerb(uriTemplate.getHttpVerb());
                    Map<String, Endpoint> map = uriTemplate.getEndpoint();
                    if (map.containsKey(APIMgtConstants.PRODUCTION_ENDPOINT)) {
                        Endpoint endpoint = map.get(APIMgtConstants.PRODUCTION_ENDPOINT);
                        dto.setProductionEndpoint(endpoint);
                    }
                    if (map.containsKey(APIMgtConstants.SANDBOX_ENDPOINT)) {
                        Endpoint endpoint = map.get(APIMgtConstants.SANDBOX_ENDPOINT);
                        dto.setSandboxEndpoint(endpoint);
                    }
                    resourceList.add(dto);
                }
                GatewaySourceGenerator gatewaySourceGenerator = getGatewaySourceGenerator();
                APIConfigContext apiConfigContext = new APIConfigContext(apiBuilder.build(),
                        config.getGatewayPackageName());
                gatewaySourceGenerator.setApiConfigContext(apiConfigContext);
                String gatewayConfig = gatewaySourceGenerator.getConfigStringFromTemplate(resourceList);
                if (log.isDebugEnabled()) {
                    log.debug("API " + apiBuilder.getName() + "gateway config: " + gatewayConfig);
                }
                apiBuilder.gatewayConfig(gatewayConfig);

                if (StringUtils.isEmpty(apiBuilder.getApiDefinition())) {
                    apiBuilder.apiDefinition(apiDefinitionFromSwagger20.generateSwaggerFromResources(apiBuilder));
                }
                if (!StringUtils.isEmpty(apiBuilder.getApiPermission())) {
                    Map<String, Integer> roleNamePermissionList;
                    roleNamePermissionList = getAPIPermissionArray(apiBuilder.getApiPermission());
                    apiBuilder.permissionMap(roleNamePermissionList);
                }

                createdAPI = apiBuilder.build();
                APIUtils.validate(createdAPI);

                //Add API to gateway
                gateway.addAPI(createdAPI);
                if (log.isDebugEnabled()) {
                    log.debug("API : " + apiBuilder.getName() + " has been identifier published to gateway");
                }

                Set<String> apiRoleList;

                //if the API has public visibility, add the API without any role checking
                //if the API has role based visibility, add the API with role checking
                if (API.Visibility.PUBLIC == createdAPI.getVisibility()) {
                    getApiDAO().addAPI(createdAPI);
                } else if (API.Visibility.RESTRICTED == createdAPI.getVisibility()) {
                    //get all the roles in the system
                    Set<String> allAvailableRoles = APIUtils.getAllAvailableRoles();
                    //get the roles needed to be associated with the API
                    apiRoleList = createdAPI.getVisibleRoles();
                    if (APIUtils.checkAllowedRoles(allAvailableRoles, apiRoleList)) {
                        getApiDAO().addAPI(createdAPI);
                    }
                }

                APIUtils.logDebug("API " + createdAPI.getName() + "-" + createdAPI.getVersion() + " was created "
                        + "successfully.", log);

                // 'API_M Functions' related code
                //Create a payload with event specific details
                Map<String, String> eventPayload = new HashMap<>();
                eventPayload.put(APIMgtConstants.FunctionsConstants.API_ID, createdAPI.getId());
                eventPayload.put(APIMgtConstants.FunctionsConstants.API_NAME, createdAPI.getName());
                eventPayload.put(APIMgtConstants.FunctionsConstants.API_VERSION, createdAPI.getVersion());
                eventPayload.put(APIMgtConstants.FunctionsConstants.API_DESCRIPTION, createdAPI.getDescription());
                eventPayload.put(APIMgtConstants.FunctionsConstants.API_CONTEXT, createdAPI.getContext());
                eventPayload.put(APIMgtConstants.FunctionsConstants.API_LC_STATUS, createdAPI.getLifeCycleStatus());
                eventPayload.put(APIMgtConstants.FunctionsConstants.API_PERMISSION, createdAPI.getApiPermission());
                // This will notify all the EventObservers(Asynchronous)
                ObserverNotifier observerNotifier = new ObserverNotifier(Event.API_CREATION, getUsername(),
                        ZonedDateTime.now(ZoneOffset.UTC), eventPayload, this);
                ObserverNotifierThreadPool.getInstance().executeTask(observerNotifier);
            } else {
                String message = "Duplicate API already Exist with name/Context " + apiBuilder.getName();
                log.error(message);
                throw new APIManagementException(message, ExceptionCodes.API_ALREADY_EXISTS);
            }
        } catch (APIMgtDAOException e) {
            String errorMsg = "Error occurred while creating the API - " + apiBuilder.getName();
            log.error(errorMsg);
            throw new APIManagementException(errorMsg, e, e.getErrorHandler());
        } catch (LifecycleException | ParseException e) {
            String errorMsg = "Error occurred while Associating the API - " + apiBuilder.getName();
            log.error(errorMsg);
            throw new APIManagementException(errorMsg, e, ExceptionCodes.APIMGT_LIFECYCLE_EXCEPTION);
        } catch (APITemplateException e) {
            String message = "Error generating API configuration for API " + apiBuilder.getName();
            log.error(message, e);
            throw new APIManagementException(message, ExceptionCodes.TEMPLATE_EXCEPTION);
        } catch (GatewayException e) {
            String message = "Error occurred while adding API - " + apiBuilder.getName() + " to gateway";
            log.error(message, e);
            throw new APIManagementException(message, ExceptionCodes.GATEWAY_EXCEPTION);
        }
        return apiBuilder.getId();
    }

    private void validateEndpoints(Map<String, Endpoint> endpointMap, boolean apiUpdate)
            throws APIManagementException {
        if (endpointMap != null) {
            for (Map.Entry<String, Endpoint> entry : endpointMap.entrySet()) {
                if (APIMgtConstants.API_SPECIFIC_ENDPOINT.equals(entry.getValue().getApplicableLevel())) {
                    Endpoint.Builder endpointBuilder = new Endpoint.Builder(entry.getValue());
                    if (StringUtils.isEmpty(endpointBuilder.getId())) {
                        endpointBuilder.id(UUID.randomUUID().toString());
                    }
                    if (StringUtils.isEmpty(endpointBuilder.getApplicableLevel())) {
                        endpointBuilder.applicableLevel(APIMgtConstants.API_SPECIFIC_ENDPOINT);
                    }
                    Endpoint endpoint = endpointBuilder.build();
                    try {
                        Endpoint existingEndpoint = getApiDAO().getEndpoint(endpoint.getId());
                        if (existingEndpoint == null) {
                            if (getApiDAO().getEndpointByName(endpoint.getName()) != null) {
                                String msg = "Endpoint Already Exist By Name : " + endpoint.getName();
                                throw new APIManagementException(msg, ExceptionCodes.ENDPOINT_ALREADY_EXISTS);
                            } else {
                                endpointMap.replace(entry.getKey(), endpointBuilder.build());
                            }
                        } else {
                            if (apiUpdate && !existingEndpoint.getName().equals(endpoint.getName())) {
                                if (getApiDAO().getEndpointByName(endpoint.getName()) != null) {
                                    String msg = "Endpoint Already Exist By Name : " + endpoint.getName();
                                    throw new APIManagementException(msg, ExceptionCodes.ENDPOINT_ALREADY_EXISTS);
                                } else {
                                    endpointMap.replace(entry.getKey(), endpointBuilder.build());
                                }
                            }
                        }
                    } catch (APIMgtDAOException e) {
                        String msg = "Couldn't find Endpoint By Name : " + endpoint.getName();
                        log.error(msg, e);
                        throw new APIManagementException(msg, e, ExceptionCodes.APIMGT_DAO_EXCEPTION);
                    }

                } else {
                    endpointMap.replace(entry.getKey(), getEndpoint(entry.getValue().getId()));
                }
            }
        }
    }

    private void validateApiPolicy(Policy policy) throws APIManagementException {
        if (policy != null) {
            Policy apiPolicy = getPolicyByName(APIMgtAdminService.PolicyLevel.api, policy.getPolicyName());
            policy.setUuid(apiPolicy.getUuid());
        }
    }

    private void createUriTemplateList(API.APIBuilder apiBuilder, boolean update) throws APIManagementException {
        Map<String, UriTemplate> uriTemplateMap = new HashMap();
        if (apiBuilder.getUriTemplates().isEmpty()) {
            apiBuilder.uriTemplates(APIUtils.getDefaultUriTemplates());
            apiBuilder.apiDefinition(apiDefinitionFromSwagger20.generateSwaggerFromResources(apiBuilder));
        }
        for (UriTemplate uriTemplate : apiBuilder.getUriTemplates().values()) {
            UriTemplate.UriTemplateBuilder uriTemplateBuilder = new UriTemplate.UriTemplateBuilder(uriTemplate);
            if (StringUtils.isEmpty(uriTemplateBuilder.getTemplateId())) {
                uriTemplateBuilder.templateId(APIUtils.generateOperationIdFromPath(uriTemplate.getUriTemplate(),
                        uriTemplate.getHttpVerb()));
            }
            Map<String, Endpoint> endpointMap = uriTemplateBuilder.getEndpoint();
            validateEndpoints(endpointMap, update);
            validateApiPolicy(uriTemplateBuilder.getPolicy());
            uriTemplateMap.put(uriTemplateBuilder.getTemplateId(), uriTemplateBuilder.build());
        }
        apiBuilder.uriTemplates(uriTemplateMap);
    }

    /**
     * @param api API Object
     * @return If api definition is valid or not.
     * @throws APIManagementException If failed to validate the API.
     */
    @Override
    public boolean isAPIUpdateValid(API api) throws APIManagementException {
        return false;
    }

    /**
     * Updates design and implementation of an existing API. This method must not be used to change API status.
     * Implementations should throw an exceptions when such attempts are made. All life cycle state changes
     * should be carried out using the changeAPIStatus method of this interface.
     *
     * @param apiBuilder {@code org.wso2.carbon.apimgt.core.models.API.APIBuilder} model object
     * @throws APIManagementException if failed to update API
     */
    @Override
    public void updateAPI(API.APIBuilder apiBuilder) throws APIManagementException {
        APIGateway gateway = getApiGateway();

        apiBuilder.provider(getUsername());
        apiBuilder.updatedBy(getUsername());
        try {
            API originalAPI = getAPIbyUUID(apiBuilder.getId());
            if (originalAPI != null) {
                //Checks whether the logged in user has the "UPDATE" permission for the API
                verifyUserPermissionsToUpdateAPI(getUsername(), originalAPI);
                apiBuilder.createdTime(originalAPI.getCreatedTime());
                //workflow status is an internal property and shouldn't be allowed to update externally
                apiBuilder.workflowStatus(originalAPI.getWorkflowStatus());
                if ((originalAPI.getName().equals(apiBuilder.getName()))
                        && (originalAPI.getVersion().equals(apiBuilder.getVersion()))
                        && (originalAPI.getProvider().equals(apiBuilder.getProvider()))
                        && originalAPI.getLifeCycleStatus().equalsIgnoreCase(apiBuilder.getLifeCycleStatus())) {

                    if (!StringUtils.isEmpty(apiBuilder.getApiPermission())) {
                        apiBuilder.apiPermission(replaceGroupNamesWithId(apiBuilder.getApiPermission()));
                        Map<String, Integer> roleNamePermissionList;
                        roleNamePermissionList = getAPIPermissionArray(apiBuilder.getApiPermission());
                        apiBuilder.permissionMap(roleNamePermissionList);
                    }
                    Map<String, Endpoint> apiEndpointMap = apiBuilder.getEndpoint();
                    validateEndpoints(apiEndpointMap, true);
                    createUriTemplateList(apiBuilder, true);
                    validateApiPolicy(apiBuilder.getApiPolicy());
                    validateSubscriptionPolicies(apiBuilder);
                    String updatedSwagger = apiDefinitionFromSwagger20.generateSwaggerFromResources(apiBuilder);
                    String gatewayConfig = getApiGatewayConfig(apiBuilder.getId());
                    GatewaySourceGenerator gatewaySourceGenerator = getGatewaySourceGenerator();
                    APIConfigContext apiConfigContext = new APIConfigContext(apiBuilder.build(),
                            config.getGatewayPackageName());
                    gatewaySourceGenerator.setApiConfigContext(apiConfigContext);
                    String updatedGatewayConfig = gatewaySourceGenerator.getGatewayConfigFromSwagger(gatewayConfig,
                            updatedSwagger);

                    API api = apiBuilder.build();

                    //Add API to gateway
                    gateway.updateAPI(api);
                    if (log.isDebugEnabled()) {
                        log.debug("API : " + apiBuilder.getName() + " has been successfully updated in gateway");
                    }

                    if (originalAPI.getContext() != null
                            && !originalAPI.getContext().equals(apiBuilder.getContext())) {
                        if (!checkIfAPIContextExists(api.getContext())) {
                            //if the API has public visibility, update the API without any role checking
                            if (API.Visibility.PUBLIC == api.getVisibility()) {
                                getApiDAO().updateAPI(api.getId(), api);
                            } else if (API.Visibility.RESTRICTED == api.getVisibility()) {
                                //get all the roles in the system
                                Set<String> availableRoles = APIUtils.getAllAvailableRoles();
                                //get the roles needed to be associated with the API
                                Set<String> apiRoleList = api.getVisibleRoles();
                                //if the API has role based visibility, update the API with role checking
                                if (APIUtils.checkAllowedRoles(availableRoles, apiRoleList)) {
                                    getApiDAO().updateAPI(api.getId(), api);
                                }
                            }
                            getApiDAO().updateApiDefinition(api.getId(), updatedSwagger, api.getUpdatedBy());
                            getApiDAO().updateGatewayConfig(api.getId(), updatedGatewayConfig, api.getUpdatedBy());
                        } else {
                            throw new APIManagementException("Context already Exist",
                                    ExceptionCodes.API_ALREADY_EXISTS);
                        }
                    } else {
                        //if the API has public visibility, update the API without any role checking
                        if (API.Visibility.PUBLIC == api.getVisibility()) {
                            getApiDAO().updateAPI(api.getId(), api);
                        } else if (API.Visibility.RESTRICTED == api.getVisibility()) {
                            //get all the roles in the system
                            Set<String> allAvailableRoles = APIUtils.getAllAvailableRoles();
                            //get the roles needed to be associated with the API
                            Set<String> apiRoleList = api.getVisibleRoles();
                            //if the API has role based visibility, update the API with role checking
                            if (APIUtils.checkAllowedRoles(allAvailableRoles, apiRoleList)) {
                                getApiDAO().updateAPI(api.getId(), api);
                            }
                        }
                        getApiDAO().updateApiDefinition(api.getId(), updatedSwagger, api.getUpdatedBy());
                        getApiDAO().updateGatewayConfig(api.getId(), updatedGatewayConfig, api.getUpdatedBy());
                    }
                    if (log.isDebugEnabled()) {
                        log.debug("API " + api.getName() + "-" + api.getVersion() + " was updated successfully.");
                        // 'API_M Functions' related code
                        //Create a payload with event specific details
                        Map<String, String> eventPayload = new HashMap<>();
                        eventPayload.put(APIMgtConstants.FunctionsConstants.API_ID, api.getId());
                        eventPayload.put(APIMgtConstants.FunctionsConstants.API_NAME, api.getName());
                        eventPayload.put(APIMgtConstants.FunctionsConstants.API_VERSION, api.getVersion());
                        eventPayload.put(APIMgtConstants.FunctionsConstants.API_DESCRIPTION, api.getDescription());
                        eventPayload.put(APIMgtConstants.FunctionsConstants.API_CONTEXT, api.getContext());
                        eventPayload.put(APIMgtConstants.FunctionsConstants.API_LC_STATUS,
                                api.getLifeCycleStatus());
                        // This will notify all the EventObservers(Asynchronous)
                        ObserverNotifier observerNotifier = new ObserverNotifier(Event.API_UPDATE, getUsername(),
                                ZonedDateTime.now(ZoneOffset.UTC), eventPayload, this);
                        ObserverNotifierThreadPool.getInstance().executeTask(observerNotifier);
                    }
                } else {
                    APIUtils.verifyValidityOfApiUpdate(apiBuilder, originalAPI);
                }
            } else {

                log.error("Couldn't found API with ID " + apiBuilder.getId());
                throw new APIManagementException("Couldn't found API with ID " + apiBuilder.getId(),
                        ExceptionCodes.API_NOT_FOUND);
            }
        } catch (APIMgtDAOException e) {
            String errorMsg = "Error occurred while updating the API - " + apiBuilder.getName();
            log.error(errorMsg, e);
            throw new APIManagementException(errorMsg, e, e.getErrorHandler());
        } catch (ParseException e) {
            String errorMsg = "Error occurred while parsing the permission json from swagger - "
                    + apiBuilder.getName();
            log.error(errorMsg, e);
            throw new APIManagementException(errorMsg, e, ExceptionCodes.SWAGGER_PARSE_EXCEPTION);
        } catch (GatewayException e) {
            String message = "Error occurred while updating API - " + apiBuilder.getName() + " in gateway";
            log.error(message, e);
            throw new APIManagementException(message, ExceptionCodes.GATEWAY_EXCEPTION);
        }
    }

    private void validateSubscriptionPolicies(API.APIBuilder apiBuilder) throws APIManagementException {
        Set<Policy> subPolicies = new HashSet();
        for (Policy subscriptionPolicy : apiBuilder.getPolicies()) {
            Policy policy = getPolicyByName(APIMgtAdminService.PolicyLevel.subscription,
                    subscriptionPolicy.getPolicyName());
            if (policy == null) {
                throw new APIManagementException("Api Policy " + apiBuilder.getApiPolicy() + "Couldn't " + "find",
                        ExceptionCodes.POLICY_NOT_FOUND);
            }
            subPolicies.add(policy);
        }
        apiBuilder.policies(subPolicies);
    }

    /**
     * This method checks whether the currently logged in user has the "UPDATE" permission for the API
     * @param user - currently logged in user
     * @param api - the api to be updated
     * @throws APIManagementException - If the user does not have "UPDATE" permission for the API
     */
    private void verifyUserPermissionsToUpdateAPI(String user, API api) throws APIManagementException {
        List<String> userPermissions = api.getUserSpecificApiPermissions();
        if (!userPermissions.contains(APIMgtConstants.Permission.UPDATE)) {
            String message = "The user " + user + " does not have permission to update the api " + api.getName();
            if (log.isDebugEnabled()) {
                log.debug(message);
            }
            throw new APIManagementException(message, ExceptionCodes.NO_UPDATE_PERMISSIONS);
        }
    }

    /**
     * This method checks whether the currently logged in user has the "DELETE" permission for the API
     * @param user - currently logged in user
     * @param api - the api to be deleted
     * @throws APIManagementException - If the user does not have "DELETE" permission for the API
     */
    private void verifyUserPermissionsToDeleteAPI(String user, API api) throws APIManagementException {
        List<String> userPermissions = api.getUserSpecificApiPermissions();
        if (!userPermissions.contains(APIMgtConstants.Permission.DELETE)) {
            String message = "The user " + user + " does not have permission to delete the api " + api.getName();
            if (log.isDebugEnabled()) {
                log.debug(message);
            }
            throw new APIManagementException(message, ExceptionCodes.NO_DELETE_PERMISSIONS);
        }
    }

    /**
     * This method will return map with role names and its permission values.
     *
     * @param permissionJsonString Permission json object a string
     * @return Map of permission values.
     * @throws ParseException If failed to parse the json string.
     */
    private HashMap<String, Integer> getAPIPermissionArray(String permissionJsonString)
            throws ParseException, APIManagementException {

        HashMap<String, Integer> rolePermissionList = new HashMap<String, Integer>();
        JSONParser jsonParser = new JSONParser();
        JSONArray baseJsonArray = (JSONArray) jsonParser.parse(permissionJsonString);
        for (Object aBaseJsonArray : baseJsonArray) {
            JSONObject jsonObject = (JSONObject) aBaseJsonArray;
            String groupId = jsonObject.get(APIMgtConstants.Permission.GROUP_ID).toString();
            JSONArray subJsonArray = (JSONArray) jsonObject.get(APIMgtConstants.Permission.PERMISSION);
            int totalPermissionValue = 0;
            for (Object aSubJsonArray : subJsonArray) {
                if (APIMgtConstants.Permission.READ.equals(aSubJsonArray.toString().trim())) {
                    totalPermissionValue += APIMgtConstants.Permission.READ_PERMISSION;
                } else if (APIMgtConstants.Permission.UPDATE.equals(aSubJsonArray.toString().trim())) {
                    totalPermissionValue += APIMgtConstants.Permission.UPDATE_PERMISSION;
                } else if (APIMgtConstants.Permission.DELETE.equals(aSubJsonArray.toString().trim())) {
                    totalPermissionValue += APIMgtConstants.Permission.DELETE_PERMISSION;
                } else if (APIMgtConstants.Permission.MANAGE_SUBSCRIPTION.equals(aSubJsonArray.toString().trim())) {
                    totalPermissionValue += APIMgtConstants.Permission.MANAGE_SUBSCRIPTION_PERMISSION;
                }
            }
            rolePermissionList.put(groupId, totalPermissionValue);
        }
        return rolePermissionList;
    }

    /**
     * This method replaces the groupId field's value to the role id instead of the name passed by the user
     *
     * @param permissionString - the permission json string which contains role names in groupId field
     * @return permission string with replaced groupId
     * @throws ParseException         - if there is an error parsing the json string
     * @throws APIManagementException - if there is an error getting the IdentityProvider instance
     */
    private String replaceGroupNamesWithId(String permissionString) throws ParseException, APIManagementException {
        JSONArray updatedPermissionArray = new JSONArray();
        JSONParser jsonParser = new JSONParser();
        JSONArray originalPermissionArray = (JSONArray) jsonParser.parse(permissionString);
        try {
            for (Object permissionObj : originalPermissionArray) {
                JSONObject jsonObject = (JSONObject) permissionObj;
                String groupName = (String) jsonObject.get(APIMgtConstants.Permission.GROUP_ID);
                String groupId = getIdentityProvider().getRoleId(groupName);
                JSONObject updatedPermissionJsonObj = new JSONObject();
                updatedPermissionJsonObj.put(APIMgtConstants.Permission.GROUP_ID, groupId);
                updatedPermissionJsonObj.put(APIMgtConstants.Permission.PERMISSION,
                        jsonObject.get(APIMgtConstants.Permission.PERMISSION));
                updatedPermissionArray.add(updatedPermissionJsonObj);
            }
        } catch (IdentityProviderException e) {
            String errorMessage = "There are invalid roles in the permission string";
            log.error(errorMessage, e);
            throw new APIManagementException(errorMessage, e, ExceptionCodes.UNSUPPORTED_ROLE);
        }
        return updatedPermissionArray.toJSONString();
    }

    /**
     * This method retrieves the set of overall permissions for a given api for the logged in user
     *
     * @param loggedInUserName - Logged in user
     * @param api              - The API whose permissions for the logged in user is retrieved
     * @return The overall list of permissions for the given API for the logged in user
     */
    private List<String> getAPIPermissionsOfLoggedInUser(String loggedInUserName, API api)
            throws APIManagementException {
        Set<String> permissionArrayForUser = new HashSet<>();
        Map<String, Integer> permissionMap = api.getPermissionMap();
        String provider = api.getProvider();
        //TODO: Remove the check for admin after IS adds an ID to admin user
        if (loggedInUserName.equals(provider) || permissionMap == null || permissionMap.isEmpty()
                || "admin".equals(loggedInUserName)) {
            permissionArrayForUser.add(APIMgtConstants.Permission.READ);
            permissionArrayForUser.add(APIMgtConstants.Permission.UPDATE);
            permissionArrayForUser.add(APIMgtConstants.Permission.DELETE);
            permissionArrayForUser.add(APIMgtConstants.Permission.MANAGE_SUBSCRIPTION);
        } else {
            try {
                String userId = getIdentityProvider().getIdOfUser(loggedInUserName);
                List<String> loggedInUserRoles = getIdentityProvider().getRoleIdsOfUser(userId);
                List<String> permissionRoleList = getRolesFromPermissionMap(permissionMap);
                List<String> rolesOfUserWithAPIPermissions = null;
                //To prevent a possible null pointer exception
                if (loggedInUserRoles == null) {
                    loggedInUserRoles = new ArrayList<>();
                }
                //get the intersection - retainAll() transforms first set to the result of intersection
                loggedInUserRoles.retainAll(permissionRoleList);
                if (!loggedInUserRoles.isEmpty()) {
                    rolesOfUserWithAPIPermissions = loggedInUserRoles;
                }
                if (rolesOfUserWithAPIPermissions != null) {
                    Integer aggregatePermissions = 0;
                    //Calculating aggregate permissions using Bitwise OR operation
                    for (String role : rolesOfUserWithAPIPermissions) {
                        aggregatePermissions |= permissionMap.get(role);
                    }
                    permissionArrayForUser = new HashSet<>(
                            APIUtils.constructApiPermissionsListForValue(aggregatePermissions));
                }
            } catch (IdentityProviderException e) {
                String errorMsg = "Error occurred while calling SCIM endpoint to retrieve user " + loggedInUserName
                        + "'s information";
                log.error(errorMsg, e);
                throw new APIManagementException(errorMsg, e, e.getErrorHandler());
            }
        }
        List<String> finalAggregatedPermissionList = new ArrayList<>();
        finalAggregatedPermissionList.addAll(permissionArrayForUser);
        if (log.isDebugEnabled()) {
            String message = "Aggregate permissions of user " + loggedInUserName + " for the API " + api.getName()
                    + " are " + StringUtils.join(finalAggregatedPermissionList, ", ") + ".";
            log.debug(message);
        }
        return finalAggregatedPermissionList;
    }

    /**
     * This method is used to extract the groupIds or roles from the permissionMap
     *
     * @param permissionMap - The map containing the group IDs(roles) and their permissions
     * @return - The list of groupIds specified for permissions
     */
    private List<String> getRolesFromPermissionMap(Map<String, Integer> permissionMap) {
        List<String> permissionRoleList = new ArrayList<>();
        for (String groupId : permissionMap.keySet()) {
            permissionRoleList.add(groupId);
        }
        return permissionRoleList;
    }

    /**
     * This method replaces the groupId field's value of the api permissions string to the role name before sending to
     * frontend
     *
     * @param permissionString - permissions string containing role ids in the groupId field
     * @return the permission string replacing the groupId field's value to role name
     * @throws ParseException - if there is an error parsing the permission json
     * @throws APIManagementException - if there is an error getting the IdentityProvider instance
     */
    private String replaceGroupIdWithName(String permissionString) throws ParseException, APIManagementException {
        JSONArray updatedPermissionArray = new JSONArray();
        JSONParser jsonParser = new JSONParser();
        JSONArray originalPermissionArray = (JSONArray) jsonParser.parse(permissionString);

        for (Object permissionObj : originalPermissionArray) {
            JSONObject jsonObject = (JSONObject) permissionObj;
            String groupId = (String) jsonObject.get(APIMgtConstants.Permission.GROUP_ID);
            try {
                String groupName = getIdentityProvider().getRoleName(groupId);
                JSONObject updatedPermissionJsonObj = new JSONObject();
                updatedPermissionJsonObj.put(APIMgtConstants.Permission.GROUP_ID, groupName);
                updatedPermissionJsonObj.put(APIMgtConstants.Permission.PERMISSION,
                        jsonObject.get(APIMgtConstants.Permission.PERMISSION));
                updatedPermissionArray.add(updatedPermissionJsonObj);
            } catch (IdentityProviderException e) {
                //lets the execution continue after logging the exception
                String errorMessage = "Error occurred while calling SCIM endpoint to retrieve role name of role "
                        + "with Id " + groupId;
                log.warn(errorMessage, e);
            }
        }
        return updatedPermissionArray.toJSONString();
    }

    /**
     * @see org.wso2.carbon.apimgt.core.api.APIPublisher#updateAPIStatus(String, String, Map)
     */
    @Override
    public WorkflowResponse updateAPIStatus(String apiId, String status, Map<String, Boolean> checkListItemMap)
            throws APIManagementException {
        WorkflowResponse workflowResponse = null;
        try {
            API api = getApiDAO().getAPI(apiId);
            if (api != null && !APILCWorkflowStatus.PENDING.toString().equals(api.getWorkflowStatus())) {
                API.APIBuilder apiBuilder = new API.APIBuilder(api);
                apiBuilder.lastUpdatedTime(LocalDateTime.now());
                apiBuilder.updatedBy(getUsername());
                LifecycleState currentState = getApiLifecycleManager().getLifecycleDataForState(
                        apiBuilder.getLifecycleInstanceId(), apiBuilder.getLifeCycleStatus());
                apiBuilder.lifecycleState(currentState);
                for (Map.Entry<String, Boolean> checkListItem : checkListItemMap.entrySet()) {
                    getApiLifecycleManager().checkListItemEvent(api.getLifecycleInstanceId(),
                            api.getLifeCycleStatus(), checkListItem.getKey(), checkListItem.getValue());
                }
                API originalAPI = apiBuilder.build();
                WorkflowExecutor executor = WorkflowExecutorFactory.getInstance()
                        .getWorkflowExecutor(WorkflowConstants.WF_TYPE_AM_API_STATE);
                APIStateChangeWorkflow workflow = new APIStateChangeWorkflow(getApiDAO(), getApiSubscriptionDAO(),
                        getWorkflowDAO(), getApiLifecycleManager(), getApiGateway());
                workflow.setApiName(originalAPI.getName());
                workflow.setApiProvider(originalAPI.getProvider());
                workflow.setApiVersion(originalAPI.getVersion());
                workflow.setCurrentState(currentState.getState());
                workflow.setTransitionState(status);

                workflow.setWorkflowReference(originalAPI.getId());
                workflow.setExternalWorkflowReference(UUID.randomUUID().toString());
                workflow.setCreatedTime(LocalDateTime.now());
                workflow.setWorkflowType(WorkflowConstants.WF_TYPE_AM_API_STATE);
                workflow.setInvoker(getUsername());

                //setting attributes for internal use. These are set to use from outside the executor's method
                //these will be saved in the AM_WORKFLOW table so these can be retrieved later
                workflow.setAttribute(WorkflowConstants.ATTRIBUTE_API_CUR_STATE, currentState.getState());
                workflow.setAttribute(WorkflowConstants.ATTRIBUTE_API_TARGET_STATE, status);
                workflow.setAttribute(WorkflowConstants.ATTRIBUTE_API_LC_INVOKER, getUsername());
                workflow.setAttribute(WorkflowConstants.ATTRIBUTE_API_LAST_UPTIME,
                        originalAPI.getLastUpdatedTime().toString());
                String workflowDescription = "API state change workflow for " + workflow.getApiName() + ":"
                        + workflow.getApiVersion() + ":" + workflow.getApiProvider() + " from "
                        + workflow.getCurrentState() + " to " + workflow.getTransitionState() + " state by "
                        + getUsername();
                workflow.setWorkflowDescription(workflowDescription);
                workflowResponse = executor.execute(workflow);
                workflow.setStatus(workflowResponse.getWorkflowStatus());

                if (WorkflowStatus.CREATED != workflowResponse.getWorkflowStatus()) {
                    completeWorkflow(executor, workflow);
                } else {
                    //add entry to workflow table if it is only in pending state
                    addWorkflowEntries(workflow);
                    getApiDAO().updateAPIWorkflowStatus(api.getId(), APILCWorkflowStatus.PENDING);
                }
            } else if (api != null && APILCWorkflowStatus.PENDING.toString().equals(api.getWorkflowStatus())) {
                String message = "Pending state transition for api :" + api.getName();
                log.error(message);
                throw new APIManagementException(message, ExceptionCodes.WORKFLOW_PENDING);
            } else {
                throw new APIMgtResourceNotFoundException("Requested API " + apiId + " Not Available");
            }
        } catch (APIMgtDAOException e) {
            String errorMsg = "Couldn't change the status of api ID " + apiId;
            log.error(errorMsg, e);
            throw new APIManagementException(errorMsg, e, e.getErrorHandler());
        } catch (LifecycleException e) {
            String errorMsg = "Couldn't change the status of api ID " + apiId;
            log.error(errorMsg, e);
            throw new APIManagementException(errorMsg, e, ExceptionCodes.APIMGT_LIFECYCLE_EXCEPTION);
        }
        return workflowResponse;
    }

    /**
     * This method used to Update the lifecycle checklist of API
     *
     * @param apiId            UUID of the API
     * @param status           Current API lifecycle status.
     * @param checkListItemMap Check list item values.
     * @throws APIManagementException If failed to update checklist item values.
     */
    @Override
    public void updateCheckListItem(String apiId, String status, Map<String, Boolean> checkListItemMap)
            throws APIManagementException {
        API api = getApiDAO().getAPI(apiId);
        try {
            if (api != null) {
                API.APIBuilder apiBuilder = new API.APIBuilder(api);
                apiBuilder.lastUpdatedTime(LocalDateTime.now());
                apiBuilder.updatedBy(getUsername());
                apiBuilder.lifecycleState(getApiLifecycleManager().getLifecycleDataForState(
                        apiBuilder.getLifecycleInstanceId(), apiBuilder.getLifeCycleStatus()));
                for (Map.Entry<String, Boolean> checkListItem : checkListItemMap.entrySet()) {
                    getApiLifecycleManager().checkListItemEvent(api.getLifecycleInstanceId(),
                            api.getLifeCycleStatus(), checkListItem.getKey(), checkListItem.getValue());
                }
            }
        } catch (LifecycleException e) {
            String errorMsg = "Couldn't get the lifecycle status of api ID " + apiId;
            log.error(errorMsg, e);
            throw new APIManagementException(errorMsg, e, ExceptionCodes.APIMGT_LIFECYCLE_EXCEPTION);
        }
    }

    /**
     * Create a new version of the <code>api</code>, with version <code>newVersion</code>
     *
     * @param apiId      The API to be copied
     * @param newVersion The version of the new API
     * @return UUID of the newly created version.
     * @throws APIManagementException If an error occurs while trying to create
     *                                the new version of the API
     */
    @Override
    public String createNewAPIVersion(String apiId, String newVersion) throws APIManagementException {
        String newVersionedId;
        LifecycleState lifecycleState;
        // validate parameters
        if (StringUtils.isEmpty(newVersion)) {
            String errorMsg = "New API version cannot be empty";
            log.error(errorMsg);
            throw new APIManagementException(errorMsg, ExceptionCodes.PARAMETER_NOT_PROVIDED);
        }
        if (StringUtils.isEmpty(apiId)) {
            String errorMsg = "API ID cannot be empty";
            log.error(errorMsg);
            throw new APIManagementException(errorMsg, ExceptionCodes.PARAMETER_NOT_PROVIDED);
        }

        try {
            API api = getApiDAO().getAPI(apiId);
            if (api != null) {
                if (api.getVersion().equals(newVersion)) {
                    String errMsg = "New API version " + newVersion + " cannot be same as the previous version for "
                            + "API " + api.getName();
                    log.error(errMsg);
                    throw new APIManagementException(errMsg, ExceptionCodes.API_ALREADY_EXISTS);
                }
                API.APIBuilder apiBuilder = new API.APIBuilder(api);
                apiBuilder.id(UUID.randomUUID().toString());
                apiBuilder.version(newVersion);
                apiBuilder.context(api.getContext().replace(api.getVersion(), newVersion));
                lifecycleState = getApiLifecycleManager().addLifecycle(APIMgtConstants.API_LIFECYCLE,
                        getUsername());
                apiBuilder.associateLifecycle(lifecycleState);
                apiBuilder.copiedFromApiId(api.getId());
                if (StringUtils.isEmpty(apiBuilder.getApiDefinition())) {
                    apiBuilder.apiDefinition(apiDefinitionFromSwagger20.generateSwaggerFromResources(apiBuilder));
                }
                getApiDAO().addAPI(apiBuilder.build());
                newVersionedId = apiBuilder.getId();
            } else {
                throw new APIMgtResourceNotFoundException("Requested API on UUID " + apiId + "Couldn't be found");
            }
        } catch (APIMgtDAOException e) {
            String errorMsg = "Couldn't create new API version from " + apiId;
            log.error(errorMsg, e);
            throw new APIManagementException(errorMsg, e, e.getErrorHandler());
        } catch (LifecycleException e) {
            String errorMsg = "Couldn't Associate  new API Lifecycle from " + apiId;
            log.error(errorMsg, e);
            throw new APIManagementException(errorMsg, e, ExceptionCodes.APIMGT_LIFECYCLE_EXCEPTION);
        }
        return newVersionedId;
    }

    /**
     * Attach Documentation (without content) to an API
     *
     * @param apiId        UUID of API
     * @param documentInfo Document Summary
     * @return UUID of document
     * @throws APIManagementException if failed to add documentation
     */
    @Override
    public String addDocumentationInfo(String apiId, DocumentInfo documentInfo) throws APIManagementException {
        try {
            LocalDateTime localDateTime = LocalDateTime.now();
            DocumentInfo document;
            DocumentInfo.Builder docBuilder = new DocumentInfo.Builder(documentInfo);
            docBuilder.createdBy(getUsername());
            docBuilder.updatedBy(getUsername());
            docBuilder.createdTime(localDateTime);
            docBuilder.lastUpdatedTime(localDateTime);
            if (StringUtils.isEmpty(docBuilder.getId())) {
                docBuilder = docBuilder.id(UUID.randomUUID().toString());
            }

            if (documentInfo.getPermission() != null && !("").equals(documentInfo.getPermission())) {
                HashMap roleNamePermissionList;
                roleNamePermissionList = APIUtils.getAPIPermissionArray(documentInfo.getPermission());
                docBuilder.permissionMap(roleNamePermissionList);
            }

            document = docBuilder.build();

            if (!getApiDAO().isDocumentExist(apiId, document)) {
                getApiDAO().addDocumentInfo(apiId, document);
                return document.getId();
            } else {
                String msg = "Document already exist for the api " + apiId;
                log.error(msg);
                throw new APIManagementException(msg, ExceptionCodes.DOCUMENT_ALREADY_EXISTS);
            }
        } catch (APIMgtDAOException e) {
            String errorMsg = "Unable to add documentation";
            log.error(errorMsg, e);
            throw new APIManagementException(errorMsg, e, e.getErrorHandler());
        } catch (ParseException e) {
            String errorMsg = "Unable to add documentation due to json parse error";
            log.error(errorMsg, e);
            throw new APIManagementException(errorMsg, e, ExceptionCodes.JSON_PARSE_ERROR);
        }
    }

    /**
     * Add a document (of source type FILE) with a file
     *
     * @param resourceId UUID of API
     * @param content    content of the file as an Input Stream
     * @param dataType   File mime type
     * @throws APIManagementException if failed to add the file
     */
    @Override
    public void uploadDocumentationFile(String resourceId, InputStream content, String dataType)
            throws APIManagementException {
        try {
            getApiDAO().addDocumentFileContent(resourceId, content, dataType, getUsername());
        } catch (APIMgtDAOException e) {
            String errorMsg = "Unable to add documentation with file";
            log.error(errorMsg, e);
            throw new APIManagementException(errorMsg, e, e.getErrorHandler());
        }
    }

    /**
     * Removes a given documentation
     *
     * @param docId Document Id
     * @throws APIManagementException if failed to remove documentation
     */
    @Override
    public void removeDocumentation(String docId) throws APIManagementException {
        try {
            getApiDAO().deleteDocument(docId);
        } catch (APIMgtDAOException e) {
            String errorMsg = "Unable to add documentation with file";
            log.error(errorMsg, e);
            throw new APIManagementException(errorMsg, e, e.getErrorHandler());
        }
    }

    /**
     * Checks if a given API Context exists in the registry
     *
     * @param context Context of the API.
     * @return boolean result
     * @throws APIManagementException If failed to check if API exist.
     */
    @Override
    public boolean checkIfAPIContextExists(String context) throws APIManagementException {
        return isContextExist(context);
    }

    /**
     * Checks if a given API name exists in the registry
     *
     * @param name Name of the API.
     * @return boolean result
     * @throws APIManagementException If failed to check if API exist.
     */
    @Override
    public boolean checkIfAPINameExists(String name) throws APIManagementException {
        return isApiNameExist(name);
    }

    /**
     * This method used to save the documentation content
     *
     * @param docId UUID of the Doc
     * @param text  Plain text content of the doc.
     * @throws APIManagementException if failed to add the document as a resource to registry
     */
    @Override
    public void addDocumentationContent(String docId, String text) throws APIManagementException {
        getApiDAO().addDocumentInlineContent(docId, text, getUsername());
    }

    /**
     * Updates a given documentation
     *
     * @param apiId        UUID of the API.
     * @param documentInfo Documentation
     * @return UUID of the updated document.
     * @throws APIManagementException if failed to update docs
     */
    @Override
    public String updateDocumentation(String apiId, DocumentInfo documentInfo) throws APIManagementException {
        try {
            LocalDateTime localDateTime = LocalDateTime.now();
            DocumentInfo document;
            DocumentInfo.Builder docBuilder = new DocumentInfo.Builder(documentInfo);
            docBuilder.updatedBy(getUsername());
            docBuilder.lastUpdatedTime(localDateTime);
            if (StringUtils.isEmpty(docBuilder.getId())) {
                docBuilder = docBuilder.id(UUID.randomUUID().toString());
            }

            if (documentInfo.getPermission() != null && !("").equals(documentInfo.getPermission())) {
                HashMap roleNamePermissionList;
                roleNamePermissionList = APIUtils.getAPIPermissionArray(documentInfo.getPermission());
                docBuilder.permissionMap(roleNamePermissionList);
            }

            document = docBuilder.build();

            if (getApiDAO().isDocumentExist(apiId, document)) {
                getApiDAO().updateDocumentInfo(apiId, document, getUsername());
                return document.getId();
            } else {
                String msg = "Document " + document.getName() + " not found for the api " + apiId;
                log.error(msg);
                throw new APIManagementException(msg, ExceptionCodes.DOCUMENT_NOT_FOUND);
            }
        } catch (APIMgtDAOException e) {
            String errorMsg = "Unable to update the documentation";
            log.error(errorMsg, e);
            throw new APIManagementException(errorMsg, e, e.getErrorHandler());
        } catch (ParseException e) {
            String errorMsg = "Unable to update the documentation due to json parse error";
            log.error(errorMsg, e);
            throw new APIManagementException(errorMsg, e, ExceptionCodes.JSON_PARSE_ERROR);
        }
    }

    /**
     * Copies current Documentation into another version of the same API.
     *
     * @param apiId     id of the String
     * @param toVersion Version to which Documentation should be copied.
     * @throws APIManagementException if failed to copy docs
     */
    @Override
    public void copyAllDocumentation(String apiId, String toVersion) throws APIManagementException {

    }

    /**
     * Returns the details of all the life-cycle changes done per API.
     *
     * @param apiId id of the String
     * @return List of life-cycle events per given API
     * @throws APIManagementException if failed to copy docs
     */
    @Override
    public List<LifeCycleEvent> getLifeCycleEvents(String apiId) throws APIManagementException {
        List<LifeCycleEvent> lifeCycleEventList = new ArrayList<>();
        try {
            API apiSummary = getApiDAO().getAPISummary(apiId);
            if (apiSummary != null) {
                List<LifecycleHistoryBean> lifecycleHistoryBeanList = getApiLifecycleManager()
                        .getLifecycleHistory(apiSummary.getLifecycleInstanceId());
                for (LifecycleHistoryBean lifecycleHistoryBean : lifecycleHistoryBeanList) {
                    lifeCycleEventList.add(new LifeCycleEvent(apiId, lifecycleHistoryBean.getPreviousState(),
                            lifecycleHistoryBean.getPostState(), lifecycleHistoryBean.getUser(),
                            lifecycleHistoryBean.getUpdatedTime()));
                }
            }
        } catch (APIMgtDAOException e) {
            String errorMsg = "Couldn't find APISummary Resource for ID " + apiId;
            log.error(errorMsg, e);
            throw new APIManagementException(errorMsg, e, e.getErrorHandler());
        } catch (LifecycleException e) {
            String errorMsg = "Couldn't find APILifecycle History for ID " + apiId;
            log.error(errorMsg, e);
            throw new APIManagementException(errorMsg, e, ExceptionCodes.APIMGT_LIFECYCLE_EXCEPTION);
        }
        return lifeCycleEventList;
    }

    /**
     * Delete an API
     *
     * @param identifier UUID of the API.
     * @throws APIManagementException if failed to remove the API
     */
    @Override
    public void deleteAPI(String identifier) throws APIManagementException {
        APIGateway gateway = getApiGateway();
        try {
            if (getAPISubscriptionCountByAPI(identifier) == 0) {
                API api = getAPIbyUUID(identifier);
                if (api != null) {
                    //Checks whether the user has required permissions to delete the API
                    verifyUserPermissionsToDeleteAPI(getUsername(), api);
                    String apiWfStatus = api.getWorkflowStatus();
                    API.APIBuilder apiBuilder = new API.APIBuilder(api);

                    //Delete API in gateway

                    gateway.deleteAPI(api);
                    if (log.isDebugEnabled()) {
                        log.debug("API : " + api.getName() + " has been successfully removed from the gateway");
                    }

                    getApiDAO().deleteAPI(identifier);
                    getApiLifecycleManager().removeLifecycle(apiBuilder.getLifecycleInstanceId());
                    APIUtils.logDebug("API with id " + identifier + " was deleted successfully.", log);

                    if (APILCWorkflowStatus.PENDING.toString().equals(apiWfStatus)) {
                        cleanupPendingTaskForAPIStateChange(identifier);
                    }
                    // 'API_M Functions' related code
                    //Create a payload with event specific details
                    Map<String, String> eventPayload = new HashMap<>();
                    eventPayload.put(APIMgtConstants.FunctionsConstants.API_ID, api.getId());
                    eventPayload.put(APIMgtConstants.FunctionsConstants.API_NAME, api.getName());
                    eventPayload.put(APIMgtConstants.FunctionsConstants.API_VERSION, api.getVersion());
                    eventPayload.put(APIMgtConstants.FunctionsConstants.API_PROVIDER, api.getProvider());
                    eventPayload.put(APIMgtConstants.FunctionsConstants.API_DESCRIPTION, api.getDescription());
                    // This will notify all the EventObservers(Asynchronous)
                    ObserverNotifier observerNotifier = new ObserverNotifier(Event.API_DELETION, getUsername(),
                            ZonedDateTime.now(ZoneOffset.UTC), eventPayload, this);
                    ObserverNotifierThreadPool.getInstance().executeTask(observerNotifier);
                }
            } else {
                throw new ApiDeleteFailureException("API with " + identifier + " already have subscriptions");
            }
        } catch (APIMgtDAOException e) {
            String errorMsg = "Error occurred while deleting the API with id " + identifier;
            log.error(errorMsg, e);
            throw new APIManagementException(errorMsg, e, e.getErrorHandler());
        } catch (LifecycleException e) {
            String errorMsg = "Error occurred while Disassociating the API with Lifecycle id " + identifier;
            log.error(errorMsg, e);
            throw new APIManagementException(errorMsg, e, ExceptionCodes.APIMGT_LIFECYCLE_EXCEPTION);
        } catch (GatewayException e) {
            String message = "Error occurred while deleting API with id - " + identifier + " from gateway";
            log.error(message, e);
            throw new APIManagementException(message, ExceptionCodes.GATEWAY_EXCEPTION);
        }
    }

    /**
     * @param limit  Limit
     * @param offset Offset
     * @param query  Search query
     * @return List of APIS.
     * @throws APIManagementException If failed to formatApiSearch APIs.
     */
    @Override
    public List<API> searchAPIs(Integer limit, Integer offset, String query) throws APIManagementException {

        List<API> apiResults;
        String user = getUsername();
        Set<String> roles = new HashSet<>();
        try {
            //TODO: Need to validate users roles against results returned
            if (!"admin".equals(user)) {
                String userId = getIdentityProvider().getIdOfUser(user);
                roles = new HashSet<>(getIdentityProvider().getRoleIdsOfUser(userId));
            }
            if (query != null && !query.isEmpty()) {
                apiResults = getApiDAO().searchAPIs(roles, user, query, offset, limit);
            } else {
                apiResults = getApiDAO().getAPIs(roles, user);
            }
            return apiResults;
        } catch (APIMgtDAOException e) {
            String errorMsg = "Error occurred while Searching the API with query " + query;
            log.error(errorMsg, e);
            throw new APIManagementException(errorMsg, e, e.getErrorHandler());
        } catch (IdentityProviderException e) {
            String errorMsg = "Error occurred while calling SCIM endpoint to retrieve user " + user
                    + "'s information";
            log.error(errorMsg, e);
            throw new APIManagementException(errorMsg, e, e.getErrorHandler());
        }
    }

    /**
     * Update the subscription status
     *
     * @param subId     Subscription ID
     * @param subStatus Subscription Status
     * @throws APIManagementException If failed to update subscription status
     */
    @Override
    public void updateSubscriptionStatus(String subId, APIMgtConstants.SubscriptionStatus subStatus)
            throws APIManagementException {
        try {
            getApiSubscriptionDAO().updateSubscriptionStatus(subId, subStatus);
            Subscription subscription = getApiSubscriptionDAO().getAPISubscription(subId);
            if (subscription != null) {
                API subscribedApi = subscription.getApi();
                List<SubscriptionValidationData> subscriptionValidationDataList = getApiSubscriptionDAO()
                        .getAPISubscriptionsOfAPIForValidation(subscribedApi.getContext(),
                                subscribedApi.getVersion(), subscription.getApplication().getId());
                getApiGateway().updateAPISubscriptionStatus(subscriptionValidationDataList);
            }
        } catch (APIMgtDAOException e) {
            throw new APIManagementException(e);
        }
    }

    /**
     * Update the subscription Policy
     *
     * @param subId     Subscription ID
     * @param newPolicy New Subscription Policy
     * @throws APIManagementException If failed to update subscription policy
     */
    @Override
    public void updateSubscriptionPolicy(String subId, String newPolicy) throws APIManagementException {
        try {
            getApiSubscriptionDAO().updateSubscriptionPolicy(subId, newPolicy);
        } catch (APIMgtDAOException e) {
            throw new APIManagementException(e);
        }
    }

    /**
     * This method returns the lifecycle data for an API including current state,next states.
     *
     * @param apiId String
     * @return {@code Map<String,Object>} a map with lifecycle data
     * @throws APIManagementException If failed to get Lifecycle data.
     */
    @Override
    public LifecycleState getAPILifeCycleData(String apiId) throws APIManagementException {
        try {
            API api = getApiDAO().getAPISummary(apiId);
            if (api != null) {
                return getApiLifecycleManager().getLifecycleDataForState(api.getLifecycleInstanceId(),
                        api.getLifeCycleStatus());
            } else {
                throw new APIMgtResourceNotFoundException("Couldn't retrieve API Summary for " + apiId);
            }
        } catch (APIMgtDAOException e) {
            String errorMsg = "Couldn't retrieve API Summary for " + apiId;
            log.error(errorMsg, e);
            throw new APIManagementException(errorMsg, e, e.getErrorHandler());
        } catch (LifecycleException e) {
            String errorMsg = "Couldn't retrieve API Lifecycle for " + apiId;
            log.error(errorMsg, e);
            throw new APIManagementException(errorMsg, e, ExceptionCodes.APIMGT_LIFECYCLE_EXCEPTION);
        }
    }

    /**
     * Get the current lifecycle status of the api
     *
     * @param string Api identifier
     * @return Current lifecycle status
     * @throws APIManagementException If failed to get lifecycle status.
     */
    @Override
    public String getAPILifeCycleStatus(String string) throws APIManagementException {
        return null;
    }

    /**
     * Get the paginated APIs from publisher
     *
     * @param start starting number
     * @param end   ending number
     * @return set of API
     * @throws APIManagementException if failed to get Apis
     */
    @Override
    public Map<String, Object> getAllPaginatedAPIs(int start, int end) throws APIManagementException {
        return null;
    }

    /**
     * Return list of endpoints
     *
     * @return List of Endpoints..
     * @throws APIManagementException If filed to get endpoints.
     */
    @Override
    public List<Endpoint> getAllEndpoints() throws APIManagementException {
        try {
            return getApiDAO().getEndpoints();
        } catch (APIMgtDAOException e) {
            String msg = "Failed to get all Endpoints";
            log.error(msg, e);
            throw new APIManagementException(msg, e, e.getErrorHandler());
        }

    }

    /**
     * Get endpoint details according to the endpointId
     *
     * @param endpointId uuid of endpoint
     * @return details of endpoint
     * @throws APIManagementException If failed to get endpoint.
     */
    @Override
    public Endpoint getEndpoint(String endpointId) throws APIManagementException {
        try {
            return getApiDAO().getEndpoint(endpointId);
        } catch (APIMgtDAOException e) {
            String msg = "Failed to get Endpoint : " + endpointId;
            log.error(msg, e);
            throw new APIManagementException(msg, e, e.getErrorHandler());
        }
    }

    @Override
    public Endpoint getEndpointByName(String endpointName) throws APIManagementException {
        try {
            return getApiDAO().getEndpointByName(endpointName);
        } catch (APIMgtDAOException e) {
            String msg = "Failed to get Endpoint : " + endpointName;
            log.error(msg, e);
            throw new APIManagementException(msg, e, e.getErrorHandler());
        }
    }

    /**
     * Add an endpoint
     *
     * @param endpoint Endpoint object.
     * @return UUID of the added endpoint.
     * @throws APIManagementException If failed to add endpoint.
     */
    @Override
    public String addEndpoint(Endpoint endpoint) throws APIManagementException {

        APIGateway gateway = getApiGateway();

        Endpoint.Builder builder = new Endpoint.Builder(endpoint);
        builder.id(UUID.randomUUID().toString());
        builder.applicableLevel(APIMgtConstants.GLOBAL_ENDPOINT);
        Endpoint endpoint1 = builder.build();
        String key = endpoint.getName();
        if (key == null || StringUtils.isEmpty(key)) {
            log.error("Endpoint name not provided");
            throw new APIManagementException("Endpoint name is not provided", ExceptionCodes.ENDPOINT_ADD_FAILED);
        }
        Endpoint endpoint2 = getApiDAO().getEndpointByName(endpoint.getName());
        if (endpoint2 != null) {
            log.error(String.format("Endpoint already exist with name %s", key));
            throw new APIManagementException("Endpoint already exist with name " + key,
                    ExceptionCodes.ENDPOINT_ALREADY_EXISTS);
        }
        gateway.addEndpoint(endpoint1);
        String config = getGatewaySourceGenerator().getEndpointConfigStringFromTemplate(endpoint1);
        endpoint1 = new Endpoint.Builder(endpoint1).config(config).build();
        try {

            getApiDAO().addEndpoint(endpoint1);
        } catch (APIMgtDAOException e) {
            String msg = "Failed to add Endpoint : " + endpoint.getName();
            log.error(msg, e);
            throw new APIManagementException(msg, e, e.getErrorHandler());
        }
        //update endpoint config in gateway
        return endpoint1.getId();
    }

    /**
     * Update and endpoint
     *
     * @param endpoint Endpoint object.
     * @throws APIManagementException If failed to update endpoint.
     */
    @Override
    public void updateEndpoint(Endpoint endpoint) throws APIManagementException {
        APIGateway gateway = getApiGateway();
        gateway.updateEndpoint(endpoint);
        String config = getGatewaySourceGenerator().getEndpointConfigStringFromTemplate(endpoint);
        Endpoint updatedEndpoint = new Endpoint.Builder(endpoint).config(config).build();
        try {
            getApiDAO().updateEndpoint(updatedEndpoint);
        } catch (APIMgtDAOException e) {
            String msg = "Failed to update Endpoint : " + endpoint.getName();
            log.error(msg, e);
            throw new APIManagementException(msg, e, e.getErrorHandler());
        }
        //update endpoint config in gateway
    }

    /**
     * Delete an endpoint
     *
     * @param endpointId UUID of the endpoint.
     * @throws APIManagementException If failed to delete the endpoint.
     */
    @Override
    public void deleteEndpoint(String endpointId) throws APIManagementException {
        APIGateway gateway = getApiGateway();

        Endpoint endpoint = getEndpoint(endpointId);
        if (!getApiDAO().isEndpointAssociated(endpointId)) {
            try {

                getApiDAO().deleteEndpoint(endpointId);
            } catch (APIMgtDAOException e) {
                String msg = "Failed to delete Endpoint : " + endpointId;
                log.error(msg, e);
                throw new APIManagementException(msg, e, e.getErrorHandler());
            }
            gateway.deleteEndpoint(endpoint);

        } else {
            String msg = "Endpoint Already Have Associated With API";
            log.error(msg);
            throw new APIManagementException(msg, ExceptionCodes.ENDPOINT_DELETE_FAILED);
        }
    }

    /**
     * Create api from Definition
     *
     * @param apiDefinition API definition stream.
     * @return UUID of the added API.
     * @throws APIManagementException If failed to add the API.
     */
    @Override
    public String addApiFromDefinition(InputStream apiDefinition) throws APIManagementException {
        try {
            String apiDefinitionString = IOUtils.toString(apiDefinition);
            API.APIBuilder apiBuilder = apiDefinitionFromSwagger20.generateApiFromSwaggerResource(getUsername(),
                    apiDefinitionString);
            apiBuilder.corsConfiguration(new CorsConfiguration());
            apiBuilder.apiDefinition(apiDefinitionString);
            addAPI(apiBuilder);
            return apiBuilder.getId();
        } catch (IOException e) {
            throw new APIManagementException("Couldn't Generate ApiDefinition from file",
                    ExceptionCodes.API_DEFINITION_MALFORMED);
        }
    }

    @Override
    public String addApiFromDefinition(HttpURLConnection urlConn) throws APIManagementException {
        try {
            urlConn.setDoOutput(true);
            urlConn.setRequestMethod(APIMgtConstants.HTTP_GET);
            urlConn.connect();
            int responseCode = urlConn.getResponseCode();
            if (responseCode == 200) {
                return addApiFromDefinition(urlConn.getInputStream());
            } else {
                throw new APIManagementException(
                        "Error while getting swagger resource from url : " + urlConn.getURL(),
                        ExceptionCodes.API_DEFINITION_MALFORMED);
            }
        } catch (ProtocolException e) {
            String msg = "Protocol exception while getting the swagger resource from url";
            log.error(msg, e);
            throw new APIManagementException(msg, ExceptionCodes.API_DEFINITION_MALFORMED);
        } catch (IOException e) {
            String msg = "Error while getting the swagger resource from url";
            log.error(msg, e);
            throw new APIManagementException(msg, ExceptionCodes.API_DEFINITION_MALFORMED);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void saveSwagger20Definition(String apiId, String jsonText) throws APIManagementException {
        try {
            LocalDateTime localDateTime = LocalDateTime.now();
            API api = getAPIbyUUID(apiId);
            Map<String, UriTemplate> oldUriTemplateMap = api.getUriTemplates();
            List<APIResource> apiResourceList = apiDefinitionFromSwagger20
                    .parseSwaggerAPIResources(new StringBuilder(jsonText));
            Map<String, UriTemplate> updatedUriTemplateMap = new HashMap<>();
            for (APIResource apiResource : apiResourceList) {
                updatedUriTemplateMap.put(apiResource.getUriTemplate().getTemplateId(),
                        apiResource.getUriTemplate());
            }
            Map<String, UriTemplate> uriTemplateMapNeedTobeUpdate = APIUtils
                    .getMergedUriTemplates(oldUriTemplateMap, updatedUriTemplateMap);
            API.APIBuilder apiBuilder = new API.APIBuilder(api);
            apiBuilder.uriTemplates(uriTemplateMapNeedTobeUpdate);
            apiBuilder.updatedBy(getUsername());
            apiBuilder.lastUpdatedTime(localDateTime);

            api = apiBuilder.build();
            GatewaySourceGenerator gatewaySourceGenerator = getGatewaySourceGenerator();
            APIConfigContext apiConfigContext = new APIConfigContext(apiBuilder.build(),
                    config.getGatewayPackageName());
            gatewaySourceGenerator.setApiConfigContext(apiConfigContext);
            String existingGatewayConfig = getApiGatewayConfig(apiId);
            String updatedGatewayConfig = gatewaySourceGenerator.getGatewayConfigFromSwagger(existingGatewayConfig,
                    jsonText);
            getApiDAO().updateAPI(apiId, api);
            getApiDAO().updateApiDefinition(apiId, jsonText, getUsername());
            getApiDAO().updateGatewayConfig(apiId, updatedGatewayConfig, getUsername());
        } catch (APIMgtDAOException e) {
            String errorMsg = "Couldn't update the Swagger Definition";
            log.error(errorMsg, e);
            throw new APIManagementException(errorMsg, e, e.getErrorHandler());
        }
    }

    @Override
    public String getAPIWSDL(String apiId) throws APIMgtDAOException {
        return getApiDAO().getWSDL(apiId);
    }

    @Override
    public InputStream getAPIWSDLArchive(String apiId) throws APIMgtDAOException {
        return getApiDAO().getWSDLArchive(apiId);
    }

    @Override
    public String addAPIFromWSDLArchive(API.APIBuilder apiBuilder, InputStream inputStream, boolean isHttpBinding)
            throws APIManagementException {
        WSDLArchiveInfo archiveInfo = extractAndValidateWSDLArchive(inputStream);
        if (log.isDebugEnabled()) {
            log.debug("Successfully extracted and validated WSDL file. Location: "
                    + archiveInfo.getAbsoluteFilePath());
        }

        apiBuilder.uriTemplates(APIMWSDLUtils.getUriTemplatesForWSDLOperations(
                archiveInfo.getWsdlInfo().getHttpBindingOperations(), isHttpBinding));
        String uuid = addAPI(apiBuilder);
        if (log.isDebugEnabled()) {
            log.debug("Successfully added the API. uuid: " + uuid);
        }

        try (InputStream fileInputStream = new FileInputStream(archiveInfo.getAbsoluteFilePath())) {
            getApiDAO().addOrUpdateWSDLArchive(uuid, fileInputStream, getUsername());
            if (log.isDebugEnabled()) {
                log.debug("Successfully added/updated the WSDL archive. uuid: " + uuid);
            }

            if (APIMgtConstants.WSDLConstants.WSDL_VERSION_20.equals(archiveInfo.getWsdlInfo().getVersion())) {
                log.info("Extraction of HTTP Binding operations is not supported for WSDL 2.0.");
            }
            return uuid;
        } catch (IOException e) {
            throw new APIMgtWSDLException("Unable to process WSDL archive at " + archiveInfo.getAbsoluteFilePath(),
                    e, ExceptionCodes.INTERNAL_WSDL_EXCEPTION);
        } finally {
            try {
                APIFileUtils.deleteDirectory(archiveInfo.getLocation());
            } catch (APIMgtDAOException e) {
                //This is not a blocker. Give a warning and continue
                log.warn("Error occured while deleting processed WSDL artifacts folder : "
                        + archiveInfo.getLocation());
            }
        }
    }

    @Override
    public String addAPIFromWSDLFile(API.APIBuilder apiBuilder, InputStream inputStream, boolean isHttpBinding)
            throws APIManagementException {
        byte[] wsdlContent;
        try {
            wsdlContent = IOUtils.toByteArray(inputStream);
        } catch (IOException e) {
            throw new APIMgtWSDLException("Error while converting input stream to byte array", e,
                    ExceptionCodes.INTERNAL_WSDL_EXCEPTION);
        }
        WSDLProcessor processor = WSDLProcessFactory.getInstance().getWSDLProcessor(wsdlContent);
        apiBuilder.uriTemplates(APIMWSDLUtils.getUriTemplatesForWSDLOperations(
                processor.getWsdlInfo().getHttpBindingOperations(), isHttpBinding));
        if (!processor.canProcess()) {
            throw new APIMgtWSDLException(
                    "Unable to process WSDL by the processor " + processor.getClass().getName(),
                    ExceptionCodes.CANNOT_PROCESS_WSDL_CONTENT);
        }

        String uuid = addAPI(apiBuilder);
        if (log.isDebugEnabled()) {
            log.debug("Successfully added the API. uuid: " + uuid);
        }
        getApiDAO().addOrUpdateWSDL(uuid, wsdlContent, getUsername());
        if (log.isDebugEnabled()) {
            log.debug("Successfully added the WSDL file to database. API uuid: " + uuid);
        }
        if (APIMgtConstants.WSDLConstants.WSDL_VERSION_20.equals(processor.getWsdlInfo().getVersion())) {
            log.info("Extraction of HTTP Binding operations is not supported for WSDL 2.0.");
        }
        return uuid;
    }

    @Override
    public String addAPIFromWSDLURL(API.APIBuilder apiBuilder, String wsdlUrl, boolean isHttpBinding)
            throws APIManagementException {
        WSDLProcessor processor = WSDLProcessFactory.getInstance().getWSDLProcessor(wsdlUrl);
        if (!processor.canProcess()) {
            throw new APIMgtWSDLException(
                    "Unable to process WSDL by the processor " + processor.getClass().getName(),
                    ExceptionCodes.CANNOT_PROCESS_WSDL_CONTENT);
        }
        apiBuilder.uriTemplates(APIMWSDLUtils.getUriTemplatesForWSDLOperations(
                processor.getWsdlInfo().getHttpBindingOperations(), isHttpBinding));
        String uuid = addAPI(apiBuilder);
        if (log.isDebugEnabled()) {
            log.debug("Successfully added the API. uuid: " + uuid);
        }
        byte[] wsdlContentBytes = processor.getWSDL();
        getApiDAO().addOrUpdateWSDL(uuid, wsdlContentBytes, getUsername());
        if (log.isDebugEnabled()) {
            log.debug("Successfully added the content of WSDL URL to database. WSDL URL: " + wsdlUrl);
        }
        if (APIMgtConstants.WSDLConstants.WSDL_VERSION_20.equals(processor.getWsdlInfo().getVersion())) {
            log.info("Extraction of HTTP Binding operations is not supported for WSDL 2.0.");
        }
        return uuid;
    }

    @Override
    public String updateAPIWSDL(String apiId, InputStream inputStream)
            throws APIMgtDAOException, APIMgtWSDLException {
        byte[] wsdlContent;
        try {
            wsdlContent = IOUtils.toByteArray(inputStream);
            WSDLProcessor processor = WSDLProcessFactory.getInstance().getWSDLProcessor(wsdlContent);
            if (!processor.canProcess()) {
                throw new APIMgtWSDLException(
                        "Unable to process WSDL by the processor " + processor.getClass().getName(),
                        ExceptionCodes.CANNOT_PROCESS_WSDL_CONTENT);
            }
            if (log.isDebugEnabled()) {
                log.debug("Successfully validated the content of WSDL. API uuid: " + apiId);
            }
            getApiDAO().addOrUpdateWSDL(apiId, wsdlContent, getUsername());
            if (log.isDebugEnabled()) {
                log.debug("Successfully added WSDL to the DB. API uuid: " + apiId);
            }
            return new String(wsdlContent, APIMgtConstants.ENCODING_UTF_8);
        } catch (IOException e) {
            throw new APIMgtWSDLException("Error while updating WSDL of API " + apiId, e,
                    ExceptionCodes.INTERNAL_WSDL_EXCEPTION);
        }
    }

    @Override
    public void updateAPIWSDLArchive(String apiId, InputStream inputStream)
            throws APIMgtDAOException, APIMgtWSDLException {
        WSDLArchiveInfo archiveInfo = null;
        InputStream fileInputStream = null;
        try {
            archiveInfo = extractAndValidateWSDLArchive(inputStream);
            if (log.isDebugEnabled()) {
                log.debug("Successfully extracted and validated WSDL file. Location: "
                        + archiveInfo.getAbsoluteFilePath());
            }
            fileInputStream = new FileInputStream(archiveInfo.getAbsoluteFilePath());
            getApiDAO().addOrUpdateWSDLArchive(apiId, fileInputStream, getUsername());
            if (log.isDebugEnabled()) {
                log.debug("Successfully updated the WSDL archive in DB. API uuid: " + apiId);
            }
        } catch (IOException e) {
            throw new APIMgtWSDLException("Unable to process WSDL archive at " + archiveInfo.getAbsoluteFilePath(),
                    e, ExceptionCodes.INTERNAL_WSDL_EXCEPTION);
        } finally {
            try {
                if (fileInputStream != null) {
                    fileInputStream.close();
                }
                if (archiveInfo != null) {
                    APIFileUtils.deleteDirectory(archiveInfo.getLocation());
                }
            } catch (APIMgtDAOException | IOException e) {
                //This is not a blocker. Give a warning and continue
                log.warn("Error occured while deleting processed WSDL artifacts folder : "
                        + archiveInfo.getLocation());
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void updateApiGatewayConfig(String apiId, String configString) throws APIManagementException {
        API api = getAPIbyUUID(apiId);
        GatewaySourceGenerator gatewaySourceGenerator = getGatewaySourceGenerator();
        APIConfigContext apiConfigContext = new APIConfigContext(api, config.getGatewayPackageName());
        gatewaySourceGenerator.setApiConfigContext(apiConfigContext);
        try {
            String swagger = gatewaySourceGenerator.getSwaggerFromGatewayConfig(configString);
            getApiDAO().updateApiDefinition(apiId, swagger, getUsername());
            getApiDAO().updateGatewayConfig(apiId, configString, getUsername());
        } catch (APIMgtDAOException e) {
            log.error("Couldn't update configuration for apiId " + apiId, e);
            throw new APIManagementException("Couldn't update configuration for apiId " + apiId,
                    e.getErrorHandler());
        } catch (APITemplateException e) {
            log.error("Error generating swagger from gateway config " + apiId, e);
            throw new APIManagementException("Error generating swagger from gateway config " + apiId,
                    ExceptionCodes.TEMPLATE_EXCEPTION);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String getApiGatewayConfig(String apiId) throws APIManagementException {
        try {
            return getApiDAO().getGatewayConfigOfAPI(apiId);

        } catch (APIMgtDAOException e) {
            log.error("Couldn't retrieve swagger definition for apiId " + apiId, e);
            throw new APIManagementException("Couldn't retrieve gateway configuration for apiId " + apiId,
                    e.getErrorHandler());
        }
    }

    /**
     * Retrieve all policies based on tier Level
     *
     * @param tierLevel Tier Level.
     * @return List of policies of the given level.
     * @throws APIManagementException If failed to get policies.
     */

    @Override
    public List<Policy> getAllPoliciesByLevel(APIMgtAdminService.PolicyLevel tierLevel)
            throws APIManagementException {
        try {
            return getPolicyDAO().getPoliciesByLevel(tierLevel);
        } catch (APIMgtDAOException e) {
            String errorMsg = "Error while retrieving Policies for level: " + tierLevel;
            log.error(errorMsg);
            throw new APIManagementException(errorMsg, e, e.getErrorHandler());
        }
    }

    @Override
    public Policy getPolicyByName(APIMgtAdminService.PolicyLevel tierLevel, String tierName)
            throws APIManagementException {
        try {
            return getPolicyDAO().getSimplifiedPolicyByLevelAndName(tierLevel, tierName);
        } catch (APIMgtDAOException e) {
            String errorMsg = "Error while retrieving Policy for level: " + tierLevel + ", name: " + tierName;
            log.error(errorMsg);
            throw new APIManagementException(errorMsg, e, e.getErrorHandler());
        }
    }

    @Override
    public List<LifecycleHistoryBean> getLifeCycleHistoryFromUUID(String uuid) throws APIManagementException {

        LifecycleEventManager lifecycleEventManager = new LifecycleEventManager();
        try {
            return lifecycleEventManager.getLifecycleHistoryFromId(uuid);
        } catch (LifecycleException e) {
            String errorMsg = "Error while retrieving the lifecycle history of the API ";
            log.error(errorMsg);
            throw new APIManagementException(errorMsg, e, ExceptionCodes.APIMGT_LIFECYCLE_EXCEPTION);
        }
    }

    @Override
    public List<Label> getAllLabels() throws LabelException {

        try {
            return getLabelDAO().getLabels();
        } catch (APIMgtDAOException e) {
            String msg = "Error occurred while retrieving labels";
            log.error(msg, e);
            throw new LabelException(msg, ExceptionCodes.LABEL_EXCEPTION);
        }
    }

    /**
     * @see APIPublisher#getLastUpdatedTimeOfEndpoint(String)
     */
    @Override
    public String getLastUpdatedTimeOfEndpoint(String endpointId) throws APIManagementException {
        String lastUpdatedTime;
        try {
            lastUpdatedTime = getApiDAO().getLastUpdatedTimeOfEndpoint(endpointId);
        } catch (APIMgtDAOException e) {
            String errorMsg = "Error occurred while retrieving the last update time of the endpoint with id "
                    + endpointId;
            log.error(errorMsg, e);
            throw new APIManagementException(errorMsg, e, e.getErrorHandler());
        }

        return lastUpdatedTime;
    }

    /**
     * Add {@code org.wso2.carbon.apimgt.core.api.EventObserver} which needs to be registered to a Map.
     * Key should be class name of the observer. This is to prevent registering same observer twice to an
     * observable.
     * <p>
     * {@inheritDoc}
     */
    @Override
    public void registerObserver(EventObserver observer) {
        if (observer != null && !eventObservers.containsKey(observer.getClass().getName())) {
            eventObservers.put(observer.getClass().getName(), observer);
        }
    }

    /**
     * Notify each registered {@link org.wso2.carbon.apimgt.core.api.EventObserver}.
     * This calls
     * {@link org.wso2.carbon.apimgt.core.api.EventObserver#captureEvent(Event, String, ZonedDateTime, Map)}
     * method of that {@link org.wso2.carbon.apimgt.core.api.EventObserver}.
     * <p>
     * {@inheritDoc}
     */
    @Override
    public void notifyObservers(Event event, String username, ZonedDateTime eventTime,
            Map<String, String> metaData) {

        Set<Map.Entry<String, EventObserver>> eventObserverEntrySet = eventObservers.entrySet();
        eventObserverEntrySet.forEach(eventObserverEntry -> eventObserverEntry.getValue().captureEvent(event,
                username, eventTime, metaData));
    }

    /**
     * Remove {@link org.wso2.carbon.apimgt.core.api.EventObserver} from the Map, which stores observers to be
     * notified.
     * <p>
     * {@inheritDoc}
     */
    @Override
    public void removeObserver(EventObserver observer) {
        if (observer != null) {
            eventObservers.remove(observer.getClass().getName());
        }
    }

    /**
     * To get the Map of all observers, which registered to {@link org.wso2.carbon.apimgt.core.api.APIPublisher}.
     *
     * @return Map of observers.
     */
    public Map<String, EventObserver> getEventObservers() {
        return eventObservers;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void removePendingLifecycleWorkflowTaskForAPI(String apiId) throws APIManagementException {

        API api = getApiDAO().getAPI(apiId);
        if (api != null) {
            if (APILCWorkflowStatus.PENDING.toString().equals(api.getWorkflowStatus())) {
                try {
                    //change the state back
                    getApiDAO().updateAPIWorkflowStatus(apiId, APILCWorkflowStatus.APPROVED);

                    // call executor's cleanup task
                    cleanupPendingTaskForAPIStateChange(apiId);

                } catch (APIMgtDAOException e) {
                    String msg = "Error occurred while changing api lifecycle workflow status";
                    log.error(msg, e);
                    throw new APIManagementException(msg, e.getErrorHandler());
                }
            } else {
                String msg = "API does not have a pending lifecycle state change.";
                log.error(msg);
                throw new APIManagementException(msg, ExceptionCodes.WORKFLOW_NO_PENDING_TASK);
            }
        } else {
            String msg = "Couldn't found API with ID " + apiId;
            log.error(msg);
            throw new APIManagementException(msg, ExceptionCodes.API_NOT_FOUND);
        }
    }

    @Override
    public boolean isEndpointExist(String name) throws APIManagementException {
        try {
            return getApiDAO().isEndpointExist(name);
        } catch (APIMgtDAOException e) {
            String msg = "Couldn't find existence of endpoint :" + name;
            throw new APIManagementException(msg, e.getErrorHandler());
        }
    }

    @Override
    public WSDLArchiveInfo extractAndValidateWSDLArchive(InputStream inputStream)
            throws APIMgtDAOException, APIMgtWSDLException {

        String path = System.getProperty(APIMgtConstants.JAVA_IO_TMPDIR) + File.separator
                + APIMgtConstants.WSDLConstants.WSDL_ARCHIVES_FOLDERNAME + File.separator
                + UUID.randomUUID().toString();
        String archivePath = path + File.separator + APIMgtConstants.WSDLConstants.WSDL_ARCHIVE_FILENAME;
        String extractedLocation = APIFileUtils.extractUploadedArchive(inputStream,
                APIMgtConstants.WSDLConstants.EXTRACTED_WSDL_ARCHIVE_FOLDERNAME, archivePath, path);
        if (log.isDebugEnabled()) {
            log.debug("Successfully extracted WSDL archive. Location: " + extractedLocation);
        }

        WSDLProcessor processor = WSDLProcessFactory.getInstance().getWSDLProcessorForPath(extractedLocation);
        if (!processor.canProcess()) {
            throw new APIMgtWSDLException(
                    "Unable to process WSDL by the processor " + processor.getClass().getName(),
                    ExceptionCodes.CANNOT_PROCESS_WSDL_CONTENT);
        }
        WSDLArchiveInfo archiveInfo = new WSDLArchiveInfo(path,
                APIMgtConstants.WSDLConstants.WSDL_ARCHIVE_FILENAME);
        archiveInfo.setWsdlInfo(processor.getWsdlInfo());
        return archiveInfo;
    }

    private void cleanupPendingTaskForAPIStateChange(String apiId) throws APIManagementException {
        String workflowExtRef = getWorkflowDAO().getExternalWorkflowReferenceForPendingTask(apiId,
                WorkflowConstants.WF_TYPE_AM_API_STATE);
        if (workflowExtRef != null) {
            WorkflowExecutor executor = WorkflowExecutorFactory.getInstance()
                    .getWorkflowExecutor(WorkflowConstants.WF_TYPE_AM_API_STATE);
            try {
                executor.cleanUpPendingTask(workflowExtRef);
            } catch (WorkflowException e) {
                String warn = "Failed to clean pending api state change task for " + apiId;
                // failed cleanup processes are ignored to prevent failing the deletion process
                log.warn(warn, e.getLocalizedMessage());
            }
            getWorkflowDAO().deleteWorkflowEntryforExternalReference(workflowExtRef);
        }
    }

}