• Like
A jar-nORM-ous Task
Upcoming SlideShare
Loading in...5

Thanks for flagging this SlideShare!

Oops! An error has occurred.

A jar-nORM-ous Task


How to deploy a JRuby app as a single .jar file, when it relies on libraries like ActiveRecord and Sinatra.

How to deploy a JRuby app as a single .jar file, when it relies on libraries like ActiveRecord and Sinatra.

Published in Technology , Self Improvement
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads


Total Views
On SlideShare
From Embeds
Number of Embeds



Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

    No notes for slide


  • 1. a jar-nORM-ous task simple data-backed web app in a single jar Ian Dees www.ian.dees.name ● @undees JRubyConf 2009 Hi. I’m Ian, a JRuby user. This talk is about deploying a Ruby app in a single .jar file. When you’re leaning on big frameworks like ActiveRecord, there are a few things to remember when you package your app.
  • 2. CRUD - CUD Imagine you’re handed some database credentials, and you want to whip up a web front end in Sinatra to browse around the data. Let’s say the schema is simple and fairly normalized: the sweet spot for ActiveRecord. Photo (cc) flickr.com/photos/treehouse1977/1425246464
  • 3. define “success” images and stylesheets A B single .jar C app.jar + jruby.jar D “Install Ruby....” F “Install....” MRI and MySQL present some deployment challenges, even when everyone is on the same platform. With JRuby, you can just hand out a couple of .jar files and have people run “java -jar app.jar” (assuming everyone has a recent JVM). For bigger deployments, you can use Warbler to make a .war file—but that requires configuring an application server: definitely overkill for small projects.
  • 4. 1-2-3-4 1. vendor everything 2. extract jars from your binary gems 3. let rawr do the heavy lifting 4. combine your jar with jruby-complete The “rawr” project provides some recipes for packing Ruby code into a .jar, with a small Java bootstrapper. Here are the four basic steps involved.
  • 5. 1. vendor everything # This file is auto-generated; # use 'rake gems:vendor' to update it. TE %w(activerecord-2.3.4 L activerecord-jdbc-adapter-0.9.2 E activesupport-2.3.4 SO activerecord-jdbcsqlite3-adapter-0.9.2 haml-2.2.13 O jdbc-sqlite3- rack-1.0.1 B sinatra-0.9.4).each do |dir| $: << File.dirname(__FILE__) + "/#{dir}/lib" end Trying to disentangle the notion of a GEM_HOME from the weird file paths inside .jars is... nontrivial. You can avoid the fuss by just putting your dependencies into the .jar as plain old Ruby libraries, a bit like Rails’ vendored gems.
  • 6. 1 (revised). use Bundler bundle_path 'lib/ruby' bin_path 'lib/ruby/bin' gem 'activerecord' gem 'activerecord-jdbc-adapter' gem 'activerecord-jdbcsqlite3-adapter' gem 'activesupport' gem 'haml' gem 'jdbc-sqlite3' gem 'rack' gem 'sinatra' The Bundler library can automate this step for you.
  • 7. 2. binary gems desc 'Extract jars from our gems into staging area' task :unjar => 'package/classes' do Dir['lib/ruby/**/*.jar'].each do |jar| path = File.expand_path(jar) Dir.chdir 'package/classes' do sh "jar -xf #{path}" end end end Some gems, notably including ActiveRecord database adapters, contain .jars. Java can’t load these from within another .jar. One workaround is to extract the .class files out of the sub-.jars for inclusion in your project. (Another approach is to use something like JarJar.)
  • 8. 3. rawr # build_configuration.rb configuration do |c| # ... c.source_dirs = ['src', 'lib/ruby'] c.source_exclude_filter = Dir['lib/ruby/**/*.java'].map do |file| Regexp.new( File.basename(file).gsub(".", ".") + "$") end # ... end The default configuration of rawr gets confused by gems that include .java files. The easiest thing to do is tell it to skip these files. It would be nice if rawr’s configuration file took a glob-style file pattern, but for now, you can make do with an array of regular expressions.
  • 9. 4a. unjar desc 'Extract app and jruby-complete for later combining' task :stage => 'package/bigjar/contents' do Dir.chdir('package/bigjar/contents') do sh 'jar -xf ../../jar/appapp.jar' sh 'jar -xf ../../jar/lib/java/jruby-complete.jar' end end At this point, you’ve got your app.jar and a stock jruby-complete.jar. That fits the “C” grade on our success criteria, so we could stop here. But it’s not too big of an extra step to combine those into a single .jar. That should earn us at least a “B.” First, extract them into the same directory.
  • 10. 4b. manifest desc 'Point the big jar manifest at our app' task :manifest do manifest = IO.read 'package/bigjar/contents/META-INF/MANIFEST.MF' manifest.gsub! /^Main-Class: .+$/, 'Main-Class: org.rubyforge.rawr.Main' File.open('package/bigjar/manifest', 'w') do |f| f.write manifest end end Then, change the main class in the manifest from the JRuby interpreter to the rawr-generated bootstrapper.
  • 11. 4c. combine desc 'Combine staged app and jruby-complete files into one jar' task :package do Dir.chdir('package/bigjar') do sh 'jar -cfm appapp.jar manifest -C contents/ .' end end Finally, combine them all into one jar.
  • 12. 5-6-7-8 task :default => %w( gems:bundle gems:unjar rawr:jar app:stage app:manifest app:package) Just to run this idea into the ground, here are those four basic steps again, expressed as Rake tasks.
  • 13. } /undees/appapp An example of this technique is available on GitHub and Bitbucket. The demo app is a silly web front end around some iPhone app store data. To run it, you’ll need to download the app store CSV data provided by busted-loop.com and run a couple of Rake tasks to populate your database first. See the Rakefile for details.
  • 14. overall grade: B- A single .jar, with no embedded stylesheets or images, is nearly enough to earn a solid B according to our success criteria. All that’s left to do is fix one minor quirk (the app currently has to be run from a subfolder of the working directory).