Droolsブログ : 07 MVEL

07 MVEL

MVEL というのは Java ベースの言語で、 Java をより簡単に表記する事を目的にしています。超ざっくり言うと、getter/setter を省略して書いたりできます。

user.name == 'ジョン レノン'

ドキュメントはこちらを参照ください。

http://mvel.documentnode.com/

Drools ではルールを簡便に書くために MVEL を内部的に利用しています。大きく分けると 3つの箇所で、MVEL が使用されます。

1) ルール制約(Constraint)

例えば

        $p : Person( age >= 26 )

の age >= 28 の部分は はじめは MVEL によって処理されます。よって先程書いた getter の省略などが可能です。

「はじめは」 って書きましたね。実はこの制約部分は20回実行されると動的に Java クラスが生成、コンパイルされ、以降は MVEL ではなく Java クラスとして実行されます。JVMJIT と同じような考え方で、よく実行される部分は最適化してパフォーマンスを上げようというものです。

ただ「いや、そもそも最初から Java クラス生成すればいいんじゃね?」という意見から、もうその方向に開発が進んでいます。これは executable-model というオプションで、すでに Drools 7 では利用可能です。おそらくバージョン 8 ではデフォルトの動作になるでしょう。

いずれにせよユーザにとって内部的な挙動は気にしなくてもよいのですが、「しばらく実行したあとに突然ルールの挙動が変わった」ような問題が発生した場合は、動的な Java クラス生成時のバグが原因だった、なんてこともあります。

2) eval

以下のように eval という文法が使えます。

  dialect "mvel"
  when
    $p1 : Parameter()
    $p2 : Parameter()
    eval($p1.list.contains( $p2.item ))

これは eval() 内部をまるごと Java もしくは MVEL で解釈し、true が返ればルールにマッチする、というものです。上記のように dialect に "mvel" を宣言した場合、MVELで記述できます。 Java 的なロジックをゴリゴリ書けるので、一部で好まれるのですが、Drools エンジンからは最適化ができないのでパフォーマンス上おすすめしません。極力普通のフィールド制約で記述するのが Drools のパフォーマンスを上げるコツです。また、eval は近いうちに deprecated になるのではないかと言われています。

3) RHS

ルールの RHS (then の部分です)を MVEL を使って表記できます。その場合、上記と同様

  dialect "mvel"

を宣言する必要があります。dialect は package 単位もしくは rule 単位で宣言できます。"mvel" 以外には "java" があります。デフォルトは "java" です。

ここで MVEL を使うメリットは。。。 BigDecimal の四則演算が簡単だからです。理由の9割くらいがそれですね。(あと getter/setter が無いと日本語フィールド名が見やすいとか)

    then
        $p.salary = $p.salary + 50000;
end

ただここまで説明しておいてなんですが、Drools チームは MVEL への依存を無くす方向へ進んでいます。ですので、新規開発では、できれば 2 と 3 の利用方法は避けていただいたほうがよいのではないかと思います。(1 は executable-model で内部的に変更される)

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

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

今日のエントリはその中の 07_mvel です。

このようなルールです。eval は使っていません。

https://github.com/tkobayas/drools-blog/blob/master/07_mvel/src/main/resources/org/example/Sample.drl

package org.example
 
import org.example.Person;

dialect "mvel"

rule "昇給"
    when
        $p : Person( age >= 26 )
    then
        $p.salary = $p.salary + 50000;
end

実行すると

$ mvn clean test

...
Running org.example.DroolsTest
...
ジョン の給料は 350000円です。
...

ルール通りに出力されましたね。

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へのバウンスした方がいい

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