user1959070
user1959070

Reputation: 43

Can I get the line number of an error in an eval'ed Groovy script?

I have a piece of code that is to run a user-provided Groovy script, as follows:

def scriptText = <something user entered>
def manager = new ScriptEngineManager()
def engine = manager.getEngineByName('Groovy')

try {
    engine.eval(scriptText)
}
catch (ScriptException e) {
    println 'Script error at line ' + e.getLineNumber() + ', column ' + e.getColumnNumber() + ': ' + e.getMessage()
}

If I deliberately put an error in the input script that is supplied, this code will print the following (containing undefined line and column numbers):

Script error at line -1, column -1: javax.script.ScriptException: groovy.lang.MissingPropertyException: No such property: blah for class: Script1

I've also tried just calling Eval.me(scriptText), as the calling code is also Groovy, but then I get a large stack trace with the cause of the script error buried inside, as follows:

Exception in thread "main" groovy.lang.MissingPropertyException: No such property: blah for class: Script1
Possible solutions: class
    at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:50)
    at org.codehaus.groovy.runtime.callsite.PogoGetPropertySite.getProperty(PogoGetPropertySite.java:49)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callGroovyObjectGetProperty(AbstractCallSite.java:231)
    at Script1.run(Script1.groovy:6)  <---- this is the script error
    at groovy.lang.GroovyShell.evaluate(GroovyShell.java:518)
    at groovy.lang.GroovyShell.evaluate(GroovyShell.java:556)
    at groovy.lang.GroovyShell.evaluate(GroovyShell.java:527)
    at groovy.util.Eval.me(Eval.java:68)
    at groovy.util.Eval.me(Eval.java:51)
    at groovy.util.Eval$me.call(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:116)
    at com.kminnovations.metrics.engine.tester.Testing.parseFile(Testing.groovy:32)
    at com.kminnovations.metrics.engine.tester.Testing$parseFile.call(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:120)
    at com.kminnovations.metrics.engine.tester.MetricsEngineTester.main(MetricsEngineTester.groovy:23)

Is there a way to execute script text from within a program and get precisely the location of the error in the script, so that I can display it clearly for the user who provided it?

Upvotes: 3

Views: 3311

Answers (1)

denis.solonenko
denis.solonenko

Reputation: 11775

I think you can still get the line by navigating to the root cause and searching for source like ScriptN.groovy, because ScriptEngineManager will automatically generate this name for every executed script.

def manager = new ScriptEngineManager()
def engine = manager.getEngineByName('Groovy')

try {
    engine.eval(scriptText)
} catch (javax.script.ScriptException e) {
    def cause = rootCause(e)
    def line = cause.stackTrace.find { 
        it.fileName ==~ /^Script\d+\.groovy$/ 
    }.lineNumber
    println "Line $line: $cause.message"
}

def rootCause(Exception e) {
    Throwable t = e;
    while (t.cause != null) t = t.cause;
    t
}

You might want to have more complicated rootCause like in apache.commons.ExceptionUtils

Upvotes: 1

Related Questions