浏览 3193 次
锁定老帖子 主题:基于SVG技术的JBPM流程显示
精华帖 (0) :: 良好帖 (4) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2009-05-22
最后修改:2009-05-22
由于我想以流程图驱动任务的执行,所以我就准备把流程图显示到页面上,首先我使用了jbpm自带的流程图,因为它的流程图生成后是一张图片,所以很难在它每个节点上做超链接,当然可以模拟在每个节点上面加个透明的div,这样做我觉得太麻烦,而且可能不够灵活,所以我选择了另一种方式,使用svg技术动态生成流程图。 实现的servlet: (这个servlet也是在网上搜来的然后进行的改造,特此感谢!) public class SvgJbpmImageServlet extends HttpServlet { private static final long serialVersionUID = 1L; private WebApplicationContext wac = null; private Element rootDiagramElement = null; private Token currentToken = null; private String basePath = ""; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { if(wac == null) { System.out.println("初始化Spring上下文"); wac = WebApplicationContextUtils.getRequiredWebApplicationContext(request.getSession().getServletContext()); } // 显示task实例状态图 response.setContentType("image/svg+xml"); String pid = request.getParameter("pid"); response.setCharacterEncod[size=medium][/size]ing("utf-8"); String path = request.getContextPath(); basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path; if(pid != null) { String result = getProcessDefinitionSvgInfoById(Long.parseLong(pid)); if(StringUtils.isNotEmpty(result)) response.getWriter().write(result); } } /** * 根据taskid获得流程定义图字节码 * * @param id taskid * @return */ private String getProcessDefinitionSvgInfoById(final long id) { JbpmTemplate jbpmTemplate = (JbpmTemplate) wac.getBean("jbpmTemplate"); return (String)jbpmTemplate.execute(new JbpmCallback() { public Object doInJbpm(JbpmContext context) { StringBuffer svgInfo = new StringBuffer(); initSvgInfo(svgInfo); try { ProcessDefinition pd = context.getProcessInstance(id).getProcessDefinition(); currentToken = context.getProcessInstance(id).getRootToken(); byte [] gpdBytes = getGpdBytesByPdfId(id); // 获取图形的基础信息 if(gpdBytes==null){ return null; } rootDiagramElement = DocumentHelper.parseText(new String(gpdBytes)).getRootElement(); for(int i=0;i<pd.getNodes().size();i++){ Node n = (Node)pd.getNodes().get(i); int[] boxConstraint = extractBoxConstraint(rootDiagramElement, n.getName()); svgInfo.append(getSvgStringInfo(boxConstraint,n)); } svgInfo.append("</svg>"); if(pd.getFileDefinition() == null) { // message = "没有流程图"; return null; } else { return svgInfo.toString(); } } catch(Exception e) { e.printStackTrace(); // message = "No TaskInstance:" + id; return null; } } }); } private void initSvgInfo(StringBuffer s){ s.append("<?xml version='1.0' encoding='UTF-8'?>"); s.append("<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' "); s.append("'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'>"); s.append("<svg xmlns='http://www.w3.org/2000/svg' version='1.1' "); s.append("width='100%' height='100%'>"); s.append("<desc>Markers</desc> "); s.append("<defs>"); s.append("<marker id='arrow' viewBox='0 0 20 20' refX='0' refY='10' markerUnits='strokeWidth' markerWidth='9' markerHeight='30' orient='auto'> "); s.append(" <path d='M 0 0 L 20 10 L 0 20 z' fill='white' stroke='black'/> "); s.append(" </marker> "); s.append(" </defs> "); s.append("<defs>"); s.append("<linearGradient id='orange_red' x1='0%' y1='0%' x2='0%' y2='100%'>"); s.append("<stop offset='0%' style='stop-color:#e6e6e6; stop-opacity:1'/>"); s.append("<stop offset='100%' style='stop-color:#ffffff; stop-opacity:1'/>"); s.append("</linearGradient>"); s.append("</defs>"); } private String getSvgStringInfo(int[] xy,Node n){ String s = ""; s +=" <a xlink:href='"+basePath+PurchaseConstants.getNodeLinkMap().get(n.getName())+"'>"; if(n instanceof Decision){//如果是个决策节点就以菱形显示 s+="<polygon points='"+(xy[0]+xy[2]/2)+","+(xy[1]+xy[3])+" "+(xy[0]+xy[2])+","+(xy[1]+xy[3]/2)+" "+(xy[0]+xy[2]/2)+","+xy[1]+" "+(xy[0])+","+(xy[1]+xy[3]/2)+"' style='fill:url(#orange_red);stroke-width:1;stroke:#bebebe'/>"; } else//否则以矩形显示 { if(n.getName().equals(this.currentToken.getNode().getName())) s +="<rect x='"+xy[0]+"' y='"+xy[1]+"' width='"+xy[2]+"' height='"+xy[3]+"' style='fill:url(#orange_red);stroke-width:1;stroke:red' />"; else s +="<rect x='"+xy[0]+"' y='"+xy[1]+"' width='"+xy[2]+"' height='"+xy[3]+"' style='fill:url(#orange_red);stroke-width:1;stroke:#bebebe' />"; } s +="<text style='fill:black;' y='"+(xy[1]+xy[3]/2)+"' x='"+(xy[0]+35)+"'>"+n.getName()+"</text>"; s +="</a>"; List list =n.getLeavingTransitionsList(); for(int i=0;i<list.size();i++){ Transition t = (Transition)list.get(i); Node to = t.getTo(); int[] toXY = extractBoxConstraint(rootDiagramElement, to.getName()); s +="<g id='chart' stroke='purple' stroke-width='1' fill='none' marker-end='url(#arrow)' >"; if(to instanceof Decision){ //如果是个决策节点就直接指向它的上边的点 s +="<path d='M"+(xy[0]+xy[2]/2)+" "+(xy[1]+xy[3])+" L"+(toXY[0]+toXY[2]/2+10)+" "+(toXY[1]-5)+"'/>"; } else { int[] param=new int[]{(xy[0]+xy[2]/2),(xy[1]+xy[3])}; double[][] rectXY = new double[4][2]; rectXY[0] =new double[]{toXY[0],(toXY[1]+toXY[3]/2)};//节点矩形左边中间点 rectXY[1] =new double[]{toXY[0]+toXY[2]/2,(toXY[1])};//节点矩形上边中间点 rectXY[2] =new double[]{toXY[0]+toXY[2]/2,(toXY[1]+toXY[3])};//节点矩形下边中间点 rectXY[3] =new double[]{toXY[0]+toXY[2],(toXY[1]+toXY[3]/2)};//节点矩形右边中间点 double[] d = calcu(param,rectXY);//求从起始点到目标节点的最短路径 s +="<path d='M"+(xy[0]+xy[2]/2)+" "+(xy[1]+xy[3])+" L"+(d[0]-10)+" "+(d[1]-10)+"'/>"; } s +="</g>"; } return s; } private double[] calcu(int a[],double x[][]){ double[] r = new double[4]; double min = 100000; int w = 0; for(int i=0;i<4;i++){ r[i] = Math.sqrt(Math.pow(a[0]-x[i][0],2)+Math.pow(a[1]-x[i][1],2)); //获取最大的r[i] 并且记录i } for(int j=0;j<4;j++){ if(r[j]<min) min = r[j]; } for(int z = 0;z<4;z++){ if(r[z]==min) w = z; } return x[w]; } private byte[] getGpdBytesByPdfId(final long id) { JbpmTemplate jbpmTemplate = (JbpmTemplate) wac.getBean("jbpmTemplate"); return (byte[])jbpmTemplate.execute(new JbpmCallback() { public Object doInJbpm(JbpmContext context) { try { ProcessDefinition pd = context.getProcessInstance(id).getProcessDefinition(); if(pd.getFileDefinition() == null) { // message = "没有流程图"; return null; } else { return pd.getFileDefinition().getBytes("gpd.xml"); } } catch(Exception e) { e.printStackTrace(); // message = "No TaskInstance:" + id; return null; } } }); } /** * 提取座标及宽高 * * @param root 根节点 * @param token * @return */ private int[] extractBoxConstraint(Element root, String nodeName) { int[] result = new int[4]; XPath xPath = new DefaultXPath("//node[@name='" + nodeName + "']"); Element node = (Element) xPath.selectSingleNode(root); result[0] = Integer.valueOf(node.attribute("x").getValue()).intValue(); result[1] = Integer.valueOf(node.attribute("y").getValue()).intValue(); result[2] = Integer.valueOf(node.attribute("width").getValue()) .intValue(); result[3] = Integer.valueOf(node.attribute("height").getValue()) .intValue(); return result; } } 以上servlet并没有经过严格的单元测试,使用者自行负责 还有个问题是 我想在任务节点上加超链接,但是这里的链接都是写死的,如果灵活的话可以扩展jbpm的Node,但时间有限未再扩展 生成的图片样子还比较丑,还有很大的优化余地.... 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |