I’m wiring the Ape up so I can run it with JRuby in a servlet in a real Java App Server, and while Marcin Mielżyński’s first-cut RubyServlet works fine, I suspect it’s not the only approach to dispatching. So I’m doing some research and thinking, and I’ve collected it here for anyone who cares.
The Problem ·
Using your app-server config files, you tell it to dispatch requests to
RubyServlet, so in your
web.xml you’d have
something like this:
<servlet-mapping> <servlet-name>RubyServlet</servlet-name> <url-pattern>/ape/*</url-pattern> </servlet-mapping>
So you have to design your URI space with some sort of a hook so you can route the right requests into Rubyland. Then RubyServlet has to figure out which Ruby class and method to call.
What Rails Did · Rails figures out what class and method to call based on parts of the URI path. Until recently there was also an egregious semicolon hack, but but as PragDave explains, they’re cleaning that up.
Marcin’s Solution ·
This isn’t actually, you know, documented anywhere, but Marcin’s Java code
is easy enough to read. It’s about like Rails, using
the last two parts of the URI to pick class
and method, defaulting to a method-name of
It also has code CamelCasing to capitalize class-names and also (not sure why
this is done in Rails either) turn ape_drool to ApeDrool.
Ape#dung (but looks for
the code in
In all cases, the Ruby code gets the Request and
Response objects, so it can find out anything it needs to about
what’s going on.
Mongrel · Mongrel’s job is easier; because in that case your Ruby code controls its server, so you can pro-actively register your URIs; docs here. For example:
h = Mongrel::HttpServer.new("0.0.0.0", "3000") h.register("/test", SimpleHandler.new) h.register("/files", Mongrel::DirHandler.new(".")) h.run.join
What Servlet Actually Does ·
Javadocs, and in particular the abstract
which is what you end up using, the idea is that you fill in
doPost and so on, and they get
called with the request and response objects.
Requests actually come in through a
service method, which they
expect you to not override, and it does the dispatching.
How Jython Works ·
Jython is clever, and took me a couple of minutes to figure out.
First, it overrides the
service method so that it can dig the
path out of the request
and do tricks similar to Rails or RubyServlet to figure out which Jython class
to call; that class has to extend HttpServlet.
Then, it compiles that Jython into bytecode, and then it
calls the generated code’s
service method, which presumbly ends
up routing to the provided
doGet and so on.
That compiling-to-bytecode trick sure is handy. JRuby guys?
The RESTless Ape · I’m perfectly OK with Marcin’s current technique; the Ape only ever calls one method, and cooking it into the URI causes me no pain. I guess I should ensure that it’s a POST not a GET because while the Ape tries to clean up after itself, it can change the state of the system.
But I kind of think the Ape is in a minority. Applications, in particular thoughtfully-designed RESTful applications, do care what the HTTP method is, and probably would like a bit more flexibility in routing requests to code than you get by mapping the last two steps in the URI path to class and method.
In fact, I suspect that the basic Java HttpServlet interface is more or less what a RESTful app designer would want, with dispatching to GET/POST/PUT/DELETE handlers. In particular, if I were building an APP server implementation, that’d be about right.
So I’ll sketch out some thoughts on what a RESTfulRubyServlet API might look like. Now, I don’t need it. In fact, maybe this is a YAGNI. But if dozens of people pipe up saying “I need that” then maybe I’ll build it; with Marcin’s code in front of me it would be no strain at all.
The initial problem remains; how do you figure out which class’
doGet and so on to call?
I’d like to borrow Rails culture: not repeat myself and rely on
Convention over Configuration to make it real easy to explain what to do to get
I note that the typical structure of a Ruby project is a root directory
test subdirectories. So, why
not use that? If you have a webapp named “foo” and
foo/WEB-INF/web.xml says to use
then you look for
foo/src/servlet.rb and call
Servlet#doPost, and so on.
It’d be easy to build. Does the world need RESTfulRubyServlet and if so, is there a better design?