org.flowable.ui.modeler.service.FlowableDecisionTableService.java Source code

Java tutorial

Introduction

Here is the source code for org.flowable.ui.modeler.service.FlowableDecisionTableService.java

Source

/* 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 com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.InputStreamReader;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;

import org.apache.commons.lang3.StringUtils;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.dmn.model.DmnDefinition;
import org.flowable.dmn.xml.converter.DmnXMLConverter;
import org.flowable.editor.dmn.converter.DmnJsonConverter;
import org.flowable.idm.api.User;
import org.flowable.ui.common.model.ResultListDataRepresentation;
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.common.util.XmlUtil;
import org.flowable.ui.modeler.domain.AbstractModel;
import org.flowable.ui.modeler.domain.Model;
import org.flowable.ui.modeler.domain.ModelHistory;
import org.flowable.ui.modeler.model.DecisionTableSaveRepresentation;
import org.flowable.ui.modeler.model.ModelKeyRepresentation;
import org.flowable.ui.modeler.model.ModelRepresentation;
import org.flowable.ui.modeler.model.decisiontable.DecisionTableDefinitionRepresentation;
import org.flowable.ui.modeler.model.decisiontable.DecisionTableRepresentation;
import org.flowable.ui.modeler.repository.ModelSort;
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;

/**
 * @author erikwinlof
 */
@Service
@Transactional
public class FlowableDecisionTableService extends BaseFlowableModelService {

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

    protected static final int MIN_FILTER_LENGTH = 1;

    @Autowired
    protected ModelService modelService;

    @Autowired
    protected ObjectMapper objectMapper;

    protected DmnJsonConverter dmnJsonConverter = new DmnJsonConverter();
    protected DmnXMLConverter dmnXmlConverter = new DmnXMLConverter();

    public List<DecisionTableRepresentation> getDecisionTables(String[] decisionTableIds) {
        List<DecisionTableRepresentation> decisionTableRepresentations = new ArrayList<>();
        for (String decisionTableId : decisionTableIds) {
            Model model = getModel(decisionTableId, true, false);
            DecisionTableRepresentation decisionTableRepresentation = createDecisionTableRepresentation(model);
            decisionTableRepresentations.add(decisionTableRepresentation);
        }
        return decisionTableRepresentations;
    }

    public ResultListDataRepresentation getDecisionTables(String filter) {
        String validFilter = makeValidFilterText(filter);

        List<Model> models = null;

        if (validFilter != null) {
            models = modelRepository.findByModelTypeAndFilter(AbstractModel.MODEL_TYPE_DECISION_TABLE, validFilter,
                    ModelSort.NAME_ASC);

        } else {
            models = modelRepository.findByModelType(AbstractModel.MODEL_TYPE_DECISION_TABLE, ModelSort.NAME_ASC);
        }

        List<DecisionTableRepresentation> reps = new ArrayList<>();

        for (Model model : models) {
            reps.add(new DecisionTableRepresentation(model));
        }

        ResultListDataRepresentation result = new ResultListDataRepresentation(reps);
        result.setTotal(Long.valueOf(models.size()));
        return result;
    }

    public void exportDecisionTable(HttpServletResponse response, String decisionTableId) {
        exportDecisionTable(response, getModel(decisionTableId, true, false));
    }

    public void exportHistoricDecisionTable(HttpServletResponse response, String modelHistoryId) {
        // Get the historic model
        ModelHistory modelHistory = modelHistoryRepository.get(modelHistoryId);

        // Load model and check we have read rights
        getModel(modelHistory.getModelId(), true, false);

        exportDecisionTable(response, modelHistory);
    }

    public void exportDecisionTableHistory(HttpServletResponse response, String decisionTableId) {
        exportDecisionTable(response, getModel(decisionTableId, true, false));
    }

    protected void exportDecisionTable(HttpServletResponse response, AbstractModel decisionTableModel) {
        DecisionTableRepresentation decisionTableRepresentation = getDecisionTableRepresentation(
                decisionTableModel);

        try {

            JsonNode editorJsonNode = objectMapper.readTree(decisionTableModel.getModelEditorJson());

            // URLEncoder.encode will replace spaces with '+', to keep the actual name replacing '+' to '%20'
            String fileName = URLEncoder.encode(decisionTableRepresentation.getName(), "UTF-8").replaceAll("\\+",
                    "%20") + ".dmn";
            response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + fileName);

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

            DmnDefinition dmnDefinition = dmnJsonConverter.convertToDmn(editorJsonNode, decisionTableModel.getId(),
                    decisionTableModel.getVersion(), decisionTableModel.getLastUpdated());
            byte[] xmlBytes = dmnXmlConverter.convertToXML(dmnDefinition);

            BufferedInputStream in = new BufferedInputStream(new ByteArrayInputStream(xmlBytes));

            byte[] buffer = new byte[8096];
            while (true) {
                int count = in.read(buffer);
                if (count == -1)
                    break;
                servletOutputStream.write(buffer, 0, count);
            }

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

        } catch (Exception e) {
            LOGGER.error("Could not export decision table model", e);
            throw new InternalServerErrorException("Could not export decision table model");
        }
    }

    public ModelRepresentation importDecisionTable(HttpServletRequest request, MultipartFile file) {

        String fileName = file.getOriginalFilename();
        if (fileName != null && (fileName.endsWith(".dmn") || fileName.endsWith(".xml"))) {
            try {

                XMLInputFactory xif = XmlUtil.createSafeXmlInputFactory();
                InputStreamReader xmlIn = new InputStreamReader(file.getInputStream(), "UTF-8");
                XMLStreamReader xtr = xif.createXMLStreamReader(xmlIn);

                DmnDefinition dmnDefinition = dmnXmlConverter.convertToDmnModel(xtr);

                if (dmnDefinition.getDecisions().size() == 0) {
                    throw new FlowableException("No decisions found in " + fileName);
                }

                ObjectNode editorJsonNode = dmnJsonConverter.convertToJson(dmnDefinition);

                // remove id to avoid InvalidFormatException when deserializing
                editorJsonNode.remove("id");

                // set to latest model version
                editorJsonNode.put("modelVersion", 2);

                ModelRepresentation modelRepresentation = new ModelRepresentation();
                modelRepresentation.setKey(dmnDefinition.getDecisions().get(0).getId());
                modelRepresentation.setName(dmnDefinition.getName());
                modelRepresentation.setDescription(dmnDefinition.getDescription());
                modelRepresentation.setModelType(AbstractModel.MODEL_TYPE_DECISION_TABLE);
                Model model = modelService.createModel(modelRepresentation, editorJsonNode.toString(),
                        SecurityUtils.getCurrentUserObject());
                return new ModelRepresentation(model);

            } catch (Exception e) {
                LOGGER.error("Could not import decision table model", e);
                throw new InternalServerErrorException("Could not import decision table model");
            }
        } else {
            throw new BadRequestException(
                    "Invalid file name, only .dmn or .xml files are supported not " + fileName);
        }

    }

    public void migrateDecisionTables() {
        List<Model> decisionTableModels = modelRepository.findByModelType(AbstractModel.MODEL_TYPE_DECISION_TABLE,
                ModelSort.NAME_ASC);

        decisionTableModels.forEach(decisionTableModel -> {
            decisionTableModel = DecisionTableModelConversionUtil.convertModelToV3(decisionTableModel);
            modelRepository.save(decisionTableModel);
        });
    }

    protected String makeValidFilterText(String filterText) {
        String validFilter = null;

        if (filterText != null) {
            String trimmed = StringUtils.trim(filterText);
            if (trimmed.length() >= MIN_FILTER_LENGTH) {
                validFilter = "%" + trimmed.toLowerCase() + "%";
            }
        }
        return validFilter;
    }

    public Model getDecisionTableModel(String decisionTableId) {

        Model decisionTableModel = getModel(decisionTableId, true, false);

        // convert to new model version
        decisionTableModel = DecisionTableModelConversionUtil.convertModel(decisionTableModel);

        return decisionTableModel;
    }

    public DecisionTableRepresentation getDecisionTable(String decisionTableId) {
        return createDecisionTableRepresentation(getDecisionTableModel(decisionTableId));
    }

    public DecisionTableRepresentation getDecisionTableRepresentation(AbstractModel decisionTableModel) {
        return createDecisionTableRepresentation(decisionTableModel);
    }

    public DecisionTableRepresentation getHistoricDecisionTable(String modelHistoryId) {
        // Get the historic model
        ModelHistory modelHistory = modelHistoryRepository.get(modelHistoryId);

        // Load model and check we have read rights
        getModel(modelHistory.getModelId(), true, false);

        return createDecisionTableRepresentation(modelHistory);
    }

    protected DecisionTableRepresentation createDecisionTableRepresentation(AbstractModel model) {
        DecisionTableDefinitionRepresentation decisionTableDefinitionRepresentation = null;
        try {
            decisionTableDefinitionRepresentation = objectMapper.readValue(model.getModelEditorJson(),
                    DecisionTableDefinitionRepresentation.class);
        } catch (Exception e) {
            LOGGER.error("Error deserializing decision table", e);
            throw new InternalServerErrorException("Could not deserialize decision table definition");
        }
        DecisionTableRepresentation result = new DecisionTableRepresentation(model);
        result.setDecisionTableDefinition(decisionTableDefinitionRepresentation);
        return result;
    }

    public DecisionTableRepresentation saveDecisionTable(String decisionTableId,
            DecisionTableSaveRepresentation saveRepresentation) {

        User user = SecurityUtils.getCurrentUserObject();
        Model model = getModel(decisionTableId, false, false);

        String decisionKey = saveRepresentation.getDecisionTableRepresentation().getKey();
        ModelKeyRepresentation modelKeyInfo = modelService.validateModelKey(model, model.getModelType(),
                decisionKey);
        if (modelKeyInfo.isKeyAlreadyExists()) {
            throw new BadRequestException("Provided model key already exists: " + decisionKey);
        }

        model.setName(saveRepresentation.getDecisionTableRepresentation().getName());
        model.setKey(decisionKey);
        model.setDescription(saveRepresentation.getDecisionTableRepresentation().getDescription());

        saveRepresentation.getDecisionTableRepresentation().getDecisionTableDefinition().setKey(decisionKey);

        String editorJson = null;
        try {
            editorJson = objectMapper.writeValueAsString(
                    saveRepresentation.getDecisionTableRepresentation().getDecisionTableDefinition());
        } catch (Exception e) {
            LOGGER.error("Error while processing decision table json", e);
            throw new InternalServerErrorException("Decision table could not be saved " + decisionTableId);
        }

        String filteredImageString = saveRepresentation.getDecisionTableImageBase64()
                .replace("data:image/png;base64,", "");
        byte[] imageBytes = Base64.getDecoder().decode(filteredImageString);
        model = modelService.saveModel(model, editorJson, imageBytes, saveRepresentation.isNewVersion(),
                saveRepresentation.getComment(), user);
        DecisionTableRepresentation result = new DecisionTableRepresentation(model);
        result.setDecisionTableDefinition(
                saveRepresentation.getDecisionTableRepresentation().getDecisionTableDefinition());
        return result;
    }
}