Skip to content

Commit

Permalink
Fix OBO renderer stuck with untranslatable axioms on concurrent managers
Browse files Browse the repository at this point in the history
protegeproject/protege#501

OBO renderers serialise untranslatable axioms as a functional syntax
string, but the FS renderer needs an ontology to work on. Using the
manager that hosts the ontology to save to create the new ontology
causes a deadlock - the read lock has already been acquired, the
creation of a new ontology tries to engage the write lock -> deadlock.

Fix is to use an injector to get a new manager to create the ontology. A
manager provider would be a better solution but there are static methods
and interface changes involved.
  • Loading branch information
ignazio1977 committed Jan 27, 2019
1 parent 7a9507b commit 49eaf8d
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 46 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.obolibrary.obo2owl;

import org.junit.Test;
import org.semanticweb.owlapi.apibinding.OWLManager;
import org.semanticweb.owlapi.formats.OBODocumentFormat;
import org.semanticweb.owlapi.io.OWLOntologyDocumentTarget;
import org.semanticweb.owlapi.io.StringDocumentTarget;
import org.semanticweb.owlapi.model.IRI;
import org.semanticweb.owlapi.model.OWLDataFactory;
import org.semanticweb.owlapi.model.OWLOntology;
import org.semanticweb.owlapi.model.OWLOntologyCreationException;
import org.semanticweb.owlapi.model.OWLOntologyStorageException;

@SuppressWarnings({"javadoc", "null"})
public class OBOWithUntranslatableAxiomTest {

@Test
public void testNoDeadlock() throws OWLOntologyStorageException, OWLOntologyCreationException {
OWLDataFactory df = OWLManager.getOWLDataFactory();
OWLOntology o = OWLManager.createConcurrentOWLOntologyManager()
.createOntology(IRI.create("urn:test:ontology"));
o.getOWLOntologyManager().addAxiom(o,
df.getOWLSubClassOfAxiom(df.getOWLNothing(), df.getOWLThing()));
OWLOntologyDocumentTarget target = new StringDocumentTarget();
o.saveOntology(new OBODocumentFormat(), target);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
import org.semanticweb.owlapi.model.OWLTransitiveObjectPropertyAxiom;
import org.semanticweb.owlapi.vocab.Namespaces;
import org.semanticweb.owlapi.vocab.OWL2Datatype;
import org.semanticweb.owlapi.vocab.OWLRDFVocabulary;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -123,7 +124,7 @@ public class OWLAPIOwl2Obo {
/**
* The fac.
*/
protected OWLDataFactory fac;
protected final OWLDataFactory fac;
/**
* The obodoc.
*/
Expand Down Expand Up @@ -167,7 +168,6 @@ protected final void init() {
// legacy:
idSpaceMap.put("http://www.obofoundry.org/ro/ro.owl#", "OBO_REL");
untranslatableAxioms = new HashSet<>();
fac = manager.getOWLDataFactory();
apToDeclare = new HashSet<>();
}

Expand All @@ -178,6 +178,7 @@ protected final void init() {
*/
public OWLAPIOwl2Obo(@Nonnull OWLOntologyManager translationManager) {
manager = translationManager;
fac = manager.getOWLDataFactory();
init();
}

Expand Down Expand Up @@ -314,8 +315,7 @@ protected OBODoc tr() {
axioms.sort(null);
axioms.forEach(this::consume);
AxiomType.skipDeclarations().flatMap(t -> getOWLOntology().getAxioms(t).stream())
.map(x->(OWLAxiom)x)
.forEach(this::consume);
.map(x -> (OWLAxiom) x).forEach(this::consume);
if (!untranslatableAxioms.isEmpty() && !discardUntranslatable) {
try {
String axiomString = OwlStringTools.translate(untranslatableAxioms, manager);
Expand Down Expand Up @@ -432,8 +432,8 @@ protected void preProcess() {
LOG.error("ECA did not fit expected pattern: {}", eca);
}
}
manager.removeAxioms(getOWLOntology(), rmAxioms);
manager.addAxioms(getOWLOntology(), newAxioms);
getOWLOntology().getOWLOntologyManager().removeAxioms(getOWLOntology(), rmAxioms);
getOWLOntology().getOWLOntologyManager().addAxioms(getOWLOntology(), newAxioms);
}
}

Expand Down Expand Up @@ -1937,7 +1937,7 @@ protected void tr(@Nonnull OWLClassAssertionAxiom ax) {
return;
}
String clsIRI = ((OWLClass) cls).getIRI().toString();
OWLAnnotationProperty labelProperty = manager.getOWLDataFactory().getRDFSLabel();
IRI labelPropertyIRI = OWLRDFVocabulary.RDFS_LABEL.getIRI();
if (IRI_CLASS_SYNONYMTYPEDEF.equals(clsIRI)) {
Frame f = getObodoc().getHeaderFrame();
Clause c = new Clause(OboFormatTag.TAG_SYNONYMTYPEDEF.getTag());
Expand All @@ -1956,7 +1956,7 @@ protected void tr(@Nonnull OWLClassAssertionAxiom ax) {
String scopeValue = null;
for (OWLAnnotation ann : getAnnotationObjects(indv, getOWLOntology(), null)) {
String value = ((OWLLiteral) ann.getValue()).getLiteral();
if (ann.getProperty().equals(labelProperty)) {
if (ann.getProperty().getIRI().equals(labelPropertyIRI)) {
nameValue = '"' + value + '"';
} else {
scopeValue = value;
Expand All @@ -1983,7 +1983,7 @@ protected void tr(@Nonnull OWLClassAssertionAxiom ax) {
String nameValue = "";
for (OWLAnnotation ann : getAnnotationObjects(indv, getOWLOntology(), null)) {
String value = ((OWLLiteral) ann.getValue()).getLiteral();
if (ann.getProperty().equals(labelProperty)) {
if (ann.getProperty().getIRI().equals(labelPropertyIRI)) {
nameValue = '"' + value + '"';
}
}
Expand Down
85 changes: 48 additions & 37 deletions oboformat/src/main/java/org/obolibrary/obo2owl/OwlStringTools.java
Original file line number Diff line number Diff line change
@@ -1,71 +1,79 @@
package org.obolibrary.obo2owl;

import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import org.semanticweb.owlapi.functional.parser.OWLFunctionalSyntaxOWLParser;
import org.semanticweb.owlapi.formats.FunctionalSyntaxDocumentFormat;
import org.semanticweb.owlapi.functional.renderer.OWLFunctionalSyntaxRenderer;
import org.semanticweb.owlapi.io.OWLOntologyDocumentSource;
import org.semanticweb.owlapi.io.OWLParserException;
import org.semanticweb.owlapi.io.OWLRendererException;
import org.semanticweb.owlapi.io.StringDocumentSource;
import org.semanticweb.owlapi.model.IRI;
import org.semanticweb.owlapi.model.OWLAxiom;
import org.semanticweb.owlapi.model.OWLOntology;
import org.semanticweb.owlapi.model.OWLOntologyCreationException;
import org.semanticweb.owlapi.model.OWLOntologyFactory;
import org.semanticweb.owlapi.model.OWLOntologyManager;
import org.semanticweb.owlapi.model.UnloadableImportException;
import org.semanticweb.owlapi.utilities.Injector;

/**
* Tools to read and write a set of owl axioms to/from a string. Used to
* preserve untranslatable axioms in an owl2obo conversion.
* Tools to read and write a set of owl axioms to/from a string. Used to preserve untranslatable
* axioms in an owl2obo conversion.
*/
public class OwlStringTools {

/**
* Exception indicating an un-recoverable error during the handling of axiom
* strings.
* Exception indicating an un-recoverable error during the handling of axiom strings.
*/
public static class OwlStringException extends Exception {

// generated
private static final long serialVersionUID = 40000L;

/**
* @param cause
* cause
* @param cause cause
*/
protected OwlStringException(Throwable cause) {
super(cause);
}
}

private static final Injector injector = injector();

/**
* Create a string for the given set of axioms. Return null for empty sets
* or if the set is null.
* Create a string for the given set of axioms. Return null for empty sets or if the set is
* null.
*
* @param axioms
* axioms
* @param translationManager
* translationManager
* @param axioms axioms
* @param translationManager translationManager
* @return string or null
* @throws OwlStringException
* OwlStringException
* @throws OwlStringException OwlStringException
* @see #translate(String, OWLOntologyManager)
*/
@Nullable
public static String translate(@Nullable Set<OWLAxiom> axioms, @Nonnull OWLOntologyManager translationManager)
throws OwlStringException {
public static String translate(@Nullable Set<OWLAxiom> axioms,
@Nonnull OWLOntologyManager translationManager) throws OwlStringException {
if (axioms == null || axioms.isEmpty()) {
return null;
}
try {
OWLOntology ontology = translationManager.createOntology();
translationManager.addAxioms(ontology, axioms);

OWLOntologyManager m =
injector.inject(injector.getImplementation(OWLOntologyManager.class));
Set<OWLOntologyFactory> set = new HashSet<>();
translationManager.getOntologyFactories().forEach(set::add);
m.setOntologyFactories(set);
OWLOntology ontology = m.createOntology();
ontology.getOWLOntologyManager().addAxioms(ontology, axioms);
OWLFunctionalSyntaxRenderer r = new OWLFunctionalSyntaxRenderer();
Writer writer = new StringWriter();
r.render(ontology, writer);
Expand All @@ -77,39 +85,42 @@ public static String translate(@Nullable Set<OWLAxiom> axioms, @Nonnull OWLOntol
}
}

private static Injector injector() {
Injector i = new Injector();
i.bindToOne(() -> new ReentrantReadWriteLock(), ReadWriteLock.class);
return i;
}

/**
* Parse the axioms from the given axiom string. Returns null for empty and
* null strings.
* Parse the axioms from the given axiom string. Returns null for empty and null strings.
*
* @param axioms
* axioms
* @param translationManager
* translationManager
* @param axioms axioms
* @param translationManager translationManager
* @return set of axioms or null
* @throws OwlStringException
* OwlStringException
* @throws OwlStringException OwlStringException
* @see #translate(Set,OWLOntologyManager)
*/
@Nullable
public static Set<OWLAxiom> translate(@Nullable String axioms, @Nonnull OWLOntologyManager translationManager)
throws OwlStringException {
public static Set<OWLAxiom> translate(@Nullable String axioms,
@Nonnull OWLOntologyManager translationManager) throws OwlStringException {
if (axioms == null || axioms.isEmpty()) {
return null;
}
try {
OWLFunctionalSyntaxOWLParser p = new OWLFunctionalSyntaxOWLParser();
OWLOntologyDocumentSource documentSource = new StringDocumentSource(axioms);
OWLOntology ontology = translationManager.createOntology();
p.parse(documentSource, ontology, translationManager.getOntologyLoaderConfiguration());
return ontology.getAxioms();
OWLOntologyDocumentSource documentSource = new StringDocumentSource(axioms,
IRI.generateDocumentIRI(), new FunctionalSyntaxDocumentFormat(), null);
OWLOntologyManager m =
injector.inject(injector.getImplementation(OWLOntologyManager.class));
Set<OWLOntologyFactory> set = new HashSet<>();
translationManager.getOntologyFactories().forEach(set::add);
m.setOntologyFactories(set);
return m.loadOntologyFromOntologyDocument(documentSource).getAxioms();
} catch (UnloadableImportException e) {
throw new OwlStringException(e);
} catch (OWLOntologyCreationException e) {
throw new OwlStringException(e);
} catch (OWLParserException e) {
throw new OwlStringException(e);
} catch (IOException e) {
throw new OwlStringException(e);
}
}
}

0 comments on commit 49eaf8d

Please sign in to comment.