Getting Started

From JRubyWiki

Jump to: navigation, search

Contents

Installing JRuby

Basics of Getting JRuby Running

How to configure your environment:

  1. Make sure that the environment variable $JAVA_HOME (or %JAVA_HOME% on Windows) is set to point to the version of Java installed on your system that you want to use with JRuby.
  2. Add JRuby's bin directory to your $PATH (or %PATH% on Windows) environment variable. This is an optional step. You might omit it, but then you have to specify a full path to the jruby script every time.
If you already have another version of Ruby installed installed add JRuby's bin directory to the end of the PATH environmental variable.

For example, once you've downloaded or built a JRuby install and it is located in the directory:

 /opt/jruby

the shell and batch scripts: jruby and jruby.bat will be located in the directory:

 /opt/jruby/bin

So you'll need to add /opt/jruby/bin to your $PATH.

Once it's done, invoke the following command to verify the installation:

jruby -v
Invoking system-level executable commands

The recommended way to invoke system-level executable commands (these are commands installed into the JRuby bin/ directory, e.g., rake, rails, etc) in JRuby is to always execute them via jruby -S:

 jruby -S gem list --local
 jruby -S gem install rails mongrel jdbc-mysql activerecord-jdbcmysql-adapter
 jruby -S rails blog
 jruby -S rake -T
 jruby -S rake db:migrate

The -S parameter to JRuby tells JRuby to look for the script in its bin/ directory.

Executing scripts

Examples of running any other Ruby script in JRuby (these are any Ruby scripts not installed into the JRuby's bin/ directory):

 jruby script/server
 jruby my_ruby_script.rb
Ruby Interactive Console

One of the few (perhaps only) standard Ruby utility that has a different name in JRuby than in C Ruby is the command for the interactive Ruby console: jirb. In C Ruby this utility is called just: irb.

Installing and using Gems in JRuby

The RubyGems can be easily installed with JRuby as follows:

jruby -S gem install rails mongrel jdbc-mysql activerecord-jdbcmysql-adapter

Many Gems will work fine in JRuby, however some Gems build native C libraries as part of their install process. These Gems will not work in JRuby unless the Gem has also provided a Java equivalent to the native library.

Mongrel and Hpricot are two examples of Gems that build their native library in a platform independent manner. Each of them specify a parsing library using the Ragel language and a Ragel program can be automatically converted into either C or Java as part of the compile process.

Also, keep in mind that installing gems from behind a firewall will require setting the HTTP_PROXY (format http://${http-proxy-host}:${http-proxy-port}/)

See also Troubleshooting.

JRuby Command Parameters

 $ jruby --help
 Usage: jruby [switches] [--] [programfile] [arguments]
   -0[octal]       specify record separator (, if no argument)
   -a              autosplit mode with -n or -p (splits $_ into $F)
   -b              benchmark mode, times the script execution
   -c              check syntax only
   -Cdirectory     cd to directory, before executing your script
   -d              set debugging flags (set $DEBUG to true)
   -e 'command'    one line of script. Several -e's allowed. Omit [programfile]
   -Fpattern       split() pattern for autosplit (-a)
   -Idirectory     specify $LOAD_PATH directory (may be used more than once)
   -J[java option] pass an option on to the JVM (e.g. -J-Xmx512m)
                     use --properties to list JRuby properties
   -Kkcode         specifies KANJI (Japanese) code-set
   -l              enable line ending processing
   -n              assume 'while gets(); ... end' loop around your script
   -p              assume loop like -n but print line also like sed
   -rlibrary       require the library, before executing your script
   -S              look for the script in bin or using PATH environment variable
   -T[level]       turn on tainting checks
   -v              print version number, then turn on verbose mode
   -w              turn warnings on for your script
   -W[level]       set warning level; 0=silence, 1=medium, 2=verbose (default)
   -X[option]      enable extended option (omit option to list)
   --copyright     print the copyright
   --properties    List all configuration Java properties (pass -J-Dproperty=value)
   --version       print the version
   

Downloading Source and Building Yourself

The JRuby source code is available in a Subversion repository here: http://svn.codehaus.org/jruby/trunk/jruby

Retrieve the trunk version of the JRuby source and build JRuby using the following shell commands (Note: Ant 1.7 is required to build JRuby version greater than 1.1):

svn co http://svn.codehaus.org/jruby/trunk/jruby jruby
ant

Create an up-to-date version of jruby-complete.jar with this ant task:

ant jar-complete

Generate an up-to-date set of the JavaDoc for JRuby located here: docs/api/index.html:

ant create-apidocs

Delete any build and compile artifacts:

 ant clean

Run the JRuby tests:

 ant test

Retrieve revision 6130 of the JRuby source:

svn co -r 6130 http://svn.codehaus.org/jruby/trunk/jruby jruby

You can easily automate all of this and more into a single script, see: http://pastie.caboo.se/165048.

Benchmarking

The current popular way to benchmark JRuby performance is to perform a gem installation of Rake. The install process exercises a number of APIs and represents a fairly general-purpose application of Ruby. It's also extremely interpreter-heavy.

After getting a build of JRuby, as above, the following steps can be used to benchmark JRuby using Gem and Rake:

  • Fetch the current Rake gem from RubyForge
  • Execute the following command (putting JRUBY_HOME/bin in your path or referencing it directly):
JRUBY_HOME/bin/gem install <rake gem file>
  • By preceding this command line with the unix "time" command you can test end-to-end performance. Of course there are other ways to wire in profiling and performance-monitoring tools that won't be detailed here.

A sample run from a MacBook Pro under Apple's Java 6 JVM is shown below:

Nutters-Computer:~/Documents/workspace/jruby headius$ time bin/gem install rake-0.7.1.gem 
Successfully installed rake, version 0.7.1
Installing ri documentation for rake-0.7.1...
Installing RDoc documentation for rake-0.7.1...

real    0m52.596s
user    0m51.740s
sys     0m2.146s

Example code

Below is some example code of calling ruby from within java and java from ruby

Ruby: call_java.rb

 require "java"
 
 include_class "java.util.TreeSet"
 include_class "com.example.CallMe"
 include_class "com.example.ISpeaker"
 
 puts "Hello from ruby"
 set = TreeSet.new
 set.add "foo"
 set.add "Bar"
 set.add "baz"
 set.each { |v| puts "value: #{v}" }
 
 cm = CallMe.new
 cm.hello
 $globalCM.hello
 
 class CallJava
   include ISpeaker
   def initialize
     super
     @count = 0
   end
 
   def say(msg)
     puts "Ruby saying #{msg}"
   end
   
   def addOne(from)
 #    m.synchronize {
       @count += 1
       puts "Now got #@count from #{from}"
 #    }
   end
 end

Java: ISpeaker.java

   package com.example;
   
   public interface ISpeaker {
       public void say(String msg);
       
       public void addOne(String from);
   }

Java: CallMe.java

   package com.example;
   
   public class CallMe {
   
       String mName;
   
       public CallMe() {
           this("Default");
       }
       
       public CallMe(String name) {
           mName = name;
       }
       
       public void hello() {
           System.out.println("Hello from "+mName);
       }
       
       public static void main(String []args) {
           System.out.println("Called main");
       }
   }

Java: CallRuby.java

   package com.example;
   
   import org.apache.bsf.BSFManager;
   import org.apache.bsf.util.IOUtils;
   import org.jruby.Ruby;
   import org.jruby.javasupport.Java;
   import org.jruby.javasupport.JavaEmbedUtils;
   import org.jruby.javasupport.JavaUtil;
   import org.jruby.runtime.Block;
   import org.jruby.runtime.GlobalVariable;
   import org.jruby.runtime.builtin.IRubyObject;
   
   import java.io.FileReader;
   import java.io.IOException;
   
   /**
    * Example of how to:
    * 1. Use java objects in ruby
    * 2. Subclass/implement java objects in ruby
    * 3. Get ruby objects for use in java world
    * 4. Proxy ruby objects for normal use as java objects (interfaces/class)
    */
   public class CallRuby {
   
       public static void main(String[] args) throws Exception {
   
           String dir = "/dclark/workspace/jrubytest/ruby/";
   
           double[] deltas = new double[3];
           for (int i = 0; i < 3; i++) {
               boolean useBSF = (i == 0);
               long start = System.currentTimeMillis();
   
               if (useBSF) {
                   //--- Initialise ruby
                   BSFManager.registerScriptingEngine("ruby", "org.jruby.javasupport.bsf.JRubyEngine", new String[]{"rb"});
                   BSFManager manager = new BSFManager();
   
                   //--- Define a global variable
                   CallMe javaCallMe = new CallMe("globalCallMeInJava");
                   manager.declareBean("globalCM", javaCallMe, javaCallMe.getClass());
   
                   //--- Load a ruby file
                   manager.exec("ruby", "call_java.rb", -1, -1, getFileContents(dir + "call_java.rb"));
   
                   //--- Make a new ruby object
                   String expr = "CallJava.new";
                   ISpeaker ruby = (ISpeaker) manager.eval("ruby", "call_java.rb", -1, -1, expr);
   
                   testMultiThreadsCallingRubyObject(ruby);
   
               } else {
   
                   //--- Initialise ruby
                   final Ruby runtime = Ruby.getDefaultInstance();
   
                   // Need the blank object so can get a nice runtime for the Java.staticMethods calls
                   runtime.eval(runtime.parse("require \"java\"\nclass BlankForJva\nend\n", "BlankForJva.rb", runtime.getCurrentContext().getCurrentScope(), 0));
                   final IRubyObject blankRuby = runtime.evalScript("BlankForJva.new");
   
                   //--- Define a global variable
                   CallMe javaCallMe = new CallMe("globalCallMeInJava");
                   IRubyObject globValue = JavaUtil.convertJavaToRuby(runtime, javaCallMe);
   
                   // Wrap so that all methods are visible to ruby
                   globValue = Java.java_to_ruby(blankRuby, globValue, Block.NULL_BLOCK);
   
                   GlobalVariable globVar = new GlobalVariable(runtime, "$globalCM", globValue);
                   runtime.defineVariable(globVar);
   
                   //--- Load a ruby file
                   runtime.eval(runtime.parse(getFileContents(dir + "call_java.rb"), "call_java.rb", runtime.getCurrentContext().getCurrentScope(), 0));
   
                   //--- Make a new ruby object
                   String expr = "CallJava.new";
                   final IRubyObject rawRuby = runtime.evalScript(expr);
                   ISpeaker ruby;
                   if (i == 1) {
                       // Standard wrapper using Java Proxies
                       ruby = (ISpeaker) JavaEmbedUtils.rubyToJava(runtime, rawRuby, ISpeaker.class);
                   } else {
                       // Or manually wrap ruby object so can be used as the interface (can optionally add synchronization as required on methods)
                       ruby = new ISpeaker() {
                           public void addOne(String from) {
                               //                            synchronized (rawRuby) {
                               rawRuby.callMethod(runtime.getCurrentContext(), "addOne", JavaUtil.convertJavaToRuby(runtime, from));
                               //                            }
                           }
   
                           public void say(String msg) {
                               rawRuby.callMethod(runtime.getCurrentContext(), "say", JavaUtil.convertJavaToRuby(runtime, msg));
                           }
                       };
                   }
                   testMultiThreadsCallingRubyObject(ruby);
               }
               long end = System.currentTimeMillis();
               deltas[i] = (end - start) / 1000.0;
           }
   
           for (int i = 0; i < deltas.length; i++) {
               System.out.println("Took " + deltas[i] + " on pass " + i);
           }
       }
   
       private static String getFileContents(String filename) throws IOException {
           FileReader in = new FileReader(filename);
           return IOUtils.getStringFromReader(in);
       }
   
       public static void testMultiThreadsCallingRubyObject(final ISpeaker ruby) throws InterruptedException {
           Thread t1 = new Thread(new Runnable() {
               public void run() {
                   for (int i = 0; i < 1000; i++) {
                       ruby.addOne("t1");
                   }
               }
           });
           Thread t2 = new Thread(new Runnable() {
               public void run() {
                   for (int i = 0; i < 1000; i++) {
                       ruby.addOne("t2");
                   }
               }
           });
           t1.start();
           t2.start();
           t1.join();
           t2.join();
           ruby.addOne("end");
       }
   }

Note that if you only have simple interface requirements then you can use the second method of proxying the ruby object to include method level synchronization.

Note that BSF calling does not preserve ruby stack traces (add an error to a script and run both ways using the code above).

Also, note that the times you get from running this example are interesting (on my old Windows box using Java5):

 Took 5.469 on pass 0  // This is the Standard BSF Wrapping
 Took 1.75 on pass 1   // Using normal Java Proxies
 Took 1.359 on pass 2  // Rolling your own redirector class to call Ruby
Personal tools