org.wso2.carbon.appmgt.rest.api.publisher.impl.AppsApiServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.wso2.carbon.appmgt.rest.api.publisher.impl.AppsApiServiceImpl.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.appmgt.rest.api.publisher.impl;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.cxf.jaxrs.ext.multipart.Attachment;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.XML;
import org.wso2.carbon.appmgt.api.APIProvider;
import org.wso2.carbon.appmgt.api.AppManagementException;
import org.wso2.carbon.appmgt.api.dto.AppPageUsageDTO;
import org.wso2.carbon.appmgt.api.dto.AppResponseTimeDTO;
import org.wso2.carbon.appmgt.api.dto.AppUsageByUserDTO;
import org.wso2.carbon.appmgt.api.exception.AppUsageQueryServiceClientException;
import org.wso2.carbon.appmgt.api.model.APIIdentifier;
import org.wso2.carbon.appmgt.api.model.App;
import org.wso2.carbon.appmgt.api.model.BusinessOwner;
import org.wso2.carbon.appmgt.api.model.Documentation;
import org.wso2.carbon.appmgt.api.model.EntitlementPolicyGroup;
import org.wso2.carbon.appmgt.api.model.FileContent;
import org.wso2.carbon.appmgt.api.model.MobileApp;
import org.wso2.carbon.appmgt.api.model.Subscriber;
import org.wso2.carbon.appmgt.api.model.SubscriptionCount;
import org.wso2.carbon.appmgt.api.model.Subscriptions;
import org.wso2.carbon.appmgt.api.model.Tag;
import org.wso2.carbon.appmgt.api.model.Tier;
import org.wso2.carbon.appmgt.api.model.WebApp;
import org.wso2.carbon.appmgt.impl.AppMConstants;
import org.wso2.carbon.appmgt.impl.AppManagerConfiguration;
import org.wso2.carbon.appmgt.impl.AppRepository;
import org.wso2.carbon.appmgt.impl.DefaultAppRepository;
import org.wso2.carbon.appmgt.impl.service.AppUsageStatisticsService;
import org.wso2.carbon.appmgt.impl.service.ServiceReferenceHolder;
import org.wso2.carbon.appmgt.impl.utils.AppManagerUtil;
import org.wso2.carbon.appmgt.rest.api.publisher.AppsApiService;
import org.wso2.carbon.appmgt.rest.api.publisher.dto.AppDTO;
import org.wso2.carbon.appmgt.rest.api.publisher.dto.AppListDTO;
import org.wso2.carbon.appmgt.rest.api.publisher.dto.BinaryDTO;
import org.wso2.carbon.appmgt.rest.api.publisher.dto.DocumentDTO;
import org.wso2.carbon.appmgt.rest.api.publisher.dto.DocumentListDTO;
import org.wso2.carbon.appmgt.rest.api.publisher.dto.LifeCycleDTO;
import org.wso2.carbon.appmgt.rest.api.publisher.dto.LifeCycleHistoryDTO;
import org.wso2.carbon.appmgt.rest.api.publisher.dto.LifeCycleHistoryListDTO;
import org.wso2.carbon.appmgt.rest.api.publisher.dto.ResponseMessageDTO;
import org.wso2.carbon.appmgt.rest.api.publisher.dto.StatSummaryDTO;
import org.wso2.carbon.appmgt.rest.api.publisher.dto.TagListDTO;
import org.wso2.carbon.appmgt.rest.api.publisher.dto.TierDTO;
import org.wso2.carbon.appmgt.rest.api.publisher.dto.TierListDTO;
import org.wso2.carbon.appmgt.rest.api.publisher.dto.UserIdListDTO;
import org.wso2.carbon.appmgt.rest.api.publisher.utils.RestApiPublisherUtils;
import org.wso2.carbon.appmgt.rest.api.publisher.utils.mappings.APPMappingUtil;
import org.wso2.carbon.appmgt.rest.api.publisher.utils.mappings.DocumentationMappingUtil;
import org.wso2.carbon.appmgt.rest.api.publisher.utils.validation.AppDTOValidator;
import org.wso2.carbon.appmgt.rest.api.util.RestApiConstants;
import org.wso2.carbon.appmgt.rest.api.util.utils.RestApiUtil;
import org.wso2.carbon.appmgt.rest.api.util.validation.BeanValidator;
import org.wso2.carbon.appmgt.rest.api.util.validation.CommonValidator;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.governance.api.exception.GovernanceException;
import org.wso2.carbon.governance.api.generic.GenericArtifactManager;
import org.wso2.carbon.governance.api.generic.dataobjects.GenericArtifact;
import org.wso2.carbon.registry.core.Registry;
import org.wso2.carbon.registry.core.exceptions.RegistryException;
import org.wso2.carbon.user.api.UserRealm;
import org.wso2.carbon.user.api.UserStoreException;
import org.wso2.carbon.user.api.UserStoreManager;
import org.wso2.carbon.user.core.service.RealmService;
import org.wso2.carbon.utils.multitenancy.MultitenantUtils;
import org.wso2.mobile.utils.utilities.ZipFileReading;

import javax.activation.MimetypesFileTypeMap;
import javax.ws.rs.core.Response;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

/**
 * This is the service implementation class for Publisher API related operations
 */
public class AppsApiServiceImpl extends AppsApiService {

    private static final Log log = LogFactory.getLog(AppsApiService.class);
    BeanValidator beanValidator;

    /**
     * Upload binary files into storage
     *
     * @param fileInputStream   Uploading fileInputStream
     * @param fileDetail        Attachment details
     * @param ifMatch
     * @param ifUnmodifiedSince
     * @return API path of the uploaded binary
     */
    @Override
    public Response appsMobileBinariesPost(InputStream fileInputStream, Attachment fileDetail, String ifMatch,
            String ifUnmodifiedSince) {

        BinaryDTO binaryDTO = new BinaryDTO();
        try {
            if (fileInputStream != null) {
                if ("application".equals(fileDetail.getContentType().getType())) {
                    String fileName = fileDetail.getContentDisposition().getParameter("filename");

                    if (!RestApiUtil.isValidFileName(fileName)) {
                        RestApiUtil.handleBadRequest("Invalid file '" + fileName + "' has been provided to upload",
                                log);
                    }

                    String fileExtension = FilenameUtils.getExtension(fileName);
                    if (AppMConstants.MOBILE_APPS_ANDROID_EXT.equals(fileExtension)
                            || AppMConstants.MOBILE_APPS_IOS_EXT.equals(fileExtension)) {

                        //Generate UUID for the uploading file
                        String filename = RestApiPublisherUtils.generateBinaryUUID() + "." + fileExtension;

                        FileContent fileContent = new FileContent();
                        fileContent.setContent(fileInputStream);
                        fileContent.setFileName(filename);
                        String filePath = RestApiPublisherUtils.uploadFileIntoStorage(fileContent);
                        ZipFileReading zipFileReading = new ZipFileReading();

                        String information = null;
                        if (AppMConstants.MOBILE_APPS_ANDROID_EXT.equals(fileExtension)) {
                            information = zipFileReading.readAndroidManifestFile(filePath);
                        } else if (AppMConstants.MOBILE_APPS_IOS_EXT.equals(fileExtension)) {
                            information = zipFileReading.readiOSManifestFile(filePath, filename);
                        }
                        JSONObject binaryObj = new JSONObject(information);
                        binaryDTO.setPackage(binaryObj.getString("package"));
                        binaryDTO.setVersion(binaryObj.getString("version"));

                        binaryDTO.setPath(filename);
                    } else {
                        RestApiUtil.handleBadRequest("Invalid Filetype is provided", log);
                    }
                } else {
                    RestApiUtil.handleBadRequest("Invalid file is provided with unsupported Media type.", log);
                }

            } else {
                RestApiUtil.handleBadRequest("'file' should be specified", log);
            }
        } catch (AppManagementException e) {
            RestApiUtil.handleInternalServerError(
                    "Error occurred while parsing binary file archive and retrieving information", e, log);
        } catch (JSONException e) {
            RestApiUtil.handleInternalServerError(
                    "Error occurred while parsing metadata of binary and retrieving information", e, log);
        }
        return Response.ok().entity(binaryDTO).build();
    }

    /**
     * Retrieve mobile binary from storage
     *
     * @param fileName          binary file name
     * @param ifMatch
     * @param ifUnmodifiedSince
     * @return
     */
    @Override
    public Response appsMobileBinariesFileNameGet(String fileName, String ifMatch, String ifUnmodifiedSince) {
        File binaryFile = null;
        String contentType = null;
        try {
            if (!RestApiUtil.isValidFileName(fileName)) {
                RestApiUtil.handleBadRequest("Invalid filename '" + fileName + "' is provided", log);
            }

            String fileExtension = FilenameUtils.getExtension(fileName);
            if (AppMConstants.MOBILE_APPS_ANDROID_EXT.equals(fileExtension)
                    || AppMConstants.MOBILE_APPS_IOS_EXT.equals(fileExtension)) {

                binaryFile = RestApiUtil.readFileFromStorage(fileName);
                contentType = new MimetypesFileTypeMap().getContentType(binaryFile);
                if (!contentType.startsWith("application")) {
                    RestApiUtil.handleBadRequest(
                            "Invalid file '" + fileName + "' with unsupported file type requested", log);
                }
            } else {
                RestApiUtil.handleBadRequest(
                        "Invalid file '" + fileName + "' with unsupported media type is requested", log);
            }
        } catch (AppManagementException e) {
            if (RestApiUtil.isDueToResourceNotFound(e) || RestApiUtil.isDueToAuthorizationFailure(e)) {
                RestApiUtil.handleResourceNotFoundError("Static Content", fileName, e, log);
            } else {
                RestApiUtil.handleInternalServerError(
                        "Error occurred while retrieving mobile binary : " + fileName + "from storage", e, log);
            }
        }
        Response.ResponseBuilder response = Response.ok((Object) binaryFile);
        response.header(RestApiConstants.HEADER_CONTENT_DISPOSITION, RestApiConstants.CONTENT_DISPOSITION_ATTACHMENT
                + "; " + RestApiConstants.CONTENT_DISPOSITION_FILENAME + "=\"" + fileName + "\"");
        response.header(RestApiConstants.HEADER_CONTENT_TYPE, contentType);
        return response.build();
    }

    @Override
    public Response appsMobileGetplistTenantTenantIdFileFileNameGet(String tenantId, String fileName, String accept,
            String ifNoneMatch) {
        return null;
    }

    /**
     * Upload static contents like images into storage
     *
     * @param fileInputStream   Upload static content's fileInputStream
     * @param fileDetail        uploading file details
     * @param ifMatch
     * @param ifUnmodifiedSince
     * @return API path of the uploaded static content
     */
    @Override
    public Response appsStaticContentsPost(String appType, InputStream fileInputStream, Attachment fileDetail,
            String ifMatch, String ifUnmodifiedSince) {

        CommonValidator.isValidAppType(appType);
        Map<String, String> response = new HashMap<>();

        try {
            if (fileInputStream != null) {
                FileContent fileContent = new FileContent();
                if ("image".equals(fileDetail.getContentType().getType())
                        || "application".equals(fileDetail.getContentType().getType())) {
                    String fileName = fileDetail.getContentDisposition()
                            .getParameter(RestApiConstants.CONTENT_DISPOSITION_FILENAME);
                    if (!RestApiUtil.isValidFileName(fileName)) {
                        RestApiUtil.handleBadRequest("Invalid file '" + fileName + "' has been provided to upload",
                                log);
                    }

                    String fileExtension = FilenameUtils.getExtension(fileName);
                    String filename = RestApiPublisherUtils.generateBinaryUUID() + "." + fileExtension;
                    fileContent.setFileName(filename);
                    fileContent.setContent(fileInputStream);
                    fileContent.setContentType(fileDetail.getContentType().toString());
                    if (AppMConstants.MOBILE_ASSET_TYPE.equals(appType)) {
                        RestApiPublisherUtils.uploadFileIntoStorage(fileContent);
                        response.put("id", filename);
                    } else if (AppMConstants.WEBAPP_ASSET_TYPE.equals(appType)) {
                        try {
                            DefaultAppRepository defaultAppRepository = new DefaultAppRepository(null);
                            UUID contentUUID = UUID.randomUUID();
                            fileContent.setUuid(contentUUID.toString());
                            fileContent.setContentLength(fileInputStream.available());
                            defaultAppRepository.persistStaticContents(fileContent);
                            response.put("id", contentUUID.toString() + File.separator + filename);
                        } catch (IOException e) {
                            RestApiUtil.handleInternalServerError("Error occurred while uploading static content",
                                    e, log);
                        }
                    }
                } else {
                    RestApiUtil.handleBadRequest("Invalid file is provided with unsupported Media type.", log);
                }
            } else {
                RestApiUtil.handleBadRequest("'file' should be specified", log);
            }
        } catch (AppManagementException e) {
            RestApiUtil.handleInternalServerError(
                    "Error occurred while parsing binary file archive and retrieving information", e, log);
        }
        return Response.ok().entity(response).build();
    }

    /**
     * Retrieve a given static content from storage
     *
     * @param fileName          request file name
     * @param ifMatch
     * @param ifUnmodifiedSince
     * @return
     */
    @Override
    public Response appsStaticContentsFileNameGet(String appType, String fileName, String ifMatch,
            String ifUnmodifiedSince) {
        CommonValidator.isValidAppType(appType);
        File staticContentFile = null;
        String contentType = null;

        try {

            if (AppMConstants.MOBILE_ASSET_TYPE.equals(appType)) {

                staticContentFile = RestApiUtil.readFileFromStorage(fileName);
                if (staticContentFile == null) {
                    RestApiUtil.handleResourceNotFoundError("Static Content", fileName, log);
                }
                contentType = RestApiUtil.readFileContentType(staticContentFile.getAbsolutePath());
            } else if (AppMConstants.WEBAPP_ASSET_TYPE.equals(appType)) {
                OutputStream outputStream = null;
                AppRepository appRepository = new DefaultAppRepository(null);
                try {
                    FileContent fileContent = appRepository.getStaticContent(fileName);
                    if (fileContent == null) {
                        RestApiUtil.handleResourceNotFoundError("Static Content", fileName, log);
                    }
                    staticContentFile = File.createTempFile("temp", ".tmp");
                    outputStream = new FileOutputStream(staticContentFile);
                    IOUtils.copy(fileContent.getContent(), outputStream);
                    contentType = fileContent.getContentType();
                } catch (IOException e) {
                    RestApiUtil.handleBadRequest(
                            "Error occurred while retrieving static content '" + fileName + "'", log);
                }
            }

            if (contentType != null && !contentType.startsWith("image")) {
                RestApiUtil.handleBadRequest("Invalid file '" + fileName + "'with unsupported file type requested",
                        log);
            }

            Response.ResponseBuilder response = Response.ok((Object) staticContentFile);
            response.header(RestApiConstants.HEADER_CONTENT_DISPOSITION,
                    RestApiConstants.CONTENT_DISPOSITION_ATTACHMENT + "; "
                            + RestApiConstants.CONTENT_DISPOSITION_FILENAME + "=\"" + fileName + "\"");
            response.header(RestApiConstants.HEADER_CONTENT_TYPE, contentType);
            return response.build();
        } catch (AppManagementException e) {
            if (RestApiUtil.isDueToResourceNotFound(e) || RestApiUtil.isDueToAuthorizationFailure(e)) {
                RestApiUtil.handleResourceNotFoundError("Static Content", fileName, e, log);
            } else {
                RestApiUtil.handleInternalServerError(
                        "Error occurred while retrieving static content : " + fileName + "from storage", e, log);
            }
        }
        return null;
    }

    @Override
    public Response appsAppTypeGet(String appType, String query, String fieldFilter, Integer limit, Integer offset,
            String accept, String ifNoneMatch) {
        List<WebApp> allMatchedApps;

        //setting default limit and offset values if they are not set
        limit = limit != null ? limit : RestApiConstants.PAGINATION_LIMIT_DEFAULT;
        offset = offset != null ? offset : RestApiConstants.PAGINATION_OFFSET_DEFAULT;
        query = query == null ? "" : query;

        try {
            //check if a valid asset type is provided
            if (!(AppMConstants.WEBAPP_ASSET_TYPE.equalsIgnoreCase(appType)
                    || AppMConstants.MOBILE_ASSET_TYPE.equalsIgnoreCase(appType))) {
                String errorMessage = "Invalid Asset Type : " + appType;
                RestApiUtil.handleBadRequest(errorMessage, log);
            }

            APIProvider apiProvider = RestApiUtil.getLoggedInUserProvider();

            List<App> result = apiProvider.searchApps(appType, RestApiUtil.getSearchTerms(query));

            AppListDTO appListDTO = null;
            if (fieldFilter == null || "BASIC".equalsIgnoreCase(fieldFilter)) {
                appListDTO = APPMappingUtil.getAppListDTOWithBasicFields(result, offset, limit);

            } else {
                appListDTO = APPMappingUtil.getAppListDTOWithAllFields(result, offset, limit);
            }

            APPMappingUtil.setPaginationParams(appListDTO, query, offset, limit, result.size());
            return Response.ok().entity(appListDTO).build();
        } catch (AppManagementException e) {
            String errorMessage = "Error while retrieving Apps";
            RestApiUtil.handleInternalServerError(errorMessage, e, log);
        }
        return null;
    }

    /**
     * Create an application
     *
     * @param appType         application type ie: webapp, mobileapp
     * @param body            Application DTO
     * @param contentType
     * @param ifModifiedSince
     * @return created application id
     */
    @Override
    public Response appsAppTypePost(String appType, AppDTO body, String contentType, String ifModifiedSince) {
        CommonValidator.isValidAppType(appType);
        beanValidator = new BeanValidator();
        //Validate common mandatory fields for mobile and webapp
        beanValidator.validate(body);
        Map<String, String> response = new HashMap<>();
        AppDTOValidator.validateAppDTO(appType, body);
        String applicationId = null;
        try {
            APIProvider appProvider = RestApiUtil.getLoggedInUserProvider();
            if (AppMConstants.MOBILE_ASSET_TYPE.equals(appType)) {

                MobileApp mobileApp = APPMappingUtil.fromDTOtoMobileApp(body);
                applicationId = appProvider.createMobileApp(mobileApp);
            } else if (AppMConstants.WEBAPP_ASSET_TYPE.equals(appType)) {

                WebApp webApp = APPMappingUtil.fromDTOToWebapp(body);
                validateWebApp(webApp, appProvider, true);
                webApp.setCreatedTime(RestApiPublisherUtils.getCreatedTimeEpoch());
                applicationId = appProvider.createWebApp(webApp);
            }
            response.put("AppId", applicationId);
        } catch (AppManagementException e) {
            if (RestApiUtil.isDueToResourceAlreadyExisting(e) || RestApiUtil.isDueToAuthorizationFailure(e)) {
                RestApiUtil.handleConflictException(
                        "A duplicate " + appType + " already exists with the name : " + body.getName(), log);
            } else {
                RestApiUtil.handleInternalServerError(
                        "Error occurred while creating mobile application : " + body.getName(), e, log);
            }
        }

        return Response.ok().entity(response).build();
    }

    /**
     * @param webApp
     * @param appProvider
     * @param isNewApp    if the app is a new app or existing app
     * @return
     * @throws AppManagementException
     */
    private boolean validateWebApp(WebApp webApp, APIProvider appProvider, boolean isNewApp)
            throws AppManagementException {
        //check if the context is unique
        if (isNewApp) {
            boolean isContextExists = appProvider.isContextExist(webApp.getContext());
            if (isContextExists) {
                throw new AppManagementException("Context - " + webApp.getContext() + " already exists");
            }
        }

        //check if the business owner exists
        if (webApp.getBusinessOwner() != null) {
            int businessOwnerId = Integer.parseInt(webApp.getBusinessOwner());
            BusinessOwner businessOwner = appProvider.getBusinessOwner(businessOwnerId);
            if (businessOwner == null) {
                throw new AppManagementException("Invalid Business Owner - " + businessOwnerId);
            }
        }

        //check if the role/tiers are exists
        //iterate through all groups
        List<EntitlementPolicyGroup> groups = webApp.getAccessPolicyGroups();
        String tenantDomainName = RestApiUtil.getLoggedInUserTenantDomain();
        for (EntitlementPolicyGroup group : groups) {
            //iterate through all roles
            List<String> roles = group.getUserRolesAsList();
            for (String role : roles) {
                try {
                    if (!"".equals(role)) {
                        PrivilegedCarbonContext carbonContext = PrivilegedCarbonContext
                                .getThreadLocalCarbonContext();
                        RealmService realmService = (RealmService) carbonContext.getOSGiService(RealmService.class,
                                null);
                        int tenantId = ServiceReferenceHolder.getInstance().getRealmService().getTenantManager()
                                .getTenantId(tenantDomainName);
                        UserRealm realm = realmService.getTenantUserRealm(tenantId);
                        UserStoreManager manager = realm.getUserStoreManager();
                        //check if the role is exists
                        if (!manager.isExistingRole(role)) {
                            throw new AppManagementException("Invalid role - " + role);
                        }
                    }

                } catch (UserStoreException e) {
                    throw new AppManagementException("Error while fetching User Store");
                }
            }

            String throttlingTier = group.getThrottlingTier();
            Set<Tier> tiers = appProvider.getTiers(tenantDomainName);
            boolean tierExists = false;
            for (Tier tier : tiers) {
                if (tier.getName().equals(throttlingTier)) {
                    tierExists = true;
                }
            }
            if (!tierExists) {
                throw new AppManagementException("Invalid Throttling Tier - " + throttlingTier);
            }
        }

        return true;
    }

    /**
     * Change lifecycle state of an application
     *
     * @param appType           application type ie: webapp, mobileapp
     * @param action            lifecycle action
     * @param appId             application uuid
     * @param ifMatch
     * @param ifUnmodifiedSince
     * @return status message
     */
    @Override
    public Response appsAppTypeChangeLifecyclePost(String appType, String action, String appId, String ifMatch,
            String ifUnmodifiedSince) {
        CommonValidator.isValidAppType(appType);
        ResponseMessageDTO responseMessageDTO = new ResponseMessageDTO();
        try {

            APIProvider appProvider = RestApiUtil.getLoggedInUserProvider();
            String[] allowedLifecycleActions = appProvider.getAllowedLifecycleActions(appId, appType);
            if (!ArrayUtils.contains(allowedLifecycleActions, action)) {
                RestApiUtil.handleBadRequest(
                        "Action '" + action + "' is not allowed to perform on " + appType + " with id: " + appId
                                + ". Allowed actions are " + Arrays.toString(allowedLifecycleActions),
                        log);
            }
            appProvider.changeLifeCycleStatus(appType, appId, action);

            responseMessageDTO.setMessage("Lifecycle action : " + action + " has been accepted for processing ");
        } catch (AppManagementException e) {
            //Auth failure occurs when cross tenant accessing APIs. Sends 404, since we don't need to expose the
            // existence of the resource
            if (RestApiUtil.isDueToResourceNotFound(e)) {
                RestApiUtil.handleResourceNotFoundError(appType, appId, e, log);
            } else if (RestApiUtil.isDueToAuthorizationFailure(e)) {
                RestApiUtil.handleAuthorizationFailedError("The user is not permitted to perform lifecycle action '"
                        + action + "' on " + appType + " with uuid " + appId, e, log);
            } else {
                String errorMessage = "Error while changing lifecycle state of app with id : " + appId;
                RestApiUtil.handleInternalServerError(errorMessage, e, log);
            }
        }
        return Response.accepted().entity(responseMessageDTO).build();
    }

    @Override
    public Response appsAppTypeIdAppIdGet(String appType, String appId, String accept, String ifNoneMatch,
            String ifModifiedSince) {
        AppDTO appDTO;
        try {

            Map<String, String> searchTerms = new HashMap<String, String>();
            searchTerms.put("id", appId);

            APIProvider apiProvider = RestApiUtil.getLoggedInUserProvider();
            List<App> result = apiProvider.searchApps(appType, searchTerms);

            if (result.isEmpty()) {
                RestApiUtil.handleResourceNotFoundError(appType, appId, log);
            }

            App app = result.get(0);
            appDTO = APPMappingUtil.fromAppToDTO(app);

            return Response.ok().entity(appDTO).build();
        } catch (AppManagementException e) {
            //Auth failure occurs when cross tenant accessing APIs. Sends 404, since we don't need to expose the
            // existence of the resource
            if (RestApiUtil.isDueToResourceNotFound(e) || RestApiUtil.isDueToAuthorizationFailure(e)) {
                RestApiUtil.handleResourceNotFoundError(appType, appId, e, log);
            } else {
                String errorMessage = "Error while retrieving App : " + appId;
                RestApiUtil.handleInternalServerError(errorMessage, e, log);
            }
        }
        return null;
    }

    /**
     * Update an application
     *
     * @param appType           appType application type ie: webapp, mobileapp
     * @param appId             application id
     * @param body              Application DTO
     * @param contentType
     * @param ifMatch
     * @param ifUnmodifiedSince
     * @return
     */
    @Override
    public Response appsAppTypeIdAppIdPut(String appType, String appId, AppDTO body, String contentType,
            String ifMatch, String ifUnmodifiedSince) {

        if (AppMConstants.MOBILE_ASSET_TYPE.equals(appType)) {
            try {
                APIProvider appProvider = RestApiUtil.getLoggedInUserProvider();
                //TODO:APP Validations
                //TODO:Get provider name from context (Token owner)
                //TODO:Permission check
                MobileApp updatingMobileApp = APPMappingUtil.fromDTOtoMobileApp(body);
                updatingMobileApp.setAppId(appId);
                appProvider.updateMobileApp(updatingMobileApp);

            } catch (AppManagementException e) {
                RestApiUtil.handleInternalServerError("Error occurred while ", e, log);
            }
        } else if (AppMConstants.WEBAPP_ASSET_TYPE.equals(appType)) {

            try {
                APIProvider apiProvider = RestApiUtil.getLoggedInUserProvider();
                body.setId(appId);
                WebApp webApp = APPMappingUtil.fromDTOToWebapp(body);
                validateWebApp(webApp, apiProvider, false);
                apiProvider.updateApp(webApp);

            } catch (AppManagementException e) {
                e.printStackTrace();
            }

        } else {
            RestApiUtil.handleBadRequest("Invalid application type :" + appType, log);
        }
        return Response.ok().build();
    }

    @Override
    public Response appsAppTypeIdAppIdDelete(String appType, String appId, String ifMatch,
            String ifUnmodifiedSince) {
        try {
            CommonValidator.isValidAppType(appType);

            Map<String, String> searchTerms = new HashMap<String, String>();
            searchTerms.put("id", appId);

            String username = RestApiUtil.getLoggedInUsername();
            APIProvider apiProvider = RestApiUtil.getLoggedInUserProvider();

            List<App> result = apiProvider.searchApps(appType, searchTerms);
            if (result.isEmpty()) {
                RestApiUtil.handleResourceNotFoundError(appType, appId, log);
            }

            App app = result.get(0);

            if (appType.equals(AppMConstants.WEBAPP_ASSET_TYPE)) {

                WebApp webApp = (WebApp) app;

                if (webApp.isAdvertiseOnly()) {
                    removeRegistryArtifact(webApp, username);
                } else {
                    //todo: pass the correct auth cookie
                    String authorizedAdminCookie = null;
                    apiProvider.deleteApp(webApp.getId(), webApp.getSsoProviderDetails(), authorizedAdminCookie);
                }
            } else if (appType.equals(AppMConstants.MOBILE_ASSET_TYPE)) {
                removeRegistryArtifact(app, username);
            }
            return Response.ok().build();
        } catch (AppManagementException e) {
            //Auth failure occurs when cross tenant accessing APIs. Sends 404, since we don't need to expose the
            // existence of the resource
            if (RestApiUtil.isDueToResourceNotFound(e) || RestApiUtil.isDueToAuthorizationFailure(e)) {
                RestApiUtil.handleResourceNotFoundError(appType, appId, e, log);
            } else {
                String errorMessage = "Error while deleting App : " + appId;
                RestApiUtil.handleInternalServerError(errorMessage, e, log);
            }
        } catch (RegistryException e) {
            RestApiUtil.handleInternalServerError("Error while initializing registry", e, log);
        } catch (UserStoreException e) {
            RestApiUtil.handleInternalServerError("Error while initializing UserStore", e, log);
        }
        return null;
    }

    @Override
    public Response appsAppTypeIdAppIdCreateNewVersionPost(String appType, String appId, AppDTO body,
            String contentType, String ifModifiedSince) {

        APIProvider apiProvider = null;
        try {
            apiProvider = RestApiUtil.getLoggedInUserProvider();

            App app = null;
            if (AppMConstants.WEBAPP_ASSET_TYPE.equals(appType)) {
                app = APPMappingUtil.fromDTOToWebapp(body);
            } else if (AppMConstants.MOBILE_ASSET_TYPE.equals(appType)) {
                MobileApp mobileAppModel = new MobileApp();
                mobileAppModel.setVersion(body.getVersion());
                mobileAppModel.setDisplayName(body.getDisplayName());
                mobileAppModel.setType(AppMConstants.MOBILE_ASSET_TYPE);
                app = mobileAppModel;
            } else {
                RestApiUtil.handleBadRequest("Invalid application type :" + appType, log);
            }

            app.setUUID(appId);
            String newUUID = apiProvider.createNewVersion(app);

            Map<String, String> response = new HashMap<>();
            response.put("AppId", newUUID);

            return Response.ok(response).build();
        } catch (AppManagementException e) {
            RestApiUtil.handleInternalServerError(
                    String.format("Error while creating new version for the app '%s':'%s'", appType, appId), e,
                    log);
        }

        return null;
    }

    @Override
    public Response appsAppTypeIdAppIdDiscoverPost(String appType, String appId, String contentType,
            String ifModifiedSince) {
        return null;
    }

    /**
     *  Returns all the documents of the given APP uuid that matches to the search condition
     *
     * @param appType application type ie:webapp,mobileapp
     * @param appId application identifier
     * @param limit
     * @param offset
     * @param accept
     * @param ifNoneMatch
     * @return matched documents as a list if DocumentDTOs
     */
    @Override
    public Response appsAppTypeIdAppIdDocsGet(String appType, String appId, Integer limit, Integer offset,
            String accept, String ifNoneMatch) {
        //pre-processing
        //setting default limit and offset values if they are not set
        limit = limit != null ? limit : RestApiConstants.PAGINATION_LIMIT_DEFAULT;
        offset = offset != null ? offset : RestApiConstants.PAGINATION_OFFSET_DEFAULT;

        try {
            APIProvider appProvider = RestApiUtil.getLoggedInUserProvider();
            String tenantDomain = RestApiUtil.getLoggedInUserTenantDomain();
            //this will fail if user does not have access to the API or the API does not exist

            WebApp webApp = appProvider.getWebApp(appId);
            APIIdentifier appIdentifier = webApp.getId();

            List<Documentation> allDocumentation = appProvider.getAllDocumentation(appIdentifier);
            DocumentListDTO documentListDTO = DocumentationMappingUtil.fromDocumentationListToDTO(allDocumentation,
                    offset, limit);
            DocumentationMappingUtil.setPaginationParams(documentListDTO, appId, offset, limit,
                    allDocumentation.size());
            return Response.ok().entity(documentListDTO).build();
        } catch (AppManagementException e) {
            //Auth failure occurs when cross tenant accessing APIs. Sends 404, since we don't need to expose the existence of the resource
            if (RestApiUtil.isDueToResourceNotFound(e) || RestApiUtil.isDueToAuthorizationFailure(e)) {
                RestApiUtil.handleResourceNotFoundError(appType, appId, e, log);
            } else {
                String msg = "Error while retrieving documents of App " + appType + " with appId " + appId;
                RestApiUtil.handleInternalServerError(msg, e, log);
            }
        }
        return null;
    }

    /**
     * Add documentation to a given app
     * @param appId application uuid
     * @param appType application type
     * @param body
     * @param contentType
     * @return
     */
    @Override
    public Response appsAppTypeIdAppIdDocsPost(String appId, String appType, DocumentDTO body, String contentType) {

        CommonValidator.isValidAppType(appType);
        try {
            if (AppMConstants.WEBAPP_ASSET_TYPE.equals(appType)) {
                APIProvider appProvider = RestApiUtil.getLoggedInUserProvider();
                Documentation documentation = DocumentationMappingUtil.fromDTOtoDocumentation(body);
                String documentName = body.getName();
                String tenantDomain = RestApiUtil.getLoggedInUserTenantDomain();
                if (body.getType() == DocumentDTO.TypeEnum.OTHER && StringUtils.isBlank(body.getOtherTypeName())) {
                    //check otherTypeName for not null if doc type is OTHER
                    RestApiUtil.handleBadRequest("otherTypeName cannot be empty if type is OTHER.", log);
                }
                String sourceUrl = body.getSourceUrl();
                if (body.getSourceType() == DocumentDTO.SourceTypeEnum.URL
                        && (StringUtils.isBlank(sourceUrl) || !RestApiUtil.isURL(sourceUrl))) {
                    RestApiUtil.handleBadRequest("Invalid document sourceUrl Format", log);
                }
                //this will fail if user does not have access to the API or the API does not exist

                WebApp webApp = appProvider.getWebApp(appId);
                APIIdentifier appIdentifier = webApp.getId();
                if (appProvider.isDocumentationExist(appIdentifier, documentName)) {
                    String errorMessage = "Requested document '" + documentName + "' already exists";
                    RestApiUtil.handleConflictException(errorMessage, log);
                }
                appProvider.addDocumentation(appIdentifier, documentation);

                //retrieve the newly added document
                String newDocumentId = documentation.getId();
                documentation = appProvider.getDocumentation(newDocumentId, tenantDomain);
                DocumentDTO newDocumentDTO = DocumentationMappingUtil.fromDocumentationToDTO(documentation);
                String uriString = RestApiConstants.RESOURCE_PATH_DOCUMENTS_DOCUMENT_ID
                        .replace(RestApiConstants.APPID_PARAM, appId)
                        .replace(RestApiConstants.DOCUMENTID_PARAM, newDocumentId);
                URI uri = new URI(uriString);
                return Response.created(uri).entity(newDocumentDTO).build();
            } else {
                RestApiUtil.handleBadRequest("App type " + appType + " not supported", log);
            }
        } catch (AppManagementException e) {
            //Auth failure occurs when cross tenant accessing APIs. Sends 404, since we don't need to expose the existence of the resource
            if (RestApiUtil.isDueToResourceNotFound(e) || RestApiUtil.isDueToAuthorizationFailure(e)) {
                RestApiUtil.handleResourceNotFoundError(appType, appId, e, log);
            } else {
                String errorMessage = "Error while adding the document for " + appType + " with id : " + appId;
                RestApiUtil.handleInternalServerError(errorMessage, e, log);
            }
        } catch (URISyntaxException e) {
            String errorMessage = "Error while retrieving location for document " + body.getName() + " of App "
                    + appId;
            RestApiUtil.handleInternalServerError(errorMessage, e, log);
        }
        return null;
    }

    /**
     * Retrieve documentation for a given documentationId and for a given app id
     * @param appType application type ie: webapp,mobileapp
     * @param appId application uuid
     * @param documentId  documentID
     * @param ifMatch
     * @param ifUnmodifiedSince
     * @return
     */
    @Override
    public Response appsAppTypeIdAppIdDocsDocumentIdGet(String appType, String appId, String documentId,
            String ifMatch, String ifUnmodifiedSince) {
        Documentation documentation;
        DocumentDTO documentDTO = null;
        try {
            if (AppMConstants.WEBAPP_ASSET_TYPE.equals(appType)) {
                //TODO:Check App access prmissions
                APIProvider appProvider = RestApiUtil.getLoggedInUserProvider();
                String tenantDomain = RestApiUtil.getLoggedInUserTenantDomain();
                documentation = appProvider.getDocumentation(documentId, tenantDomain);
                if (documentation == null) {
                    RestApiUtil.handleResourceNotFoundError(RestApiConstants.RESOURCE_DOCUMENTATION, documentId,
                            log);
                }

                documentDTO = DocumentationMappingUtil.fromDocumentationToDTO(documentation);
            } else {
                RestApiUtil.handleBadRequest("App type " + appType + " not supported", log);
            }
        } catch (AppManagementException e) {
            //Auth failure occurs when cross tenant accessing APIs. Sends 404, since we don't need to expose the existence of the resource
            if (RestApiUtil.isDueToResourceNotFound(e) || RestApiUtil.isDueToAuthorizationFailure(e)) {
                RestApiUtil.handleResourceNotFoundError(appType, appId, e, log);
            } else {
                String errorMessage = "Error while retrieving document : " + documentId;
                RestApiUtil.handleInternalServerError(errorMessage, e, log);
            }
        }
        return Response.ok().entity(documentDTO).build();
    }

    /**
     * Delete a documentation for a given document id
     * @param appType appType
     * @param appId application id
     * @param documentId document Id
     * @param ifMatch
     * @param ifUnmodifiedSince
     * @return
     */
    @Override
    public Response appsAppTypeIdAppIdDocsDocumentIdDelete(String appType, String appId, String documentId,
            String ifMatch, String ifUnmodifiedSince) {
        Documentation documentation;
        try {
            if (AppMConstants.WEBAPP_ASSET_TYPE.equals(appType)) {
                APIProvider appProvider = RestApiUtil.getLoggedInUserProvider();
                String tenantDomain = RestApiUtil.getLoggedInUserTenantDomain();
                WebApp webApp = appProvider.getWebApp(appId);
                APIIdentifier appIdentifier = webApp.getId();
                documentation = appProvider.getDocumentation(documentId, tenantDomain);
                if (documentation == null) {
                    RestApiUtil.handleResourceNotFoundError(RestApiConstants.RESOURCE_DOCUMENTATION, documentId,
                            log);
                }
                appProvider.removeDocumentation(appIdentifier, documentId);
            }

        } catch (AppManagementException e) {
            //Auth failure occurs when cross tenant accessing APIs. Sends 404, since we don't need to expose the existence of the resource
            if (RestApiUtil.isDueToResourceNotFound(e) || RestApiUtil.isDueToAuthorizationFailure(e)) {
                RestApiUtil.handleResourceNotFoundError(appType, appId, e, log);
            } else {
                String errorMessage = "Error while retrieving " + appType + " : " + appId;
                RestApiUtil.handleInternalServerError(errorMessage, e, log);
            }
        }
        return Response.ok().build();
    }

    /**
     * Update a document with a given document id
     * @param appId application id
     * @param documentId documentation id
     * @param appType application type
     * @param body Documentation DTO
     * @param contentType
     * @param ifMatch
     * @param ifUnmodifiedSince
     * @return
     */
    @Override
    public Response appsAppTypeIdAppIdDocsDocumentIdPut(String appId, String documentId, String appType,
            DocumentDTO body, String contentType, String ifMatch, String ifUnmodifiedSince) {
        try {
            APIProvider appProvider = RestApiUtil.getLoggedInUserProvider();
            String tenantDomain = RestApiUtil.getLoggedInUserTenantDomain();

            WebApp webApp = appProvider.getWebApp(appId);
            String sourceUrl = body.getSourceUrl();
            Documentation oldDocument = appProvider.getDocumentation(documentId, tenantDomain);

            //validation checks for existence of the document
            if (oldDocument == null) {
                RestApiUtil.handleResourceNotFoundError(RestApiConstants.RESOURCE_DOCUMENTATION, documentId, log);
                return null;
            }
            if (body.getType() == DocumentDTO.TypeEnum.OTHER && StringUtils.isBlank(body.getOtherTypeName())) {
                //check otherTypeName for not null if doc type is OTHER
                RestApiUtil.handleBadRequest("otherTypeName cannot be empty if type is OTHER.", log);
                return null;
            }
            if (body.getSourceType() == DocumentDTO.SourceTypeEnum.URL
                    && (StringUtils.isBlank(sourceUrl) || !RestApiUtil.isURL(sourceUrl))) {
                RestApiUtil.handleBadRequest("Invalid document sourceUrl Format", log);
                return null;
            }

            //overriding some properties
            body.setName(oldDocument.getName());

            Documentation newDocumentation = DocumentationMappingUtil.fromDTOtoDocumentation(body);
            //this will fail if user does not have access to the API or the API does not exist
            APIIdentifier appIdentifier = webApp.getId();
            appProvider.updateDocumentation(appIdentifier, newDocumentation);

            //retrieve the updated documentation
            newDocumentation = appProvider.getDocumentation(documentId, tenantDomain);
            return Response.ok().entity(DocumentationMappingUtil.fromDocumentationToDTO(newDocumentation)).build();
        } catch (AppManagementException e) {
            //Auth failure occurs when cross tenant accessing APIs. Sends 404, since we don't need to expose the existence of the resource
            if (RestApiUtil.isDueToResourceNotFound(e) || RestApiUtil.isDueToAuthorizationFailure(e)) {
                RestApiUtil.handleResourceNotFoundError(appType, appId, e, log);
            } else {
                String errorMessage = "Error while updating the document " + documentId + " for API : " + appId;
                RestApiUtil.handleInternalServerError(errorMessage, e, log);
            }
        }
        return null;
    }

    /**
     * Document Content retrieve
     * @param appId  id of the application
     * @param documentId id of the documentation
     * @param appType type of the application
     * @param accept
     * @param ifNoneMatch
     * @param ifModifiedSince
     * @return
     */
    @Override
    public Response appsAppTypeIdAppIdDocsDocumentIdContentGet(String appId, String documentId, String appType,
            String accept, String ifNoneMatch, String ifModifiedSince) {

        Documentation documentation;
        try {
            String username = RestApiUtil.getLoggedInUsername();
            APIProvider appProvider = RestApiUtil.getLoggedInUserProvider();
            String tenantDomain = RestApiUtil.getLoggedInUserTenantDomain();

            WebApp webApp = appProvider.getWebApp(appId);
            APIIdentifier appIdentifier = webApp.getId();
            documentation = appProvider.getDocumentation(documentId, tenantDomain);
            if (documentation == null) {
                RestApiUtil.handleResourceNotFoundError(RestApiConstants.RESOURCE_DOCUMENTATION, documentId, log);
                return null;
            }

            //gets the content depending on the type of the document
            if (documentation.getSourceType().equals(Documentation.DocumentSourceType.FILE)) {
                String resource = documentation.getFilePath();
                Map<String, Object> docResourceMap = AppManagerUtil.getDocument(username, resource, tenantDomain);
                Object fileDataStream = docResourceMap.get(AppMConstants.DOCUMENTATION_RESOURCE_MAP_DATA);
                Object contentType = docResourceMap.get(AppMConstants.DOCUMENTATION_RESOURCE_MAP_CONTENT_TYPE);
                contentType = contentType == null ? RestApiConstants.APPLICATION_OCTET_STREAM : contentType;
                String name = docResourceMap.get(AppMConstants.DOCUMENTATION_RESOURCE_MAP_NAME).toString();
                return Response.ok(fileDataStream).header(RestApiConstants.HEADER_CONTENT_TYPE, contentType)
                        .header(RestApiConstants.HEADER_CONTENT_DISPOSITION,
                                "attachment; filename=\"" + name + "\"")
                        .build();
            } else if (documentation.getSourceType().equals(Documentation.DocumentSourceType.INLINE)) {
                String content = appProvider.getDocumentationContent(appIdentifier, documentation.getName());
                return Response.ok(content).header(RestApiConstants.HEADER_CONTENT_TYPE,
                        AppMConstants.DOCUMENTATION_INLINE_CONTENT_TYPE).build();
            } else if (documentation.getSourceType().equals(Documentation.DocumentSourceType.URL)) {
                String sourceUrl = documentation.getSourceUrl();
                return Response.seeOther(new URI(sourceUrl)).build();
            }
        } catch (AppManagementException e) {
            if (RestApiUtil.isDueToResourceNotFound(e) || RestApiUtil.isDueToAuthorizationFailure(e)) {
                RestApiUtil.handleResourceNotFoundError(appType, appId, e, log);
            } else {
                String errorMessage = "Error while retrieving document " + documentId + " of the " + appType
                        + " with id : " + appId;
                RestApiUtil.handleInternalServerError(errorMessage, e, log);
            }
        } catch (URISyntaxException e) {
            String errorMessage = "Error while retrieving source URI location of " + documentId;
            RestApiUtil.handleInternalServerError(errorMessage, e, log);
        }
        return null;
    }

    /**
     * Document content upload
     *
     * @param appType           application type
     * @param appId             application uuid
     * @param documentId        documentation id
     * @param fileInputStream   document content stream
     * @param fileDetail        document file details
     * @param inlineContent
     * @param ifMatch
     * @param ifUnmodifiedSince
     * @return
     */
    @Override
    public Response appsAppTypeIdAppIdDocsDocumentIdContentPost(String appType, String appId, String documentId,
            InputStream fileInputStream, Attachment fileDetail, String inlineContent, String ifMatch,
            String ifUnmodifiedSince) {

        try {
            String username = RestApiUtil.getLoggedInUsername();
            APIProvider appProvider = RestApiUtil.getLoggedInUserProvider();
            String tenantDomain = RestApiUtil.getLoggedInUserTenantDomain();

            WebApp webApp = appProvider.getWebApp(appId);
            APIIdentifier appIdentifier = webApp.getId();
            if (fileInputStream != null && inlineContent != null) {
                RestApiUtil.handleBadRequest("Only one of 'file' and 'inlineContent' should be specified", log);
            }
            Documentation documentation = appProvider.getDocumentation(documentId, tenantDomain);
            if (documentation == null) {
                RestApiUtil.handleResourceNotFoundError(RestApiConstants.RESOURCE_DOCUMENTATION, documentId, log);
                return null;
            }
            //add content depending on the availability of either input stream or inline content
            if (fileInputStream != null) {
                if (!documentation.getSourceType().equals(Documentation.DocumentSourceType.FILE)) {
                    RestApiUtil.handleBadRequest("Source type of document " + documentId + " is not FILE", log);
                }
                RestApiPublisherUtils.attachFileToDocument(webApp, documentation, fileInputStream, fileDetail);
            } else if (inlineContent != null) {
                if (!documentation.getSourceType().equals(Documentation.DocumentSourceType.INLINE)) {
                    RestApiUtil.handleBadRequest("Source type of document " + documentId + " is not INLINE", log);
                }
                appProvider.addDocumentationContent(appIdentifier, documentation.getName(), inlineContent);
            } else {
                RestApiUtil.handleBadRequest("Either 'file' or 'inlineContent' should be specified", log);
            }

            //retrieving the updated doc and the URI
            Documentation updatedDoc = appProvider.getDocumentation(documentId, tenantDomain);
            DocumentDTO documentDTO = DocumentationMappingUtil.fromDocumentationToDTO(updatedDoc);
            String uriString = RestApiConstants.RESOURCE_PATH_DOCUMENT_CONTENT
                    .replace(RestApiConstants.APPID_PARAM, appId)
                    .replace(RestApiConstants.DOCUMENTID_PARAM, documentId);
            URI uri = new URI(uriString);
            return Response.created(uri).entity(documentDTO).build();
        } catch (AppManagementException e) {
            //Auth failure occurs when cross tenant accessing APIs. Sends 404, since we don't need to expose the existence of the resource
            if (RestApiUtil.isDueToResourceNotFound(e) || RestApiUtil.isDueToAuthorizationFailure(e)) {
                RestApiUtil.handleResourceNotFoundError(appType, appId, e, log);
            } else {
                String errorMessage = "Error while retrieving document " + documentId + " of the API " + appId;
                RestApiUtil.handleInternalServerError(errorMessage, e, log);
            }
        } catch (URISyntaxException e) {
            String errorMessage = "Error while retrieving source URI location of " + documentId;
            RestApiUtil.handleInternalServerError(errorMessage, e, log);
        }

        return null;
    }

    @Override
    public Response appsAppTypeIdAppIdLifecycleGet(String appType, String appId, String accept,
            String ifNoneMatch) {
        LifeCycleDTO lifeCycleDTO = new LifeCycleDTO();
        try {
            //Validate App Type
            CommonValidator.isValidAppType(appType);
            String username = RestApiUtil.getLoggedInUsername();
            String tenantDomainName = MultitenantUtils.getTenantDomain(username);
            String tenantUserName = MultitenantUtils.getTenantAwareUsername(username);
            int tenantId = ServiceReferenceHolder.getInstance().getRealmService().getTenantManager()
                    .getTenantId(tenantDomainName);
            Registry registry = ServiceReferenceHolder.getInstance().getRegistryService()
                    .getGovernanceUserRegistry(tenantUserName, tenantId);
            boolean isAsynchronousFlow = org.wso2.carbon.appmgt.impl.workflow.WorkflowExecutorFactory.getInstance()
                    .getWorkflowExecutor("AM_APPLICATION_PUBLISH").isAsynchronus();
            GenericArtifactManager artifactManager = new GenericArtifactManager(registry, appType);
            GenericArtifact artifact = artifactManager.getGenericArtifact(appId);
            //Validate App Id
            if (artifact == null) {
                RestApiUtil.handleResourceNotFoundError(appType, appId, log);
            }

            String state = artifact.getLifecycleState().toUpperCase();
            String[] actions;
            if (AppMConstants.MOBILE_ASSET_TYPE.equalsIgnoreCase(appType)) {
                actions = artifact.getAllLifecycleActions(AppMConstants.MOBILE_LIFE_CYCLE);
            } else {
                actions = artifact.getAllLifecycleActions(AppMConstants.WEBAPP_LIFE_CYCLE);
            }

            lifeCycleDTO.setActions(Arrays.asList(actions));
            lifeCycleDTO.setAsync(isAsynchronousFlow);
            lifeCycleDTO.setState(state);
        } catch (Exception e) {
            String errorMessage = "Error while retrieving lifecycle state of app with id : " + appId;
            RestApiUtil.handleInternalServerError(errorMessage, e, log);
        }
        return Response.ok().entity(lifeCycleDTO).build();
    }

    @Override
    public Response appsAppTypeIdAppIdLifecycleHistoryGet(String appType, String appId, String accept,
            String ifNoneMatch) {
        LifeCycleHistoryListDTO lifeCycleHistoryListDTO = new LifeCycleHistoryListDTO();
        try {
            //Validate App Type
            CommonValidator.isValidAppType(appType);
            String username = RestApiUtil.getLoggedInUsername();
            String tenantDomainName = MultitenantUtils.getTenantDomain(username);
            String tenantUserName = MultitenantUtils.getTenantAwareUsername(username);
            int tenantId = ServiceReferenceHolder.getInstance().getRealmService().getTenantManager()
                    .getTenantId(tenantDomainName);
            Registry registry = ServiceReferenceHolder.getInstance().getRegistryService()
                    .getGovernanceUserRegistry(tenantUserName, tenantId);
            GenericArtifactManager artifactManager = new GenericArtifactManager(registry, appType);
            GenericArtifact artifact = artifactManager.getGenericArtifact(appId);
            //Validate App Id
            if (artifact == null) {
                RestApiUtil.handleResourceNotFoundError(appType, appId, log);
            }

            String historyRegPath = getHistoryPath(artifact);
            String historyResourceXMLStr = IOUtils.toString(registry.get(historyRegPath).getContentStream());
            JSONObject historyResourceObj = XML.toJSONObject(historyResourceXMLStr);

            JSONArray historyResourceJsonArray = (historyResourceObj.getJSONObject("lifecycleHistory"))
                    .getJSONArray("item");
            List<LifeCycleHistoryDTO> lifeCycleHistoryDTOList = new ArrayList<>();
            //iterate life cycle history json
            for (int i = 0; i < historyResourceJsonArray.length() - 1; i++) {
                JSONObject lifecycleHistoryStateObj = (JSONObject) historyResourceJsonArray.get(i);
                LifeCycleHistoryDTO lifeCycleHistoryDTO = new LifeCycleHistoryDTO();
                lifeCycleHistoryDTO.setOrder(Integer.parseInt(lifecycleHistoryStateObj.get("order").toString()));
                lifeCycleHistoryDTO.setState((String) lifecycleHistoryStateObj.get("state"));
                lifeCycleHistoryDTO.setTargetState((String) lifecycleHistoryStateObj.get("targetState"));
                lifeCycleHistoryDTO.setTimestamp((String) lifecycleHistoryStateObj.get("timestamp"));
                lifeCycleHistoryDTO.setUser((String) lifecycleHistoryStateObj.get("user"));
                lifeCycleHistoryDTOList.add(lifeCycleHistoryDTO);
            }
            lifeCycleHistoryListDTO.setLifeCycleHistoryList(lifeCycleHistoryDTOList);
        } catch (GovernanceException e) {
            String errorMessage = "GovernanceException while retrieving lifecycle History of app with id : "
                    + appId;
            RestApiUtil.handleInternalServerError(errorMessage, e, log);
        } catch (IOException e) {
            String errorMessage = "IOException while retrieving lifecycle History of app with id : " + appId;
            RestApiUtil.handleInternalServerError(errorMessage, e, log);
        } catch (UserStoreException e) {
            String errorMessage = "UserStoreException while retrieving lifecycle History of app with id : " + appId;
            RestApiUtil.handleInternalServerError(errorMessage, e, log);
        } catch (RegistryException e) {
            String errorMessage = "RegistryException while retrieving lifecycle History of app with id : " + appId;
            RestApiUtil.handleInternalServerError(errorMessage, e, log);
        } catch (JSONException e) {
            String errorMessage = "JSONException while retrieving lifecycle History of app with id : " + appId;
            RestApiUtil.handleInternalServerError(errorMessage, e, log);
        }
        return Response.ok().entity(lifeCycleHistoryListDTO).build();
    }

    @Override
    public Response appsAppTypeIdAppIdSubscriptionsGet(String appType, String appId, String accept,
            String ifNoneMatch, String ifModifiedSince) {
        UserIdListDTO userIdListDTO = new UserIdListDTO();
        try {
            APIProvider appProvider = RestApiUtil.getLoggedInUserProvider();
            if (AppMConstants.WEBAPP_ASSET_TYPE.equals(appType)) {

                AppManagerConfiguration appManagerConfiguration = ServiceReferenceHolder.getInstance()
                        .getAPIManagerConfigurationService().getAPIManagerConfiguration();
                Boolean isSelfSubscriptionEnabled = Boolean
                        .valueOf(appManagerConfiguration.getFirstProperty(AppMConstants.ENABLE_SELF_SUBSCRIPTION));
                Boolean isEnterpriseSubscriptionEnabled = Boolean.valueOf(
                        appManagerConfiguration.getFirstProperty(AppMConstants.ENABLE_ENTERPRISE_SUBSCRIPTION));
                if (isSelfSubscriptionEnabled || isEnterpriseSubscriptionEnabled) {
                    WebApp webApp = appProvider.getAppDetailsFromUUID(appId);
                    Set<Subscriber> subscriberSet = appProvider.getSubscribersOfAPI(webApp.getId());
                    userIdListDTO.setUserIds(subscriberSet);
                } else {
                    RestApiUtil.handleBadRequest("Subscription is disabled", log);
                }
            } else {
                RestApiUtil.handleBadRequest("Unsupported application type '" + appType + "' provided", log);
            }
        } catch (AppManagementException e) {
            if (RestApiUtil.isDueToResourceNotFound(e) || RestApiUtil.isDueToAuthorizationFailure(e)) {
                RestApiUtil.handleResourceNotFoundError(appType, appId, e, log);
            } else {
                String errorMessage = "Error while changing lifecycle state of app with id : " + appId;
                RestApiUtil.handleInternalServerError(errorMessage, e, log);
            }

        }
        return Response.ok().entity(userIdListDTO).build();
    }

    @Override
    public Response appsAppTypeIdAppIdTagsGet(String appType, String appId, String accept, String ifNoneMatch) {
        TagListDTO tagListDTO = new TagListDTO();
        List<String> tags = new ArrayList<>();
        try {
            if (AppMConstants.MOBILE_ASSET_TYPE.equals(appType)
                    || AppMConstants.WEBAPP_ASSET_TYPE.equals(appType)) {

                APIProvider appProvider = RestApiUtil.getLoggedInUserProvider();
                for (Tag tag : appProvider.getAllTags(appType, appId)) {
                    tags.add(tag.getName());
                }
                tagListDTO.setTags(tags);
            } else {
                RestApiUtil.handleBadRequest("Unsupported application type '" + appType + "' provided", log);
            }
        } catch (AppManagementException e) {
            //Auth failure occurs when cross tenant accessing APIs. Sends 404, since we don't need to expose the
            // existence of the resource
            if (RestApiUtil.isDueToResourceNotFound(e) || RestApiUtil.isDueToAuthorizationFailure(e)) {
                RestApiUtil.handleResourceNotFoundError(appType, appId, e, log);
            } else {
                String errorMessage = "Error retrieving tags for " + appType + " with id : " + appId;
                RestApiUtil.handleInternalServerError(errorMessage, e, log);
            }
        }
        return Response.ok().entity(tagListDTO).build();
    }

    /**
     * Add a tag to an application
     *
     * @param appType           appType application type ie: webapp, mobileapp
     * @param appId             application uuid
     * @param body              tag list
     * @param contentType
     * @param ifMatch
     * @param ifUnmodifiedSince
     * @return
     */
    @Override
    public Response appsAppTypeIdAppIdTagsPut(String appType, String appId, TagListDTO body, String contentType,
            String ifMatch, String ifUnmodifiedSince) {
        beanValidator = new BeanValidator();
        //Validate common mandatory fields for mobile and webapp
        beanValidator.validate(body);
        try {
            if (AppMConstants.MOBILE_ASSET_TYPE.equals(appType)
                    || AppMConstants.WEBAPP_ASSET_TYPE.equals(appType)) {
                List<String> tagList = body.getTags();
                APIProvider appProvider = RestApiUtil.getLoggedInUserProvider();
                appProvider.addTags(appType, appId, tagList);
            } else {
                RestApiUtil.handleBadRequest("Unsupported application type '" + appType + "' provided", log);
            }
        } catch (AppManagementException e) {
            //Auth failure occurs when cross tenant accessing APIs. Sends 404, since we don't need to expose the
            // existence of the resource
            if (RestApiUtil.isDueToResourceNotFound(e) || RestApiUtil.isDueToAuthorizationFailure(e)) {
                RestApiUtil.handleResourceNotFoundError(appType, appId, e, log);
            } else {
                String errorMessage = "Error while adding a tag to " + appType + " with id : " + appId;
                RestApiUtil.handleInternalServerError(errorMessage, e, log);
            }
        }
        return Response.ok().build();
    }

    @Override
    public Response appsAppTypeIdAppIdTagsDelete(String appType, String appId, TagListDTO body, String ifMatch,
            String ifUnmodifiedSince) {

        beanValidator = new BeanValidator();
        //Validate common mandatory fields for mobile and webapp
        beanValidator.validate(body);
        try {
            if (AppMConstants.MOBILE_ASSET_TYPE.equals(appType)
                    || AppMConstants.WEBAPP_ASSET_TYPE.equals(appType)) {
                List<String> tags = body.getTags();
                APIProvider appProvider = RestApiUtil.getLoggedInUserProvider();
                appProvider.removeTag(appType, appId, tags);
            } else {
                RestApiUtil.handleBadRequest("Unsupported application type '" + appType + "' provided", log);
            }
        } catch (AppManagementException e) {
            //Auth failure occurs when cross tenant accessing APIs. Sends 404, since we don't need to expose the
            // existence of the resource
            if (RestApiUtil.isDueToResourceNotFound(e) || RestApiUtil.isDueToAuthorizationFailure(e)) {
                RestApiUtil.handleResourceNotFoundError(appType, appId, e, log);
            } else {
                String errorMessage = "Error while deleting tags from " + appType + " with id : " + appId;
                RestApiUtil.handleInternalServerError(errorMessage, e, log);
            }
        }
        return Response.ok().build();
    }

    @Override
    public Response appsAppTypeThrottlingtiersGet(String appType, String accept, String ifNoneMatch) {
        TierListDTO tierListDTO = new TierListDTO();
        try {
            //check appType validity (currently support only webApps)
            if (!AppMConstants.WEBAPP_ASSET_TYPE.equals(appType)) {
                RestApiUtil.handleBadRequest("Unsupported application type '" + appType + "' provided", log);
            }

            List<TierDTO> tierDTOList = new ArrayList<>();
            APIProvider apiProvider = RestApiUtil.getLoggedInUserProvider();
            Set<Tier> tiers = apiProvider.getTiers();
            if (tiers.isEmpty()) {
                return RestApiUtil.buildNotFoundException("Tiers", null).getResponse();
            }

            for (Tier tier : tiers) {
                TierDTO tierDTO = new TierDTO();
                tierDTO.setTierName(tier.getName());
                tierDTO.setTierDisplayName(tier.getDisplayName());
                tierDTO.setTierDescription(tier.getDescription() != null ? tier.getDescription() : "");
                tierDTO.setTierSortKey(tier.getRequestPerMinute());
                tierDTOList.add(tierDTO);
            }
            tierListDTO.setTierList(tierDTOList);
        } catch (AppManagementException e) {
            String errorMessage = "Error while retrieving Throttling Tier details";
            RestApiUtil.handleInternalServerError(errorMessage, e, log);
        }
        return Response.ok().entity(tierListDTO).build();
    }

    @Override
    public Response appsAppTypeStatsStatTypeGet(String appType, String statType, String startTimeStamp,
            String endTimeStamp, Integer limit, String accept, String ifNoneMatch) {
        StatSummaryDTO statSummaryDTO = new StatSummaryDTO();
        try {
            if (AppMConstants.WEBAPP_ASSET_TYPE.equals(appType)) {
                APIProvider appProvider = RestApiUtil.getLoggedInUserProvider();
                String username = RestApiUtil.getLoggedInUsername();
                String tenantDomainName = MultitenantUtils.getTenantDomain(username);
                String providerName = RestApiConstants.STATS_ALL_PROVIDERS;
                // check whether start and end dates are in correct format.
                if (!StringUtils.isEmpty(startTimeStamp) || !StringUtils.isEmpty(endTimeStamp)) {
                    if (!isTimeStampValid(startTimeStamp) || !isTimeStampValid(endTimeStamp)) {
                        String errorMessage = "Start timestamp and end timestamp should be in YYYY-MM-DD HH:MM:SS"
                                + " format";
                        RestApiUtil.handleBadRequest(errorMessage, log);
                    }
                }

                switch (statType) {
                case "getSubscriptionCountsPerApp":
                    statSummaryDTO = getSubscriptionCountsPerApp(appProvider, providerName, startTimeStamp,
                            endTimeStamp);
                    break;
                case "getSubscriptionsPerApp":
                    statSummaryDTO = getSubscriptionsPerApp(appProvider, startTimeStamp, endTimeStamp);
                    break;
                case "getAppUsagePerUser":
                    statSummaryDTO = getAppUsagePerUser(providerName, username, tenantDomainName, startTimeStamp,
                            endTimeStamp);
                    break;
                case "getAppResponseTime":
                    statSummaryDTO = getAppResponseTime(providerName, username, tenantDomainName, startTimeStamp,
                            endTimeStamp, limit);
                    break;
                case "getAppUsagePerPage":
                    statSummaryDTO = getAppUsagePerPage(providerName, username, tenantDomainName, startTimeStamp,
                            endTimeStamp);
                    break;
                default:
                    RestApiUtil.handleBadRequest("Unsupported statistics type '" + statType + "' has provided",
                            log);
                }
            } else {
                RestApiUtil.handleBadRequest("Unsupported application type '" + appType + "' provided", log);
            }
        } catch (AppManagementException e) {
            String errorMessage = "Error occurred while retrieving statistics details for " + statType;
            RestApiUtil.handleInternalServerError(errorMessage, e, log);
        }
        return Response.ok().entity(statSummaryDTO).build();
    }

    private boolean isTimeStampValid(String timeStamp) {
        SimpleDateFormat format = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        try {
            format.parse(timeStamp);
            return true;
        } catch (ParseException e) {
            return false;
        }
    }

    private StatSummaryDTO getAppUsagePerUser(String providerName, String userName, String tenantDomainName,
            String startTimeStamp, String endTimeStamp) {
        StatSummaryDTO statSummaryDTO = new StatSummaryDTO();
        try {
            AppUsageStatisticsService appUsageStatisticsService = new AppUsageStatisticsService(userName);
            List<AppUsageByUserDTO> appUsageByUserList = appUsageStatisticsService.getAppUsageByUser(providerName,
                    startTimeStamp, endTimeStamp, tenantDomainName);
            List<Object> appObjectList = new ArrayList<>();
            for (AppUsageByUserDTO appUsageByUser : appUsageByUserList) {
                appObjectList.add(appUsageByUser);
            }
            statSummaryDTO.setResult(appObjectList);
        } catch (AppUsageQueryServiceClientException e) {
            String errorMessage = "Error occurred while retrieving statistics of app usage per users for the period "
                    + startTimeStamp + "to " + endTimeStamp;
            RestApiUtil.handleInternalServerError(errorMessage, e, log);
        }
        return statSummaryDTO;
    }

    private StatSummaryDTO getAppUsagePerPage(String providerName, String userName, String tenantDomainName,
            String startTimeStamp, String endTimeStamp) {
        StatSummaryDTO statSummaryDTO = new StatSummaryDTO();
        try {
            AppUsageStatisticsService appUsageStatisticsService = new AppUsageStatisticsService(userName);
            List<AppPageUsageDTO> appUsageByPageList = appUsageStatisticsService.getAppUsageByPage(providerName,
                    startTimeStamp, endTimeStamp, tenantDomainName);
            List<Object> appObjectList = new ArrayList<>();
            for (AppPageUsageDTO appUsageByPage : appUsageByPageList) {
                appObjectList.add(appUsageByPage);
            }
            statSummaryDTO.setResult(appObjectList);
        } catch (AppUsageQueryServiceClientException e) {
            String errorMessage = "Error occurred while retrieving statistics of app usage per page for the period "
                    + startTimeStamp + "to " + endTimeStamp;
            RestApiUtil.handleInternalServerError(errorMessage, e, log);
        }
        return statSummaryDTO;
    }

    private StatSummaryDTO getAppResponseTime(String providerName, String userName, String tenantDomainName,
            String startTimeStamp, String endTimeStamp, int limit) {
        StatSummaryDTO statSummaryDTO = new StatSummaryDTO();
        try {
            AppUsageStatisticsService appUsageStatisticsService = new AppUsageStatisticsService(userName);
            List<AppResponseTimeDTO> appResponseTimeList = appUsageStatisticsService
                    .getResponseTimesByApps(providerName, startTimeStamp, endTimeStamp, limit, tenantDomainName);
            List<Object> appObjectList = new ArrayList<>();
            for (AppResponseTimeDTO appResponseTime : appResponseTimeList) {
                appObjectList.add(appResponseTime);
            }
            statSummaryDTO.setResult(appObjectList);
        } catch (AppUsageQueryServiceClientException e) {
            String errorMessage = "Error occurred while retrieving statistics of app response time for the period "
                    + startTimeStamp + "to " + endTimeStamp;
            RestApiUtil.handleInternalServerError(errorMessage, e, log);
        }
        return statSummaryDTO;
    }

    private StatSummaryDTO getSubscriptionCountsPerApp(APIProvider appProvider, String providerName,
            String startTimeStamp, String endTimeStamp) {
        StatSummaryDTO statSummaryDTO = new StatSummaryDTO();
        try {
            Boolean isSubscriptionEnabled = RestApiUtil.isSubscriptionEnable();
            Map<String, Long> subscriptionCountMap = appProvider.getSubscriptionCountByAPPs(providerName,
                    startTimeStamp, endTimeStamp, isSubscriptionEnabled);
            if (subscriptionCountMap != null) {
                List<Object> appObjectList = new ArrayList<>();
                for (String key : subscriptionCountMap.keySet()) {
                    SubscriptionCount subscription = new SubscriptionCount();
                    // Key contains appName + "/" + appVersion + "&" + appuuid;
                    String appName = key.split("/")[0];
                    String appVersionWithUuid = key.split("/")[1];
                    String appVersion = appVersionWithUuid.split("&")[0];
                    String appId = appVersionWithUuid.split("&")[1];
                    subscription.setAppId(appId);
                    subscription.setAppName(appName);
                    subscription.setAppVersion(appVersion);
                    Long subscriptionCount = subscriptionCountMap.get(key);
                    subscription.setSubscriptionCount(subscriptionCount);
                    appObjectList.add(subscription);
                }
                statSummaryDTO.setResult(appObjectList);
            }
        } catch (AppManagementException e) {
            String errorMessage = "Error occurred while retrieving statistics of subscribers count per apps for the "
                    + "period " + startTimeStamp + "to " + endTimeStamp;
            RestApiUtil.handleInternalServerError(errorMessage, e, log);
        }
        return statSummaryDTO;
    }

    private StatSummaryDTO getSubscriptionsPerApp(APIProvider appProvider, String startTimeStamp,
            String endTimeStamp) {
        StatSummaryDTO statSummaryDTO = new StatSummaryDTO();
        try {
            Map<String, List> subscribedAppsByUserMap = appProvider.getSubscribedAPPsByUsers(startTimeStamp,
                    endTimeStamp);
            if (subscribedAppsByUserMap != null) {
                List<Object> appObjectList = new ArrayList<>();
                for (String key : subscribedAppsByUserMap.keySet()) {
                    Subscriptions subscriptions = new Subscriptions();
                    // Key contains appName + "/" + appVersion
                    String appName = key.split("/")[0];
                    String appVersion = key.split("/")[1];
                    subscriptions.setAppName(appName);
                    subscriptions.setAppVersion(appVersion);
                    List<Subscriber> subscriptionList = subscribedAppsByUserMap.get(key);
                    subscriptions.setSubscribersList(subscriptionList);
                    appObjectList.add(subscriptions);
                }
                statSummaryDTO.setResult(appObjectList);
            }
        } catch (AppManagementException e) {
            String errorMessage = "Error occurred while retrieving statistics of subscriptions per app for the period"
                    + " " + startTimeStamp + "to " + endTimeStamp;
            RestApiUtil.handleInternalServerError(errorMessage, e, log);
        }
        return statSummaryDTO;
    }

    @Override
    public Response appsAppTypeTagsGet(String appType, String accept, String ifNoneMatch) {
        Set<Tag> tagSet = new HashSet<>();
        try {
            if (AppMConstants.MOBILE_ASSET_TYPE.equals(appType)
                    || AppMConstants.WEBAPP_ASSET_TYPE.equals(appType)) {
                APIProvider appProvider = RestApiUtil.getLoggedInUserProvider();
                tagSet = appProvider.getAllTags(appType);
                if (tagSet.isEmpty()) {
                    return RestApiUtil.buildNotFoundException("Tags", null).getResponse();
                }
            } else {
                RestApiUtil.handleBadRequest("Unsupported application type '" + appType + "' provided", log);
            }
        } catch (AppManagementException e) {
            String errorMessage = "Error retrieving tags for " + appType + "s.";
            RestApiUtil.handleInternalServerError(errorMessage, e, log);

        }
        return Response.ok().entity(tagSet).build();
    }

    /**
     * Validate webapp context
     *
     * @param appType         application type
     * @param appContext      context of the webapp
     * @param contentType
     * @param ifModifiedSince
     * @return whether context is valid or not
     */
    @Override
    public Response appsAppTypeValidateContextPost(String appType, String appContext, String contentType,
            String ifModifiedSince) {
        boolean isContextExists = false;
        Map<String, Boolean> responseMap = new HashMap<>();
        try {
            if (AppMConstants.WEBAPP_ASSET_TYPE.equals(appType)) {
                if (StringUtils.isEmpty(appContext)) {
                    RestApiUtil.handleBadRequest("Webapp context is not provided", log);
                }

                if (appContext.indexOf("/") != 0) {
                    appContext = "/" + appContext;
                }
                APIProvider appProvider = RestApiUtil.getLoggedInUserProvider();
                isContextExists = appProvider.isContextExist(appContext);
                responseMap.put("IsContextExists", isContextExists);
            } else {
                RestApiUtil.handleBadRequest("Unsupported application type '" + appType + "' provided", log);
            }
        } catch (AppManagementException e) {
            String errorMessage = "Error retrieving tags for " + appType + "s.";
            RestApiUtil.handleInternalServerError(errorMessage, e, log);
        }
        return Response.ok().entity(responseMap).build();
    }

    @Override
    public Response appsAppTypeNameAppNameVersionVersionUuidGet(String appType, String appName, String version,
            String accept, String ifNoneMatch) {
        AppDTO appDTO = new AppDTO();
        if (AppMConstants.WEBAPP_ASSET_TYPE.equals(appType)) {
            try {
                APIProvider appProvider = RestApiUtil.getLoggedInUserProvider();
                int tenantId = ServiceReferenceHolder.getInstance().getRealmService().getTenantManager()
                        .getTenantId(RestApiUtil.getLoggedInUserTenantDomain());

                String uuid = appProvider.getAppUUIDbyName(appName, version, tenantId);
                if (log.isDebugEnabled()) {
                    log.debug("UUID of the app: " + appName + ", version: " + version + " is " + uuid);
                }
                appDTO.setId(uuid);
            } catch (AppManagementException e) {
                String errorMessage = "Error while retrieving UUID for app: " + appName + " and version: "
                        + version;
                RestApiUtil.handleInternalServerError(errorMessage, e, log);
            } catch (UserStoreException e) {
                String errorMessage = "Error while retrieving tenant details";
                RestApiUtil.handleInternalServerError(errorMessage, e, log);
            }

        } else {
            RestApiUtil.handleBadRequest("Unsupported application type '" + appType + "' provided", log);
        }
        return Response.ok().entity(appDTO).build();
    }

    //remove artifact from registry
    private void removeRegistryArtifact(App webApp, String username)
            throws RegistryException, AppManagementException, UserStoreException {
        String tenantDomainName = MultitenantUtils.getTenantDomain(username);
        String tenantUserName = MultitenantUtils.getTenantAwareUsername(username);
        int tenantId = ServiceReferenceHolder.getInstance().getRealmService().getTenantManager()
                .getTenantId(tenantDomainName);
        Registry registry = ServiceReferenceHolder.getInstance().getRegistryService()
                .getGovernanceUserRegistry(tenantUserName, tenantId);

        GenericArtifactManager artifactManager = AppManagerUtil.getArtifactManager(registry,
                AppMConstants.MOBILE_ASSET_TYPE);
        artifactManager.removeGenericArtifact(webApp.getUUID());
    }

    private String getHistoryPath(GenericArtifact genericArtifact) throws GovernanceException {
        String assetPath = genericArtifact.getPath();
        //Replace the / in the assetPath
        String partialHistoryPath = assetPath.replace("/", "_");
        String fullPath = RestApiConstants.HISTORY_PATH + "__system_governance" + partialHistoryPath;
        return fullPath;
    }
}