JRuby – Java and Ruby: Concepts, Methods and Examples

23 July, 2008 at 16:39 3 comments

JRuby logoJRuby is a Java implementation of the Ruby interpreter, being developed by the JRuby team.

JRuby is free software released under a three-way CPL/GPL/LGPL license.

JRuby is tightly integrated with Java to allow the embedding of the interpreter into any Java application with full two-way access between the Java and the Ruby code. (Compare Jython for the Python language.)

JRuby’s lead developers are Charles Nutter , Thomas Enebo Ola Bini and Nick Sieger. In September 2006, Sun Microsystems hired Enebo and Nutter to work on JRuby full time. In June 2007, ThoughtWorks hired Ola Bini to work on Ruby and JRuby.

Why You Want Rails

Several sessions have been devoted to Rails at JavaOne 2008. The nice thing is that have invariably used the same example: a simple blog app that Rails creates in seconds.

Whether by accident or design, that confluence has worked out well. It has made it possible to see the same application from multiple different vantage points. So far, I’ve seen it constructed from the command line and from NetBeans, worked with it in an interactive lab, and seen a walk-through of the process that explained what was going on and discussed various options along the way.

At this point, I’ve got enough familiarity with it that I could reach in and do something with it–and the high-level understanding of the architecture I’ve gained will make it easier to come up to speed when I’m looking at someone else’s app. Most importantly, though, design thoughts are swimming in my head, along with experiments I want to try. So this convention has been unexpectedly good for learning about Rails.

There are good reasons for wanting to know about Rails, too, if there are any web apps in your future:

  • You can create a running app in seconds, with a couple of commands. You won’t spend hours just to get Hello World to run.
  • Less Rails code than Java application configuration (which should pretty well end the discussion).
  • Rails uses Rake for builds–and Rake is a hecka-fun build language.

But perhaps most importantly, Rails lets you do agile development, primarliy due to the fact that Ruby is interpreted:

  • You can do true “test first” development: You can create your test and watch it fail before you begin writing the code that will make it succeed. The RSpec framework even distinguishes between code that fails (red) and code that hasn’t been developed yet (yellow). (Were it a compiled language, the tests wouldn’t compile until the application classes and interfaces had been written.)
  • You make a change and run it. There is no need to recompile, redeploy, and restart. That makes the process a lot more fun. (Some changes require you to run a new instance of the application–database changes, if memory serves. But for most changes, you just refresh the browser!)

Rails apps are also more maintainable, due to:

  • A “database create” task that is useful for testing.
  • A “database migrate” task that’s helpful when you’re debugging a problem in a previous version of the application.
  • A fixed directory structure, so developers have an easier time coming up to speed on new applications.
  • A “convention over configuration” mantra that also makes it easy for new developers, so there are fewer configuration connections to understand before you can make sense of the app. In addition, the skills developed on one project translate readily to the next. (For example, if you see a data class called “Tractor”, you will expect to find a “tractors” table in the database.

Why You Want JRuby

According to Ola Bini, a core JRuby developer from Thoughtworks, “JRuby on Rails is taking the world by storm”. Bini, the author of Practical JRuby on Rails, went on to show a lengthening list of companies that are using it.

There are a variety of reasons that JRuby is popular for Rails apps:

  • Database connections are made through JDBC, which provides broader, more scalable database support.

  • On average JRuby, runs 2 and a half times faster than Ruby, except at startup. (That time is inconsequential for production, but does slow down development cycles a tad. Fortunately, the JRuby guys have found a simple library trick that will shorten the current startup time of 1.5 seconds, bringing it down to .5 seconds.)

  • In addition to native threads, JRuby supports Unicode natively.

  • There is a whole team at work on performance and garbage collecting on the JVM, so there is that much less for the JRuby implementors to worry about.

  • Code can be fully compiled ahead of time or just in time. (For performance, you need to experiment to find out which works best. Hotspot can do a pretty amazing job of runtime optimization. But pre-compiling has another advantage: protection of source for a commercial application.)

  • A JRuby on Rails application can be packaged into a WAR, and then deployed onto any compliant server. There tend to be a lot of those in the IT world, these days, so you can deploy your Rails app without asking your IT department to support a whole different server with unknown security vulnerabilities. (The packaging can be done with Goldspike, or with the new kid on the block: Warbler. More on that tomorrow.)

  • Glassfish–the Java web app server that scales well–is available as a JRuby gem. (Right now, there is a bit of configuration to do to make everything work. But glassfish v3 will have direct support for Rails, so you just develop and run.)

  • A Ruby on Rails app can scale, but it does so at the cost of one process per connection. A large application like Twitter was running 180 processes, at last count. Each process consumes system resources, and context switching adds latency, so minimizing the process count is a good thing for scaling. Glassfish gives you multiple listener threads, while JRoR gives you multiple processing threads. Result: Big performance, vanishingly small process count (on the order of, like, one).

  • With JRuby you get the best of both worlds: Ruby applications and libraries, plus Java libraries. And you can access those libraries with Ruby syntax (or Java syntax, if you want).

  • JRuby provides a plethora of language features that are missing in Java–features that tend to make programming more fun. Some, like closures, are under consideration for addition to Java, but simple things like Ruby’s literal syntax for hashmaps make code easier to read and write:

    require 'java';
    import java.util.HashMap
    h = hashmap.new
    access with ruby syntax: h["foo"] = "bar"
    h.key.sort { |key| ...do something here... }

One JRuby on Rails application worth examining will be Mingle, Bini’s tool for project collaboration. It’s the first JRuby on Rails application, and it does some selective pre-compilation to protect internal sources. (The pricing is interesting, though. The first 5 licenses are always free. After that, you spend anywhere from $300 for a six-month license to $1000 for a perpetual license.)

JRuby – Enterprise 2.0 Presentation

JRuby – calling Ruby code

Here is a simple Java code that executes a Ruby script defining some functions and the executing them:

package cz.cesnet.meta.jruby;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.script.ScriptContext;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;

public class JRubyExample1 {

    public static void main(String[] args) throws ScriptException, FileNotFoundException {
        //list all available scripting engines
        listScriptingEngines();
        //get jruby engine
        ScriptEngine jruby = new ScriptEngineManager().getEngineByName("jruby");
        //process a ruby file
        jruby.eval(new BufferedReader(new FileReader("myruby.rb")));

        //call a method defined in the ruby source
        jruby.put("number", 6);
        jruby.put("title", "My Swing App");

        long fact = (Long) jruby.eval("showFactInWindow($title,$number)");
        System.out.println("fact: " + fact);

        jruby.eval("$myglobalvar = fact($number)");
        long myglob = (Long) jruby.getBindings(ScriptContext.ENGINE_SCOPE).get("myglobalvar");
        System.out.println("myglob: " + myglob);
    }

    public static void listScriptingEngines() {
        ScriptEngineManager mgr = new ScriptEngineManager();
        for (ScriptEngineFactory factory : mgr.getEngineFactories()) {
            System.out.println("ScriptEngineFactory Info");
            System.out.printf("\tScript Engine: %s (%s)\n", factory.getEngineName(), factory.getEngineVersion());
            System.out.printf("\tLanguage: %s (%s)\n", factory.getLanguageName(), factory.getLanguageVersion());
            for (String name : factory.getNames()) {
                System.out.printf("\tEngine Alias: %s\n", name);
            }
        }
    }
}

And here is the Ruby code in file myruby.rb:

def fact(n)
 if n==0
    return 1
 else
    return n*fact(n-1)
 end
end

class CloseListener
 include java.awt.event.ActionListener
 def actionPerformed(event)
    puts "CloseListere.actionPerformed() called"
    java.lang.System.exit(0)
 end
end

def showFactInWindow(title,number)
    f = fact(number)
    frame = javax.swing.JFrame.new(title)
    frame.setLayout(java.awt.FlowLayout.new())
    button = javax.swing.JButton.new("Close")
    button.addActionListener(CloseListener.new)
    frame.contentPane.add(javax.swing.JLabel.new(number.to_s+"! = "+f.to_s))
    frame.contentPane.add(button)
    frame.defaultCloseOperation=javax.swing.WindowConstants::EXIT_ON_CLOSE
    frame.pack()
    frame.visible=true
    return f
end

The Ruby script defines a function fact(n) which computes the factorial of a given number. Then it defines a (Ruby) class CloseListener, which extend a (Java) class java.awt.event.ActionListener. And finaly it defines a function showFactInWindow, which builds a GUI window displaying a label and a close button, assigns the CloseListener class as a listener for the button action, and returns the value of n! :

Java JFrame window displayed from Ruby code

Please note that a Ruby and Java classes can be mixed together.

(To run the example save the codes above into files cz/cesnet/meta/jruby/JRubyExample1.java and myruby.rb, and compile them and run using

javac cz/cesnet/meta/jruby/JRubyExample1.java
java cz.cesnet.meta.jruby.JRubyExample1

)

You can pass any Java object using the put("key",object) method on the ScriptingEngine class, the key becomes a global variable in Ruby, so you can access it using $key. The numerical value returned by showFactInWindow is Ruby’s Fixnum clas, which is converted into java.lang.Long and returned by the eval() method.

Any additional global variable in the Ruby script can be obtained in Java by getBindings(), as is shown by getting the $myglobalvar RUby global variable.

more

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.

Example Program at Startup

Figure 4. Example Program at Startup.

Example Program After Button Click

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);
        }
      ...
    }
  }
}

more

[wikipedia.org]
[http://www.artima.com]
[http://docs.codehaus.org]
[http://www.slideshare.net/]
[http://jruby.codehaus.org/]
[http://java.sun.com/developer]


Bookmark and Share

Entry filed under: Java, JRuby, JRuby on Rails, Ruby, Ruby on Rails. Tags: , , .

JSON (JavaScript Object Notation): Concepts, Methods, Examples and Security Threats Google Web Toolkit (GWT) – Create Ajax applications in Java: Examples and Concepts

3 Comments Add your own

  • 1. la electronica m  |  16 August, 2008 at 09:18

    hello…

    exellent…

    Reply
  • 2. Ecko  |  14 June, 2010 at 08:23

    great post,,, thanks

    Reply
  • 3. Click Here  |  28 May, 2014 at 17:03

    Howdy superb website! Does running a blog such as this take a
    massive amount work? I have no knowledge of programming but I had been hoping to start my own blog
    in the near future. Anyway, if you have any recommendations or tips
    for new blog owners please share. I know this is off topic
    but I simply needed to ask. Kudos!

    Reply

Leave a comment

Trackback this post  |  Subscribe to the comments via RSS Feed


IT Passion’s Store

Archives

Communities

Get the Source
OSGi supporter
JUG Milano

Upcoming Events



....

Blog Stats

  • 424,815 hits

My PageRank

What's My Google PageRank?