Showing posts with label dsl. Show all posts
Showing posts with label dsl. Show all posts

Wednesday, June 22, 2011

groovy - parsing reading builder content from an external file

we all know that groovy builders are great and fun and can make your life so much easier.

For example if you want to create a simple xml file in your groovy script you can just do the following


def writer = new StringWriter()
def xml = new MarkupBuilder(writer)
xml.records() {
car(name:'HSV Maloo', make:'Holden', year:2006) {
country('Australia')
record(type:'speed', 'Production Pickup Truck with speed of 271kph')
}
car(name:'P50', make:'Peel', year:1962) {
country('Isle of Man')
record(type:'size', 'Smallest Street-Legal Car at 99cm wide and 59 kg in weight')
}
car(name:'Royale', make:'Bugatti', year:1931) {
country('France')
record(type:'price', 'Most Valuable Car at $15 million')
}
}



looks easy and is easy. But now you come to the point where you think. Mhm it would be nice, if I could just read the builder code from an external file and generate the xml on the fly using this. For example you have to generate an xml configuration file and rather simplify this process using groovy and this has to be done more than once...

After a couple of hours googleing the best solution I came up with, was to provide your own binding to the builder, which simplified things quite a bit.



class XmlBinding extends Binding{
def builder


Object getVariable(String name) {
return { Object... args -> builder.invokeMethod(name,args) }
}
}


let's assume we like to read the following builder code, defined in a file named 'test.groovy'



config{
bin{
allow(minimumClassSize:1)
}
}



which all you need to do, now you could for example have a main method somewhere containing this:


def groovyScriptContent = new File("test.groovy").text
def writer = new StringWriter()
def xml = new MarkupBuilder(writer)
def binding = new XmlBinding()
binding.builder = xml

def dslScript = new GroovyShell().parse(groovyScriptContent)
dslScript.binding = binding
dslScript.run()

println writer.toString()


and as a result it generates the following xml document


<config>
<bin>
<allow minimumClassSize='1' />
</bin>
</config>

Thursday, September 10, 2009

groovy 1.5.6 and delegates

while trying to write a DSL I discovered an annoying bug in groovy 1.5.6, I guess. Basically if I define a delegate for a closure it has to be in the same file as the closure. If i define it in another file I always get a nullpointer exception...


example:



class MyDelegate {
def test() {
println "tada"
}
}

static ExpandoMetaClass createEMC(Class clazz, Closure cl) {
ExpandoMetaClass emc = new ExpandoMetaClass(clazz, false)

cl(emc)

emc.initialize()
return emc
}

Script dslScript = new GroovyShell().parse("test{\ntest()\n}")

dslScript.metaClass = createEMC(dslScript.class, {
ExpandoMetaClass emc ->

emc.test = {

Closure closure ->
closure.delegate = new MyDelegate()
closure.resolveStrategy = Closure.DELEGATE_FIRST

try {
closure()
}
catch (Exception e) {
e.printStackTrace()
}
}
})
dslScript.run()



this works fine. Now if I move the class 'MyDelegate' to it's own file I end up with the following exception.


java.lang.NullPointerException
at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.getDelegateMethod(ClosureMetaClass.java:207)
at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:280)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodOnCurrentN(ScriptBytecodeAdapter.java:78)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodOnCurrent0(ScriptBytecodeAdapter.java:112)
at Script1$_run_closure1.doCall(Script1.groovy:2)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:230)
at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:248)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodOnCurrentN(ScriptBytecodeAdapter.java:78)
at Script1$_run_closure1.doCall(Script1.groovy)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:230)
at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:248)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:756)
at org.codehaus.groovy.runtime.InvokerHelper.invokePogoMethod(InvokerHelper.java:778)
at org.codehaus.groovy.runtime.InvokerHelper.invokeMethod(InvokerHelper.java:758)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodN(ScriptBytecodeAdapter.java:170)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeClosure(ScriptBytecodeAdapter.java:605)
at edu.ucdavis.genomics.metabolomics.binbase.quantificator.dsl.Tester$_run_closure1_closure2.doCall(Tester.groovy:38)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:230)
at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:248)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:756)
at groovy.lang.Closure.call(Closure.java:292)
at org.codehaus.groovy.runtime.metaclass.ClosureMetaMethod.invoke(ClosureMetaMethod.java:72)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:230)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:912)
at groovy.lang.ExpandoMetaClass.invokeMethod(ExpandoMetaClass.java:946)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodOnCurrentN(ScriptBytecodeAdapter.java:78)
at Script1.run(Script1.groovy:1)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:230)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:912)
at groovy.lang.ExpandoMetaClass.invokeMethod(ExpandoMetaClass.java:946)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:756)
at org.codehaus.groovy.runtime.InvokerHelper.invokePogoMethod(InvokerHelper.java:778)
at org.codehaus.groovy.runtime.InvokerHelper.invokeMethod(InvokerHelper.java:758)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodN(ScriptBytecodeAdapter.java:170)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethod0(ScriptBytecodeAdapter.java:198)
at edu.ucdavis.genomics.metabolomics.binbase.quantificator.dsl.Tester.run(Tester.groovy:45)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:230)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:912)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:756)
at org.codehaus.groovy.runtime.InvokerHelper.invokePogoMethod(InvokerHelper.java:778)
at org.codehaus.groovy.runtime.InvokerHelper.invokeMethod(InvokerHelper.java:758)
at org.codehaus.groovy.runtime.InvokerHelper.runScript(InvokerHelper.java:401)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:230)
at groovy.lang.MetaClassImpl.invokeStaticMethod(MetaClassImpl.java:1105)
at org.codehaus.groovy.runtime.InvokerHelper.invokeMethod(InvokerHelper.java:749)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodN(ScriptBytecodeAdapter.java:170)
at edu.ucdavis.genomics.metabolomics.binbase.quantificator.dsl.Tester.main(Tester.groovy)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:230)
at groovy.lang.MetaClassImpl.invokeStaticMethod(MetaClassImpl.java:1105)
at org.codehaus.groovy.runtime.InvokerHelper.invokeMethod(InvokerHelper.java:749)
at groovy.lang.GroovyShell.runMainOrTestOrRunnable(GroovyShell.java:244)
at groovy.lang.GroovyShell.run(GroovyShell.java:218)
at groovy.lang.GroovyShell.run(GroovyShell.java:147)
at groovy.ui.GroovyMain.processOnce(GroovyMain.java:493)
at groovy.ui.GroovyMain.run(GroovyMain.java:308)
at groovy.ui.GroovyMain.process(GroovyMain.java:294)
at groovy.ui.GroovyMain.processArgs(GroovyMain.java:111)
at groovy.ui.GroovyMain.main(GroovyMain.java:92)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.codehaus.groovy.tools.GroovyStarter.rootLoader(GroovyStarter.java:101)
at org.codehaus.groovy.tools.GroovyStarter.main(GroovyStarter.java:130)


time to give groovy 1.6.4 a try and maybe migrate my project over to it.

Wednesday, September 9, 2009

DSL - love or hate?

recently I started to work more with DSL's (domain specific language) since I wanted to go away from doing everything in XML.

The approach of XML is great, but I'm not a big fan of the overhead with it.

So the first approach was to create a DSL for the BinBase system, precisely to provide the user with a really simple way of calculating an experiment.

It should look something like this:


/**
* our quantification dsl
*/
quantify {

//define the name of the setup
name "quantification for ${new Date()}"

//define the samples for the calculations
sample "sample 1"
sample "sample 2"
sample "sample 3"
sample "sample 4"
sample "sample 5"
sample "sample 6"
sample "sample 7"
sample "sample 8"
sample "sample 9"
sample "sample 10"

//defines the bins in the table
bin 321, 22.0
bin 322
bin 323
bin 324
bin 325, 22.0

//report definition
report {

sizeDown 50

format "xls"

replace true
}
}


And the result should calculate the data, send them to the cluster and generates a quantification report. If this all works out the way we want it. We will than adapt it to schedule BinBase experiment's this way.

My current fazit is that I love the idea of DSL's, but hate the implementations of it.