Useful Framework for Semantic Web and Linked Data Applications: Apache Jena

Atakan Güney
5 min readOct 17, 2019

--

Today’s blog post, I will give a brief introduction to Apache Jena. The framework also known as Jena. This is used to build semantic web and linked data applications. In the post, we will cover how to create Resource Description Framework(RDF) data with Jena and how to make query on the data(runnin SPARQL queries) and we will have little bit of ontology. Before that, if you need detailed information on RDF and SPARQL, please read my previous blog.

  1. Brief Introduction into Jena

Apache Jena is an open source Apache project, which provides some handy tools to developers to build, work or analyse on semantic web or linked data. Therefore, you can develop semantic web applications on top of Apache Jena. Here is the list of applications which are enhancing Jena or built on top of Jena.

Architecture of Jena Framework

Now, we have seen architecture and what is Apache Jena. It is time to code 🚀.

2. RDF Examples

First of all, you can see all examples in my GitLab repo. We first begin with our previous example of Green Goblin and Spiderman. Jena has already some vocabulary in it but in the example, we need to import external vocabulary whose URI is “http://www.perceive.net/schemas/relationship/”. After I downloaded vocabulary rdf data, to produce Java class, jena has useful command-line tool called “schemagen”. For detail, follow this HowTo. I created new vocabulary by following command under Jena directory, which I downloaded from here.

bin/schemagen -i ~/Downloads/relationship.rdf -a "http://www.perceive.net/schemas/relationship/" -o .

Note that, I run this under Jena home folder. So, it produced Java class and I moved the class into the same folder of my example code, cool 😈

We are now ready to produce our RDF data.

import org.apache.jena.rdf.model.*;
import org.apache.jena.sparql.vocabulary.FOAF;
import org.apache.jena.vocabulary.*;

So as you can see, Jena has already vocabulary FOAF.

Model model = ModelFactory.createDefaultModel();

// create the resource
// and add the properties cascading style
Resource greenGoblin
= model.createResource()
.addProperty(RDF.type, FOAF.Person)
.addProperty(FOAF.name, "Green Goblin");

Resource spiderman
= model.createResource()
.addProperty(RDF.type, FOAF.Person)
.addProperty(FOAF.name, "Spiderman");

greenGoblin.addProperty(Relationship.enemyOf, spiderman);
spiderman.addProperty(Relationship.enemyOf, greenGoblin);

// list the statements in the graph
StmtIterator iter = model.listStatements();

// print out the predicate, subject and object of each statement
while (iter.hasNext()) {
Statement stmt = iter.nextStatement();
Resource subject = stmt.getSubject(); // get the subject
Property predicate = stmt.getPredicate(); // get the predicate
RDFNode object = stmt.getObject(); // get the object

System.out.print(subject.toString());
System.out.print(" " + predicate.toString() + " ");
if (object instanceof Resource) {
System.out.print(object.toString());
} else {
// object is a literal
System.out.print(" \"" + object.toString() + "\"");
}
System.out.println(" .");
}

So, lets dive into what is happenning here. In Jena, an RDF element consists of Resource, Property and RDFNode; subject, predicate, object accordingly and each RDF record is a Statement. Now, see the results.

79bb0efc-4b0a-4b5a-8c8e-5dfd33bf6d0b http://www.perceive.net/schemas/relationship/enemyOf 205788d7-255c-4cf7-b652-bce0c60f2a88 .
79bb0efc-4b0a-4b5a-8c8e-5dfd33bf6d0b http://xmlns.com/foaf/0.1/name "Spiderman" .
79bb0efc-4b0a-4b5a-8c8e-5dfd33bf6d0b http://www.w3.org/1999/02/22-rdf-syntax-ns#type http://xmlns.com/foaf/0.1/Person .
205788d7-255c-4cf7-b652-bce0c60f2a88 http://www.perceive.net/schemas/relationship/enemyOf 79bb0efc-4b0a-4b5a-8c8e-5dfd33bf6d0b .
205788d7-255c-4cf7-b652-bce0c60f2a88 http://xmlns.com/foaf/0.1/name "Green Goblin" .
205788d7-255c-4cf7-b652-bce0c60f2a88 http://www.w3.org/1999/02/22-rdf-syntax-ns#type http://xmlns.com/foaf/0.1/Person .

We can also convert it into “TURTLE” format

FileWriter fileWriter = new FileWriter("results/example-1.ttl");
model.write(fileWriter, "TURTLE");

And the file content is:

_:b0    a       <http://xmlns.com/foaf/0.1/Person> ;
<http://www.perceive.net/schemas/relationship/enemyOf>
_:b1 ;
<http://xmlns.com/foaf/0.1/name>
"Spiderman" .

_:b1 a <http://xmlns.com/foaf/0.1/Person> ;
<http://www.perceive.net/schemas/relationship/enemyOf>
_:b0 ;
<http://xmlns.com/foaf/0.1/name>
"Green Goblin" .

As a second example, by using Jena we can read external RDF file(XML/RDF format in following example) and write into local file.

Model model = ModelFactory.createDefaultModel();

model.read("https://www.w3.org/TR/2002/WD-rdf-schema-20020430/rdfs-namespace.xml", null, "RDF/XML");
FileWriter writer = new FileWriter("results/example-2.ttl");
model.write(writer, "TURTLE");

3. SPARQL Queries in Apache Jena

Now, as I have stated before, Jena supports SPARQL queries as well. Let see some examples. Assume we have the following RDF data

@prefix : <http://example.org/> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .

:atakan
a foaf:Person ;
foaf:name "Atakan" ;
foaf:mbox <mailto:atakan@example.org> ;
foaf:knows :suzan ;
foaf:knows :riza ;
foaf:knows :selen ;
.

:suzan
foaf:name "Suzan" ;
foaf:knows :riza ;
foaf:knows :selen;
.

:riza
foaf:name "Riza" ;
foaf:knows :atakan ;
foaf:knows :selen;
foaf:knows :suzan;
.

:selen
foaf:name "Selen";
foaf:knows :riza;
.

We have 4 people: Atakan, Suzan, Riza and Selen. Now, we will extract people who knows Suzan.

String queryString = "PREFIX foaf: <http://xmlns.com/foaf/0.1/> " +
"PREFIX : <http://example.org/> " +
"SELECT ?name WHERE { " +
" ?person foaf:knows :suzan ." +
" ?person foaf:name ?name ." +
"}";

This is the query string. With Jena, we can run this query on dataset

Query query = QueryFactory.create(queryString);
QueryExecution qexec = QueryExecutionFactory.create(query, model);
try{
ResultSet results = qexec.execSelect();
while (results.hasNext()){
QuerySolution soln = results.nextSolution();
Literal name = soln.getLiteral("name");
System.out.println(name);
}
} finally {
qexec.close();
}

Results as follows

Riza
Atakan

4. Ontologies

As a last example, we will see how ontology API of Jena is working. To be able to use it, first we need to have a base ontology. Our example ontology is here.

First, we need to load our base ontology

String sourceURL = "https://www.w3.org/TR/2003/PR-owl-guide-20031215/wine";
OntModel base = ModelFactory.createOntologyModel();
base.read( sourceURL, "RDF/XML" );

Then, creating new item on top of the ontology.

String namespace = "http://www.w3.org/TR/2003/PR-owl-guide-20031209/wine#";
OntClass wine = base.getOntClass(namespace + "RedBordeaux");
Individual atakanWine = base.createIndividual(namespace + "AtakanWine-madebyAtakan", wine);

So, we take a “RedBordeaux” class and create an individual which is “AtakanWine-madebyAtakan” under that class. This is an assertion.

for (Iterator<Resource> i = atakanWine.listRDFTypes(true); i.hasNext();){
System.out.println(atakanWine.getURI() + " is asserted in class " + i.next());
}

This would produce

http://www.w3.org/TR/2003/PR-owl-guide-20031209/wine#AtakanWine-madebyAtakan is asserted in class http://www.w3.org/TR/2003/PR-owl-guide-20031209/wine#RedBordeaux

To get inferences, we need to do some extra work

OntModel inf = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM_MICRO_RULE_INF, base);

We need to specify ontology model specification to get some kind of inference. Now we are ready to make inference.

atakanWine = inf.getIndividual(namespace + "AtakanWine-madebyAtakan");
for (Iterator<Resource> i = atakanWine.listRDFTypes(true); i.hasNext(); ){
System.out.println(atakanWine.getURI() + " is inferred to be in class " + i.next());
}

And this would result

http://www.w3.org/TR/2003/PR-owl-guide-20031209/wine#AtakanWine-madebyAtakan is inferred to be in class http://www.w3.org/TR/2003/PR-owl-guide-20031209/wine#RedBordeaux
http://www.w3.org/TR/2003/PR-owl-guide-20031209/wine#AtakanWine-madebyAtakan is inferred to be in class http://www.w3.org/TR/2003/PR-owl-guide-20031209/wine#FrenchWine

As you can observe that, since AtakanWine-madebyAtakan is asserted as RedBordeaux, it is also inferred as FrenchWine.

Conclusion

Apache Jena is one of the prominent tools in Semantic Web and Linked Data Applications. To be able to develop advanced applications, one should definetly get its hand dirty on Apache Jena. So, for more semantic web tools, stay tunned 😃

References

--

--