Skip to content

Method: getDefaultGraph()

1: /*
2: * JOPA
3: * Copyright (C) 2024 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.jena.connector;
19:
20: import cz.cvut.kbss.ontodriver.config.DriverConfigParam;
21: import cz.cvut.kbss.ontodriver.config.DriverConfiguration;
22: import cz.cvut.kbss.ontodriver.jena.exception.ReasonerInitializationException;
23: import org.apache.jena.query.Dataset;
24: import org.apache.jena.query.DatasetFactory;
25: import org.apache.jena.rdf.model.InfModel;
26: import org.apache.jena.rdf.model.Model;
27: import org.apache.jena.rdf.model.ModelFactory;
28: import org.apache.jena.rdf.model.Property;
29: import org.apache.jena.reasoner.IllegalParameterException;
30: import org.apache.jena.reasoner.Reasoner;
31: import org.apache.jena.reasoner.ReasonerFactory;
32: import org.apache.jena.reasoner.ValidityReport;
33: import org.apache.jena.system.Txn;
34: import org.apache.jena.vocabulary.ReasonerVocabulary;
35:
36: import java.lang.reflect.InvocationTargetException;
37: import java.lang.reflect.Method;
38: import java.util.*;
39: import java.util.stream.Collectors;
40:
41: import static org.apache.jena.rdf.model.ResourceFactory.createProperty;
42:
43: class SnapshotStorageWithInference extends SnapshotStorage {
44:
45: /**
46: * Configuration parameters supported by at least one of the Jena reasoners. Used to pre-filter reasoner config.
47: */
48: private static final Set<String> SUPPORTED_CONFIG = new HashSet<>(Arrays.asList(
49: ReasonerVocabulary.PROPderivationLogging.getURI(),
50: ReasonerVocabulary.PROPenableCMPScan.getURI(),
51: ReasonerVocabulary.PROPenableFunctorFiltering.getURI(),
52: ReasonerVocabulary.PROPenableOWLTranslation.getURI(),
53: ReasonerVocabulary.PROPenableTGCCaching.getURI(),
54: ReasonerVocabulary.PROPruleMode.getURI(),
55: ReasonerVocabulary.PROPruleSet.getURI(),
56: ReasonerVocabulary.PROPsetRDFSLevel.getURI(),
57: ReasonerVocabulary.PROPtraceOn.getURI()
58: ));
59:
60: private final ReasonerFactory reasonerFactory;
61: private final Map<String, String> reasonerConfig;
62:
63: private final Map<String, InfModel> inferredGraphs = new HashMap<>();
64:
65: SnapshotStorageWithInference(DriverConfiguration configuration, Map<String, String> reasonerConfig) {
66: super(configuration);
67: this.reasonerFactory = initReasonerFactory(configuration);
68: this.reasonerConfig = reasonerConfig.entrySet().stream()
69: .filter(e -> SUPPORTED_CONFIG.contains(e.getKey()))
70: .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
71: }
72:
73: private static ReasonerFactory initReasonerFactory(DriverConfiguration configuration) {
74: final String factoryClass = configuration.getProperty(DriverConfigParam.REASONER_FACTORY_CLASS, "");
75: LOG.trace("Creating reasoner using reasoner factory class {}.", factoryClass);
76: try {
77: final Class<? extends ReasonerFactory> rfClass =
78: (Class<? extends ReasonerFactory>) Class.forName(factoryClass);
79: final Method instanceMethod = rfClass.getMethod("theInstance");
80: return (ReasonerFactory) instanceMethod.invoke(null);
81: } catch (ClassNotFoundException e) {
82: throw new ReasonerInitializationException("Reasoner factory class " + factoryClass + " not found.", e);
83: } catch (NoSuchMethodException e) {
84: throw new ReasonerInitializationException("Class " + factoryClass +
85: " is not a ReasonerFactory implementation or does not contain static 'theInstance' method.");
86: } catch (IllegalAccessException | InvocationTargetException e) {
87: throw new ReasonerInitializationException(
88: "Unable to instantiate Jena reasoner from factory " + factoryClass, e);
89: }
90: }
91:
92: @Override
93: void addCentralData(Dataset central) {
94: Txn.executeRead(central, () -> {
95: final Iterator<String> it = central.listNames();
96: InfModel clonedModel = cloneModel(central.getDefaultModel());
97: inferredGraphs.put(null, clonedModel);
98: // Lazily initialize the dataset to force it to use the cloned model (more specifically its Graph)
99: // This ensures the dataset and the model in inferredGraphs use the same underlying graph as default
100: // This change is force by Jena 4.x, where Dataset.setDefaultModel just copies data to the default
101: // graph instead of using it directly (which is what it does for named graphs)
102: this.dataset = DatasetFactory.create(clonedModel);
103: while (it.hasNext()) {
104: final String name = it.next();
105: clonedModel = cloneModel(central.getNamedModel(name));
106: inferredGraphs.put(name, clonedModel);
107: dataset.addNamedModel(name, clonedModel);
108: }
109: });
110: }
111:
112: private InfModel cloneModel(Model model) {
113: return ModelFactory.createInfModel(createReasoner(), ModelFactory.createDefaultModel().add(model));
114: }
115:
116: @Override
117: public InfModel getDefaultGraph() {
118: return inferredGraphs.get(null);
119: }
120:
121: private Reasoner createReasoner() {
122: final Reasoner reasoner = reasonerFactory.create(null);
123: reasonerConfig.forEach((key, value) -> {
124: final Property prop = createProperty(key);
125: try {
126: reasoner.setParameter(prop, value);
127: } catch (IllegalParameterException ex) {
128: LOG.error("Failed to set property " + prop + " on reasoner.", ex);
129: }
130: });
131: return reasoner;
132: }
133:
134: Model getRawDefaultGraph() {
135: return inferredGraphs.containsKey(null) ? inferredGraphs.get(null).getRawModel() : dataset.getDefaultModel();
136: }
137:
138: @Override
139: public InfModel getNamedGraph(String context) {
140: return inferredGraphs.computeIfAbsent(context, c -> {
141: // If the context does not exist, we need to create it, so that the default Dataset behavior is preserved
142: final InfModel model = ModelFactory.createInfModel(createReasoner(), ModelFactory.createDefaultModel());
143: dataset.addNamedModel(context, model);
144: return model;
145: });
146: }
147:
148: Model getRawNamedGraph(String context) {
149: return inferredGraphs.containsKey(context) ? inferredGraphs.get(context).getRawModel() :
150: dataset.getNamedModel(context);
151: }
152:
153: ValidityReport checkConsistency(String context) {
154: return context != null ? getNamedGraph(context).validate() : getDefaultGraph().validate();
155: }
156: }