说说 jBPM 流程定义语言(10)—— 自动活动

jBPM4 能够很好地支持多种自动活动,所谓自动活动指的是在执行过程中完全无须人工干预。

jBPM4 默认支持的自动活动类型有:
* java - Java 程序活动
* script - 脚本活动
* hql - Hibernate 查询语言活动
* SQL - SQL 查询语言活动
* mail - 邮件活动

1 Java 程序活动

java 活动可以指定一个 Java 类的方法,当流程执行到此活动时,便会自动执行此 Java 方法 。

java 活动的属性:

属性类型默认值是否必需描述
class类名字符串class 或 expr 二者必选其一带有包路径的完整 Java 类名 。 此 Java 类会被 “用户代码类加载器” 加载到 JVM。此类的对象在活动执行时被 “ 延迟创建 ” (注意:需要提供无参构造方法),即不随 jBPM 工作流引擎启动而创建 。 当这些对象创建后,将作为流程定义的一部分被缓存 。
expr表达式字符串class 或 expr 二者必选其一此表达式返回一个 Java 类对象,必须包含 下面 method 属性指定的方法。
method方法名字符串必需调用的方法名称。
var流程变量名字符串可选存储方法返回结果的流程变量名称。

java 活动支持的元素:

元素数目描述
field0..*在方法被调用之前给指定的类成员域注入指定的值。
arg0..*给被调用的方法提供参数。

java 活动的流程定义

jPDL:

<?xml version="1.0" encoding="UTF-8"?>

<process name="Java" xmlns="http://jbpm.org/4.4/jpdl">
   <start g="197,164,48,48" name="start1">
      <transition g="-52,-22" name=" " to="问候"/>
   </start>
   <!-- 定义 java 活动,调用类对象的方法,并把结果存储在流程实例的 answer 变量中-->
   <java class="net.deniro.jbpm.java.Deniro" g="321,159,92,52" method="hello" name="问候" var="answer">
      <!-- 为调用的方法提供参数 -->
      <arg><string value="早,饭吃了吗?"/></arg>
      <!-- 为成员域注入值 -->
      <field name="state"><string value="吃咯"/></field>
      <transition to="握手"/>
   </java>

   <!-- 定义 java 活动,调用类对象(通过表达式调用流程变量对象)的方法,并把结果存储在流程实例的 hand 变量中-->
   <java expr="#{hand}" g="466,158,92,52" method="shake" name="握手" var="hand">
      <!-- 使用表达式引用流程变量,为 shake 方法提供参数(按顺序) -->
      <arg><object expr="#{lily.handshakes.force}"/></arg>
      <arg><object expr="#{lily.handshakes.duration}"/></arg>
      <transition to="等待"/>
   </java>
   <state g="619,160,92,52" name="等待"/>
</process>

在【问候】活动中,类 net.deniro.jbpm.java.Deniro 会被实例化为一个对象,然后调用 hello 方法,并将调用返回的对象存入名为 answer 的流程变量中。

下面是类 Deniro 的代码:

public class Deniro {
    /**
     * 在流程定义中注入值
     */
    String state;

    public String hello(String msg) {
        if (msg.indexOf("早,饭吃了吗?") != -1) {
            return "我已经" + state + ",你呢?";
        } else {
            return null;
        }

    }
}

接着会调用第二个 Java 活动(握手),流程会处理 #{hand} 表达式,根据此表达式获取名为 hand 的流程变量对象,这个对象具有名为 shake 的方法,下面是类 Hand 的代码:

public class Hand implements Serializable {

    private boolean isShaken;


    public Hand shake(Integer force, Integer duration) {
        if (force > 3 && duration > 7) {
            isShaken = true;
        }

        return this;
    }

    public boolean isShaken() {
        return isShaken;
    }
}

要调用的 shake 方法需要两个参数,这两个参数被定义为表达式 #{lily.handshakes.force}#{lily.handshakes.duration},这里的 lily 是 Lily 类的实例,它具有类型为 Map 名为 handshakes 的成员对象,这个成员对象有两个键,一个名为 force,另一个名为 duration:

public class Lily implements Serializable {

    static Map<String,Integer> handshakes=new HashMap<>();
    {
        handshakes.put("force",5);
        handshakes.put("duration",12);
    }

    /**
     * 可以在 EL 表达式中引用(#{lily.handshakes})
     * @return
     */
    public Map<String,Integer> getHandshakes(){
        return handshakes;
    }
}

测试代码:

//设置流程变量
Map<String, Object> variables = new HashMap<>();
variables.put("hand", new Hand());
variables.put("lily", new Lily());

//发起实例
ProcessInstance processInstance = executionService.startProcessInstanceByKey
        ("Java", variables);

String pId = processInstance.getId();

//获取流程变量 answer
String answer = (String) executionService.getVariable(pId, "answer");

//断言 Java 活动 greet 的结果
assertEquals("我已经吃咯,你呢?", answer);

//获取流程变量 hand
Hand hand = (Hand) executionService.getVariable(pId, "hand");
assertTrue(hand.isShaken());

2 script 脚本活动

我们可以在 script 活动中定义一段 EL 表达式脚本,工作流引擎执行到此活动时就会解析这段脚本 。 不仅是 EL 表达式,任何一种符合 JSR-223 规范的脚本语言都可以在这里使用,这样做的前提是在配置文件 jbpm.default.scriptmanager.xml 中定义要使用的脚本语言。

jBPM4 默认的脚本语言是 juel ,即 EL 表达式 。

2.1 脚本表达式(script expression)方式

script 活动的属性:

属性类型默认值是否必需描述
expr字符串必需需要执行的脚本表达式
lang字符串EL可选脚本语言
var字符串可选流程变量,用于存放脚本执行后的返回值

包含 script 活动的流程定义

jPDL:

<?xml version="1.0" encoding="UTF-8"?>

<process name="ScriptExpression" xmlns="http://jbpm.org/4.4/jpdl">
   <start g="216,232,48,48" name="start1">
      <transition to="触发脚本"/>
   </start>
   <!-- 脚本表达式返回一个字符串值,这个值被存储在名为 text 的流程变量中-->
   <script expr="发送包裹给 #{order.owner}" g="336,229,104,52" name="触发脚本" var="text">
      <transition to="等待"/>
   </script>
   <state g="486,232,92,52" name="等待"/>
</process>

流程定义中,我们引用了 Order 类:

public class Order implements Serializable{

    /**
     * 用户 ID
     */
    String owner;

    public Order(String owner) {
        this.owner = owner;
    }

    public String getOwner() {
        return owner;
    }

    public void setOwner(String owner) {
        this.owner = owner;
    }
}

测试代码:

Map<String,Object> variables=new HashMap<>();
variables.put("order",new Order("Deniro"));

//发起实例
ProcessInstance processInstance = executionService.startProcessInstanceByKey
        ("ScriptExpression",variables);

String pId=processInstance.getId();

//获取流程变量 text
String text=(String) executionService.getVariable(pId,"text");
assertTextPresent("发送包裹给 Deniro",text);

2.2 脚本文本(script text)方式

如果脚本很长,可能有多行,那么建议使用脚本文本的方式来定义脚本。

script 活动中 text 元素的属性:

属性类型默认值是否必需描述
lang字符串EL可选脚本语言
var字符串可选流程变量,用于存放脚本执行后的返回值

text 元素的内容:

元素个数描述
文本1脚本文本,支持多行

包含 script 活动的流程定义

jPDL:

<?xml version="1.0" encoding="UTF-8"?>

<process name="ScriptText" xmlns="http://jbpm.org/4.4/jpdl">
   <start g="215,232,48,48" name="start1">
      <transition to="触发脚本"/>
   </start>
   <script g="336,229,104,52" name="触发脚本" var="text">
      <text>发送包裹给 #{order.owner}</text>
        <!-- text 元素可以编写多行脚本 -->
      <transition to="等待"/>
   </script>
   <state g="486,232,92,52" name="等待"/>
</process>

测试代码:

Map<String,Object> variables=new HashMap<>();
variables.put("order",new Order("Deniro"));

//发起实例
ProcessInstance processInstance = executionService.startProcessInstanceByKey
        ("ScriptText",variables);

String pId=processInstance.getId();

//获取流程变量 text
String text=(String) executionService.getVariable(pId,"text");
assertTextPresent("发送包裹给 Deniro",text);

3 hql (Hibernate 查询语言)活动

有时候需要直接从持久化层读取流程数据,因为 jBPM4 的持久化层是基于 Hibernate 框架实现的,所以可以直接使用 hql 活动,执行 HQL 查询语句,并将返回结果保存到流程变量中。

hql 活动的属性:

属性类型默认值是否必需描述
var字符串可选存储 HQL 执行结果的流程变量名。
uniquetrue/falsefalse可选为 true 时,在查询结果上调用 uniqueResult() 方法获得唯一结果集;为 false 时,即查询结果调用 list() 方法得到结果集列表 。

hql 活动的元素:

元素数目描述
query1HQL 查询的语句
parameter0..*HQL 查询语句的外部参数。

带有 hql 活动的流程定义

jPDL:

<?xml version="1.0" encoding="UTF-8"?>

<process name="Hql" xmlns="http://jbpm.org/4.4/jpdl">
   <start g="197,185,48,48" name="start1">
      <transition to="获取任务名称"/>
   </start>
   <!-- HQL 活动,查询名称中含有字母 “i" 的任务名称 -->
   <hql g="308,180,116,52" name="获取任务名称" var="tasknames with i">
      <query>select task.name from org.jbpm.pvm.internal.task.TaskImpl as task
      where task.name like :taskName
      </query>

      <!-- 定义 HQL 语句的输入参数值,这将替换 HQL 语句中的 taskName 部分-->
      <parameters>
         <string name="taskName" value="%i%"/>
      </parameters>

      <transition to="统计任务数"/>
   </hql>
    <!-- HQL 活动,unique 设置为 true -->
   <hql g="461,180,92,52" name="统计任务数" unique="true" var="tasks">
      <query>select count(*)
      from org.jbpm.pvm.internal.task.TaskImpl
      </query>
      <transition to="等待"/>
   </hql>
   <state g="622,184,92,52" name="等待"/>
</process>

测试代码:

//发起实例
ProcessInstance processInstance = executionService.startProcessInstanceByKey
        ("Hql");

String pId = processInstance.getId();

//获取第一个 hql 活动的执行结果
Collection<String> taskNames = (Collection<String>) executionService.getVariable
        (pId, "tasknames with i");
taskNames = new HashSet<>(taskNames);

Set<String> expectedTaskNames = new HashSet<>();
//        expectedTaskNames.add("活动名称");
assertEquals(expectedTaskNames, taskNames);//验证第一个活动

Object activities = executionService.getVariable(pId, "tasks");
assertEquals("0", activities.toString());

4 sql (SQL 查询语言)活动

sql 活动与 hql 活动的属性与元素基本相同,只不过在 sql 活动中的 query 元素的内容填入的是 SQL 语句。

强调一下,在 sql 活动与 hql 活动中,它们都有以下的特性:
* 查询获取的数据库结果集(RecorderSet)是 java.lang.Object[] 的对象数组形式。
* 查询获取的数据库结果集列表是 java.util.Collection

<?xml version="1.0" encoding="UTF-8"?>

<process name="Sql" xmlns="http://jbpm.org/4.4/jpdl">
   <start g="397,138,48,48" name="start1">
      <transition to="获取任务对象列表"/>
   </start>
   <!-- sql 活动;从任务表中获取记录集的 5 个属性 -->
   <sql g="487,134,137,52" name="获取任务对象列表" var="taskObjects">
      <query>
        select DBID_,NAME_,STATE_,PRIORITY_,DUEDATE_ from JBPM4_TASK
      </query>
      <transition to="等待"/>
   </sql>
   <state g="654,136,92,52" name="等待"/>
</process>

测试代码:

//发起实例
ProcessInstance processInstance = executionService.startProcessInstanceByKey
        ("Sql");
String pId=processInstance.getId();

//获取查询结果
Collection<Object[]> taskObjects=( Collection<Object[]>)executionService
        .getVariable(pId,"taskObjects");
assertEquals(0,taskObjects.size());

mail 活动(邮件)

通过 mail 活动,我们可以发送邮件给一个或多个邮件地址。可以在定义模板中指定邮件的内容。模板可以在 mail 活动的元素中指定,也可以在 jBPM 的配置文件(jbpm.mail.templates.xml)中的 process-engine-context → mail-template 元素中定义。

mail 活动的独有属性:

属性类型默认值是否必需描述
template字符串配置文件中 mail-template 元素的名称,即邮件模板。如果找不到,就使用 mail 活动的 text 或 html 元素内容作为邮件的内容。

mail 活动的元素:

元素数目描述
from0..1发件人列表
to1收件人列表
cc0..1抄送收件人列表
bcc0..1密送收件人列表
subject1邮件标题
text0..1邮件内容,纯文本格式
html0..1邮件内容,HTML 格式
attachments0..1邮件附件,可以引用 URL 、classpath 以及本地文件资源

带 mail 活动的流程定义

注意:jBPM4 的 eclipse 插件没有提供 mail 活动的组件,我们可以直接定义在 jPDL 中:

jPDL:

<?xml version="1.0" encoding="UTF-8"?>

<process name="sendMail" xmlns="http://jbpm.org/4.4/jpdl">
    <start g="201,197,48,48" name="start1">
        <transition to="发送邮件"/>
    </start>
    <mail g="307,198,80,40" name="发送邮件">
        <!-- 收件人-->
        <to addresses="lisq037@163.com"></to>

        <!-- 抄送收件人(user 指定用户;groups 指定群组)-->
        <cc users="lily@163.com" groups="group"></cc>

        <!-- 密送收件人-->
        <bcc groups="group"></bcc>

        <!-- 邮件标题-->
        <subject>${account} 确认函</subject>

        <!-- 纯文本-->
        <!--<text>${account} ${date} 约定于 xx 日,进行会议商讨,感谢您的支持。</text>-->

        <!-- html 格式-->
        <html>
            <table>
                <tr>
                    <td>${account}</td>
                </tr>
            </table>
        </html>

        <!-- 指定附件-->
        <attachments>
            <!-- web URL-->
            <attachment url="https://www.baidu.com/img/bd_logo1.png"/>
            <!-- classpath-->
            <attachment resource="xxx/xxx.zip"/>
            <!-- 本地文件系统-->
            <attachment file="c:/xxx.zip"/>
        </attachments>
        <transition to="结束"/>
    </mail>
    <state name="结束" g="432,193,92,52"/>
</process>

邮件服务器地址配置在 jbpm.mail.properties 文件中,它有这些属性:

属性描述
mail.smtp.hostsmtp 邮件服务器地址
mail.smtp.portsmtp 邮件服务器端口
mail.from默认的邮件发送人地址
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值