Figured out how to run a ruby script from Java today using trunk of the jruby. I am considering using Mule for some stuff at work, but would like to use Ruby code as the components that run in the container. Mule supports scriptable components, so the first step was to ensure I could run somewhat complex Ruby code from Java. I wanted to use JSR-223 so that the code would work for any scripting language, not just Ruby. I had enough problems doing it that I thought I'd share how here.

My first requirement for this whole integration was the ability to use trunk of jruby. The performance improvements they have been making on trunk have made jruby a viable production tool. Unfortunately, the current build of the jruby scripting engine implementation is done against a pre 1.0 jruby version (I think). So the first step was to get the jruby scripting engine working against jruby trunk. I did this by checking out the sources from cvs (see instructions here) and patching them using the following diff:

diff -r1.12 JRubyScriptEngine.java
199c199
<             return runtime.parse(getRubyScript(script), filename, null, 0);
---
>             return runtime.parseEval(getRubyScript(script), filename, null, 0);
218c218
<                 return runtime.parse(script, filename, null, 0);
---
>                 return runtime.parseEval(script, filename, null, 0);
220,221c220,222
<             Reader rubyReader = getRubyReader(filename);
<             return runtime.parse(rubyReader, filename, null, 0);
---
>             //Reader rubyReader = getRubyReader(filename);
>             InputStream rubyInstr = getRubyInputStream(filename);
>             return runtime.parseFile(rubyInstr, filename, null);
255a257,261
>     private InputStream getRubyInputStream(String filename) throws FileNotFoundException {
>         File file = new File(filename);
>         return new FileInputStream(file);
>     }
> 

Then build the jruby-scripting.jar as normal. I used the following Java code as my test bed:

Test.java

import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.io.InputStreamReader;
import java.io.FileInputStream;

public class Test
{
  public static void main(String[] args) {
    FileInputStream fileStr = null;
    try {
      String scriptName = args[0];
      ScriptEngineManager m = new ScriptEngineManager();
      ScriptEngine rubyEngine = m.getEngineByName("jruby");
      ScriptContext context = rubyEngine.getContext();

      fileStr = new FileInputStream(scriptName);
      InputStreamReader reader = new InputStreamReader(fileStr);
      rubyEngine.eval(reader);

    } catch (Throwable t) {
      System.err.println("Error");
      t.printStackTrace(System.err);
    } finally {
      try {
        if (fileStr != null) {
          fileStr.close();
        }
      } catch (Throwable t) {
        // ignore intentionally
      }
    }
  }
}

Then, to run, write a ruby script like this one, for example:

foo.rb

puts "Hello World!"
h = {:foo => 'bar', :baz => 'bip'}
require 'yaml'
puts h.inspect
puts h.to_yaml

and use a java invocation like this:

java -cp .:scripts:bsf.jar:jruby.jar:jruby-engine.jar -Djruby.home=/home/doug/tools/jruby Test foo.rb
Hello World!
{:foo=>"bar", :baz=>"bip"}
--- 
:baz: bip
:foo: bar

where bsf.jar and jruby.jar are right out of the jruby binary 1.1b release and jruby-engine.jar is the scripting engine implementation you built above.

The only sticking point getting this to work was the fact that you must define the jruby.home environment variable when running the code or else the jruby runtime won't be able to find system libraries like 'date' or 'yaml'. If you don't, you will get an exception:

Error
javax.script.ScriptException: org.jruby.exceptions.RaiseException: library `yaml_internal' could not be loaded:
   org.jruby.exceptions.RaiseException: no such file to load -- date
        at com.sun.script.jruby.JRubyScriptEngine.evalNode(JRubyScriptEngine.java:398)
        at com.sun.script.jruby.JRubyScriptEngine.eval(JRubyScriptEngine.java:146)
        at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:232)
        at Test.main(Test.java:20)
Caused by: org.jruby.exceptions.RaiseException: library `yaml_internal' could not be loaded:
org.jruby.exceptions.RaiseException: no such file to load -- date