JRuby and the Java Platform

   
By Monica Pawlan, June 2007  

Articles Index

Scripts are gaining in popularity with developers because they are easy to write, take less time to write than compiled programs, and require little maintenance. Although hundreds of scripting languages are in use today, developers continue to create new ones because each language is geared to a particular purpose or domain space. For example, the JavaScript programming language is commonly used for web-based user interface (UI) development, and Perl is popular on the server side.

The Ruby programming language was released to the public in 1995 and gained widespread adoption in 2006. A multipurpose language that focuses on simplicity and productivity, it combines the best features of many compiled and interpreted languages, such as easy development of large programs, rapid prototyping, almost-real-time development, and compact code. Ruby is a reflective, dynamic, and interpreted object-oriented scripting language, and JRuby is a Java programming language implementation of the Ruby language syntax, core libraries, and standard libraries.

With JRuby, you get all of the advantages of Ruby plus interoperability with Java platform applications (Java applications) and access to the full range of Java platform functionality. JRuby and the Java platform are a compelling combination that you can apply to any programming situation. For example, from a JRuby script, you can call the Java platform Math library to access its powerful computational capabilities or call the Java platform Swing library to display a dialog box that requires end-user input before allowing the script to proceed. Also, you can use the JSR 223 Scripting APIs or the Bean Scripting Framework (BSF) APIs to call a JRuby script from within a Java application to, for example, invoke back-end processing scripts from a servlet to update or generate web content.

This introductory article gets you started using JRuby and the Java platform. It shows you how to set up JRuby, explains the basics of using JRuby scripts with Java applications and the NetBeans integrated development environment (NetBeans IDE), and provides links to help you learn more. Also, the Getting Started section briefly introduces RubyGems and Ruby on Rails.

Contents
Getting Started

Get the latest JRuby build from the download section of the JRuby site, where both source and binary bundles are available. Download and install the binary bundle if you just want to learn JRuby and run scripts. If you want to customize the code and participate in the open-source community, download and install the source bundle. In either case, be sure to read and comply with the licensing terms.

The examples in this article are written to the JRuby 0.9.8 binary release. JRuby requires Java Development Kit (JDK) 1.4 or later, but you need JDK 6 software if you want to run the examples that use the JSR 223 Scripting APIs to invoke a JRuby script from a Java application. See the section Executing JRuby Code From Within a Java Application for more information.

Once you have installed the software, you can run JRuby interactively or invoke scripts in files. As a quick test that everything is working, start interactive Ruby by typing jirb at the command line. The interactive Ruby prompt appears shortly: irb(main):003:0, where you can test the puts command as shown. Note that => nil is returned by the puts command and means that the return value is nil.

server jirb
irb(main):003:0> puts "Hi There!"
Hi There!
=> nil
irb(main):004:0>
 

If you put the puts line into the myprog.rb file, you can run it like this:

server jruby myprog.rb
Hi There!
server
 

The JRuby installation includes documentation, examples, and sample programs to help you get started. The /jruby-0.9.8/lib/ directory and its subdirectories provide built-in and standard libraries for you to use in your JRuby programs, as well as support for RubyGems.

RubyGems

RubyGems is software for managing third-party programs and libraries. You use the /jruby-0.9.8/bin/gem command to manage RubyGems. You can learn more about RubyGems from the RubyGems User Guide. This article's For More Information section also has a list of free tutorials and articles for learning Ruby syntax.

Ruby on Rails

Ruby on Rails is a software package that works with a database and a web server for rapidly developing database-backed web applications that follow the model-view-controller (MVC) design pattern. Rolling With Ruby on Rails Revisited is an excellent tutorial for learning the basics. You can use JRuby on Rails with the Netbeans IDE or by using RubyGems to install Rails into your JRuby installation as explained on the JRuby on Rails site.

Calling Java Platform APIs From JRuby

You can call standard Java platform APIs, third-party Java platform APIs, or both from interactive Ruby ( jirb) or a JRuby script by either putting the Java archive (JAR) files in the $RUBY_HOME/lib directory or modifying the CLASSPATH environment variable, and then using the require keyword in your program to include the libraries.

Code Example 1: Third-Party Library and Code Snippet

Code Example 1 shows how to use the require keyword to reference or include both third-party and Java platform API libraries.

#Load Java platform and third-party API support using 
#JRuby 0.9.1 and later syntax.
require 'java'
require 'xml-apis-1.3.02.jar'

#Third-party code
...
org.w3c.dom.Element root = g.getRoot()
svgdocument = impl.createDocument(svgNS, "svg", null)
org.w3c.dom.Node node = svgdocument.importNode(root, true) 
...
 
Code Example 2: Java Platform API Libraries and Example Swing Program

Code Example 2 takes a very simple SwingUI.java application and converts it to the JRuby naming conventions and syntax shown. Some usages, such as not terminating statements with semicolons ( ;) and prefixing variables with underscores ( _), are optional. Other usages, such as using a dollar sign ( $) to declare global variables or double colons ( ::) to reference constants, are required.

Also, notice the scriptlike structure with the def and end keywords instead of curly braces ( { and }). Although curly braces work for closures, they do not work in other places, and the scriptlike syntax is more readable and easier to debug. The new operator is a method call to instantiate a class, for example, $_button.addActionListener(Click.new()) instead of button.addActionListener(this);.

The JRuby code is shorter, and its class declarations are simplified. It has no constructor, and the code to completely exit the application in the JRuby line

_frame.defaultCloseOperation = JFrame::EXIT_ON_CLOSE
 

replaces the more involved WindowListener lines in the main method. In fact, the JRuby version has no main method at all.

Figure 1 shows what the simple program looks like when it executes, and Figure 2 shows what it looks like after you click the Click Me button.

Figure 1. Example Program at Startup.
 
Figure 2. Example Program After Button Click.
 
#Load Java platform support using JRuby 0.9.1 and later syntax
require 'java'

JFrame = javax.swing.JFrame
JLabel = javax.swing.JLabel
JPanel = javax.swing.JPanel
JButton = javax.swing.JButton
BorderLayout = java.awt.BorderLayout
Event = java.awt.event
java.lang.boolean $_clickMeMode = true

_frame = JFrame.new()
_panel = JPanel.new()
_panel.setLayout(BorderLayout.new())
_panel.setBackground(java.awt.Color::white)
_frame.getContentPane().add(_panel)
_frame.defaultCloseOperation = JFrame::EXIT_ON_CLOSE
$_button = JButton.new("Click Me")
$_text = JLabel.new("I'm a Simple Program")

_panel.add(BorderLayout::CENTER, $_text)
_panel.add(BorderLayout::SOUTH, $_button)

class Click < java.awt.event.ActionListener def actionPerformed(event)
   java.lang.Object source = event.getSource()
   if (source == $_button)
    if ($_clickMeMode)
        $_text.setText("Button Clicked")
        $_button.setText("Click Again")
        $_clickMeMode = false
    else
       $_text.setText("I'm a Simple Program")
       $_button.setText("Click Me")
       $_clickMeMode = true
    end
  end
 end
end

$_button.addActionListener(Click.new())

_frame.setTitle("Example")
_frame.pack()
_frame.setVisible(true)
 
Executing JRuby Code From Within a Java Application

JSR 223, Scripting for the Java Platform, provides an API framework for calling scripting code from within a Java application and passing data between the application and the script. These features make it possible to combine existing scripts with Java applications and to extend a Java application with general-purpose scripts that other Java applications can also use.

JSR 223 Scripting APIs are available in JDK 6 software, and by default, the APIs support the JavaScript programming language. With a little setup as described in the section JRuby Scripting Engine Setup, you can use the JSR 223 Scripting APIs with any JSR 223-compliant scripting engine such as JRuby.

The Bean Scripting Framework (BSF), discussed later in this article, is another way to call scripting code from within a Java application. Although inspired by BSF, JSR 223 adds additional features such as more flexible global variable handling, multiple global scopes, and the ability to generate simple scripts.

Code Example 3: Using JSR 223 Scripting APIs to Invoke JavaScript Technology Code

The Java Platform Scripting Programmer's Guide explains how to use JSR 223 Scripting APIs with the JavaScript programming language. Code Example 3 is the first example in the programming guide, and Code Example 4 is the equivalent program modified to call JRuby scripting code.

import javax.script.*;

public class EvalScript {
    public static void main(String[] args) throws Exception {
      // Create a script engine manager.
      ScriptEngineManager factory = new ScriptEngineManager();

      // Create a JavaScript technology engine.
      ScriptEngine engine = factory.getEngineByName("JavaScript");

      // Evaluate JavaScript technology code from string.
      try {
        engine.eval("print('Hello')");
      } catch (ScriptException exception) {
        exception.printStackTrace();
      }
    }
}
 
Code Example 4: Using JSR 223 Scripting APIs to Invoke JRuby Code

This is the code from Code Example 3 converted to use JRuby. The code is almost identical except for minor modifications on two lines. See also the article Scripting for the Java Platform for more information on the JSR 223 Scripting APIs in JDK 6 software.

import javax.script.*;

public class EvalScript {
    public static void main(String[] args) throws Exception {
      ScriptEngineManager factory = new ScriptEngineManager();

      // Create a JRuby engine.
       
                     ScriptEngine engine = factory.getEngineByName("jruby");

      // Evaluate JRuby code from string.
      try {
         
                     engine.eval("puts('Hello')");
      } catch (ScriptException exception) {
        exception.printStackTrace();
      }
    }
}
                  
 
JRuby Scripting Engine Setup

At runtime, the JSR 223 Scripting APIs must locate the appropriate script engine for the scripting language you want to use. The script engine interprets and executes the script. You can get the current JSR 223 third-party script engines from the Scripting Project on java.net by downloading the jsr223-engines.tar.gz or the jsr223-engines file and expanding it somewhere on your system, for example, in the scriptengines directory.

The scriptengines directory contains a directory for each JSR 223 script engine in the project, so you can see all the engines that are currently supported. The build directory for each script engine contains a JAR file with the script engine classes. In the case of JRuby, the script engine JAR file is jruby-engine.jar. Each script engine also has a lib directory where you copy engine-specific JAR file libraries. To use the JRuby engine, copy the asm-2.2.3.jar and jruby.jar files from your JRuby installation's lib directory into the JRuby script engine's lib directory, as shown in Figure 3.

Figure 3. Move the JAR Files.
 
Note: You could leave the jruby.jar and asm-2.2.3.jar files under the JRuby installation and adjust the -cp option to the java interpreter command shown in the Compile and Run section accordingly. However, moving the files makes it possible to run the JRuby engine interactively without editing the jruby.bat or jruby.sh scripts in the script engine's bin directory.
 
 
Compile and Run

Code Example 4 compiles with the JDK 6 software's javac command, but in order for the JSR 223 Scripting APIs to find the JRuby classes it needs at runtime, you must use the -cp option with the java command as shown here to reference the JAR files.

Here is the Windows system syntax:

D:\scriptengines\jruby>java -cp .;
D:\scriptengines\jruby\build\jruby-engine.jar; 
D:\scriptengines\jruby\lib\asm-2.2.3.jar;
D:\scriptengines\jruby\lib\jruby.jar EvalScript
 

The UNIX syntax follows:

java -cp .:
/scriptengines/jruby/lib/asm-2.2.3.jar:
/scriptengines/jruby/build/jruby-engine.jar: 
/scriptengines/jruby/lib/jruby.jar EvalScript
 
Bean Scripting Framework (BSF)

JRuby comes with Bean Scripting Framework (BSF) support in the bsf.jar file located in the JRuby lib directory. BSF, which is part of the Apache Jakarta open-source project, enables the use of scripting languages within a Java application. BSF supports a wide range of scripting languages, including JRuby. See the BSF API documentation for more information on the BSF APIs.

Code Example 5: Using BSF to Invoke JRuby Code

Code Example 5 takes Code Example 4 and adapts it to use the BSF APIs.

import org.apache.bsf.*;

public class BSF {
 public static void main(String[] args) throws Exception {

   // Create a script manager.
   BSFManager bsfmanager=new BSFManager();

   // Evaluate the ruby expression.
   try {
     bsfmanager.eval("ruby","Test",0,0,"puts('Hello')");
   } catch (BSFException exception) {
     exception.printStackTrace();
   }
}
 

Table 1 shows the parameters to the eval method.

Table 1: Parameters to the eval Method
Type
Value
Definition
java.lang.String
ruby
Language identifier. Both Ruby and JRuby are specified by the string ruby. The classpath points to either the Ruby or JRuby JAR file.
java.lang.String
Test
Source of this expression such as the name of the file containing the script. In this case, a dummy value is supplied.
int
0,0
Row and column values to pinpoint the error location in the source script.
java.lang.Object
puts('Hello')")
JRuby expression to be evaluated.
 
Compile and Run

Code Example 5 compiles with the JDK 6 software javac and java commands, but in order for the BSF APIs to find the JRuby classes that the code example needs at both compile and runtime, use the -cp option as shown here to reference the JAR files.

Here is the Windows system syntax:

D:javac -cp .;
D:\home\yourdirectory\jruby-0.9.8\lib\bsf.jar:
D:\home\yourdirectory\jruby-0.9.8\lib\jruby.jar BSF.java

D:java -cp .:
D:\home\yourdirectory\jruby-0.9.8\lib\bsf.jar:
D:\home\yourdirectory\jruby-0.9.8\lib\jruby.jar BSF
 

The UNIX syntax follows:

javac -cp .:
/home/yourdirectory/jruby-0.9.8/lib/bsf.jar:
/home/yourdirectory/jruby-0.9.8/lib/jruby.jar BSF.java

java -cp .:
/home/yourdirectory/jruby-0.9.8/lib/bsf.jar:
/home/yourdirectory/jruby-0.9.8/lib/jruby.jar BSF
 
NetBeans IDE and JRuby

The NetBeans IDE supports Java applications that use JSR 223 Scripting APIs and pure JRuby and Ruby development. It also provides RubyGems and Ruby on Rails functionality. At the time of this writing, you can get Ruby and JRuby support with the NetBeans IDE in either of the following two ways.

Once setup is complete, you can run Code Examples 3, 4, and 5 in the NetBeans IDE by creating a project for each example and adding the appropriate JAR files to the Libraries tree in each project. Also, at the time of this writing, the easiest way to run Code Example 2 in the NetBeans IDE is to download the NetBeans IDE 6.0 for Windows, create a new Ruby project, copy the JRuby code for Code Example 2 into main.rb, and run the project.

Using the NetBeans IDE provides a number of time-saving advantages including code completion, escape codes within literal strings and regular expressions, integrated documentation pop-up windows for Ruby API calls, semantic analysis with highlighting of parameters and unused local variables, and occurrence highlighting. See NetBeans IDE 6.0 Preview (M9) Information for details.

Structuring a Customizable Application

You can use JSR 223 and JRuby with a Java application to decouple compiled code from scripting functions to simplify updates, changes, and maintenance. The advantages to structuring an application to call JRuby functions are as follows:

  1. The application code is more compact and thus easier to read, maintain, and manage because the code to call and execute an external script function is shorter than using Java platform APIs to do the same thing.
  2. You can easily modify the JRuby functions to work with a different file system or a database without affecting the application code.
  3. The JRuby functions are readily accessible by other applications.
Code Example 6: Data Input and Output With JSR 223 and JRuby

FileIO.java is a simple data input and output application that illustrates how to use JSR 223 Scripting APIs to execute scripts from within a Java application. FileIO prompts the user for a text string, stores the text string to a file, reads the text string from the file, and displays it to the user. It uses JSR 223 Scripting APIs to call connectivity.rb, which is a JRuby script that contains read and write functions to handle data input and output for the application.

Figure 4 shows what the simple data input and output program looks like before user-entered text is written to the file. Figure 5 shows the same program after it reads the saved text back out of the file.

Figure 4. Example Program at Startup.
 
Figure 5. Example Program After Button Click.
 

JRuby Functions

These are the two JRuby functions in the connectivity.rb file. The f1 and f2 variables are handles for accessing the text.txt file.

#Read lines from file text.txt
def read
  File.open('text.txt', 'r') do |f1|
  while line = f1.gets
    return line
  end
 end
end

#Write data to file text.txt
def write(data)
  File.open('text.txt', 'w') do |f2|
  f2.puts data
end
end
 

Java Application With JSR 223 Scripting APIs

The JSR 223 Scripting APIs are used in the application's actionPerformed method, and the relevant parts of that method are shown here. To compile and run the full example, use the steps for Code Example 4, but substitute the correct application file name.

The example uses Invocable, which is an optional interface that enables a Java application to invoke a specific script function or script method. The JRuby and JavaScript engines implement this interface, but if you want to use a different scripting language, check that this interface is implemented for it.

The example opens a file reader on the connectivity.rb file, which contains the read and write JRuby functions. The engine.eval(f); line executes the script and ScriptException is thrown if there is a problem in the script.

The inv.invokeFunction("write", text); line calls the write function in the connectivity.rb file and passes it the text parameter. The next line calls the read function and returns the data to be displayed in the application.

public void actionPerformed(ActionEvent event){
  ScriptEngineManager manager = new ScriptEngineManager();
  ScriptEngine engine = manager.getEngineByName("jruby");

// Enable the JRuby script engine to invoke a script in a file.
   
                     Invocable inv = (Invocable) engine;
  Object obj = null;
  try {
// Open a file reader on the external JRuby script file.
     
                     FileReader f = new FileReader("connectivity.rb");

// Evaluate (execute) the script. Once evaluated, any functions 
// in the script can be called with the invokeFunction method.
     
                     engine.eval(f);
// This error is thrown when there is a problem in the script.
  } catch (
                     javax.script.ScriptException e) {
    System.out.println("Script Exception");
  } catch(java.io.FileNotFoundException e) {
    System.out.println("Cannot write to text.txt");
    e.printStackTrace();
  }
  Object source = event.getSource();
  if (source == button) {
    if (_clickMeMode){
      try {
// Get application data.
        
                     Object text = textField.getText();
// Invoke write function with text parameter.
        
                     inv.invokeFunction("write", text);
// Invoke read function and get the return value.
        
                     obj = inv.invokeFunction("read");
       }  catch (javax.script.ScriptException e) {
          System.out.println("Script Exception");
          String mess = e.getMessage();
          System.out.println(mess);
       }  catch (java.lang.NoSuchMethodException e) {
          System.out.println("No such method");
          e.printStackTrace();
      }
// Display data returned from function.
        textField.setText("");
         
                     text.setText("Text retrieved from file:");

        if(obj.toString() == null) {
          String empty = null;
          textField.setText(empty);
        } else {
          String s = obj.toString();
          textField.setText(s);
        }
      ...
    }
  }
}
                  
 
Summary

JRuby combines the convenience of scripting with the power of the Java platform. The JSR 223 Scripting APIs make it possible to easily extend applications with scripts, create modular applications that use scripts, and share data among applications and scripts. Scripts provide speed, convenience, and flexibility to any software development effort. Although scripting code is generally slower than a compiled program the first time it executes, it subsequently compares to a compiled program. As technology advances and computers get faster, the initial speed of scripting code will improve dramatically.

For More Information
Rate and Review
Tell us what you think of the content of this page.
Excellent   Good   Fair   Poor  
Comments:
Your email address (no reply is possible without an address):
Sun Privacy Policy

Note: We are not able to respond to all submitted comments.