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.
- a JDK
- the brand new JRuby 22.214.171.124
- the Tomcat 8 servlet container
- and, of course, Redmine, version 3.1
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.
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)
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
JRUBY_HOME with the directory where your JRuby now actually is. Check if it
$ jruby -v jruby 126.96.36.199.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.
$ sudo ruby-install jruby 188.8.131.52 $ chruby jruby-184.108.40.206 $ 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
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
source /usr/local/share/chruby/chruby.sh chruby jruby-220.127.116.11
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.
Get Redmine, either by downloading the latest stable release from Redmine.org, or from github:
git clone firstname.lastname@example.org: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.
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
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 18.104.22.168 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
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
edit it. At the very least you should configure the various email settings, and
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
Next is to have Redmine use Tomcat’s native database connection pool, but I’ll leave that for another post.