Java style stacktrace for JRuby code called from Java

madlep

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.


Leave a Reply