Nasz pierwszy semantyczny program z biblioteką Jena
Wczoraj otrzymałem ciekawy komentarz/zapytanie do artykułu o wnioskowaniu z RDF Schema. Ponieważ przykład daje więcej niż tysiące opisów i wyjaśnień postanowiłem pokazać jak łatwo można wykonać wnioskowanie z RDF Schema korzystając z biblioteki Jena, o której pisałem niedawno.
Jena jest biblioteką dostarczającą API w języku Java. Zacznijmy więc od zebrania potrzebnych narzędzi:
- kompilator i maszyna wirtualna Java, najlepiej w wersji JDK 5.0
- środowisko programistyczne, np.: Eclipse
- biblioteka Jena i biblioteki zależne [pobierz najnowszą wersję tutaj]
W Eclipse rozpoczynamy nowy projekt typu Java, i przegrywamy i dodajemy do projektu JARy dostarczone z biblioteką Jena:

Następnie tworzymy nową klasę i zaczynamy nasz pierwszy "semantyczny" program. Polecam specyfikację biblioteki Jena: http://www.openjena.org/javadoc/index.html.
Zdefiniujmy dwie przestrzenie nazw, jedną na elementy ontologii w RDF Schema, a drugą na zasoby:
static final String NS_ONT = "http://www.semanticschool.com/rdfs#";
static final String NS_DATA = "http://www.semanticschool.com/data#";
Następnie tworzymy nowy model, czyli obiekt który będzie reprezentował tworzony przez nas graf RDF; pamiętajmy że RDF Schema jest również zapisana w postaci grafu RDF.
Model model = ModelFactory.createDefaultModel();
W następnym kroku dodajemy do modelu definicje naszych klas i właściwości.
//[semschool:Samochod] [rdf:type] [rdfs:Class].
Resource cSamochod = model.createResource(NS_ONT+"Samochod");
cSamochod.addProperty(RDF.type, RDFS.Class);
//[semschool:Czlowiek] [rdf:type] [rdfs:Class].
Resource cCzlowiek = model.createResource(NS_ONT+"Czlowiek");
cCzlowiek.addProperty(RDF.type, RDFS.Class);
//[semschool:kierowac] [rdf:type] [rdf:Property].
Property pKierowac = model.createProperty(NS_ONT, "prowadzi");
pKierowac.addProperty(RDF.type, RDF.Property);
pKierowac.addProperty(RDFS.domain, cCzlowiek);
pKierowac.addProperty(RDFS.range, cSamochod);
W tym momencie możemy wypisać nasz graf RDF:
model.write(System.out, "N-TRIPLE");
otrzymamy w następujący wynik (z dokładnością do kolejności trójek):
<http://www.semanticschool.com/rdfs#prowadzi> <http://www.w3.org/2000/01/rdf-schema#range> <http://www.semanticschool.com/rdfs#Samochod> .
<http://www.semanticschool.com/rdfs#prowadzi> <http://www.w3.org/2000/01/rdf-schema#domain> <http://www.semanticschool.com/rdfs#Czlowiek> .
<http://www.semanticschool.com/rdfs#prowadzi> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> .
<http://www.semanticschool.com/rdfs#Czlowiek> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2000/01/rdf-schema#Class> .
<http://www.semanticschool.com/rdfs#Samochod> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2000/01/rdf-schema#Class> .
W kolejnym kroku stwórzmy nowy model bazujący na poprzednim. Model ten nie będzie wykorzystywał wnioskowania bazującego na RDF Schema (tzw. inferencing)
Model model2 = ModelFactory.createDefaultModel();
model2.add(model);
A następnie dodajmy do niego dwa zasoby: [Jan] i [Mazda], oraz stwórzmy zdanie [Jan] [kieruje] [Mazdę].
Resource rJan = model2.createResource(NS_DATA+"Jan");
Resource rMazda = model2.createResource(NS_DATA+"Mazda");
rJan.addProperty(pKierowac, rMazda);
I ponownie jak poprzednio wypiszmy nasz obecny graf:
model2.write(System.out, "N-TRIPLE");
Jak widać poniżej, w wyniku doszło tylko jedno dodatkowe zdanie RDF:
<http://www.semanticschool.com/rdfs#prowadzi> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> .
<http://www.semanticschool.com/rdfs#prowadzi> <http://www.w3.org/2000/01/rdf-schema#domain> <http://www.semanticschool.com/rdfs#Czlowiek> .
<http://www.semanticschool.com/rdfs#prowadzi> <http://www.w3.org/2000/01/rdf-schema#range> <http://www.semanticschool.com/rdfs#Samochod> .
<http://www.semanticschool.com/data#Jan> <http://www.semanticschool.com/rdfs#prowadzi> <http://www.semanticschool.com/data#Mazda> .
<http://www.semanticschool.com/rdfs#Samochod> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2000/01/rdf-schema#Class> .
<http://www.semanticschool.com/rdfs#Czlowiek> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2000/01/rdf-schema#Class> .
A teraz czas na wnioskowanie w akcji. Dzięki bibliotece Jena uruchomienie wnioskowania jest bardzo proste.
Musimy najpierw stworzyć nowy model bazujący na poprzednim. Tym razem, nasz model będzie dedykowany dla wnioskowania z RDF Schema.
InfModel infmodel = ModelFactory.createRDFSModel(model);
Podobnie jak poprzednio dodajemy nasze przykładowe zdanie:
Resource rJan = infmodel.createResource(NS_DATA+"Jan");
Resource rMazda = infmodel.createResource(NS_DATA+"Mazda");
rJan.addProperty(pKierowac, rMazda);
Ponieważ cały model infmodel zawiera również trójki związane ze specyfikacją języka ontologii RDF Schema, więc odpytajmy nasz model tylko o typy zasobów [Jan] i [Mazda].
System.out.println(rJan.getProperty(RDF.type));
System.out.println(rMazda.getProperty(RDF.type));
W wyniku otrzymamy:
[http://www.semanticschool.com/data#Jan, http://www.w3.org/1999/02/22-rdf-syntax-ns#type, http://www.semanticschool.com/rdfs#Czlowiek]
[http://www.semanticschool.com/data#Mazda, http://www.w3.org/1999/02/22-rdf-syntax-ns#type, http://www.semanticschool.com/rdfs#Samochod]
Co tym samym kończy nasz dowód: maszyna wnioskująca poprawnie określiła typ zasobów [Jan] i [Mazda].
Pełen kod projektu razem z biblioteką Jena, znajdziecie [tutaj]

