论坛首页 Java企业应用论坛

基于SVG技术的JBPM流程显示

浏览 3193 次
精华帖 (0) :: 良好帖 (4) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2009-05-22   最后修改:2009-05-22
什么是svg :http://zh.wikipedia.org/wiki/SVG



由于我想以流程图驱动任务的执行,所以我就准备把流程图显示到页面上,首先我使用了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,但时间有限未再扩展
生成的图片样子还比较丑,还有很大的优化余地....
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics