20
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

KotlinでDroolsを動かす

Last updated at Posted at 2018-01-25

需要があるかわからないですが、業務で使用したので備忘までに書いておきます。

Droolsとは

オープンソースのBRMS(Business Rule Management System)。Redhat製。

BRMSを使うことで、ビジネスルール(if文の分岐)をアプリケーションから外出しにすることが出来ます。
DroolsではDRLと呼ばれる独自言語やExcelでビジネスルールを記述します。

Kotlinで動かす

DroolsはJavaのライブラリが提供されているので、Kotlinでも動かすことが出来ます。
サンプルコードはGitHubにあげてあります。

実装内容

りんごの仕分け(ランク付け)を行います。
りんごには重さがあって、重さによってランクをS、A、B、C、Dの5段階に分けるというルールをKotlinとDroolsを使って実装していきます。

環境

  • Kotlin: 1.2.21
  • Gradle: 4.3.1
  • Drools: 7.5.0.Final
  • IntelliJ IDEA

gradleの設定

dependenciesに以下の2行を追加します

    compile "org.kie:kie-api:7.5.0.Final"
    compile "org.drools:drools-decisiontables:7.5.0.Final"

アプリの実装

りんごはKotlinのdata classを使って実装します

data class Apple(val size: Long, var rank: String = "")

ルールを実行するルールエンジンの取得。実装はこちらの記事を参考にさせて頂きました。

fun getKieSession(filepath: String) : KieSession? {

    val kieServices = KieServices.Factory.get()
    val kieFileSystem = kieServices.newKieFileSystem()

    val file = Paths.get(filepath).toFile()

    // (debug) xlsをdrl形式で表示する
    file.inputStream().use {
        val sc = SpreadsheetCompiler()
        println(sc.compile(it, InputType.XLS))
    }

    var kieSession: KieSession? = null

    file.inputStream().use {
        // inputStreamからの読み込み
        kieFileSystem.write(
                "src/main/resources/rules.xls",
                kieServices.resources.newInputStreamResource(it)
        )
        val kieBuilder = kieServices.newKieBuilder(kieFileSystem).buildAll()

        // ルールファイル記述内容のチェック
        val results = kieBuilder.results
        if (results.hasMessages(Message.Level.ERROR)) {
            println(results.toString())
            throw IllegalStateException(">>>ルールファイルの記述に問題があります。")
        }

        kieSession = kieServices
                .newKieContainer(kieServices.repository.defaultReleaseId)
                .newKieSession()
    }

    return kieSession
}

取得したルールエンジンを元にルールを実行します。

fun main(args: Array<String>) {

    val filepath = if(args.isNotEmpty()) {
        args[0]
    } else {
        throw IllegalArgumentException("ルールファイルが指定されていません")
    }

    val apples = listOf(
            Apple(size = 1),
            Apple(size = 5),
            Apple(size = 10),
            Apple(size = 30),
            Apple(size = 70),
            Apple(size = 120)
    )

    val kieSession = getKieSession(filepath)
    kieSession?.let {

        apples.forEach { apple ->
            it.insert(apple)
            it.fireAllRules()    // ルール実行

            println("Size: ${apple.size} -> Rank: ${apple.rank}")
        }

        it.dispose()
    }
}

ルール定義

Excelを使ってルールを記述します。

ルールエンジンで使用するclassをimportします。
スクリーンショット 2018-01-25 13.13.01.png

CONDITIONの列に判断条件を書き、ACTION列に条件に合致した場合に実行する処理を書きます。
今回の場合、サイズが

  • 100以上 -> Rank S
  • 99〜50 -> Rank A
  • 49〜10 -> Rank B
  • 9〜5 -> Rank C
  • 5未満 -> Rank D
    というルールになります。
スクリーンショット 2018-01-25 13.13.27.png

CONDITIONのeval($param)という記述はCONDITION列のコード(100 > size && size >= 50とか)をJavaのコードとして解釈し実行してくれるものです。

実行してみる

$ ./gradlew run -Pargs="data/rules1.xls"

(省略)

Size: 1 -> Rank: D
Size: 5 -> Rank: C
Size: 10 -> Rank: B
Size: 30 -> Rank: B
Size: 70 -> Rank: A
Size: 120 -> Rank: S

サイズごとにランクをつけて返してくれました。

ルールを↓のように変更して実行すると、
スクリーンショット 2018-01-25 13.25.52.png

$ ./gradlew run -Pargs="data/rules2.xls"

(省略)

Size: 1 -> Rank: D
Size: 5 -> Rank: D
Size: 10 -> Rank: D
Size: 30 -> Rank: D
Size: 70 -> Rank: C
Size: 120 -> Rank: A

ルールの変更によって結果が変わったことがわかります。

まとめ

このくらいの条件ならコードにif文を書けばいいじゃないか
BRMSを使用することでビジネスルールをコードと分離でき、運用側・業務側でルールを変更することが可能となり、仕様変更に柔軟な対応を行うことが出来ます。
BRMS製品の一つであるDroolsはJavaのライブラリが提供されており、KotlinでもDroolsを利用したアプリを作成することが可能です。

20
9
5

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
20
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?