Jens Krämer

Running Redmine on Tomcat with JRuby

 |  jruby, tomcat, ruby, rails, redmine

Today I’ll show how to get a relatively complex Rails application (here: the Redmine project management app) up and running in a Java Application server.

Involved are:

To a certain degree you will be able to deploy to JBoss or any other container by following this advice, too. You should also be able to adapt most of the following to other Rails apps.

Java

If you are only remotely interested in running Redmine on JRuby you probably already have Java installed and can skip this step. In short: for Mac or Windows, get the latest JDK from Oracle, on Linux, go there as well, or use your package manager to install the latest openJDK.

Check it’s working:

$ java -version
java version "1.7.0_79"
OpenJDK Runtime Environment (IcedTea 2.5.5) (7u79-2.5.5-1~deb7u1)
OpenJDK 64-Bit Server VM (build 24.79-b02, mixed mode)

JRuby

Go to jruby.org and download the latest stable release of the JRuby 9 series.

Unzip it somewhere, and add JRUBY_HOME/bin to your PATH, replacing JRUBY_HOME with the directory where your JRuby now actually is. Check if it works:

$ jruby -v
jruby 9.0.0.0.rc2 (2.2.2) 2015-07-09 ff331eb OpenJDK 64-Bit Server VM 24.79-b02 on 1.7.0_79-b14 +jit [linux-amd64]

How to Handle Multiple Ruby Versions

It’s a bit out of scope for this article, but if for whatever reason you are working with more than one version of Ruby, it really pays off to invest some time in a decent Ruby version management and switching setup.

There are many ways to do this, personally I prefer to install Rubies via ruby-install, and switch between Ruby versions with chruby:

$ sudo ruby-install jruby 9.0.0.0
$ chruby jruby-9.0.0.0
$ ruby -v

This way I have all Ruby versions globally available in /opt/rubies, and chruby takes care of the path wrangling to make the desired ruby command (and any related commands like gem, bundle etc) available.

To automate things further I use direnv for switching to the desired version when I enter any project directory. After installing direnv, simply create a file named .envrc in your project directory, which will then be executed whenever you cd there:

source /usr/local/share/chruby/chruby.sh
chruby jruby-9.0.0.0

This approach works equally well on OSX and Linux and makes handling different projects with different Ruby version requirements absolutely effortless.

An alternative approach is to use RVM, which puts all of the above (and a lot more) in a single tool.

Redmine

Get Redmine, either by downloading the latest stable release from Redmine.org, or from github:

git clone git@github.com:redmine/redmine.git
cd redmine
git checkout -b 3.1-stable origin/3.1-stable

cp config/database.yml.example config/database.yml

Edit database.yml to suit your needs, also create dev, production and test databases. We will use the development db for running the app on jruby but outside tomcat, and the production db for the deployed app running in tomcat.

gem install bundler
bundle install
bundle exec rake db:migrate
bundle exec rake generate_secret_token

You will see a warning about incomplete support for AR 4.2 by the AR jdbc adapter, but from looking at the linked github issue it appears to be mostly there, especially for the more common rdbms like mysql and postgresql, so there are no problems to be expected.

If you study the Redmine Installation Manual, you will notice that besides the potential JDBC issues, the Loofah library is mentioned as a show stopper since it is required by Rails 4.2, but does not work properly with JRuby. You can read more about this issue in a previous article of mine, the short version is that by using an alternative HTML sanitizer you don’t have to care about Loofah anymore.

To do so, create a file named Gemfile.local including the external Sanitizer:

platforms :jruby do
  gem 'rails-html-sanitizer-jruby', github: 'jkraemer/rails-html-sanitizer-jruby'
end

Don’t forget to run bundle install to actually install the additional gem after making that change.

You are now ready to give Redmine on JRuby a first test drive by firing up the development environment:

bundle exec rails s

Running the test suite takes a while, but should complete without failures. Redmine’s test suite is quite large, if you are hit by one of the JDBC adapter issues mentioned above you should notice it now.

bundle exec rake db:migrate RAILS_ENV=test
bundle exec rake

Building a WAR and deploying to Tomcat

No need to be afraid, this is surprisingly easy.

Install Tomcat

Download the latest release from tomcat.apache.org and unpack the archive at a convenient location. For the rest of the article I’m assuming tomcat resides in ~/apache-tomcat-8.0.24.

Fire up tomcat:

$ cd ~/apache-tomcat-8.0.24
$ bin/startup.sh
... some environment variables
Tomcat started.

If you now point your browser to localhost:8080, you should see the default tomcat welcome page.

Build and deploy the WAR file

WAR files are just glorified ZIP archives with a somewhat standardized directory layout. If everything works as intended, they make delivering / deploying a web application a breeze - you drop the file into the application server and are done. As always the devil’s in the details, but for our simple scenario we come very close.

Warbler is a Ruby gem that will turn your Rails (or, more generally speaking, Rack) app into a single, ready-to-deploy WAR file. It looks at your Gemfile, collects all dependencies (including JRuby itself) and adds some meta data as well as JRuby-Rack which adapts the Java Servlet API to Rack.

First, add Warbler to your Gemfile.local. In order to get JRuby 9.0.0.0 support we have to use Warbler 2.0 which as of this writing is available as a pre-release version only:

platforms :jruby do
  gem 'rails-html-sanitizer-jruby', github: 'jkraemer/rails-html-sanitizer-jruby'
  gem 'warbler', '2.0.0.pre3', group: :development
end

Bundle install should pull down warbler and some dependencies. Then we let Warbler generate its config file:

bundle install
bundle exec warble config

That generates config/warble.rb, which needs to be adjusted slightly so Redmine’s Gemfile.local is included:

# Additional files/directories to include, above those in config.dirs
config.includes = FileList["Gemfile.local"]

Before building the WAR file you should also have a look at Redmines configuration. There is a sample config file located in config/configuration.yml.sample, copy that to config/configuration.yml and edit it. At the very least you should configure the various email settings, and set attachments_storage_path to a directory where uploaded files should go. By default this is Rails.root/files, which would, depending on how the application server handles WAR files, be either inside the WAR file (usually this is not writable by the application), or, if the server unpacks WAR files (which Tomcat does by default), inside $TOMCAT_HOME/webapps/redmine/, which is most probably writable, but still not suitable for a production setup.

With that out of the way, it’s time to build a WAR archive:

warble war
cp redmine.war $TOMCAT_HOME/webapps

Now that was easy! Redmine should now run in Tomcat and be reachable at localhost:8080/redmine.

Next is to have Redmine use Tomcat’s native database connection pool, but I’ll leave that for another post.

Comments

Yuan

I encountered an error. How to fix it?

[sysadmin@yftestserver redmine-3.1.1]$ warble war
warble aborted!
TypeError: no implicit conversion from nil to integer
/home/sysadmin/.gem/jruby/2.2.2/gems/warbler-2.0.0.rc1/lib/warbler/traits/bundler.rb:129:in `relative_from_pwd'
/home/sysadmin/.gem/jruby/2.2.2/gems/warbler-2.0.0.rc1/lib/warbler/traits/bundler.rb:76:in `add_bundler_gems'
/home/sysadmin/.gem/jruby/2.2.2/gems/warbler-2.0.0.rc1/lib/warbler/traits/bundler.rb:31:in `after_configure'
/home/sysadmin/.gem/jruby/2.2.2/gems/warbler-2.0.0.rc1/lib/warbler/traits.rb:33:in `block in after_configure'
/home/sysadmin/.gem/jruby/2.2.2/gems/warbler-2.0.0.rc1/lib/warbler/traits.rb:33:in `after_configure'
/home/sysadmin/.gem/jruby/2.2.2/gems/warbler-2.0.0.rc1/lib/warbler/config.rb:213:in `initialize'
/home/sysadmin/.gem/jruby/2.2.2/gems/warbler-2.0.0.rc1/lib/warbler/task.rb:48:in `initialize'
/home/sysadmin/.gem/jruby/2.2.2/gems/warbler-2.0.0.rc1/lib/warbler/application.rb:27:in `load_rakefile'
/home/sysadmin/.gem/jruby/2.2.2/gems/warbler-2.0.0.rc1/lib/warbler/application.rb:77:in `run'
/home/sysadmin/.gem/jruby/2.2.2/gems/warbler-2.0.0.rc1/bin/warble:11:in `'
/home/sysadmin/.gem/jruby/2.2.2/bin/warble:1:in `'
/home/sysadmin/.gem/jruby/2.2.2/bin/jruby_executable_hooks:15:in `'

Jens

Strange. I tried with the exact same version of Warbler but couldn't reproduce that. Maybe try to check with the warbler guys?