jbpm 中 ProcessEventListener 顺序问题

在 jBPM 商业流程中有一个 ProcessEventListener ,可以用来回调流程的执行过程,但是这个 Listener 的执行顺序非常奇怪。

首先我们先看看这个 interface

public interface ProcessEventListener {

  void beforeProcessStarted( ProcessStartedEvent event );
  void afterProcessStarted( ProcessStartedEvent event );
  void beforeProcessCompleted( ProcessCompletedEvent event );
  void afterProcessCompleted( ProcessCompletedEvent event );
  void beforeNodeTriggered( ProcessNodeTriggeredEvent event );
  void afterNodeTriggered( ProcessNodeTriggeredEvent event );
  void beforeNodeLeft( ProcessNodeLeftEvent event );
  void afterNodeLeft( ProcessNodeLeftEvent event );
  void beforeVariableChanged(ProcessVariableChangedEvent event);
  void afterVariableChanged(ProcessVariableChangedEvent event);

}

我相信大多数人看到这些方法回调大致可以猜测 afterProcessStarted 应该是在流程开始之后被调用,然而实际的调用顺序是这样的:

- beforeProcessStarted
  - beforeNodeTriggered
    - beforeNodeLeft
      - beforeNodeTriggered
        - beforeVariableChanged
          afterVariableChanged
        - beforeNodeLeft
          - beforeNodeTriggered
            - beforeNodeLeft
              - beforeNodeTriggered
                afterNodeTriggered
              afterNodeLeft
            - beforeNodeLeft
                beforeNodeTriggered
                afterNodeTriggered
              afterNodeLeft
            afterNodeTriggered
          afterNodeLeft
        afterNodeTriggered
      afterNodeLeft
    afterNodeTriggered
  afterProcessStarted

afterProcessStarted 会在流程结束时被调用。有人提过 bug 但是官方认为这是程序设计,所以使用文档的形式 将这种方式说明了。

查看源代码可以在 RuleExecutor 中可以看到:

jbpm listener

实验

假设有如下图的流程

jbpm process

那么打印所有的日志可以观察到:

- DefaultRuleContainer beforeVariableChanged ==>[ProcessVariableChanged(id=age; instanceId=age; oldValue=null; newValue=18; processName=BPTest; processId=FlowTest.BPTest)]
- DefaultRuleContainer afterVariableChanged ==>[ProcessVariableChanged(id=age; instanceId=age; oldValue=null; newValue=18; processName=BPTest; processId=FlowTest.BPTest)]
- DefaultRuleContainer beforeProcessStarted ==>[ProcessStarted(name=BPTest; id=FlowTest.BPTest)]
- event ==>[ProcessStarted(name=BPTest; id=FlowTest.BPTest)]
- DefaultRuleContainer beforeNodeTriggered ==>[ProcessNodeTriggered(nodeId=4; id=0; nodeName=StartPoint; processName=BPTest; processId=FlowTest.BPTest)]
- DefaultRuleContainer beforeNodeLeft ==>[ProcessNodeLeft(nodeId=4; id=0; nodeName=StartPoint; processName=BPTest; processId=FlowTest.BPTest)]
- DefaultRuleContainer beforeNodeTriggered ==>[ProcessNodeTriggered(nodeId=7; id=1; nodeName= 年龄是否大于 18; processName=BPTest; processId=FlowTest.BPTest)]
- DefaultRuleContainer beforeNodeLeft ==>[ProcessNodeLeft(nodeId=7; id=1; nodeName= 年龄是否大于 18; processName=BPTest; processId=FlowTest.BPTest)]
- DefaultRuleContainer beforeNodeTriggered ==>[ProcessNodeTriggered(nodeId=2; id=2; nodeName=A1Task; processName=BPTest; processId=FlowTest.BPTest)]
- DefaultRuleContainer beforeNodeLeft ==>[ProcessNodeLeft(nodeId=2; id=2; nodeName=A1Task; processName=BPTest; processId=FlowTest.BPTest)]
- DefaultRuleContainer beforeNodeTriggered ==>[ProcessNodeTriggered(nodeId=6; id=3; nodeName=A2Task; processName=BPTest; processId=FlowTest.BPTest)]
- DefaultRuleContainer beforeNodeLeft ==>[ProcessNodeLeft(nodeId=6; id=3; nodeName=A2Task; processName=BPTest; processId=FlowTest.BPTest)]
- DefaultRuleContainer beforeNodeTriggered ==>[ProcessNodeTriggered(nodeId=3; id=4; nodeName=End; processName=BPTest; processId=FlowTest.BPTest)]
- DefaultRuleContainer beforeNodeLeft ==>[ProcessNodeLeft(nodeId=3; id=4; nodeName=End; processName=BPTest; processId=FlowTest.BPTest)]
- DefaultRuleContainer beforeProcessCompleted ==>[ProcessCompleted(name=BPTest; id=FlowTest.BPTest)]
- DefaultRuleContainer afterProcessCompleted ==>[ProcessCompleted(name=BPTest; id=FlowTest.BPTest)]
- event ==>[ProcessCompleted(name=BPTest; id=FlowTest.BPTest)]
- DefaultRuleContainer afterNodeLeft ==>[ProcessNodeLeft(nodeId=3; id=4; nodeName=End; processName=BPTest; processId=FlowTest.BPTest)]
- DefaultRuleContainer afterNodeTriggered ==>[ProcessNodeTriggered(nodeId=3; id=4; nodeName=End; processName=BPTest; processId=FlowTest.BPTest)]
- DefaultRuleContainer afterNodeLeft ==>[ProcessNodeLeft(nodeId=6; id=3; nodeName=A2Task; processName=BPTest; processId=FlowTest.BPTest)]
- DefaultRuleContainer afterNodeTriggered ==>[ProcessNodeTriggered(nodeId=6; id=3; nodeName=A2Task; processName=BPTest; processId=FlowTest.BPTest)]
- DefaultRuleContainer afterNodeLeft ==>[ProcessNodeLeft(nodeId=2; id=2; nodeName=A1Task; processName=BPTest; processId=FlowTest.BPTest)]
- DefaultRuleContainer afterNodeTriggered ==>[ProcessNodeTriggered(nodeId=2; id=2; nodeName=A1Task; processName=BPTest; processId=FlowTest.BPTest)]
- DefaultRuleContainer afterNodeLeft ==>[ProcessNodeLeft(nodeId=7; id=1; nodeName= 年龄是否大于 18; processName=BPTest; processId=FlowTest.BPTest)]
- DefaultRuleContainer afterNodeTriggered ==>[ProcessNodeTriggered(nodeId=7; id=1; nodeName= 年龄是否大于 18; processName=BPTest; processId=FlowTest.BPTest)]
- DefaultRuleContainer afterNodeLeft ==>[ProcessNodeLeft(nodeId=4; id=0; nodeName=StartPoint; processName=BPTest; processId=FlowTest.BPTest)]
- DefaultRuleContainer afterNodeTriggered ==>[ProcessNodeTriggered(nodeId=4; id=0; nodeName=StartPoint; processName=BPTest; processId=FlowTest.BPTest)]
- DefaultRuleContainer afterProcessStarted ==>[ProcessStarted(name=BPTest; id=FlowTest.BPTest)]

reference


2019-05-23 jbpm , drools , business-process , rule , rule-engine

每天学习一个命令:pidstat 查看进程消耗资源

The pidstat command is used for monitoring individual tasks currently being managed by the Linux kernel.

installation

apt-get install sysstat

usage

pidstat [options] [interval] [times]

Most common use options:

  • -r show memory usage
  • -d show io usage per process id
  • -p PID specify PID

Examples

Show CPU usage of all process

pidstat -p ALL
pidstat

Show memory usage

Following command will display PID memory usage 10 times every 2 seconds:

pidstat -r -p PID 2 10

Result fields:

  • PID
  • Minflt/s
  • Majflt/s
  • VSZ virtual memory usage KB
  • RSS KB
  • Command task name

show IO usage

pidstat -d -p PID

Result field:

  • PID
  • kB_rd/s read from disk each second KB
  • kB_wr/s write to disk each second KB
  • kB_ccwr/s
  • Command task name

reference


2019-05-14 linux , commmand , network

每天学习一个命令:pidof 查找进程 PID

pidof finds the process id’s(pids) of the names programs.

Usage

pidof [name]

Example

find chrome pid

pidof chrome
pidof -s chrome

Options:

  • -s instructs the program to only return one pid.

Extension

  • pgrep

2019-05-14 pid , netstat , command , linux

Cross-platform Softwares I am using

This post will only list cross-platform softwares I used now. Each software has its own behaviors, and binded with its own shortcut. It takes time to be familiar with it. So I decided to use only cross-platform softwares in daily life in case I have to swith my main System. But it seems fine till now. I am happy with these great softwares.

IntelliJ IDEA

DescriptionIntelliJ IDEA is a Java integrated development environment for developing computer software. It is developed by JetBrains, and is available as an Apache 2 Licensed community edition, and in a proprietary commercial edition. Both can be used for commercial development.

Open Sources Alternatives:

  • Eclipse

Chrome

DescriptionGoogle Chrome is a cross-platform web browser developed by Google. It was first released in 2008 for Microsoft Windows, and was later ported to Linux, macOS, iOS, and Android. The browser is also the main component of Chrome OS, where it serves as the platform for web apps.

Alternatives:

  • vivaldi

SmartGit

SmartGit is a Git GUI client with support for SVN, GitHub, BitBucket pull requests and comments.

WizNote

WizNote for Windows/Mac/Linux is a cross-platform cloud based note-taking client.

GoldenDict

GoldenDict is an open-source dictionary program that gives translations of words and phrases for different languages. It allows the use of several popular dictionary file formats simultaneously and without conversion. The project aims to create a feature-rich dictionary search program.

TeamViewer

TeamViewer is proprietary software for remote control, desktop sharing, online meetings, web conferencing and file transfer between computers.

pCloud

pCloud is the secure cloud storage, where you can store, share and work on all your files.

Crypt

VeraCrypt is a source-available freeware utility used for on-the-fly encryption. It can create a virtual encrypted disk within a file or encrypt a partition or the entire storage device with pre-boot authentication. VeraCrypt is a fork of the discontinued TrueCrypt project.

TagSpace

TagSpaces is an open-source data manager and file navigator. It helps organize files on local drives by adding tags to files. Users get the same user interface to manage their files on different platforms. TagSpaces is compatible with Windows, Linux, Mac, Android, iPhone, Firefox and Chrome.

Calibre

Calibre is a cross-platform open-source suite of e-book software. Calibre supports organizing existing e-books into virtual libraries, displaying, editing, creating and converting e-books, as well as syncing e-books with a variety of e-readers. Editing books is supported for EPUB and AZW3 formats.

Telegram

Telegram is a cloud-based instant messaging and voice over IP service developed by Telegram Messenger LLP, a privately held company registered in London, United Kingdom, founded by the Russian entrepreneur Pavel Durov and his brother Nikolai.

Etcher

Etcher is a powerful OS image flasher built with web technologies to ensure flashing an SDCard or USB drive is a pleasant and safe experience.


2019-05-10 applications , apps

使用 Stylus 扩展自定义页面样式

有油猴脚本可以用来修改网页页面,那么相应的也能通过挂载自己的定义的 CSS 来实现对页面的修改。

如果有了这样的概念,那么可以在页面做到,完全修改页面显示的样式,增加背景,移除广告等等事情。

installation

安装扩展

CSS 样式

从该站点寻找可用的样式

InoReader

  • https://userstyles.org/styles/119240/inoreader-countrystyle-flat-ui-remove-ads

reference


2019-05-08 stylus , css , chrome , firefox , extension

威联通折腾篇十三:替换默认 shell 为 zsh

默认 SSH 登录到 QNAP 的 login shell 可以通过 echo $SHELL 来查看到是

/bin/sh

默认的这个 bash 是 QNAP 自带的

/bin/sh --version
GNU bash, version 3.2.57(1)-release (x86_64-QNAP-linux-gnu)
Copyright (C) 2007 Free Software Foundation, Inc.

也是一个相对比较简陋的版本,自动补全很难用,也没有语法高亮。所以想能不能把 QNAP 自带的 shell 换成日常使用的 ZSH,QNAP 本质上就是一个 Linux,所以理所当然的应该是可行的。

Installation

首先要要解决的问题是安装问题,普通的 Linux 发行版直接使用一行命令就可以完成,QNAP 可以使用 QNAP club 别人提供的版本,直接安装即可

wget https://cdn.qnapclub.eu/qpkg_artifacts/ZSH_5.7.0_x86_64/ZSH_5.7.0_x86_64.qpkg
sh ZSH_5.7.0_x86_64.qpkg

如果你参考过我之前的文章 你应该知道添加了 QNAP Club 的地址可以直接在 App Center 中直接搜索下载。

安装好之后,默认的 zsh 会安装到

/opt/ZSH

Usage

SSH 登陆 QNAP 之后直接在终端输入 zsh 即可进入 zsh。

ZSH 一些强大的功能:

  • 强大的补全功能
  • 错误检查以及自动更正
  • 命令别名、路径别名
  • 强大的提示信息

默认的配置在 /opt/ZSH 中,可以自己做一些微调,比如

vi /opt/ZSH/zshrc.zsh-template
or
vi /root/.zshrc

修改主题

ZSH_THEME="agnoster"

等等。

Tips

如果在使用过程中发现,delete功能异常,比如delete虽然删除了字符但是向右多显示一个空格,而且不显示移动光标 那么可以编辑zsh配置文件

vi ~/.zshrc

在.zshrc里添加

TERM=xterm

保存, 然后执行, 使配置生效

source ~/.zshrc

2019-05-01 qnap , qnap-tutorial , qpkg , linux , shell , login-shell , zsh , oh-my-zsh , bash

Cinnamon alt+f2 命令

很早就开始用 Cinnamon 作为 Linux 的主要桌面环境,还折腾过 Cinnamon 的主题,但是其实一直没怎么关注 Alt + F2 的功能,唯一知道的命令就是 r 用来重启 Cinnamon。所以这里就总结总结 Alt+F2 可以用的命令。

内置命令

-‘r’ - restart Cinnamon

  • ‘rt’ - reload the current Cinnamon theme
  • ‘lg’ - start the Looking Glass debugger (which itself seems not to be documented anywhere)

任何 Xwindows 应用

在 Alt + F2 下运行无界面的命令,比如 ls 可能得不到任何结果,但是可以运行比如 firefox 这样有窗口的命令。该命令需要在 PATH 下。

快速打开文件夹

在 Alt + F2 窗口下输入文件目录可以快速使用默认文件浏览器打开该目录。


2019-04-29 cinnamon , commands

The Silver Searcher 使用

Once I wrote a post about ack-grep, and Franklin Yu left a comment about the silver searcher. He said this tool is much faster than the ack-grep. So this post is about The Silver Searcher. As always, I will introduce what is the Silver Searcher, how to install and use it.

Follow the link, we can see that author claim that this tool started off as a clone of ack, but then diverged slightly. And in most case, this tool is 5-10x faster then ack.

Installation

Find the download link at official site.

apt-get install silversearcher-ag

Usage

ag [file-type] [options] PATTERN [PATH]

As the man describe, recursively search for PATTERN in PATH.

Usage

Most options are similar to grep, like -i, -v, -B, -A etc

Find text under a path

most use case will be find a keyword from a bunch of files. 最常用的一个方式便是在目录中搜索关键词,ag 能够非常快速的搜索文件内容,所以非常适合查询日志,或者代码等文本文件。

ag keyword /path/to/

Count the matches of keywords

ag -c keyword /path/

自定义查询深度

Default depth is 25, if you want unlimited, you should use -1

ag --depth NUM keyword .

查找包含关键字的文件名

If you forget the filename, but only remeber only few letters, you can use -g to print only filenames.

-g 选项表示查询文件名,而不会去查询文件内容。比如说想要查询目录下文件名中包含 Pattern 的文件名,那么可以使用:

ag -g PATTERN .

results will be only filenames. 这样结果就只会打印出符合的文件名。

打印出包含关键字的文件名

上面 -g 选项只会去查询文件名,那么如果想要查找内容中包含某个关键字的文件名,可以使用 -l 选项:

ag -l 'pattern' /path/to

使用 -L 会显示没有匹配的文件名

在特定的文件中查询

ag 提供了 -G 选项,可以用来缩小查询的范围,-G PATTERN 可以指定 Pattern,那么 ag 只会在 -G 指定的文件中查询,比如只查询 .+\.java.java 结尾的文件。

ag -G ".+\.java" "string to search" /path/to

For more details, you can check man ag

reference


2019-04-25 ack-grep , search , grep

Maven 多仓库和镜像配置

多仓库配置

设置多仓库有两种方法,第一种直接在项目 POM 中定义

<project>
...
  <repositories>
    <repository>
      <id>my-repo1</id>
      <name>your custom repo</name>
      <url>http://jarsm2.dyndns.dk</url>
    </repository>
    <repository>
      <id>my-repo2</id>
      <name>your custom repo</name>
      <url>http://jarsm2.dyndns.dk</url>
    </repository>
  </repositories>
...
</project>

这里的 id 就是 mirrorOf 使用的 ID。

第二种方法是在 ~/.m2/settings.xml 文件中全局修改。

<settings>
 ...
 <profiles>
   ...
   <profile>
     <id>myprofile</id>
     <repositories>
       <repository>
         <id>my-repo2</id>
         <name>your custom repo</name>
         <url>http://jarsm2.dyndns.dk</url>
       </repository>
       ...
     </repositories>
   </profile>
   ...
 </profiles>

 <activeProfiles>
   <activeProfile>myprofile</activeProfile>
 </activeProfiles>
 ...
</settings>

别忘了激活 profile,或者也可以使用 mvn 参数

mvn -Pmyprofile ...

这里提供一下 jboss 官方的配置

<profiles>
    <profile>
      <id>jboss</id>
      <repositories>
        <repository>
          <id>jboss-public-repository-group</id>
          <name>JBoss Public Maven Repository Group</name>
          <url>https://repository.jboss.org/nexus/content/groups/public-jboss/</url>
          <layout>default</layout>
          <releases>
            <enabled>true</enabled>
            <updatePolicy>never</updatePolicy>
          </releases>
          <snapshots>
            <enabled>true</enabled>
            <updatePolicy>never</updatePolicy>
          </snapshots>
        </repository>
      </repositories>
      <pluginRepositories>
        <pluginRepository>
          <id>jboss-public-repository-group</id>
          <name>JBoss Public Maven Repository Group</name>
          <url>https://repository.jboss.org/nexus/content/groups/public-jboss/</url>
          <layout>default</layout>
          <releases>
            <enabled>true</enabled>
            <updatePolicy>never</updatePolicy>
          </releases>
          <snapshots>
            <enabled>true</enabled>
            <updatePolicy>never</updatePolicy>
          </snapshots>
        </pluginRepository>
      </pluginRepositories>
    </profile>
</profiles>

官方文档

使用单一仓库

某些情况下可以 force maven 使用单一仓库,mirror 所有请求到单一仓库,公司内部可这么做。这个仓库需要包含所有需要的 artifacts,或者需要设置代理去请求其他仓库,否则 maven 可能找不到某些构建。要做到单一仓库,设置 mirrorOf*

maven 2.0.5+ 以上版本支持

<settings>
  ...
  <mirrors>
    <mirror>
      <id>internal-repository</id>
      <name>Maven Repository Manager running on repo.mycompany.com</name>
      <url>http://repo.mycompany.com/proxy</url>
      <mirrorOf>*</mirrorOf>
    </mirror>
  </mirrors>
  ...
</settings>

设置镜像

设置镜像的作用是为了加快下载速度,理论上来说任何一个仓库 B 可以提供仓库 A 所有的内容,那么可以认为 B 是 A 的一个镜像,比如说 阿里提供了很多仓库的镜像 使用这些镜像可以提高下载速度。

<mirror>
      <id>mirror</id>
      <mirrorOf>external:*,!repo</mirrorOf>
      <name>nexus repository</name>
      <url>http://nexus.xxx/repository/maven-proxy</url>
</mirror>

说明:

  • id 唯一标识
  • mirrorOf 指定镜像规则,什么情况下从镜像仓库拉取,官方文档
    • * 匹配所有
    • external:* 除了本地缓存的所有仓库
    • repo,repo1 repo 或者 repo1 ,这里的 repo 指的仓库 ID
    • *,!repo1 除了 repo1 的所有仓库
  • name 名称描述
  • url 地址

使用场景

大部分情况下公司或者自用都会自建 nexus 仓库,那么首先 profile 中会配置需要的远程仓库,比如 id 为 repo url 为 http://nexus.xxx.xxx 那么可以在 mirror 中配置 mirrorOf

<mirrorOf>repo</mirrorOf>
<url>指向你自己的搭的代理</url>

这样对 repo 的所有请求都会转发给你自己的 Nexus。

mirrorOf 也有很多其他的语法,可以参考上面。


2019-04-23 maven , repository , build-tools , java

Asciidoctor Maven Plugin 使用

Asciidoctor Maven Plugin 这一款 maven 插件可以使用 Asciidoctor 将 AsciiDoc 文档转变成可读文档。

Setup

<plugins>
    <plugin>
        <groupId>org.asciidoctor</groupId>
        <artifactId>asciidoctor-maven-plugin</artifactId>
        <version>1.5.6</version>
        ...
    </plugin>
</plugins>

and

<plugin>
    ...
    <executions>
        <execution>
            <id>output-html</id>
            <phase>generate-resources</phase>
            <goals>
                <goal>process-asciidoc</goal>
            </goals>
        </execution>
    </executions>
</plugin>

reference


2019-04-23 maven , asciidoctor , maven-plugin

电子书

最近文章

  • Substratum Android 上的主题引擎 Substratum 是一款 Android 上的主题工具,能够不用 root 来达到系统级主题修改。并且在该工具下有一群爱好者和社区开发了无数的美观且实用的主题。Substratum 开始于以前非常流行的 CyanogenMod, Cyanogen 内置了该主题引擎,并使得修改主题异常简单。但是不幸的是,Cyanogen 没有继续下去, 但是 Substratum 死灰复燃。最初的时候 Substratum 还需要 Root 来使用,但是 Android Oreo 之后就可以不依赖于 Root 了。
  • Flask Babel 使用 Flask babel 是 Flask 的语言扩展,允许非常简单的方式让 Flask 支持多语言。
  • Android 内核中的 CPU 调频 CPU 调频模块主要分为三块:
  • QQ 音乐 qmcflac 文件解密 首先说重点,代码来自:
  • Vim 插件之全局搜索:ack.vim 这篇文章看开始陆陆续续记录一下用过的 Vim Plugin,虽然有些一直也在用但从没有好好整理过,正好这篇开一个计划吧。