Activiti——流程任务详解(二)

Activiti流程任务

4、服务任务

服务任务用于请求流程外部服务或者自动执行程序,Activiti为服务任务提供了三种实现:Java Service Task、Web Service Task和Shell Task,其中Java Service Task允许直接提供Java类,当流程到达该任务时,执行相应的Java类,当流程到达Web Service Task时,会自动调用预先定义好的Web Service,Shell Task则会执行Shell命令。

BPMN2.0中只提供了serviceTask来表示服务任务,因此Activiti的这三种Task,均使用serviceTask进行配置。另外,除了这三种任务可以使用serviceTask来配置外,Email Task同样可以使用serviceTask,但是这两种Task也支持使用sendTask。

4.1、Java 服务任务

Activiti中提供了Java Service Task用于执行Java程序,图所示为Java Service Task的流程图。
在这里插入图片描述

使用Service Task执行Java程序有以下4种途径。

  • 使用activiti:class属性指定一个Java类,但是该Java类必须是JavaDelegate或者ActivityBehavior的实现类。
  • 使用activiti:delegateExpression属性并配合UEL表达式指定一个流程参数的实例,该实例的Java类同样需要是JavaDelegate或者ActivityBehavior的实现类,并且需要实现序列化接口。
  • 使用activiti::expression属性配合JUEL表达式指定一个流程参数,该参数是一个对象的实例,并且需要指定使用的方法。
  • 使用activiti::expression属性配合JUEL表达式指定一个流程参数,该参数是一个对象的实例,需要指定使用的对象的属性,该对象需要为这个使用的属性提供getter方法。

注意,如果在配置activiti:class属性时,使用了实现ActivityBehavior接口的方式,那么有可能会对流程的走向产生影响,因此Activiti的官方文档并不推荐使用该方式来指定Java类,并且在Activiti开放的API中也找不到该接口,因此本章不会涉及该部分内容。

4.2、实现JavaDelegate

为serviceTask提供activiti:class属性可以指定执行的Java类,配置时需要提供全限定的Java类名,被指定的Java类必须实现JavaDelegate接口,但不需要实现序列化接口。Activiti在解析流程文件时,会将配置的值缓存起来,当流程到达Service Task时,会使用Java的反射
将类初始化,因此在实现JavaDelegate时,需要为其提供一个无参数的构造器,否则将抛出异常,提示无法进行实例化。

代码中定义了两个Service Task,同时使用了activiti:class属性,指定了同样的JavaDelegate,代码清单12-22为对应的JavaDelegate类的实现以及运行代码。

<process id="process1" name="process1">
	<startEvent id="startevent1" name="Start"></startEvent>
	<serviceTask id="servicetask1" name="Service Task 1"
		activiti:class="pers.zhang.delegate.MyJavaDelegate"></serviceTask>
	<serviceTask id="servicetask4" name="Service Task 2"
		activiti:class="pers.zhang.delegate.MyJavaDelegate"></serviceTask>
	<endEvent id="endevent1" name="End"></endEvent>
	<sequenceFlow id="flow1" name="" sourceRef="startevent1"
		targetRef="servicetask1"></sequenceFlow>
	<sequenceFlow id="flow6" name="" sourceRef="servicetask1"
		targetRef="servicetask4"></sequenceFlow>
	<sequenceFlow id="flow7" name="" sourceRef="servicetask4"
		targetRef="endevent1"></sequenceFlow>
</process>
public class MyJavaDelegate implements JavaDelegate {
    public void execute(DelegateExecution execution) {
        System.out.println("实现JavaDelegate的JavaServiceTask:" + this);
    }
}
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = engine.getRepositoryService();
RuntimeService runtimeService = engine.getRuntimeService();
repositoryService.createDeployment().addClasspathResource("demo12/ImplementServiceTask.bpmn").deploy();
// 启动流程
ProcessInstance pi = runtimeService.startProcessInstanceByKey("process1");
实现JavaDelegateJavaServiceTaskpers.zhang.delegate.MyJavaDelegate@724bade8
实现JavaDelegateJavaServiceTaskpers.zhang.delegate.MyJavaDelegate@16fb356

根据输出的结果可以看出,使用这种方式,每次都会为JavaDelegate创建新的实例。除了这种方法外,还可以使用activiti:delegateExpression属性结合JUEL表达式来配置执行的Java
类:activiti:delegateExpression="${myDelegate}",使用这种方法配置运行的Java类,同样需要实现JavaDelegate接口,但是创建JavaDelegate实例的过程将会交由提供者实现,提供者创建JavaDelegate的实例后,需要将这个实例设置到流程参数中。

<process id="process1" name="process1" isExecutable="true">
	<startEvent id="startevent1" name="Start"></startEvent>
	<serviceTask id="servicetask1" name="Service Task"
		activiti:delegateExpression="${myDelegate}"></serviceTask>
	<serviceTask id="servicetask2" name="Service Task"
		activiti:delegateExpression="${myDelegate}"></serviceTask>
	<endEvent id="endevent1" name="End"></endEvent>
	<sequenceFlow id="flow1" sourceRef="startevent1"
		targetRef="servicetask1"></sequenceFlow>
	<sequenceFlow id="flow2" sourceRef="servicetask1"
		targetRef="servicetask2"></sequenceFlow>
	<sequenceFlow id="flow3" sourceRef="servicetask2"
		targetRef="endevent1"></sequenceFlow>
</process>
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = engine.getRepositoryService();
RuntimeService runtimeService = engine.getRuntimeService();
repositoryService.createDeployment().addClasspathResource("demo12/JUELClass.bpmn").deploy();
Map<String, Object> vars = new HashMap<String, Object>();
vars.put("myDelegate", new MyJavaDelegate());
// 启动流程
ProcessInstance pi = runtimeService.startProcessInstanceByKey("process1", vars);
实现JavaDelegateJavaServiceTaskpers.zhang.delegate.MyJavaDelegate@2427e004
实现JavaDelegateJavaServiceTaskpers.zhang.delegate.MyJavaDelegate@2427e004

根据输出结果可以看出,每次执行输出的均为同一个实例,因为使用对象作为流程参数,所以会将该对象序列化并保存到资源表中,该参数会一直存在,直到流程结束。

4.3、使用普通JavaBean

除了可以使用JavaDelegate的实现类来指定Service Task外,还可以使普通的Java Bean作为执行的程序,使用方法与使用JUEL表达式分配用户任务权限类似:${myBean.method(},除了可以使用这种形式来调用Java方法外,还可以调用JavaBean的属性,并且将其设置到流
程参数中:

<process id="process1" name="process1">
	<startEvent id="startevent1" name="Start"></startEvent>
	<endEvent id="endevent1" name="End"></endEvent>
	<serviceTask id="servicetask1" name="Service Task"
		activiti:expression="${myBean.print(execution)}"></serviceTask>
	<serviceTask id="servicetask2" name="Service Task"
		activiti:expression="${execution.setVariable('myName', myBean.name)}"></serviceTask>
	<sequenceFlow id="flow1" name="" sourceRef="startevent1"
		targetRef="servicetask1"></sequenceFlow>
	<sequenceFlow id="flow2" name="" sourceRef="servicetask1"
		targetRef="servicetask2"></sequenceFlow>
	<userTask id="usertask1" name="End Task"></userTask>
	<sequenceFlow id="flow3" name="" sourceRef="servicetask2"
		targetRef="usertask1"></sequenceFlow>
	<sequenceFlow id="flow4" name="" sourceRef="usertask1"
		targetRef="endevent1"></sequenceFlow>
</process>
public class MyJavaBean implements Serializable {
    
    private String name = "crazyit";

    public String getName() {
        return name;
    }
    
    public void print(Execution exe) {
        System.out.println("使用Java Bean的print方法:" + exe.getId());
    }
}
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = engine.getRepositoryService();
RuntimeService runtimeService = engine.getRuntimeService();
repositoryService.createDeployment().addClasspathResource("demo12/JavaBeanServiceTask.bpmn").deploy();
Map<String, Object> vars = new HashMap<String, Object>();
vars.put("myBean", new MyJavaBean());
// 启动流程
ProcessInstance pi = runtimeService.startProcessInstanceByKey("process1", vars);
// 进行任务参数查询
System.out.println("运行两个Service Task的myName参数值为:"
        + runtimeService.getVariable(pi.getId(), "myName"));
使用Java Bean的print方法:25009
运行两个Service Task的myName参数值为:crazyit

4.4、在Activiti中调用Web Service

Web Service Task是BPMN的规范之一,从另外一个角度看,该任务实际上也是一个发送任务(Send Task),但由于在调用Web Service的过程中,也有可能产生返回值,因此将其归类为服务任务。

如果要在Activiti中调用外部系统的Web Service,也可以在JavaDelegate中实现。在Java中调用Web Service的方法有很多,例如使用Apache的commons-httpclient、Xfire、axis和CXF等。在Activiti中除了可以在JavaDelegate中调用Web Service外,还可以使用BPMN2.0的XML配置来实现Web Service的调用,将相应的Web Service信息放到流程配置文件中,研发人员无须关心Web Service的调用过程。

在Web Service Task中调用外部的Web Service,需要先了解Activiti的几个元素。

4.5、import元素

当流程文件中的内容需要使用到外部的元素时,可以使用import元素来声明一个外部元素定义,定义一个import需要提供以下3个属性。

  • importType:外部元素的类型,例如外部的元素是遵守MLl.0规范的文档,那么需要将该值设置为http:/www.w3.org/200l/XMLSchema。
  • location:外部元素所在的文档路径,如果需要调用Web Service,则提供的是wsdl的路径。
  • namespace:该import元素的命名空间。

以下配置片断定义了一个import元素:

<import importType="http://schemas.xmlsoap.org/wsdl/" location="http://localhost:9090/sale?wsdl" namespace="http://webservice.activiti.crazyit.org/" />

4.6、itemDefinition和message元素

itemDefinition用于定义数据对象或者消息对象,这些对象可以在流程中操作、传输、转换或者存储,一个itemDefinition最重要的是它的结构,BPMN规范对这个数据结构并没有特别的要求,但是需要为其指定相应的语法规则,因此一个itemDefinition的结构会受其指定的语法规则所约束,默认情况下会使用XML作为itemDefinition的数据结构约束,即不指定数据结构规则的话,itemDefinitio需要遵守XML的语法规则。

message用于定义流程参与者之间的交互信息,因此每个message均有相应的格式,在BPMN2.0提供的XML规范中,定义Message的格式由itemDefinition完成,因此一个具有格式的message会引用一个定义好的itemDefinition。以下配置片断定义了一个itemDefinition和
一个message元素。

<message id="myMessage" itemRef="myItem"></message>
<itemDefinition id="myItem" structureRef="元素结构" />

以上的配置片断定义了一个id为“myItem”的itemDefinition,由于需要为itemDefinition指定数据结构约束,因此需要使用structureRef属性,而message元素则引用了itemDefinition。
在上一节中定义的import元素,其类型为wsdl,同时也提供了相应的wsdl和路径,如果在itemDefinition中配置structureRef,那么该structureRef所引用的元素结构必须要在相应的wsdl中体现。

4.7、interface与operation元素

interface元素用于定义服务接口,interface元素下可以定义多个operation元素,表示一个服务下的多个操作。如果有一个支付服务接口,在里面定义了支付明细查询、处理支付等操作,那么此时可以定义一个interface表示该支付服务,再为其定义支付明显细查询操作和处理支付操作。以下配置片断定义了一个interface和一个operation:

<interface name="Payment Service" implementationRef="WSDL中portType的名称">
	<operation id="createPayment" name="Create Payment Operation" implementationRef="WSDL中的操作名称">
		<inMessageRef>WSDL中操作的input message</inMessageRef>
		<outMessageRef>WSDL中操作的output message</outMessageRef>
	</operation>
</interface>

定义了一个operation之后,在使用Serice Task时,可以为serviceTask元素加入operationRef属性来指定该Service Task使用的操作。

4.8、设置Web Service参数与返回值

要定义Web Service的参数与返回值,可以在serviceTask元素中添加dataInputAssociation和dataOutputAssociation子元素,dataInputAssociation元素表示输入的参数,
dataOutputAssociation元素表示执行Web Service后的返回结果

<process id="process1" name="process1" isExecutable="true">
	<serviceTask id="servicetask1" name="Web service invocation" 
		implementation="##WebService" operationRef="createSaleOper">
		<dataInputAssociation>					
			<sourceRef>creatorVar</sourceRef>
			<targetRef>creator</targetRef>
		</dataInputAssociation>
		<dataOutputAssociation>		
			<sourceRef>newSale</sourceRef>
			<targetRef>saleVar</targetRef>
		</dataOutputAssociation>
	</serviceTask>
</process>
<itemDefinition id="newSale" structureRef="sale:sale" />
<itemDefinition id="saleVar" structureRef="string" />
<itemDefinition id="creatorVar" structureRef="string" />
<itemDefinition id="creator" structureRef="string" />

代码中定义了一个dataInputAssociation,该元素下有sourceRef和targetRef子元素,由于在调用时,流程引擎并不知道所传入的参数的数据结构,因此sourceRef和targetRef均需要引用相应的itemDefinition。定义了一个dataOutputAssociation,表示调用后产生的返回值,其中sourceRef定义返回值的数据结构,如果返回的是普通字符串,则可以引用代码中定义的itemDefinition,如果返回的是一个对象,则可以引用定义的itemDefinition。

4.9、发布Web Service

@WebService
public interface SaleService {

	@WebMethod
	@WebResult(name = "newSale")
	Sale createSale(@WebParam(name = "creator")String creator, 
			@WebParam(name = "createDate")String createDate);
}

@WebService()
public class SaleServiceImpl implements SaleService {
	
	public Sale createSale(String creator, String createDate) {
		System.out.println("创建人:" + creator);
		System.out.println("创建日期:" + createDate);		
		Sale sale = new Sale();
		sale.setSaleCode("SA00001");
		return sale;
	} 
}

使用@WebService注解发布一个SaleService,该SaleService中只
有一个接口方法,主要用于创建销售单对象(Sale),调用该方法需要传入creator和createDate参数,这两个参数均为字符串类型,在实现类中,只是输出两个参数,再创建一个Sale对象并返回。编写完Veb Service的实现代码后,再编写运行类,在main方法中启动Web容器并
发布代码清单12-29的Web Service,服务器类以及客户端类如代码所示。

public class Main {

    public static void main(String args[]) throws Exception {
    	Endpoint e = Endpoint.publish("http://localhost:9090/sale", new SaleServiceImpl());
        System.out.println("服务启动...");        
    }

}


public class CallClient {

	public static void main(String[] args) throws Exception {
	    JaxWsDynamicClientFactory dcf = JaxWsDynamicClientFactory.newInstance();	    
	    Client client = dcf.createClient("http://localhost:9090/sale?wsdl");
	    Object[] vars = new Object[]{"crazyit", "2018-10-10 10:10:10"};
	    Object[] object = client.invoke("createSale", vars);
	    Sale sale = (Sale)object[0];
	    System.out.println("请求WebService后返回的销售单号:" + sale.getSaleCode());
	}
}

4.10、使用Web Sercice Task

<import importType="http://schemas.xmlsoap.org/wsdl/" location="http://localhost:9090/sale?wsdl"
	namespace="http://webservice.activiti.crazyit.org/" />
<process id="testProcess" name="testProcess">
	<startEvent id="startevent1" name="Start"></startEvent>
	<userTask id="usertask1" name="Ready Task"></userTask>
	<serviceTask id="servicetask1" name="Web service invocation"
		implementation="##WebService" operationRef="createSaleOper">
		<dataInputAssociation>
			<sourceRef>creatorVar</sourceRef>
			<targetRef>creator</targetRef>
		</dataInputAssociation>
		<dataInputAssociation>
			<sourceRef>createDateVar</sourceRef>
			<targetRef>createDate</targetRef>
		</dataInputAssociation>
		<dataOutputAssociation>
			<sourceRef>newSale</sourceRef>
			<targetRef>saleVar</targetRef>
		</dataOutputAssociation>
	</serviceTask>
	<userTask id="usertask2" name="EndTask"></userTask>
	<endEvent id="endevent1" name="End"></endEvent>
	<sequenceFlow id="flow1" name="" sourceRef="startevent1"
		targetRef="usertask1"></sequenceFlow>
	<sequenceFlow id="flow2" name="" sourceRef="usertask1"
		targetRef="servicetask1"></sequenceFlow>
	<sequenceFlow id="flow3" name="" sourceRef="servicetask1"
		targetRef="usertask2"></sequenceFlow>
	<sequenceFlow id="flow4" name="" sourceRef="usertask2"
		targetRef="endevent1"></sequenceFlow>
</process>

<!-- 定义两个消息 -->
<message id="createSaleMsg" itemRef="tns:createSaleItem"></message>
<message id="createSaleResponseMsg" itemRef="tns:createSaleResponseItem"></message>

<!-- 定义两个item,用于定义消息的格式 -->
<itemDefinition id="createSaleItem" structureRef="sale:createSale" />
<itemDefinition id="createSaleResponseItem"
	structureRef="sale:createSaleResponse" />
<!-- 定义item,在调用webservice时,需要指定参数与返回结果的结构  -->
<itemDefinition id="creatorVar" structureRef="string" />
<itemDefinition id="creator" structureRef="string" />
<itemDefinition id="createDateVar" structureRef="string" />
<itemDefinition id="createDate" structureRef="string" />
<itemDefinition id="newSale" structureRef="sale:sale" />
<itemDefinition id="saleVar" structureRef="string" />

<interface name="Sale Service" implementationRef="SaleService">
	<operation id="createSaleOper" name="Create Sale Operation"
		implementationRef="sale:createSale">
		<!-- 输入消息与输出消息 -->
		<inMessageRef>createSaleMsg</inMessageRef>
		<outMessageRef>createSaleResponseMsg</outMessageRef>
	</operation>
</interface>
public static void main(String[] args) {		
	// 创建流程引擎
	ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
	// 得到流程存储服务组件
	RepositoryService repositoryService = engine.getRepositoryService();
	// 得到运行时服务组件
	RuntimeService runtimeService = engine.getRuntimeService();
	// 得到任务服务组件
	TaskService taskService = engine.getTaskService();
	// 部署流程文件
	repositoryService.createDeployment()
			.addClasspathResource("bpmn/WebService.bpmn").deploy();
	// 初始化参数
	Map<String, Object> vars = new HashMap<String, Object>();
	vars.put("creatorVar", "angus");
	vars.put("createDateVar", "2018-02-02 10:10:10");
	ProcessInstance pi = runtimeService.startProcessInstanceByKey(
			"testProcess", vars);
	// 完成第一个任务
	Task task = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult();
	taskService.complete(task.getId());
	// 输出调用Web Service后的参数
	Sale sale = (Sale) runtimeService.getVariable(pi.getId(), "saleVar");
	System.out.println("请求创建销售单后,返回的销售单号:" + sale.getSaleCode());
}

4.11、JavaDelegate属性注入

使用BPMN的配置来调用Web Service,配置较为烦琐,因此可以考虑在自定义的JavaDelegate中直接调用Web Service。使用编码方式调用Web Service,需要知道wsdl路径、操作、请求参数和调用返回值等属性,如果将这些属性配置到流程文件中,那么就需要使用
JavaDelegate的属性注入。为一个JavaDelegate注入值有以下两种形式:字符串注入和表达式注入。对于一些常量,可以在流程文件中通过配置进行字符串注入,而对于一些变量或者对象,可以配置成JUEL表达式。

<process id="process1" name="process1">
	<startEvent id="startevent1" name="Start"></startEvent>
	<userTask id="usertask1" name="End Task"></userTask>
	<endEvent id="endevent1" name="End"></endEvent>
	<sequenceFlow id="flow3" name="" sourceRef="usertask1"
		targetRef="endevent1"></sequenceFlow>
	<serviceTask id="servicetask1" name="Service Task"
		activiti:class="org.crazyit.activiti.StringInjectionDelegate">
		<extensionElements>
			<activiti:field name="userName" stringValue="Crazyit" />
			<activiti:field name="passwd">
				<activiti:string>123456</activiti:string>
			</activiti:field>
		</extensionElements>
	</serviceTask>
	<sequenceFlow id="flow4" name="" sourceRef="startevent1"
		targetRef="servicetask1"></sequenceFlow>
	<sequenceFlow id="flow5" name="" sourceRef="servicetask1"
		targetRef="usertask1"></sequenceFlow>
</process>
public class StringInjectionDelegate implements JavaDelegate {

	// 用户名属性
	private Expression userName;

	// 密码属性
	private Expression passwd;

	public void setUserName(Expression userName) {
		this.userName = userName;
	}

	public void setPasswd(Expression passwd) {
		this.passwd = passwd;
	}

	public void execute(DelegateExecution execution) {
		// 输出属性
		System.out.println("在JavaDelegate中注入字符串,userName值:"
				+ userName.getValue(null) + ", passwd值:"
				+ passwd.getValue(null));
	}

}
public static void main(String[] args) {
	// 创建流程引擎
	ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
	// 得到流程存储服务组件
	RepositoryService repositoryService = engine.getRepositoryService();
	// 得到运行时服务组件
	RuntimeService runtimeService = engine.getRuntimeService();
	// 部署流程文件
	repositoryService.createDeployment()
			.addClasspathResource("bpmn/StringInjection.bpmn").deploy();
	// 启动流程
	ProcessInstance pi = runtimeService.startProcessInstanceByKey("process1");
}

除了可以注入字符串外,还可以使用JUEL表达式为JavaDelegate注入对象或者经过计算后的值:

<process id="process1" name="process1">
	<startEvent id="startevent1" name="Start"></startEvent>
	<serviceTask id="servicetask1" name="Service Task"
		activiti:class="org.crazyit.activiti.ExpressionInjectionDelegate">
		<extensionElements>
			<activiti:field name="user" expression="${user}"></activiti:field>
			<activiti:field name="amountResult">
				<activiti:expression>${user.countAmount(amount)}</activiti:expression>
			</activiti:field>
		</extensionElements>
	</serviceTask>
	<endEvent id="endevent1" name="End"></endEvent>
	<sequenceFlow id="flow1" name="" sourceRef="startevent1"
		targetRef="servicetask1"></sequenceFlow>
	<sequenceFlow id="flow2" name="" sourceRef="servicetask1"
		targetRef="endevent1"></sequenceFlow>
</process>
public class ExpressionInjectionDelegate implements JavaDelegate {

	private Expression user;

	private Expression amountResult;

	public void setAmountResult(Expression amountResult) {
		this.amountResult = amountResult;
	}

	public void setUser(Expression user) {
		this.user = user;
	}

	@Override
	public void execute(DelegateExecution execution) {
		UserBean userBean = (UserBean) user.getValue(execution);
		System.out.println("在JavaDelegate中注入对象:" + userBean.getName() + "  "
				+ userBean.getPasswd());
		System.out.println("使用UserBean的方法计算后结果::"
				+ amountResult.getValue(execution));
	}

}
public static void main(String[] args) {
	// 创建流程引擎
	ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
	// 得到流程存储服务组件
	RepositoryService repositoryService = engine.getRepositoryService();
	// 得到运行时服务组件
	RuntimeService runtimeService = engine.getRuntimeService();
	// 部署流程文件
	repositoryService.createDeployment()
			.addClasspathResource("bpmn/ExpressionInjection.bpmn").deploy();
	// 初始化参数
	Map<String, Object> vars = new HashMap<String, Object>();
	UserBean user = new UserBean("crazyit", "123456");
	vars.put("user", user);
	vars.put("amount", 10);
	// 启动流程
	runtimeService.startProcessInstanceByKey("process1", vars);
}

4.12、在JavaDelegate中调用Web Service

可以将Web Service的相关信息动态化,以设置到流程变量中。调用一个Web Service需要wsdl路径、请求参数、操作名称(方法等)。

public static void main(String[] args) throws Exception {
    JaxWsDynamicClientFactory dcf = JaxWsDynamicClientFactory.newInstance();	    
    Client client = dcf.createClient("http://localhost:9090/sale?wsdl");
    Object[] vars = new Object[]{"crazyit", "2018-10-10 10:10:10"};
    Object[] object = client.invoke("createSale", vars);
    Sale sale = (Sale)object[0];
    System.out.println("请求WebService后返回的销售单号:" + sale.getSaleCode());
}
<process id="JavaDelegateWebService" name="JavaDelegateWebService">
	<startEvent id="startevent1" name="Start"></startEvent>
	<serviceTask id="servicetask1" name="Service Task"
		activiti:class="org.crazyit.activiti.WebServiceDelegate">
		<extensionElements>
			<activiti:field name="wsdl"
				stringValue="http://localhost:9090/sale?wsdl" />
			<activiti:field name="operation" stringValue="createSale" />
			<activiti:field name="creator" stringValue="crazyit" />
			<activiti:field name="createDate" stringValue="2018-10-10 10:10:10" />
		</extensionElements>
	</serviceTask>
	<endEvent id="endevent1" name="End"></endEvent>
	<sequenceFlow id="flow1" name="" sourceRef="startevent1"
		targetRef="servicetask1"></sequenceFlow>
	<sequenceFlow id="flow2" name="" sourceRef="servicetask1"
		targetRef="endevent1"></sequenceFlow>
</process>
public class WebServiceDelegate implements JavaDelegate {

	private Expression wsdl;

	private Expression operation;

	private Expression creator;

	private Expression createDate;

	public void setWsdl(Expression wsdl) {
		this.wsdl = wsdl;
	}

	public void setOperation(Expression operation) {
		this.operation = operation;
	}

	public void setCreator(Expression creator) {
		this.creator = creator;
	}

	public void setCreateDate(Expression createDate) {
		this.createDate = createDate;
	}

	public void execute(DelegateExecution execution) {
		try {
			JaxWsDynamicClientFactory dcf = JaxWsDynamicClientFactory.newInstance();
			// 使用wsdl路径创建Client		
			Client client = dcf.createClient((String) wsdl.getValue(null));
			// 使用配置的值创建参数对象
			Object[] vars = new Object[] { creator.getValue(null),
					createDate.getValue(null) };
			// 调用
			Object[] object = client
					.invoke((String) operation.getValue(null), vars);
			Sale sale = (Sale) object[0];
			System.out.println("在JavaDelegate中调用Web Service后,结果: "
					+ sale.getSaleCode());
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

}

4.13、Shell任务

public class JavaShell {

	public static void main(String[] args) throws Exception {
		//创建命令集合
		List<String> argList = new ArrayList<String>();
		argList.add("cmd");
		argList.add("/c");
		argList.add("echo");
		argList.add("hello");
		argList.add("crazyit");
		ProcessBuilder processBuilder = new ProcessBuilder(argList);
		// 执行命令返回进程
		Process process = processBuilder.start();
		// 解析输出
		String result = convertStreamToStr(process.getInputStream());
		System.out.println(result);
	}
	
	//读取输出流并转换为字符串
	public static String convertStreamToStr(InputStream is) throws IOException {
		if (is != null) {
			Writer writer = new StringWriter();
			char[] buffer = new char[1024];
			try {
				Reader reader = new BufferedReader(new InputStreamReader(is,
						"UTF-8"));
				int n;
				while ((n = reader.read(buffer)) != -1) {
					writer.write(buffer, 0, n);
				}
			} finally {
				is.close();
			}
			return writer.toString();
		} else {
			return "";
		}
	}
}

main方法创建一个字符串集合,然后向其中添加需要执行的命令,
由于使用的是Windows操作系统,因此需要先执行“cmd”命令进入命令行,“/c”表示执行完成后关闭窗口,从第三条命令开始,表示在命令行中输出“hello crazyit”。使用ProcessBuilder的start方法启动进程后,会返回一个Process对象,然后使用convertStreamToStr方法读取Process对象的输出流并转换为字符串,最后输出到控制台中,运行代码最终输出结果为:hello crazyit。.

代码的逻辑是,Activiti的Shell Task已经实现,Activiti中为Service Task设置执行类,可以通过设置JavaDelegate类实现,也可以通过设置ActivityBehavior类来实现,ShellTask就是使用Activiti内置的ActivityBehavior实现类来执行Shell命令的,因此,在配置一个
Shell Task时,需要告诉它执行参数,这些参数包括

  • command:执行的Shel命令,命令字符串集合的第一个元素,必须提供该参数。
  • argl-5:执行的命令参数,字符串集合中的第2~6个元素,为可选参数。
  • wait:是否等待命令执行完成,为可选参数,默认为true。
  • redirectError:是否合并错误输出和标准输出,为可选参数,默认为false。
  • cleanEnv:执行命令前是否清空当前进程的环境变量信息,默认为false。
  • outpuVariable:如果配置该值,则将执行命令的输出作为流程参数存到流程中。
  • errorCodeVariable:如果配置该值,则将执行命令的进程errorCode存到流程参数中。
  • directory:设置此命令的工作目录,默认为当前目录。

这些参数均为字符串类型,为一个JavaDelegate或者ActivityBehavior进行属性注入,只需要使用activiti:field元素并配置stringValue即可。

<process id="process1" name="process1">
	<startEvent id="startevent1" name="Start"></startEvent>
	<serviceTask id="servicetask1" name="Service Task" activiti:type="shell">
		<extensionElements>
			<activiti:field name="command" stringValue="cmd"/>
			<activiti:field name="arg1" stringValue="/c"/>
			<activiti:field name="arg2" stringValue="echo"/>
			<activiti:field name="arg3" stringValue="%JAVA_HOME%"/>
			<activiti:field name="outputVariable" stringValue="javaHome"/>
		</extensionElements>
	</serviceTask>
	<endEvent id="endevent1" name="End"></endEvent>
	<sequenceFlow id="flow1" name="" sourceRef="startevent1"
		targetRef="servicetask1"></sequenceFlow>
	<userTask id="usertask1" name="End Task"></userTask>
	<sequenceFlow id="flow2" name="" sourceRef="servicetask1"
		targetRef="usertask1"></sequenceFlow>
	<sequenceFlow id="flow3" name="" sourceRef="usertask1"
		targetRef="endevent1"></sequenceFlow>
</process>
// 创建流程引擎
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// 得到流程存储服务组件
RepositoryService repositoryService = engine.getRepositoryService();
// 得到运行时服务组件
RuntimeService runtimeService = engine.getRuntimeService();
// 部署流程文件
repositoryService.createDeployment()
		.addClasspathResource("bpmn/ShellTask.bpmn").deploy();
// 启动流程
ProcessInstance pi = runtimeService
		.startProcessInstanceByKey("process1");
// 查询流程参数
System.out.println("运行Shell Task得到JAVA_HOME的环境变量值为:"
		+ runtimeService.getVariable(pi.getId(), "javaHome"));
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Activiti中,可以使用多实例子流程来处理并发任务。以下是一个简单的示例: 假设我们有一个主流程,其中包含一个子流程,子流程中有一个并发多实例任务,需要同时处理多个子任务。 1. 首先,在子流程中创建一个并发多实例任务,可以使用以下XML代码实现: ``` <subProcess id="subProcess1" name="Sub Process"> <multiInstanceLoopCharacteristics isSequential="false"> <loopCardinality>3</loopCardinality> <completionCondition>${nrOfCompletedInstances/nrOfInstances >= 1}</completionCondition> </multiInstanceLoopCharacteristics> <userTask id="subProcessTask" name="Sub Process Task" /> </subProcess> ``` 上面的代码中,`multiInstanceLoopCharacteristics` 元素表示这是一个多实例任务,`isSequential="false"` 表示任务是并行处理的,`loopCardinality` 表示需要处理的子任务数量,这里设置为3。`completionCondition` 表示任务完成的条件,这里设置为当所有子任务都完成时,子流程才算完成。 2. 在主流程中调用子流程,可以使用以下XML代码实现: ``` <callActivity id="subProcessCall" name="Sub Process Call" calledElement="subProcess1" /> ``` 上面的代码中,`calledElement` 属性指定了被调用的子流程的ID,这里为 `subProcess1`。 3. 在子流程中处理多实例任务,可以使用以下Java代码实现: ``` public class SubProcessTaskDelegate implements JavaDelegate { @Override public void execute(DelegateExecution execution) throws Exception { // 获取当前子任务的ID String subTaskId = execution.getCurrentActivityId(); // 处理子任务 System.out.println("Processing sub task " + subTaskId); } } ``` 上面的代码中,`execute` 方法是任务处理的入口。可以通过 `getCurrentActivityId` 方法获取当前子任务的ID,然后处理任务。 以上就是在Activiti中处理并发多实例子流程任务的简单示例。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值