LoginSignup
4
0

More than 3 years have passed since last update.

Red Hat Decision Managerのルール実行環境をSpringBootで素早く構築してルールアプリを動かしてみる(後編)

Last updated at Posted at 2020-05-29

前編では、KieServer on Spring Bootを起動するところまでを書いています。
この記事はその続きで、ルールアプリをKieServer on Spring Bootにデプロイして実行するところについてです。

前編はこちら

まずは、Red Hat Decision Managerのルール実行環境をSpringBootで素早く構築してルールアプリを動かしてみる(前編)からどうぞ。

(後編) ルールアプリを作成しKieServer上で実行する

次に、ダウンロードしたzipを解凍して出来た、3つのプロジェクトは、ほぼ中身が空ですので、ここにルールアプリを追加して動かしてみましょう。

プロジェクト構成

3つのプロジェクトを確認します。お好きなIDE上に展開してください。(IDE無くても良いですが、まあ使ったほうが便利かと)
スクリーンショット.png

  • <business-application>-kjar ---ルール定義はこのプロジェクトに配置
  • <business-application>-model ---ファクトをここに配置(ファクトはkjarに含めてもよいが、ファクトはルール呼び出し側でも共有することが多いので、別プロジェクトにするのが一般的)
  • <business-application>-service ---Spring Bootのアプリはここ。今回はここはいじらない。

サンプルルールアプリの概要

簡単なルールアプリを作成します。

ルール概要

とある加工製品の注文データをもとに、注文の可否を判定して結果を返却する。

ファクト

注文者情報と注文情報、2つのファクトを渡す。

注文者ファクト 備考
顧客ID
顧客種別 普通/大口
注文上限 大口顧客の場合の注文数上限
注文ファクト 備考
注文ID
顧客ID
商品ID
注文数量
加工種別 カットなし/カット極小/カット中/カット大
注文可否 デフォルトはtrue(可)
コメント 否の場合の理由
ルール詳細

以下にあてはまる場合は、注文不可とする。それ以外は注文可とする。

  • 商品IDが"T"で始まる商品、かつ加工種別が"カット大"、かつ注文数量が30以下
  • 商品IDが"M"で始まる商品、かつ加工種別が"カット極小"、かつ注文数量が50以下
  • 顧客種別が"大口"以外、かつ注文数量が100以上
  • 顧客種別が"大口"かつ、注文数量>注文上限
ファクト実装

<business-application>-model/src/main/java/com/company/model配下に、注文者クラスと注文クラスを新規追加しましょう。

注文者.java
package com.company.model;

import lombok.Data;

@Data
public class 注文者 {

    private String 顧客ID;
    private String 顧客種別;
    private int 注文上限;
}
注文.java
package com.company.model;

import lombok.Data;

@Data
public class 注文 {

    private String 注文ID;
    private String 顧客ID;
    private String 商品ID;
    private int 注文数量;
    private String 加工種別;

    private boolean 注文可否 = true;
    private String コメント;

}

ここでは、lombok使っています。lombok使わない場合は、それぞれのフィールドにgetter,setterを明示的に実装してください。(ルールはgetter,setterメソッドを使ってファクトの価を参照するため、必須です)
VSCodeのLombok拡張機能、Lombok Annotations Support for VS Codeを入れると簡単にlombok使えます。(pom.xmlにdependency追記も忘れずに)

ルール実装

DRLでルール書きます。(Excelでももちろん書けますよ)
<business-application>-kjar/src/main/resources配下に、ルール格納用フォルダを作り、そこにDRLファイルを新規作成します。

スクリーンショット.png

ordercheckフォルダを新規作成し、その配下に注文可否判定.drlを新規作成しました。
先ほどの4つのルールを、DRLで実装すると、こんな感じになります。

注文可否判定.drl
package ordercheck;

import com.company.model.*;

rule "T商品の加工種別と数量基準内チェック"
    when
        $注文:注文(商品ID matches "T.*", 加工種別 == "カット大", 注文数量 <= 30)
    then
        modify($注文) {
            set注文可否(false),
            setコメント("注文数下限未満(T_大)")
        }
end

rule "M商品の加工種別と数量基準内チェック"
    when
        $注文:注文(商品ID matches "M.*", 加工種別 == "カット極小", 注文数量 <= 50)
    then
        modify($注文) {
            set注文可否(false),
            setコメント("注文数下限未満(M_極小)")
        }
end

rule "大口顧客以外の注文数量上限チェック"
    when
        注文者(顧客種別 != "大口", $ID:顧客ID)
        $注文:注文(顧客ID == $ID, 注文数量 >= 100)
    then
        modify($注文) {
            set注文可否(false),
            setコメント("注文数上限超過")
        }
end

rule "大口顧客の注文数量上限チェック"
    when
        注文者(顧客種別 == "大口", $ID:顧客ID, $注文上限:注文上限)
        $注文:注文(顧客ID == $ID, 注文数量 > $注文上限)
    then
        modify($注文) {
            set注文可否(false),
            setコメント("注文数上限超過(大口)")
        }
end

このルールモジュールの定義を<business-application>-kjar/src/main/resources/META-INF/kmodule.xmlに以下のように追加します。

kmodule.xml
<kmodule xmlns="http://www.drools.org/xsd/kmodule" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <kbase name="ordercheck" packages="ordercheck">
        <ksession name="ksession-ordercheck" type="stateless"/>
    </kbase>
</kmodule>

そして、<business-applicatiton>-kjar/pom.xmlに、ファクトのjarを参照するよう、dependencyを追加します。

pom.xml
<dependencies>
      <dependency>
        <groupId>com.company</groupId>
        <artifactId>business-application-model</artifactId>
        <version>1.0-SNAPSHOT</version>
      </dependency>
  </dependencies>

さて、これでルールアプリが完成しました。
ビルドしてデプロイしてみましょう。

デプロイ

再度、KieServer on Spring Bootを起動します。
(以下コマンドで、起動する際に、再ビルドします。)

$ cd business-application-service/
$  ./launch.sh clean install
ルール実行

ルールアプリをデプロイしたので、KieServer上に、KieContainerが作成されているはずです。
curlで確認してみましょう。

curl -X GET --header 'Accept: application/json' 'http://localhost:8090/rest/server/conainers' -u user:user

KieContainerの情報が取得できたら、container-idを探してメモします。

{
  "type" : "SUCCESS",
  "msg" : "List of created containers",
  "result" : {
    "kie-containers" : {
      "kie-container" : [ {
        "container-id" : "business-application-kjar-1_0-SNAPSHOT",
        "release-id" : {
          "group-id" : "com.company",
          "artifact-id" : "business-application-kjar",
          "version" : "1.0-SNAPSHOT"
        },

中略
  }

"container-id" : "business-application-kjar-1_0-SNAPSHOT"とありますね。
ルール実行の際は、このコンテナIDを指定する必要があります。

ルール実行APIについては、以前のブログ(Red Hat Decision Manager - Decision Serverにルールをデプロイして呼び出す)に少し解説していますので、こちらを参照ください。

さて、まずは、いずれのルールにも一致しないデータでルールを実行してみましょう。
注文可否=trueで結果が返ってくるはずです。

  • 商品IDが"T"で始まる商品、かつ加工種別が"カット大"、かつ注文数量が30以下
  • 商品IDが"M"で始まる商品、かつ加工種別が"カット極小"、かつ注文数量が50以下
  • 顧客種別が"大口"以外、かつ注文数量が100以上
  • 顧客種別が"大口"かつ、注文数量>注文上限
curlコマンド
 curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' -d '{ 
   "lookup": "ksession-ordercheck", 
   "commands":[ 
     {"insert":{"object":{"com.company.model.注文者":{"顧客ID":"1001","顧客種別":"普通"}}}},
     {"insert":{"object":{"com.company.model.注文":{"注文ID":"O-501","顧客ID":"1001","商品ID":"RA29001","加工種別":"カット大","注文数量":50}},"out-identifier":"注文結果"}}, 
     {"fire-all-rules":""} 
   ] 
 }' 'http://localhost:8090/rest/server/containers/instances/business-application-kjar-1_0-SNAPSHOT'

ルールに渡しているファクトの中身に注目してください。
顧客種別=普通なので、大口顧客ではなく、注文数量=50なので、ルールに一致しません。
また、商品IDはT始まりでもM始まりでもないので、ルールに一致しません。

結果
{
  "type" : "SUCCESS",
  "msg" : "Container business-application-kjar-1_0-SNAPSHOT successfully called.",
  "result" : {
    "execution-results" : {
      "results" : [ {
        "value" : 0,
        "key" : ""
      }, {
        "value" : {"com.company.model.注文":{
  "注文ID" : "O-501",
  "顧客ID" : "1001",
  "商品ID" : "RA29001",
  "注文数量" : 80,
  "加工種別" : "カット大",
  "注文可否" : true,
  "コメント" : null
}},
        "key" : "注文結果"
      } ],
      "facts" : [ {
        "value" : {"org.drools.core.common.DefaultFactHandle":{
  "external-form" : "0:2:2067392882:1330587205:2:DEFAULT:NON_TRAIT:com.company.model.注文"
}},
        "key" : "注文結果"
      } ]
    }
  }

結果のファクトを見ると、注文可否=trueになっていることが確認できますね。

では、ルールに渡すファクトの中身を一部変えて、再度実行してみましょう。

curlコマンド
 curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' -d '{ 
   "lookup": "ksession-ordercheck", 
   "commands":[ 
     {"insert":{"object":{"com.company.model.注文者":{"顧客ID":"1001","顧客種別":"大口","注文上限":200}}}},
     {"insert":{"object":{"com.company.model.注文":{"注文ID":"O-501","顧客ID":"1001","商品ID":"RA29001","加工種別":"カット大","注文数量":201}},"out-identifier":"注文結果"}}, 
     {"fire-all-rules":""} 
   ] 
 }' 'http://localhost:8090/rest/server/containers/instances/business-application-kjar-1_0-SNAPSHOT'

今度は、大口顧客で、かつ注文上限数を超えた注文数になっています。
- 顧客種別が"大口"かつ、注文数量>注文上限
のルールに一致するはずです。

結果
{
  "type" : "SUCCESS",
  "msg" : "Container business-application-kjar-1_0-SNAPSHOT successfully called.",
  "result" : {
    "execution-results" : {
      "results" : [ {
        "value" : 1,
        "key" : ""
      }, {
        "value" : {"com.company.model.注文":{
  "注文ID" : "O-501",
  "顧客ID" : "1001",
  "商品ID" : "RA29001",
  "注文数量" : 201,
  "加工種別" : "カット大",
  "注文可否" : false,
  "コメント" : "注文数上限超過(大口)"
}},
        "key" : "注文結果"
      } ],
      "facts" : [ {
        "value" : {"org.drools.core.common.DefaultFactHandle":{
  "external-form" : "0:2:1704910627:1355438064:3:DEFAULT:NON_TRAIT:com.company.model.注文"
}},
        "key" : "注文結果"
      } ]
    }
  }

さて、予想通り、注文可否=false、コメントに「注文数上限超過(大口)」とセットされていますね。
"大口顧客の注文数量上限チェック"ルールが動いたことがわかります。

rule "大口顧客の注文数量上限チェック"
    when
        注文者(顧客種別 == "大口", $ID:顧客ID, $注文上限:注文上限)
        $注文:注文(顧客ID == $ID, 注文数量 > $注文上限)
    then
        modify($注文) {
            set注文可否(false),
            setコメント("注文数上限超過(大口)")
        }
end
Swagger UIを使ったルール実行

curlコマンドではなく、Swagger UIを使ったルール実行も同じように可能です。
Swagger UIはhttp://localhost:8090/rest/api-docs?url=http://localhost:8090/rest/swagger.jsonで開きます。
コンテナIDと、パラメータ部分を入力して、「Try it out!」をクリックします。

スクリーンショット.png

Response Body部分に、結果が表示されます。

curlコマンド実行時にorg.kie.server.api.marshalling.json.JSONMarshallerのエラーになる場合

curl実行時に、-dでjson形式のパラメータをつけた際、Internal Server Error 500で、以下のようなエラーが出る場合があります。

curl実行結果
{"timestamp":"2020-05-xxT07:00:06.636+0000","status":500,"error":"Internal Server Error","message":"org.kie.server.api.marshalling.json.JSONMarshaller$3.idResolver(Lcom/fasterxml/jackson/databind/cfg/MapperConfig;Lcom/fasterxml/jackson/databind/JavaType;Ljava/util/Collection;ZZ)Lcom/fasterxml/jackson/databind/jsontype/TypeIdResolver;","path":"/rest/server/containers/instances/business-application-kjar-1_0-SNAPSHOT"}
コンソールログ
Caused by: java.lang.NoSuchMethodError: org.kie.server.api.marshalling.json.JSONMarshaller$3.idResolver(Lcom/fasterxml/jackson/databind/cfg/MapperConfig;Lcom/fasterxml/jackson/databind/JavaType;Ljava/util/Collection;ZZ)Lcom/fasterxml/jackson/databind/jsontype/TypeIdResolver;

これは、KieServer側のバグで、すでに報告済みなのでそのうち修正されるはずなんですが、とりあえず現時点では、spring-boot-startarのバージョンを少し古いものに変えると、これを回避できます。

<business-application>-service/pom.xmlの以下の箇所を修正します。

pom.xml
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <!--<version>2.2.2.RELEASE</version>-->
    <version>2.0.9.RELEASE</version>
  </parent>

(ちなみに、私は製品側のバージョン: 7.33.0.Final-redhat-00002、spring-boot-startar :2.2.2.RELEASE でやると、このエラーになりました)

はい、以上です。

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0