Jess Rule Engine og JSR 94

1. Oversikt

Å bruke en regelmotor er en fin måte å skille virksomhetslogikken fra vår kjelekode og beskytte applikasjonskoden mot forretningsendringer.

I en tidligere artikkel om Java Rule Engines nevnte vi JSR 94-spesifikasjonen. Jess Rule Engine har særlig betydningsom referansen regler driverimplementering for JSR 94, så la oss ta en titt på det.

2. Jess Rule Engine

Jess er en av de tidligste regelmotorene som enkelt kan integreres med Java. Jess bruker en forbedret implementering av den svært effektive Rete-algoritmen, noe som gjør den mye raskere enn en enkel Java-løkke for de fleste scenarier.

Regler kan utføres fra regelsett skrevet på det opprinnelige Jess Rules Language, en utvidet Lisp-basert syntaks, eller fra et mer detaljert XML-format. Vi bruker det opprinnelige formatet.

Det er en Eclipse-basert IDE for utvikling (for eldre versjoner av Eclipse) og noe utmerket dokumentasjon om bruk og integrering av Jess med Java. Det er til og med et REPL kommandolinjegrensesnitt der vi kan prøve ideene våre før vi oppretter en reglerfil.

Som referanseregelmotor for JSR 94 er Jess per definisjon JSR 94-kompatibel, selv om den ikke lenger er under aktiv utvikling.

2.1. Et raskt ord om JSR 94

JSR 94 gir et API som vi kan bruke til å gi oss uavhengighet fra hvilken regelmotor vi velger. Vi kan koble en hvilken som helst JSR 94-kompatibel regelmotor til koden vår og kjøre noen regler uten å måtte endre måten vi samhandler med regelmotoren i applikasjonen vår.

Dette betyr ikke at de underliggende reglene til regelmotoren vil se like ut - det kan hende at vi må omskrive dem hvis vi endrer regelmotoren, men det betyr at vi ikke trenger å omskrive deler av applikasjonen vår for å bruke den nye regelmotoren. De eneste kodeendringene vi trenger er å oppdatere drivernavnet og noen regelfilnavn.

2.2. Jess JSR 94 Driver

Selv om det er en referanseregelmotor sjåfør for Jess inkludert for JSR 94 er ikke Jess selv inkludert, da det er et lisensiert kommersielt produkt. Referansedriveren kommer i org.jcp.jsr94.jess pakken, men en nyere driver er tilgjengelig i jess.jsr94 pakken når vi laster ned Jess.

La oss begynne med å se på Jess native Java-integrasjon før vi går videre for å se hvordan JSR 94-laget endrer dette.

3. Tilveiebragte eksempler

Før vi begynner å integrere Jess i koden vår, la oss sørge for at vi har lastet den ned og gjort den tilgjengelig på klassestien vår. Vi må registrere deg for gratis 30-dagers prøve nedlasting med mindre vi allerede har lisens.

Så la oss laste ned Jess, pakke ut den nedlastede Jess71p2.jar, og kjør et av eksemplene for å sikre at vi har en fungerende versjon.

3.1. Frittstående Jess

La oss se i Jess71p2 / eksempler katalog, hvor jess katalog inneholder noen eksempler på regelsett. De prismotor katalog viser en integrasjon som kan utføres via en maur build.xml manus. La oss endre katalogen vår til eksemplet på prissettingsmotoren og kjøre programmet via maurprøve:

cd Jess71p2 / eksempler / pricing_engine ant test

Dette bygger og kjører et eksempel på prisregler:

Buildfile: Jess71p2 \ examples \ pricing_engine \ build.xml ... test: [java] Varer for ordre 123: [java] 1 CD Writer: 199,99 ... [java] Varer for ordre 666: [java] 1 Utrolig DVD: 29.99 [java] Tilbud for ordre 666: [java] BYGG SUCKESSFULL Total tid: 1 sekund

3.2. Jess med JSR 94

Nå som vi har Jess i arbeid, la oss laste ned JSR 94 og deretter pakke den ut for å lage en jsr94-1.0-katalog med ant, doc, lib og src kataloger inne.

pakke ut jreng-1_0a-fr-spec-api.zip

Dette gir oss JSR 94 API og Jess referansedriver, men det følger ikke med den lisensierte Jess-implementeringen, så hvis vi prøver å kjøre et eksempel nå, får vi følgende feil:

Feil: Referanseimplementeringen Jess ble ikke funnet.

Så la oss legge til Jess-referanseimplementeringen, jess.jar, som kom som en del av Jess71p2 vi lastet ned tidligere og kopierte den til JSR 94 lib-katalogen, og kjørte eksemplet:

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

Eksemplet kjører noen regler for å bestemme kundens gjenværende kreditt når fakturaer betales:

Administration API Acquired RuleAdministrator: [email protected] ... Runtime API Acquired RuleRuntime: [email protected] Resultat for kundekredittgrense: 3000 ... Faktura 2 beløp: 1750 status: betalt Utgitt Stateful Rule Session.

4. Integrering av Jess med Java

Nå som vi har lastet ned Jess og JSR 94 og kjørt noen regler både innfødt og via JSR, la oss se på hvordan vi kan integrere et Jess-regelsett i et Java-program.

I vårt eksempel starter vi med å kjøre en enkel Jess-reglerfil, hellojess.clp, fra Java-kode, og se deretter på en annen reglerfil, bonus.clp, som vil bruke og endre noen av objektene våre.

4.1. Maven avhengighet

Det er ingen Maven-avhengighet tilgjengelig for Jess, så hvis vi ikke allerede har gjort det, la oss laste ned og pakke ut Jess-krukken (jess.jar) og mvn installere den til vårt lokale Maven-arkiv:

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

Vi kan da legge det til som en avhengighet på vanlig måte:

 gov.sandia jess 7.1p2 

4.2. Hei Jess Rules File

Deretter la oss lage de enkleste reglerfilene for å skrive ut en melding. Vi lagrer reglefilen som hellojess.clp:

(utskrift t "Hei fra Jess!" crlf)

4.3. Jess Rule Engine

La oss nå lage en forekomst av Jess Rete regelmotor, nullstille() den til sin opprinnelige tilstand, last opp reglene i hellojess.clp, og kjør dem:

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

For dette enkle eksemplet har vi nettopp lagt til potensialet JessException til vår hoved- metoden kaster klausul.

Når vi kjører programmet vårt, ser vi resultatet:

Hei fra Jess!

5. Integrering av Jess til Java med data

Nå som alt er riktig installert og vi kan kjøre regler, la oss se hvordan vi legger til data for regelmotoren som skal behandles og hvordan vi henter resultatene.

Først trenger vi noen Java-klasser å jobbe med, og deretter et nytt regelsett som bruker dem.

5.1. Modell

La oss lage noen enkle Spørsmål og Svar klasser:

offentlig klasse Spørsmål {privat String spørsmål; privat int-balanse;  // getters og setters  public Question (String question, int balance) {this.question = question; denne. balanse = balanse; }} offentlig klasse Svar {privat String svar; private int newBalance;  // getters og setters  public Answer (String answer, int newBalance) {this.answer = svar; this.newBalance = newBalance; }}

5.2 Jess regel med inngang og utgang

La oss nå lage et enkelt Jess-regelsett som heter bonus.clp at vi får passere en Spørsmål til og motta en Svar fra.

Først vi import våre Spørsmål og Svar klasser og deretter bruke Jess deftemplate funksjon for å gjøre dem tilgjengelige for regelmotoren:

(importer com.baeldung.rules.jsr94.jess.model. *) (deftemplate Question (erklær (fra klasse spørsmål))) (deftemplate Answer (erklær (fra klasse svar))))

Legg merke til bruken av parenteser, som betegner Jess-funksjonsanrop.

La oss nå bruke det defrule for å legge til en enkelt regel unngå kassakreditt i Jess utvidede Lisp-format som gir oss en bonus på $ 50 hvis balansen i vår Spørsmål er under null:

(defrule unngå overtrekk "Gi $ 50 til noen som er overtrukket"? q <- (Spørsmål {saldo (legg til (nytt svar "Overdratt bonus" (+? q.balanse 50))))

Her er “?” binder et objekt til en variabel q når forholdene på høyre side av<-“ kamp. I dette tilfellet er det da regelmotoren finner en Spørsmål som har en balansere mindre enn 0.

Når det gjør det, blir handlingene til høyre for “=>” utløses slik at motoren legge tils a nytt svar motsette seg arbeidsminnet. Vi gir det de to nødvendige konstruktørargumentene: “Overdrawn bonus” for svar parameter og a (+) funksjon for å beregne newAmount parameter.

5.3. Manipulere data med Jess Rule Engine

Vi kan bruke legge til() å legge til et enkelt objekt om gangen i arbeidsminnet til regelmotoren vår, eller Legg til alle() for å legge til en samling data. La oss bruke legge til() for å legge til et enkelt spørsmål:

Spørsmålsspørsmål = nytt spørsmål ("Kan jeg få en bonus?", -5); engine.add (data);

Med alle dataene våre på plass, la oss utføre reglene våre:

engine.run ();

Jess Rete motoren vil arbeide med sin magi og komme tilbake når alle relevante regler er utført. I vårt tilfelle vil vi ha en Svar å inspisere.

La oss bruke en jess. filter å trekke ut vårt Svar fra regelmotoren til en Iterabel resultatobjekt:

Iteratorresultater = engine.getObjects (ny jess.Filter.ByClass (Answer.class)); mens (results.hasNext ()) {Answer answer = (Answer) results.next (); // behandle svaret vårt}

Vi har ingen referansedata i vårt enkle eksempel, men når vi gjør det, kan vi bruke en WorkingMemoryMarker og engine.mark () for å markere tilstanden til arbeidsminnet til regelmotoren etter å ha lagt til dataene. Så kan vi ringe motor.resetToMark med markøren vår for å tilbakestille arbeidsminnet til vår "lastede" tilstand og effektivt gjenbruke regelmotoren til et annet sett med objekter:

WorkingMemoryMarker markør; // last referansedatamarkør = motor.mark (); // laste spesifikke data og kjøre regler engine.resetToMark (markør);

La oss nå se på hvordan vi kjører det samme regelsettet ved hjelp av JSR 94.

6. Bruke JSR 94 til å integrere Jess Rule Engine

JSR 94 standardiserer hvordan koden vår samhandler med en regelmotor. Dette gjør det lettere å endre regelmotoren uten å endre applikasjonen vesentlig hvis det kommer et bedre alternativ.

JSR 94 API kommer i to hovedpakker:

  • javax.rules.admin - for lasting av sjåfører og regler
  • javax.regler - å kjøre reglene og trekke ut resultater

Vi ser på hvordan du bruker klassene i begge disse.

6.1. Maven avhengighet

La oss først legge til en Maven-avhengighet for jsr94:

 jsr94 jsr94 1.1 

6.2. Administrasjons-API

For å begynne å bruke JSR 94, må vi sette i gang a RuleServiceProvider. La oss lage en, ved å gi den vår Jess-driver:

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

La oss nå få Jess's JSR 94 Regeladministrator, last inn vårt eksempel på regelsett i en JSR 94 Regelutførelse sett, og registrer den for utførelse med en URI etter eget valg:

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

Jess-sjåføren trenger ikke selgerEiendommer kartet vi leverte til Regeladministrator, men det er en del av grensesnittet, og andre leverandører kan kreve det.

Nå som vår leverandør av regelmotorer, Jess, har blitt initialisert og vårt regelsett er registrert, er vi nesten klare til å kjøre reglene våre.

Før vi kan kjøre dem, trenger vi en runtime-forekomst og en økt for å kjøre dem i. La oss også legge til en plassholder, CalcResults (), for hvor magien vil skje, og slipp økten:

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

6.3. API for kjøring

Nå som vi har alt på plass, la oss implementere det beregne resultater for å levere våre første data, utføre reglene våre i en statsløs økt, og trekke ut resultatene:

Listedata = ny ArrayList (); data.add (nytt spørsmål ("Kan jeg få en bonus?", -5)); Listeresultater = statelessRuleSession.executeRules (data);

Siden JSR 94 ble skrevet før JDK 5 kom sammen, bruker ikke API generikk, så la oss bare bruke en Iterator for å se resultatene:

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

Vi har brukt en statsløs økt i vårt eksempel, men vi kan også lage en StatefuleRuleSession hvis vi vil opprettholde tilstanden mellom påkallelser.

7. Konklusjon

I denne artikkelen lærte vi hvordan vi integrerer Jess-regelmotoren i applikasjonen vår ved å bruke Jess's native klasser, og med litt mer innsats ved å bruke JSR 94. Vi har sett hvordan forretningsregler kan deles inn i separate filer som blir behandlet av regelmotoren når applikasjonen vår kjører.

Hvis vi har regler for den samme forretningslogikken, skrevet for en annen JSR 94-kompatibel regelmotor, kan vi ganske enkelt legge til driveren for den alternative regelmotoren vår, og oppdatere drivernavnet vårt program skal bruke, og ingen ytterligere kodeendringer skal være nødvendig.

Det er flere detaljer på jess.sandia.gov for å legge inn Jess i et Java-program, og Oracle har en nyttig guide for å komme i gang med Java Rule Engine API (JSR 94).

Som vanlig er koden vi så på i denne artikkelen tilgjengelig på GitHub.