Ahead-of-time compiling JRuby, packaging a Java Jar, creating a Gemspec, using Warbler

In this blog post I'll share some code to demonstrate ahead-of-time compiling and packaging JRuby code into a standalone Java Jar. This functionality is nice if you develop locally via RVM, and want to deploy to a JVM server.

Created file:.ruby-version

jruby-1.7.4

Created file: .ruby-gemset

jruby_jar

Created file: Gemfile

source 'https://rubygems.org'

#gem 'warbler'
# ^ LoadError: no such file to load -- zip/zip:
gem 'warbler', :git => 'git://github.com/jruby/warbler.git'

Executed bundle to install required gems

$ bundle

Created main module, file: lib/jruby-jar-example.rb

require 'jruby-jar-example/some_class'

module JRubyJarExample

  #PATH = File.expand_path(File.dirname(__FILE__))

  extend self

  def my_var
    @my_var ||= get_my_var
  end

  def my_class_var
    @my_class_var ||= get_my_class_var
  end

  private

  def get_my_var
    'JRubyJarExample var'
  end

  def get_my_class_var
    SomeClass.my_var
  end

end

Created sample class, file: lib/jruby-jar-example/some_class.rb

module JRubyJarExample

  class SomeClass

    def self.my_var
      @my_var ||= get_my_var
    end

    class << self

      private

      def get_my_var
        'JRubyJarExample::SomeClass var'
      end

    end

  end

end

Created main bin file, file: bin/jruby-jar-example-main.rb. This file will be the main bin file that drives the Jar execution.

#!/usr/bin/env jruby

# include rubygems
require 'rubygems'

# load main module conditionally
require "#{File.expand_path(File.dirname(__FILE__))}/../lib/jruby-jar-example" unless Module.const_defined? 'JRubyJarExample'

# do stuff here
puts JRubyJarExample.my_var
puts JRubyJarExample.my_class_var

Created a gemspec file, file: jruby-jar-example.gemspec

Gem::Specification.new do |s|
  s.name             = 'jruby-jar-example'
  s.version          = '1.0.0'

  s.authors          = ['Eric London']
  s.date             = '2013-10-01'
  s.description      = 'JRuby Jar Example'
  s.email            = ['ericlondon@example.com']
  s.homepage         = 'http://ericlondon.com'
  s.require_paths    = ['lib','bin']
  s.rubygems_version = '1.8.24'
  s.summary          = 'JRuby Jar Example'

  s.files            = Dir.glob("{bin,lib}/**/*")
  #s.licenses        = ['MIT']

  s.executables      = ['jruby-jar-example-main.rb']
end

[Optional] Now that the gemspec defines the project, it can be built and tested via IRB:

$ gem build jruby-jar-example.gemspec
$ gem install ./jruby-jar-example-1.0.0.gem

$ gem list

*** LOCAL GEMS ***

bundler (1.3.5)
jruby-jar-example (1.0.0)
jruby-jars (1.7.4)
jruby-rack (1.1.13.2)
rake (10.1.0)
rubyzip (1.0.0, 0.9.9)
warbler (1.3.8)

$ irb
jruby-1.7.4 :001 > require 'jruby-jar-example'
 => true
jruby-1.7.4 :002 > JRubyJarExample.my_var
 => "JRubyJarExample var"
jruby-1.7.4 :003 > JRubyJarExample.my_class_var
 => "JRubyJarExample::SomeClass var"

Warbler will use a gemspec if it exists. If you didn't create one, or need to modify additional settings, you can execute "warble config" to generate a config/warble.rb file to override project settings.

At this point the code can be compiled and packaged into a Java jar using warble:

$ warble compiled jar
rm -f jruby_jar.jar
Creating jruby_jar.jar
rm -f bin/jruby-jar-example-main.class lib/jruby-jar-example.class lib/jruby-jar-example/some_class.class

Your Jar can now be executed or deployed to a JVM:

$ java -jar jruby_jar.jar
JRubyJarExample var
JRubyJarExample::SomeClass var

View source code on Github