Jena tip: importing ontologies from the database
12th Nov, 2007
Various people have asked on jena-dev how to reference ontologies that are known to be in a Jena database model, without going out to the web to resolve the source URI. Here's one solution.
Background
One OWL ontology can import another by naming the imported URI in an owl:imports
statement. For example:
<http://example.com/ontologies/a> owl:imports <http://example.com/ontologies/b>.
By default, when Jena finds such statements in an OntModel
during the load process, it will attempt
to resolve the URI and load the import into a sub-model. This works if the URL is visible from the loading machine,
though it can be slow. The LocationMapper provides a general mechanism for re-writing URL's, so
that http://example.com/ontologies/a can be transparently re-written to file:///var/cache/ontologies/a.
However, this mechanism doesn't support re-directing the import to a pre-existing model in a persistent store,
such as the a Jena DB store. A single Jena database store can hold N different models, each labelled with a
different name string. The convention with ontology documents is that the label is the source URI.
Solution
If the location mapper can't be used to re-direct the imports to the database, the next obvious intervention
point is the OntDocumentManager, or ODM for short. The ODM is a helper class that assists
OntModel's in the document importing process. Each OntModel has an ODM. By default,
this is a single shared instance, but the OntModelSpec that is used to construct the OntModel
can have a non-default ODM, which will be inherited by the constructed OntModel. So, the essence
of the solution is to customise an ODM sub-class to intercept requests to import a model URI, and check to see
if it's already in the database.
The example class DbAwareDocumentManager (source)
does just that. It's fairly simple: we just have to override the loadImports method. At the time
of writing (Nov 2007), a couple of crucial methods in DocumentManager are incorrectly marked private,
so they have to be cloned in the sub-class body. This will be fixed in the next Jena release.
protected void loadImport( OntModel model, String importURI, List readQueue ) {
if (m_processImports) {
log.debug( "OntDocumentManager loading " + importURI );
// add this model to occurs check list
model.addLoadedImport( importURI );
// check first to see if we have already loaded this model into the db
Model in = checkExistingDbImport( importURI );
// otherwise, try to find it
if (in == null) {
in = fetchPossiblyCachedImportModel( model, importURI );
}
To test the updated document manager, and illustrate how to use it, I've added an example program
DbImportTest.java (source). This creates
two vestigial ontologies in a local MySQL database, named, imaginatively, A and B. A third ontology,
C, imports A and B. Since all three are declared with example.org URI's, they can't
actually be read from the web. Without the local ODM, Jena reports an error when loading C. With
the updated ODM, however, we can see that the triples from the imports are correctly loaded.
protected void loadImportingOntology() {
String SOURCE = "@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>.\n" +
"@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>.\n" +
"@prefix owl: <http://www.w3.org/2002/07/owl#>.\n" +
"<" + ONTOLOGY_C + "> a owl:Ontology \n" +
" ; owl:imports <" + ONTOLOGY_A + ">\n" +
" ; owl:imports <" + ONTOLOGY_B + ">.\n";
log.debug( "About to load source ontology:" );
log.debug( SOURCE );
// create an ont model spec that uses a custom document manager to
// look for imports in the database
OntModelSpec oms = getMaker();
oms.setDocumentManager( new DbAwareDocumentManager( m_maker ) );
// create the ontology model
Model base = m_maker.createModel( ONTOLOGY_C );
OntModel om = ModelFactory.createOntologyModel( oms, base );
// read in some content which does importing
om.read( new StringReader( SOURCE ), ONTOLOGY_C, "N3" );
// as a test, write everything
System.out.println( "Combined model contents:" );
om.writeAll( System.out, "N3", null );
}
