» home
» articles index

Creative Commons License
This work is licensed under a Creative Commons Attribution-ShareAlike 2.0 License.

Valid XHTML 1.0 Strict
Valid CSS

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:

<//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 );
}