Friday, April 17, 2009

Darren on Ruby: Migrating Rails to Tomcat / JEE, Part 3: Using Warbler (Rack) to Deploy to Tomcat, Jetty or GlassFish

The Series So Far

Part 1: Switching to JRuby & Apache Derby showed you how to migrate from the Ruby+SQLite3 technology stack to JRuby+Derby.
Part 2: Migrating Your Data From SQLite3 to Derby showed you how to export your data out of your SQLite3 database and import that data into your new Apache Derby database.

Now I will take you through using Warbler to package your Rails app into a Web ARchive, ready for running on Tomcat, Jetty, Glassfish or whatever Servlet container you like.

Packaging Rails as a WAR

  1. Install Warbler:
    • jgem install warbler -y
  2. Configure Production Database:
    • Replace the production spec in RAILS_ROOT\config\database.yml with
        adapter: jdbc
        url: jdbc:derby:db\blog_ production;create=true
        driver: org.apache.derby.jdbc.EmbeddedDriver
        username: app
        password: app
  3. Satisfy Ruby Dependencies:
    • We need to make sure that all the Rails app's Ruby runtime dependencies are taken care of. We only need to worry about the dependencies that are required explicitly i.e. not provided transitively by Rails or Warbler. In our case it boils down to the JDBC/Derby stuff.
    • To specify the inclusion of these deps, we need to generate and modify the Warbler configuration; first run warble config, then edit the RAILS_ROOT\config\warble.rb file, replacing (or inserting after) this line
        # config.gems += ["activerecord-jdbcmysql-adapter", "jruby-openssl"]
      ...with this line
        config.gems += ["activerecord-jdbc-adapter", "activerecord-jdbcderby-adapter", "jdbc-derby", "jruby-openssl"]
  4. Satisfy Java Dependencies:
    • We need to make sure that all the Rails app's Java runtime dependencies are taken care of, that is, we must ensure the Derby jars will wind-up on the deployed applications CLASSPATH. Here are 3 ways to achieve this, in increasing scope:
      1. Copy derby.jar, derbyclient.jar and derbynet.jar in DERBY_HOME\lib to RAILS_ROOT\lib
      2. For Tomcat, copy these jars into TOMCAT_HOME\lib
      3. Copy them to JAVA_HOME\jre\lib\ext
      Taking the first option, Warbler will by default put these jars into WEB-INF\lib of the built WAR file.
  5. Rails 2.3.2 Session Management Issue:
    • We need to make few extra configuration changes due to an incompatibility with Warbler.
    • Uncomment the following line in RAILS_ROOT\config\initializers\session_store.rb
      # ActionController::Base.session_store = :active_record_store
    • Run the following commands to bring the database upto spec:
      jrake db:sessions:create RAILS_ENV=production
      jrake db:migrate RAILS_ENV=production
    • Now add the following line of code to the RAILS_ROOT\config\warble.rb file:
        config.webxml.jruby.session_store = 'db'
    • In RAILS_ROOT\config\environment.rb, just before do |config|
      ... add the following
      if defined?(JRUBY_VERSION)
        # hack to fix jruby-rack's incompatibility with rails edge
        module ActionController
          module Session
            class JavaServletStore
              def initialize(app, options={}); end
              def call(env); end
Now we should be good to go. Run warble which should produce blog.war. Now take this and drop it into TOMCAT_HOME\webapps. Restart Tomcat if it does not auto-deploy the application then navigate to your blog. This concludes this 3 part series on migrating from Ruby on Rails+SQLite3+WEBrick to JRuby on Rails+Derby+Tomcat. I hope through this blog I can spare you the pain I have gone through in figuring out how to get all this to work.


Next step for me is to get this procedure (of constructing the WAR file) mavenized. Maven handles WAR artifacts seemlessly; furthermore it knows how to 'overlay' WAR archives, reducing multiple WARs to one. This obviously simplifies the deployment and maintenance effort, a benefit I hope to reap in my current project that (now) produces three WAR achives. Good luck in your next steps. Cheers


Wei said...

Great article! Thanks Darren.

"Uncomment the following line in RAILS_ROOT\initializers\session_store.rb"

Do you mean RAILS_ROOT\config\initializers\session_store.rb?

Darren Bishop said...

Yes I did - thanks for pointing this out. I will update the post accordingly.

Sasikumar said...

i am using jruby-rack for rails 2.3.2 i read the note given in the your blog.

i followed the steps given in your can i retrieve that session value to java app?it have session id and data in sessions table...

i am getting that session in my jsp page as null only. can you please tell what I am missing.

in rails app -controller have this code

def index
servlet_request["hello"] = "world!"
session["rails"] = "hi going to show in java"
forward_to "/attributes.jsp"

my source code in jsp page is

i am getting o/p as

session["rails"] | session.getAttribute("rails")

can you please tell me what i am missing?