Skip to main content

Jenabean: Easily bind JavaBeans to RDF

Learn how Jenabean simplifies binding JavaBeans to the Jena Semantic Web framework

Taylor Cowan (taylor_cowan@yahoo.com), Senior Software Systems Engineer, Travelocity
Taylor Cowan is an architect at Travelocity. Throughout his career, he has worked with XML and Java code in the context of translation work flows, UI technologies, and more recently the Semantic Web (RDF/OWL). He is a committer to the open source Jenabean project.

Summary:  The Resource Description Framework (RDF) is the World Wide Web Consortium (W3C) proposed standard for linking and expressing data on the Web. Java™ developers who develop applications for the Semantic Web will need to convert RDF properties to or from Java types. Jenabean uses the Jena Semantic Web framework's flexible RDF/OWL API to persist JavaBeans, making the task of writing these applications easier and more familiar to Java developers.

Date:  29 Apr 2008
Level:  Intermediate PDF:  A4 and Letter (57KB)Get Adobe® Reader®
Activity:  4403 views

Java developers are fortunate because in Jena they have one of the better RDF frameworks available (see Resources). Jena provides an API for writing and reading RDF, which can be stored and persisted in various ways. If you aren't yet familiar with Jena, I strongly recommend you read Philip McCarthy's "Introduction to Jena" before you read this article.

Jena is engineered to work well with RDF data models, just as JDBC is suited for working with relational models. Database applications consist largely of code written to save Java objects, as well as code written to marshal objects from the database. Semantic Web applications written in Java code face a similar problem: they must either convert Java objects to RDF or convert RDF to objects. As a consequence, developers must write a considerable amount of code to bridge the gap between their own model (usually JavaBeans) and Jena's RDF-centric API.

This article shows you how the Jenabean Java-to-RDF binding framework (see Resources) helps simplify this process and reduce the amount of code you need to write. You'll examine some example Jena client code and compare it to Jenabean's JavaBean-based programming model. Starting with a simple example, I'll show you how to:

  • Save a bean as RDF
  • Bind its properties to specific RDF properties
  • Relate it to other objects
  • Read it back out again

RDF and the Semantic Web

For a good introduction to Semantic Web concepts — including Uniform Resource Identifiers (URIs), RDF, and OWL Web Ontology Language (OWL) — see "The future of the Web is Semantic."

The Jenabean programming model

Consider the simple example RDF in Listing 1, which is in Notation3 (N3) format for readability (see Resources):


Listing 1. Example RDF (in N3 format)
                
<http://www.ibm.com/developerworks/xml/library/j-jena/>
     a       dc:Article ;
     dc:creator "Philip McCarthy"^^xsd:string ;
     dc:subject "jena, rdf, java, semantic web"^^xsd:string ;
     dc:title "Introduction to Jena"^^xsd:string .

Listing 1 declares that the article "Introduction to Jena" was written by Philip McCarthy and covers the topics jena, rdf, java, and the semantic web. The vocabulary is part of the Dublin Core metadata taxonomy (see Resources). To duplicate these RDF statements using Jena's raw Java API, you might do something like Listing 2:


Listing 2. Asserting example RDF using raw Jena API
                
String NS = "http://purl.org/dc/elements/1.1/";
OntModel m = createModel();
OntClass articleCls = m.createClass(NS +"Article");
Individual i = articleCls.createIndividual(
  "http://www.ibm.com/developerworks/xml/library/j-jena/");
Property title = m.getProperty(NS + "title");
Literal l = m.createTypedLiteral("Introduction to Jena");
i.setPropertyValue(title,l);
Property creator = m.getProperty(NS + "creator");
l = m.createTypedLiteral("Philip McCarthy");
i.setPropertyValue(creator,l);
Property subject = m.getProperty(NS + "subject");
l = m.createTypedLiteral("jena, rdf, java, semantic web");
i.setPropertyValue(subject,l);
m.write(System.out, "N3");

Each value assertion in Listing 2 requires three lines to:

  1. Access the property
  2. Create a typed literal
  3. Assert the property relationship

This code's advantage is that it maps directly to RDF concepts in a transparent and explicit way. The situation is similar to what you'd see in JDBC client code, wherein the API applies directly to the relational model.

If you spend much time with Jena, you'll begin to realize that a gap exists between what's considered to be normal object-oriented code and your client code that interacts with the Jena API. Instead of setting properties, you're asserting edges in a graph.

Listing 3 shows the same assertions made with Jenabean:


Listing 3. Creating the same assertions using Jenabean
                
Model m = _
Bean2RDF writer = new Bean2RDF(m);
Article article = new Article("http://www.ibm.com/developerworks/xml/library/j-jena/");
article.setCreator("Philip McCarthy");
article.setTitle("Introduction to Jena");
article.setSubject("jena, rdf, java, semantic web");
writer.save(article);
m.write(System.out, "N3");

I included the m.write(...,"N3") line as encouragement. Try it for yourself with the code included with this article (see Download).

Listing 2 and Listing 3 differ significantly but result in almost identical changes to the Jena model. They both create a superset of the triples from Listing 1. Listing 2 declares a new article by calling m.createClass(...). Listing 3 accomplishes the same objective by creating a new instance of the Article class — new Article(...). Without Jenabean, each property assertion requires you to access the property from the model, create a literal, and assert the new statement into the model by calling setPropertyValue(...). Using Jenabean's Bean2RDF converter, you simply call the JavaBean's setter methods. Listing 3 results in code that's natural to most Java developers.


OOP-to-RDF binding

Jenabean's primary advantage, as you've seen, is in bridging the gap between Java objects and RDF. This allows you to code Semantic Web applications in a familiar OOP style using the same beans you use in your domain model. But that's not to say that Java objects and RDF are one and the same. Objects and RDF represent data in different ways. A good binding tool must solve the following three problems.

The object-to-RDF impedance mismatch

Often developers are keen simply to replicate an ontology as a set of Java objects. I find that a one-to-one correlation between Java objects and OWL classes doesn't really exist. OWL permits multiple inheritance, allows for many classes to share the same property, and allows properties themselves to inherit from one another. Again, just like relational database management systems (RDBMS), objects and RDF are similar in some ways and different in others. That still leaves us needing to produce and consume RDF from within Java code. Many tools take the code-generation approach, which involves reading an RDF schema or OWL ontology and replicating the classes and properties verbosely in the Java language. Such tools lack the capability to let you assert common RDF properties from existing JavaBean classes. RDF binding tools must let you arbitrarily relate a JavaBean property to an RDF property URI (assuming they are type compatible).

Shallow vs. deep

Another problem any binding tool needs to solve is how much of an object graph to persist. Saving an object in a dense, heavily connected object graph could result in persisting the entire model. Loading presents a similar situation. The binding tool must prevent loading the full RDF model into memory as JavaBeans but also allow for this as a feature when necessary.

Cycles

Cyclic relationships are a common situation in RDF and in object models. When persisting and loading, the binding tool must be able to detect and handle cycles. Obviously, it should prevent never-ending loops, and it should detect previously loaded objects and save time by reusing them.

Jenabean solves these three issues while trying to keep the process as simple as possible. It doesn't require a code-generation phase, a bytecode-generation step, or a run-time bytecode-manipulation library. It's only dependency is on Jena and its libraries. Jenabean defaults to a conservative policy of saving the object with its direct singular properties. Developers must indicate they intentionally desire a "deep" copy in order to save the object, its relatives, and so on fully. Alternatively, you can specify that a particular collection property of the instance be saved or loaded.


Creating JavaBeans for your RDF

Jenabean's most important annotations

@Id helps Jenabean create a unique URI for each JavaBean instance. You should place it on the getter method returning a unique int or String.

@Namespace is applied at the class level. It overrides the default behavior, allowing you to define which namespace you'd like to use. The value must be a valid URI, ending in either / or #.

@RdfProperty binds JavaBean properties to arbitrary RDF properties. You simply annotate the getter method and supply the RDF property URI as an argument to the annotation.

If you are familiar with Hibernate or other binding tools, you may be wondering where the magic happens. Jenabean uses Java annotations to declare how beans are mapped to RDF. Just like other annotation-based binding layers, Jenabean is most useful when your model is being driven from Java objects. Obviously this is not necessarily always the case, but when it is, Jenabean is ready to help.

Simplest working example

Jenabean has many features for customizing how beans are serialized as RDF, but if you are happy with the defaults, you can begin writing and reading beans quickly. Let's create a simple example JavaBean that fulfills all the necessary requirements. Just as with the Java Persistence API (JPA) or Hibernate, you need to ensure that your objects have a unique ID. Jenabean requires you to add a single annotation — @Id — to at least one bean field, which acts as the unique identifier. Listing 4 shows this simple bean:


Listing 4. A simple bean ready for saving
                
package example;
import thewebsemantic.Id;
public class Person {
   private String email;
      @Id
      public String getEmail() { return email;}
      public void setEmail(String email) { this.email = email;}
}

Listing 4 provides enough information for Jenabean to save and load instances of the Person class reliably. You don't need to extend anything or write an XML descriptor file. An e-mail address is an effective ID because it is unique. Listing 5 shows how you save an instance of Person to the Jena model:


Listing 5. Saving an instance of the Person class, with resulting RDF
                
Model m = ModelFactory.createOntologyModel();
Bean2RDF writer = new Bean2RDF(m);
Person p = new Person();
p.setEmail("person@example.com");
writer.save(p);
m.write(System.out, "N3");

...

<http://example/Person>
     a       owl:Class ;
<http://thewebsemantic.com/javaclass>
     "example.Person" .

<http://example/Person/taylor_cowan@yahoo.com>
     a       <http://example/Person> ;
<http://example/email>
     "taylor_cowan@yahoo.com"^^xsd:string .

Bean2RDF is a Jenabean class that writes objects as RDF. It defaults to shallow mode, which means it saves the instance and its singular properties. If a Person class hasn't been added to the model yet, it asserts the new class as an instance of owl:Class. Notice in Listing 5 that Jenabean uses the example package as the namespace for the new ontology class. The second assertion is an annotation that indicates the Java class that was used to create the individual. The Person instance along with its e-mail address is asserted as well. Jenabean helps by first creating a URI for the saved instance. It also takes notice of the e-mail property and asserts it as a string literal value.

Retrieving JavaBeans from a Jena model

Java type mappings

Jenabean inherits Jena's type-mapping defaults between Java and RDF for all primitive types and their wrappers. Jenabean has additional support for java.util.Date, which it maps to xsd:dateTime. Array properties are mapped to rdf:Seq, but the most natural way to express multiplicity is with the java.util.Collection<?> interface. For example, if your Person class has many Appointments, you'd give it a property of type java.util.Collection<Appointment>. Jenabean will not support specific implementations of java.util.Collection such as List or ArrayList, because RDF does not guarantee the order. Of course you can also have properties of other beans: either a single instance, an array, or a collection.

Individuals represented in RDF need a URI, whereas Java developers tend to think in terms of unique IDs. Jenabean assists you by appending the namespace (in this case defaulted from the package and class name) with the declared ID field. Once it's created, you can load information back from the model using RDF2Bean:

RDF2Bean reader = new RDF2Bean(m);
Person p2 = reader.load(
    Person.class,"person@example.com");

Jenabean also loads all instances of Person:

Collection<Person> people = reader.load(Person.class);

These are the easiest ways to access beans in your model. Jenabean also has support for binding to a SPARQL (SPARQL Query Language for RDF) result. To summarize, Jenabean at a minimum requires bean authors to indicate which field holds a value that is unique for all instances of that type. When saved, the bean's class and properties are given default URIs based on the class's package and name. This allows you to begin creating RDF quickly from the Java tier without much trouble.

Specifying namespaces and properties

So far, I've shown how Jenabean can create default URIs for you based upon your bean's classpath and name. Jenabean also has support for declaring which namespaces you'd like to work with. You can map your beans to particular namespaces with the @Namespace annotation. For example purposes, I'll use a namespace that I know isn't already taken, Jenabean's own project URL:

@Namespace("http://jenabean.googlecode.com/")
public class Person { _


<http://jenabean.googlecode.com/Person/person@example.com>
  a       <http://jenabean.googlecode.com/Person> ;


Notice that instead of the default package, the Person class and its properties are given a new namespace that matches the namespace I provided as an argument to the @Namespace annotation. By default, this namespace will be used for the class and its properties.

In the world of RDF, it's advisable to use common properties; otherwise, a Semantic Web will never develop. By using well-known properties, you make your data more semantic and familiar to others. If a spider (of the Web-crawling variety) encountered the RDF snippet I generated from the Jenabean project URL namespace, it wouldn't be able to make anything of it. But you can modify the bean just a bit with a more common and well-known assertion. The FOAF (Friend of a Friend) language (see Resources), a common vocabulary for linking people, has a special property for e-mail addresses: foaf:mbox. All your need to do now is use the @RdfProperty annotation on the Person bean:

@Id
@RdfProperty("http://xmlns.com/foaf/0.1/mbox")
public String getEmail() {  return email;}

<http://xmlns.com/foaf/0.1/mbox>
  "person@example.com"^^xsd:string .

Now the email property asserts itself as a foaf:mbox, which will be understood as an e-mail address by other RDF agents interested in your data.


Object relations

In the world of OWL and RDF, relationships of various cardinalities are expressed through multiple assertions of the same property. Jenabean makes this fairly easy using the java.util.Collection interface. Listing 6 extends the Person class to support friends (in a loose foaf:knows kind of way):


Listing 6. Extending Person to support friend relationships
                
public Collection<Person> friends = new
  LinkedList<Person>();

@RdfProperty("http://xmlns.com/foaf/0.1/knows")
public Collection<Person> getFriends() { return friends;}

public void setFriends(Collection<Person> friends) { this.friends = friends;}

This is nothing fancy — just a new field called friends of type Collection<Person>, with associated get and set methods. Now you can create a Person and several friends and relate them together by adding each friend to the friends collection. The @RdfProperty annotation is optional but important if you want to bind to existing third-party vocabularies. The annotation specifies that you'd like the "friend" property to bind to the foaf:knows RDF property within your Jena model. Listing 7 shows how to use traditional JavaBean techniques to create the friend relationships:


Listing 7. Expressing friend relationships, with resulting RDF
                
Model m = ModelFactory.createOntologyModel();
Bean2RDF writer = new Bean2RDF(m);
Person p = new Person();
p.setEmail("person@example.com");
Person f1 = new Person();
f1.setEmail("friend1@example.com");
Person f2 = new Person();
f2.setEmail("friend2@example.com");

p.getFriends().add(f1);
p.getFriends().add(f2);
writer.save(p); // modifies the Jena model
m.write(System.out, "N3");

...

foaf:knows
   jb:friend2@example.com, jb@friend1@example.com .

After the simple Person bean is saved via Bean2RDF.write(...), the model contains new data conforming to the FOAF specification.

Retrieving an ordered list

In RDF, ordering of child nodes is undefined, so you can't assume that Jena will retrieve the list of friends in any particular order. If you need ordered lists, Jenabean maps Java arrays to RDF sequences. To demonstrate, I'll give the Person class a set of telephone numbers represented as an array of Strings, as in Listing 8:


Listing 8. Example array property with resulting RDF
                
private String[] phoneNumbers;

public String[] getPhoneNumbers() { return phoneNumbers;}
public void setPhoneNumbers(String[] phoneNumbers) {
     this.phoneNumbers = phoneNumbers;}


 jb:phoneNumbers
     [ a       rdf:Seq ;
     rdf:_1  "321-321-3210"^^xsd:string ;
     rdf:_2  "321-321-3211"^^xsd:string
     ] ;



Conclusion

Jenabean helps you get started reading and writing RDF using the familiar JavaBean model. I've covered the basics: how to create a simple bean, save it, and read it back out. This is only a small part of the full story for writing a Semantic Web application in the Java language. Visit the Jenabean project's home page to learn more, provide feedback, or ask for help (see Resources).



Download

DescriptionNameSizeDownload method
Code samples for this articlej-jenbean.zip4KB HTTP

Information about download methods


Resources

Learn

Get products and technologies

Discuss

About the author

Taylor Cowan is an architect at Travelocity. Throughout his career, he has worked with XML and Java code in the context of translation work flows, UI technologies, and more recently the Semantic Web (RDF/OWL). He is a committer to the open source Jenabean project.

Comments (Undergoing maintenance)



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Java technology, XML, Open source
ArticleID=304821
ArticleTitle=Jenabean: Easily bind JavaBeans to RDF
publish-date=04292008
author1-email=taylor_cowan@yahoo.com
author1-email-cc=jaloi@us.ibm.com

My developerWorks community

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Special offers