The Service

require 'java'java_import 'javax.ws.rs.Path'

java_import 'javax.ws.rs.GET'

java_import 'javax.ws.rs.Produces'



java_package 'com.headius.demo.jersey'

java_annotation 'Path("/helloworld")'

class HelloWorld

java_annotation 'GET'

java_annotation 'Produces("text/plain")'

def cliched_message

"Hello World"

end

end

~/projects/jruby ➔ jrubyc -c ../jersey-archive-1.2/lib/jsr311-api-1.1.1.jar --javac restful_service.rb

Generating Java class HelloWorld to /Users/headius/projects/jruby/com/headius/demo/jersey/HelloWorld.java

javac -d /Users/headius/projects/jruby -cp /Users/headius/projects/jruby/lib/jruby.jar:../jersey-archive-1.2/lib/jsr311-api-1.1.1.jar /Users/headius/projects/jruby/com/headius/demo/jersey/HelloWorld.java

~/projects/jruby ➔ javap com.headius.demo.jersey.HelloWorld

Compiled from "HelloWorld.java"

public class com.headius.demo.jersey.HelloWorld extends org.jruby.RubyObject{

public static org.jruby.runtime.builtin.IRubyObject __allocate__(org.jruby.Ruby, org.jruby.RubyClass);

public com.headius.demo.jersey.HelloWorld();

public java.lang.Object cliched_message();

static {};

}

The Server Script

require 'java'

java_import com.sun.jersey.api.container.grizzly.GrizzlyWebContainerFactory



base_uri = "http://localhost:9998/"

init_params = {

"com.sun.jersey.config.property.packages" => "com.headius.demo.jersey"

}



puts "Starting grizzly"

thread_selector = GrizzlyWebContainerFactory.create(

base_uri, init_params.to_java)



puts <<EOS

Jersey app started with WADL available at #{base_uri}application.wadl

Try out #{base_uri}helloworld

Hit enter to stop it...

EOS



gets



thread_selector.stop_endpoint



exit(0)

The CLASSPATH

jersey-core-1.2.jar

jersey-server-1.2.jar

jsr311-api-1.1.1.jar

asm-3.1.jar

grizzly-servlet-webserver-1.9.9.jar

Testing It Out

~/projects/jruby ➔ jruby main.rb

Starting grizzly

Jersey app started with WADL available at http://localhost:9998/application.wadl

Try out http://localhost:9998/helloworld

Hit enter to stop it...

Jun 1, 2010 6:36:55 PM com.sun.jersey.api.core.PackagesResourceConfig init

INFO: Scanning for root resource and provider classes in the packages:

com.headius.demo.jersey

Jun 1, 2010 6:36:55 PM com.sun.jersey.api.core.ScanningResourceConfig logClasses

INFO: Root resource classes found:

class com.headius.demo.jersey.HelloWorld

Jun 1, 2010 6:36:55 PM com.sun.jersey.api.core.ScanningResourceConfig init

INFO: No provider classes found.

Jun 1, 2010 6:36:55 PM com.sun.jersey.server.impl.application.WebApplicationImpl initiate

INFO: Initiating Jersey application, version 'Jersey: 1.2 05/07/2010 02:04 PM'

~ ➔ curl http://localhost:9998/helloworld

Hello World

What We've Learned

JRuby's new jrubyc --java(c) support for generating "real" Java classes

Tagging a Ruby class with Java annotations, so it can be seen by a Java framework

Booting a Grizzly server from Ruby code

Implementing a JAX-RS service with Jersey in Ruby code

There's lots of ways to present RESTful web services these days, and REST has obviously become the new "it's IPC no it's not" hotness. And of course Rubyists have been helping to lead the way, building restfulness into just about everything they write. Rails itself is built around REST, with most controllers doubling as RESTful interfaces, and it even provides extra tools to help you transparently make RESTful calls from your application. If you're doing RESTful services for a typical Ruby application, Rails is the way to go (and even if you're not using Ruby in general...you should be considering JRuby + Rails).In the Java world the options aren't quite as clear, but one API has at least attempted to standardize the idea of doing RESTful services on the Java platform: JSR-311, otherwise known as JAX-RS.JAX-RS in theory makes it easy for you to simply mark up a piece of Java code with annotations and have it automatically be presented as a RESTful service. Of the available options, it may be the simplest, quickest way to get a Java-based service published and running.So I figured I'd try to use it from JRuby, and when doing it in Ruby it's actually surprisingly clean, even compared to Ruby options.I followed the Jersey Getting Started tutorial using Ruby for everything (and not using Maven in this case).My version of their HelloWorldResource looks like this in Ruby:Notice that we're using the new features in JRuby 1.5 for producing "real" Java classes: java_package to specify a target package for the Java class, java_annotation to specify class and method annotations.We compile it using jrubyc from JRuby 1.5 like this:The new --java(c) flags in jrubyc examine your source for any classes, spitting out .java source for each one in turn. Along the way, if you have marked it up with signatures, annotations, imports, and so on, it will emit those into the Java source as well. If you specify --javac (as opposed to --java), it will also compile the resulting sources for you with jruby and your user-specified jars in the classpath, as I've done here.The result looks like this to Java:Under the covers, this class will load in the source of our restful_service.rb file and wire up all the Ruby and Java pieces so that both sides see HelloWorld as the in-memory representation of the Ruby HelloWorld class. Method calls are dispatched to the Ruby code, constructors dispatch to initialize, and so on. It's truly living in both worlds.With the service in hand, we now need a server script to start it up.Continuing with the tutorial, I've taken their simple Java-based server script and ported it directly to Ruby:It's somewhat cleaner and shorter, but it wasn't particularly large to begin with. At any rate, it shows how simple it is to launch a Grizzly server and how nice annotation-based APIs can be for auto-configuring our JAX-RS service.Ahh CLASSPATH. You are so maligned when all you hope to do is make it explicit where libraries are coming from. The world should learn from your successes and your failures.There's five jars required for this Jersey example to run. I've tossed them into my CLASSPATH env var, but you're free to do it however you likeThe first four are available in the jersey-archive download , and you can fetch the Grizzly jar from Maven or other places.The lovely bit about this is that it's essentially a four-step process to do the entire thing: write and compile the service, write the server script, set up CLASSPATH, and run the server. Here's the server output, finding my HelloWorld service right where it should:And perhaps the most anti-climactic climax ever, curling our newly-deployed service:It works!This was a simple example, but we demonstrated several things:Do you have any examples of other nice annotation-based APIs we could test out with JRuby?