JRuby – Java and Ruby: Concepts, Methods and Examples
23 July, 2008 at 16:39 3 comments
JRuby 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! :
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.
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.
|
|
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); } ... } } }
[wikipedia.org]
[http://www.artima.com]
[http://docs.codehaus.org]
[http://www.slideshare.net/]
[http://jruby.codehaus.org/]
[http://java.sun.com/developer]
Entry filed under: Java, JRuby, JRuby on Rails, Ruby, Ruby on Rails. Tags: Java, JRuby, Ruby.
3 Comments Add your own
Leave a comment
Trackback this post | Subscribe to the comments via RSS Feed
1. la electronica m | 16 August, 2008 at 09:18
hello…
exellent…
2. Ecko | 14 June, 2010 at 08:23
great post,,, thanks
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!