当使用jbpm时,正常情况下是要显示未知数量的业务表单页面。
一般情况下,在使用struts2(本人使用的情况下)的一般情况下,页面控制是已经写在struts的配置文件当中,也就是说要显示的页面基本是已知的了(未知页面,显然是无法配置的),因此一般情况下,当有新业务时,或者业务流程发生变化时(任务节点新增与删除等),就会导致业务表单页面的新增与删除,然后这种以xml配置方式,显然是无法满足需求的。
我们知道在使用springmvc时,viewname是可以在程序里指定,这样业务表单也是可以在程序里指定的,可以避免xml配置的不灵活性。显然struts2肯定也应该提供了这样的方式。
扩展以下两个类即可:
1、StrutsActionProxyFactory;
2、DefaultActionInvocation;
我们需要覆盖DefaultActionInvocation.createResult方法,
因为该方法的默认实现是从resultmappings里根据resultcode(action相关方法返回值)获取视图的,因此如果业务表单为在struts2的配置文件里进行配置,应该是会抛出异常。因此,我们只要覆盖该方法,并根据resultcode寻找视图(而不是从resultmapping里寻找)。一般情况,resultcode可以是业务表单页面的相对路径。
为了使用我们自己的ActionInvocation,就必须覆盖StrutsActionProxyFactory.createActionProxy方法,因为该方法已经指定返回StrutsActionProxy,我们覆盖StrutsActionProxyFactory.createActionProxy方法,返回我们自己的ActionInvocation。
具体实现如下:
public class IcomStrutsActionInvocation extends DefaultActionInvocation { public static final String JBPM_PREFIX = "icom:"; public IcomStrutsActionInvocation(Map<String, Object> extraContext, boolean pushAction) { super(extraContext, pushAction); } /** * */ private static final long serialVersionUID = 1L; public Result createResult() throws Exception { if (resultCode != null && resultCode.startsWith(JBPM_PREFIX)) {// 如果是工作流表单(返回代码必须以icom:开头哦) Map<String, String> resultParams = new HashMap<String, String>(); resultParams.put("location", resultCode.replace(JBPM_PREFIX, "")); ResultConfig resultConfig = new ResultConfig.Builder(resultCode, ServletDispatcherResult.class.getName()).addParams(resultParams) .build(); return objectFactory.buildResult(resultConfig, invocationContext .getContextMap()); } else { return super.createResult(); } } }
public class IcomStrtuts2ActionFactory extends StrutsActionProxyFactory { public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map<String, Object> extraContext, boolean executeResult, boolean cleanupContext) { ActionInvocation inv = new IcomStrutsActionInvocation(extraContext, true); container.inject(inv); return super.createActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext); } }
因为struts-default.xml文件是没有配置我们自己的实现类,因此需要在struts的配置文件添加我们自己的相关实现类,配置如下:
<struts> <bean class="com.icom.tech.struts2.IcomStrtuts2ActionFactory" name="default" type="com.opensymphony.xwork2.ActionProxyFactory" /> ... </struts>
struts-action返回resultcode例子:
public String forTask() throws Exception { TaskService taskService = processEngine.getTaskService(); task = taskService.getTask(taskId); if (null == task) throw new NullPointerException("id为:" + taskId + " 的任务已经被处理..."); taskService.getVariableNames(taskId); Map<String, Object> variables = taskService.getVariables(taskId, taskService.getVariableNames(taskId)); if (!(null == variables || variables.isEmpty())) { ValueStack stack = ActionContext.getContext().getValueStack(); Iterator<String> it = variables.keySet().iterator(); while (it.hasNext()) { String key = it.next(); logger.info("将要压入的参数:" + key + " 值为:" + variables.get(key)); Object value = variables.get(key); stack.set(key, value); } } if(flag){ // 获取表单页面路径信息 return IcomStrutsActionInvocation.JBPM_PREFIX+retriveFormResource(); } return IcomStrutsActionInvocation.JBPM_PREFIX + task.getFormResourceName(); } private String retriveFormResource() throws Exception { String formResourceName = task.getFormResourceName(); String excutionId = task.getExecutionId(); Execution e = processEngine.getExecutionService().findExecutionById( excutionId); ProcessDefinition pd = processEngine.getRepositoryService() .createProcessDefinitionQuery().processDefinitionId( e.getProcessDefinitionId()).uniqueResult(); DeploymentImpl deployment = (DeploymentImpl) processEngine .getRepositoryService().createDeploymentQuery().deploymentId( pd.getDeploymentId()).uniqueResult(); String path = deployment.getId() + "/" + pd.getId() + "/" + formResourceName; File formFile = new File(formBaseDir, path); // 首先判断文件是否存在,不存在则创建表单文件 if (!formFile.exists()) synchronized (processEngine) { if(!formFile.getParentFile().exists()){ formFile.getParentFile().mkdirs(); } byte[] buf = deployment.getBytes(formResourceName); // 写入特定存放目录中 FileOutputStream fos = new FileOutputStream(formFile); fos.write(buf); fos.close(); } return DEFAULT_FORM_BASE_DIR + path; }
以下是jpdl编写的流程定义文件片段:
<task assignee="${user}" name="材料补正" form="printSupplyMaterial.jsp"> <on event="start"> <event-listener factory="businessInstanceListener" method="getInstance" /> </on> <on event="timeout"> <timer duedate="${tc_supplyMaterial_timeout}" /> <event-listener class="com.icom.cpb.workflow.listener.ChangeColorListener" auto-wire="true" > <property name="fontColor"> <string value="${tc_supplyMaterial_timeout_color}"/> </property> <property name="businessInstanceService"> <ref object="businessInstanceService"/> </property> </event-listener> </on> <transition name="未补全" to="不受理" > <event-listener class="com.icom.cpb.workflow.listener.IncreaseDocNumberListner" > <property name="userInfoService"> <ref object="userInfoService" /> </property> <property name="docNumberService"> <ref object="docNumberService" /> </property> <property name="docnumberName"> <string value="不予受理" /> </property> <property name="variableName"> <string value="recode" /> </property> </event-listener> <event-listener class="com.icom.cpb.workflow.listener.ResetCreateTimeListener"> <property name="businessInstanceService"> <ref object="businessInstanceService"/> </property> </event-listener> </transition> <!-- <transition name="已补全" to="受理" > <event-listener class="com.icom.cpb.workflow.listener.IncreaseDocNumberListner" > <property name="userInfoService"> <ref object="userInfoService" /> </property> <property name="docNumberService"> <ref object="docNumberService" /> </property> <property name="docnumberName"> <string value="受理" /> </property> <property name="variableName"> <string value="accode" /> </property> </event-listener> </transition> --> <transition name="已补全" to="受理" > <event-listener class="com.icom.cpb.workflow.listener.ClearMaterialsListener" > <property name="businessInstanceService"> <ref object="businessInstanceService"/> </property> <property name="receivedDocInfoService"> <ref object="receivedDocInfoService"/> </property> </event-listener> <event-listener class="com.icom.cpb.workflow.listener.ResetCreateTimeListener"> <property name="businessInstanceService"> <ref object="businessInstanceService"/> </property> </event-listener> <event-listener class="com.icom.cpb.workflow.listener.IncreaseDocNumberListner" > <property name="userInfoService"> <ref object="userInfoService" /> </property> <property name="docNumberService"> <ref object="docNumberService" /> </property> <property name="docnumberName"> <string value="受理" /> </property> <property name="variableName"> <string value="accode" /> </property> </event-listener> </transition> </task>