Monday, April 23, 2007

Rails in Jetty--deployment as a simple war file

In previous posts (here, here and here) I blogged about deployment of Rails in various ways with a Java developer focus. Some time ago I started looking at JRuby again and thought about deployment of Rails on the JVM as a neat idea.

In this post, I will first show how easily this is accomplished. Then, I will write a bit about the perspectives of this for a Java developer. Because this is cool in many ways.

Creating the war
Today, I got around to trying it out for real. I took a real application of mine written in Rails and deployed it as a standalone war. Hugh! How is that done? Easily, just follow the instructions here at the JRuby wiki. Here are the steps I needed to perform (in short form):
  • Installed the Rails-Integration plugin
  • Installed the ActiveRecord-JDBC gem
  • Added mysql driver jar dependency
  • Added RedCloth gem dependency (my application uses RedCloth)
  • Modified config/database.yml to reflect ActiveRecord-JDBC setup for production env
I then simply ran "rake war:standalone:create" and a standalone war file was build for me. A quick look into the war shows me that it packages:
  • The rails application structure
  • The mysql jar in lib
  • The jruby-complete.jar file
  • The jruby "platform", including the jruby bin and lib dirs (and gems)
I then typed "rake war:standalone:run" and jetty was downloaded for me, started and the warfile deployed. No pain, just works.

What is nice about this?
One important benefit is obvious. In many places, the barrier to entry into dynamic languages like Ruby and its frameworks like Rails, is set not only by the developers, but also by the part of the business that performs the deployment and systems management. When you can simply drop a warfile to systems management, there are no questions asked. All built up knowledge can be reused there.

Second, it is nice again to be able to perform a self-contained deployment unit in a single file. As you might have read above, my Rails application is using RedCloth, which must exist as a gem on the Ruby installation it is deployed on. With the war, I can make it a packaged dependency.

Third, we are back in one process and multiple threads, which we in Java land has come to love. It is more lightweight to the operating system and it is easier to monitor. The Rails-Integration in war deployment automatically instantiates multiple JRuby runtimes to overcome the problem of Rails not being thread-safe.

And fourth, Sun is working on introducing a new "invokedynamic" bytecode to better support dynamic languages. If this gets implemented (I think it is planned for Java 7), JRuby on the JVM might perform better than MRI.

I am so excited!

Monday, April 09, 2007

Rails Fixture Tips

Here is how to impose ordering on fixture loads to handle foreign keys and how to load binary data in YAML fixtures!

Rails has tests baked into it which is nice. While learning Rails, I've been reading the "Agile Web Development with Rails" ebook which is okay, and of course it shows how to use tests and fixtures. But it leaves out some problems you will meet when doing real development:
  • YAML fixture data files will contain data with foreign key references to data in other YAML files, which requires an order on the load sequence of fixtures
  • A model might contain a self-reference, expressed through a foreign key constraint to itself, which requires an order on the load sequence of individual records inside the YAML file
  • The YAML file format is great for loading data that can easily be represented in readable text format, but how are binary data like images loaded?
YAML file load order
You can load all fixtures by doing "rake db:fixtures:load", which will load fixture files in alphabetical order. This is bad when there are FK constraints between the models.

You can solve this by simply doing "rake db:fixtures:load FIXTURES=parent,child", which will ensure fixtures are loaded in given order. That is ok, but requires us to put it on the command line each time.

Taking advantage of rails environments, you can put "ENV['FIXTURES'] ||= 'parent,child'" in the "environment.rb" file, which gives you have a predefined ordering for loading in all environments. You can even override this on the command line.

For a more "advanced" solution (have not tried it myself), you might want to have a look at this blog entry.

YAML file delete order
There is one problem with the above solution. When reloading fixtures into tables which already have data in them, Rails starts by emptying out the table with a delete statement. Rails does this in the order of inserts, which breaks FK references in the delete statements. There are two solutions to this, both represented through a new rake task.

db:fixtures:delete
You can make an explicit task to empty out fixture data from tables. This needs to be done in reverse order of the inserts. Place a ".rake" file in "lib/tasks" with this content:
namespace :db do
namespace :fixtures do
desc "Deletes all fixture tables mentioned in FIXTURES environment in reverse order to avoid constraint problems"
task :delete => :environment do
require 'active_record/fixtures'
ActiveRecord::Base.establish_connection(RAILS_ENV.to_sym)
ENV['FIXTURES'].split(',').reverse.each do |fixture_name|
ActiveRecord::Base.connection.update "DELETE FROM #{fixture_name}"
end
end
end
end
Now, you can simply do "rake db:fixtures:delete" before you do "rake db:fixtures:load".

load_fixtures_without_constraints
The above solution is nice and simple, but you will have to remember to explicitly empty tables before loading. On the Rails wiki I found a solution where FK constraints are disabled before load and enabled after load. Naturally, this is quite database specific and will not work on databases not supporting such (weird, some might say) functionality.

Individual record load order
You can also pose an ordering on the individual records in a YAML file. This is actually quite simple, as you can use the Ordered YAML format. Here is an example:
--- !omap
- parent:
id: 1
name: I am the parent
- child:
id: 2
name: I am the child
parent_id: 1
Normally, Rails would load the "child" YAML entry before "parent" as it comes first in the alphabet. Using the ordered mapping format, we ensure that parent is loaded before child.

Loading binary data into fixtures
When you want to load binary data, YAML can be a bit irritating. For me, it has mostly been loading images into MySQL, so I do not know if it works on other databases. Full credit should go to this blog poster. My example here is based on that a lot.

YAML supports binary data in one of two ways: "Canonical" or "generic". Someone out there seems to have had problems with the "canonical" format and this example is using the "generic" format. Basically, in YAML you can write
entry: !binary |
...here comes base64 encoded data...
And this rails code inside the YAML file can load a binary file, BASE64 encode it and substitute some spaces into it to make it YAML formatted.
<%
def binary_fixture_data(name)
filename = "#{RAILS_ROOT}/test/fixtures/binaries/#{name}"
data = File.open(filename,'rb').read
"!binary | #{[data].pack('m').gsub(/\n/,"\n ")}\n"
end
%>
picture_data_1:
id: 1
blob_data: <%= binary_fixture_data('test1.jpg') %>
There are problems in this, I think. But it works. The problems I see are:
  1. The definition of "binary_fixture_data" is inside the YAML file, which means it must be duplicated in other fixtures that needs binaries (tried putting in in "test_helper.rb" to no luck--any other good place to put such code?)
  2. It can easily break, as it substitutes a number of indent spaces after newlines, which must conform to the level of indention where the binary data are used in the YAML file
But again, it works. And I cannot find a better solution. Again, full credit shall go to Peter Donald for this solution.

Friday, April 06, 2007

JRuby and Rails performance

I admit it, I am excited about what performance will be of Rails executing on JRuby. After all, JRuby is an interpreter of the Ruby language, but sitting on top of a virtual machine. Hmm! But then again, I have been using Matz' Ruby VM for executing my Ruby code, and that certainly is no race horse when compared to what the amazing, superb JVM can do today with all its optimization voo-doo.

Some stuff that caught my eye lately with respect to (J)Ruby execution speed:
  • If you read Charless Nutter, you will know that the JRuby project is closing in on a 1.0 release and performance is one of the things they are looking at.
  • I just saw a presentation by Gilad Bracha from Sun about their work for supporting dynamic languages on the JVM. Basically, it is a new byte-code instruction called "invokedynamic" and about hotswapping code while keeping type-safety (JSR-292)
Of cause, the JRuby project will have to rewrite their interpreter into a compiler for native JVM byte-code when (if) JSR-292 comes through on its promises, but boy, will that be cool for performance of my Rails projects deployed on the Java Platform :-)

JSR-292 is actually targeted for Dolphin, ...

Rails on JRuby makes Rails deployment a breeze

Currently, deploying a Rails application sucks. Rails not beeing thread-safe has the consequence that we must use multi-process deployment scenarios.

Well, not anymore. At least not with Rails running on the JVM using JRuby. Look at this post, where Charles Nutter discusses deployment of Rails on a Glassfish application server as a single war.

Apparently, simply by instantiating multiple JRuby runtimes inside the war file, we are able to circumvent the problem of thread-safety in the Rails framework. Just have to make sure there is only one active thread in each JRuby runtime.

Wednesday, April 04, 2007

Pruning: How deprecation in Java EE will happen

Back when the Java EE 5 specification was developed, I was wondering if application server vendors were required to support both the old-style beans and the new-style beans. Well, it turned out that they should support both, which I also think was kind of essential for all the upgrading customers with legacy applications. But for new-comers to Java EE, there is a huge legacy of old APIs, which in Java EE 5 was replaced with lightweight versions that are easier to use.

Now, in the new JSR 313 for Java EE 6, there is a proposal for something called "Pruning". An Umbrella Expert Group shall look at and decide which parts of the specification, that are to be deemed ready for pruning. This will then be noted in the current specification. The Umbrella Export Group for the next release of the Java EE specification will then decide, if the--by the former specification named--features shall be marked as "optional" in that specification. If a feature is marked "optional", the vendors are free to not include it in their products.

The specification currently under development for Java EE 6 will consider technologies, which can be marked as optional in the future Java EE 7 specification. Features already mentioned are "EJB CMP" and "JAX-RPC".

With Java EE 6 targeted for 2008 and who knows when for the Java EE 7 specification, the old "EJB CMP" specifications will definitely be with us for a long time.

c:0:Warning: require_gem is obsolete. Use gem instead.

After upgrading something in my rails environment--must be the gem tool, I think--it started giving me warnings here and there about "require_gem" being obsolete. Some of the warnings come from inside rails scripts. These can easily be fixed by searching for "require_gem" and replacing with "gem".

But my "rake" tool kept saying:
c:0:Warning: require_gem is obsolete.  Use gem instead.
Well, now I got around to fixing it. In your Ruby installation bin directory, there most probably is a rake.bat file (yes, I'm on a Windows box here). In this file, there is a small embedded Ruby script, which uses "require_gem". Fix this to "gem", and the deprecation warning goes away.