All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.flowable.ui.modeler.service.AppDefinitionExportService Maven / Gradle / Ivy

The newest version!
/* 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 org.flowable.ui.modeler.service;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;

import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.StartEvent;
import org.flowable.cmmn.model.CmmnModel;
import org.flowable.dmn.model.DmnDefinition;
import org.flowable.editor.language.json.converter.BpmnJsonConverter;
import org.flowable.editor.language.json.converter.util.CollectionUtils;
import org.flowable.ui.common.service.exception.BadRequestException;
import org.flowable.ui.common.service.exception.InternalServerErrorException;
import org.flowable.ui.modeler.domain.AbstractModel;
import org.flowable.ui.modeler.domain.AppDefinition;
import org.flowable.ui.modeler.domain.AppModelDefinition;
import org.flowable.ui.modeler.domain.Model;
import org.flowable.ui.modeler.model.AppDefinitionRepresentation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.util.UriUtils;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;

@Service
@Transactional
public class AppDefinitionExportService extends BaseAppDefinitionService {

    private static final Logger LOGGER = LoggerFactory.getLogger(AppDefinitionExportService.class);

    protected BpmnJsonConverter bpmnJsonConverter = new BpmnJsonConverter();

    public void exportAppDefinition(HttpServletResponse response, String modelId) throws IOException {

        if (modelId == null) {
            throw new BadRequestException("No application definition id provided");
        }

        Model appModel = modelService.getModel(modelId);
        AppDefinitionRepresentation appRepresentation = createAppDefinitionRepresentation(appModel);

        createAppDefinitionZip(response, appModel, appRepresentation);
    }

    public void exportDeployableAppDefinition(HttpServletResponse response, String modelId) throws IOException {

        if (modelId == null) {
            throw new BadRequestException("No application definition id provided");
        }

        Model appModel = modelService.getModel(modelId);
        AppDefinitionRepresentation appRepresentation = createAppDefinitionRepresentation(appModel);

        createAppDefinitionBar(response, appModel, appRepresentation);
    }

    protected void createAppDefinitionZip(HttpServletResponse response, Model appModel, AppDefinitionRepresentation appDefinition) {
        try {
            response.setHeader("Content-Disposition", "attachment; filename=\"" + appDefinition.getName() + ".zip\"; filename*=utf-8''" + UriUtils.encode(appDefinition.getName() + ".zip", "utf-8"));

            ServletOutputStream servletOutputStream = response.getOutputStream();
            response.setContentType("application/zip");

            ZipOutputStream zipOutputStream = new ZipOutputStream(servletOutputStream);

            createZipEntry(zipOutputStream, appModel.getName() + ".json", createModelEntryJson(appModel));

            ConverterContext converterContext = new ConverterContext(modelService, objectMapper);

            List modelDefinitions = appDefinition.getDefinition().getModels();
            if (CollectionUtils.isNotEmpty(modelDefinitions)) {
                createBpmnZipEntries(modelDefinitions, zipOutputStream, converterContext);
            }

            List cmmnModelDefinitions = appDefinition.getDefinition().getCmmnModels();
            if (CollectionUtils.isNotEmpty(cmmnModelDefinitions)) {
                createCmmnZipEntries(cmmnModelDefinitions, zipOutputStream, converterContext);
            }

            Collection allProcessModels = converterContext.getAllProcessModels();
            if (allProcessModels != null) {
                createBpmnZipEntries(allProcessModels, zipOutputStream, converterContext);
            }

            Collection allCaseModels = converterContext.getAllCaseModels();
            if (allCaseModels != null) {
                createCmmnZipEntries(allCaseModels, zipOutputStream, converterContext);
            }

            Collection allFormModels = converterContext.getAllFormModels();
            if (allFormModels != null) {
                for (Model formModel : allFormModels) {
                    createZipEntries(formModel, "form-models", zipOutputStream);
                }
            }

            Collection allDirectDecisionTableModels = converterContext.getAllDecisionTableModels();
            Collection allReferencedDecisionTableModels = converterContext.getAllReferencedDecisionTableModels();
            Collection allDecisionServiceModels = converterContext.getAllDecisionServiceModels();

            allReferencedDecisionTableModels
                    .forEach(referencedDecisionTableModel -> converterContext.getDecisionTableKeyToJsonStringMap().put(
                            referencedDecisionTableModel.getKey(), referencedDecisionTableModel.getModelEditorJson()));

            Collection allDecisionTableModels = Stream.of(allDirectDecisionTableModels, allReferencedDecisionTableModels)
                    .flatMap(Collection::stream)
                    .collect(Collectors.toMap(
                            Model::getKey,
                            d -> d,
                            (Model x, Model y) -> x))
                    .values();

            createDecisionTableZipEntries(allDecisionTableModels, converterContext, zipOutputStream);
            createDecisionServiceZipEntries(allDecisionServiceModels, converterContext, zipOutputStream);

            zipOutputStream.close();

            // Flush and close stream
            servletOutputStream.flush();
            servletOutputStream.close();

        } catch (Exception e) {
            LOGGER.error("Could not generate app definition zip archive", e);
            throw new InternalServerErrorException("Could not generate app definition zip archive");
        }
    }

    protected void createDecisionTableZipEntries(Collection decisionTableModels, ConverterContext converterContext, ZipOutputStream zipOutputStream) throws Exception {
        for (Model decisionTableModel : decisionTableModels) {
            createZipEntries(decisionTableModel, "decision-table-models", zipOutputStream);
            try {
                JsonNode decisionTableNode = objectMapper.readTree(decisionTableModel.getModelEditorJson());
                DmnDefinition dmnDefinition = dmnJsonConverter.convertToDmn(decisionTableNode, decisionTableModel.getId());
                byte[] dmnXMLBytes = dmnXMLConverter.convertToXML(dmnDefinition);
                createZipEntry(zipOutputStream, "decision-table-models/" + decisionTableModel.getKey() + ".dmn", dmnXMLBytes);
            } catch (Exception e) {
                throw new InternalServerErrorException(String.format("Error converting decision table %s to XML", decisionTableModel.getName()));
            }
        }
    }

    protected void createDecisionServiceZipEntries(Collection decisionServiceModels, ConverterContext converterContext, ZipOutputStream zipOutputStream) throws Exception {
        for (Model decisionServiceModel : decisionServiceModels) {
            createZipEntries(decisionServiceModel, "decision-service-models", zipOutputStream);
            try {
                JsonNode decisionServiceNode = objectMapper.readTree(decisionServiceModel.getModelEditorJson());
                DmnDefinition dmnDefinition = dmnJsonConverter.convertToDmn(decisionServiceNode, decisionServiceModel.getId(), converterContext);
                byte[] dmnXMLBytes = dmnXMLConverter.convertToXML(dmnDefinition);
                createZipEntry(zipOutputStream, "decision-service-models/" + decisionServiceModel.getKey() + ".dmn", dmnXMLBytes);
            } catch (Exception e) {
                throw new InternalServerErrorException(String.format("Error converting decision service %s to XML", decisionServiceModel.getName()));
            }
        }
    }

    public void createAppDefinitionBar(HttpServletResponse response, Model appModel, AppDefinitionRepresentation appDefinition) {

        try {
            response.setHeader("Content-Disposition", "attachment; filename=\"" + appDefinition.getName() + ".bar\"; filename*=utf-8''" + UriUtils.encode(appDefinition.getName() + ".bar", "utf-8"));

            byte[] deployZipArtifact = createDeployableZipArtifact(appModel, appDefinition.getDefinition());

            ServletOutputStream servletOutputStream = response.getOutputStream();
            response.setContentType("application/zip");
            servletOutputStream.write(deployZipArtifact);

            // Flush and close stream
            servletOutputStream.flush();
            servletOutputStream.close();

        } catch (Exception e) {
            LOGGER.error("Could not generate app definition bar archive", e);
            throw new InternalServerErrorException("Could not generate app definition bar archive");
        }
    }

    protected void createBpmnZipEntries(List modelDefinitions, ZipOutputStream zipOutputStream,
                                        ConverterContext converterContext) throws Exception {

        for (AppModelDefinition modelDef : modelDefinitions) {
            Model model = modelService.getModel(modelDef.getId());

            List referencedModels = modelRepository.findByParentModelId(model.getId());
            for (Model childModel : referencedModels) {
                if (Model.MODEL_TYPE_FORM == childModel.getModelType()) {
                    converterContext.addFormModel(childModel);

                } else if (Model.MODEL_TYPE_DECISION_TABLE == childModel.getModelType()) {
                    converterContext.addDecisionTableModel(childModel);
                } else if (Model.MODEL_TYPE_DECISION_SERVICE == childModel.getModelType()) {
                    converterContext.addDecisionServiceModel(childModel);
                    List referencedDecisionTableModels = modelRepository.findByParentModelId(childModel.getId());
                    referencedDecisionTableModels.stream()
                            .filter(refModel -> Model.MODEL_TYPE_DECISION_TABLE == refModel.getModelType())
                            .forEach(converterContext::addReferencedDecisionTableModel);
                }
            }

            BpmnModel bpmnModel = modelService.getBpmnModel(model, converterContext);
            Map noneStartEventMap = new HashMap<>();
            postProcessFlowElements(new ArrayList<>(), noneStartEventMap, bpmnModel);

            for (Process process : bpmnModel.getProcesses()) {
                processUserTasks(process.getFlowElements(), process, noneStartEventMap);
            }

            byte[] modelXML = modelService.getBpmnXML(bpmnModel);

            // add BPMN XML model
            createZipEntry(zipOutputStream, "bpmn-models/" + model.getKey().replaceAll(" ", "") + ".bpmn", modelXML);

            // add JSON model
            createZipEntries(model, "bpmn-models", zipOutputStream);
        }
    }

    protected void createBpmnZipEntries(Collection models, ZipOutputStream zipOutputStream,
                                        ConverterContext converterContext) throws Exception {

        for (Model model : models) {

            BpmnModel bpmnModel = modelService.getBpmnModel(model, converterContext);
            Map noneStartEventMap = new HashMap<>();
            postProcessFlowElements(new ArrayList<>(), noneStartEventMap, bpmnModel);

            for (Process process : bpmnModel.getProcesses()) {
                processUserTasks(process.getFlowElements(), process, noneStartEventMap);
            }

            byte[] modelXML = modelService.getBpmnXML(bpmnModel);

            // add BPMN XML model
            createZipEntry(zipOutputStream, "bpmn-models/" + model.getKey().replaceAll(" ", "") + ".bpmn", modelXML);

            // add JSON model
            createZipEntries(model, "bpmn-models", zipOutputStream);
        }
    }

    protected void createCmmnZipEntries(List modelDefinitions, ZipOutputStream zipOutputStream, ConverterContext converterContext) throws Exception {

        for (AppModelDefinition modelDef : modelDefinitions) {
            Model model = modelService.getModel(modelDef.getId());

            List referencedModels = modelRepository.findByParentModelId(model.getId());
            for (Model childModel : referencedModels) {
                if (Model.MODEL_TYPE_FORM == childModel.getModelType()) {
                    converterContext.addFormModel(childModel);

                } else if (Model.MODEL_TYPE_DECISION_TABLE == childModel.getModelType()) {
                    converterContext.addDecisionTableModel(childModel);

                } else if (Model.MODEL_TYPE_DECISION_SERVICE == childModel.getModelType()) {
                    converterContext.addDecisionServiceModel(childModel);

                } else if (Model.MODEL_TYPE_BPMN == childModel.getModelType()) {
                    converterContext.addProcessModel(childModel);

                } else if (Model.MODEL_TYPE_CMMN == childModel.getModelType()) {
                    converterContext.addCaseModel(childModel);

                }
            }

            CmmnModel cmmnModel = modelService.getCmmnModel(model, converterContext);
            byte[] modelXML = modelService.getCmmnXML(cmmnModel);

            // add CMMN XML model
            createZipEntry(zipOutputStream, "cmmn-models/" + model.getKey().replaceAll(" ", "") + ".cmmn", modelXML);

            // add JSON model
            createZipEntries(model, "cmmn-models", zipOutputStream);
        }
    }

    protected void createCmmnZipEntries(Collection models, ZipOutputStream zipOutputStream,
                                        ConverterContext converterContext) throws Exception {

        for (Model model : models) {
            CmmnModel cmmnModel = modelService.getCmmnModel(model, converterContext);

            byte[] modelXML = modelService.getCmmnXML(cmmnModel);

            // add CMMN XML model
            createZipEntry(zipOutputStream, "cmmn-models/" + model.getKey().replaceAll(" ", "") + ".cmmn", modelXML);

            // add JSON model
            createZipEntries(model, "cmmn-models", zipOutputStream);
        }

    }

    protected void createZipEntries(Model model, String directoryName, ZipOutputStream zipOutputStream) throws Exception {
        createZipEntry(zipOutputStream, directoryName + "/" + model.getKey() + ".json", createModelEntryJson(model));

        if (model.getThumbnail() != null) {
            createZipEntry(zipOutputStream, directoryName + "/" + model.getKey() + ".png", model.getThumbnail());
        }
    }

    protected String createModelEntryJson(Model model) {
        ObjectNode modelJson = objectMapper.createObjectNode();

        modelJson.put("id", model.getId());
        modelJson.put("name", model.getName());
        modelJson.put("key", model.getKey());
        modelJson.put("description", model.getDescription());

        try {
            modelJson.set("editorJson", objectMapper.readTree(model.getModelEditorJson()));
        } catch (Exception e) {
            LOGGER.error("Error exporting model json for id {}", model.getId(), e);
            throw new InternalServerErrorException("Error exporting model json for id " + model.getId());
        }

        return modelJson.toString();
    }

    protected AppDefinitionRepresentation createAppDefinitionRepresentation(AbstractModel model) {
        AppDefinition appDefinition = null;
        try {
            appDefinition = objectMapper.readValue(model.getModelEditorJson(), AppDefinition.class);
        } catch (Exception e) {
            LOGGER.error("Error deserializing app {}", model.getId(), e);
            throw new InternalServerErrorException("Could not deserialize app definition");
        }
        AppDefinitionRepresentation result = new AppDefinitionRepresentation(model);
        result.setDefinition(appDefinition);
        return result;
    }

    protected void createZipEntry(ZipOutputStream zipOutputStream, String filename, String content) throws Exception {
        createZipEntry(zipOutputStream, filename, content.getBytes(StandardCharsets.UTF_8));
    }

    protected void createZipEntry(ZipOutputStream zipOutputStream, String filename, byte[] content) throws Exception {
        ZipEntry entry = new ZipEntry(filename);
        zipOutputStream.putNextEntry(entry);
        zipOutputStream.write(content);
        zipOutputStream.closeEntry();
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy