源代码只支持单实例,为了增加可用性本人进行了优化,现分享如下:
使用方式:
public void traceImage(String processid,HttpServletResponse response,String... executionids) throws IOException {
Command cmd = new HistoryProcessInstanceDiagramCmd(processid,executionids);
InputStream is = managementService.executeCommand(cmd);
int len = 0;
byte[] b = new byte[1024];
while ((len = is.read(b, 0, 1024)) != -1) {
response.getOutputStream().write(b, 0, len);
}
}
补充:controller层实战代码,调用traceImage示例。注意:传入的executionids最好排序asc
@RequestMapping(path="/showproccess")
@ResponseBody
public Result viewtraceImageByActiviti(@RequestParam String eventid,String prepeople,HttpServletResponse response){
try {
String businessKey = BPM_KEY+"."+eventid;
ProcessInstance p = runtimeService.createProcessInstanceQuery().processInstanceBusinessKey(businessKey).singleResult();
if(p==null){//查历史流程 从新到旧
List his_pros = historyService.createHistoricProcessInstanceQuery().processInstanceBusinessKey(businessKey).excludeSubprocesses(false).orderByProcessInstanceId().desc().list();
if(his_pros.size()==0){
return Result.failure("无流程");
}else{
service.traceImage(his_pros.get(0).getId(),response);
}
}else{//查运行的流程
//通过关联人、查出 执行中的executionid
List list_hisTask = historyService.createHistoricTaskInstanceQuery().processInstanceId(p.getProcessInstanceId()).taskInvolvedUser(prepeople).list();
if(list_hisTask.size()==0){
service.traceImage(p.getProcessInstanceId(),response);//看当前总流程
return Result.failure("无相关流程");
}
String executionid = list_hisTask.get(0).getExecutionId();
List ex_list = runtimeService.createExecutionQuery().processInstanceId(p.getProcessInstanceId()).list();
service.traceImage(list_hisTask.get(0).getProcessInstanceId(),response,getExecutions(ex_list,executionid));
}
return Result.success(null);
} catch (Exception e) {
e.printStackTrace();
return Result.failure(e.getMessage(),null);
}
}
public String[] getExecutions(List list,String executionid){
List lists = new ArrayList();
//获取
Execution e = getExecution(list,executionid);
//获取父id
Execution ex =e;
while(ex!=null){
lists.add(ex.getId());
ex = getExecution(list,ex.getParentId());
}
//获取子id
lists.addAll(getExecutionChilds(list,executionid));
//排序
Collections.sort(lists);
String[] arr = lists.toArray(new String[lists.size()]);
for(String str:arr){
System.out.println(str);
}
return arr;
}
private Execution getExecution(List list,String executionid){
for(Execution e : list){
if(e.getId().equals(executionid))
return e;
}
return null;
}
private List getExecutionChilds(List list,String executionid){
List list1 = new ArrayList();
//加载次节点
for(Execution e : list){
if(executionid.equals(e.getParentId()))
list1.add(e.getId());
}
//加载次节点的子节点
for(String childid :list1){
list1.addAll(getExecutionChilds(list,childid));
}
return list1;
}
以下部分为工具类的所有代码:
package com.zihai.activiti.util;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.pvm.PvmActivity;
import org.activiti.engine.impl.pvm.PvmTransition;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ActivitiHistoryGraphBuilder {
private static Logger logger = LoggerFactory
.getLogger(ActivitiHistoryGraphBuilder.class);
private ProcessDefinitionEntity processDefinitionEntity;
private List historicActivityInstances;
private List visitedHistoricActivityInstances = new ArrayList();
private Map nodeMap = new HashMap();
public ActivitiHistoryGraphBuilder(ProcessDefinitionEntity processDefinitionEntity,List historicActivityInstances) {
this.processDefinitionEntity = processDefinitionEntity;
this.historicActivityInstances = historicActivityInstances;
}
public Graph build() {
Graph graph = new Graph();
for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) {
Node currentNode = new Node();
currentNode.setId(historicActivityInstance.getId());
currentNode.setName(historicActivityInstance.getActivityId());
currentNode.setType(historicActivityInstance.getActivityType());
currentNode
.setActive(historicActivityInstance.getEndTime() == null);
logger.debug("currentNode : {}:{}", currentNode.getName(),
currentNode.getId());
Edge previousEdge = this.findPreviousEdge(currentNode,
historicActivityInstance.getStartTime().getTime());
if (previousEdge == null) {
graph.addInitial(currentNode);
} else {
logger.debug("previousEdge : {}", previousEdge.getName());
}
nodeMap.put(currentNode.getId(), currentNode);
visitedHistoricActivityInstances.add(historicActivityInstance);
}
if (graph.getInitial() == null) {
throw new IllegalStateException("cannot find initial.");
}
return graph;
}
public Edge findPreviousEdge(Node currentNode, long currentStartTime) {
String activityId = currentNode.getName();
ActivityImpl activityImpl = processDefinitionEntity
.findActivity(activityId);
HistoricActivityInstance nestestHistoricActivityInstance = null;
String temporaryPvmTransitionId = null;
// 遍历进入当前节点的所有连线
for (PvmTransition pvmTransition : activityImpl
.getIncomingTransitions()) {
PvmActivity source = pvmTransition.getSource();
String previousActivityId = source.getId();
HistoricActivityInstance visitiedHistoryActivityInstance = this
.findVisitedHistoricActivityInstance(previousActivityId);
if (visitiedHistoryActivityInstance == null) {
continue;
}
// 如果上一个节点还未完成,说明不可能是从这个节点过来的,跳过
if (visitiedHistoryActivityInstance.getEndTime() == null) {
continue;
}
logger.debug("current activity start time : {}", new Date(
currentStartTime));
logger.debug("nestest activity end time : {}",
visitiedHistoryActivityInstance.getEndTime());
// 如果当前节点的开始时间,比上一个节点的结束时间要早,跳过
if (currentStartTime < visitiedHistoryActivityInstance.getEndTime()
.getTime()) {
continue;
}
if (nestestHistoricActivityInstance == null) {
nestestHistoricActivityInstance = visitiedHistoryActivityInstance;
temporaryPvmTransitionId = pvmTransition.getId();
} else if ((currentStartTime - nestestHistoricActivityInstance
.getEndTime().getTime()) > (currentStartTime - visitiedHistoryActivityInstance
.getEndTime().getTime())) {
// 寻找离当前节点最近的上一个节点
// 比较上一个节点的endTime与当前节点startTime的差
nestestHistoricActivityInstance = visitiedHistoryActivityInstance;
temporaryPvmTransitionId = pvmTransition.getId();
}
}
// 没找到上一个节点,就返回null
if (nestestHistoricActivityInstance == null) {
return null;
}
Node previousNode = nodeMap
.get(nestestHistoricActivityInstance.getId());
if (previousNode == null) {
return null;
}
logger.debug("previousNode : {}:{}", previousNode.getName(),
previousNode.getId());
Edge edge = new Edge();
edge.setName(temporaryPvmTransitionId);
previousNode.getEdges().add(edge);
edge.setSrc(previousNode);
edge.setDest(currentNode);
return edge;
}
public HistoricActivityInstance findVisitedHistoricActivityInstance(
String activityId) {
for (int i = visitedHistoricActivityInstances.size() - 1; i >= 0; i--) {
HistoricActivityInstance historicActivityInstance = visitedHistoricActivityInstances
.get(i);
if (activityId.equals(historicActivityInstance.getActivityId())) {
return historicActivityInstance;
}
}
return null;
}
}
package com.zihai.activiti.util;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.Stroke;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Ellipse2D.Double;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
import org.activiti.bpmn.constants.BpmnXMLConstants;
import org.activiti.bpmn.model.Artifact;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowElement;
import org.activiti.bpmn.model.FlowElementsContainer;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.GraphicInfo;
import org.activiti.bpmn.model.Lane;
import org.activiti.bpmn.model.Pool;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.impl.HistoricActivityInstanceQueryImpl;
import org.activiti.engine.impl.Page;
import org.activiti.engine.impl.cmd.GetBpmnModelCmd;
import org.activiti.engine.impl.cmd.GetDeploymentProcessDefinitionCmd;
import org.activiti.engine.impl.context.Context;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.apache.commons.io.FilenameUtils;
/**
* 流程图绘制工具
*/
public class CustomProcessDiagramGenerator {
public static final int OFFSET_SUBPROCESS = 5;
public static final int OFFSET_TASK = 20;
private static List taskType = new ArrayList();
private static List eventType = new ArrayList();
private static List gatewayType = new ArrayList();
private static List subProcessType = new ArrayList();
private static Color RUNNING_COLOR = Color.RED;
private static Color HISTORY_COLOR = Color.GREEN;
private static Color SKIP_COLOR = Color.GRAY;
private static Stroke THICK_BORDER_STROKE = new BasicStroke(3.0f);
private int minX;
private int minY;
BpmnModel bpmnModel;
ProcessDefinitionEntity definition;
List activityInstances = new ArrayList();
public CustomProcessDiagramGenerator() {
init();
}
protected static void init() {
taskType.add(BpmnXMLConstants.ELEMENT_TASK_MANUAL);
taskType.add(BpmnXMLConstants.ELEMENT_TASK_RECEIVE);
taskType.add(BpmnXMLConstants.ELEMENT_TASK_SCRIPT);
taskType.add(BpmnXMLConstants.ELEMENT_TASK_SEND);
taskType.add(BpmnXMLConstants.ELEMENT_TASK_SERVICE);
taskType.add(BpmnXMLConstants.ELEMENT_TASK_USER);
gatewayType.add(BpmnXMLConstants.ELEMENT_GATEWAY_EXCLUSIVE);
gatewayType.add(BpmnXMLConstants.ELEMENT_GATEWAY_INCLUSIVE);
gatewayType.add(BpmnXMLConstants.ELEMENT_GATEWAY_EVENT);
gatewayType.add(BpmnXMLConstants.ELEMENT_GATEWAY_PARALLEL);
eventType.add("intermediateTimer");
eventType.add("intermediateMessageCatch");
eventType.add("intermediateSignalCatch");
eventType.add("intermediateSignalThrow");
eventType.add("messageStartEvent");
eventType.add("startTimerEvent");
eventType.add(BpmnXMLConstants.ELEMENT_ERROR);
eventType.add(BpmnXMLConstants.ELEMENT_EVENT_START);
eventType.add("errorEndEvent");
eventType.add(BpmnXMLConstants.ELEMENT_EVENT_END);
subProcessType.add(BpmnXMLConstants.ELEMENT_SUBPROCESS);
subProcessType.add(BpmnXMLConstants.ELEMENT_CALL_ACTIVITY);
}
/**
* 根据任务id获得轨迹图
* */
public InputStream generateDiagram(String processInstanceId,String...executionIds)
throws IOException {
HistoricProcessInstance historicProcessInstance = Context
.getCommandContext().getHistoricProcessInstanceEntityManager()
.findHistoricProcessInstance(processInstanceId);
String processDefinitionId = historicProcessInstance
.getProcessDefinitionId();
GetBpmnModelCmd getBpmnModelCmd = new GetBpmnModelCmd(
processDefinitionId);
bpmnModel = getBpmnModelCmd.execute(Context
.getCommandContext());
Point point = getMinXAndMinY(bpmnModel);
/*
* this.minX = point.x; this.minY = point.y; this.minX = (this.minX <=
* 5) ? 5 : this.minX; this.minY = (this.minY <= 5) ? 5 : this.minY;
* this.minX -= 5; this.minY -= 5;
*
* this.minX =0; this.minY=0;
*/
/*
* this.minX = point.x; this.minY = point.y;
* System.out.println(this.minX + "=======" + this.minY); this.minX =
* (this.minX <= 5) ? 0 : this.minX; this.minY = (this.minY <= 5) ? 0 :
* this.minY;
*/
this.minX = point.x;
this.minY = point.y;
if (this.minX > 0) {
this.minX = 0;
}
if (this.minY > 0) {
this.minY = 0;
}
definition = new GetDeploymentProcessDefinitionCmd(
processDefinitionId).execute(Context.getCommandContext());
String diagramResourceName = definition.getDiagramResourceName();
String deploymentId = definition.getDeploymentId();
byte[] bytes = Context
.getCommandContext()
.getResourceEntityManager()
.findResourceByDeploymentIdAndResourceName(deploymentId,
diagramResourceName).getBytes();
InputStream originDiagram = new ByteArrayInputStream(bytes);
BufferedImage image = ImageIO.read(originDiagram);
HistoricActivityInstanceQueryImpl historicActivityInstanceQueryImpl = new HistoricActivityInstanceQueryImpl();
Page page = new Page(0, 100);
if(executionIds.length==0){
historicActivityInstanceQueryImpl.processInstanceId(processInstanceId);
activityInstances = Context .getCommandContext().getHistoricActivityInstanceEntityManager()
.findHistoricActivityInstancesByQueryCriteria( historicActivityInstanceQueryImpl, page);
}else{
for(int i=0;i act = Context .getCommandContext().getHistoricActivityInstanceEntityManager()
.findHistoricActivityInstancesByQueryCriteria( historicActivityInstanceQueryImpl, page);
activityInstances.addAll(act);
}
}
this.drawHistoryFlow(image, processInstanceId);
for (HistoricActivityInstance historicActivityInstance : activityInstances) {
String historicActivityId = historicActivityInstance
.getActivityId();
ActivityImpl activity = definition.findActivity(historicActivityId);
if (activity != null) {
if (historicActivityInstance.getEndTime() == null) {
// 节点正在运行中
signRunningNode(image, activity.getX() - this.minX,
activity.getY() - this.minY, activity.getWidth(),
activity.getHeight(),
historicActivityInstance.getActivityType());
} else {
String deleteReason = null;
if (historicActivityInstance.getTaskId() != null) {
deleteReason = Context
.getCommandContext()
.getHistoricTaskInstanceEntityManager()
.findHistoricTaskInstanceById(
historicActivityInstance.getTaskId())
.getDeleteReason();
}
// 节点已经结束
if ("completed".equals(deleteReason)) {
signSkipNode(image, activity.getX(),
activity.getY(),
activity.getWidth(), activity.getHeight(),
historicActivityInstance.getActivityType());
} else {
signHistoryNode(image, activity.getX() - this.minX,
activity.getY() - this.minY,
activity.getWidth(), activity.getHeight(),
historicActivityInstance.getActivityType());
}
}
}
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
String formatName = getDiagramExtension(diagramResourceName);
ImageIO.write(image, formatName, out);
return new ByteArrayInputStream(out.toByteArray());
}
private static String getDiagramExtension(String diagramResourceName) {
return FilenameUtils.getExtension(diagramResourceName);
}
/**
* 标记运行节点
*
* @param image
* 原始图片
* @param x
* 左上角节点坐在X位置
* @param y
* 左上角节点坐在Y位置
* @param width
* 宽
* @param height
* 高
* @param activityType
* 节点类型
*/
private static void signRunningNode(BufferedImage image, int x, int y,
int width, int height, String activityType) {
Color nodeColor = RUNNING_COLOR;
Graphics2D graphics = image.createGraphics();
try {
drawNodeBorder(x, y, width, height, graphics, nodeColor,
activityType);
} finally {
graphics.dispose();
}
}
/**
* 标记历史节点
*
* @param image
* 原始图片
* @param x
* 左上角节点坐在X位置
* @param y
* 左上角节点坐在Y位置
* @param width
* 宽
* @param height
* 高
* @param activityType
* 节点类型
*/
private static void signHistoryNode(BufferedImage image, int x, int y,
int width, int height, String activityType) {
Color nodeColor = HISTORY_COLOR;
Graphics2D graphics = image.createGraphics();
try {
drawNodeBorder(x, y, width, height, graphics, nodeColor,
activityType);
} finally {
graphics.dispose();
}
}
private static void signSkipNode(BufferedImage image, int x, int y,
int width, int height, String activityType) {
Color nodeColor = HISTORY_COLOR;
Graphics2D graphics = image.createGraphics();
try {
drawNodeBorder(x, y, width, height, graphics, nodeColor,
activityType);
} finally {
graphics.dispose();
}
}
/**
* 绘制节点边框
*
* @param x
* 左上角节点坐在X位置
* @param y
* 左上角节点坐在Y位置
* @param width
* 宽
* @param height
* 高
* @param graphics
* 绘图对象
* @param color
* 节点边框颜色
* @param activityType
* 节点类型
*/
protected static void drawNodeBorder(int x, int y, int width, int height,
Graphics2D graphics, Color color, String activityType) {
graphics.setPaint(color);
graphics.setStroke(THICK_BORDER_STROKE);
if (taskType.contains(activityType)) {
drawTask(x, y, width, height, graphics);
} else if (gatewayType.contains(activityType)) {
drawGateway(x, y, width, height, graphics);
} else if (eventType.contains(activityType)) {
drawEvent(x, y, width, height, graphics);
} else if (subProcessType.contains(activityType)) {
drawSubProcess(x, y, width, height, graphics);
}
}
/**
* 绘制任务
*/
protected static void drawTask(int x, int y, int width, int height,
Graphics2D graphics) {
RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width,
height, OFFSET_TASK, OFFSET_TASK);
graphics.draw(rect);
}
/**
* 绘制网关
*/
protected static void drawGateway(int x, int y, int width, int height,
Graphics2D graphics) {
Polygon rhombus = new Polygon();
rhombus.addPoint(x, y + (height / 2));
rhombus.addPoint(x + (width / 2), y + height);
rhombus.addPoint(x + width, y + (height / 2));
rhombus.addPoint(x + (width / 2), y);
graphics.draw(rhombus);
}
/**
* 绘制任务
*/
protected static void drawEvent(int x, int y, int width, int height,
Graphics2D graphics) {
Double circle = new Ellipse2D.Double(x, y, width, height);
graphics.draw(circle);
}
/**
* 绘制子流程
*/
protected static void drawSubProcess(int x, int y, int width, int height,
Graphics2D graphics) {
RoundRectangle2D rect = new RoundRectangle2D.Double(x + 1, y + 1,
width - 2, height - 2, OFFSET_SUBPROCESS, OFFSET_SUBPROCESS);
graphics.draw(rect);
}
protected Point getMinXAndMinY(BpmnModel bpmnModel) {
// We need to calculate maximum values to know how big the image will be in its entirety
double theMinX = java.lang.Double.MAX_VALUE;
double theMaxX = 0;
double theMinY = java.lang.Double.MAX_VALUE;
double theMaxY = 0;
for (Pool pool : bpmnModel.getPools()) {
GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(pool.getId());
theMinX = graphicInfo.getX();
theMaxX = graphicInfo.getX() + graphicInfo.getWidth();
theMinY = graphicInfo.getY();
theMaxY = graphicInfo.getY() + graphicInfo.getHeight();
}
List flowNodes = gatherAllFlowNodes(bpmnModel);
for (FlowNode flowNode : flowNodes) {
GraphicInfo flowNodeGraphicInfo = bpmnModel.getGraphicInfo(flowNode
.getId());
// width
if ((flowNodeGraphicInfo.getX() + flowNodeGraphicInfo.getWidth()) > theMaxX) {
theMaxX = flowNodeGraphicInfo.getX()
+ flowNodeGraphicInfo.getWidth();
}
if (flowNodeGraphicInfo.getX() < theMinX) {
theMinX = flowNodeGraphicInfo.getX();
}
// height
if ((flowNodeGraphicInfo.getY() + flowNodeGraphicInfo.getHeight()) > theMaxY) {
theMaxY = flowNodeGraphicInfo.getY()
+ flowNodeGraphicInfo.getHeight();
}
if (flowNodeGraphicInfo.getY() < theMinY) {
theMinY = flowNodeGraphicInfo.getY();
}
for (SequenceFlow sequenceFlow : flowNode.getOutgoingFlows()) {
List graphicInfoList = bpmnModel
.getFlowLocationGraphicInfo(sequenceFlow.getId());
for (GraphicInfo graphicInfo : graphicInfoList) {
// width
if (graphicInfo.getX() > theMaxX) {
theMaxX = graphicInfo.getX();
}
if (graphicInfo.getX() < theMinX) {
theMinX = graphicInfo.getX();
}
// height
if (graphicInfo.getY() > theMaxY) {
theMaxY = graphicInfo.getY();
}
if (graphicInfo.getY() < theMinY) {
theMinY = graphicInfo.getY();
}
}
}
}
List artifacts = gatherAllArtifacts(bpmnModel);
for (Artifact artifact : artifacts) {
GraphicInfo artifactGraphicInfo = bpmnModel.getGraphicInfo(artifact
.getId());
if (artifactGraphicInfo != null) {
// width
if ((artifactGraphicInfo.getX() + artifactGraphicInfo
.getWidth()) > theMaxX) {
theMaxX = artifactGraphicInfo.getX()
+ artifactGraphicInfo.getWidth();
}
if (artifactGraphicInfo.getX() < theMinX) {
theMinX = artifactGraphicInfo.getX();
}
// height
if ((artifactGraphicInfo.getY() + artifactGraphicInfo
.getHeight()) > theMaxY) {
theMaxY = artifactGraphicInfo.getY()
+ artifactGraphicInfo.getHeight();
}
if (artifactGraphicInfo.getY() < theMinY) {
theMinY = artifactGraphicInfo.getY();
}
}
List graphicInfoList = bpmnModel
.getFlowLocationGraphicInfo(artifact.getId());
if (graphicInfoList != null) {
for (GraphicInfo graphicInfo : graphicInfoList) {
// width
if (graphicInfo.getX() > theMaxX) {
theMaxX = graphicInfo.getX();
}
if (graphicInfo.getX() < theMinX) {
theMinX = graphicInfo.getX();
}
// height
if (graphicInfo.getY() > theMaxY) {
theMaxY = graphicInfo.getY();
}
if (graphicInfo.getY() < theMinY) {
theMinY = graphicInfo.getY();
}
}
}
}
int nrOfLanes = 0;
for (org.activiti.bpmn.model.Process process : bpmnModel.getProcesses()) {
for (Lane l : process.getLanes()) {
nrOfLanes++;
GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(l.getId());
// // width
if ((graphicInfo.getX() + graphicInfo.getWidth()) > theMaxX) {
theMaxX = graphicInfo.getX() + graphicInfo.getWidth();
}
if (graphicInfo.getX() < theMinX) {
theMinX = graphicInfo.getX();
}
// height
if ((graphicInfo.getY() + graphicInfo.getHeight()) > theMaxY) {
theMaxY = graphicInfo.getY() + graphicInfo.getHeight();
}
if (graphicInfo.getY() < theMinY) {
theMinY = graphicInfo.getY();
}
}
}
// Special case, see http://jira.codehaus.org/browse/ACT-1431
if ((flowNodes.size() == 0) && (bpmnModel.getPools().size() == 0)
&& (nrOfLanes == 0)) {
// Nothing to show
theMinX = 0;
theMinY = 0;
}
return new Point((int) theMinX, (int) theMinY);
}
protected static List gatherAllArtifacts(BpmnModel bpmnModel) {
List artifacts = new ArrayList();
for (org.activiti.bpmn.model.Process process : bpmnModel.getProcesses()) {
artifacts.addAll(process.getArtifacts());
}
return artifacts;
}
protected static List gatherAllFlowNodes(BpmnModel bpmnModel) {
List flowNodes = new ArrayList();
for (org.activiti.bpmn.model.Process process : bpmnModel.getProcesses()) {
flowNodes.addAll(gatherAllFlowNodes(process));
}
return flowNodes;
}
protected static List gatherAllFlowNodes(
FlowElementsContainer flowElementsContainer) {
List flowNodes = new ArrayList();
for (FlowElement flowElement : flowElementsContainer.getFlowElements()) {
if (flowElement instanceof FlowNode) {
flowNodes.add((FlowNode) flowElement);
}
if (flowElement instanceof FlowElementsContainer) {
flowNodes
.addAll(gatherAllFlowNodes((FlowElementsContainer) flowElement));
}
}
return flowNodes;
}
public void drawHistoryFlow(BufferedImage image, String processInstanceId) {
Graph graph = new ActivitiHistoryGraphBuilder(definition, activityInstances)
.build();
for (Edge edge : graph.getEdges()) {
drawSequenceFlow(image, edge.getName());
}
}
public void drawSequenceFlow(BufferedImage image, String sequenceFlowId) {
Graphics2D graphics = image.createGraphics();
graphics.setPaint(HISTORY_COLOR);
graphics.setStroke(new BasicStroke(2f));
try {
List graphicInfoList = bpmnModel
.getFlowLocationGraphicInfo(sequenceFlowId);
int[] xPoints = new int[graphicInfoList.size()];
int[] yPoints = new int[graphicInfoList.size()];
for (int i = 1; i < graphicInfoList.size(); i++) {
GraphicInfo graphicInfo = graphicInfoList.get(i);
GraphicInfo previousGraphicInfo = graphicInfoList.get(i - 1);
if (i == 1) {
xPoints[0] = (int) previousGraphicInfo.getX() - minX;
yPoints[0] = (int) previousGraphicInfo.getY() - minY;
}
xPoints[i] = (int) graphicInfo.getX() - minX;
yPoints[i] = (int) graphicInfo.getY() - minY;
}
int radius = 15;
Path2D path = new Path2D.Double();
for (int i = 0; i < xPoints.length; i++) {
Integer anchorX = xPoints[i];
Integer anchorY = yPoints[i];
double targetX = anchorX;
double targetY = anchorY;
double ax = 0;
double ay = 0;
double bx = 0;
double by = 0;
double zx = 0;
double zy = 0;
if ((i > 0) && (i < (xPoints.length - 1))) {
Integer cx = anchorX;
Integer cy = anchorY;
// pivot point of prev line
double lineLengthY = yPoints[i] - yPoints[i - 1];
// pivot point of prev line
double lineLengthX = xPoints[i] - xPoints[i - 1];
double lineLength = Math.sqrt(Math.pow(lineLengthY, 2)
+ Math.pow(lineLengthX, 2));
double dx = (lineLengthX * radius) / lineLength;
double dy = (lineLengthY * radius) / lineLength;
targetX = targetX - dx;
targetY = targetY - dy;
// isDefaultConditionAvailable = isDefault && i == 1 && lineLength > 10;
if ((lineLength < (2 * radius)) && (i > 1)) {
targetX = xPoints[i] - (lineLengthX / 2);
targetY = yPoints[i] - (lineLengthY / 2);
}
// pivot point of next line
lineLengthY = yPoints[i + 1] - yPoints[i];
lineLengthX = xPoints[i + 1] - xPoints[i];
lineLength = Math.sqrt(Math.pow(lineLengthY, 2)
+ Math.pow(lineLengthX, 2));
if (lineLength < radius) {
lineLength = radius;
}
dx = (lineLengthX * radius) / lineLength;
dy = (lineLengthY * radius) / lineLength;
double nextSrcX = xPoints[i] + dx;
double nextSrcY = yPoints[i] + dy;
if ((lineLength < (2 * radius))
&& (i < (xPoints.length - 2))) {
nextSrcX = xPoints[i] + (lineLengthX / 2);
nextSrcY = yPoints[i] + (lineLengthY / 2);
}
double dx0 = (cx - targetX) / 3;
double dy0 = (cy - targetY) / 3;
ax = cx - dx0;
ay = cy - dy0;
double dx1 = (cx - nextSrcX) / 3;
double dy1 = (cy - nextSrcY) / 3;
bx = cx - dx1;
by = cy - dy1;
zx = nextSrcX;
zy = nextSrcY;
}
if (i == 0) {
path.moveTo(targetX, targetY);
} else {
path.lineTo(targetX, targetY);
}
if ((i > 0) && (i < (xPoints.length - 1))) {
// add curve
path.curveTo(ax, ay, bx, by, zx, zy);
}
}
graphics.draw(path);
// draw arrow
Line2D.Double line = new Line2D.Double(xPoints[xPoints.length - 2],
yPoints[xPoints.length - 2], xPoints[xPoints.length - 1],
yPoints[xPoints.length - 1]);
int ARROW_WIDTH = 5;
int doubleArrowWidth = 2 * ARROW_WIDTH;
Polygon arrowHead = new Polygon();
arrowHead.addPoint(0, 0);
arrowHead.addPoint(-ARROW_WIDTH, -doubleArrowWidth);
arrowHead.addPoint(ARROW_WIDTH, -doubleArrowWidth);
AffineTransform transformation = new AffineTransform();
transformation.setToIdentity();
double angle = Math.atan2(line.y2 - line.y1, line.x2 - line.x1);
transformation.translate(line.x2, line.y2);
transformation.rotate((angle - (Math.PI / 2d)));
AffineTransform originalTransformation = graphics.getTransform();
graphics.setTransform(transformation);
graphics.fill(arrowHead);
graphics.setTransform(originalTransformation);
} finally {
graphics.dispose();
}
}
}
package com.zihai.activiti.util;
public class Edge extends GraphElement {
private Node src;
private Node dest;
public Node getSrc() {
return src;
}
public void setSrc(Node src) {
this.src = src;
}
public Node getDest() {
return dest;
}
public void setDest(Node dest) {
this.dest = dest;
}
}
package com.zihai.activiti.util;
import java.util.ArrayList;
import java.util.List;
public class Graph {
private List initials = new ArrayList();
public Node getInitial(){
if(initials.size()>0){
return initials.get(0);
}
return null;
}
public List getNodes() {
List nodes = new ArrayList();
for(Node initial:initials){
visitNode(initial, nodes);
}
return nodes;
}
public void visitNode(Node node, List nodes) {
nodes.add(node);
for (Edge edge : node.getEdges()) {
Node nextNode = edge.getDest();
visitNode(nextNode, nodes);
}
}
public List getEdges() {
List edges = new ArrayList();
for(Node initial:initials){
visitEdge(initial, edges);
}
return edges;
}
public void visitEdge(Node node, List edges) {
for (Edge edge : node.getEdges()) {
edges.add(edge);
Node nextNode = edge.getDest();
visitEdge(nextNode, edges);
}
}
public Node findById(String id) {
for (Node node : this.getNodes()) {
if (id.equals(node.getId())) {
return node;
}
}
return null;
}
public void addInitial(Node currentNode) {
initials.add(currentNode);
}
}
package com.zihai.activiti.util;
public class GraphElement {
private String id;
private String name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
package com.zihai.activiti.util;
import java.io.InputStream;
import org.activiti.engine.impl.interceptor.Command;
import org.activiti.engine.impl.interceptor.CommandContext;
public class HistoryProcessInstanceDiagramCmd implements Command {
protected String historyProcessInstanceId;
protected String[] executionids;
public HistoryProcessInstanceDiagramCmd(String proccessid,String... executionids) {
this.historyProcessInstanceId = proccessid;
this.executionids = executionids;
}
public InputStream execute(CommandContext commandContext) {
try {
CustomProcessDiagramGenerator customProcessDiagramGenerator = new CustomProcessDiagramGenerator();
return customProcessDiagramGenerator
.generateDiagram(historyProcessInstanceId,executionids);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
}