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

org.flowable.ui.modeler.service.AppDefinitionImportService 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.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.cmmn.editor.json.converter.CmmnJsonConverter;
import org.flowable.cmmn.model.CmmnModel;
import org.flowable.dmn.editor.converter.DmnJsonConverter;
import org.flowable.dmn.editor.converter.DmnJsonConverterUtil;
import org.flowable.editor.language.json.converter.BpmnJsonConverter;
import org.flowable.editor.language.json.converter.util.CollectionUtils;
import org.flowable.ui.common.security.SecurityUtils;
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.AppDefinitionPublishRepresentation;
import org.flowable.ui.modeler.model.AppDefinitionRepresentation;
import org.flowable.ui.modeler.model.AppDefinitionUpdateResultRepresentation;
import org.flowable.ui.modeler.repository.ModelRepository;
import org.flowable.ui.modeler.serviceapi.ModelService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

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

@Service
@Transactional
public class AppDefinitionImportService {

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

    @Autowired
    protected AppDefinitionPublishService appDefinitionPublishService;

    @Autowired
    protected ModelService modelService;

    @Autowired
    protected ModelRepository modelRepository;

    @Autowired
    protected ObjectMapper objectMapper;

    protected BpmnJsonConverter bpmnJsonConverter = new BpmnJsonConverter();
    protected CmmnJsonConverter cmmnJsonConverter = new CmmnJsonConverter();
    protected DmnJsonConverter dmnJsonConverter = new DmnJsonConverter();

    public AppDefinitionRepresentation importAppDefinition(HttpServletRequest request, MultipartFile file) {
        try {
            InputStream is = file.getInputStream();
            String fileName = file.getOriginalFilename();

            ConverterContext converterContext = new ConverterContext(modelService, objectMapper);
            return importAppDefinition(is, fileName, null, converterContext);

        } catch (IOException e) {
            throw new InternalServerErrorException("Error loading file", e);
        }
    }

    public AppDefinitionRepresentation importAppDefinitionNewVersion(HttpServletRequest request, MultipartFile file, String appDefId) {
        try {
            InputStream is = file.getInputStream();
            String fileName = file.getOriginalFilename();
            Model appModel = modelService.getModel(appDefId);
            if (!appModel.getModelType().equals(Model.MODEL_TYPE_APP)) {
                throw new BadRequestException("No app definition found for id " + appDefId);
            }

            AppDefinitionRepresentation appDefinition = createAppDefinitionRepresentation(appModel);

            ConverterContext converterContext = new ConverterContext(modelService, objectMapper);
            if (appDefinition.getDefinition() != null && CollectionUtils.isNotEmpty(appDefinition.getDefinition().getModels())) {
                for (AppModelDefinition modelDef : appDefinition.getDefinition().getModels()) {
                    Model processModel = modelService.getModel(modelDef.getId());

                    List referencedModels = modelRepository.findByParentModelId(processModel.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 referencedDecisionTableChildModels = modelRepository.findByParentModelId(childModel.getId());
                            for (Model decisionTableChildModel : referencedDecisionTableChildModels) {
                                converterContext.addDecisionTableModel(decisionTableChildModel);
                            }
                        }
                    }

                    converterContext.addProcessModel(processModel);
                }
            }
            
            if (appDefinition.getDefinition() != null && CollectionUtils.isNotEmpty(appDefinition.getDefinition().getCmmnModels())) {
                for (AppModelDefinition modelDef : appDefinition.getDefinition().getCmmnModels()) {
                    Model caseModel = modelService.getModel(modelDef.getId());

                    List referencedModels = modelRepository.findByParentModelId(caseModel.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 referencedDecisionTableChildModels = modelRepository.findByParentModelId(childModel.getId());
                            for (Model decisionTableChildModel : referencedDecisionTableChildModels) {
                                converterContext.addDecisionTableModel(decisionTableChildModel);
                            }
                        }
                    }

                    converterContext.addCaseModel(caseModel);
                }
            }

            return importAppDefinition(is, fileName, appModel, converterContext);

        } catch (IOException e) {
            throw new InternalServerErrorException("Error loading file", e);
        }
    }

    protected AppDefinitionRepresentation importAppDefinition(InputStream is, String fileName,
            Model existingAppModel, ConverterContext converterContext) {

        if (fileName != null && (fileName.endsWith(".zip"))) {
            Model appDefinitionModel = readZipFile(is, converterContext);
            if (StringUtils.isNotEmpty(appDefinitionModel.getKey()) && StringUtils.isNotEmpty(appDefinitionModel.getModelEditorJson())) {

                importForms(converterContext);
                importDecisionTables(converterContext);
                importDecisionServices(converterContext);
                importBpmnModels(converterContext);
                importCmmnModels(converterContext);

                return importAppDefinitionModel(appDefinitionModel, existingAppModel, converterContext);

            } else {
                throw new BadRequestException("Could not find app definition json");
            }

        } else {
            throw new BadRequestException("Invalid file name, only .zip files are supported not " + fileName);
        }
    }

    public AppDefinitionUpdateResultRepresentation publishAppDefinition(String modelId, AppDefinitionPublishRepresentation publishModel) {

        String currentUserId = SecurityUtils.getCurrentUserId();
        Model appModel = modelService.getModel(modelId);

        // Create pojo representation of the model and the json
        AppDefinitionRepresentation appDefinitionRepresentation = createAppDefinitionRepresentation(appModel);
        AppDefinitionUpdateResultRepresentation result = new AppDefinitionUpdateResultRepresentation();

        // Actual publication
        appDefinitionPublishService.publishAppDefinition(publishModel.getComment(), appModel, currentUserId);

        result.setAppDefinition(appDefinitionRepresentation);
        return result;

    }

    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 Model readZipFile(InputStream inputStream, ConverterContext converterContext) {

        Model appDefinitionModel = null;
        ZipInputStream zipInputStream = null;
        try {
            zipInputStream = new ZipInputStream(inputStream);
            ZipEntry zipEntry = zipInputStream.getNextEntry();
            while (zipEntry != null) {
                String zipEntryName = zipEntry.getName();
                if (zipEntryName.endsWith("json") || zipEntryName.endsWith("png")) {

                    String modelFileName = null;
                    if (zipEntryName.contains("/")) {
                        modelFileName = zipEntryName.substring(zipEntryName.indexOf('/') + 1);
                    } else {
                        modelFileName = zipEntryName;
                    }

                    if (modelFileName.endsWith(".png")) {
                        converterContext.getModelKeyToThumbnailMap().put(modelFileName.replace(".png", ""), IOUtils.toByteArray(zipInputStream));

                    } else {
                        modelFileName = modelFileName.replace(".json", "");
                        String json = IOUtils.toString(zipInputStream, StandardCharsets.UTF_8);

                        if (zipEntryName.startsWith("bpmn-models/")) {
                            converterContext.getProcessKeyToJsonStringMap().put(modelFileName, json);
                            
                        } else if (zipEntryName.startsWith("cmmn-models/")) {
                            converterContext.getCaseKeyToJsonStringMap().put(modelFileName, json);

                        } else if (zipEntryName.startsWith("form-models/")) {
                            converterContext.getFormKeyToJsonStringMap().put(modelFileName, json);

                        } else if (zipEntryName.startsWith("decision-table-models/")) {
                            converterContext.getDecisionTableKeyToJsonStringMap().put(modelFileName, json);

                        } else if (zipEntryName.startsWith("decision-service-models/")) {
                            converterContext.getDecisionServiceKeyToJsonStringMap().put(modelFileName, json);

                        } else if (!zipEntryName.contains("/")) {
                            appDefinitionModel = createModelObject(json, Model.MODEL_TYPE_APP);
                        }
                    }
                }

                zipEntry = zipInputStream.getNextEntry();
            }
        } catch (Exception e) {
            LOGGER.error("Error reading app definition zip file", e);
            throw new InternalServerErrorException("Error reading app definition zip file");

        } finally {
            if (zipInputStream != null) {
                try {
                    zipInputStream.closeEntry();
                } catch (Exception e) {
                }
                
                try {
                    zipInputStream.close();
                } catch (Exception e) {
                }
            }
        }

        return appDefinitionModel;
    }

    protected void importForms(ConverterContext converterContext) {

        Map formMap = converterContext.getFormKeyToJsonStringMap();
        Map thumbnailMap = converterContext.getModelKeyToThumbnailMap();

        for (String formKey : formMap.keySet()) {

            Model formModel = createModelObject(formMap.get(formKey), Model.MODEL_TYPE_FORM);

            Model existingModel = converterContext.getFormModelByKey(formModel.getKey());
            Model updatedFormModel = null;
            if (existingModel != null) {
                byte[] imageBytes = null;
                if (thumbnailMap.containsKey(formKey)) {
                    imageBytes = thumbnailMap.get(formKey);
                }
                updatedFormModel = modelService.saveModel(existingModel, formModel.getModelEditorJson(), imageBytes,
                        true, "App definition import", SecurityUtils.getCurrentUserId());

            } else {
                formModel.setId(null);
                updatedFormModel = modelService.createModel(formModel, SecurityUtils.getCurrentUserId());

                if (thumbnailMap.containsKey(formKey)) {
                    updatedFormModel.setThumbnail(thumbnailMap.get(formKey));
                    modelRepository.save(updatedFormModel);
                }
            }

            converterContext.addFormModel(updatedFormModel, formModel.getId());
        }
    }

    protected void importDecisionTables(ConverterContext converterContext) {

        Map decisionTableMap = converterContext.getDecisionTableKeyToJsonStringMap();
        Map thumbnailMap = converterContext.getModelKeyToThumbnailMap();

        String currentUserId = SecurityUtils.getCurrentUserId();

        for (String decisionTableKey : decisionTableMap.keySet()) {

            Model decisionTableModel = createModelObject(decisionTableMap.get(decisionTableKey), Model.MODEL_TYPE_DECISION_TABLE);

            // migrate to new version
            DecisionTableModelConversionUtil.convertModelToV3(decisionTableModel);

            String oldDecisionTableId = decisionTableModel.getId();

            Model existingModel = converterContext.getDecisionTableModelByKey(decisionTableModel.getKey());
            Model updatedDecisionTableModel = null;
            if (existingModel != null) {
                byte[] imageBytes = null;
                if (thumbnailMap.containsKey(decisionTableKey)) {
                    imageBytes = thumbnailMap.get(decisionTableKey);
                }
                updatedDecisionTableModel = modelService.saveModel(existingModel, decisionTableModel.getModelEditorJson(), imageBytes,
                        true, "App definition import", currentUserId);

            } else {
                decisionTableModel.setId(null);
                updatedDecisionTableModel = modelService.createModel(decisionTableModel, currentUserId);

                if (thumbnailMap.containsKey(decisionTableKey)) {
                    updatedDecisionTableModel.setThumbnail(thumbnailMap.get(decisionTableKey));
                    modelRepository.save(updatedDecisionTableModel);
                }
            }

            converterContext.addDecisionTableModel(updatedDecisionTableModel, oldDecisionTableId);
        }
    }

    protected void importDecisionServices(ConverterContext converterContext) {

        Map decisionServicesMap = converterContext.getDecisionServiceKeyToJsonStringMap();
        Map thumbnailMap = converterContext.getModelKeyToThumbnailMap();

        String currentUserId = SecurityUtils.getCurrentUserId();
        for (String decisionServiceKey : decisionServicesMap.keySet()) {
            Model decisionServiceModel = createModelObject(decisionServicesMap.get(decisionServiceKey), Model.MODEL_TYPE_DECISION_SERVICE);

            String oldDecisionServiceId = decisionServiceModel.getId();
            ObjectNode decisionServiceModelNode;
            try {
                decisionServiceModelNode = (ObjectNode) objectMapper.readTree(decisionServiceModel.getModelEditorJson());
            } catch (Exception e) {
                LOGGER.error("Error reading decision service json for {}", decisionServiceKey, e);
                throw new InternalServerErrorException("Error reading decision service json for " + decisionServiceKey);
            }

            // remove modelId from import json
            // and update decision table references
            decisionServiceModelNode.remove("modelId");
            DmnJsonConverterUtil.updateDecisionTableModelReferences(decisionServiceModelNode, converterContext);
            String updatedDecisionServiceJson = decisionServiceModelNode.toString();

            Model existingModel = converterContext.getDecisionServiceModelByKey(decisionServiceModel.getKey());
            Model updatedDecisionServiceModel;
            if (existingModel != null) {
                byte[] imageBytes = null;
                if (thumbnailMap.containsKey(decisionServiceKey)) {
                    imageBytes = thumbnailMap.get(decisionServiceKey);
                }
                existingModel.setModelEditorJson(updatedDecisionServiceJson);
                updatedDecisionServiceModel = modelService.saveModel(existingModel, existingModel.getModelEditorJson(), imageBytes,
                    true, "App definition import", currentUserId);

            } else {
                decisionServiceModel.setId(null);
                decisionServiceModel.setModelEditorJson(updatedDecisionServiceJson);

                updatedDecisionServiceModel = modelService.createModel(decisionServiceModel, currentUserId);

                if (thumbnailMap.containsKey(decisionServiceKey)) {
                    updatedDecisionServiceModel.setThumbnail(thumbnailMap.get(decisionServiceKey));
                    modelRepository.save(updatedDecisionServiceModel);
                }
            }

            converterContext.addDecisionServiceModel(updatedDecisionServiceModel, oldDecisionServiceId);
        }
    }

    protected void importBpmnModels(ConverterContext converterContext) {

        Map bpmnModelMap = converterContext.getProcessKeyToJsonStringMap();
        Map thumbnailMap = converterContext.getModelKeyToThumbnailMap();

        String currentUserId = SecurityUtils.getCurrentUserId();

        for (String bpmnModelKey : bpmnModelMap.keySet()) {

            String bpmnModelJson = bpmnModelMap.get(bpmnModelKey);
            Model bpmnModelObject = createModelObject(bpmnModelJson, Model.MODEL_TYPE_BPMN);
            String oldBpmnModelId = bpmnModelObject.getId();

            JsonNode bpmnModelNode = null;
            try {
                bpmnModelNode = objectMapper.readTree(bpmnModelObject.getModelEditorJson());
            } catch (Exception e) {
                LOGGER.error("Error reading BPMN json for {}", bpmnModelKey, e);
                throw new InternalServerErrorException("Error reading BPMN json for " + bpmnModelKey);
            }

            BpmnModel bpmnModel = bpmnJsonConverter.convertToBpmnModel(bpmnModelNode, converterContext);
            String updatedBpmnJson = bpmnJsonConverter.convertToJson(bpmnModel, converterContext).toString();

            Model existingModel = converterContext.getProcessModelByKey(bpmnModelKey);
            Model updatedProcessModel = null;
            if (existingModel != null) {
                byte[] imageBytes = null;
                if (thumbnailMap.containsKey(bpmnModelKey)) {
                    imageBytes = thumbnailMap.get(bpmnModelKey);
                }

                existingModel.setModelEditorJson(updatedBpmnJson);

                updatedProcessModel = modelService.saveModel(existingModel, existingModel.getModelEditorJson(), imageBytes, true, "App definition import",
                        currentUserId);

            } else {
                bpmnModelObject.setId(null);
                bpmnModelObject.setModelEditorJson(updatedBpmnJson);
                updatedProcessModel = modelService.createModel(bpmnModelObject, currentUserId);

                if (thumbnailMap.containsKey(bpmnModelKey)) {
                    updatedProcessModel.setThumbnail(thumbnailMap.get(bpmnModelKey));
                    modelService.saveModel(updatedProcessModel);
                }
            }

            converterContext.addProcessModel(updatedProcessModel, oldBpmnModelId);
        }
    }
    
    protected void importCmmnModels(ConverterContext converterContext) {

        Map cmmnModelMap = converterContext.getCaseKeyToJsonStringMap();
        Map thumbnailMap = converterContext.getModelKeyToThumbnailMap();

        String currentUserId = SecurityUtils.getCurrentUserId();

        for (String cmmnModelKey : cmmnModelMap.keySet()) {

            String cmmnModelJson = cmmnModelMap.get(cmmnModelKey);
            Model cmmnModelObject = createModelObject(cmmnModelJson, Model.MODEL_TYPE_CMMN);
            String oldCmmnModelId = cmmnModelObject.getId();

            JsonNode cmmnModelNode = null;
            try {
                cmmnModelNode = objectMapper.readTree(cmmnModelObject.getModelEditorJson());
            } catch (Exception e) {
                LOGGER.error("Error reading CMMN json for {}", cmmnModelKey, e);
                throw new InternalServerErrorException("Error reading CMMN json for " + cmmnModelKey);
            }

            CmmnModel cmmnModel = cmmnJsonConverter.convertToCmmnModel(cmmnModelNode, converterContext);
            String updatedCmmnJson = cmmnJsonConverter.convertToJson(cmmnModel, converterContext).toString();

            Model existingModel = converterContext.getCaseModelByKey(cmmnModelKey);
            Model updatedCaseModel = null;
            if (existingModel != null) {
                byte[] imageBytes = null;
                if (thumbnailMap.containsKey(cmmnModelKey)) {
                    imageBytes = thumbnailMap.get(cmmnModelKey);
                }

                existingModel.setModelEditorJson(updatedCmmnJson);

                updatedCaseModel = modelService.saveModel(existingModel, existingModel.getModelEditorJson(), imageBytes, true, "App definition import",
                        currentUserId);

            } else {
                cmmnModelObject.setId(null);
                cmmnModelObject.setModelEditorJson(updatedCmmnJson);
                updatedCaseModel = modelService.createModel(cmmnModelObject, currentUserId);

                if (thumbnailMap.containsKey(cmmnModelKey)) {
                    updatedCaseModel.setThumbnail(thumbnailMap.get(cmmnModelKey));
                    modelService.saveModel(updatedCaseModel);
                }
            }

            converterContext.addCaseModel(updatedCaseModel, oldCmmnModelId);
        }
    }

    protected AppDefinitionRepresentation importAppDefinitionModel(Model appDefinitionModel,
            Model existingAppModel, ConverterContext converterContext) {

        AppDefinition appDefinition = null;
        try {
            appDefinition = objectMapper.readValue(appDefinitionModel.getModelEditorJson(), AppDefinition.class);
        } catch (Exception e) {
            LOGGER.error("Error reading app definition {}", appDefinitionModel.getName(), e);
            throw new BadRequestException("Error reading app definition", e);
        }

        if (appDefinition.getModels() != null) {
            for (AppModelDefinition modelDef : appDefinition.getModels()) {
                Model newModel = converterContext.getProcessModelById(modelDef.getId());
                if (newModel != null) {
                    modelDef.setId(newModel.getId());
                    modelDef.setName(newModel.getName());
                    modelDef.setCreatedBy(newModel.getCreatedBy());
                    modelDef.setLastUpdatedBy(newModel.getLastUpdatedBy());
                    modelDef.setLastUpdated(newModel.getLastUpdated());
                    modelDef.setVersion(newModel.getVersion());
                }
            }
        }
        
        if (appDefinition.getCmmnModels() != null) {
            for (AppModelDefinition modelDef : appDefinition.getCmmnModels()) {
                Model newModel = converterContext.getCaseModelById(modelDef.getId());
                if (newModel != null) {
                    modelDef.setId(newModel.getId());
                    modelDef.setName(newModel.getName());
                    modelDef.setCreatedBy(newModel.getCreatedBy());
                    modelDef.setLastUpdatedBy(newModel.getLastUpdatedBy());
                    modelDef.setLastUpdated(newModel.getLastUpdated());
                    modelDef.setVersion(newModel.getVersion());
                }
            }
        }

        String currentUserId = SecurityUtils.getCurrentUserId();

        try {
            String updatedAppDefinitionJson = objectMapper.writeValueAsString(appDefinition);

            if (existingAppModel != null) {
                appDefinitionModel = modelService.saveModel(existingAppModel, updatedAppDefinitionJson, null, true, "App definition import",
                        currentUserId);
            } else {
                appDefinitionModel.setId(null);
                appDefinitionModel.setModelEditorJson(updatedAppDefinitionJson);
                appDefinitionModel = modelService.createModel(appDefinitionModel, currentUserId);
            }

            AppDefinitionRepresentation result = new AppDefinitionRepresentation(appDefinitionModel);
            result.setDefinition(appDefinition);
            return result;

        } catch (Exception e) {
            LOGGER.error("Error storing app definition", e);
            throw new InternalServerErrorException("Error storing app definition");
        }
    }

    protected Model createModelObject(String modelJson, int modelType) {
        try {
            JsonNode modelNode = objectMapper.readTree(modelJson);
            Model model = new Model();
            model.setId(modelNode.get("id").asText());
            model.setName(modelNode.get("name").asText());
            model.setKey(modelNode.get("key").asText());

            JsonNode descriptionNode = modelNode.get("description");
            if (descriptionNode != null && !descriptionNode.isNull()) {
                model.setDescription(descriptionNode.asText());
            }

            model.setModelEditorJson(modelNode.get("editorJson").toString());
            model.setModelType(modelType);

            return model;

        } catch (Exception e) {
            LOGGER.error("Error reading model json", e);
            throw new InternalServerErrorException("Error reading model json");
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy