About
fv_2007
Agile innovative developer with deep insight into lots of platforms, technologies and protocols. Absolute “early adopter” in Web 2.0 technologies and more. Large professional network and eagerly talking about architecture, strategy, design patterns, restful ressources, object-oriented thinking and modeling languages such as PML. Special interest in programminglanguages constructs, knowledge on languages like Smalltalk, Erlang, Java, Clojure, Scala, Ruby... read more
Comments
Language

JSR223 & DSL January 16, 2009 01:28 over 3 years ago

Jeg har to problemer! Jeg vil kunne tilføre nye regler og undtagelser dynamisk i en process. Min kode, eller notation skal være sprogneutral og den skal uden videre kunne læses af forretningskyndige.

Dette er et par ydmyg krav og opgaven er at overføre policer fra et system til et andet. Konverteringsprocessen løber over flere dage og undervejs vil der komme undtagelser på enkelte policer og nye forretningsregler vil opstå. Det er ikke en option at stoppe, restarte eller deploye applikationen igen for at realisere evt nye regler.

Jeg har valgt at isolere en process omkring en pensions-regel på laveste niveau. En pension kan bestå af dusinvis af disse niveauer og en samling af dem udgør en fuld pension.

Jeg tager udspring en den eksisterende kode. Det kan være Java, .NET eller andre sprog som er konstrueret i samme programmering model.

DummyPolice police = new DummyPolice();
PoliceLevel level = new PoliceLevel();
level.setPolice(“150364xxxx”);
level.setGrundlag(“omregning”);
level.setProcent(3);
level.setGrundform(212);
level.setYdelse(123.340,25);
police.add(level);

Udviklere fra den dynamiske eller funktionelle verden er ikke imponeret. En af de første ting man kunne fornemme er en høj grad af syntaktisk støj. I denne situation findes to slags støj, unødvendige forstyrrende elementer, som parenteser, semikolon og lodret skrivemaskineapostrof/tegnsætningsapostrof, og en del repeterende kode som fx “set”.

level.police “150364xxxx”
level.grundlag “omregning”
level.procent 3 
level.grundform 212 
level.ydelse 123.340,25

Hvordan skeler man mellem “set” kontra “get” metode signaturer. En “set” metode modtager input hvorimod en get metode afgiver output! Hvordan bestemmes hvilken type som bliver overført i beskeden mellem objekterne? Når man arbejder med dynamiske sprog er en af de første ting man bemærker og sætter pris på, den mindre betydning som typer udgør. Med mindre systemet fejler er man faktisk ligeglad. Fx police identifikator er en integer, biginteger eller en streng når man som her indføre “xxx” som erstatning for de 4 sidste tal. Hvis man vil have en numerisk værdi skulle “xxxx” udskiftes med “0000”.

Denne kode er en Intern DSL hovedsagligt opbygget gennem “DSL Method Chaining” med en tydelig kontekst(DSL Context). Konteksten er en police på laveste niveau. Det betyder at når linjen “procent 3” eksekveres er det af en regel på et police niveau.

police 150364xxxx
grundlag omregning
procent 3
grundform 212
ydelse 123.340,25

Ved denne transformation har jeg nu noget business like kode som forretningens domain eksperter kan fortolke. Det er sprog neutralt og fungere som en pensions DSL input. Nu mangler jeg blot at skrive selve DSL fortolker koden, men det vil jeg ikke komme ind på i denne post.

De yder omstændigheder gør at dette skal eksekveres på en JVM og med Java SE kan det ikke lade sig gøre. Med Java’s Scripting API JSR223 kan det lade sig gøre.

JSR223

Scripting API’et gør det muligt at eksekvere mange former script-sprog på Java platformen. Jeg gider ikke fortælle om de små detaljer da frameworket er ganske lille og super enkelt. Java SE 6 indeholder en javascript-engine baseret på Rhino og det betyder at man kan fortolke dynamisk javascript i Java koden uden at addere ekstra jars.

Hvorfor vil man eksekvere script kode internt i Java kode?

  • Java mangler en dynamiske tilgang
  • Man kan skabe nye variabler uden at definere en type, man kan skifte type undervejs, typekonvertering sker i mange tilfælde helt automatisk.
  • Man kan edittere kode og eksekvere, uden brug af rekompilering.
  • Man kan eksekvere konfiguration scripts, forretningslogik/regler og matematiske udtryk for finansielle applikationer fra en ekstern kontekst.
  • Man kan invokere “shell” scriptsekvenser i kørende applikationer.

Hvis man vil have fortolket andre script-sprog, fx ruby i Java applikationen kræver det at man lægger en jar fil med ruby-script engine i classpath. se nederst på siden for andre muligheder.

Når man skal fortolke script sprog undervejs er hastigheden ikke fantastisk! Derfor er der indbygget mulighed for at kompilere et script til bytecode og optimere hotspots.

Alle binære script-engiens er bygget med Java SE 6 hvilket gør bagudkompatibilitet irriterende besværligt. Med Java 1.4 er det nærmest umuligt idet der i implementationen er brugt varargs fra Java 5. I Java7 er flere dynamiske API’er på vej fx JRuby, Jython og Beanshell. Det betyder at det er muligt at skrive fx ruby kode i Java kode. Faktisk mangler man så kun at kunne at kunne markere blokke med ruby koden med tags direkte i Java koden. På samme måde som Pascal kunne håndtere assembler i sin tid.

Tilbage til eksemplet. Jeg vil udnytte groovy’s smarte og lette notation til denne opgave i en kørende Java applikation så lad os antage at jar filer og fortolker er tilstede i projekt konfigurationen. I teorien kunne det være hvilken af de scriptsprog som understøttes.

Nu vil det være muligt at skrive denne kode:

public void run_groovy_code(Sting code) throws Exception { 
	ScriptEngineManager factory = new ScriptEngineManager(); 
	ScriptEngine engine = factory.getEngineByName("groovy"); 
	Object ret = engine.eval(code); 
	assertEquals('this is groovy code', ret); 
} 
run_groovy_code("return 'this is groovy code'")

Nu er det altså muligt at overføre kode som eksekveres “on the fly”.

Lad os nu antage at Java processen har kørt i flere timer og der kommer forskellige fejl. De bliver listet i en kø og kan browses med en gui. Man kan hente hver enkelt ind og se beskeden og fejl beskeden. Der er også mulighed for at indtaste en regel som kan løse netop denne situation. Det modtagende system kan ikke modtage 212 for ægtefælder, den skal konverteres til 216.

rule if grundlag == omregning && grundform == 212 then grundform 216

Nu findes den nye regel i systemet og policen kan reeksekveres igen. Hvis køen er smart konstrueret opdager den selv at en ændring er sket og policen submittes ind i systemet mens den nye regel bytter 212 ud til 216 som det modtagne system kan håndtere.

Hvis man betragter applikationen som ren scaffolding for forretningsprocesser og forretningsregler kan man invokere et meget tættere samarbejde med de forretningskyldige. Ligeledes kan man separerer/abstrahere forretningsspecifikke regler fra applikationer og dermed også de lange round-trips som vi lider under. En af de store nøgler er at isolere enkelte processer til en niveau hvor det er muligt at skrive en DSL og skabe en mere deklarativ måde at anskue problemerne på-. Processen kan ovenikøbet være interaktiv og tillade at tilføre regler undervejs, måske af forretningen selv. Skønt!

JSR223 gør det muligt!

Her er en kort liste med et par af de script-sprog som kan eksekveres i Java med JavaScripting API.

  • BeanShell – Small, free, embeddable Java interpreter
  • FreeMarker – Java-based general purpose template engine
  • Groovy – Groovy is an agile dynamic language for the Java
  • Jaskell – Lazy functional programming language
  • JavaScript Rhino 1.6R7 based JavaScript script engine.
  • JEP – Parsing and evaluating mathematical expressions
  • Jexl – Java Expression Language, expression language engine
  • JudoScript – Scripting language built on top Java
  • JUEL – Java Unified Expression Language
  • OGNL – Object-Graph Navigation Language
  • Ruby – Best language ever
  • Scheme – multi-paradigm programming language, one of two main dialects of Lisp
  • Tcl – Tool Command Language
  • XPath – Finding information in an XML document
  • XSLT – language for transforming XML
  • JavaFX Script – Declarative scripting language
  • AppleScript – The Language of Automation
  • Bex script
  • OCaml – fast, concise and powerful language
  • PHP
  • PHP
  • Python
  • Smalltalk – Dynamically typed, reflective programming language
  • CajuScript – Powerfull script to use with Java.


By Frank Vilhelmsen - 3 tags: dsl java groovy - Add comment