/**
* Copyright (c) 2014, 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 org.wso2.carbon.bpmn.core.mgt.services;
import org.activiti.engine.HistoryService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricProcessInstanceQuery;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.DeploymentQuery;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.repository.ProcessDefinitionQuery;
import org.activiti.engine.runtime.ProcessInstance;
import org.apache.axiom.util.base64.Base64Utils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.bpmn.core.BPMNConstants;
import org.wso2.carbon.bpmn.core.BPMNServerHolder;
import org.wso2.carbon.bpmn.core.BPSFault;
import org.wso2.carbon.bpmn.core.deployment.TenantRepository;
import org.wso2.carbon.bpmn.core.mgt.model.BPMNDeletableInstances;
import org.wso2.carbon.bpmn.core.mgt.model.BPMNDeployment;
import org.wso2.carbon.bpmn.core.mgt.model.BPMNProcess;
import org.wso2.carbon.bpmn.core.utils.BPMNActivitiConfiguration;
import org.wso2.carbon.context.CarbonContext;
import org.wso2.carbon.registry.api.Registry;
import org.wso2.carbon.registry.api.RegistryException;
import org.wso2.carbon.registry.api.RegistryService;
import org.wso2.carbon.registry.api.Resource;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
public class BPMNDeploymentService {
private static Log log = LogFactory.getLog(BPMNDeploymentService.class);
private int deploymentCount = -1;
private int maximumDeleteCount = BPMNConstants.ACTIVITI_INSTANCE_MAX_DELETE_COUNT;
public BPMNDeploymentService(){
initializeVariable();
}
public BPMNProcess[] getDeployedProcesses() throws BPSFault {
Integer tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
TenantRepository tenantRepository = BPMNServerHolder.getInstance().getTenantManager().getTenantRepository(tenantId);
List<ProcessDefinition> processDefinitions = tenantRepository.getDeployedProcessDefinitions();
BPMNProcess[] bpmnProcesses = new BPMNProcess[processDefinitions.size()];
for (int i = 0; i < processDefinitions.size(); i++) {
ProcessDefinition def = processDefinitions.get(i);
BPMNProcess bpmnProcess = new BPMNProcess();
bpmnProcess.setProcessId(def.getId());
bpmnProcess.setDeploymentId(def.getDeploymentId());
bpmnProcess.setKey(def.getKey());
bpmnProcess.setVersion(def.getVersion());
bpmnProcesses[i] = bpmnProcess;
}
return bpmnProcesses;
}
public BPMNProcess getProcessById(String processId) {
Integer tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
ProcessEngine engine = BPMNServerHolder.getInstance().getEngine();
ProcessDefinitionQuery query = engine.getRepositoryService().createProcessDefinitionQuery();
ProcessDefinition process = query.processDefinitionTenantId(tenantId.toString())
.processDefinitionId(processId).singleResult();
DeploymentQuery deploymentQuery = engine.getRepositoryService().createDeploymentQuery();
Deployment deployment = deploymentQuery.deploymentId(process.getDeploymentId()).singleResult();
BPMNProcess bpmnProcess = new BPMNProcess();
bpmnProcess.setDeploymentId(process.getDeploymentId());
bpmnProcess.setName(process.getName());
bpmnProcess.setKey(process.getKey());
bpmnProcess.setProcessId(process.getId());
bpmnProcess.setVersion(process.getVersion());
bpmnProcess.setDeploymentTime(deployment.getDeploymentTime());
bpmnProcess.setDeploymentName(deployment.getName());
return bpmnProcess;
}
public BPMNProcess[] getProcessesByDeploymentId(String deploymentId) {
List<BPMNProcess> bpmnProcesses = new ArrayList<>();
Integer tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
ProcessEngine engine = BPMNServerHolder.getInstance().getEngine();
ProcessDefinitionQuery query = engine.getRepositoryService().createProcessDefinitionQuery();
DeploymentQuery deploymentQuery = engine.getRepositoryService().createDeploymentQuery();
Deployment deployment = deploymentQuery.deploymentId(deploymentId).singleResult();
List<ProcessDefinition> processes = query.processDefinitionTenantId(tenantId.toString())
.deploymentId(deploymentId).list();
for(ProcessDefinition process: processes){
BPMNProcess bpmnProcess = new BPMNProcess();
bpmnProcess.setDeploymentId(process.getDeploymentId());
bpmnProcess.setName(process.getName());
bpmnProcess.setKey(process.getKey());
bpmnProcess.setProcessId(process.getId());
bpmnProcess.setVersion(process.getVersion());
bpmnProcess.setDeploymentTime(deployment.getDeploymentTime());
bpmnProcess.setDeploymentName(deployment.getName());
bpmnProcesses.add(bpmnProcess);
}
return bpmnProcesses.toArray(new BPMNProcess[bpmnProcesses.size()]);
}
public BPMNDeployment[] getDeployments() {
List<BPMNDeployment> bpmnDeploymentList = new ArrayList<>();
Integer tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
DeploymentQuery query = BPMNServerHolder.getInstance().getEngine().getRepositoryService().createDeploymentQuery();
query = query.deploymentTenantId(tenantId.toString());
List<Deployment> deployments = query.list();
for(Deployment deployment: deployments){
BPMNDeployment bpmnDeployment = new BPMNDeployment();
bpmnDeployment.setDeploymentId(deployment.getId());
bpmnDeployment.setDeploymentName(deployment.getName());
bpmnDeployment.setDeploymentTime(deployment.getDeploymentTime());
bpmnDeploymentList.add(bpmnDeployment);
}
return bpmnDeploymentList.toArray(new BPMNDeployment[bpmnDeploymentList.size()]);
}
/**
* Get the deployments for given package name , order by deploymentID
*
* @param deploymentName
* @return
*/
public BPMNDeployment[] getDeploymentsByName(String deploymentName) {
List<BPMNDeployment> bpmnDeploymentList = new ArrayList<>();
Integer tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
DeploymentQuery query = BPMNServerHolder.getInstance().getEngine().getRepositoryService()
.createDeploymentQuery();
// Set deployment name and order by ID
query = query.deploymentTenantId(tenantId.toString()).deploymentName(deploymentName).orderByDeploymentId()
.desc();
List<Deployment> deployments = query.list();
for (Deployment deployment : deployments) {
BPMNDeployment bpmnDeployment = new BPMNDeployment();
bpmnDeployment.setDeploymentId(deployment.getId());
bpmnDeployment.setDeploymentName(deployment.getName());
bpmnDeployment.setDeploymentTime(deployment.getDeploymentTime());
bpmnDeploymentList.add(bpmnDeployment);
}
return bpmnDeploymentList.toArray(new BPMNDeployment[bpmnDeploymentList.size()]);
}
public BPMNDeployment[] getPaginatedDeploymentsByFilter(String method, String filter, int start, int size) {
List<BPMNDeployment> bpmnDeploymentList = new ArrayList<>();
Integer tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
DeploymentQuery query = BPMNServerHolder.getInstance().getEngine().getRepositoryService().createDeploymentQuery();
query = query.deploymentTenantId(tenantId.toString());
if(filter != null && !filter.equals("") && method != null && !method.equals("")){
if(method.equals("byDeploymentNameLike")){
query = query.deploymentNameLike("%" + filter + "%");
} else {
query = query.processDefinitionKeyLike("%" + filter + "%");
}
}
deploymentCount = (int) query.count();
List<Deployment> deployments = query.listPage(start, size);
for(Deployment deployment: deployments){
BPMNDeployment bpmnDeployment = new BPMNDeployment();
bpmnDeployment.setDeploymentId(deployment.getId());
bpmnDeployment.setDeploymentName(deployment.getName());
bpmnDeployment.setDeploymentTime(deployment.getDeploymentTime());
bpmnDeploymentList.add(bpmnDeployment);
}
return bpmnDeploymentList.toArray(new BPMNDeployment[bpmnDeploymentList.size()]);
}
public int getDeploymentCount() throws BPSFault {
if (deploymentCount == -1) {
Integer tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
DeploymentQuery query = BPMNServerHolder.getInstance().getEngine().getRepositoryService().createDeploymentQuery();
deploymentCount = (int) query.deploymentTenantId(tenantId.toString()).count();
}
return deploymentCount;
}
public String getProcessDiagram(String processId) throws BPSFault {
Integer tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
try {
RepositoryService repositoryService = BPMNServerHolder.getInstance().getEngine().getRepositoryService();
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionTenantId(tenantId.toString())
.processDefinitionId(processId)
.singleResult();
String diagramResourceName = processDefinition.getDiagramResourceName();
InputStream imageStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), diagramResourceName);
BufferedImage bufferedImage = ImageIO.read(imageStream);
return encodeToString(bufferedImage, "png");
}
catch (IOException e) {
String msg = "Failed to create the diagram for process: " + processId;
// log.error(msg, e);
throw new BPSFault(msg, e);
}
}
public String getProcessModel(String processId) throws BPSFault {
Integer tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
BufferedReader br = null;
try {
RepositoryService repositoryService = BPMNServerHolder.getInstance().getEngine().getRepositoryService();
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionTenantId(tenantId.toString())
.processDefinitionId(processId)
.singleResult();
InputStream stream = repositoryService.getProcessModel(processDefinition.getId());
br = new BufferedReader(new InputStreamReader(stream, Charset.defaultCharset()));
StringBuilder sb = new StringBuilder();
String line;
while ((line = br.readLine()) != null) {
sb.append(line);
}
return sb.toString();
} catch (IOException e) {
String msg = "Failed to create the diagram for process: " + processId;
log.error(msg, e);
throw new BPSFault(msg, e);
} finally {
try {
if (br != null) {
br.close();
}
} catch (IOException e) {
log.error("Could not close the reader", e);
}
}
}
public void undeploy (String deploymentName ) throws BPSFault {
Integer tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
ProcessEngine processEngine = BPMNServerHolder.getInstance().getEngine();
DeploymentQuery query = processEngine.getRepositoryService().createDeploymentQuery();
query = query.deploymentTenantId(tenantId.toString());
query = query.deploymentNameLike("%" + deploymentName + "%");
int deploymentCount = (int) query.count();
log.info("Package " + deploymentName + " id going to be undeployed for the deployment count : " + deploymentCount);
BPMNDeletableInstances bpmnDeletableInstances = new BPMNDeletableInstances();
bpmnDeletableInstances.setTenantId(tenantId);
List<Deployment> deployments = query.listPage(0, deploymentCount+1);
for(Deployment deployment: deployments){
aggregateRemovableProcessInstances(bpmnDeletableInstances, deployment.getId(), tenantId, processEngine);
}
if( (bpmnDeletableInstances.getActiveInstanceCount() + bpmnDeletableInstances.getCompletedInstanceCount()) > maximumDeleteCount){
String errorMessage = " Failed to un deploy the package. Please delete the instances before un deploying " +
"the package";
throw new BPSFault(errorMessage, new Exception(errorMessage));
}
deleteInstances(bpmnDeletableInstances, processEngine);
TenantRepository tenantRepository = BPMNServerHolder.getInstance().getTenantManager().getTenantRepository(tenantId);
tenantRepository.undeploy(deploymentName, false);
}
private void aggregateRemovableProcessInstances(BPMNDeletableInstances bpmnDeletableInstances, String
deploymentId, Integer tenantId, ProcessEngine processEngine) throws BPSFault {
ProcessDefinitionQuery query = processEngine.getRepositoryService().createProcessDefinitionQuery();
List<ProcessDefinition> processes = query.processDefinitionTenantId(tenantId.toString())
.deploymentId(deploymentId).list();
for(ProcessDefinition process: processes){
if( !constructBPMNInstancesByProcessID(bpmnDeletableInstances, process.getId(), tenantId, processEngine) ){
String errorMessage = " Failed to undeploy the package. Please delete the instances before undeploying " +
"the package";
throw new BPSFault(errorMessage);
}
}
}
private boolean constructBPMNInstancesByProcessID(BPMNDeletableInstances bpmnDeletableInstances, String processId, Integer
tenantId, ProcessEngine processEngine){
//first going to get the instances list of unfinished instances
HistoricProcessInstanceQuery runtimeQuery = processEngine.getHistoryService()
.createHistoricProcessInstanceQuery().processInstanceTenantId(tenantId.toString())
.includeProcessVariables().unfinished().processDefinitionId(processId);
int processInstanceCount = (int) runtimeQuery.count();
bpmnDeletableInstances.setActiveInstanceCount(processInstanceCount);
if(bpmnDeletableInstances.getActiveInstanceCount() > maximumDeleteCount){
return false;
}
if(log.isDebugEnabled()){
log.debug("Process ID has un completed instances count : " + processInstanceCount);
}
if(processInstanceCount > 0){
List<HistoricProcessInstance> instances = runtimeQuery.listPage(0, processInstanceCount + 1);
bpmnDeletableInstances.setActiveProcessInstance(instances);
}
//next get the count of finished instance for the same process id
runtimeQuery = processEngine.getHistoryService()
.createHistoricProcessInstanceQuery().processInstanceTenantId(tenantId.toString())
.includeProcessVariables().finished().processDefinitionId(processId);
int completedProcessInstanceCount = (int) runtimeQuery.count();
if((completedProcessInstanceCount + bpmnDeletableInstances.getActiveInstanceCount()) > maximumDeleteCount){
return false;
}
bpmnDeletableInstances.setCompletedInstanceCount(completedProcessInstanceCount);
bpmnDeletableInstances.addCompletedProcessDefinitionIds(processId);
if(log.isDebugEnabled()){
log.debug("Process ID has completed instances count : " + completedProcessInstanceCount);
}
return true;
}
private void initializeVariable(){
BPMNActivitiConfiguration bpmnActivitiConfiguration = BPMNActivitiConfiguration.getInstance();
if(bpmnActivitiConfiguration != null){
String countPropertyValue = bpmnActivitiConfiguration.getBPMNPropertyValue(BPMNConstants
.ACTIVITI_INSTANCE_MAX_DELETE_CONFIG, BPMNConstants
.ACTIVITI_INSTANCE_MAX_DELETE_CONFIG_MAX_COUNT_PROPERTY);
if(countPropertyValue != null) {
maximumDeleteCount = Integer.valueOf(countPropertyValue);
}
}
if (log.isDebugEnabled()) {
log.debug("Maximum Delete count : " + maximumDeleteCount);
}
}
private void deleteInstances(BPMNDeletableInstances bpmnDeletableInstances, ProcessEngine processEngine){
List<HistoricProcessInstance> activeHistoricProcessInstance = bpmnDeletableInstances
.getActiveHistoricProcessInstance();
for (HistoricProcessInstance instance: activeHistoricProcessInstance) {
String instanceId = instance.getId();
RuntimeService runtimeService = processEngine.getRuntimeService();
List<ProcessInstance> processInstances = runtimeService.createProcessInstanceQuery()
.processInstanceTenantId(bpmnDeletableInstances.getTenantId().toString()).processInstanceId
(instanceId).list();
if(!processInstances.isEmpty()){
runtimeService.deleteProcessInstance(instance.getId(), "Deleted by user: " + bpmnDeletableInstances.getTenantId());
}
}
List<String> completedProcessDefinitionIds = bpmnDeletableInstances.getCompletedProcessDefinitionIds();
for (String processId:completedProcessDefinitionIds){
HistoricProcessInstanceQuery runtimeQuery = processEngine.getHistoryService()
.createHistoricProcessInstanceQuery().processInstanceTenantId(bpmnDeletableInstances.getTenantId()
.toString()).includeProcessVariables().finished().processDefinitionId(processId);
int completedProcessInstanceCount = (int) runtimeQuery.count();
if(completedProcessInstanceCount > 0){
List<HistoricProcessInstance> instances = runtimeQuery.listPage(0, completedProcessInstanceCount + 1);
HistoryService historyService = processEngine.getHistoryService();
for (HistoricProcessInstance instance: instances) {
String instanceId = instance.getId();
historyService.deleteHistoricProcessInstance(instanceId);
}
}
}
}
private String encodeToString(BufferedImage image, String type) {
String imageString = null;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
ImageIO.write(image, type, bos);
byte[] imageBytes = bos.toByteArray();
imageString = Base64Utils.encode(imageBytes);
} catch (IOException e) {
log.error("Could not write image data", e);
} finally {
try {
bos.close();
} catch (IOException e) {
log.error("Could not close the byte stream", e);
}
}
return imageString;
}
/**
* Get the checksum of latest deployment for given deployment name
*
* @param deploymentName
* @return
* @throws BPSFault
*/
public String getLatestChecksum(String deploymentName) throws BPSFault {
try {
Integer tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
RegistryService registryService = BPMNServerHolder.getInstance().getRegistryService();
Registry tenantRegistry = registryService.getConfigSystemRegistry(tenantId);
String deploymentRegistryPath = BPMNConstants.BPMN_REGISTRY_PATH + BPMNConstants.REGISTRY_PATH_SEPARATOR
+ deploymentName;
if (tenantRegistry.resourceExists(deploymentRegistryPath)) {
Resource deploymentEntry = tenantRegistry.get(deploymentRegistryPath);
return deploymentEntry.getProperty(BPMNConstants.LATEST_CHECKSUM_PROPERTY);
} else {
return null;
}
} catch (RegistryException e) {
String msg = "Error while accessing registry to get latest checksum for package : " + deploymentName;
throw new BPSFault(msg, e);
}
}
}