The Web Semantic

Avatar

data for humans and computers and the tools that make it available

Jena Ruby bindings: accessing Jena’s feature rich RDF api from Ruby

While working on jenabean I began to wonder if dynamic languages had features that would make editing ontology instance data easier.  The traditional method for bindind a schemas to objects normally involves creating some kind of binding descriptor file in XML.  The tedium of that process is well known and so painfull that it may not even continue to be a viable pattern.  What I discovered is that it’s rather easy to manipulate RDF files from Ruby (JRuby to be exact) using Jena.  The technique I decided upon was to dynamically create classes that map to RDF types in the ontology.  This is a type of “metaprogramming” that is familiar to rails developers, and to be fair, I spent a few hours reading ActiveRecord code to glean some ideas on how this type of binding works.  So let’s get started.  Here’s what you’ll need to follow along in a hands on manner:

JRuby comes as a zip or tar, just expand it in your favorite open source project directory.  You’ll need to have jena.rb and song.owl in your current directory as well.  Instead of the customary “hello world” I’d rather we use interactive ruby, it’ll make it easier to see what’s happening and play around a bit with jena.  JRuby comes with an executable called “jirb”.  Run it like this…

jirb interactive ruby shell

Next we need to tell JRuby where to find our Jena libs.  Somebody please correct me if I’m wrong, but this is the easiest way I’ve found, cut-n-past this into your interactive shell:

require '../jenabean/jenalib/jena.jar'
require '../jenabean/jenalib/log4j-1.2.12.jar'
require '../jenabean/jenalib/commons-logging-1.1.jar'
require '../jenabean/jenalib/concurrent.jar'
require '../jenabean/jenalib/icu4j_3_4.jar'
require '../jenabean/jenalib/stax-api-1.0.jar'
require '../jenabean/jenalib/wstx-asl-3.0.0.jar'
require '../jenabean/jenalib/xercesImpl.jar'
require '../jenabean/jenalib/xml-apis.jar'
require '../jenabean/jenalib/iri.jar'
require '../jenabean/dist/jenabean.jar'
require 'jena'

You’ll need to modify the paths to suit the location of your jena installation. The require ‘jena’ line loads jena.rb, assuming it’s in your current working directory.  Now we’re going to use the Jena helper to create a model, and then print it out just to demonstrate that all’s well.  Notice we’re able to access the familiar java singleton class “System”…


rb(main):013:0> m = Jena.create_model
irb(main):014:0> m.write(System.out)
<rdf:RDF
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#”
xmlns:owl=”http://www.w3.org/2002/07/owl#”
xmlns:daml=”http://www.daml.org/2001/03/daml+oil#”
xmlns:rdfs=”http://www.w3.org/2000/01/rdf-schema#” >
</rdf:RDF>
=> #<Java::ComHpHplJenaRdfModelImpl::ModelCom:0×1af1915 @java_object=<ModelCom
{} | >>

Now it’s time to read in the ontology, this time it’s 100% straight Jena access via our model instance…


irb(main):014:0> m.read('file:song.owl')

Sorry about the the verbose output…that’s not us, that’s jirb and jena. Jena’s OntModel has a terribly verbose toString() implementation, and since we’re interacting directly with Jena there’s not much we can do other than turning off echo, and I want that for some stuff we’re about to do. We’re about to…drum role…bind Ruby objects to our ontology so get out those XML editors. No, just kidding. Instead, cut-n-past the following lines into your ruby shell:


Jena.make(m,'http://example.org/music#Genre')
Jena.make(m,'http://example.org/music#Song')
Jena.make(m,'http://example.org/music#Composer')
Jena.make(m,'http://example.org/music#Instrument')
Jena.make(m,'http://example.org/music#Musician')
Jena.make(m,'http://example.org/music#Recording')

There, we’re done. Now we should have some ontology specific classes ready for us to work with. Jena.make finds the OntClasses at the specified URI’s and dynamically creates ruby classes for us. Yes, you have to be carefull with the URI’s, they have to be right on. (perhaps I need to create a utility to pull the urls out and store as constants). Now we can interact with the ontology in a structured way.


irb> jazz = Genre.new('http://example.org/genre/jazz')
a type of http://example.org/music#Genre
my uri is http://example.org/genre/jazz
bindings:
hasName -> http://example.org/music#hasName
.
=>
irb(main):024:0>

Genre is a fullfledged ruby class. Genre.new creates a new isntance for us, given a URI, just like we normally do with Jena. Notice our new instance has a property called “hasName”…we can set it like this..


irb>jazz.hasName = "Jazz Music"

We can save the new individual by calling jazz.save, or save it to a new clean model, which will help us see exactly what happened:

irb(main):031:0> jazz.save_to(m2)
=> {"hasName"=>"Jazz music"}
irb(main):032:0> m2.write(System.out)
<rdf:RDF
xmlns:j.0="http://example.org/music#”
xmlns:rdf=”http://www.w3.org/1999/02/22-rdf-syntax-ns#”
xmlns:owl=”http://www.w3.org/2002/07/owl#”
xmlns:daml=”http://www.daml.org/2001/03/daml+oil#”
xmlns:rdfs=”http://www.w3.org/2000/01/rdf-schema#” >
<rdf:Description rdf:about=”http://jazz“>
<j.0:hasName rdf:datatype=”http://www.w3.org/2001/XMLSchema#string”>Jazz mus
ic</j.0:hasName>
<rdf:type rdf:resource=”http://example.org/music#Genre”/>
</rdf:Description>
<rdf:Description rdf:about=”http://example.org/music#Genre“>
<rdf:type rdf:resource=”http://www.w3.org/2002/07/owl#Class”/>
</rdf:Description>
</rdf:RDF>
=>

One Comment, Comment or Ping

  1. Roger Marin

    Taylor, i am very impressed with what you’ve got so far, in fact, i plan to use this approach for my thesis project. Something that i was thinking, it would be cool if a groovy version of this could be made, i plan on working on it as soon as i get some free time, another cool idea was to somehow integrate it with grails that would make a really “groovy” platform for developing semantic web apps :)

    BTW: jenabean rocks! congrats on coming up with such a usable and easy to use API. i absolutely love it, i’m just starting with it but it definitely makes working with RDF a lot more fun.

    Cheers!.

Before you go

places we like