Last updated November 02, 2009 01:01, by yokolet
Feedicon  

JRuby Embed (aka Red Bridge) Wiki

JRuby Embed Users:

JRuby Embed codebase and documents have been merged into JRuby. You can see wiki pages at JRuby Project.

Plese visit there. The pages here won't be updated. Also, update your bookmarks. Thank you.



JRuby Embed provides Java API to use Ruby scripts from Java leveraging powerful features of JRuby. You can call methods defined in Ruby as well as get Ruby scripts run from Java. Also, you are able to get an instance of a Java interface even though the interface is implemented by Ruby. Interface implementations are no more closed in Java world. In JRuby Embed world, you are allowed to implement Java interfaces by Ruby and use them in Java program.

Not only methods but also variables can be shared between Java and Ruby. By sharing variables mechanism of JRuby Embed, various types of variables are going back and forth between Java and Ruby. In case of Java to Ruby, variables are given through method arguments or directly injected to Ruby scripts. On the other hand, from Ruby, variables are implicitly handed over to Java. Ruby code doesn’t need to do something special for sharing variables. When Ruby scripts return values, Java program can get them as normal Java objects. When Ruby scripts use variables, those names and values are available to take in to Java. What Java program should do is just getting a value with a variables name from a variable map. Local, instance, global variables and constants used in Ruby scripts are collected and hold in the variable map that JRuby Embed takes care of. The variable map is also used to give values to Ruby from Java; thus, when you put key-value pair into the map, the pair is injected into Ruby code.

In addition to executing scripts and sharing variables, JRuby Embed offers a way of configuring its environment and behavior to leverage the power of JRuby. For example, JRuby Embed has options to set Ruby version or compile mode. JRuby Embed is highly configurable, so it will fit in various use cases. A downside of this is that you need to go over the document to find what you should do to get the best result.

However, looking at the document, you will find many useful methods. JRuby Embed provides many kinds of convenient methods in terms of ease of use. Those are shortcut methods or methods that return Java friendly objects. The methods defined in JRuby Embed will help you to avoid writing boilerplate code in your Java programa. See API document for further detail.

JRuby Embed is consists of two parts, core and implementations of scripting API. Currently, JSR223 and BSF are implemented on top of JRuby Embed core. You can use the core if you don’t use other scripting languages at the same time. Core API is more flexible than the existing API implementations. Since JSR223/BSF scripting APIs are designed to give common interfaces to many scripting languages, implementations are ended up in a subset of core API. Those are not JRuby specific, thus, sometimes no way is out while core API can do. However, the JSR223/BSF APIs are stable, so the choice is yours.

Look at API document, too.


Download

JRuby Embed (Red Bridge) binary is included in JRuby 1.4.0RC1. Visit http://www.jruby.org/download to get the archive.The Archive of JRuby Embed version 1.2.0 is also available to download at: http://kenai.com/projects/jruby-embed/downloads, too. JRuby Embed source codes are not included in JRuby 1.4.0x source archives. Get them at the Source Code Repository of this project, http://kenai.com/projects/jruby-embed/sources. However, JRuby 1.5.0 source archives will have JRuby Embed.

All users, please understand. Embed Core API may change in future releases.

For maven users:
JRuby 1.4.0x archives in maven repo doesn't have JRuby Embed in it. It is on codehause maven repo, so add below to your pom.xml:

.....
    <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>
....

Also, JRuby 1.5.0 will have JRuby Embed in it, so you don't need to add jruby-embed dependency.


Getting Started

To get to know how to develop it, a simple 'Hello World' sample is always the first friend. Here are samples to say 'Hello World' in a JRuby Embed way.

Before you start programming, make sure 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


Programs to execute a simple Ruby script, puts "Hello World!," using JRuby Embed are:

  • 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

Although default setting works well, JRuby Embed allows you to configure its environment and behavior. In this section, you can find what configuration options are and how to configure them. In case of Embed core, most options are set through arguments of constructors or methods easily. Meanwhile, JSR223/BSF are often not flexible to accept JRuby specific parameters. Therefore, System properties and JSR223’s attributes are used for this purpose.

Please remember you need to set parameters before instantiating a container/engine when you want to make per container configurations work.

The per container configurations are
JRuby Home
Class Path
Context Instance Type
Local Variable Behavior
Compile Mode
Ruby Version

If you want set per evaluation configurations, set them just before the evaluation or the method arguments.

The per evaluations configuration is
Line Number

JRuby Home

Scope: Per Container
Propety Name: jruby.home


JRuby Home is a home directory (or folder) of JRuby and is used to load JRuby’s built-in libraries and for other purposes by JRuby. Internally, JRuby Embed tries to get a directory from the order of JRUBY_HOME environment variable, , jruby.home system property, then jruby.home assembled in jruby-complete.jar. If you are using jruby-complete.jar, you are free from worrying about this configuration. If you aren’t using jruby-complete.jar, it is better to set it to avoid unexpected troubles although setting JRuby Home is optional in the domain of JRuby Embed.

When you write a web application, you should use jruby-complete.jar because setting an absolute path to JRuby does not make sense. You should keep a war archive portable.

JRUBY_HOME environment variable setting depends on OS. For example, on bash of Unix and OS X:

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

When you want to use jruby.home system property, you have two ways of setting the value: a command line option or 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();

If you use Embed Core, then you can directly set jruby.home using the method:

  • Core

ScriptingContainer container = new ScriptingContainer();
container.getProvider().getRubyInstanceConfig().setJRubyHome("/Users/yoko/Tools/jruby-1.3.1");

Class 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. 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);
        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 trunk; rev. 9e557a2 and later), theadsafe (default in version 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", "threadsafe");


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 JSR 223 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 [jJ]?(r|R)(u|U)(b|B)(y|Y)1[\\._]?9

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();    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

When you want to configure Ruby runtime more than explained here, you can use methods of org.jruby.RubyInstanceConfig on Embed Core. Get the reference of RubyInstanceConfig by ScriptingContainer.getProvider().getRubyInstanceConfig() and set values in it before you set/get key-value pairs, get runtime, or parse/eval/runScriptlet. However, JSR223/BSF can't do more configurations. If you want some configurations to work, file it in the issue tracker.

Code Examples

See Examples page.

Servlet Examples

See ServletExamples page.


Blog

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