Droolsブログ : 06 Quarkus で Drools/jBPM を動かす Kogito

06 Quarkus で Drools/jBPM を動かす Kogito

これまで入門ネタを書いてきましたが、今回はちょっと最新情報を!

Drools/jBPM には Kogito という新規プロジェクトがあり、Drools/jBPM を軽量に適用するアプローチを目指しています。そしてその中心となるのが Quarkus との組み合わせです。

Quarkus についてはこちらを参照ください!

https://www.slideshare.net/agetsuma/quarkus

で、この Quarkus で Kogito を使うガイドがこちらです。

https://quarkus.io/guides/kogito-guide

ハンズオン的にアプリの作成、実行がガイドされていますが、そもそも動くモノが "using-kogito" にあるので、以下、手っ取り早く実行するための手順を書いていきます。

git clone https://github.com/quarkusio/quarkus-quickstarts.git
cd quarkus-quickstarts/using-kogito
./mvnw clean compile quarkus:dev

はい、これで dev モードで起動します。

ビルドはともかく、Quarkus は 3 秒くらいで起動します。速い!

2019-06-28 17:49:15,946 INFO  [io.quarkus] (main) Quarkus 0.18.0 started in 3.015s. Listening on: http://[::]:8080

REST アクセスでアプリを動作させます。Droolsによるルール、jBPMによるプロセスに沿って動作します。詳しくは上記ガイドを参照してください。"using-kogito" の中のソースを見れば分かりますが、とてもシンプルです。

1) Person を POST します。20才なのでルールで Adult と判定され、プロセスを通過して終了します。

curl -X POST http://localhost:8080/persons \
    -H 'content-type: application/json' \
    -H 'accept: application/json' \
    -d '{"person": {"name":"John Quark", "age": 20}}'

2) アクティブなプロセスを GET します。先程のプロセスは終了したのでレスポンスは空です。

curl -X GET http://localhost:8080/persons \
    -H 'content-type: application/json' \
    -H 'accept: application/json'

3) 別の Person を POST します。15才なのでルールで Child と判定され、"Special handling for children" タスクで停止します。

curl -X POST http://localhost:8080/persons \
    -H 'content-type: application/json' \
    -H 'accept: application/json' \
    -d '{"person": {"name":"Jenny Quark", "age": 15}}'

4) もう一度 GET で確認すると id:2 のアクティブなプロセスインスタンスがあることがわかります。

curl -X GET http://localhost:8080/persons \
    -H 'content-type: application/json' \
    -H 'accept: application/json'
[{"id":2,"person":{"adult":false,"age":15,"name":"Jenny Quark"}}]

5) 以下の POST でタスクを complete します。プロセスが進行し、終了します。

curl -X POST http://localhost:8080/persons/2/ChildrenHandling/1 \
    -H 'content-type: application/json' \
    -H 'accept: application/json' \
    -d '{}'

Ctrl+C で quarkus を停止します。

まだ終わりませんよ!

次はネイティブビルドしてみましょう。現時点(2019/06/29)で、このデモはネイティブビルドに GraalVM 19.0.2+ が必要です。https://www.graalvm.org/downloads/ からダウンロードしましょう(CE でも EE でもいいです)

$GRAALVM_HOME/bin に $PATH を通し、さらに native-image コンポーネントをインストールしておきます。

gu install native-image

using-kogito ディレクトリで以下のコマンドを実行します。ビルドには結構時間がかかります。。。

./mvnw clean package -Dnative

これでバイナリイメージ target/using-kogito-1.0-SNAPSHOT-runner が生成されるので直接実行します。

./target/using-kogito-1.0-SNAPSHOT-runner

4ms で起動。爆速ですねー

2019-06-29 12:56:18,984 INFO  [io.quarkus] (main) Quarkus 0.18.0 started in 0.004s. Listening on: http://[::]:8080

さっきと同じ REST で動作します。

Minishift 備忘録

libvirtqemu-kvm は入ってるとして

$ sudo usermod -a -G libvirt $(whoami)
$ newgrp libvirt
# curl -L https://github.com/dhiltgen/docker-machine-kvm/releases/download/v0.10.0/docker-machine-driver-kvm-centos7 > /usr/local/bin/docker-machine-driver-kvm
$ sudo chmod +x /usr/local/bin/docker-machine-driver-kvm
$ sudo systemctl status libvirtd

ここからダウンロードして解凍 https://github.com/minishift/minishift/releases/

$ ./minishift start --memory 8GB

$ oc login -u system:admin

$ oc login -u developer -p developer
$ oc adm policy add-cluster-role-to-user cluster-admin tkobayas

$ oc port-forward docker-registry-1-tfz56 5000:5000

rheb.hatenablog.com

eval $(minishift oc-env)
source <(oc completion bash)
sudo cp ~/.minishift/cache/oc/v3.11.0/linux/oc ~/usr/local/bin/oc
oc completion bash | sudo tee /etc/bash_completion.d/oc

Droolsブログ : 05 KJAR

05 KJAR

前回まではルールをクラスパスに配置していました。アプリケーションにリソースを組み込むという意味では普通のやりかたです。一方 Drools ではルールをアプリケーションと分離して管理する方法も強力にサポートされています。

  1. ルールを Maven を使って jar にまとめる。jar は Maven リポジトリにデプロイされる
  2. アプリケーションは jar の GAV(GroupId, ArtifactId, Version) を指定して、Maven リポジトリから jar を読み込んでルールを使用する

この jar を KJAR (Knowledge JAR) と呼びます。

おっと、随分 Maven に依存してるように見えますね。上記のステップ 1 は kie-maven-plugin という Maven プラグインを利用して実現します。ステップ 2 では実際アプリケーションは内部的に Maven のライブラリを利用して動作します。開発者はそれほど意識しなくても大丈夫です(最終的な本番環境のアーキテクチャーなどは考慮が必要です。これはまた後日)。動かしてみましょう。

サンプルコードはこちらから clone してください。

git clone https://github.com/tkobayas/drools-blog.git

今日のエントリはその中の 05_kjar です。ディレクトリ "05_kjar" の中に2つのディレクトリがあります。

  • drools-hello-kjar : こちらが KJAR のプロジェクト
  • drools-hello-client : KJAR を利用するアプリケーション
$ cd 05_kjar
$ cd drools-hello-kjar
$ mvn clean install

pom.xml をちょっと見ておきましょう。

  <groupId>org.example</groupId>
  <artifactId>drools-hello-kjar</artifactId>
  <version>1.0.0</version>
  <packaging>kjar</packaging>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>2.1</version>
        <configuration>
          <debug>true</debug>
          <source>1.8</source>
          <target>1.8</target>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.kie</groupId>
        <artifactId>kie-maven-plugin</artifactId>
        <version>${drools.version}</version>
        <extensions>true</extensions>
      </plugin>
    </plugins>
  </build>

このように <packaging> に kjar を指定し、<build> に kie-maven-plugin を設定します。

また、 src/java/resources 下に META-INF/kmodules.xml が必要です。

<?xml version="1.0" encoding="UTF-8"?>
<kmodule xmlns="http://jboss.org/kie/6.0.0/kmodule">
</kmodule>

ここに kbase や ksession に関する設定が書けますが、とりあえず上記のように空っぽでも大丈夫です。

生成される jar は基本的に普通の jar と変わりませんが、ルールのコンパイルなどを行い、エラーチェックをしてくれます。

これで KJAR が Maven ローカルリポジトリ (~/.m2/repository/) にインストールされます。ローカルリポジトリにあるので、次のクライアントコードは Maven 経由でこの jar を見つけることができます。

$ cd ../drools-hello-client
$ mvn clean test
        KieServices ks = KieServices.Factory.get();
        ReleaseId releaseId = ks.newReleaseId("org.example", "drools-hello-kjar", "1.0.0");
        KieContainer kcontainer = ks.newKieContainer(releaseId);
        KieSession ksession = kcontainer.newKieSession();

このように、getKieClasspathContainer() の代わりに GAV を指定して KieContainer を生成します。後は同じです。

Hello Child, ポール
Hello Adult, ジョン
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.715 sec

KJAR をアプリケーションから分離することで、アプリケーションを変更/再デプロイせずにルールだけ更新する、という運用が可能になります。

Droolsブログ : 04 ステートレス ksession

04 ステートレス ksession

ここまで、普通の ksession つまり ステートフル ksession を使ってきましたが、今回は ステートレス ksession の使い方です。

ステートレス ksession とは、「ステートフル ksession のラッパーで、insert, fireAllrules, dispose をまとめてやってくれる便利クラス」です。「まとめてやって、終わってくれる」ので、ユーザが ksession の状態(ステート)を気にする必要はありません。(StatelessKieSession オブジェクト自体に状態が無いわけではないので、名前がちょっとミスリーディングなんですけど。。。)

サンプルではステートフル ksession を使うことが多いのですが、実際のユースケースでは ステートレス ksession で十分な場合が多く、またそのほうがコードも簡潔になるのでおすすめです。

サンプルコードはこちらから clone してください。

git clone https://github.com/tkobayas/drools-blog.git

今日のエントリはその中の 04_stateless です。

ルールは前回の 03 推論(Inference) と全く同じです。Java コードの以下の部分だけが違っています。

        StatelessKieSession ksession = kcontainer.newStatelessKieSession();

newKieSession() のかわりに newStatelessKieSession() で ksession を取得します。

        Command insertElementsCommand = CommandFactory.newInsertElements(Arrays.asList(john, order));
        List<InternalFactHandle> factHandleList = (List<InternalFactHandle>) ksession.execute(insertElementsCommand);

Insert したいファクトを List にして newInsertElements() で Command を作成します。その Command を ksession.execute() に渡せばOKです。

Insert だけしか指示していないようですが、最後に自動的に fireAllRules() を行い、さらに dispose() もやってくれます。

mvn test の結果は 03 推論(Inference) のときと同じであることが確認できると思います。

ステートレス ksession は上記のような、InsertElementsCommand 以外にも様々な Command を実行できます。複数の Command をまとめて BatchExecutionCommand にして実行させることもできます。いずれにせよ ksession.execute() で全てが実行され、終了します。

Logic Pro X プラグイン

Hexagon Sky

www.samplescience.ca

Boards Of Canada 風シンセ。Logic のプリセットシンセは今ひとつ好みに合わないので(といってもES2もAlchemyもまるで使いこなせてないが)ありがたい。

注意点

  • AU (Universal じゃないやつ) を Macintosh HD/Library/Audio/Plug-Ins/Components に置く
  • Universal はピアノロールがバグった
  • AU でもプロジェクト間の貼り付けをやった後、プロジェクトが開けなくなった。こまめに名前変えて保存、WAVへのバウンスした方がいい

  • オートメーションもコントローラーのアサインも効かない。あー、あまり使えないかもー

Droolsブログ : 03 推論(Inference)

03 推論(Inference)

今回は Drools の重要な機能のひとつ、推論(Inference)を紹介します。推論というと少し難しく聞こえますが、「再評価」と言い換えてもよいです。

サンプルコードはこちらから clone してください。

git clone https://github.com/tkobayas/drools-blog.git

今日のエントリはその中の 03_inference です。

以下のルールを見てください。前回と同様のポイント計算ルールですが、前回より簡略化しています(ポイント率の話は抜き)。

rule "春のキャンペーン"
    when
     $o : Order(consumer.memberCreatedAt >= "2019-04-01" && consumer.memberCreatedAt <=  "2019-04-30")
    then
        $o.setExtraPoint($o.getExtraPoint() + 2000);
        update($o);
end

rule "高額商品キャンペーン"
    when
     $o : Order(itemPrice > 100000)
    then
        $o.setExtraPoint($o.getExtraPoint() + 4000);
        update($o);
end

rule "大量ポイント獲得オーダー"
    when
     $o : Order(extraPoint > 5000)
    then
        $o.setSpecialPointOrder(true);
end

"update($o)" というのが今回の肝です。キャンペーンルールに該当し、ポイントが追加された場合、「update()」によりルールの再評価を行います。このとき「$o」つまり Order が更新されたよ、ということを伝えています。

最後のルール "大量ポイント獲得オーダー" は、"春のキャンペーン" と "高額商品キャンペーン" の両方に該当してポイントが 6000 になってはじめてマッチします。このようにルールの実行中に条件が変わってマッチするようなケースに「再評価」が必要になります。ファクトの更新「update()」以外にも挿入「insert()」や削除「delete()」も再評価のトリガーになります。

ここで「あれ? "春のキャンペーン" や "高額商品キャンペーン" も再評価されて2重に実行されたりしないの?」と疑問に思った人もいるかも知れません。実は重要なポイントです。さっき「$o が更新されたよ、と伝える」と書きましたがこのときルールエンジンは $o のどのプロパティが変更されたかを意識します。つまり $o の extraPoint が変更された、と知っているので consumer や itemPrice は再評価しないのです。これは「Property Reactive」という機能で、Drools のバージョンによってデフォルトの動作が違う場合があります。このサンプルで使用している Drools 7.18.0.Final ではデフォルトで「Property Reactive」が有効です。

実行してみましょう。

$ cd 03_inference
$ mvn clean test

以下のようなアウトプットが出ます。

insert : Person [name=ジョン, memberCreatedAt=2019-04-11]
insert : Order [consumer=ジョン, itemName=ギター, itemPrice=200000]
実行 : 春のキャンペーン
実行 : 高額商品キャンペーン
実行 : 大量ポイント獲得オーダー
======================================
ポイントキャンペーンのご活用ありがとうございます!
======================================

ここで、"大量ポイント獲得オーダー" による specialPointOrder フラグはメッセージの表示にしか使っていませんが、アプリでの特別なオーダー処理や、更なるルールの判定につながるような使い方も考えられられます。

推論は強力な機能ですがユースケースによっては使う必要がないでしょう。使うときも自分が混乱しない程度に抑えて使うことをおすすめします。