Introduction

This is a quick guide to working with ontologies using the OWLAPI library. The guide assumes some previous knowledge, primarily of what an ontology is and some of the basics of working with them - most of these are covered by the excellent Protege Tutorial, which introduces many of the basic concepts in ontology through the desktop tool Protege, which is one of the seminal tools for manually working with ontologies.

The guide also assumes some basic programming knowledge, and the language we will be using Groovy. Groovy is a JVM language, and thus most Java programmers should be able to infer what is going on, but if this is not the case, or if you aren’t yet versed in much programming, check out my Groovy Guide.

Given the previous requirement, you may have been able to deduct that OWLAPI is a JVM library. It is written in Java, but thanks to the JVM we are able to work with it in a very similar way using any JVM-compatible language. We have chosen Groovy because it is a lot nicer than Java, while preserving a syntax which will be familiar to most programmers. Groovy is also the language our research group currently does most of its work with OWLAPI in.

OWLAPI is a relatively well designed library, and supports almost all the functionality you could want involving an ontology. Instead of covering the conceptual basics, here we will cover the basics of loading an ontology, and then some common use cases, and then an example of building a hierarchy out of some data. If you prefer to learn from real-world code, I have included some links to projects which use OWLAPI at the end of this guide.

Loading an Ontology

To start with, we will go a little beyond something analogous to Hello World, and create a small script which loads and then saves on ontology from an online repository.

@Grapes([
  @Grab(group='net.sourceforge.owlapi', module='owlapi-api', version='4.1.0'),
  @Grab(group='net.sourceforge.owlapi', module='owlapi-apibinding', version='4.1.0'),
  @Grab(group='net.sourceforge.owlapi', module='owlapi-impl', version='4.1.0'),
  @Grab(group='net.sourceforge.owlapi', module='owlapi-parsers', version='4.1.0'),
  @Grab(group='org.slf4j', module='slf4j-log4j12', version='1.7.10'),
  @GrabConfig(systemClassLoader=true)
])
 
import org.semanticweb.owlapi.io.* 
import org.semanticweb.owlapi.model.*
import org.semanticweb.owlapi.util.*
import org.semanticweb.owlapi.apibinding.*

def ontology
def manager = OWLManager.createOWLOntologyManager()
def config = new OWLOntologyLoaderConfiguration()
def iri = new IRIDocumentSource(IRI.create("http://aber-owl.net/ontology/"+args[0]+"/download"))
def dest = new File(args[1])

config.setFollowRedirects(true)
config.setMissingImportHandlingStrategy(MissingImportHandlingStrategy.SILENT)

println 'Downloading and loading ' + args[0] + ' ontology from AberOWL'
try {
  ontology = manager.loadOntologyFromOntologyDocument(iri, config)

  println 'Saving ontology to ' + args[1]
  manager.saveOntology(ontology, IRI.create(dest.toURI()))
} catch(e) {
  println 'Oh no! There was a problem: ' + e.getClass().getSimpleName() 
}

Taking your first command line argument, it creates an ontology IRI (URIs, but Internationalized) pointing at the download link for the given ontology in AberOWL (an online ontology repository), loads this up in OWLAPI, and then saves it in the file determined by your second command line argument. So for example, we could run groovy load.groovy BFO bfo.owl, and our script would download, load and then save BFO into the file ‘bfo.owl’ in the current directory.

The advantage of this over more obvious approaches, using curl for example, is that by first loading the ontology in OWLAPI, we ensure it is a valid ontology before saving it to our disk permanently. This is because if there is a problem with loading the ontology, in our loadOntologyFromOntologyDocument line, we will skip out to the catch statement, before ever reaching saveOntology.

As well as for cases of actual invalid ontologies, this works for trivial cases, such as in the case we pass a random non-existent ontology ID, because OWLAPI will attempt to load the ‘Page Not Found’ as an ontology!

At the top of the script, we see our grapes, and our import statements, which grab various parts of the OWLAPI library - these should be sufficient for most purposes, until we start working with reasoners, and so I will not repeat this part in future example scripts until the Reasoning an Ontology section (though they are necessary to include to run the scripts).

We then create our OWLOntologyManager from the OWLManager factory. This class will be necessary in almost all OWLAPI applications, as it ‘manages’ the ontologies, and is how we will create, load and save our ontologies.

Then we define our IRI. As you can see, these can be either web links or files (or technically just about anything, but mostly just these). To be loadable by OWLAPI, these also need to be passed through into an IRIDocumentSource.

To load our ontologies, we need another class, the OWLOntologyLoaderConfiguration. After creating this class, we set some sane options. The first being to ask our ontology loader to load the additional ontologies specified in its owl:import closure (this is a list of IRIs to other ontologies which our target ontology uses). We also tell it not to get upset and raise an exception if one of the imports in the previously mentioned closure aren’t obtainable or loadable.

Using these objects, we can enter our try closure, and use the loadOntologyFromOntology method, passing the IRI and our configuration. This will acquire the ontology from whatever source is encoded in the IRI object, and then parse it. An exception will be thrown if it is not obtainable or loadable for any reason.

When loading the ontology, we assign the result to our ontology variable. This will be an object of the class OWLOntology, which, predictably, represents an ontology!

Upon successful completion, we ask our ontology manager to save the ontology to our given file. Note that we create a new IRI out of the File object; it should be easy to see that we would be able to load an ontology from a file using the same method shown here, then passing it into an IRIDocumentSource, in the same way as above. While this works, OWLAPI is actually a little kinder to us in this situation and allows us to use the loadOntology on a simple File object.

Reasoning an Ontology

Finding Unsatisfiable Classes and Explanations

Building an Ontology

Real-world Examples and Further Reading