How to launch a rack framework using a custom launch script

Edit on January 6th, 2009: These instructions have been replaced by the ones at this more recent blog post.

So, this goes back to a topic that I covered back in The Rack!, where I mentioned that you could deploy a framework that we didn't autodetect by using a custom ruby script. Just saying that you can is rarely enough, though, so today I'll step through the process of creating one of these scripts. The example that I'll be using is merb, which will autodetect but serves as a good example of what you'll need to do if you are getting support for SuperUltraAwesomeFramework to work on GlassFish.

 The first thing you'll need to do, for now, is to go get the latest nightly build of GlassFish from here. Right now, this is all semi-experimental stuff, and hasn't made it into the promoted builds thus far. It might even be so advanced that the nightlies don't have it, in which case you'll have to wait a bit for it, unfortunately. That, of course, means that the second thing you should do is report any instances of it not working, so we can fix it.

Alright, now you're got yourself a rack-enabled GlassFish. You're going to need to tell it that it should be deploying in a custom way, so you should specify -Djruby.applicationType=/path/to/your/startup/script.rb on the command line or in domain.xml. Now, what should that script do?

That script should, at the end, return an initialized rack applition. We're going to take whatever you hand us out of that script and plug it into our Java <-> Rack translator, and if you hand us a String or something like that and it breaks, that's not our fault. Of course, it also probably won't be so simple as just saying "make me a new app!" Lets see some code:

# Merb init file.
# Thanks to Yehuda Katz for coding help

#load required files
require 'rubygems'
gem 'merb-core', '>= 0'
require 'merb-core'
require 'rack/handler/grizzly'
require 'rack/adapter/merb'
Merb::Rack::Adapter.register %w{grizzly}, :GrizzlyMerb

# Set up the server and log stream, supress merb signal trapping

# Start merb
   :merb_root => $glassfish_appRoot,
   :adapter => "grizzly",
   :verbose => true,
   :log_level => :info,
   :path_prefix => $root

 That doesn't look so complicated, does it? It can really be divided into two sections: setting Merb up, and starting Merb. As always, the first thing to do is thank anyone that helped you code your script, especially when it means that it not only works, but is more elegant than it was before. Next, we go through and require some things that we'll need, and set the merb properties. We'll cover the two other files we needed, rack/handler/grizzly and rack/adapter/merb, a bit farther down.

The first one that we set (Adapter.register and, later, :adapter =>"grizzly") is the fairly universal step of telling the framework what web server we want it to be using, which in this case (and your case too) will be Grizzly rather than WEBrick or Mongrel or whatever. The key is that we don't want the framework to start up its own web server, since all of the web-facing stuff will be already handled for it.

The second one, Merb.disable(:signals), turns off Merb's trapping of console signals (SIGINT, SIGTERM, etc.), which allows all of that to be controlled by GlassFish rather than the framework.

 Lastly, we call Merb's start method, which is the public interface to start a Merb server, with some of the information that GlassFish passes into the runtime. It would be cheating if I didn't tell you what those were, so:

$glassfish_appRoot is the full path to the directory that you tried to deploy. /path/to/my/merb/app/, for example

$root is the deployment of the location, or GlassFish context root. By default, it's the name of the directory that you are deploying out of.

And then, we cheat a little bit, since we don't actually return the initialized runtime here. But, if this were your script, you'd need to return your initialized application at the very end. How you get that application will depend on your framework, for Merb we ended up sticking it into a global variable (since it's much easier to pull global variables out from Java). You can do whatever you want to to get it returned, though.

 rack/handler/grizzly is just a translator between Java requests and Rack requests.

rack/adapter/merb is a dummy adapter, and you might not even need to do as much as this one does. Merb passes an initialized copy of itself to the adapter, so we need to grab that and put it somewhere for Java to access later. Here is the whole thing:

module Merb
  module Rack
    class GrizzlyMerb
      def self.start(opts={})
      $merb_app = opts[:app] #this is where we cheat. Java later reads this out, rather than us returning it

 Now, obviously, you would need to change things a bit for your framework, but the principle remains the same: set your options, start your framework, and pass the initialized app back to us. We take care of all of the details of what happens on either side of the call, pooling of your framework (if it's not capable of handling multiple concurrent requests), and so on. That means that you can configure your framework without ever having to write Java code, or anything like that.

One final word of warning: As a legacy from the days when we only supported Rails, you may need to create an app/controllers/application.rb file in the directory that you are deploying in, until we can settle on something more generic and add it to the code base. That file just has to exist, it doesn't have to have anything in it.

So, with that, have fun enabling all of your favorite web frameworks, and remember to report any problems you have to us!


Post a Comment:
  • HTML Syntax: NOT allowed

Jacob Kessler


« July 2016