JRuby 1.1.6: Gems-in-a-jar

Posted by Nick Sieger Sat, 10 Jan 2009 20:04:00 GMT

As a result of some fruitful hacking at RubyConf 2008, I was able to modify JRuby so that gems can be loaded and used without having to unpack them. The feature became generally available with the 1.1.6 release last month. Gems in a jar!

gemjar

by splorp on flickr

This opens up a couple new possibilities for running, packaging and deploying JRuby-based applications. Here are some ideas:

Run Gem-based applications with jruby-complete.jar

JRuby has bundled Rake and RSpec since version 1.0. As of JRuby 1.1.6 the versions we bundle are Rake 0.8.3 and RSpec 1.1.11. With jruby-complete-1.1.6.jar you can easily run these with java -jar:

$ java -jar jruby-complete-1.1.6.jar -S rake --help
rake [-f rakefile] {options} targets...

Options are ...

$ java -jar jruby-complete-1.1.6.jar -S spec --help
Usage: spec (FILE|DIRECTORY|GLOB)+ [options]

    -p, --pattern [PATTERN] ...

Package gem collections into reusable jar files

One of the features I added was to enhance RubyGems to search for directories named specifications on the classpath and add them to the Gem.path automatically. This means you can package up a whole gem repository into a jar file for easy reuse and sharing of commonly used gems. There isn’t a tool for this yet, but the process is pretty straightforward. (If someone plays with this and can come up with a patch to build this into JRuby, we’ll gladly accept one.)

First, create a gem repository by installing the gems you want into it. Let’s say you want to package the natural language date/time parser chronic:

$ java -jar jruby-complete-1.1.6.jar -S gem install -i ./chronic-gems chronic --no-rdoc --no-ri
Successfully installed rubyforge-1.0.2
Successfully installed rake-0.8.3
Successfully installed hoe-1.8.2
Successfully installed chronic-0.2.3
4 gems installed

With this command, RubyGems created chronic-gems/bin, chronic-gems/cache, chronic-gems/gems, and chronic-gems/specifications directories and installed chronic and its dependencies into it. Now, simply package those directories into a jar file. WARNING: make sure you don’t give the jar file name the same basename as the library or gem inside you wish to use (e.g., chronic.jar), because JRuby will load chronic.jar when you require 'chronic'!

$ jar cf chronic-gems.jar -C chronic .

When you inspect the contents of the jar, you’ll see the gem repository structure in the root of the jar file:

$ jar tf chronic-gems.jar | head
META-INF/
META-INF/MANIFEST.MF
bin/
bin/rake
bin/rubyforge
bin/sow
cache/
cache/chronic-0.2.3.gem
cache/hoe-1.8.2.gem
cache/rake-0.8.3.gem

Chronic is now a re-useable jar library that can be easily loaded, either by requiring the jar in ruby code or adding to the classpath:

$ # Without chronic-gems.jar
$ java -jar jruby-complete-1.1.6.jar -S gem list

*** LOCAL GEMS ***

rake (0.8.3)
rspec (1.1.11)
sources (0.0.1)

$ # With chronic-gems.jar
$ java -jar jruby-complete-1.1.6.jar -rchronic-gems.jar -S gem list

*** LOCAL GEMS ***

chronic (0.2.3)
hoe (1.8.2)
rake (0.8.3)
rspec (1.1.11)
rubyforge (1.0.2)
sources (0.0.1)

Bundle pure-Ruby gem applications into an uber-jar

Taking a cue from the previous techniques, we can stuff the gem directories into a copy of jruby-complete-1.1.6.jar rather than creating a new jar, and distribute an entire gem-based application in a single file. Imagine something like:

$ java -jar jruby-complete-1.1.6.jar -S gem install -i ./mycoolapp mycoolapp
$ jar uf jruby-complete-1.1.6.jar -C mycoolapp .
$ mv jruby-complete-1.1.6.jar mycoolapp.jar
$ java -jar mycoolapp.jar -S mycoolapp

Bonus points to the enterprising individual who provides a patch to make this a one-step process, including creating a mechanism to provide a default -S script so that java -jar mycoolapp is all that’s needed to run the application.

I hope you find interesting uses for this new feature. Let us know what you make with it!

Tags ,

Comments

  1. Avatar James Britt said about 2 hours later:

    This seriously rules. Thanks!

  2. Avatar Andy Shen said about 13 hours later:

    I started to try to make use of activerecord’s schema migration in Java projects . Before this post I had to require users to set GEM_PATH but now I modified the jruby-complete jar as you suggested and that should make deploying the tool much easier :)

    Thanks Nick.

  3. Avatar Jay Phillips said about 23 hours later:

    This is awesome, Nick! A few questions:

    • How well does ActiveRecord play when running in a .jar? I heard that the ActiveRecord’s model reloading logic broke down at one point due to the JAR filesystem using URIs for its files.
    • How different is the .ear format from .jar and...
    • ...does this now mean I can deploy Adhearsion apps as an .ear file into a JEE container? :)
  4. Avatar Nick said 2 days later:

    Jay: I haven’t personally tried running a lot of gems out of a jar file yet. JRuby is not yet fully consistent with all its file APIs in knowing how to manipulate file:// urls. For example, the common practice of require File.dirname(__FILE__) + '/../relpath' is probably not going to work. If you try stuff, please file bugs!

    As for deploying Adhearsion apps in an .ear, that probably deserves a larger discussion in #jruby. You need to tell me what kind of services and interfaces Adhearsion needs and provides, and we can figure out whether it should be in an .ear, a .war, or possibly just a standalone application (something like the last example).