JRuby on Rails and Websphere
[UPDATE] At the suggestion from Charles, I changed the warbler config to drop the JRuby/Rack libs and use the most recent of both. The way this is accomplished is pretty easy. Drop the new libs (JRuby 1.1.5, JRuby/Rack 0.9.3) in the lib directory and then add this line to the warbler config:
config.java_libs = []
Now when you war the project, the new libs get used. JRuby 1.1.5 has the fix for the InterfaceImpl exception I noted below. I still use the RackServlet but now I can drop the com.ibm.ws.webcontainer.invokeFiltersCompatibility from the Custom Properties on the server. One less step!
Now back to the orinal post…
Yes you can.
I just spent the last two days deploying a Rails application to a Webshpere Application Server. All in all it was rather easy. However, the time involved was tremendous considering that a deployment to Websphere (WAS) should be in the timeframe of minutes. I’m going to walkthrough the deployment process I had and give my experience. I’m doing this because I wish someone else had while I was doing it. I found hints and ideas on the web, but nowhere was there a specific experience outlining all of the troubles.
The Rails application
There is nothing special during development of the Rails app. However there are considerations to take when deciding on the database of choice. For example SQLite and JRuby are so far as I can tell not friendly yet (somebody please point me to the door on this one). JRuby does not handle native extensions and so the SQLite Ruby gem etc have issues. I didn’t really investigate this all too much since our deployment was to an Oracle database. During development I would use regular Ruby (MRI) with SQLite and during testing I would use JRuby with Oracle. The database.yml was pointing to different database implementations. Database agnosticism FTW!
The Warble’ing
Warbler is a gem that packages your Rails app into a .war file.
sudo gem install warbler
cd /<rails_root>/
warble config
This will provide you with a warble.rb file inside of the config folder in your app.
There are a few key points about your warble.rb configuration that need to be spelled out here.
- Be sure you have all of your gems listed in the
config.gems += []section. This was a big source of trouble/confusion for our deployment. For a long time we were trying to deploy without these gems and the Rails app wouldn’t work – regardless of successful deployment. In other words: plugins come with, gems won’t unless you specify. - Coordinate the Rails version. I spent a few minutes confused about the rails gem due to packaging the most recent Rails version (2.2) and specifying an older version in the environment.rb config file. Again – the app would never work regardless of successful deployment.
The rest of the config is as you need. Those were the things that got me hung up a few times and were worth mentioning.
warble war:webxml
This last command will build a web.xml file based on your warble.rb configuration. It’s going to be found in tmp/war/WEB-INFO/. Copy this file into your config directory. Let’s edit this a little to get things happy.
Find this:
<filter>
<filter-name>RackFilter</filter-name>
<filter-class>org.jruby.rack.RackFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>RackFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
And replace it with this:
<servlet> <servlet-name>Rails</servlet-name> <servlet-class>org.jruby.rack.RackServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>Rails</servlet-name> <url-pattern>/*>/url-pattern> </servlet-mapping>
The RackFilter is not quite cool with WebSphere yet, but the RackServlet works just fine. We’ll need to make WebSphere pay attention to it later on.
sudo warble war
This will perform the packaging and leave you with a .war file in the root of your rails application. The name of the file defaults to the name of the rails root directory.
WAS
WAS is a large system of web servers and app servers. Navigating your way around this quagmire can be intimidating. With nodes, servers, cells and all forms of containers it is easily confusing. For the purposes of this experience, simply deal with a new Application (Applications -> Install New Application). During this setup most of the defaults are good. Let’s step through it:
- Browse to the .war file you created
- Set the Context Root, usually the name of your app or just “/”
- Next
- (in my case sit patiently)
- Next
- (patiently sitting)
- Next
- (…whistle…)
- Browse to the JNDI name (this is the datasource – not in scope here, kind of on your own unless you want to ask me about this later.)
- Next
- (…tapping fingers on desk…)
- Next
- At this stage you should have a summary with no messages. If so ..
- Finish
- Watch the installing log roll …
- Save directly to the master configuration
Granted there are many things you can configure along the way there … your mileage may vary.
All set! Uuh … mmm … not yet.
If you browse to your app now it won’t work. You’ll end up receiving an error similar to this:
Error 500: (InterfaceImpl-1556371120) class name is invalid at offset=0
And if you dig into your WAS logs and look at SystemOut.log you’ll find this in the stack:
ServletWrappe E SRVE0068E: Uncaught exception thrown in one of the service methods of the servlet: Rails. Exception thrown : java.lang.ClassFormatError: (InterfaceImpl-1471369636) class name is invalid at offset=0
WAS is crapping out on the requests because it isn’t wired to look at the JRuby servlet yet. (ASIDE: I’m confused here because if you look at the stack you will see the RackServlet being called – but it craps out. I haven’t dug into the JRuby/Rack source to try and figure out why this is yet.)
First we need to be sure to tell the application to use it’s own class loader. Do this: Applications -> Enterprise Applications -> < your app > -> Class loading and update detection -> Classes loaded with application class loader first -> OK. The setting won’t take unless you put 0 in the Polling interval field. Do that then hit OK. Save directly to the master configuration.
Second we need to be sure to have a container custom property set (Servers -> Application Servers -> < your server > -> Web Container Settings -> Web Container -> Custom Properties. (And I said this navigation stuff is intimidating remember?). Now from here we need to add (New) a custom property. This one: com.ibm.ws.webcontainer.invokeFiltersCompatibility. Set the value to “true”.
OK!
Let’s rock (Applications -> Enterprise Applications). Select your app and “Start”. You should be able to browse to your app now. TIP: run tail -f SystemOut.log so you can watch your rails app requests processing.
Good luck. I mean that. This isn’t the easiest thing to setup. At least not for me. I would much prefer to just use Apache. But in a corporate environment where the universe is run by Websphere, sleep easy knowing that yes, yes you can run Rails!