Deploy a Rails 3, Sqlite3 application in Tomcat using JRuby

and have a Ruby version running side-by-side.

A few months ago I got interested in JRuby while researching for text mining algorithms. I found some gems but they are either unmaintained or inadequate while the mature libraries I found were written in Java. No problem! JRuby to the rescue. Thank God.

Next stop, I decided to take Rails 3 and JRuby for a spin. Incidentally, I will be on a 3-city Rails tour in the Philippines this September and since there are many Filipino Java developers, they might find it interesting to see their favorite Java platform works nicely with Ruby on Rails.

Setup

I will be using the following for this tutorial:

java 1.6 + JDK
tomcat 7.0.2
rvm 1.0.1
jruby 1.5.0
ruby 1.9.2p0

Further below, I outline how to install these software. First, let’s see my current environment.

  $ more /etc/issue
  Ubuntu 9.10 \n \l
  
  $ java -version
  java version "1.6.0_20"
  Java(TM) SE Runtime Environment (build 1.6.0_20-b02)
  Java HotSpot(TM) Server VM (build 16.3-b01, mixed mode)
  
  $ rvm -v
  rvm 1.0.1 by Wayne E. Seguin (wayneeseguin@gmail.com) [http://rvm.beginrescueend.com/]
  
  $ jruby -v
  jruby 1.5.0 (ruby 1.8.7 patchlevel 249) (2010-05-12 6769999) (Java HotSpot(TM) Client VM 1.6.0_20) [i386-java]
  
  $ TOMCAT/bin/version.sh 
  Using CATALINA_BASE:   /usr/local/apache-tomcat-7.0.2
  Using CATALINA_HOME:   /usr/local/apache-tomcat-7.0.2
  Using CATALINA_TMPDIR: /usr/local/apache-tomcat-7.0.2/temp
  Using JRE_HOME:        /usr
  Using CLASSPATH:       /usr/local/apache-tomcat-7.0.2/bin/bootstrap.jar:/usr/local/apache-tomcat-7.0.2/bin/tomcat-juli.jar
  Server version: Apache Tomcat/7.0.2
  Server built:   Aug 4 2010 12:23:47
  Server number:  7.0.2.0
  OS Name:        Linux
  OS Version:     2.6.31-22-generic
  Architecture:   i386
  JVM Version:    1.6.0_20-b02
  JVM Vendor:     Sun Microsystems Inc.
  
  $ ruby -v
  ruby 1.9.2p0 (2010-08-18 revision 29036) [i686-linux]
  
  # install jdk and tomcat
  
  $ aptitude install curl sun-java6-bin sun-java6-jre sun-java6-jdk
  $ wget  http://apache.mobiles5.com/tomcat/tomcat-7/v7.0.2-beta/bin/apache-tomcat-7.0.2.tar.gz
  $ tar zxvf apache-tomcat-7.0.2.tar.gz
  $ mv apache-tomcat-7.0.2 /usr/local
  

Of course, these assume you want to use 7.0.2 and you want it installed at your /usr/local.

Install JRuby, Rails 3

I assume you already have rvm installed. If not, I highly recommend that you do. I can’t imagine a Ruby developer not using rvm 🙂

  $ rvm install jruby
  $ rvm jruby
  $ rvm gemset create railsjam
  $ rvm jruby@railsjam
  $ gem install rails

Try a sample app

I’ve created sample app for the RailsJam tour. This have several functionalities already and better than creating a Rails app from scratch.

$ git clone git://github.com/gregmoreno/railsjam.git

Update the Gemfile

You need a separate set of gems to make your Rails 3 application work with JRuby. For learning purposes, I want my Rails 3 application to work other than JRuby. To accomplish that, we need to specify what gems are needed solely by JRuby.

  source 'http://rubygems.org'
  
  gem 'rails', '3.0.0'
  
  if defined?(JRUBY_VERSION)
    gem 'jdbc-sqlite3'
    gem 'activerecord-jdbc-adapter'
    gem 'activerecord-jdbcsqlite3-adapter'
    gem 'jruby-openssl'
    gem 'jruby-rack'
    gem 'warbler'
  else
    gem 'sqlite3-ruby', :require => 'sqlite3'
  end

(A copy of this Gemfile is available at the ‘jruby’ folder of the railsjam application.)

Now, it’s time to intall the gems. You must delete ‘Gemfile.lock’. Otherwise, bundle picks up wrong version of jdbc

  $ rm Gemfile.lock  
  $ jruby -S bundle install

Prepare the database.

The first time I worked on this tutorial, I needed to specify the jdbcsqlite3 as the database adapter. However, when I tried the tutorial on the same machine with a fresh gemset, it worked pretty well with just ‘sqlite3’. Just to be sure, I modified ‘database.yml’ to check for JRuby.

  development:
    adapter: <%= defined?(JRUBY_VERSION) ? 'jdbcsqlite3' : 'sqlite3' %>
    database: db/development.sqlite3
    pool: 5
    timeout: 5000
  
  production:
    adapter: <%= defined?(JRUBY_VERSION) ? 'jdbcsqlite3' : 'sqlite3' %>
    database: /home/greg/dev/railsjam/db/development.sqlite3 
    pool: 5
    timeout: 5000

When you deploy to Tomcat, it will be on ‘production’ mode by default. Since sqlite3 is file based and for simplicity, I used the same development database.

Now, do the migration.

  $ jruby -S rake db:migrate

Deploy to Tomcat

We use ‘warble’ which is an excellent tool for packaging your Rails application. It packages everything you need to run your Rails application inside a Java container.

  $ warble
  $ cp railsjam.war  $TOMCAT/webapps
  
  # start Tomcat
  # assuming you arein $TOMCAT dir
  $ sudo ./startup.sh

Check your Rails 3 application

  # You should see the famous Rails welcome
  localhost:3000/railsjam
  
  # Play around with your application
  localhost:3000/railsjam/users

Deploy Rails 3 using Ruby 1.9.2

Without shutting down your JRuby and Tomcat version, let’s try to run our app using Ruby 1.9.2

  # In a new console
  $ rvm 1.9.2
  $ rvm gemset create railsjam
  $ rvm 1.9.2@railsjam
  $ gem install rails
  
  # Assuming you are in the ‘railsjam’ folder
  # This will install sqlite3-ruby gem
  $ bundle install
  
  $ rails server
  
  Now, go play with your Rails 3 applications
  
  # jruby + tomcat
  http://localhost:8080/railsjam/users
  
  # ruby 1.9.2
  http://localhost:3000/users

In case you encountered some problems, here are some ways to solve them. If your problem is not listed here, you can email me. I only accept Paypal 🙂

JRuby does not support native extensions

You did not update the Gemfile to use the jdbc version of sqlite3. You will encounter this error when you install the gems.

   
  $ bundle install
  ....
  Installing sqlite3-ruby (1.3.1) with native extensions /home/greg/.rvm/rubies/jruby-1.5.2/lib/ruby/site_ruby/1.8/rubygems/installer.rb:482:in `build_extensions': ERROR: Failed to build gem native extension. (Gem::Installer::ExtensionBuildError)
  
  /home/greg/.rvm/rubies/jruby-1.5.2/bin/jruby extconf.rb 
  WARNING: JRuby does not support native extensions or the `mkmf' library.
           Check http://kenai.com/projects/jruby/pages/Home for alternatives.
  extconf.rb:9: undefined method `dir_config' for main:Object (NoMethodError)

undefined method `attributes_with_quotes’ for class `ActiveRecord::Base’

I first encountered this problem when doing migration.

 
  $ rake db:migrate
  rake aborted!
  undefined method 'attributes_with_quotes' for class 'ActiveRecord::Base'

This is caused by an old version of your jdbc gems. In my case, sometimes bundler installs the old versions:

  Installing activerecord-jdbc-adapter (0.9.2) 
  Installing activerecord-jdbcsqlite3-adapter (0.9.2)

As of this writing, the latest version is 0.9.7

  Installing activerecord-jdbc-adapter-0.9.7-java
  Installing activerecord-jdbcsqlite3-adapter-0.9.7-java

Bundler keeps installing 0.9.2

  
  $ rm Gemfile.lock
  $ jruby -S bundle install

no such file to load — sqlite3

  $ rake db:migrate
  (in /home/greg/dev/projects/jruby/railsjam)
  rake aborted!
  no such file to load -- sqlite3

‘sqlite3’ is the default name of the database adapter but with jruby, it should be ‘jdbcsqlite3’.
But, when I tried ‘sqlite3’ with a fresh gemset and a new machine, it went well.
Anyway, just in case you run into the same problem in the future, add a condition
in your database.yml

  development:
    adapter: <%= defined?(JRUBY_VERSION) ? 'jdbcsqlite3' : 'sqlite3' %>
    database: db/development.sqlite3
    pool: 5
    timeout: 5000
  
  production:
    adapter: <%= defined?(JRUBY_VERSION) ? 'jdbcsqlite3' : 'sqlite3' %>
    database: /home/greg/dev/railsjam/db/development.sqlite3 
    pool: 5
    timeout: 5000

We’re sorry, but something went wrong.

If you see the famous Rails error message, you need to dig in Tomcat’s log files.

  $ cd /usr/local/apache-tomcat-7.0.2/logs
  $ ls -al localhost*
  
  -rw-r--r-- 1 root root 1181 2010-09-01 00:17 localhost.2010-09-01.log
  -rw-r--r-- 1 root root 1062 2010-09-01 00:18 localhost_access_log.2010-09-01.txt
  
  $ tail -f localhost.2010-09-01.log 

In the log file, you will see the errors like missing database.

  org.jruby.rack.RackInitializationException: The driver encountered an error: java.sql.SQLException: path to '/home/greg/dev/tmp/apache-tomcat-7.0.2/webapps/railsjam/WEB-INF/db/production.sqlite3': '/home/greg/dev/tmp/apache-tomcat-7.0.2/webapps/railsjam/WEB-INF/db' does not exist