Jess Rule Engine dan JSR 94

1. Ikhtisar

Menggunakan mesin aturan adalah cara terbaik untuk memisahkan logika bisnis dari kode boilerplate kami dan melindungi kode aplikasi kami dari perubahan bisnis.

Dalam artikel sebelumnya tentang Java Rule Engine, kami menyebutkan spesifikasi JSR 94. Mesin Aturan Jess sangat penting sebagai implementasi driver aturan referensi untuk JSR 94 , jadi mari kita lihat.

2. Mesin Aturan Jess

Jess adalah salah satu mesin aturan paling awal yang mudah diintegrasikan dengan Java. Jess menggunakan implementasi yang disempurnakan dari algoritma Rete yang sangat efisien, membuatnya jauh lebih cepat daripada pengulangan Java sederhana untuk sebagian besar skenario.

Aturan dapat dieksekusi dari kumpulan aturan yang ditulis dalam Bahasa Aturan Jess asli, sintaks berbasis Lisp yang diperluas, atau dari format XML yang lebih verbose. Kami akan menggunakan format asli.

Ada IDE berbasis Eclipse untuk pengembangan (untuk versi Eclipse yang lebih lama) dan beberapa dokumentasi bagus tentang penggunaan dan integrasi Jess dengan Java. Bahkan ada antarmuka baris perintah REPL di mana kita dapat mencoba ide-ide kita sebelum membuat file aturan.

Sebagai mesin aturan referensi untuk JSR 94, Jess menurut definisi memenuhi JSR 94, meskipun tidak lagi dalam pengembangan aktif.

2.1. Sepatah Kata Singkat Tentang JSR 94

JSR 94 menyediakan API yang dapat kita gunakan untuk memberi kita kebebasan dari mesin aturan mana pun yang kita pilih. Kita dapat memasang mesin aturan yang memenuhi JSR 94 ke dalam kode kita dan menjalankan beberapa aturan tanpa perlu mengubah cara kita berinteraksi dengan mesin aturan dalam aplikasi kita.

Ini tidak berarti aturan yang mendasari mesin aturan akan terlihat sama - kita mungkin harus menulis ulang jika kita mengubah mesin aturan, tetapi itu berarti kita tidak perlu menulis ulang bagian dari aplikasi kita untuk menggunakan mesin aturan baru. Satu-satunya perubahan kode yang kami perlukan adalah memperbarui nama driver dan beberapa nama file aturan.

2.2. Pengemudi Jess JSR 94

Meskipun ada driver mesin aturan referensi untuk Jess yang disertakan untuk JSR 94, Jess sendiri tidak disertakan, karena ini adalah produk komersial berlisensi. Driver referensi ada di paket org.jcp.jsr94.jess , tetapi driver yang lebih baru tersedia di paket jess.jsr94 saat kita mengunduh Jess.

Mari kita mulai dengan melihat integrasi Java asli Jess sebelum kita melanjutkan untuk melihat bagaimana lapisan JSR 94 mengubah ini.

3. Memberikan Contoh

Sebelum kita mulai mengintegrasikan Jess ke kode kita, pastikan kita telah mengunduhnya dan membuatnya tersedia di classpath kita. Kami harus mendaftar untuk mengunduh uji coba gratis selama 30 hari kecuali kami sudah memiliki lisensi.

Jadi, mari unduh Jess, keluarkan Jess71p2.jar yang diunduh , dan jalankan salah satu contohnya untuk memastikan kami memiliki versi yang berfungsi.

3.1. Jess yang berdiri sendiri

Mari kita lihat di direktori Jess71p2 / contoh , di mana direktori jess menyimpan beberapa contoh kumpulan aturan. Direktori pricing_engine menunjukkan integrasi yang dapat dijalankan melalui skrip ant build.xml . Mari kita ubah direktori kita menjadi contoh mesin harga dan jalankan program melalui uji ant :

cd Jess71p2/examples/pricing_engine ant test

Ini membangun dan menjalankan contoh kumpulan aturan harga:

Buildfile: Jess71p2\examples\pricing_engine\build.xml ... test: [java] Items for order 123: [java] 1 CD Writer: 199.99 ... [java] Items for order 666: [java] 1 Incredibles DVD: 29.99 [java] Offers for order 666: [java] BUILD SUCCESSFUL Total time: 1 second

3.2. Jess Dengan JSR 94

Sekarang kita memiliki Jess yang berfungsi, mari kita unduh JSR 94 dan kemudian unzip untuk membuat direktori jsr94-1.0 dengan direktori ant, doc, lib, dan src di dalamnya.

unzip jreng-1_0a-fr-spec-api.zip

Ini memberi kita JSR 94 API dan driver referensi Jess, tetapi tidak disertai dengan implementasi Jess berlisensi, jadi jika kita mencoba menjalankan contoh sekarang, kita akan mendapatkan kesalahan berikut:

Error: The reference implementation Jess could not be found.

Jadi, mari tambahkan implementasi referensi Jess, jess.jar , yang datang sebagai bagian dari Jess71p2 yang kita unduh sebelumnya dan salin ke direktori lib JSR 94, lalu jalankan contoh:

cp Jess71p2/lib/jess.jar jsr94-1.0/lib/ java -jar jsr94-1.0/lib/jsr94-example.jar

Contoh tersebut menjalankan beberapa aturan untuk menentukan sisa kredit pelanggan saat faktur dibayarkan:

Administration API Acquired RuleAdministrator: [email protected] ... Runtime API Acquired RuleRuntime: [email protected] Customer credit limit result: 3000 ... Invoice 2 amount: 1750 status: paid Released Stateful Rule Session.

4. Mengintegrasikan Jess Dengan Java

Sekarang kita memiliki Jess dan JSR 94 yang diunduh dan telah menjalankan beberapa aturan baik secara native maupun melalui JSR, mari kita lihat bagaimana mengintegrasikan Jess ruleset ke dalam program Java.

Dalam contoh kita, kita akan mulai dengan mengeksekusi file aturan Jess sederhana, hellojess.clp, dari kode Java, dan kemudian melihat file aturan lain, bonus.clp , yang akan menggunakan dan memodifikasi beberapa objek kita.

4.1. Ketergantungan Maven

Tidak ada ketergantungan Maven yang tersedia untuk Jess, jadi jika kita belum melakukannya, mari kita unduh dan ekstrak jar Jess ( jess.jar ) dan mvn instal ke repositori Maven lokal kita:

mvn install:install-file -Dfile=jess.jar -DgroupId=gov.sandia -DartifactId=jess -Dversion=7.1p2 -Dpackaging=jar -DgeneratePom=true

Kami kemudian dapat menambahkannya sebagai ketergantungan dengan cara biasa:

 gov.sandia jess 7.1p2 

4.2. Halo Jess Rules File

Selanjutnya, mari buat file aturan yang paling sederhana untuk mencetak pesan. Kami akan menyimpan file aturan sebagai hellojess.clp :

(printout t "Hello from Jess!" crlf)

4.3. Mesin Aturan Jess

Now, let's create an instance of the Jess Rete rule engine, reset() it to its initial state, load up the rules in hellojess.clp, and run them:

public class HelloJess { public static void main(String[] args) throws JessException { Rete engine = new Rete(); engine.reset(); engine.batch("hellojess.clp"); engine.run(); }

For this simple example, we've just added the potential JessException to our main method's throws clause.

When we run our program, we'll see the output:

Hello from Jess!

5. Integrating Jess to Java With Data

Now that everything is installed correctly and we can run rules, let's see how we add data for the rule engine to process and how we retrieve the results.

First, we'll need some Java classes to work with, and then a new ruleset that uses them.

5.1. Model

Let's create some simple Question and Answer classes:

public class Question { private String question; private int balance;  // getters and setters  public Question(String question, int balance) { this.question = question; this.balance = balance; } } public class Answer { private String answer; private int newBalance;  // getters and setters  public Answer(String answer, int newBalance) { this.answer = answer; this.newBalance = newBalance; } }

5.2 Jess Rule With Input and Output

Now, let's create a simple Jess ruleset called bonus.clp that we'll pass a Question to and receive an Answer from.

First, we import our Question and Answer classes and then use Jess's deftemplate function to make them available to the rule engine:

(import com.baeldung.rules.jsr94.jess.model.*) (deftemplate Question (declare (from-class Question))) (deftemplate Answer (declare (from-class Answer)))

Note the use of parentheses, which denote Jess function calls.

Now, let's use defrule to add a single rule avoid-overdraft in Jess's extended Lisp format that gives us a bonus of $50 if the balance in our Question is below zero:

(defrule avoid-overdraft "Give $50 to anyone overdrawn" ?q <- (Question { balance  (add (new Answer "Overdrawn bonus" (+ ?q.balance 50))))

Here, the “?” binds an object to a variable q when the conditions on the right-hand side of the “<-“ match. In this case, that's when the rule engine finds a Question that has a balance less than 0.

When it does, then the actions to the right of the “=>” are triggered so the engine adds a new Answer object to the working memory. We give it the two required constructor arguments: “Overdrawn bonus” for the answer parameter and a (+) function to calculate the newAmount parameter.

5.3. Manipulating Data With the Jess Rule Engine

We can use add() to add a single object at a time to our rule engine's working memory, or addAll() to add a collection of data. Let's use add() to add a single question:

Question question = new Question("Can I have a bonus?", -5); engine.add(data);

With all of our data in place, let's execute our rules:

engine.run();

The Jess Rete engine will work its magic and return when all relevant rules have executed. In our case, we'll have an Answer to inspect.

Let's use a jess.Filter to extract our Answer from the rule engine into an Iterable results object:

Iterator results = engine.getObjects(new jess.Filter.ByClass(Answer.class)); while (results.hasNext()) { Answer answer = (Answer) results.next(); // process our Answer }

We don't have any reference data in our simple example, but when we do, we can use a WorkingMemoryMarker and engine.mark() to mark the state of the rule engine's working memory after adding the data. Then we can call engine.resetToMark with our marker to reset the working memory to our “loaded” state and efficiently reuse the rule engine for a different set of objects:

WorkingMemoryMarker marker; // load reference data marker = engine.mark(); // load specific data and run rules engine.resetToMark(marker);

Now, let's take a look at how we run this same ruleset using JSR 94.

6. Using JSR 94 to Integrate the Jess Rule Engine

JSR 94 standardizes how our code interacts with a rule engine. This makes it easier to change our rule engine without significantly changing our application if a better alternative comes along.

The JSR 94 API comes in two main packages:

  • javax.rules.admin – for loading drivers and rules
  • javax.rules – to run the rules and extract results

We'll look at how to use the classes in both of these.

6.1. Maven Dependency

First, let's add a Maven dependency for jsr94:

 jsr94 jsr94 1.1 

6.2. Administration API

To start using JSR 94, we need to instantiate a RuleServiceProvider. Let's create one, passing it our Jess rules driver:

String RULE_SERVICE_PROVIDER="jess.jsr94"; Class.forName(RULE_SERVICE_PROVIDER + ".RuleServiceProviderImpl"); RuleServiceProvider ruleServiceProvider = RuleServiceProviderManager.getRuleServiceProvider(RULE_SERVICE_PROVIDER);

Now, let's get Jess's JSR 94 RuleAdministrator, load our example ruleset into a JSR 94 RuleExecutionSet, and register it for execution with a URI of our choice:

RuleAdministrator ruleAdministrator = serviceProvider.getRuleAdministrator(); InputStream ruleInput = JessRunner.class.getResourceAsStream(rulesFile); HashMap vendorProperties = new HashMap(); RuleExecutionSet ruleExecutionSet = ruleAdministrator .getLocalRuleExecutionSetProvider(vendorProperties) .createRuleExecutionSet(ruleInput, vendorProperties); String rulesURI = "rules://com/baeldung/rules/bonus"; ruleAdministrator.registerRuleExecutionSet(rulesURI, ruleExecutionSet, vendorProperties);

The Jess driver doesn't need the vendorProperties map we supplied to RuleAdministrator, but it's part of the interface, and other vendors may require it.

Now that our rule engine provider, Jess, has been initialized and our ruleset has been registered, we are almost ready to run our rules.

Before we can run them, we need a runtime instance and a session to run them in. Let's also add a placeholder, calculateResults(), for where the magic will happen, and release the session:

RuleRuntime ruleRuntime = ruleServiceProvider.getRuleRuntime(); StatelessRuleSession statelessRuleSession = (StatelessRuleSession) ruleRuntime.createRuleSession(rulesURI, new HashMap(), RuleRuntime.STATELESS_SESSION_TYPE); calculateResults(statelessRuleSession); statelessRuleSession.release();

6.3. Execution API

Now that we have everything in place, let's implement calculateResults to supply our initial data, execute our rules in a stateless session, and extract the results:

List data = new ArrayList(); data.add(new Question("Can I have a bonus?", -5)); List results = statelessRuleSession.executeRules(data);

Since JSR 94 was written before JDK 5 came along, the API doesn't use generics so let's just use an Iterator to see the results:

Iterator itr = results.iterator(); while (itr.hasNext()) { Object obj = itr.next(); if (obj instanceof Answer) { int answerBalance = ((Answer) obj).getCalculatedBalance()); } }

We've used a stateless session in our example, but we can also create a StatefuleRuleSession if we want to maintain state between invocations.

7. Conclusion

In this article, we learned how to integrate the Jess rule engine into our application by using Jess's native classes and, with a bit more effort, by using JSR 94. We've seen how business rules can be separated into separate files that get processed by the rule engine when our application runs.

Jika kita memiliki aturan untuk logika bisnis yang sama, ditulis untuk mesin aturan lain yang sesuai dengan JSR 94, maka kita cukup menambahkan driver untuk mesin aturan alternatif kita, dan memperbarui nama driver yang harus digunakan aplikasi kita, dan tidak ada perubahan kode lebih lanjut yang harus dilakukan. perlu.

Ada detail lebih lanjut di jess.sandia.gov untuk Menyematkan Jess di Aplikasi Java, dan Oracle memiliki panduan berguna untuk Memulai dengan Java Rule Engine API (JSR 94).

Seperti biasa, kode yang kami lihat di artikel ini tersedia di GitHub.