8

For background, I am experimenting with writing a DSL parser, using this great example. Unfortunately when I adapt this line for use in my own app:

Script dslScript = new GroovyShell().parse(dsl.text)

I get class resolve errors at runtime, as my DSL domain files have code that references other external classes. The context app has access to these classes, but I don't know how to give access to them to the new GroovyShell object, or alternatively somehow use the context app's runtime environment to parse the file.

Josh Diehl
  • 2,913
  • 2
  • 31
  • 43

2 Answers2

10

Have you tried using the following constructor: public GroovyShell(ClassLoader parent)

Like this: Script dslScript = new GroovyShell(this.class.classLoader).parse(dsl.text)

Hope that helps...

clmarquart
  • 4,721
  • 1
  • 27
  • 23
  • Yep. Just came to a similar solution since this in Grails: Script dslScript = new GroovyShell(grailsApplication.classLoader).parse(dsl.text) – Josh Diehl Jan 04 '12 at 18:12
  • Does this solve the issues with the PermSpace, where this leave the class information there and the you can run out of PermSpace eventually? See here: http://stackoverflow.com/questions/24169976/understanding-groovy-grails-classloader-leak https://issues.apache.org/jira/browse/GROOVY-2875 Thus I wonder if this can be recommended for server apps or not, – Axel Heider Apr 13 '16 at 22:54
  • It's been a while, but I imagine it is still just compiling a new class. If that's the case, the effect will be similar to `parseClass` – clmarquart Apr 19 '16 at 16:04
8

Here is a code snippet that shows how to inject a context object, configuration properties and the classpath.

Service parse(
String dslFile, List<String> classpath,
Map<String, Object> properties, ServiceContext context) {

// add POJO base class, and classpath
CompilerConfiguration cc = new CompilerConfiguration();
cc.setScriptBaseClass( BaseDslScript.class.getName() );
cc.setClasspathList(classpath);

// inject default imports
ic = new ImportCustomizer();
ic.addImports( ServiceUtils.class.getName() );
cc.addCompilationCustomizers(ic);

// inject context and properties
Binding binding = new Binding();
binding.setVariable("context", context);
for (prop: properties.entrySet()) {
  binding.setVariable( prop.getKey(), prop.getValue());
}

// parse the recipe text file
ClassLoader classloader = this.class.getClassLoader();
GroovyShell gs = new GroovyShell(classloader, binding, cc);
FileReader reader = new FileReader(dslFile);
try {
  return (Service) gs.evaluate(reader);
} finally {
  reader.close();
}

Notice that this code also injects the base class in order to gain fine grained control on property parsing and support for inheritance between different DSL files. For more information and working source code from the Cloudify project see http://cloudifysource.tumblr.com/post/23046765169/parsing-complex-dsls-using-groovy

itaifrenkel
  • 1,578
  • 1
  • 12
  • 26