Rails WAR with a Tomcat
{Monday, March 5th, 2007}Want to deploy a Rails app WAR on an application server and confused how to go about it? In this blog I’ll summarise how you can create a WAR. This information is ‘out there’ but so disjointed you’ll rightly give-up before you try, to make matters worse it’s usually specific to GlassFish.
Here I’ll focus on deploying to Tomcat using Java 5.
Download JRuby
In my case I checked out the trunk although using 0.9.2 may be an option. Note that you’ll need to perform a couple of modifications if you intend on using JRuby as a substitute for Ruby:
- Add rake.bat to the bin directory. I quickly threw one together based on gem.bat. Note that there are issues executing rake from the bin directory.
- Place JRuby’s bin directory before Ruby’s bin on your OS path
- Install all of your dependent gem’s in JRuby. Logically the ‘JRuby Way’ is identical to Rubys, gem install
If you need to create the JRuby jar, ant jar constructs jruby.jar in the lib directory.
I had an issue successfully executing the tests, the ANT jar provided in the lib directory appeared to be incorrect (org.apache.tools.ant.util.FileUtils.close(Writer) was not found). Replacing this jar with the latest solved the problem.
Download Rails-Integration
Rails-Integration is part of the JRuby Extras project. Unfortunately, it hasn’t been released yet so you’ve no alternative but to grab the package off the trunk.
Rails-Integration has two key parts:
- Servlets that create a Ruby Runtime and delegate to the Rails dispatcher
- Rake tasks used to compose a Rails application WAR
You’ll need to use ANT to JAR-up those servlets: ant jar should do the trick.
Note that the jar task causes tests to execute which will probably initially fail and kill JAR creation. You’ve a few options here:
- Alter the build so that tests are not run
- Correct the failing tests
I ended up taking the latter option to ensure that my snapshot was legitimate. After tweaking with library dependencies I managed to get green lights here.
Download Active-Record JDBC integration
- Install the ActiveRecord-JDBC integration library in JRuby, gem install ActiveRecord-JDBC should do the trick
- Add the following to environment.rb before Rails::Initializer.run do |config|:
if RUBY_PLATFORM =~ /java/ require 'rubygems' RAILS_CONNECTION_ADAPTERS = %w(jdbc) end - Change database.yml to use JDBC for connection management. Something like the following:
adapter: jdbc driver: com.mysql.jdbc.Driver url: jdbc:mysql://<host>/<database> username : <username> password: <password>
Customise Rails
As all of this stuff is experimental (some pre-release, all pre-version 1.0) nothing comes easy. You’ve some tweaking to do. The Rails libraries dispatcher.rb can’t be interpreted correctly by JRuby at the time of writing.
I needed to comment out all lines pertaining to breakpoints as the constant BREAKPOINT_SERVER_PORT (a constant unassociated with a module) could not be interpreted. This is not so bad given Breakpoint/Debug Driven Development should be discouraged.
Create WAR
There are 2 packaging strategies for your Rails application:
- Dependent gems bundled in the WAR
- Access dependent gems by configuring JRuby’s home directory
I preferred the latter as it significantly reduces the WAR’s payload, but the foremost option is reasonable in cases where you’d like to drop the WAR and run.
Here’s the key steps to create the WAR:
- Copy the Rails-Integration war plugin to your Rails application root directory
- Alter the plugins war_config.rb, specifying all dependent JAR’s. All JAR’s are expected to be located in your Maven repository by default. Note that JRuby appears to hijack classloading such that it can only resolve classes via the applications classloader, bypassing the server classloader for some reason unknown to me. For Tomcat, this lead to failures in loading Tomcat specific classes. To quickly remedy this I included catalina.jar in the WAR. You’ll also need to include a jar containing your JDBC driver.
- (Bundle option only) Alter the plugins war_config.rb to specify all dependent gem’s
- All your Rails applications directories will be archived in the WAR by default, to reduce the payload alter the plugins packer.rb WebappPacker class to filter out directories such as test and doc
- Create a dependent WAR using rake create_war:standalone or rake create_war:shared to exclude dependent gems
If you’ve followed to this point you should have a WAR in your Rails applications root directory. Drop it in your app server and cross your fingers!
Tip: You should also be able to jruby script\server your application to life via WEBrick, this can be useful to iron out configuration issues.
Related information:
JRuby on Rails
Rails on Glassfish
Rails with ActiveRecord JDBC
