Skip to contentMethod: writeToFile()
1: /*
2: * JOPA
3: * Copyright (C) 2023 Czech Technical University in Prague
4: *
5: * This library is free software; you can redistribute it and/or
6: * modify it under the terms of the GNU Lesser General Public
7: * License as published by the Free Software Foundation; either
8: * version 3.0 of the License, or (at your option) any later version.
9: *
10: * This library is distributed in the hope that it will be useful,
11: * but WITHOUT ANY WARRANTY; without even the implied warranty of
12: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13: * Lesser General Public License for more details.
14: *
15: * You should have received a copy of the GNU Lesser General Public
16: * License along with this library.
17: */
18: package cz.cvut.kbss.ontodriver.owlapi.connector;
19:
20: import cz.cvut.kbss.ontodriver.OntologyStorageProperties;
21: import cz.cvut.kbss.ontodriver.config.DriverConfigParam;
22: import cz.cvut.kbss.ontodriver.config.DriverConfiguration;
23: import cz.cvut.kbss.ontodriver.exception.OntoDriverException;
24: import cz.cvut.kbss.ontodriver.owlapi.change.TransactionalChange;
25: import cz.cvut.kbss.ontodriver.owlapi.config.OwlapiConfigParam;
26: import cz.cvut.kbss.ontodriver.owlapi.exception.InvalidOntologyIriException;
27: import cz.cvut.kbss.ontodriver.owlapi.exception.OntologySnapshotException;
28: import cz.cvut.kbss.ontodriver.owlapi.exception.OntologyStorageException;
29: import cz.cvut.kbss.ontodriver.owlapi.exception.OwlapiDriverException;
30: import cz.cvut.kbss.ontodriver.owlapi.exception.ReasonerNotAvailableException;
31: import cz.cvut.kbss.ontodriver.owlapi.util.DefaultOntologyIriMapper;
32: import cz.cvut.kbss.ontodriver.owlapi.util.MappingFileParser;
33: import org.semanticweb.owlapi.apibinding.OWLManager;
34: import org.semanticweb.owlapi.model.AddImport;
35: import org.semanticweb.owlapi.model.IRI;
36: import org.semanticweb.owlapi.model.OWLDataFactory;
37: import org.semanticweb.owlapi.model.OWLOntology;
38: import org.semanticweb.owlapi.model.OWLOntologyChange;
39: import org.semanticweb.owlapi.model.OWLOntologyCreationException;
40: import org.semanticweb.owlapi.model.OWLOntologyIRIMapper;
41: import org.semanticweb.owlapi.model.OWLOntologyManager;
42: import org.semanticweb.owlapi.model.OWLOntologyStorageException;
43: import org.semanticweb.owlapi.reasoner.OWLReasoner;
44: import org.semanticweb.owlapi.reasoner.OWLReasonerFactory;
45: import org.slf4j.Logger;
46: import org.slf4j.LoggerFactory;
47:
48: import java.io.FileNotFoundException;
49: import java.lang.reflect.InvocationTargetException;
50: import java.net.URI;
51: import java.util.List;
52: import java.util.Optional;
53: import java.util.concurrent.locks.Lock;
54: import java.util.concurrent.locks.ReentrantReadWriteLock;
55: import java.util.function.Consumer;
56: import java.util.function.Function;
57: import java.util.stream.Collectors;
58:
59: /**
60: * Default file-based storage connector.
61: * <p>
62: * Each call to {@link #getOntologySnapshot()} returns a new snapshot of the current state of the ontology. The changes
63: * are the applied to a shared ontology, which represents the current state of the underlying storage.
64: * <p>
65: * Note: This connector currently does not handle concurrent updates.
66: */
67: public class BasicStorageConnector extends AbstractConnector {
68:
69: private static final Logger LOG = LoggerFactory.getLogger(BasicStorageConnector.class);
70:
71: private static final ReentrantReadWriteLock LOCK = new ReentrantReadWriteLock();
72: private static final Lock READ = LOCK.readLock();
73: private static final Lock WRITE = LOCK.writeLock();
74:
75: private OWLOntologyManager ontologyManager;
76: private OWLOntology ontology;
77: private OWLReasoner reasoner;
78: private OWLReasonerFactory reasonerFactory;
79:
80: private OWLOntologyIRIMapper iriMapper;
81:
82: BasicStorageConnector(DriverConfiguration configuration) throws OwlapiDriverException {
83: super(configuration);
84: }
85:
86: @Override
87: protected void initializeConnector() throws OwlapiDriverException {
88: if (isOpen()) {
89: return;
90: }
91: final OntologyStorageProperties storageProperties = configuration.getStorageProperties();
92: LOG.debug("Loading ontology {} from {}.", storageProperties.getOntologyURI(),
93: storageProperties.getPhysicalURI());
94: resolveIriMapper();
95: this.ontologyManager = OWLManager.createOWLOntologyManager();
96: setIriMapper(ontologyManager);
97: loadOntology(storageProperties);
98: initializeReasonerFactory();
99: this.reasoner = getReasoner(ontology);
100: }
101:
102: private void resolveIriMapper() {
103: if (configuration.isSet(OwlapiConfigParam.MAPPING_FILE_LOCATION)) {
104: this.iriMapper = new DefaultOntologyIriMapper(new MappingFileParser(configuration));
105: }
106: }
107:
108: private void setIriMapper(OWLOntologyManager manager) {
109: if (iriMapper != null) {
110: manager.getIRIMappers().add(new DefaultOntologyIriMapper(new MappingFileParser(configuration)));
111: }
112: }
113:
114: private void loadOntology(OntologyStorageProperties storageProperties) throws OwlapiDriverException {
115: final Optional<IRI> ontologyIri = storageProperties.getOntologyURI().map(IRI::create);
116: try {
117: this.ontology =
118: ontologyManager.loadOntologyFromOntologyDocument(IRI.create(storageProperties.getPhysicalURI()));
119: if (!ontology.getOntologyID().getOntologyIRI().equals(ontologyIri)) {
120: throw new InvalidOntologyIriException(
121: "Expected ontology with IRI " + storageProperties.getOntologyURI() +
122: " but the loaded ontology has IRI " + ontology.getOntologyID().getOntologyIRI());
123: }
124: } catch (OWLOntologyCreationException e) {
125: if (e.getCause() instanceof FileNotFoundException) {
126: LOG.trace("Ontology file {} does not exist.", storageProperties.getPhysicalURI());
127: } else {
128: LOG.trace("Unable to load ontology from document.", e);
129: }
130: tryCreatingOntology(ontologyIri.orElse(null));
131: }
132: }
133:
134: private void tryCreatingOntology(IRI ontologyIri) throws OwlapiDriverException {
135: LOG.trace("Creating new ontology in {}.", configuration.getStorageProperties().getPhysicalURI());
136: try {
137: this.ontology = ontologyManager.createOntology(ontologyIri);
138: ontology.saveOntology(IRI.create(configuration.getStorageProperties().getPhysicalURI()));
139: } catch (OWLOntologyCreationException | OWLOntologyStorageException e) {
140: throw new OwlapiDriverException(
141: "Unable to create ontology in " + configuration.getStorageProperties().getPhysicalURI(), e);
142: }
143: }
144:
145: private void initializeReasonerFactory() {
146: final String reasonerFactoryClass = configuration.getProperty(DriverConfigParam.REASONER_FACTORY_CLASS);
147: if (reasonerFactoryClass == null) {
148: LOG.warn("Reasoner factory class not found. Reasoner won't be available.");
149: return;
150: }
151: try {
152: this.reasonerFactory = (OWLReasonerFactory) Class.forName(reasonerFactoryClass).getDeclaredConstructor()
153: .newInstance();
154: } catch (InstantiationException | InvocationTargetException | IllegalAccessException e) {
155: final String msg = "Unable to instantiate reasoner factory class " + reasonerFactoryClass;
156: LOG.error(msg);
157: throw new ReasonerNotAvailableException(msg, e);
158: } catch (NoSuchMethodException e) {
159: final String msg = "Reasoner factory class " + reasonerFactoryClass + " does not have a public no-arg constructor.";
160: LOG.error(msg);
161: throw new ReasonerNotAvailableException(msg, e);
162: } catch (ClassNotFoundException e) {
163: final String msg = "Reasoner factory class " + reasonerFactoryClass + " not found!";
164: LOG.error(msg);
165: throw new ReasonerNotAvailableException(msg, e);
166: }
167: }
168:
169: @Override
170: public URI getOntologyUri() {
171: assert ontology.getOntologyID().getOntologyIRI().isPresent();
172:
173: return ontology.getOntologyID().getOntologyIRI().get().toURI();
174: }
175:
176: @Override
177: public OntologySnapshot getOntologySnapshot() {
178: ensureOpen();
179: READ.lock();
180: try {
181: final OWLOntology snapshot = ontologyManager.createOntology();
182: cloneOntologyContent(snapshot);
183: return new OntologySnapshot(snapshot, ontologyManager, ontologyManager.getOWLDataFactory(),
184: getReasoner(snapshot));
185: } catch (OWLOntologyCreationException e) {
186: throw new OntologySnapshotException("Unable to create ontology snapshot.", e);
187: } finally {
188: READ.unlock();
189: }
190: }
191:
192: private void cloneOntologyContent(OWLOntology target) {
193: ontologyManager.addAxioms(target, ontology.axioms());
194: ontologyManager.applyChanges(
195: ontology.importsDeclarations().map(i -> new AddImport(target, i)).collect(Collectors.toList()));
196: }
197:
198: private OntologySnapshot getLiveOntology() {
199: ensureOpen();
200: return new OntologySnapshot(ontology, ontologyManager, ontologyManager.getOWLDataFactory(), reasoner);
201: }
202:
203: @Override
204: public <R> R executeRead(Function<OntologySnapshot, R> function) {
205: ensureOpen();
206: READ.lock();
207: try {
208: return function.apply(getLiveOntology());
209: } finally {
210: READ.unlock();
211: }
212: }
213:
214: @Override
215: public void executeWrite(Consumer<OntologySnapshot> function) {
216: ensureOpen();
217: WRITE.lock();
218: try {
219: function.accept(getLiveOntology());
220: } finally {
221: WRITE.unlock();
222: }
223: }
224:
225: private OWLReasoner getReasoner(OWLOntology ontology) {
226: if (reasonerFactory == null) {
227: LOG.warn("Creating ontology snapshot without reasoner, because reasoner factory class was not specified.");
228: return null;
229: }
230: return reasonerFactory.createReasoner(ontology);
231: }
232:
233: @Override
234: public void applyChanges(List<TransactionalChange> changes) {
235: ensureOpen();
236: assert changes != null;
237: WRITE.lock();
238: try {
239: final OWLDataFactory df = ontologyManager.getOWLDataFactory();
240: final List<OWLOntologyChange> toApply = changes.stream()
241: .flatMap(o -> o.toOwlChanges(ontology).stream())
242: .collect(Collectors.toList());
243: ontologyManager.applyChanges(toApply);
244: try {
245: writeToFile();
246: } catch (OntologyStorageException e) {
247: LOG.error("Unable to write out ontology." + e);
248: }
249: } finally {
250: WRITE.unlock();
251: }
252: }
253:
254: @Override
255: public void closeSnapshot(OntologySnapshot snapshot) {
256: ensureOpen();
257: assert snapshot != null;
258: ontologyManager.removeOntology(snapshot.getOntology());
259: }
260:
261: @Override
262: void reloadData() throws OwlapiDriverException {
263: WRITE.lock();
264: try {
265: ontologyManager.clearOntologies();
266: loadOntology(configuration.getStorageProperties());
267: this.reasoner = getReasoner(ontology);
268: } finally {
269: WRITE.unlock();
270: }
271: }
272:
273: @Override
274: public void close() throws OntoDriverException {
275: if (!isOpen()) {
276: return;
277: }
278: WRITE.lock();
279: try {
280: writeToFile();
281: super.close();
282: } finally {
283: WRITE.unlock();
284: }
285: }
286:
287: private void writeToFile() throws OntologyStorageException {
288: try {
289: ontologyManager.saveOntology(ontology, IRI.create(configuration.getStorageProperties().getPhysicalURI()));
290: } catch (OWLOntologyStorageException e) {
291: throw new OntologyStorageException(
292: "Error when saving ontology to " + configuration.getStorageProperties().getPhysicalURI(), e);
293: }
294: }
295: }