By Ramon Wartala
The Ruby language and the Rails framework work together to provide a popular option for rapid development of web applications. Rails speeds development by defining much of the program's structure with minimal input from the programmer. Despite the recent success of Ruby on Rails, however, many developers still prefer Java. The Java environment offers several advantages for the developer, including more sophisticated parallel processing and better Unicode support. In some cases, the development team might prefer to stay with Java simply for compatibility with existing Java libraries and legacy program components.
JRuby [1] is a Ruby interpreter that offers a bridge from Ruby to the world of Java. According to the website, the JRuby package provides the following:
If you're partial to Java but intrigued by the potential of Ruby on Rails, JRuby might be an interesting option. In this article, I will help you get started with JRuby and offer a look at some benefits of blending Java and Ruby.
Jan Arne Petersen, a student at the University of Bonn, Germany, create the first Java port of Ruby 1.6 back in 2001. To be able to use the Rails framework natively on JRuby, Thomas Enebo and Charles Nutter looked into porting Ruby 1.8 in 2006.
The project started to pick up speed in September of the same year, when Sun Microsystems signed up the two developers and assigned them full-time to developing JRuby.
If you already have a Java SDK on the target system, JRuby is easy to install on Linux. The various releases are available in TAR or ZIP archive format. For this article, I used the first beta release of the 1.1 version throughout.
After unpacking the archive where you can reference it with the JRUBY_HOME environmental variable, you can test the installation with a couple of sample programs that reside below the samples directory. The line
jruby $JRUBY_HOME/samples/swing2.rb
should draw a simple Swing frame with a button. After negotiating this step, you can start to add critical components. Out of the box, Ruby includes its own package management tool, RubyGem. Typing
jruby -S gem list r -r
for example, searches gem.rubyforge.org for packages starting with the letter r. Typing
jruby -S gem help commands
gives an overview of available commands.
jruby -S gem install rake
installs the rake build tool, which plays the role of the classical Unix make utility under Ruby.
jruby -S gem install -y rails
installs the current version of the complete Rails framework with a full set of packages to resolve dependencies. If you want to work with databases, it makes sense to use the JRuby ActiveRecord JDBC package, which was programmed by the core JRuby developers.
jruby -S gem install ActiveRecord-JDBC
completes the installation of the required components. The packages are all neatly organized below $JRUBY_HOME/lib/ruby/gems/1.8/gems.
In familiar Ruby style, you can create a Rails project on JRuby as follows:
jruby -S rails mybooks
The new mybooks directory will now contain the complete skeleton of the new web application. However, it lacks some flesh on its bones, which is easy to change by creating the following entity, referred to as a model in Rail-speak:
jruby script/generate model Book
Besides a couple of other files, the Rails generator now creates a file called 001_create_books.rb in the db/migrate directory for the sample application (Listing 1).
Listing 1: create_books.rb |
01 class CreateBooks < ActiveRecord::Migration 02 def self.up 03 create_table :books do |t| 04 t.column :title, :string 05 t.column :isbn, :string 06 end 07 end 08 09 def self.down 10 drop_table :books 11 end 12 end |
The database migration file in Listing 1, which is used for database schema versioning, contains both the components needed for creating and destroying a specific model table. Lines 4 and 5 specify two model attributes, which point to corresponding columns in the books database table.
Before you can test these mappings, you need to perform the only configuration step you can't do with Ruby on Rails: the database configuration. You need a new database for this. On MySQL, you would type:
mysql> CREATE DATABASE mybooks_development; mysql> GRANT ALL ON mybooks_development.* TO'root'@'localhost' IDENTIFIED BY 'root';
To allow Rails to connect to the database, there is a YAML-formatted configuration file in the config/database.yml directory. Using the ActiveRecord JDBC package, the connector looks like the example shown in Listing 2.
Listing 2: config/database.yml |
01 development: 02 adapter: jdbc 03 driver: com.mysql.jdbc.Driver 04 url: jdbc:mysql://localhost/mybooks_development 05 username: root 06 password: root |
Also, add a line like
require 'jdbc_adapter'
before
Rails::Initializer.run do |config|
to the environment.rb file in the same directory. To populate the database, type
jruby -S rake db:migrate
to create a new database migration, which you can then try out at the console using the JRuby Console ntool:
jruby script/console
which loads the current MyBooks environment. The commands in Listing 3 test the ActiveRecord object mapper. Typing the following runs the Rails Scaffold Generator:
jruby script/generate scaffold book
Now the Rails application on JRuby is ready to run. Typing
jruby script/server
starts the WEBrick HTTP server, which plays the role of a lightweight developer server in Rails. After starting the server, look at the C(reate)R(etrieve)U(pdate)D(delete) interface for the Book model at http://localhost:3000/books.
Listing 3: ActiveRecord Test |
01 >> b = Book.new 02 => #<Book:0xfc01db @attributes={"isbn"=>nil, "title"=>nil}, @new_record=true> 03 >> b.title = "Web Applications with Ruby on Rails" 04 => "Web Applications with Ruby on Rails" 05 >> b.isbn = "9783827324917" 06 => "9783827324917" 07 >> b.save |
So far, the differences between Ruby and JRuby have been slight, with just a few changes to the database connection. Once the system is running, you can easily deploy self-programmed and third-party Java libraries in this extremely agile environment. For the purposes of the Book example, a simple Java class [2] will do just fine; it tests the validity of ISBN numbers.
The isbn.jar Java archive is copied to the same location as the calling model class below app/models. The code from Listing 4 is added to the Book model class.
Listing 4: Book Extension |
01 require 'java' 02 require 'isbn.jar' 03 04 class Book < ActiveRecord::Base 05 def validate 06 unless validate_isbn 07 errors.add('isbn','is invalid') 08 end 09 end 10 11 def validate_isbn 12 begin 13 isbn = com.openly.info.ISBN.new(self.isbn) 14 rescue 15 isbn = nil 16 end 17 end 18 end |
The first two lines bind both Java and the ISBN Java package. Within the model class, the ISBN Java constructor is called against the model instance's isbn attribute. This call to the ISBN constructor occurs in the validate_isbn method. Ruby exception handling prevents the application from throwing wild Java exceptions when it finds an invalid ISBN number.
Validation methods are an approach to giving the Rails application a way to respond to invalid user input. The program typically executes the validate method before storing the object instance. You can monitor this behavior in the Rails Console (Listing 5).
Listing 5: Error Messages at the Rails Console |
01 >> b = Book.new(:title => 'No Book', :isbn => '4712') 02 => #<Book:0xd849e0 @attributes={"isbn"=>"4712", "title"=>"No Book"}, @new_record=true> 03 >> b.save 04 => false 05 >> b.validate 06 => ["is invalid", "is invalid"] |
Of course, this kind of validation is also possible in Ruby itself, but this simple example shows how quickly and easily you can integrate Java code with (J)Ruby on Rails.
A combination of Apache and multiple Mongrel instances [3] provides a standard approach for delivering Ruby on Rails applications. Configuring an environment of this kind is non-trivial and will probably put off any experienced J2EE developer, especially if you are used to dropping your application in the form of a Web Application Archive (WAR) onto any J2EE-compatible application server and letting it auto-deploy. Thanks to JRuby, the WAR technique is now open to Rails applications. As of this writing, you do need to install the GoldSpike plugin for your own application, as follows:
jruby script/plugin install http://jruby-extras.rubyforge.org/svn/trunk/rails-integration/plugins/goldspike
Before you can create the WAR archive, you typically need a valid database connector to support the process. GoldSpike creates a war.rb file in the config directory for settings of this type.
Assuming a MySQL connector, deleting the pound sign (#) in the following line should do the trick:
#maven_library 'mysql', 'mysql-connector-java', '5.0.4'
Then call the following Rake target:
jruby -S rake war:standalone:create
You can monitor the archives that have been added to the WAR file at the command line. If you have Apache Tomcat [4] as your J2EE container, simply type
cp mybooks.war $TOMCAT_HOME/webapps/.
to launch into auto-deployment. The Rails application is installed at http://localhost:8080/mybooks/books.
In this article, I presented a quick view of the possible deployment scenarios for JRuby on Rails. Developers interested in wandering between the worlds might also appreciate the ability to use JRuby code within Java. This feature is really exciting if you want to use a session bean to offer a web service that retrieves data from a Rails application.
Some developers argue that Java/Ruby interaction options like those described in this article simply dilute the Ruby or Java model. Many real-world projects, however, need a means for integrating the agile development philosophy embodied in a framework like Rails with legacy applications or expensive, proprietary libraries. Java developers also will have an easier time adjusting to the Ruby on Rails environment if they can take their tried-and-trusted tool chains with them.
INFO |
[1] JRuby: http://jruby.codehaus.org
[2] ISBN Tools: http://isbntools.com [3] Mongrel: http://mongrel.rubyforge.org/index.html [4] Apache Tomcat: http://tomcat.apache.org |
THE AUTHOR |
Ramon Wartala is Head of IT with online marketers orangemedia.de based in Hamburg, Germany, and the co-author of Web Applications with Ruby on Rails, which is published by Addison-Wesley. |