Grails + Activiti + Activiti Modeler 整合詳解
- 本次整合使用 activiti 的版本爲5.22
- 需要下載的文件 war包 和 源碼
-
下載jar地址:https://github.com/Activiti/Activiti/releases/tag/activiti-5.22.0
-
下載完成後,將兩個zip文件解壓
activiti-5.22.0 目錄下找到 wars 裏面的兩個 war包,將它們放到 tomcat 的 webapp 目錄下,啓動tomcat ,啓動後,我們修改解壓後的db.db.properties 文件,文件所在目錄: activiti-explorer\WEB-INF\classes 下
訪問地址:http://localhost:8090/activiti-explorer/ 就可以看見它自身所帶的流程前端控制器
在你連接的數據中的 act_id_user 表中,選擇一個用戶進行登錄 就可以看到 它身的 前端流程控制器
搭建屬於我們自己的前端流程控制器 grails + activiti 5.22
// https://mvnrepository.com/artifact/org.activiti/activiti-engine
compile group: 'org.activiti', name: 'activiti-engine', version: '5.22.0'
// https://mvnrepository.com/artifact/org.activiti/activiti-spring
compile group: 'org.activiti', name: 'activiti-spring', version: '5.22.0'
// https://mvnrepository.com/artifact/org.activiti/activiti-bpmn-model
compile group: 'org.activiti', name: 'activiti-bpmn-model', version: '5.22.0'
// https://mvnrepository.com/artifact/org.activiti/activiti-json-converter
compile group: 'org.activiti', name: 'activiti-json-converter', version: '5.22.0'
// https://mvnrepository.com/artifact/org.activiti/activiti-bpmn-layout
compile group: 'org.activiti', name: 'activiti-bpmn-layout', version: '5.22.0'
// https://mvnrepository.com/artifact/org.apache.xmlgraphics/batik-transcoder
compile group: 'org.apache.xmlgraphics', name: 'batik-transcoder', version: '1.7'
// https://mvnrepository.com/artifact/org.apache.xmlgraphics/batik-css
compile group: 'org.apache.xmlgraphics', name: 'batik-css', version: '1.7'
// https://mvnrepository.com/artifact/org.apache.xmlgraphics/batik-svg-dom
compile group: 'org.apache.xmlgraphics', name: 'batik-svg-dom', version: '1.7'
// https://mvnrepository.com/artifact/org.apache.xmlgraphics/batik-svggen
compile group: 'org.apache.xmlgraphics', name: 'batik-svggen', version: '1.7'
// https://mvnrepository.com/artifact/org.apache.xmlgraphics/batik-codec
compile group: 'org.apache.xmlgraphics', name: 'batik-codec', version: '1.7'
// https://mvnrepository.com/artifact/mysql/mysql-connector-java
compile group: 'mysql', name: 'mysql-connector-java', version: '5.1.17'
5.將 Activiti-activiti-5.22.0\modules\activiti-webapp-explorer2\src\main\webapp 目錄下的兩個目錄和一個個文件複製到 grails 的webapp 目錄下面
- idea 創建一個 grails 項目
- 到 maven 創庫中去導入所需要的 activiti 5.22 的 jar
- maven 創庫地址:http://mvnrepository.com/
- 需要導入的 jar (activi 工作流引擎的jar 以及 avg 生產圖片的 jar)
6.將 Activiti-activiti-5.22.0\modules\activiti-webapp-explorer2\src\main\resources 下的 stencilset.json 複製到 conf 目錄下
7.將 Activiti-activiti-5.22.0\modules\activiti-modeler\src\main\java\org\activiti\rest\editor 目錄下的 兩個目錄裏面的三個 java 文件取出來
StencilsetRestResource.java : 頁面顯示資源的獲取的接口 修改爲 groovy 語法格式
ModelEditorJsonRestResource.java :流程模型資源的獲取的接口 修改爲groovy 語法格式
ModelSaveRestResource.java :流程模型獲取後修改編輯保存的接口 修改爲 groovy 語法格式
8.修改後的代碼如下:
package com.activiti
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.node.ObjectNode
import grails.converters.JSON
import grails.io.IOUtils
import org.activiti.bpmn.converter.BpmnXMLConverter
import org.activiti.bpmn.model.BpmnModel
import org.activiti.editor.constants.ModelDataJsonConstants
import org.activiti.editor.language.json.converter.BpmnJsonConverter
import org.activiti.engine.ActivitiException
import org.activiti.engine.ProcessEngine
import org.activiti.engine.ProcessEngines
import org.activiti.engine.RepositoryService
import org.activiti.engine.repository.Deployment
import org.activiti.engine.repository.Model
import org.activiti.engine.runtime.ProcessInstance
import org.apache.batik.transcoder.TranscoderInput
import org.apache.batik.transcoder.TranscoderOutput
import org.apache.batik.transcoder.image.PNGTranscoder
import org.apache.commons.lang.StringUtils
class ActivitController {
def index() {
}
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine()
RepositoryService repositoryService = processEngine.getRepositoryService()
ObjectMapper objectMapper = new ObjectMapper()
/**根據Id獲取創建的模板信息*/
def getEditorJson(){
def record = request.JSON ?: params
ObjectNode modelNode = null
Model model = repositoryService.getModel(record.id)
if (model != null) {
try {
if (StringUtils.isNotEmpty(model.getMetaInfo())) {
modelNode = (ObjectNode) objectMapper.readTree(model.getMetaInfo())
} else {
modelNode = objectMapper.createObjectNode()
modelNode.put("name", model.getName())
}
modelNode.put("modelId", model.getId())
ObjectNode editorJsonNode = (ObjectNode) objectMapper.readTree(new String(repositoryService.getModelEditorSource(model.getId()), "utf-8"))
modelNode.put("model",editorJsonNode)
} catch (Exception e) {
throw new ActivitiException("Error creating model JSON", e)
}
}
render modelNode
}
/**保存修改的模型*/
def saveModel() {
def record = params
try {
Model model = repositoryService.getModel(record.id)
ObjectNode modelJson = (ObjectNode) objectMapper.readTree(model.getMetaInfo())
modelJson.put("name", record.name);
modelJson.put("description", record.description)
model.setMetaInfo(modelJson.toString())
model.setName(record.name)
repositoryService.saveModel(model)
repositoryService.addModelEditorSource(model.getId(), record.json_xml.getBytes("utf-8"))
InputStream svgStream = new ByteArrayInputStream(record.svg_xml.getBytes("utf-8"))
TranscoderInput input = new TranscoderInput(svgStream)
PNGTranscoder transcoder = new PNGTranscoder()
ByteArrayOutputStream outStream = new ByteArrayOutputStream()
TranscoderOutput output = new TranscoderOutput(outStream)
transcoder.transcode(input, output)
final byte[] result = outStream.toByteArray()
repositoryService.addModelEditorSourceExtra(model.getId(), result)
outStream.close()
} catch(Exception e){
throw new ActivitiException("Error saving model", e)
}
render true
}
def getStencilset() {
InputStream stencilsetStream = this.getClass().getClassLoader().getResourceAsStream("stencilset.json")
try {
render IOUtils.toString(stencilsetStream, "utf-8")
} catch (Exception e) {
throw new ActivitiException("Error while loading stencil set", e)
}
}
/** 新建一個空模型 */
def newModel(){
def record = request.JSON ?: params
def modelId = []
RepositoryService repositoryService = processEngine.getRepositoryService()
//初始化一個空模型
Model model = repositoryService.newModel()
//設置一些默認信息
int revision = 1
String key = "process"
ObjectNode modelNode = objectMapper.createObjectNode()
modelNode.put(ModelDataJsonConstants.MODEL_NAME, record.name)
modelNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, record.description)
modelNode.put(ModelDataJsonConstants.MODEL_REVISION, revision)
model.setName(record.name)
model.setKey(key)
model.setMetaInfo(modelNode.toString())
repositoryService.saveModel(model)
String id = model.getId()
//完善ModelEditorSource
ObjectNode editorNode = objectMapper.createObjectNode()
editorNode.put("id", "canvas")
editorNode.put("resourceId", "canvas")
ObjectNode stencilSetNode = objectMapper.createObjectNode()
stencilSetNode.put("namespace", "http://b3mn.org/stencilset/bpmn2.0#")
editorNode.put("stencilset", stencilSetNode)
repositoryService.addModelEditorSource(id,editorNode.toString().getBytes("utf-8"))
modelId.add(id)
render modelId
}
/** 獲取所有模型 */
def modelList(){
RepositoryService repositoryService = processEngine.getRepositoryService()
render repositoryService.createModelQuery().list() as JSON
}
/** 發佈模型爲流程定義 */
def deploy(){
def record = request.JSON ?: params
//獲取模型
Model modelData = repositoryService.getModel(record.id)
byte[] bytes = repositoryService.getModelEditorSource(modelData.getId())
if (bytes == null) {
render "模型數據爲空,請先設計流程併成功保存,再進行發佈"
}
JsonNode modelNode = new ObjectMapper().readTree(bytes)
BpmnModel model = new BpmnJsonConverter().convertToBpmnModel(modelNode)
if(model.getProcesses().size()==0){
render "數據模型不符要求,請至少設計一條主線流程"
}
byte[] bpmnBytes = new BpmnXMLConverter().convertToXML(model)
//發佈流程
String processName = modelData.getName() + ".bpmn20.xml"
Deployment deployment = repositoryService.createDeployment()
.name(modelData.getName())
.addString(processName, new String(bpmnBytes, "UTF-8"))
.deploy()
modelData.setDeploymentId(deployment.getId())
repositoryService.saveModel(modelData)
render true
}
/**啓動流程*/
def startProcess() {
def record = request.JSON ?: params
ProcessInstance process = processEngine.getRuntimeService().startProcessInstanceByKey(record.keyName)
render process.getId() + " : " + process.getProcessDefinitionId()
}
}
9.編輯 訪問路徑
設置 UrlMappings 訪問路徑
package activitimodeler
class UrlMappings {
static mappings = {
"/$controller/$action?/$id?(.$format)?"{
constraints {
// apply constraints here
}
}
get "/$controller/$id(.$format)?/json"(action:"getEditorJson")
put "/$controller/$id(.$format)?/save"(action:"saveModel")
get "/$controller/editor/stencilset"(action: "getStencilset")
post "/$controller/create"(action: "newModel")
get "/$controller/modelList"(action: "modelList")
get "/$controller/deploy"(action: "deploy")
get "/$controller/start"(action: "startProcess")
"500"(view:'/error')
"404"(view:'/notFound')
}
}
修改前端訪問接口的路徑
editor-app目錄下的app-cfg.js 文件 'contextRoot' : '/Activit', 設置訪問後臺接口路徑的前綴
editor-app——>configuration——>url-config.js 設置訪問路徑 接口的路徑
editor-app ——>enitor——>oryx.debug.js 設置訪問 獲取 stencilset.json(前臺資源文件) 的路徑
最後將 stencilset.json 文件 以及 editor-app\i18n\en.json 文件 變爲 中文解釋
當這一切都準備好了之後,啓動 grails ,啓動完成後 訪問地址:http://localhost:8080/modeler.html 就可以看到 頁面上的信息
代碼中的功能涉及:模板創建、獲取所有模板、獲取單個模板、修改模板、保存修改的模板、發佈模型爲流程定義、啓動流程