By Sergey Gernyak, Back-end Engineer.
Some time ago, we started to move one of our Rails projects from MRI to a jRuby platform. Why we did that, what goals we set and what we expected from the change is not really the reason for this post. What I really want to do, is share my experience in the hope it might help you when you try to do the same thing.
At the beginning, we were worried because we understood there was a certain level of incompatibility between gems and jRuby. Considering we had 100 gems in our Gemfiles with about 300 dependencies amongst them and we only needed to change 4 gems, our confidence began to increase.
So first of all here are the changes we had to make:
- Remove gem ‘mysql2’, ‘~> 0.3.18’ and add gem ‘activerecord-jdbcmysql-adapter’, ‘~> 1.3.21’
- If you have libv8 gem entry eliminate it
- Add gem ‘therubyrhino’
- Add gem ‘tzinfo-data’, platforms: [:mingw, :mswin, :x64_mingw, :jruby]
- Eliminate web-console gem — for some reason it does not work on jRuby (?)
- Add gem ‘warbler’ — will help you to wrap your Rails application into the WAR file to be able to run it, for example, via Tomcat webserver.
We use rvm and I highly recommend that you use it too. There are other solutions and those can work well too, so you’ll need to choose something that you feel confident with. We used jRuby v220.127.116.11., the latest release, which is compatible with MRI v2.3.
Warbler in play
In development, you can run your Rails application using the jRuby platform like your usual rails server (do not forget to set the default ruby to jRuby). However, this doesn’t work in the production environment. Especially because you’re using jRuby. This approach is different to what we’ve done with MRI many times before. Now warbler is in play. There is very good documentation inside its repository so it’s only necessary to outline general notes:
- It wraps the whole Rails application into a WAR file
- With a generated WAR file you can deploy to the Tomcat webserver.
I suggest you read the original documentation on Warbler, it has several features and many configuration options that are worth getting to know. All I can say is…it just works 🙂
lot of configurations and generations are obviously painful but Warbler does them for you instead.. Super easy)
Let’s go inside
The WAR file is a type of ZIP archive, this means you can unarchive the file and see what’s inside. Let’s take a moment to consider the WAR file that was generated to run via the Tomcat webserver.
The top-level looks like the picture below:
The top level contains:
- Content from the public folder
- All the required WAR file directories: META-INF and WEB-INF.
META-INF contains only some of the files for the booting process:
You can find the additional file here — web.xml. This file is the deployment descriptor. It contains the information on how URLs map to servlets. When the webserver receives a request for the application, it uses the deployment descriptor to map the URL of that request to the code that ought to handle the request. You can read a more detailed explanation about this file here. In our case it contains the following code:
Here you can see some parameters definitions, filters and filter mapping. I think the most interesting part is the listener’s definition. If I understand it correctly this is the sweet spot that connects two worlds: Java webserver (Tomcat in our case) and Rails application (Ruby sources).
At this point I’d like to show the content of the WEB-INF/lib folder because it’s really important:
Yip, that’s right! There are the jRuby core and standard libraries! They create all the magic. helping JVM understand Ruby code. And even cooler, they are automatically loaded once the application has been deployed. You can also see the jruby-rack library adapting one and handling Rack-based Ruby application.
Then I went a little further.
The jRuby-rack library is hosted on github. So I decided to look inside it to better understand, what it takes to run the rails application. Actually, I was looking for some kind of line in the config.ru that said this: run Rails.application.
Inside jruby-rack library
At the start of the investigation I was aware of only one checkpoint: In the web.xml file a listener org.jruby.rack.rails.RailsServletContextListener is used and obviously, it should be located in this library.
Here it is. In this file, you’ll find the following line:
- final RackApplicationFactory factory = new RailsRackApplicationFactory();
Let’s find out where RailsRackApplicationFactory is defined. Here. And, finally, I’ve found what I needed in this file:
- return createRackServletWrapper(runtime, “run JRuby::Rack::RailsBooter.to_app”);
The goal was to understand exactly where the Rails application started so at this stage my investigation is complete . for further actions explore this folder. You’ll find the Ruby code inside the jruby-rack library and it explains what happens when a Rails application is loading.
The movement from MRI to the jRuby platform can go smoothly and with relatively little pain. The main problem you’ll be faced with is how to deploy your Rails application using jRuby platform. But there’s no cause for concern because the community has a very convenient tool to make this process very easy and manageable.