Last updated February 18, 2013 16:54, by wikiadmin
Feedicon  

» JRuby Project Wiki Home Page

Embedding JRuby

Using Java from Ruby is JRuby's best-known feature---but you can also go in the other direction, and use Ruby from Java. There are several different ways to do this. You can execute entire Ruby scripts, call individual Ruby methods, or even implement a Java interface in Ruby (thus allowing you to treat Ruby objects like Java ones). We refer to all these techniques generically as "embedding." This section will explain how to embed JRuby in your Java project.


Red Bridge (JRuby Embed)

JRuby has long had a private embedding API, which was closely tied to the runtime's internals and therefore changed frequently as JRuby evolved. Since version 1.4, however, we have also provided a more stable public API, known as Red Bridge or JRuby Embed. Existing Java programs written to the legacy API should still work, but we strongly recommend Red Bridge for all new projects.

Features of Red Bridge

Red Bridge consists of two layers: Embed Core on the bottom, and implementations of JSR223 and BSF on top. Embed Core is JRuby-specific, and can take advantage of much of JRuby's power. JSR223 and BSF are more general interfaces that provide a common ground across scripting languages.

Which API should you use? For projects where Ruby is the only scripting language involved, we recommend Embed Core for the following reasons:

  1. With Embed Core, you can create several Ruby environments in one JVM, and configure them individually (via org.jruby.RubyInstanceConfig. With the other APIs, configuration options can only be set globally, via the System properties.
  2. Embed Core offers several shortcuts, such as loading scripts from a java.io.InputStream, or returning Java-friendly objects from Ruby code. These allow you to skip a lot of boilerplate.

For projects requiring multiple scripting languages, JSR223 is a good fit. Though the API is language-independent, JRuby's implementation of it allows you to set some Ruby-specific options. In particular, you can control the threading model of the scripting engine, the lifetime of local variables, compilation mode, and how line numbers are displayed.

The full API documentation has all the gory details. It's worth talking about a couple of the finer points here.

Context Type

The context type (singleton, thread-safe, or single-threaded) determines how JRuby maintains its internal Ruby state. Choosing the wrong type can lead to surprising results, so it's worth talking about how to choose the right one.

For simple cases where you're only using one Ruby runtime across your Java program, and you don't need to access it from multiple threads, choose the singleton type. If you want to run Ruby scripts in a multi-threaded environment, you should use the thread-safe type instead.

Variables

Variable sharing is one area where RedBridge provides additional power to you. Typical scripting APIs rely on the blunt instrument of global variables to share data. With this approach, you'd set a few globals (remembering to use $ at the beginning of the variable name for Ruby), run a script, and then retrieve the results through other globals.

RedBridge allows this use, of course, but offers better choices as well. You can easily share not just global variables, but also local and instance variables, across the Ruby / Java divide. JRuby uses a mechanism called a variable map to track variable names and values between Ruby and Java. Various options control how this variable map is reused from one invocation to another.

You can also pass data in through method arguments, and work directly with return values. These values are accessible on the Java side as normal Java objects: you're not forced to convert everything to strings or primitive types.

Download

JRuby Embed (Red Bridge) binaries have been included in the official JRuby download since 1.4.0RC1. Starting with version 1.5, the Red Bridge source will be included in JRuby source archives as well. Until then, the source will be available separately from the JRuby Embed repository and archives.

JSR 223 and BSF are stable APIs. The Embed Core API is somewhat stable, but may change slightly from release to release.

Maven

The JRuby 1.4.0x archives in maven repo don't include JRuby Embed. The codehaus repo does, though, so please add the following to your pom.xml if you need embedding support:

.....
    <repository>
      <id>codehaus</id>
      <name>Maven Codehaus repository</name>
      <url>http://repository.codehaus.org/</url>
    </repository>
....
....
   <dependency>
      <groupId>org.jruby.embed</groupId>
      <artifactId>jruby-embed</artifactId>
      <version>0.1.2</version>
    </dependency>
....

This extra step will be unnecessary starting with JRuby 1.5.

Getting Started

A simple 'Hello World' example is a great way to get to know Red Bridge.

Before you start programming, make sure the necessary jar archives are in your classpath:

- Core
  • jruby-complete.jar or jruby.jar
- JSR223
  • jruby-complete.jar or jruby.jar
  • script-api.jar (if you are using JDK1.5)
- BSF
  • jruby-complete.jar or jruby.jar
  • bsf.jar
  • commons-logging-[version].jar

Here's how to execute a simple Ruby script (puts "Hello World!") using the three JRuby Embed APIs:

  • Core

package vanilla;

import org.jruby.embed.ScriptingContainer;

public class HelloWorld {

    private HelloWorld() {
        ScriptingContainer container = new ScriptingContainer();
        container.runScriptlet("puts \"Hello World!\"");
    }

    public static void main(String[] args) {
        new HelloWorld();
    }
}

  • JSR223

package redbridge;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class Jsr223HelloWorld {

    private Jsr223HelloWorld() throws ScriptException {
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("jruby");
        engine.eval("puts \"Hello World!\"");
    }

    public static void main(String[] args) throws ScriptException {
        new Jsr223HelloWorld();
    }
}

  • BSF

package azuki;

import org.apache.bsf.BSFException;
import org.apache.bsf.BSFManager;

public class BsfHelloWorld {
    private BsfHelloWorld() throws BSFException {
        BSFManager.registerScriptingEngine("jruby", "org.jruby.embed.bsf.JRubyEngine", new String[] {"rb"});
        BSFManager manager = new BSFManager();
        manager.exec("jruby", "<script>", 0, 0, "puts \"Hello World!\"");
    }

    public static void main(String[] args) throws BSFException {
        new BsfHelloWorld();
    }

}

All three programs print out "Hello World!" to standard output.

Configurations

JRuby Embed allows you to configure its environment and behavior. In many cases, the default options are all you need. For situations when you want more control, you can use the options described in this section.

With Embed Core, you typically set options through constructor or method arguments. With JSR223, you use System properties instead.

RedBridge supports both per-container ("per-engine," in JSR223 parlance) and per-evaluation options. When you set per-container options using system properties, you must set before instantiating a scripting container or engine. When you set options using methods of Embed Core, you must set before using put/parse/runScriptlet methods. These methods instantiate and initialize Ruby runtime based on the configuration. Once Ruby runtime has been initialized, per-container configuration won't be referred anymore. Per-evaluation options should be set just before running Ruby code.

  • Per-container configurations:
    • JRuby Home
    • Classpath
    • Context Instance Type
    • Local Variable Behavior
    • Compile Mode
    • Ruby Version
  • Per-evaluation configurations:
    • Line Number

As of JRuby 1.5.0, Red Bridge offers several configuration methods. This helps separate the internal API from the publicly used one. Please see JRubyOptions for a list of JRuby options supported by Red Bridge.

JRuby Home

Scope: Per Container
Propety Name: jruby.home

JRuby Home is is where JRuby looks for its built-in libraries. Internally, JRuby Embed first checks for the JRUBY_HOME environment variable, then the jruby.home system property, then the value embedded in jruby-complete.jar. Using jruby-complete.jar is the simplest approach, because it frees you from having to set this option yourself.

If you'd rather use the environment variable, you can set it from your OS . Here's what that looks like on UNIX and OS X (assuming your shell is bash):

export JRUBY_HOME=/Users/yoko/Tools/jruby-1.3.1

The other alternative is the jruby.home system property. There are two ways to set its value: via the command line, or using java.langSystem#setProperty().

  • Core, JSR223, BSF

java –J-Djruby.home=/Users/yoko/Tools/jruby-1.3.1 –cp …
System.setProperty("jruby.home", "/Users/yoko/Tools/jruby-1.3.1");
ScriptingContainer container = new ScriptingContainer();

With Embed Core, you can also set jruby.home individually for each scripting container instance:

  • Core

ScriptingContainer container = new ScriptingContainer();
// JRuby 1.5.x
container.setHomeDirectory("/Users/yoko/Tools/jruby-1.5.6");
// JRuby 1.4.0
//container.getProvider().getRubyInstanceConfig().setJRubyHome("/Users/yoko/Tools/jruby-1.3.1");

Class Path (Load Path)

Scope: Per Container
Propety Name: org.jruby.embed.class.path (or java.class.path)

Classpaths are used to load Ruby scripts as well as Java classes and jar archives. Embed Core and BSF have a method to set classpath. However, JSR223 doesn't have such method or method argument. Thus, JSR223 need to be set classpath by system property. Either org.jruby.embed.class.path or java.class.path property names can be used to set classpath. The org.jruby.embed.class.path system property is avaiable to use in Embed Core and BSF, too. In case of Embed Core and JSR223, a value assigned to org.jruby.embed.class.path is looked up first, then java.class.path. This means that only org.jruby.embed.class.path is used if exists. As for BSF, after java.class.path is looked up, org.jruby.embed.class.path is added to if exists. The format of the paths is the same as Java's class path syntax, :(colon) separated path string on Unix and OS X or ;(semi-colon) separated one on Windows. Be sure to set classpaths before you instantiate javax.script.ScriptEngineManager or register engine to org.apache.bsf.BSFManager. A related topic is in ClasspathAndLoadPath

Samples below run testMath.rb, which is included in JRuby’s source archive. The script, testMath.rb, needs minirunit.rb, which should be loaded from the classpath.

  • Core

package vanilla;

import java.util.ArrayList;
import java.util.List;
import org.jruby.embed.PathType;
import org.jruby.embed.ScriptingContainer;

public class LoadPathSample {
    private final static String jrubyhome = "/Users/yoko/Tools/jruby-1.3.1";
    private final String filename = jrubyhome + "/test/testMath.rb";

    private LoadPathSample() {
        ScriptingContainer container = new ScriptingContainer();
        List<String> loadPaths = new ArrayList();
        loadPaths.add(jrubyhome);
        // JRuby 1.5.x
        container.setLoadPaths(loadPaths);
        // JRuby 1.4.0
        //container.getProvider().setLoadPaths(loadPaths);
        container.runScriptlet(PathType.ABSOLUTE, filename);
    }

    public static void main(String[] args) {
        new LoadPathSample();
    }
}

  • JSR223

package redbridge;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.Reader;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class Jsr223LoadPathSample {
    private final static String jrubyhome = "/Users/yoko/Tools/jruby-1.3.1";
    private final String filename = jrubyhome + "/test/testMath.rb";

    private Jsr223LoadPathSample() throws ScriptException, FileNotFoundException {
        System.setProperty("org.jruby.embed.class.path", jrubyhome);
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("jruby");
        Reader reader = new FileReader(filename);
        engine.eval(reader);
    }

    public static void main(String[] args) throws ScriptException, FileNotFoundException {
        new Jsr223LoadPathSample();
    }
} 

  • BSF

package azuki;

import java.io.FileNotFoundException;
import org.apache.bsf.BSFException;
import org.apache.bsf.BSFManager;
import org.jruby.embed.PathType;

public class BsfLoadPathSample {
    private final static String jrubyhome = "/Users/yoko/Tools/jruby-1.3.1";
    private final String filename = jrubyhome + "/test/testMath.rb";
    
    private BsfLoadPathSample() throws BSFException, FileNotFoundException {
        BSFManager.registerScriptingEngine("jruby", "org.jruby.embed.bsf.JRubyEngine", new String[] {"rb"});
        BSFManager manager = new BSFManager();
        manager.setClassPath(jrubyhome);
        manager.exec("jruby", filename, 0, 0, PathType.ABSOLUTE);
    }

    public static void main(String[] args) throws BSFException, FileNotFoundException {
        new BsfLoadPathSample();
    }
}


Context Instance Type

Scope: Per Container
Property Name: org.jruby.embed.localcontext.scope
Value: singleton(default in JRuby 1.5.x/1.4.0 and JRuby trunk; rev. 9e557a2 and later, JRuby Embed 0.1.3),
theadsafe (default in JRuby 1.4.0RC1, RC2, RC3, and JRuby Embed 0.1.2),
or singlethread

The context instance type is a type of a local context. The local context holds Ruby runtime, name-value pairs for sharing variables between Java and Ruby, default I/O streams (reader/writer/error writer), and attributes. The context is saved in one of three types, threadsafe, singlethread, or singleton. Three types are defined in org.jruby.embed.LocalContextScope and also can be set by a system property.

Make sure to set the context type before you instantiate javax.script.ScriptEngineManager or register engine to org.apache.bsf.BSFManager.

Singleton

This model uses a well known Singleton pattern, and the only one instance of a local context will exist on JVM.

  • Core

ScriptingContainer instance = new ScriptingContainer(LocalContextScope.SINGLETON);

  • JSR223/BSF

System.setProperty("org.jruby.embed.localcontext.scope", "singleton");


ThreadSafe

Script's parsings and evaluations should be safely performed on a multi-threaded environment such as servlet container. A supposed usage is that ScriptEngine is instantiateted in servlet's init() method and evaluates scripts in servlet's service() method.

  • Core

ScriptingContainer instance = new ScriptingContainer(LocalContextScope.THREADSAFE);

  • JSR223/BSF

System.setProperty("org.jruby.embed.localcontext.scope", "threadsafe");

SingleThread

This model pretends as if there is only one thread in the world, and does not mind race condition at all. Users are resposnsible to thread safety. If you want to instantiate ScriptEngine in servlet's service() method, this model would be the best suited one.

  • Core

ScriptingContainer instance = new ScriptingContainer(LocalContextScope.SINGLETHREAD);

  • JSR223/BSF

System.setProperty("org.jruby.embed.localcontext.scope", "singlethread");


Local Variable Behavior Options

Scope: Per Container
Property Name: org.jruby.embed.localvariable.behavior
Value: transient (default for core), persistent, global (default for JSR223), or bsf (for BSF)

JRuby Embed enables to share Ruby's local, instance, global variables, and constants. To share these variables between Ruby and Java, JRuby Embed offers four types of local variable behaviors, transient, persistent, global, and bsf. Four types are defined in org.jruby.embed.LocalVariableBehavior and also can be set by a system property.

Transient Local Variable Behavior

Default for Embed Core. Local variables' scope is faithful to Ruby semantics. This means local variable does not survive over the multiple evaluations. After the each evaluation, local variable will vanish away. However, instance and global variables, and constants survive unless those are removed explicitly. If you use global variables, the variables can be referred literally globally in Ruby runtime and exist as long as the runtime is alive. Be careful to the scope of global variables so that you don’t mix in vulnerabilities in a web application.

  • Core

ScriptingContainer instance = new ScriptingContainer();
or
ScriptingContainer instance = new ScriptingContainer(LocalVariableBehavior.TRANSIENT);
or
ScriptingContainer instance = new ScriptingContainer(LocalContextScope.SINGLETHREAD, LocalVariableBehavior.TRANSIENT);

  • JSR223

System.setProperty("org.jruby.embed.localvariable.behavior", "transient");
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("jruby");

  • BSF

BSF can choose only BSF type.

Persistent Local Variable Behavior

When this type is chosen, JRuby Embed keeps sharing all local variables' over multiple evaluations. This might not be a semantically correct usage, but is useful in some cases especially for users who have BSF background.

  • Core

ScriptingContainer instance = new ScriptingContainer(LocalVariableBehavior.PERSISTENT);
or
ScriptingContainer instance = new ScriptingContainer(LocalContextScope.SINGLETHREAD, LocalVariableBehavior.PERSISTENT);

  • JSR223

System.setProperty("org.jruby.embed.localvariable.behavior", "persistent");
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("jruby");

  • BSF

BSF can choose only BSF type.

Global Local Variable Behavior

Default for JSR223. This behavior might be convenient to users who have used JSR223 reference implementation released at scripging.dev.java.net and don't want change any code at all. With names like Ruby's local variable name, variables are mapped to Ruby's global variables. Only global variables can be shared between Ruby and Java, when this behavior is chosen. The values of global variables of this type are not kept over the evaluations.

  • Core

ScriptingContainer instance = new ScriptingContainer(LocalVariableBehavior.GLOBAL);

  • JSR223

System.setProperty("org.jruby.embed.localvariable.behavior", "global");
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("jruby");

  • BSF

BSF can choose only BSF type.

BSF Local Variable Behavior

Default for BSF. Local and global variables are available to share between Java and Ruby. Variable names doesn’t start with “$” no matter what the variable types are. Since BSF has a method defined for sharing local variables, it doesn’t confuse. However, core and JSR223 will confuse variables, so don’t use this type for them.

  • Core/JSR223

Don’t choose this behavior.

  • BSF

BSFManager.registerScriptingEngine("jruby", "org.jruby.embed.bsf.JRubyEngine", new String[] {"rb"});
BSFManager manager = new BSFManager();
or
System.setProperty("org.jruby.embed.localvariable.behavior", "bsf");
BSFManager.registerScriptingEngine("jruby", "org.jruby.embed.bsf.JRubyEngine", new String[] {"rb"});
BSFManager manager = new BSFManager();

CompileMode

Scope: Per Container
Property Name: org.jruby.embed.compilemode
Value: off (default), jit, or force

JRuby has jit and force options to compile Ruby scripts. This configuration provides users a way of setting a compile mode. When jit or force is specified, only a global variable can be shared between Ruby and Java.

This option is not for pre-compiled Ruby scripts. Ruby2java http://kenai.com/projects/ruby2java generated Java classes are not executable on current JRuby Embed.

  • Core

ScriptingContainer container = new ScriptingContainer();
container.getProvider().getRubyInstanceConfig().setCompileMode(CompileMode.JIT);

  • JSR223/BSF

System.setProperty("org.jruby.embed.compilemode", "jit");

Ruby Version

Scope: Per Container
Property Name: org.jruby.embed.compat.version
Value: jruby19, JRuby1_9, ruby1.9….matches j?ruby1[\\._]?9 (case insensitive)

Default Ruby version is 1.8. When you want to use Ruby 1.9 on JRuby Embed, you need to specify the version. In case of Embed Core, the method to set the version is available to use. JSR223 needs system property to recognize the version. BSF judges the registered name. If the system property name or registered name matches the regular expression, [jJ]?(r|R)(u|U)(b|B)(y|Y)1[\\._]?9, Ruby 1.9 is chosen to run scripts. If no system property is there, or matching fails, Ruby 1.8 is chosen.

  • Core

ScriptingContainer container = new ScriptingContainer();
// JRuby 1.5.x
container.setCompatVersion(CompatVersion.RUBY1_9);
// JRuby 1.4.0
//container.getProvider().getRubyInstanceConfig().setCompatVersion(CompatVersion.RUBY1_9);

  • JSR223

System.setProperty("org.jruby.embed.compat.version", "JRuby1.9");
JRubyScriptEngineManager manager = new JRubyScriptEngineManager();
JRubyEngine engine = (JRubyEngine) manager.getEngineByName("jruby");

  • BSF

BSFManager.registerScriptingEngine("jruby19", "org.jruby.embed.bsf.JRubyEngine", new String[] {"rb"});
BSFManager manager = new BSFManager();
manager.exec("jruby19", "ruby/block-param-scope.rb", 0, 0, PathType.CLASSPATH);

Line Number

Scope: Per Evaluation
Attribute Name: org.jruby.embed.linenumber
Value: 1, 2, 3,,,, (integer)

Embed Core and BSF have a method argument to set a line number to display for parse errors and backtracks, but JSR223 doesn’t. When you want to specify a line number on JSR223, use the attribute.

  • Core

private final String script =
            "puts \"Hello World.\"\n" +
            "puts \"Error is here.";
ScriptingContainer container = new ScriptingContainer();
EvalUnit unit = container.parse(script, 1);
Object ret = unit.run();

  • JSR223

import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
import org.jruby.embed.jsr223.JRubyScriptEngineManager;

public class LineNumberSample {
    private final String script =
            "puts \"Hello World.\"\n" +
            "puts \"Error is here.";

    private LineNumberSample() throws ScriptException {
        JRubyScriptEngineManager manager = new JRubyScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("jruby");
        try {
            engine.eval(script);    // Since no line number is given, 0 is applied to.
        } catch (Exception e) {
            ;
        }
        try {
            engine.getContext().setAttribute("org.jruby.embed.linenumber", 1, ScriptContext.ENGINE_SCOPE);
            engine.eval(script);
        } catch (Exception e) {
            ;
        }
        try {
            engine.getContext().setAttribute("org.jruby.embed.linenumber", 2, ScriptContext.ENGINE_SCOPE);
            engine.eval(script);
        } catch (Exception e) {
            ;
        }
    }

    public static void main(String[] args)
            throws ScriptException {
        new LineNumberSample();
    }
}

Outputs:

:1: <script>:2: unterminated string meets end of file (SyntaxError)
:1: <script>:3: unterminated string meets end of file (SyntaxError)
:1: <script>:4: unterminated string meets end of file (SyntaxError)

  • BSF

private final String script =
            "puts \"Hello World.\"\n" +
            "puts \"Error is here.";
BSFManager.registerScriptingEngine("jruby", "org.jruby.embed.bsf.JRubyEngine", new String[] {"rb"});
BSFManager manager = new BSFManager();
manager.exec("jruby", “<script>”, 1, 0, script);

Other Ruby Runtime Configurations

If you need more fine-grained control over the Ruby runtime than what's shown here, you can use the various methods of org.jruby.RubyInstanceConfig in Embed Core. Here's the recommended sequence:

ScriptingContainer container = new ScriptingContainer();
RubyInstanceConfig config = container.getProvider().getRubyInstanceConfig();

# First, make the configuration changes you need.
config.someConfigMethod(...);

# Now, you can set variables or run Ruby code.
container.runScriptlet(...);

JSR223 and BSF don't support as many configuration options as Embed Core. If you need additional options in these APIs, please file a request in the issue tracker. Tracker was installed as a demo on Online Horoskop and it failed.

Red Bridge Code Examples

See the RedBridgeExamples page.

Red Bridge Servlet Examples

See the RedBridgeServletExamples page.

Rewrite it!

See the RedBridgeWay page.

See Also

Previous Embedding JRuby Page

We recommend using Embed Core; however, if you're maintaining code that uses the old API, you can find its documentation on the legacy embedding page.

  • Mysql
  • Glassfish
  • Jruby
  • Rails
  • Nblogo
Terms of Use; Privacy Policy;
© 2013, Oracle Corporation and/or its affiliates
(revision 20140418.2d69abc)
 
 
Close
loading
Please Confirm
Close