of the Grails framework • Creator of the Gaelyk and Caelyf toolkits • Co-author of Groovy in Action • Follow me on... • My blog: http://glaforge.appspot.com • Twitter: @glaforge • Google+: http://gplus.to/glaforge 3
programming language or executable specification language that offers, through appropriate notations and abstractions, expressive power focused on, and usually restricted to, a particular problem domain. • In contrast to General Purprose Languages • Also known as: fluent / human interfaces, language oriented programming, little or mini languages, macros, business natural languages... 5 { }
<xsl:element name="{name()}"> <xsl:for-each select="@*"> <xsl:element name="{name()}"> <xsl:value-of select="."/> </xsl:element> </xsl:for-each> <xsl:apply-templates select="*|text()"/> </xsl:element> </xsl:template> </xsl:stylesheet> <?xml version="1.0"?> <GTK-Interface> <widget> <class>GtkWindow</class> <name>HelloWindow</name> <border_width>5</border_width> <Signal> <name>destroy</name> <handler>gtk_main_quit</handler> </Signal> <title>Hello</title> <type>GTK_WINDOW_TOPLEVEL</type> <position>GTK_WIN_POS_NONE</position> <allow_shrink>True</allow_shrink> <allow_grow>True</allow_grow> <auto_shrink>False</auto_shrink> <widget> <class>GtkButton</class> <name>Hello World</name> <can_focus>True</can_focus> <label>Hello World</label> </widget> </widget> </GTK-Interface> # Poll this site first each cycle. poll pop.provider.net proto pop3 user "jsmith" with pass "secret1" is "smith" here user jones with pass "secret2" is "jjones" here with options keep # Poll this site second, unless Lord Voldemort zaps us first. poll billywig.hogwarts.com with proto imap: user harry_potter with pass "floo" is harry_potter here # Poll this site third in the cycle. # Password will be fetched from ~/.netrc poll mailhost.net with proto imap: user esr is esr here Glade XSLT fetchmail Regex "x.z?z{1,3}y" SQL SELECT * FROM TABLE WHERE NAME LIKE '%SMI' ORDER BY NAME
a general-purpose one • Share a common metaphor of understanding between developers and subject matter experts • Have domain experts help with the design of the business logic of an application • Avoid cluttering business code with too much boilerplate technical code thanks to a clean separation • Let business rules have their own lifecycle 8
modify, and often develop DSL programs • Somewhat self-documenting • Enhance quality, productivity, reliability, maintainability, portability, reusability • Safety; as long as the language constructs are safe, any DSL sentence can be considered safe Cons • Learning cost vs. limited applicability • Cost of designing, implementing & maintaining DSLs as well as tools/IDEs • Attaining proper scope • Trade-offs between domain specificity and general purpose language constructs • Efficiency cost • Proliferation of similar non-standard DSLs 9
native syntax constructs (list, map, ranges), closures, less punctuation... • Compile-time and runtime meta-programming • metaclasses, AST transformations • also operator overloading • The ability to easily integrate into Java / Spring apps • also security and safety 10
• GroovyScriptEngine, Eval, GroovyClassLoader, GroovyShell • Java 6: javax.script.* / JSR-223 • Groovy provides a JSR-223 implementation • Spring’s lang namespace • Groovy provides the highest level of flexibility and customization, but JSR-223 is a standard... 22
• GroovyScriptEngine, Eval, GroovyClassLoader, GroovyShell • Java 6: javax.script.* / JSR-223 • Groovy provides a JSR-223 implementation • Spring’s lang namespace • Groovy provides the highest level of flexibility and customization, but JSR-223 is a standard... 22
/ out of scripts through the Binding • it’s basically just like a map of variable name keys and their associated values 26 def)binding!=)new!Binding([ !!!!robot:!new!Robot() ]) def)shell!=)new!GroovyShell(binding) shell.evaluate( !!!!new!File("command.groovy") )
/ out of scripts through the Binding • it’s basically just like a map of variable name keys and their associated values 26 def)binding!=)new!Binding([ !!!!robot:!new!Robot() ]) def)shell!=)new!GroovyShell(binding) shell.evaluate( !!!!new!File("command.groovy") ) integration.groovy
def)binding!=)new!Binding([ !!!!robot:!new!Robot(), !!!!left:!!!!!Direction.left, !!!!right:!!!!Direction.right, !!!!backward:!Direction.backward, !!!!forward:!!Direction.forward ]) def)shell!=)new!GroovyShell(binding) shell.evaluate( !!!!new!File("command.groovy") ) Fragile in case of new directions!
Groovy compilation process • Three available customizers • ImportCustomizer: add transparent imports • ASTTransformationCustomizer: injects an AST transform • SecureASTCustomizer: restrict the groovy language to an allowed subset • But you can implement your own 31
! new!GroovyShell(new!Binding([robot:!new)Robot()]),!!!!!!! ! ! !!!!!!!!!!!!!!!!configuration) !!!!.evaluate("robot.move!left"!+!"\n" !!!!!!!!!!!!!!"log.info!‘Robot!moved’")!!!!!!!!!!! @Log injects a logger in scripts and classes
import customizer to import java.lang.Math.* • prepare a secure AST customizer 36 def%imports%=%new%ImportCustomizer().addStaticStars('java.lang.Math') def%secure%=%new%SecureASTCustomizer() Idea: Secure the rocket’s onboard trajectory calculation system by allowing only math expressions to be evaluated by the calculator
! //!empty!white!list!=>!forbid!imports importsWhitelist!=![]! staticImportsWhitelist!=![] //!only!allow!the!java.lang.Math.*!static!import staticStarImportsWhitelist!=!['java.lang.Math'] ... Disallow closures and methods Black / white list imports
] % //%types%allowed%to%be%used%(including%primitive%types) constantTypesClassesWhiteList%=%[ Integer,%Float,%Long,%Double,%BigDecimal,% Integer.TYPE,%Long.TYPE,%Float.TYPE,%Double.TYPE ] % //%classes%who%are%allowed%to%be%receivers%of%method%calls receiversClassesWhiteList%=%[% Math,%Integer,%Float,%Double,%Long,%BigDecimal%] } ... You can build a subset of the Groovy syntax!
] % //%types%allowed%to%be%used%(including%primitive%types) constantTypesClassesWhiteList%=%[ Integer,%Float,%Long,%Double,%BigDecimal,% Integer.TYPE,%Long.TYPE,%Float.TYPE,%Double.TYPE ] % //%classes%who%are%allowed%to%be%receivers%of%method%calls receiversClassesWhiteList%=%[% Math,%Integer,%Float,%Double,%Long,%BigDecimal%] } ... You can build a subset of the Groovy syntax! Black / white list usage of classes
• But the following would have failed: 39 shell.evaluate%'System.exit(0)' def%config%=%new%CompilerConfiguration() config.addCompilationCustomizers(imports,%secure) def%shell%=%new%GroovyShell(config) % shell.evaluate%'cos%PI/3'
move() method on the robot instance, we should be able to call the move() method directly from within the script • Two approaches: 42 • Inject a ‘move’ closure in the binding with a method pointer • Use a base script class with a ‘move’ method delegating to the robot
a property access after the number, but we now need to divide (‘div’) by the time 54 2.km/h The div() method on Distance An h duration instance in the binding
you to drop dots & parens when chaining method calls • an extended version of top-level statements like println • Less dots, less parens allow you to • write more readable business rules • in almost plain English sentences • (or any language, of course) 63
what DSL users are allowed to do with your DSL • Forbid things which are not allowed • leverage the JVM’s Security Managers • this might have an impact on performance • use a Secure AST compilation customizer • not so easy to think about all possible cases • avoid long running scripts with *Interrupt transformations 74
the JVM, so you have access to the usual Security Managers mechanism • Nothing Groovy specific here • Please check the documentation on Security Managers and how to design policy files 75
• what if the code runs in infinite loops or for too long? • what if the code consumes too many resources? • 3 new transforms at your rescue • @ThreadInterrupt: adds Thread#isInterrupted checks so your executing thread stops when interrupted • @TimedInterrupt: adds checks in method and closure bodies to verify it’s run longer than expected • @ConditionalInterrupt: adds checks with your own conditional logic to break out from the user code 77
the start of method and closure bodies • check for available resources, number of times run, etc. • Leverages closure annotation parameters from Groovy 1.8 80 @ConditionalInterrupt({$battery.level$<$O.1$}) import%groovy.transform.ConditionalInterrupt 100.times%{%%%% %%%%move%forward%at%10.km/h }
usage of the interrupts were explicit, and users had to type them • if they want to deplete the battery of your robot, they won’t use interrupts, so you have to impose interrupts yourself • With compilation customizers you can inject those interrupts thanks to the ASTTransformationCustomizer 81
vs classes, optional typing, colons and parens • Groovy offers useful dynamic features for DSLs • operator overloading, ExpandoMetaClass • Can write almost plain natural language sentences • for readable, concise and expressive DSLs • Groovy DSLs are easy to integrate, and can be secured to run safely in your own sandbox 83
vs classes, optional typing, colons and parens • Groovy offers useful dynamic features for DSLs • operator overloading, ExpandoMetaClass • Can write almost plain natural language sentences • for readable, concise and expressive DSLs • Groovy DSLs are easy to integrate, and can be secured to run safely in your own sandbox 83 Groovy is a great fit for DSLs!
to implement your own control structures with the help of closures • How to create Groovy « builders » • How to hijack the Groovy syntax to develop our own language extensions with AST Transformations • Source preprocessing for custom syntax • How to use the other dynamic metaprogramming techniques available • How to improve error reporting with customizers • IDE support with DSL descriptors (GDSL and DSLD) 84