Custom launch scripts! For real this time!
By Jacob Kessler on Jan 06, 2009
So, a while ago I had promised that you could launch any jruby framework that you wanted to using glassfish. Some people had said that they were having some trouble getting it to work, so after going through the process myself, I'm here with another example. It doesn't even involve cheating or extra files, like it did last time! What you can't seem, of course, is that I went and fixed a few bugs in the old system, and in general it should be a bit more friendly.
Our guniea pig framework today is Sinatra, notable because it's not a MVC framework like Merb or Rails. The file structure is also much smaller (just a single file, for my test case). Hopefully, this will go to show how flexible the new launching method is.
So, the first thing that I did was wrote a Sinatra application, in this case an extremely simple one:
get "/" do
Yep. That's the whole thing. Then, having saved it as myapp.rb, we'll go on to making the file that will launch it for us.
You'll recall from last time that we need to create a script that will start the framework up, and return the application object that, in the future, glassfish can invoke call(env) on. So, how do we do that?
First, we know that we're going to have to make sure that we have everything that we need:
gem 'sinatra', '>=0'
Here, I'm directly requiring the app file since I've already required sinatra inside it. We'll be using the built-in grizzly handler, but there is no reason you couldn't write your own. We need a handler since that's what is going to recieve the initialized app from Sinatra, but aside from that it doesn't actually need to do anything: Grizzly is already set up and running independent of whatever framework we are using.
Next, we'll set all of the properties that we need to pass into Sinatra:
PATH = '/home/jacob/sinatratest/myapp.rb'
ROOT = '/home/jacob/sinatratest/'
set :server, 'grizzly'
set :run, true
set :env, 'development'
set :root, ROOT
set :views, ROOT + '/views'
set :public, ROOT + '/public'
set :sessions, false
set :logging, true
set :app_file, PATH
set :adapter, 'grizzly'
Some of these properties aren't actually needed: my test app doesn't have any views or a public directory, for example, but if it did I'd have to set them here. The only really significant bits here, in fact, are setting the app_file and telling Sinatra to use Grizzly (in this case, automatically translated to Rack::Handler::Grizzly by Sinatra) as its web server. You'll also note that this is rather application-specific: Because my launcher script won't have access to any of the variables in Glassfish, you'll have to tune it to each application that you want to launch.
Lastly, we need to actually start Sinatra and get the initialized app:
The dummy adapter will automatically store the app that it is handed on "startup" into a global variable to make it easy to hand it back out to your script. As the last line of the script, it will get handed back into GlassFish, and everything will be set for actually running it.
To run Sinatra using this custom launching script, you'll need to set two system properties. The first, jruby.rackup_script, will be the path to your launching script. The second, jruby.MTsafe=true, will tell Glassfish that the framework you are running (Sinatra in this case) is safe to use with multiple threads. Obviously, if you have a non-multithreaded framework, you should omit that. Glassfish will trust you completely when you tell it that something is thread-safe, and the results of running thread-unsafe code on multiple threads can be quite messy.
Another way to use this same functionality is to name your starting script something ending in .ru and put it in the directory that you deploy. The Glassfish framework auto-detection will then pick it up and do the right thing with it.
A caveat: If you explicitly specify a launcher script, rather than using the auto-detection, this whole process overrides the normal framework detection and you will only be able to deploy that single app on glassfish while you are using it: attempts to deploy other apps will just result in deploying copies of the explicitly-stated app. If you are using the auto-detect launch script, you'll still be able to deploy other apps.
So, there you are: a few short lines of code, and an entirely new framework is available for you. Of course, this particular example is, like the merb example, now unneeded: I've added auto-detection support for Sinatra apps, so you don't need a custom launcher script at all for Sinatra. That's expected, though: We like having automatic support for as many frameworks as we can, and once I'd written the launch script for this integrating it into the autodetector was too simple to not do.
While this won't be available on the main Glassfish release until V3 final is released around June, everything here will be in the next release of Glassfish Gem, which should be coming out extremely soon. If you want the latest bits that we're writing, go get it and try it out!
- jruby.rackup_script : path to the script that you've written that returns an initailized app
- jruby.MTsafe : tells Glassfish that the framework that it will be launching is safe to access with more than one thread, which it will use if it can't be sure one way or the other.
And, if you've got other frameworks that you'd like to see added to the Glassfish autodetector, just leave a comment and we'll see what we can do to get them into a Gem release coming to you soon!