Activiti新手常見的問題是,部署成功流程后,獲取顯示的流程圖片(PNG)為亂碼,主要體現為中文無法正確顯示。在這里分析一下亂碼出現的原因,以及解決方案。不喜歡問為什么的同學可以直接跳到解決方法段落。
表現
Activiti流程圖亂碼常見有兩種情況:
-
所有中文字符變成方塊
-
所有中文字符變成無意義漢字
造成這兩種情況的錯誤原因以及解決方法并不相同,但都與Activiti部署、生成流程圖的方法有關。下面先介紹Activiti的流程圖生成方式。
背景介紹
Activiti中,使用的流程定義一般都是符合BPMN2.0標準的xml文本文件,后綴可以是.bpmn20.xml,.xml。其中包含了流程的全部定義內容,包括各節點、節點關聯關系,以及用于定義顯示的DI元素。
在部署流程定義時,Activiti引擎會判斷,是否同時提供了流程圖文件?如果一起提供了流程圖文件,Activiti就省事了,直接使用這個文件作為流程圖。
一般來說我們都不會先制作好流程圖文件再部署,也就是說,部署時只有一個xml文件。這時候Activiti就需要自己生成對應的流程圖文件了。
流程圖文件會保存在Activiti的數據庫ACT_GE_BYTEARRAY表中,作為BLOB保存。每個流程對應一個流程圖文件。所以流程圖在部署時就已經確定,除非重新部署或手動處理,否則不管配置怎么修改,顯示的都是最初的流程圖。
Activiti用于生成流程圖的工具類是
org.activiti.image.impl.DefaultProcessDiagramGenerator
這個類不止可以生成流程圖,還可以生成流程運行狀態圖。具體可以參閱其中各方法的注釋。
出錯原因分析
中文字符變成方塊
在部署流程時,生成流程圖的代碼位于
org.activiti.engine.impl.bpmn.deployer.BpmnDeployer.deploy():154 (Activiti 5.22中)
byte[] diagramBytes = IoUtil.readInputStream(processEngineConfiguration.
getProcessDiagramGenerator().generateDiagram(bpmnParse.getBpmnModel(), "png", processEngineConfiguration.getActivityFontName(),
processEngineConfiguration.getLabelFontName(),processEngineConfiguration.getAnnotationFontName(), processEngineConfiguration.getClassLoader()), null);
可見在這里,需要在processEngineConfiguration里,保存有正確的LabelFontName,以及AnnotationFontName作為參數,Generator才能正確生成(非英文)的流程圖片。
中文字符變成無意義漢字
出現這種問題,基本上都是在Activiti提供的demo程序——Explorer中設計、部署流程的時候出現的。原因是demo程序有bug。
Activiti Explorer中提供的Activiti Modeler,是一個Web流程設計器。用于編輯、保存流程模型。這里請注意,不能用于新建,它生成的也只是流程模型,不是流程定義。生成的流程模型是Json格式的,也保存在ACT_GE_BYTEARRAY表中。
然后在Activiti Explorer中提供了“部署”的操作。對應的代碼為(Activiti 5.22中)(實際有兩個部署方式,不過畫線部署的是這個。另一個是填表單方式部署,問題類似)
org.activiti.editor.ui.EditorProcessDefinitionDetailPanel.deployModelerModel()
protected void deployModelerModel(final ObjectNode modelNode) {
BpmnModel model = new BpmnJsonConverter().convertToBpmnModel(modelNode);
byte[] bpmnBytes = new BpmnXMLConverter().convertToXML(model);
String processName = modelData.getName() + ".bpmn20.xml";
Deployment deployment = repositoryService.createDeployment()
.name(modelData.getName())
.addString(processName, new String(bpmnBytes))
.deploy();
ExplorerApp.get().getViewManager().showDeploymentPage(deployment.getId());
}
大意是,將Modeler的數據格式(Json modelNode),轉換為Activiti內部交換格式(BpmnModel model),再轉成xmlbyte(byte[] bpmnBytes),然后在部署的時候再作為String加入部署
.addString(processName, new String(bpmnBytes))
很繞是不是?Activiti的開發者也把自己繞暈了,導致這里出現了bug。
public byte[] convertToXML(BpmnModel model) {
return convertToXML(model, DEFAULT_ENCODING);
}
轉換為xmlbyte的方法里,指定了編碼方式(為UTF-8)。但是再轉回字符串的時候,卻沒有指定編碼方式!
new String(bpmnBytes)
在未指定編碼方式的時候,new String使用jvm定義的默認編碼方式解析,而我們一般使用的都是gb2312,因此導致問題。
解決方法
再次強調,修改之后,需要重新部署或手動生成流程圖片,才能看到效果!
中文字符變成方塊
在Activiti的配置中,加上字體配置即可。
對于Spring用戶,在Spring配置文件中找到Activiti流程引擎定義的地方
<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
<property name="dataSource" ref="dataSource"/>
<property name="transactionManager" ref="transactionManager"/>
<property name="databaseSchemaUpdate" value="true"/>
...
在其中加上幾個參數(按照Activiti的版本不同,參數數量不一定。用IDE提示,把所有帶有font的都設置上就好了)。字體可以按照喜好設置,但需要保證tomcat運行時可以找到(例如默認安裝的linux服務器很可能就沒有)。
<property name="activityFontName" value="宋體"/>
<property name="labelFontName" value="宋體"/>
<property name="annotationFontName" value="宋體"/>
重啟tomcat使配置生效,重新部署流程以重新生成流程圖。方塊字就ok啦。
中文字符變成無意義漢字
由于問題出在編碼方式上,因此有幾種修改方式
1. 修改jvm默認參數。
在tomcat的vm運行參數上,加上-Dfile.encoding=UTF-8。不過副作用是導致整個項目都運行在utf-8下,對于寫的不嚴謹的項目,可能導致其它地方默認使用gb2312編碼的代碼出錯。
2. 修改Explorer部署部分的代碼
將org.activiti.editor.ui.EditorProcessDefinitionDetailPanel.deployModelerModel():348
修改為.addString(processName, new String(bpmnBytes, "UTF-8"))
即可。
可以直接修改activiti的源碼,編譯后使用。
也可以在自己的項目下,手動創建
org.activiti.editor.ui.EditorProcessDefinitionDetailPanel
類,把Activiti的源碼貼進去,再修改正確。這樣我們重寫的類就會由classloader優先加載,覆蓋Activiti自己的實現,達到修改的目的。
3. 說到底Explorer只是Activiti提供的demo樣例。自己寫的時候,可以參考Explorer的代碼,可別直接拿來用哦。
顯示字符為空白
這個嚴格來說并不是“亂碼”,解決方法也很簡單:畫流程圖的時候,少寫幾個字,或者把框框拖動搞大一點就可以了~