Jess Rule Engine dan JSR 94

1. Gambaran keseluruhan

Menggunakan enjin peraturan adalah kaedah terbaik untuk memisahkan logik perniagaan dari kod plat boiler kami dan melindungi kod aplikasi kami dari perubahan perniagaan.

Dalam artikel sebelumnya tentang Java Rule Engines, kami menyebutkan spesifikasi JSR 94. Jess Rule Engine sangat penting kerana rujukan peraturan pelaksanaan pemacu untuk JSR 94 , jadi mari kita lihat.

2. Jess Rule Engine

Jess adalah salah satu enjin peraturan awal yang mudah disatukan dengan Java. Jess menggunakan pelaksanaan yang lebih baik dari algoritma Rete yang sangat efisien, menjadikannya lebih pantas daripada gelung Java sederhana untuk kebanyakan senario.

Peraturan boleh dilaksanakan dari kumpulan peraturan yang ditulis dalam Bahasa Peraturan Jess asli, sintaks berasaskan Lisp yang diperluas, atau dari format XML yang lebih verbose. Kami akan menggunakan format asli.

Terdapat IDE berasaskan Eclipse untuk pembangunan (untuk versi Eclipse yang lebih lama) dan beberapa dokumentasi yang sangat baik untuk menggunakan dan mengintegrasikan Jess dengan Java. Bahkan ada antara muka baris perintah REPL di mana kita boleh mencuba idea kita sebelum membuat fail peraturan.

Sebagai enjin peraturan rujukan untuk JSR 94, Jess menurut definisi JSR 94 patuh, walaupun ia tidak lagi dalam pembangunan aktif.

2.1. Perkataan Pantas Mengenai JSR 94

JSR 94 menyediakan API yang dapat kita gunakan untuk memberi kita kebebasan dari mana-mana enjin peraturan yang kita pilih. Kami boleh memasukkan enjin peraturan yang mematuhi JSR 94 ke dalam kod kami dan menjalankan beberapa peraturan tanpa perlu mengubah cara kami berinteraksi dengan enjin peraturan dalam aplikasi kami.

Ini tidak bermaksud peraturan yang mendasari mesin peraturan akan sama - kita mungkin perlu menulis semula peraturan tersebut jika kita menukar enjin peraturan, tetapi ini tidak bermaksud kita tidak perlu menulis semula bahagian aplikasi kita untuk menggunakan enjin peraturan baru. Satu-satunya perubahan kod yang kita perlukan adalah mengemas kini nama pemacu dan beberapa nama fail peraturan.

2.2. Pemandu Jess JSR 94

Walaupun ada pemandu enjin peraturan rujukan untuk Jess yang disertakan untuk JSR 94, Jess sendiri tidak termasuk, kerana itu adalah produk komersial berlesen. Pemacu rujukan datang dalam pakej org.jcp.jsr94.jess , tetapi pemacu yang lebih baru tersedia dalam pakej jess.jsr94 semasa kami memuat turun Jess.

Mari mulakan dengan melihat integrasi Java asli Jess sebelum kita beralih untuk melihat bagaimana lapisan JSR 94 mengubahnya.

3. Contoh yang Diberikan

Sebelum kita mula mengintegrasikan Jess ke kod kita, pastikan kita telah memuat turunnya dan membuatnya tersedia di classpath kami. Kami perlu mendaftar untuk muat turun percubaan 30 hari percuma kecuali kami sudah mempunyai lesen.

Oleh itu, mari muat turun Jess, bongkar Jess71p2.jar yang dimuat turun , dan jalankan salah satu contohnya untuk memastikan kami mempunyai versi yang berfungsi.

3.1. Jess yang berdiri sendiri

Mari lihat di direktori Jess71p2 / contoh , di mana direktori jess menyimpan beberapa contoh peraturan. The pricing_engine menunjukkan direktori integrasi yang boleh dilaksanakan melalui semut build.xml skrip. Mari ubah direktori kami ke contoh mesin harga dan jalankan program melalui ujian semut :

cd Jess71p2/examples/pricing_engine ant test

Ini membina dan menjalankan contoh peraturan penetapan 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 setelah Jess bekerja, mari muat turun JSR 94 dan kemudian unzipnya 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 pemacu rujukan JSR 94 API dan Jess, tetapi tidak disertakan dengan pelaksanaan Jess berlesen, jadi jika kita mencuba menjalankan contoh sekarang, kita akan mendapat ralat berikut:

Error: The reference implementation Jess could not be found.

Oleh itu, mari tambahkan pelaksanaan rujukan Jess, jess.jar , yang datang sebagai sebahagian daripada Jess71p2 yang kami muat turun sebelumnya dan salin ke direktori lib JSR 94, kemudian jalankan contohnya:

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

Contohnya menjalankan beberapa peraturan untuk menentukan baki kredit pelanggan kerana invois dibayar:

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. Menggabungkan Jess dengan Java

Sekarang kita telah memuat turun Jess dan JSR 94 dan menjalankan beberapa peraturan baik secara asli dan melalui JSR, mari kita lihat bagaimana mengintegrasikan kumpulan peraturan Jess ke dalam program Java.

Dalam contoh kami, kami akan memulakan dengan melaksanakan fail peraturan Jess yang sederhana, hellojess.clp, dari kod Java, dan kemudian melihat fail peraturan lain, bonus.clp , yang akan menggunakan dan mengubah beberapa objek kami.

4.1. Ketergantungan Maven

Tidak ada kebergantungan Maven yang tersedia untuk Jess, jadi jika kami belum melakukannya, mari memuat turun dan membongkar balang Jess ( jess.jar ) dan mvn memasangnya ke repositori Maven tempatan kami:

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. Fail Peraturan Hello Jess

Seterusnya, mari buat fail peraturan termudah untuk mencetak mesej. Kami akan menyimpan fail peraturan sebagai hellojess.clp :

(printout t "Hello from Jess!" crlf)

4.3. Enjin Peraturan 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.

Sekiranya kita mempunyai peraturan untuk logik perniagaan yang sama, ditulis untuk enjin peraturan yang mematuhi JSR 94 yang lain, maka kita hanya boleh menambah pemacu untuk enjin peraturan alternatif kita, dan mengemas kini nama pemacu yang harus digunakan oleh aplikasi kita, dan tidak perlu ada perubahan kod selanjutnya. perlu.

Terdapat lebih banyak perincian di jess.sandia.gov untuk Memasukkan Jess dalam Aplikasi Java, dan Oracle mempunyai panduan berguna untuk Bermula dengan API Java Rule Engine (JSR 94).

Seperti biasa, kod yang kita lihat dalam artikel ini terdapat di GitHub.