Java style stacktrace for JRuby code called from Java
As part of a project to write some nice Ruby APIs for creating Swing GUIs, I’ve been a little frustrated with the exceptions from JRuby in the Java code that calls it - particularly getting stack traces, and error messages.
Say you’ve got some code like this:
try {
new ScriptEngineManager().
getEngineByName("jruby").
eval("raise 'foo is too bar'");
} catch (Exception e) {
e.printStackTrace();
}
You’ll get output that looks something like:
javax.script.ScriptException: org.jruby.exceptions.RaiseException
at com.sun.script.jruby.JRubyScriptEngine.evalNode(JRubyScriptEngine.java:376)
at com.sun.script.jruby.JRubyScriptEngine.eval(JRubyScriptEngine.java:138)
at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:245)
at Borked.main(Borked.java:9)
Caused by: org.jruby.exceptions.RaiseException
Not very helpful. How are you supposed to know what went wrong where?
After a bit of poking round with the debugger, it turns out there is more information in the exception you get back. You just have to work to get it. The following code will grab the Ruby exception message and stacktrace and spit it back out in the same style as native Java exceptions are output.
try {
new ScriptEngineManager().
getEngineByName("jruby").
eval("raise 'foo is too bar'");
} catch (Exception e) {
RaiseException re = (RaiseException)e.getCause();
System.out.println(re.getException().getMetaClass() + ":" + re.getException().toString());
RubyArray rubyStackTrace = (RubyArray) re.getException().backtrace();
for(Object stackLine : rubyStackTrace) {
System.out.println("\tat " + stackLine);
}
}
Here’s the output from some code I’ve been working on
ArgumentError:unknown key:foo
at /development/wring/classes/wrung_test.rb:17:in `each'
at /development/wring/classes/wrung.rb:43:in `populate_defaults'
at /development/wring/classes/wrung_test.rb:17:in `add_comp'
at /development/wring/classes/wrung_test.rb:17
at :0:in `require'
at :0
Which is pretty much exactly what a Java stack trace would look like - except its for the Ruby code running inside JRuby called by Java.