Java Integration
From JRubyWiki
Contents |
Using the JRuby Interpreter from Java
Java 6 (using JSR 223: Scripting)
Java integration with Java 6 will be using the standard scripting API (JSR223). A JRuby scripting engine already exists and is located at https://scripting.dev.java.net/ Download and unzip the collection of jars from the documents and files section of the site (jsr223-engines.tar.gz or jsr223-engines.zip). Look in the uncompressed files for the jruby/build/jruby-engine.jar file. Add this file to your classpath and then use the code below to access the engine. If you're using JRuby 1.1RC3 and 1.1.x on Java 6, use version 1.1.2 or later of the JRuby engine.
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
{...}
ScriptEngineManager m = new ScriptEngineManager();
ScriptEngine rubyEngine = m.getEngineByName("jruby");
ScriptContext context = rubyEngine.getContext();
context.setAttribute("label", new Integer(4), ScriptContext.ENGINE_SCOPE);
try{
rubyEngine.eval("puts 2 + $label", context);
} catch (ScriptException e) {
e.printStackTrace();
}
ScriptEngine.eval also takes a java.io.Reader object, which allows you to get load scripts from Files or other resource streams very simply, through the same interface. The context parameter is optional.
If you want to use scripting API on Java 5, use version 1.1.3 or later of the JRuby engine. Plus download sjp-1_0-fr-ri.zip from http://www.jcp.org/en/jsr/detail?id=223, then, unzip it and add script-api.jar to your classpath. Java 5 users might be better to use com.sun.script.jruby.JRubyScriptEngineManager instead of javax.script.ScriptEngineManager to avoid version mismatch error. However, if you are sure that you don't have any other script engines' archives compiled on JDK 1.6, you can use javax.script.ScriptEngingManager to get engine's instance.
Otherwise, you can use the Apache Bean Scripting Framework if you don't have the luxury of using Java 6.
When running the compiled code, be sure to use a java invocation similar to the following:
java -cp .:scripts:bsf.jar:jruby.jar:jruby-engine.jar -Djruby.home=/path/to/jruby/home my.class.ScriptRunner
The -Djruby.home part is necessary or the system ruby libraries won't be found.
See Walkthroughs and Tutorials: JSR 223 scripting for more information.
Embedding with Bean Scripting Framework (BSF)
The Bean Scripting Framework, when used with JRuby, will allow you to conveniently to pass your own Java objects to your JRuby script. You can then use these objects in JRuby, and changes will affect your Java program directly. To run a JRuby script using BSF, you must first copy the BSF.jar file into your <JAVA_HOME>/lib/ext/ folder. Then, try the following:
import org.jruby.Ruby.*;
import org.jruby.*;
import org.jruby.javasupport.bsf.*;
import org.apache.bsf.BSFException;
import org.apache.bsf.BSFManager;
{...}
JLabel mylabel = new JLabel();
BSFManager.registerScriptingEngine("ruby",
"org.jruby.javasupport.bsf.JRubyEngine",
new String[] { "rb" });
BSFManager manager = new BSFManager();
/* Import an object using declareBean then you can access it in JRuby with $<name> */
manager.declareBean("label", mylabel, JFrame.class);
manager.exec("ruby", "(java)", 1, 1, "$label.setText(\"This is a test.\")");
Directly calling JRuby APIs
See Direct JRuby Embedding. The BSF and javax.scripting APIs are strongly recommended, as they are most likely to always do the "right thing", which may change over time in the direct version.
Gotchas
If you plan on calling gems from an embedded script, there are a couple of things you need to be aware of:
If you require 'rubygems', you need to make sure you set a few system properties: jruby.base, jruby.home, jruby.lib, jruby.shell, and jruby.script. You can look in bin/jruby (it's a shell script) or jruby.bat to examples of setting these from the command line. If, for some reason, you can't set them on the command line, you'll need to set them programmatically, or else you'll receive a NullPointerException when RbConfigLibrary loads.
Also, make sure you get the load path set properly. Running jirb and calling $LOAD_PATH.inspect should give you a good idea what paths need to be included. All of those paths can be set the same way you'd set a Java classpath. However, one reference to *lib/ruby/1.8* has to remain relative. This is because some files (*digest/sha2*, for example) are loaded from the jruby.jar. If you are running unit tests from Ant, you may have problems because Ant tends to expand pathelements. Fortunately, it's easy enough to append lib/ruby/1.8 to the load path before calling require 'rubygems' in your scripts.
Here's an example load path from Linux:
irb(main):004:0> puts $LOAD_PATH /home/username/jruby/lib/ruby/site_ruby/1.8 /home/username/jruby/lib/ruby/site_ruby /home/username/jruby/lib/ruby/1.8 /home/username/jruby/lib/ruby/1.8/java lib/ruby/1.8 . => nil
Here's an example load path for Windows:
- C:/common/jruby-0.9.2/lib/ruby/site_ruby/1.8
- C:/common/jruby-0.9.2/lib/ruby/site_ruby/1.8/java
- C:/common/jruby-0.9.2/lib/ruby/site_ruby
- C:/common/jruby-0.9.2/lib/ruby/1.8
- C:/common/jruby-0.9.2/lib/ruby/1.8/java
- lib/ruby/1.8
These settings are specific to my system. Make sure the paths are correct for your system.
If you declare a bean using BSF, make sure you undeclare it when you are done using it even if you declare another bean using the same name. BSF internally adds declared beans to a vector, and only removes them once they are undeclared. Or, as an alternative, you can call registerBean and access the object from JRuby using the global $bsh reference.

