apim.restful.importexport.utils.APIExportUtil.java Source code

Java tutorial

Introduction

Here is the source code for apim.restful.importexport.utils.APIExportUtil.java

Source

/*
 *
 *  Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 *  Licensed 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 apim.restful.importexport.utils;

import apim.restful.importexport.APIExportException;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.apache.axiom.om.OMElement;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.apimgt.api.APIDefinition;
import org.wso2.carbon.apimgt.api.APIManagementException;
import org.wso2.carbon.apimgt.api.APIProvider;
import org.wso2.carbon.apimgt.api.model.*;
import org.wso2.carbon.apimgt.impl.APIConstants;
import org.wso2.carbon.apimgt.impl.APIManagerFactory;
import org.wso2.carbon.apimgt.impl.definitions.APIDefinitionFromSwagger20;
import org.wso2.carbon.apimgt.impl.utils.APIUtil;
import org.wso2.carbon.context.CarbonContext;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.context.RegistryType;
import org.wso2.carbon.registry.api.Registry;
import org.wso2.carbon.registry.api.RegistryException;
import org.wso2.carbon.registry.api.Resource;
import org.wso2.carbon.registry.core.RegistryConstants;
import org.wso2.carbon.utils.multitenancy.MultitenantConstants;
import org.wso2.carbon.utils.multitenancy.MultitenantUtils;

import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamException;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.StringReader;

import java.util.*;

/**
 * This is the util class which consists of all the functions for exporting API
 */
public class APIExportUtil {

    private static final Log log = LogFactory.getLog(APIExportUtil.class);
    private static String archiveBasePath = null;

    private APIExportUtil() {
    }

    /**
     * Set base path where exporting archive should be generated
     *
     * @param path Temporary directory location
     */
    public static void setArchiveBasePath(String path) {
        archiveBasePath = path;
    }

    /**
     * Retrieve API provider
     *
     * @param userName User name
     * @return APIProvider Provider of the supplied user name
     * @throws APIExportException If an error occurs while retrieving the provider
     */
    public static APIProvider getProvider(String userName) throws APIExportException {
        APIProvider provider;
        try {
            provider = APIManagerFactory.getInstance().getAPIProvider(userName);

            if (log.isDebugEnabled()) {
                log.debug("Current provider retrieved successfully");
            }

            return provider;

        } catch (APIManagementException e) {
            log.error("Error while retrieving provider" + e.getMessage());
            throw new APIExportException("Error while retrieving current provider", e);
        }

    }

    /**
     * Retrieve registry for the current tenant
     *
     * @param userName user name of the tenant
     * @return Registry registry of the current tenant
     */
    public static Registry getRegistry(String userName) {
        boolean isTenantFlowStarted = false;
        String tenantDomain = MultitenantUtils.getTenantDomain(userName);
        try {
            if (tenantDomain != null && !MultitenantConstants.SUPER_TENANT_DOMAIN_NAME.equals(tenantDomain)) {
                isTenantFlowStarted = true;
                PrivilegedCarbonContext.startTenantFlow();
                PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(tenantDomain, true);
            }
            Registry registry = CarbonContext.getThreadLocalCarbonContext()
                    .getRegistry(RegistryType.SYSTEM_GOVERNANCE);

            if (log.isDebugEnabled()) {
                log.debug("Registry of logged in user retrieved successfully");
            }
            return registry;

        } finally {
            if (isTenantFlowStarted) {
                PrivilegedCarbonContext.endTenantFlow();
            }
        }
    }

    /**
     * This method retrieves all meta information and registry resources required for an API to
     * recreate
     *
     * @param apiID    Identifier of the exporting API
     * @param userName User name of the requester
     * @return HttpResponse indicating whether resource retrieval got succeed or not
     * @throws APIExportException If an error occurs while retrieving API related resources
     */
    public static Response retrieveApiToExport(APIIdentifier apiID, String userName) throws APIExportException {

        API apiToReturn;
        String archivePath = archiveBasePath.concat(File.separator + apiID.getApiName() + "-" + apiID.getVersion());
        //initializing provider
        APIProvider provider = getProvider(userName);
        //registry for the current user
        Registry registry = APIExportUtil.getRegistry(userName);

        int tenantId = APIUtil.getTenantId(userName);

        //directory creation
        APIExportUtil.createDirectory(archivePath);

        try {
            apiToReturn = provider.getAPI(apiID);
        } catch (APIManagementException e) {
            log.error("Unable to retrieve API", e);
            return Response.status(Response.Status.NOT_FOUND).entity("Unable to retrieve API")
                    .type(MediaType.APPLICATION_JSON).build();
        }

        //export thumbnail
        exportAPIThumbnail(apiID, registry);

        //export documents
        List<Documentation> docList;
        try {
            docList = provider.getAllDocumentation(apiID);
        } catch (APIManagementException e) {
            log.error("Unable to retrieve API Documentation", e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity("Internal Server Error")
                    .type(MediaType.APPLICATION_JSON).build();
        }

        if (!docList.isEmpty()) {
            exportAPIDocumentation(docList, apiID, registry);
        }

        //export wsdl
        String wsdlUrl = apiToReturn.getWsdlUrl();
        if (wsdlUrl != null) {
            exportWSDL(apiID, registry);
        }

        //export sequences
        exportSequences(apiToReturn, apiID, tenantId, registry);

        //set API status to created
        apiToReturn.setStatus(APIStatus.CREATED);

        //export meta information
        exportMetaInformation(apiToReturn, registry);

        return Response.ok().build();

    }

    /**
     * Retrieve thumbnail image for the exporting API and store it in the archive directory
     *
     * @param apiIdentifier ID of the requesting API
     * @param registry      Current tenant registry
     * @throws APIExportException If an error occurs while retrieving image from the registry or
     *                            storing in the archive directory
     */
    private static void exportAPIThumbnail(APIIdentifier apiIdentifier, Registry registry)
            throws APIExportException {
        String thumbnailUrl = APIConstants.API_IMAGE_LOCATION + RegistryConstants.PATH_SEPARATOR
                + apiIdentifier.getProviderName() + RegistryConstants.PATH_SEPARATOR + apiIdentifier.getApiName()
                + RegistryConstants.PATH_SEPARATOR + apiIdentifier.getVersion() + RegistryConstants.PATH_SEPARATOR
                + APIConstants.API_ICON_IMAGE;

        InputStream imageDataStream = null;
        OutputStream outputStream = null;
        String archivePath = archiveBasePath
                .concat(File.separator + apiIdentifier.getApiName() + "-" + apiIdentifier.getVersion());
        try {
            if (registry.resourceExists(thumbnailUrl)) {
                Resource icon = registry.get(thumbnailUrl);

                imageDataStream = icon.getContentStream();

                String mediaType = icon.getMediaType();
                String extension = getThumbnailFileType(mediaType);

                if (extension != null) {
                    createDirectory(archivePath + File.separator + "Image");

                    outputStream = new FileOutputStream(
                            archivePath + File.separator + "Image" + File.separator + "icon." + extension);

                    IOUtils.copy(imageDataStream, outputStream);

                    if (log.isDebugEnabled()) {
                        log.debug("Thumbnail image retrieved successfully");
                    }
                }
            }
        } catch (IOException e) {
            //Exception is ignored by logging due to the reason that Thumbnail is not essential for
            // an API to be recreated
            log.error("I/O error while writing API Thumbnail to file" + e.getMessage());
        } catch (RegistryException e) {
            log.error("Error while retrieving API Thumbnail " + e.getMessage());
        } finally {
            IOUtils.closeQuietly(imageDataStream);
            IOUtils.closeQuietly(outputStream);
        }
    }

    /**
     * Retrieve content type of the thumbnail image for setting the exporting file extension
     *
     * @param mediaType Media type of the thumbnail registry resource
     * @return File extension for the exporting image
     */
    private static String getThumbnailFileType(String mediaType) {
        if (("image/png").equals(mediaType)) {
            return "png";
        } else if (("image/jpg").equals(mediaType)) {
            return "jpg";
        } else if ("image/jpeg".equals(mediaType)) {
            return "jpeg";
        } else if (("image/bmp").equals(mediaType)) {
            return "bmp";
        } else if (("image/gif").equals(mediaType)) {
            return "gif";
        } else {
            //api gets imported without thumbnail
            log.error("Unsupported media type for icon " + mediaType);
        }

        return null;
    }

    /**
     * Retrieve documentation for the exporting API and store it in the archive directory
     * FILE, INLINE and URL documentations are handled
     *
     * @param apiIdentifier ID of the requesting API
     * @param registry      Current tenant registry
     * @param docList       documentation list of the exporting API
     * @throws APIExportException If an error occurs while retrieving documents from the
     *                            registry or storing in the archive directory
     */
    public static void exportAPIDocumentation(List<Documentation> docList, APIIdentifier apiIdentifier,
            Registry registry) throws APIExportException {
        Gson gson = new GsonBuilder().setPrettyPrinting().create();
        String archivePath = archiveBasePath
                .concat(File.separator + apiIdentifier.getApiName() + "-" + apiIdentifier.getVersion());
        createDirectory(archivePath + File.separator + "Docs");
        InputStream fileInputStream = null;
        OutputStream outputStream = null;
        try {
            for (Documentation doc : docList) {
                String sourceType = doc.getSourceType().name();
                if (Documentation.DocumentSourceType.FILE.toString().equalsIgnoreCase(sourceType)) {
                    String fileName = doc.getFilePath()
                            .substring(doc.getFilePath().lastIndexOf(RegistryConstants.PATH_SEPARATOR) + 1);
                    String filePath = APIUtil.getDocumentationFilePath(apiIdentifier, fileName);

                    //check whether resource exists in the registry
                    Resource docFile = registry.get(filePath);
                    String localFilePath = File.separator + "Docs" + File.separator + fileName;
                    outputStream = new FileOutputStream(archivePath + localFilePath);
                    fileInputStream = docFile.getContentStream();

                    IOUtils.copy(fileInputStream, outputStream);

                    doc.setFilePath(localFilePath);

                    if (log.isDebugEnabled()) {
                        log.debug(fileName + " retrieved successfully");
                    }
                }
            }

            String json = gson.toJson(docList);
            writeFile(archivePath + File.separator + "Docs" + File.separator + "docs.json", json);

            if (log.isDebugEnabled()) {
                log.debug("API Documentation retrieved successfully");
            }

        } catch (IOException e) {
            log.error("I/O error while writing API documentation to file" + e.getMessage());
            throw new APIExportException("I/O error while writing API documentation to file", e);
        } catch (RegistryException e) {
            log.error("Error while retrieving documentation " + e.getMessage());
            throw new APIExportException("Error while retrieving documentation", e);
        } finally {
            IOUtils.closeQuietly(fileInputStream);
            IOUtils.closeQuietly(outputStream);
        }
    }

    /**
     * Retrieve WSDL for the exporting API and store it in the archive directory
     *
     * @param apiIdentifier ID of the requesting API
     * @param registry      Current tenant registry
     * @throws APIExportException If an error occurs while retrieving WSDL from the registry or
     *                            storing in the archive directory
     */
    public static void exportWSDL(APIIdentifier apiIdentifier, Registry registry) throws APIExportException {

        InputStream wsdlStream = null;
        OutputStream outputStream = null;
        String archivePath = archiveBasePath
                .concat("/" + apiIdentifier.getApiName() + "-" + apiIdentifier.getVersion());

        try {
            String wsdlPath = APIConstants.API_WSDL_RESOURCE_LOCATION + apiIdentifier.getProviderName() + "--"
                    + apiIdentifier.getApiName() + apiIdentifier.getVersion() + ".wsdl";
            if (registry.resourceExists(wsdlPath)) {
                createDirectory(archivePath + File.separator + "WSDL");

                Resource wsdl = registry.get(wsdlPath);

                wsdlStream = wsdl.getContentStream();

                outputStream = new FileOutputStream(archivePath + File.separator + "WSDL" + File.separator
                        + apiIdentifier.getApiName() + "-" + apiIdentifier.getVersion() + ".wsdl");

                IOUtils.copy(wsdlStream, outputStream);

                if (log.isDebugEnabled()) {
                    log.debug("WSDL file retrieved successfully");
                }
            }
        } catch (IOException e) {
            log.error("I/O error while writing WSDL to file" + e.getMessage());
            throw new APIExportException("I/O error while writing WSDL to file", e);
        } catch (RegistryException e) {
            log.error("Error while retrieving WSDL " + e.getMessage());
            throw new APIExportException("Error while retrieving WSDL", e);
        } finally {
            IOUtils.closeQuietly(wsdlStream);
            IOUtils.closeQuietly(outputStream);
        }
    }

    /**
     * Retrieve available custom sequences for the exporting API
     *
     * @param api           exporting API
     * @param apiIdentifier ID of the requesting API
     * @param registry      current tenant registry
     * @throws APIExportException If an error occurs while exporting sequences
     */
    public static void exportSequences(API api, APIIdentifier apiIdentifier, int tenantId, Registry registry)
            throws APIExportException {

        Map<String, String> sequences = new HashMap<String, String>();

        if (api.getInSequence() != null) {
            sequences.put(APIConstants.API_CUSTOM_SEQUENCE_TYPE_IN, api.getInSequence());
        }

        if (api.getOutSequence() != null) {
            sequences.put(APIConstants.API_CUSTOM_SEQUENCE_TYPE_OUT, api.getOutSequence());
        }

        if (api.getOutSequence() != null) {
            sequences.put(APIConstants.API_CUSTOM_SEQUENCE_TYPE_FAULT, api.getFaultSequence());
        }

        if (!sequences.isEmpty()) {
            String archivePath = archiveBasePath
                    .concat(File.separator + apiIdentifier.getApiName() + "-" + apiIdentifier.getVersion());
            createDirectory(archivePath + File.separator + "Sequences");

            String sequenceName;
            String direction;
            for (Map.Entry<String, String> sequence : sequences.entrySet()) {
                sequenceName = sequence.getValue();
                direction = sequence.getKey();
                AbstractMap.SimpleEntry<String, OMElement> sequenceDetails;
                if (sequenceName != null) {
                    if (APIConstants.API_CUSTOM_SEQUENCE_TYPE_IN.equalsIgnoreCase(direction)) {
                        sequenceDetails = getCustomSequence(sequenceName, APIConstants.API_CUSTOM_SEQUENCE_TYPE_IN,
                                registry);
                        writeSequenceToFile(sequenceDetails, APIConstants.API_CUSTOM_SEQUENCE_TYPE_IN,
                                apiIdentifier);
                    } else if (APIConstants.API_CUSTOM_SEQUENCE_TYPE_OUT.equalsIgnoreCase(direction)) {
                        sequenceDetails = getCustomSequence(sequenceName, APIConstants.API_CUSTOM_SEQUENCE_TYPE_OUT,
                                registry);
                        writeSequenceToFile(sequenceDetails, APIConstants.API_CUSTOM_SEQUENCE_TYPE_OUT,
                                apiIdentifier);
                    } else {
                        sequenceDetails = getCustomSequence(sequenceName,
                                APIConstants.API_CUSTOM_SEQUENCE_TYPE_FAULT, registry);
                        writeSequenceToFile(sequenceDetails, APIConstants.API_CUSTOM_SEQUENCE_TYPE_FAULT,
                                apiIdentifier);
                    }
                }
            }
        }
    }

    /**
     * Retrieve custom sequence details from the registry
     *
     * @param sequenceName Name of the sequence
     * @param type         Sequence type
     * @param registry     Current tenant registry
     * @return Registry resource name of the sequence and its content
     * @throws APIExportException If an error occurs while retrieving registry elements
     */
    private static AbstractMap.SimpleEntry<String, OMElement> getCustomSequence(String sequenceName, String type,
            Registry registry) throws APIExportException {
        AbstractMap.SimpleEntry<String, OMElement> sequenceDetails;

        org.wso2.carbon.registry.api.Collection seqCollection = null;

        try {
            if (APIConstants.API_CUSTOM_SEQUENCE_TYPE_IN.equals(type)) {
                seqCollection = (org.wso2.carbon.registry.api.Collection) registry
                        .get(APIConstants.API_CUSTOM_INSEQUENCE_LOCATION);
            } else if (APIConstants.API_CUSTOM_SEQUENCE_TYPE_OUT.equals(type)) {
                seqCollection = (org.wso2.carbon.registry.api.Collection) registry
                        .get(APIConstants.API_CUSTOM_OUTSEQUENCE_LOCATION);
            } else if (APIConstants.API_CUSTOM_SEQUENCE_TYPE_FAULT.equals(type)) {
                seqCollection = (org.wso2.carbon.registry.api.Collection) registry
                        .get(APIConstants.API_CUSTOM_FAULTSEQUENCE_LOCATION);
            }

            if (seqCollection != null) {
                String[] childPaths = seqCollection.getChildren();

                for (int i = 0; i < childPaths.length; i++) {
                    Resource sequence = registry.get(childPaths[i]);
                    OMElement seqElment = APIUtil.buildOMElement(sequence.getContentStream());
                    if (sequenceName.equals(seqElment.getAttributeValue(new QName("name")))) {
                        String sequenceFileName = sequence.getPath().substring(sequence.getPath().lastIndexOf('/'));
                        sequenceDetails = new AbstractMap.SimpleEntry<String, OMElement>(sequenceFileName,
                                seqElment);
                        return sequenceDetails;
                    }
                }
            }

        } catch (RegistryException e) {
            log.error("Error while retrieving sequence from the registry " + e.getMessage());
            throw new APIExportException("Error while retrieving sequence from the registry", e);
        } catch (Exception e) { //APIUtil.buildOMElement() throws a generic exception
            log.error("Error while reading sequence content " + e.getMessage());
            throw new APIExportException("Error while reading sequence content", e);
        }

        return null;

    }

    /**
     * Store custom sequences in the archive directory
     *
     * @param sequenceDetails   Details of the sequence
     * @param direction      Direction of the sequence "in", "out" or "fault"
     * @param apiIdentifier  ID of the requesting API
     * @throws APIExportException If an error occurs while serializing XML stream or storing in
     *                            archive directory
     */
    private static void writeSequenceToFile(AbstractMap.SimpleEntry<String, OMElement> sequenceDetails,
            String direction, APIIdentifier apiIdentifier) throws APIExportException {
        OutputStream outputStream = null;
        String archivePath = archiveBasePath
                .concat(File.separator + apiIdentifier.getApiName() + "-" + apiIdentifier.getVersion())
                + File.separator + "Sequences" + File.separator;

        String pathToExportedSequence = archivePath + direction + "-sequence" + File.separator;
        String sequenceFileName = sequenceDetails.getKey();
        OMElement sequenceConfig = sequenceDetails.getValue();
        String exportedSequenceFile = pathToExportedSequence + sequenceFileName;
        try {
            createDirectory(pathToExportedSequence);
            outputStream = new FileOutputStream(exportedSequenceFile);
            sequenceConfig.serialize(outputStream);

            if (log.isDebugEnabled()) {
                log.debug("Sequence retrieved successfully");
            }

        } catch (FileNotFoundException e) {
            log.error("Unable to find file" + e.getMessage());
            throw new APIExportException("Unable to find file: " + exportedSequenceFile, e);
        } catch (XMLStreamException e) {
            log.error("Error while processing XML stream" + e.getMessage());
            throw new APIExportException("Error while processing XML stream", e);
        } finally {
            IOUtils.closeQuietly(outputStream);
        }
    }

    /**
     * Create directory at the given path
     *
     * @param path Path of the directory
     * @throws APIExportException If directory creation failed
     */
    public static void createDirectory(String path) throws APIExportException {
        if (path != null) {
            File file = new File(path);
            if (!file.exists() && !file.mkdirs()) {
                log.error("Error while creating directory : " + path);
                throw new APIExportException("Directory creation failed " + path);
            }
        }
    }

    /**
     * Retrieve meta information of the API to export
     * URL template information are stored in swagger.json definition while rest of the required
     * data are in api.json
     *
     * @param apiToReturn API to be exported
     * @param registry    Current tenant registry
     * @throws APIExportException If an error occurs while exporting meta information
     */
    private static void exportMetaInformation(API apiToReturn, Registry registry) throws APIExportException {
        APIDefinition definitionFromSwagger20 = new APIDefinitionFromSwagger20();
        String archivePath = archiveBasePath
                .concat(File.separator + apiToReturn.getId().getApiName() + "-" + apiToReturn.getId().getVersion());

        createDirectory(archivePath + File.separator + "Meta-information");
        //Remove unnecessary data from exported Api
        cleanApiDataToExport(apiToReturn);

        Gson gson = new GsonBuilder().setPrettyPrinting().create();
        String apiInJson = gson.toJson(apiToReturn);
        writeFile(archivePath + File.separator + "Meta-information" + File.separator + "api.json", apiInJson);

        try {
            String swaggerDefinition = definitionFromSwagger20.getAPIDefinition(apiToReturn.getId(), registry);
            JsonParser parser = new JsonParser();
            JsonObject json = parser.parse(swaggerDefinition).getAsJsonObject();
            String formattedSwaggerJson = gson.toJson(json);
            writeFile(archivePath + File.separator + "Meta-information" + File.separator + "swagger.json",
                    formattedSwaggerJson);

            if (log.isDebugEnabled()) {
                log.debug("Meta information retrieved successfully");
            }

        } catch (APIManagementException e) {
            log.error("Error while retrieving Swagger definition" + e.getMessage());
            throw new APIExportException("Error while retrieving Swagger definition", e);
        }
    }

    /**
     * Clean api by removing unnecessary details
     *
     * @param api api to be exported
     */
    private static void cleanApiDataToExport(API api) {
        // Thumbnail will be set according to the importing environment. Therefore current URL is removed
        api.setThumbnailUrl(null);
        // Swagger.json contains complete details about scopes and URI templates. Therefore scope and URI template
        // details are removed from api.json
        api.setScopes(new TreeSet<Scope>());
        api.setUriTemplates(new TreeSet<URITemplate>());
    }

    /**
     * Write content to file
     *
     * @param path    Location of the file
     * @param content Content to be written
     * @throws APIExportException If an error occurs while writing to file
     */
    private static void writeFile(String path, String content) throws APIExportException {
        FileWriter writer = null;

        try {
            writer = new FileWriter(path);
            IOUtils.copy(new StringReader(content), writer);
        } catch (IOException e) {
            log.error("I/O error while writing to file" + e.getMessage());
            throw new APIExportException("I/O error while writing to file", e);
        } finally {
            IOUtils.closeQuietly(writer);
        }

    }

}