JRuby on Rails: The power of Java, the simplicity of Ruby on Rails

Benefit from the combination of the latest and greatest Web framework and the powerful, industry-standard JVM

Ruby, the full-featured object-oriented dynamic (scripting) language, with strong support for functional programming and metaprogramming, has drawn attention recently for its flexibility and ease of development. JRuby, a JVM-based interpreter for Ruby, combines the ease of the Ruby language with execution in the powerful JVM, including full integration to and from Java libraries.

Since my previous JavaWorld article on the topic ("JRuby for the Java World"), there have been some exciting developments for JRuby. Sun Microsystems hired the two lead JRuby developers, Charles Nutter and Thomas E. Enebo, in a sign of support for Ruby in the JVM. Java Platform, Standard Edition 6 (Java SE 6) was released with a new standard API for plugging in interpreters for dynamic languages. Plans are firming up for the Java 7 VM to support dynamic languages directly with a new "invoke dynamic" bytecode and hot-swapping of class definitions at runtime. Meanwhile, the JRuby team has released version 0.9.2 with broader support for Ruby on Rails, and the next big release of JRuby, expected in February, will include full support for Ruby on Rails.

Ruby on Rails, an easy-to-use but powerful Web framework built on the Ruby language, has rapidly gained popularity for new database-backed Web applications, especially in the Web 2.0 world. I'll refer you elsewhere for details of Ruby on Rails, also called Rails. Even though the project is only 3 years old, plenty of articles and books have been written about it, and its documentation is outstanding for an open source project (see the Ruby on Rails Web site). Likewise, I refer you to my earlier article for an introduction to JRuby.

In this article, I examine the junction between Rails and Java. I compare the Rails and Java Web frameworks, describe the benefits of running Rails with JRuby, and review some lessons that a Java developer — even one who does not use Rails — can learn from this innovative framework.

Power plus simplicity

Rails radically speeds and simplifies the development of Web applications, but it suffers from an image of immaturity, especially in high-end enterprise-strength capabilities.

On the other hand, the Java platform, with its virtual machines, libraries and application servers, has been gaining in speed, stability and functionality, to the point where it is generally considered the leading platform for high-end server applications. Yet as long as it remains tied to the Java language, the Java platform risks falling behind as newer languages gain popularity.

JRuby ties together the complementary strengths of all these technologies, promising added popularity for both Ruby and Rails, while giving the Java platform a new role in running non-Java languages.

Rails: Where Java frameworks are heading

To a Java developer, Rails seems like the natural culmination of trends in the evolution of Java Web frameworks: less unnecessary code, more abstraction and dynamism, and fuller out-of-the-box functionality.

Convention over configuration

Early versions of Java Platform, Enterprise Edition (Java EE) required extensive configuration and code for each component. Enterprise JavaBeans, for example, had multiple source-code and XML configuration files for each bean. This complexity turned EJB into a byword for heavyweight development, and eventually led to a 180 degree turn in EJB 3, which aims for POJO (plain-old Java objects) beans with minimal redundancy and configuration. Even so, heavyweight Java EE applications still require developers to develop code to express the same business objects across multiple software tiers — GUI, business logic and persistence. Then, despite the redundancy and similarity between the layers, developers must glue the layers together with configuration files. In contrast, the newer Java Web frameworks Seam and Spring expose business objects with much less configuration and code.

Java frameworks have also been moving towards standardization and integration of a stack across the tiers of a Web application. In the earliest days, Java Web application developers hand-coded HTML output from servlets, created their own Model-View-Controller architectures, and accessed their databases with SQL over Java Database Connectivity (JDBC). Later, they gathered components to execute much of the common functionality, such as tag libraries, Struts and Hibernate. Recently, Spring integrated much of the functionality in a single top-to-bottom lightweight stack.

From the start, Rails has embodied these principles of simplicity, principles known to the Rails community as "Don't Repeat Yourself" and "Convention over Configuration." (Non-redundancy and meaningful defaults are among the oldest principles of software engineering; it's a wonder that we have had to wait so long for something like Rails.) The framework guesses the connection between different tiers based on straightforward conventions. For example, there is no need for XML, annotations or the like, to tell the framework that the customer class is backed by the customers table; Rails' ActiveRecord database-wrapping layer guesses this (while taking into account pluralization and capitalization). Rails goes so far as to implicitly and dynamically add attributes to reflect database columns: a last_name column automatically brings a last_name attribute into being.

In special cases, where the conventions don't meet your needs, you can still add configuration, using pure Ruby code or the lightweight Ruby-like YAML format, both of which omit XML's redundant brackets and closing tags. But you should stick to the defaults where possible. Rails is "opinionated software," which makes it far easier when you go with the flow.

Rails is a "batteries included" framework (a phrase popularized by Python): it includes everything you need for a standard database-backed Web application, from data-access layer, through model, view, and controller. It lets you focus on what's specific to your application, instead of recoding common functionality or searching for open source libraries that integrate well together.

Dynamism and reflection

Java frameworks have also been moving towards greater use of reflection and metaprogramming. Spring, for example, uses reflection to plug all of its pieces together with dependency injection, in contrast to the more static approach of the standard Java EE server stack. Hibernate, the popular object-relational mapping framework, does its mapping with dynamic metaprogramming, updating the bytecode at runtime, in contrast to early data-access frameworks, which required cumbersome source-code or bytecode generation at development time.

Hibernate's developers had to use some advanced techniques to accomplish this functionality, but in Ruby, metaprogramming is such a natural part of the language that Rails, at runtime, dynamically generates not only mappings, but also the business-layer class definitions needed to access and display the underlying database, thereby minimizing the need for manual coding or creation of inflexible generated code.

Supporting the development process

Around the end of the 1990s, Java programmers got "test infected" with the JUnit frameworks, but writing tests for server-side applications has always been difficult. Spring now generates tests together with the Web application. Rails does the same, taking advantage of dynamism and metaprogramming to support multiple types of tests: unit tests, which exercise the individual methods of the model classes; functional tests, which work on the level of the individual Web request; and integration tests, which run a series of Web requests in a simulated user session.

The popular Ant and Maven tools standardized the automation of builds in Java. Rails, too, makes builds easy with Ruby's rake build tool; it adds an innovative migration system, which automates the upgrade (or rollback) of database schemas and data.

Why run Rails on the JVM?

If your project is now using Java frameworks such as Spring and Hibernate, there is no need to change. But if you have the flexibility to choose a new approach for your next project, consider Rails. Even easier to use than lightweight Java frameworks, Ruby on Rails seems like a natural next step in a Web developer's journey towards simplicity and expressiveness.

Unfortunately, making the transition to a new language is generally considered a dangerous step, and managers are leery of the risk. JRuby can make Rails more acceptable to management. On the JVM, Rails becomes a Java framework. In fact, non-Java languages in "Java" Web frameworks are commonly used, as, for example. in JavaServer Pages. (See Bruce Tate's book Java to Ruby for more strategies for getting management buy-in.)

Just as operating systems, typically written in C/C++, provide the infrastructure for applications written in more abstract languages, so too the Java platform plays the role of "system software" for dynamic languages like Ruby, providing infrastructure-level support. A tremendous variety of functionality is accessible through Java today. APIs such as JDBC and JMS (Java Message Service) are best in class, and many irreplaceable in-house or independent software vendor enterprise information systems are accessible through Java APIs. By using JRuby, Rails applications can call into existing Java libraries as easily as Java code can.

With JRuby, Rails applications can run alongside Java Web applications on existing Java EE application servers. These application servers have a strong technical infrastructure. On the human side, education programs, experienced developers  and support personnel are commonly available. And simply by running on the JVM, these application servers gain the benefit of the immense optimization efforts put into the JVM over the last decade.

The JVM has a much more sophisticated security model than Ruby's, giving JRuby on Rails tools for dealing with Web applications' typical security challenges, including control of Ruby scripts received from various sources. It also includes built-in support for internationalization.

In "Ruby for the Java World," I described the move towards dynamic languages as part of a long-term trend up the abstraction ladder towards higher-level languages. Running the Ruby interpreter in the managed environment of the JVM rather than at the operating system level is, again, a step up to a more abstract layer. So is the use of Rails as a standard implementation of the Web-application layer, leaving only application-specific functionality in developers' hands.

Getting started with JRuby on Rails

The JRuby interpreter is closing in on its goal of near complete compatibility with the original interpreter, known as Matz's Ruby Interpreter (MRI) for its creator, Yukihiro Matsumoto. Though some rough edges need to be smoothed out, as of version 0.9.2, JRuby can now run Rails.

Although you can skip straight to installing Rails with JRuby, I suggest you get it going with MRI as a way of learning the system and validating your installation. The simplest way to install Ruby on Rails is with one of the all-in-one bundles that includes Ruby, Rails, Apache and the MySQL database. (See InstantRails and Locomotive in Resources.)

Install JRuby. Next, install Rails code into JRuby using the gem tool, available in the bin directory under your JRuby home, with the command gem install rails -y --no-ri --no-rdoc. (The last two switches skip the documentation generation, which can be slow.)

The above instructions will get you going with JRuby on Rails, but as you get comfortable with it, you have some other choices. Rails lets you use most common relational databases such as Oracle and SQL Server, Web servers such as Apache, and application servers such as Tomcat. You also have the option of accessing databases using the ActiveRecord-JDBC adapter (see JRuby wiki) rather than Ruby-native drivers.

As you continue working with JRuby on Rails, you may want to temporarily keep MRI on hand, as it is faster and more stable than JRuby (although that gap will close soon). It's useful for running development scripts such as migrations and generators, as well as for validating your Web application if you suspect a bug in JRuby. To do this, you'll need to keep your application free of calls to Java libraries or else let it choose between Java libraries and pure-Ruby calls. You can distinguish the interpreter by checking a built-in constant: if RUBY_PLATFORM =~/java/ {…}.

The Java libraries used by the application have to be in JRuby's classpath. To customize the jar files used by each JRuby application, create a batch (or shell) file, which sets the classpath and then calls the basic JRuby batch file. You can also alter the system classpath or edit the basic batch file.

I've provided an example, available for download from Resources, that shows a simple Ruby on Rails Web application that distributes long running tasks among worker processes. This example illustrates the combination of the distributed processing power of a JavaSpace with the speed of development of a Rails application. The code for integrating the two, calling from Rails into a Java API, is as simple as calling from Java to Java. In addition, because tasks are expressed in Ruby, users can develop and submit them with ease. Neither Ruby nor Java alone could have achieved this. (See Resources for downloadable code. Instructions are at doc/README_FOR_APP. The application's Ant file build.xml also illustrates how to set up and run JRuby on Rails applications from Ant.)

Challenges

As the release number suggests, JRuby 0.9.2 is still not ready to run production applications. Some bugs need to be fixed, and for the time being, JRuby is slower than MRI. Using a Java application server with Rails requires a non-standard adapter servlet, and building war files requires a special Ant script, neither of which is yet a standard part of the JRuby distribution.

Rails is particularly weak at dealing with legacy components, a result of the opinionated software philosophy, which provides excellent support for one good way of solving most common problems, but less flexibility for alternative approaches. For example, ActiveRecord assumes that each table has a single primary key column called id. Although you can substitute a key column for another name, you cannot define a multiple-column key without introducing a special plug-in. Java frameworks like Hibernate, in contrast, slow development in the simple (and common) cases, but handle the corner cases and legacy code much better than Rails.

Ultimately, the major challenge to adopting Rails will be the desire to standardize on existing languages and frameworks. Rails shines for new projects and new organizations, where this is less of a barrier.

Lessons from Rails

Even if you can't adopt Rails into your Java shop today, you can learn a lot by studying it and running some sample code. Rails serves as an epitome of the trend towards lightweight technologies and as an outstanding example of principles that every application should follow. Rails demonstrates dynamism and metaprogramming techniques, which can be adopted in Java, albeit with some difficulty. Rails' "batteries-included" integration of common functionalities sets an example for other framework providers. (See Bruce Tate's "Crossing Borders" series.)

As an example, the Trails framework implements many of the principles of Rails in the Java language. Although it cannot achieve the full flexibility of a Ruby-based framework, it uses concepts like reflection, metaprogramming (using annotations), and convention over configuration. It builds on the lightweight Java frameworks Spring, Hibernate and Tapestry to allow all tiers of a high-quality, maintainable Web application to be declared with a POJO class representing the business domain. Though Trails, like Rails, targets Web applications, the lessons can be applied to frameworks in other areas as well.

JRuby on Rails: The perfect combination

Rails is quickly becoming the leader in lightweight Web application development. With JRuby, Rails will gain access to the functionality, power, and industry acceptance of Java libraries and the JVM. Rails makes it a pleasure to develop for the Web and lets you focus on what's specific to your application.

What has your experience been with JRuby and/or Ruby on Rails? Share your thoughts in the discussion thread that appears below.

My thanks to Charles O. Nutter for his valuable comments on this article. Any remaining errors are mine.

Joshua Fox is a software architect based in Israel. He has worked with Java since 1996. His experience with dynamic languages in the JVM runs from JavaScript-controlled VoIP telephony in the browser to an implementation of a Python-based ontological expression language.