Red Hat Decision Manager - Decision Serverにルールをデプロイして呼び出す - 

Red Hat Decision Manager 7.0には、Decision Serverという機能があります。
ルールを書いてビルド後、この Decision Serverにデプロイすると、すぐにRESTのサービスとしてルールを実行することができる、というものです。
以前のバージョンにもあったのですが、7.0からは、Decision Serverを単体で使うこともできるようになり、便利になりました。

ということで、今回は、Decision Serverの構築方法と、ルールのデプロイ・実行方法について書きます。

注）記事内容はすべて個人の見解に基づくものであり、RedHat社の公式見解ではありません。

## Decision Server構築に必要な環境を整える
Decision Serverは、APサーバ上にデプロイして使うものです。「.war」形式で提供されます。
ちなみに、warの名前は、**kie-server.war**です。なので、Kie Serverという言われ方をすることもあります。以前はExecution Serverと言ったりもしていました。全部同じものを指していると思ってください。

さて、まずはAPサーバが必要です。
Red Hat Decision Managaer 7.0において、Decision Serverをデプロイ可能な基盤は以下。

<img width="900" alt="スクリーンショット 2018-08-15 16.23.54.png" src="https://qiita-image-store.s3.amazonaws.com/0/276989/c2c7c18a-e0e8-bb8a-8ef5-215dafa8075d.png">

ここでは、JBoss EAP7.1を使いたいと思います。
ちなみに、Red Hat Decision Managerを正式にサブスクリプション契約すると、JBoss EAPも使うことができます。なので、よほどの事情がない限り、APサーバはJBoss EAPを選択しておけばよいです。

### 必要なもの
- JDK 1.8
- Red Hat JBoss EAP 7.1
- Red Hat Decision ManagerのDecision Server

EAPとDecision Serverについては、Red Hatのサイトからダウンロードする必要があります。
[Red Hat Customer Portal](https://access.redhat.com/downloads/)からダウンロードが可能です。

上記サイトで「Request an Evaluation」というリンクがありますので、そこをクリックして、アカウント無い人は、個人アカウントでもなんでも作成することで、90日間の評価版がダウンロード出来ます。
評価版といっても、製品版と中身はまったく同じです。とりあえず使うだけならば、ここから自由にダウンロードが出来るようになっています。（一部製品を除く）

<img width="850" alt="スクリーンショット 2018-08-15 16.32.49.png" src="https://qiita-image-store.s3.amazonaws.com/0/276989/45134b8e-81b7-c92e-fe17-7cacea3a93ab.png">

まずは、EAPを上記からダウンロードします。
バージョンは、7.1をダウンロードします。インストーラーからインストールしてもよいですが、以下をダウンロードして、解凍するだけでもOKです。

<img width="960" alt="スクリーンショット 2018-08-15 16.36.17.png" src="https://qiita-image-store.s3.amazonaws.com/0/276989/1c21fc47-a508-f303-f04b-75a2a57f0d00.png">

次に、Decision Serverをダウンロードして、EAP上にデプロイします。
同じく、[Red Hat Customer Portal](https://access.redhat.com/downloads/)から、ダウンロードします。

<img width="870" alt="スクリーンショット 2018-08-15 16.39.01.png" src="https://qiita-image-store.s3.amazonaws.com/0/276989/de41d556-1bde-f731-0113-49ae4cb8df1a.png">

Red Hat Decision Manager 7.0.1 Decision Server for All Supported EE7 Containersを選択します。

<img width="967" alt="スクリーンショット 2018-08-15 16.42.20.png" src="https://qiita-image-store.s3.amazonaws.com/0/276989/f9435d92-892f-a062-7460-466224f16d75.png">

zip(rhdm-7.0.1.GA-kie-server-ee7.zip)がダウンロードされるので、解凍します。
解凍すると、kie-server.warというディレクトリがあるのがわかると思います。

## Decision Serverのデプロイ
1. *rhdm-7.0.1.GA-kie-server-ee7/kie-server.war*ディレクトリを、*EAP_HOME/standalone/deployments/* にコピーする
2. *rhdm-7.0.1.GA-kie-server-ee7/SecurityPolicy*ディレクトリを、*EAP_HOME/bin* に上書きする
3. *EAP_HOME/standalone/deployments/*に、**kie-server.war.dodeploy**というファイルを作成する。（中身は空でOK。このファイルがあると、EAP起動時にkie-server.warが自動デプロイされる）

以上です。

## Decision Serverの起動
EAPを起動します。
が、その前にユーザを作っておきます。

ユーザを追加するシェルは、*EAP_HOME/bin/add-user.sh*です。

```
$ ./add-user.sh -a --user <USERNAME>  --password <PASSWORD> --role kie-server,admin
```

kie-serverとadminのロールを与える必要があります。
ここでは、USERNAME = redhat, PASSWORD = password1! として作成しました。
（パスワードは、８文字以上で、数値と記号をそれぞれ１つ以上入れる必要があります）

ユーザを追加後、EAPを起動します。

```
$ ./standalone.sh -c standalone-full.xml
```

起動が成功したら、Decision Serverにアクセスしてみましょう。
RESTfulのインターフェースで実行ができるところからであれば、確認が可能です。
とりあえず、ブラウザに以下を入力して実行してみましょう。
```
http://localhost:8080/kie-server/services/rest/server
```
これは、Decision Serverの情報を取得するREST APIです。
XML形式でレスポンスが表示されるはずです。"SUCCESS"とあれば、Decision Serverにアクセス出来ています。

curlコマンドから実行することも可能です。

```
curl -X GET "http://localhost:8080/kie-server/services/rest/server" -H "accept: application/json" -u <USERNAME>:<PASSWORD>
```
`USERNAME`と`PASSWORD`は、add-userで追加したユーザとパスワードをセット。

レスポンスで、"SUCCESS"が帰ってきていたら、Decision Serverにアクセス出来ています。
レスポンス例

```json
{
  "type" : "SUCCESS",
  "msg" : "Kie Server info",
  "result" : {
    "kie-server-info" : {
      "id" : "15ad5bfa-7532-3eea-940a-abbbbc89f1e8",
      "version" : "7.5.0.Final-redhat-6",
      "name" : "KieServer@/kie-server",
      "location" : "http://localhost:8230/kie-server/services/rest/server",
      "capabilities" : [ "KieServer", "BRM", "BPM", "CaseMgmt", "BPM-UI", "BRP", "DMN", "Swagger" ],
      "messages" : [ {
        "severity" : "INFO",
        "timestamp" : {
  "java.util.Date" : 1534320753784
},
        "content" : [ "Server KieServerInfo{serverId='15ad5bfa-7532-3eea-940a-abbbbc89f1e8', version='7.5.0.Final-redhat-6', location='http://localhost:8230/kie-server/services/rest/server', capabilities=[KieServer, BRM, BPM, CaseMgmt, BPM-UI, BRP, DMN, Swagger]}started successfully at Wed Aug 15 17:12:33 JST 2018" ]
      } ]
    }
  }
```

### swagger
Decision Serverで使えるREST API一覧は、swaggerでリストされています。
Decision Serverが起動している状態で、以下にアクセスしてみてください。
swagger UIの画面が表示されます。

```
http://localhost:8080/kie-server/docs/
```
<img width="1150" alt="スクリーンショット 2018-08-15 17.28.34.png" src="https://qiita-image-store.s3.amazonaws.com/0/276989/5a0fdfff-cdb2-2dd7-ee4b-d0d67c4046e4.png">

kie-server.warは、Red Hat Process Automation ManagementというBPM製品でも使われるもののため、Decision Managerでは使われないAPIもずらっとリストされます。
ルール実行で使うAPIは、
**Kie Server::CORE、Decision Service::DMN、Rules Evalutaion::BRM**
のあたりだけです。

***追記：
kie-serverをDecision Serverとしてだけ使う場合、EAP起動時に、`-Dorg.jbpm.server.ext.disabled=true`オプションを付与すると、BPM関連のAPIが除外されます。
EAPの起動時オプションは、`EAP_HOME/bin/standalone.conf`を修正すればいちいち付けなくてよいです。***


このswagger UIから、REST実行も可能になっています。

## ルールをDecision Serverにデプロイする
Decision Serverでは、ルール実行単位、デシジョンサービス単位に、Kie Containerというのを作る必要がある。
まずは、Kie Containerを作るところから。
Kie Containerは、任意のIDを付け、どのルールパッケージを実行するのかを指定する必要がある。
ルールパッケージは、MavenのGAV(GroupID,ArtifactID,Version）を指定する。

事前に、ルールパッケージを作っておく必要があるわけだが、ここでは、
[Red Hat Decision Managerをとりあえず触ってみたい場合　-サンプルプロジェクト実行編-]
(https://qiita.com/Erina/items/85aaf496920ebaf58687)
で作成した、サンプルプロジェクトを使うことにします。
ただし、いくつか手をいれる必要があります。

### 修正点１ pom.xmlの修正

```xml:pom.xml
  <dependencies>
    <dependency>
      <groupId>org.kie</groupId>
      <artifactId>kie-api</artifactId>
      <version>${runtime.version}</version>
      <scope>provided</scope>   ←　ここを追加すればOK
    </dependency>
    <dependency>
      <groupId>org.drools</groupId>
      <artifactId>drools-core</artifactId>
      <version>${runtime.version}</version>
      <scope>provided</scope>  ←　ここを追加すればOK
    </dependency>
    <dependency>
      <groupId>org.drools</groupId>
      <artifactId>drools-decisiontables</artifactId>
      <version>${runtime.version}</version>
      <scope>provided</scope> ←　ここを追加すればOK
    </dependency>
    <dependency>
      <groupId>org.jbpm</groupId>
      <artifactId>jbpm-test</artifactId>
      <version>${runtime.version}</version>
      <scope>provided</scope> ←　ここを追加すればOK
    </dependency>
```

org.drools,org.jbpm等のjarは、Decision Server内のものを参照するため、dependencyのscopeを変更します。

### 修正点２ Messageファクトクラスを作成

サンプルプロジェクトでは、Messageファクトは、com.sample.DroolsTestクラス内のインナークラスとして定義されていましたが、独立したクラスとして定義します。

```java:Message.java
package com.sample;

public class Message {

	@Override
	public String toString() {
		return "Message [message=" + message + ", status=" + status + "]";
	}

    public static final int HELLO = 0;
    public static final int GOODBYE = 1;

    private String message;

    private int status;

    public String getMessage() {
        return this.message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public int getStatus() {
        return this.status;
    }

    public void setStatus(int status) {
        this.status = status;
    }

}
```

また、上記により、Messageファクトのパッケージが変更になりますので、drlのimport文も修正します。

```text:Sample.drl
package com.sample

import com.sample.Message;   ←ここを変更

以下略
```

サンプルプロジェクトを、Maven Installします。Decision Serverはデフォルトでは、ローカルの.m2ディレクトリを見に行きます。

サンプルプロジェクトのGAVは以下の通り。

```xml:pom.xml
  <groupId>com.sample</groupId>
  <artifactId>Sampleprj</artifactId>
  <version>1.0.0-SNAPSHOT</version>
```
Kie ContainerIDはなんでもよいので、ここでは、`testcon`とします。

Kie Containerを新規作成するAPIは、これです。

```
[PUT]/server/containers/{id}
```
idは、Kie ContainerID

bodyには、GAVを指定します。

```json
{
  "release-id": {
    "group-id": "com.sample",
    "artifact-id": "Sampleprj",
    "version": "1.0.0-SNAPSHOT"
  }
}
```
そして、REST実行。

curlコマンドからでもいいですが、swaggerUIからの実行が簡単です。
swaggerUIで、実行するAPIを検索して、「Try it out」をクリック。
<img width="1151" alt="スクリーンショット 2018-08-15 23.50.59.png" src="https://qiita-image-store.s3.amazonaws.com/0/276989/14e49ad3-3742-b7c2-507a-ac679a0d558b.png">

必須パラメータと、body部を記入して、「Execute」を実行。リクエスト・レスポンスの形式は、xmlかjsonかを選択出来ます。

<img width="1156" alt="スクリーンショット 2018-08-15 23.49.47.png" src="https://qiita-image-store.s3.amazonaws.com/0/276989/fbc90a28-8529-4812-8a55-d27dffbbf908.png">

さて、ここで「Execute」を押して、Kie Containerの作成が成功すれば、サンプルプロジェクトのルールがデプロイされたことになります。Kie Containerは作成すると同時に開始します。
成功した場合、EAPのサーバログには以下のようなログが出されます。

```
23:38:06,768 INFO  [org.kie.server.services.impl.KieServerImpl] (EJB default - 1) Container testcon (for release id com.sample:Sampleprj:1.0.0-SNAPSHOT) successfully started
```

### Kie Containerの作成（ルールのデプロイ）に失敗する場合
#### Cannot find kbase, either it does not exist or there are multiple default kbases in kmodule.xml というエラーになる場合

```
23:29:07,431 ERROR [org.kie.server.services.jbpm.JbpmKieServerExtension] (default task-5) Error when creating container testcon by extension jBPM KIE Server extension: java.lang.RuntimeException: java.lang.IllegalStateException: Cannot find kbase, either it does not exist or there are multiple default kbases in kmodule.xml
```

これは、上のほうの***追記***にも書きましたが、EAP起動時オプションに、`-Dorg.jbpm.server.ext.disabled=true`を付けたら、解消されました。

#### 大量のWARNログが出る
Kie Container作ろうとすると、以下のようなWARNログが大量に出る、という場合は、ルールパッケージのdependencyに原因がある可能性が高い。

```
02:48:20,141 WARN  [org.kie.server.services.drools.DroolsKieServerExtension] (EJB default - 1) Unexpected error while create instance of type org.dom4j.xpath.DefaultNamespaceContext due to Failed to link org/dom4j/xpath/DefaultNamespaceContext (Module "deployment.kie-server.war" from Service Module Loader): org/jaxen/NamespaceContext
02:48:20,189 WARN  [org.kie.server.services.drools.DroolsKieServerExtension] (EJB default - 1) Unable to create instance of type org.jaxen.expr.iter.IterableFollowingSiblingAxis due to org.jaxen.expr.iter.IterableFollowingSiblingAxis
02:48:20,191 WARN  [org.kie.server.services.drools.DroolsKieServerExtension] (EJB default - 1) Unable to create instance of type org.jaxen.expr.iter.IterableAncestorAxis due to org.jaxen.expr.iter.IterableAncestorAxis
```

サンプルプロジェクトのpom.xmlを見ると、org.droolsやorg.jbpmのjarを参照していると思います。
これらは、Decision Server上で提供されるため、build packageの際に含めないように、スコープを`provided`にする必要があります。

```xml:pom.xml
  <dependencies>
    <dependency>
      <groupId>org.kie</groupId>
      <artifactId>kie-api</artifactId>
      <version>${runtime.version}</version>
      <scope>provided</scope>   ←　ここを追加すればOK
    </dependency>
    <dependency>
      <groupId>org.drools</groupId>
      <artifactId>drools-core</artifactId>
      <version>${runtime.version}</version>
      <scope>provided</scope>  ←　ここを追加すればOK
    </dependency>
    <dependency>
      <groupId>org.drools</groupId>
      <artifactId>drools-decisiontables</artifactId>
      <version>${runtime.version}</version>
      <scope>provided</scope> ←　ここを追加すればOK
    </dependency>
    <dependency>
      <groupId>org.jbpm</groupId>
      <artifactId>jbpm-test</artifactId>
      <version>${runtime.version}</version>
      <scope>provided</scope> ←　ここを追加すればOK
    </dependency>
```

このWARNログが出た場合、Kie Containerの作成は成功したように見えても、その後のルール実行がうまくいかないので、注意が必要です。

## ルールを実行する
さて、それでは、デプロイしたサンプルルールをRESTで実行してみたいと思います。
ルール実行のAPIは、以下です。

```
[POST]/server/containers/instances/{id}
```
idは、`testcon`
body部に、ルール実行に関するコマンドや、ルールに渡すデータ（ファクト）を指定します。

たとえば、こんな感じです。

```json
{
  "lookup": "ksession-rules",
  "commands":[
    {
      "insert":{
         "object":{
            "com.sample.Message":{
                     "message": "Hello World",
                     "status": 0
            }
         }
      }
    },
    {"fire-all-rules":""}
  ]
}
```

| name              | description                   | 
|:------------------|:------------------------------|
| lookup            | 実行するkie-session名を指定する。(kmodule.xmlで指定した値）|   
| commands          | BatchExecutionCommandを渡す。<br>複数ある場合は、リスト形式で。 <br>(ここでは、insertとfire-all-rulesの２つ)| 
| insert            | InsertObjectCommand実行。|
| object            | インサートするオブジェクト。|
| fire-all-rules    | FireAllRulesCommand実行。|


複数のファクトを渡したい場合は、insertを繰り返します。↓こんな感じです。

```json
  {"insert":
    {"object":{"com.sample.Message":{
                "message":"hello1",
                "status":0
               }
              }
    }
  },
  {"insert":
    {"object":{"com.sample.Message":{
                "message":"hello2",
                "status":0
               }
              }
    }
  },
```


さて、ルール実行が成功すると、サンプルプロジェクトをローカルで実行した際と同様のログが、EAPのサーバログに出力されるはずです。

```
00:49:48,935 INFO  [stdout] (default task-49) Hello World
00:49:48,936 INFO  [stdout] (default task-49) Goodbye cruel world
```

### ルール実行結果を返したい場合

ルール実行後のファクトを取得したい場合は、取得したいファクトに`out-identifier`でキー名をつけます。

```json
{
  "lookup": "ksession-rules",
  "commands":[
    {
      "insert":{
         "object":{
            "com.sample.Message":{
                     "message": "Hello World",
                     "status": 0
            }
         },
         "out-identifier": "fact1"
      }
    },
    {"fire-all-rules":""}
  ]
}
```

すると、レスポンスがこのようになります。

```json
{
  "type" : "SUCCESS",
  "msg" : "Container testcon successfully called.",
  "result" : {
    "execution-results" : {
      "results" : [ {
        "value" : {"com.sample.Message":{
  "message" : "Goodbye cruel world",
  "status" : 1
}},
        "key" : "fact1"
      } ],
      "facts" : [ {
        "value" : {"org.drools.core.common.DefaultFactHandle":{
  "external-form" : "0:1:279781204:279781204:2:DEFAULT:NON_TRAIT:com.sample.Message"
}},
        "key" : "fact1"
      } ]
    }
  }
}
```

ちなみに、このようなJSONをいちいち作成していられないので、もともとクライアント用のJavaクラスが用意されています。
このクライアントの使い方については、また別の投稿で。