From 8e31cfc8ba884085125e2550a0e2ba7c1725b56d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martynas=20Jusevi=C4=8Dius?= Date: Thu, 18 Jun 2026 13:36:40 +0200 Subject: [PATCH 01/14] Migrate vocabularies to Jena ontapi; add ontology characterization tests - Bump client dependency to 4.3.1-SNAPSHOT (pulls jena-ontapi transitively via Core) - Migrate 19 vocabularies off the deprecated-for-removal org.apache.jena.ontology API to org.apache.jena.ontapi (OntModelFactory/OntSpecification), with a top-of-class static JenaSystem.init() guard (ontapi NPEs if a vocab class is the first Jena touch) - Add characterization tests pinning current ontology behavior so the migration can be proven to retain it: imports closure + RDFS materialization, dual-key cache (addDocumentModel), SPARQL-first resolution (OntologyModelGetter), vocab triples Co-Authored-By: Claude Opus 4.8 (1M context) --- pom.xml | 4 +- .../linkeddatahub/vocabulary/ACL.java | 45 ++-- .../linkeddatahub/vocabulary/Admin.java | 14 +- .../linkeddatahub/vocabulary/Cert.java | 28 ++- .../linkeddatahub/vocabulary/DH.java | 26 ++- .../linkeddatahub/vocabulary/Default.java | 17 +- .../linkeddatahub/vocabulary/FOAF.java | 46 ++-- .../linkeddatahub/vocabulary/Google.java | 20 +- .../linkeddatahub/vocabulary/LACL.java | 38 ++-- .../linkeddatahub/vocabulary/LAPP.java | 46 ++-- .../linkeddatahub/vocabulary/LDH.java | 56 ++--- .../linkeddatahub/vocabulary/LDHC.java | 107 ++++----- .../linkeddatahub/vocabulary/LDHT.java | 21 +- .../linkeddatahub/vocabulary/NFO.java | 21 +- .../linkeddatahub/vocabulary/ORCID.java | 20 +- .../linkeddatahub/vocabulary/PROV.java | 38 ++-- .../linkeddatahub/vocabulary/SIOC.java | 206 +++++++++--------- .../linkeddatahub/vocabulary/VoID.java | 28 ++- .../com/atomgraph/server/vocabulary/HTTP.java | 30 +-- .../com/atomgraph/server/vocabulary/LDT.java | 64 +++--- .../filter/request/OntologyFilterTest.java | 79 +++++++ .../OntologyImportsCharacterizationTest.java | 96 ++++++++ .../server/util/OntologyModelGetterTest.java | 117 ++++++++++ .../vocabulary/VocabularyHolderTest.java | 63 ++++++ 24 files changed, 838 insertions(+), 392 deletions(-) create mode 100644 src/test/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyFilterTest.java create mode 100644 src/test/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyImportsCharacterizationTest.java create mode 100644 src/test/java/com/atomgraph/linkeddatahub/server/util/OntologyModelGetterTest.java create mode 100644 src/test/java/com/atomgraph/linkeddatahub/vocabulary/VocabularyHolderTest.java diff --git a/pom.xml b/pom.xml index bcdd7851c0..fc68de8c63 100644 --- a/pom.xml +++ b/pom.xml @@ -163,13 +163,13 @@ ${project.groupId} client - 4.3.0 + 4.3.1-SNAPSHOT classes ${project.groupId} client - 4.3.0 + 4.3.1-SNAPSHOT war diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/ACL.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/ACL.java index 259d34a0ed..ab8afcc1c0 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/ACL.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/ACL.java @@ -16,11 +16,11 @@ */ package com.atomgraph.linkeddatahub.vocabulary; -import org.apache.jena.ontology.ObjectProperty; -import org.apache.jena.ontology.OntClass; -import org.apache.jena.ontology.OntModel; -import org.apache.jena.ontology.OntModelSpec; -import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.ontapi.OntModelFactory; +import org.apache.jena.ontapi.OntSpecification; +import org.apache.jena.ontapi.model.OntModel; +import org.apache.jena.rdf.model.Property; + import org.apache.jena.rdf.model.Resource; /** @@ -30,8 +30,13 @@ */ public class ACL { + + static + { + org.apache.jena.sys.JenaSystem.init(); // ensure Jena (RDFS vocab) is initialized before ontapi touches it + } /** The RDF model that holds the vocabulary terms */ - private static OntModel m_model = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM, null); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM); /** The namespace of the vocabulary as a string */ public static final String NS = "http://www.w3.org/ns/auth/acl#"; @@ -49,45 +54,45 @@ public static String getURI() public static final Resource NAMESPACE = m_model.createResource( NS ); /** acl:Authorization class */ - public static final OntClass Authorization = m_model.createClass( NS + "Authorization" ); + public static final Resource Authorization = m_model.createOntClass( NS + "Authorization" ); /** acl:Read access mode */ - public static final OntClass Read = m_model.createClass( NS + "Read" ); + public static final Resource Read = m_model.createOntClass( NS + "Read" ); /** acl:Write access mode */ - public static final OntClass Write = m_model.createClass( NS + "Write" ); + public static final Resource Write = m_model.createOntClass( NS + "Write" ); /** acl:Append access mode */ - public static final OntClass Append = m_model.createClass( NS + "Append" ); + public static final Resource Append = m_model.createOntClass( NS + "Append" ); /** acl:Control access mode */ - public static final OntClass Control = m_model.createClass( NS + "Control" ); + public static final Resource Control = m_model.createOntClass( NS + "Control" ); /** acl:AuthenticatedAgent class */ - public static final OntClass AuthenticatedAgent = m_model.createClass( NS + "AuthenticatedAgent" ); + public static final Resource AuthenticatedAgent = m_model.createOntClass( NS + "AuthenticatedAgent" ); /** acl:delegates property **/ - public static final ObjectProperty delegates = m_model.createObjectProperty( NS + "delegates" ); + public static final Property delegates = m_model.createObjectProperty( NS + "delegates" ); /** acl:owner property */ - public static final ObjectProperty owner = m_model.createObjectProperty( NS + "owner" ); + public static final Property owner = m_model.createObjectProperty( NS + "owner" ); /** acl:agent property */ - public static final ObjectProperty agent = m_model.createObjectProperty( NS + "agent" ); + public static final Property agent = m_model.createObjectProperty( NS + "agent" ); /** acl:agentClass property */ - public static final ObjectProperty agentClass = m_model.createObjectProperty( NS + "agentClass" ); + public static final Property agentClass = m_model.createObjectProperty( NS + "agentClass" ); /** acl:agentGroup property */ - public static final ObjectProperty agentGroup = m_model.createObjectProperty( NS + "agentGroup" ); + public static final Property agentGroup = m_model.createObjectProperty( NS + "agentGroup" ); /** acl:mode property */ - public static final ObjectProperty mode = m_model.createObjectProperty( NS + "mode" ); + public static final Property mode = m_model.createObjectProperty( NS + "mode" ); /** acl:accessTo property */ - public static final ObjectProperty accessTo = m_model.createObjectProperty( NS + "accessTo" ); + public static final Property accessTo = m_model.createObjectProperty( NS + "accessTo" ); /** acl:accessToClass property */ - public static final ObjectProperty accessToClass = m_model.createObjectProperty( NS + "accessToClass" ); + public static final Property accessToClass = m_model.createObjectProperty( NS + "accessToClass" ); } diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/Admin.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/Admin.java index d2bda94534..8f8901c666 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/Admin.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/Admin.java @@ -16,9 +16,10 @@ */ package com.atomgraph.linkeddatahub.vocabulary; -import org.apache.jena.ontology.OntModel; -import org.apache.jena.ontology.OntModelSpec; -import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.ontapi.OntModelFactory; +import org.apache.jena.ontapi.OntSpecification; +import org.apache.jena.ontapi.model.OntModel; + import org.apache.jena.rdf.model.Resource; /** @@ -28,9 +29,14 @@ */ public class Admin { + + static + { + org.apache.jena.sys.JenaSystem.init(); // ensure Jena (RDFS vocab) is initialized before ontapi touches it + } /** The RDF model that holds the vocabulary terms */ - private static OntModel m_model = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM, null); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM); /** The namespace of the vocabulary as a string */ public static final String NS = "https://w3id.org/atomgraph/linkeddatahub/admin#"; diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/Cert.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/Cert.java index c0906f3767..655a2582d9 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/Cert.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/Cert.java @@ -16,12 +16,11 @@ */ package com.atomgraph.linkeddatahub.vocabulary; -import org.apache.jena.ontology.DatatypeProperty; -import org.apache.jena.ontology.ObjectProperty; -import org.apache.jena.ontology.OntClass; -import org.apache.jena.ontology.OntModel; -import org.apache.jena.ontology.OntModelSpec; -import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.ontapi.OntModelFactory; +import org.apache.jena.ontapi.OntSpecification; +import org.apache.jena.ontapi.model.OntModel; +import org.apache.jena.rdf.model.Property; + import org.apache.jena.rdf.model.Resource; /** @@ -32,8 +31,13 @@ public class Cert { + + static + { + org.apache.jena.sys.JenaSystem.init(); // ensure Jena (RDFS vocab) is initialized before ontapi touches it + } /** The RDF model that holds the vocabulary terms */ - private static OntModel m_model = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM, null); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM); /** The namespace of the vocabulary as a string */ public static final String NS = "http://www.w3.org/ns/auth/cert#"; @@ -50,18 +54,18 @@ public static String getURI() public static final Resource NAMESPACE = m_model.createResource( NS ); /** Public key class */ - public static final OntClass PublicKey = m_model.createClass(NS + "PublicKey"); + public static final Resource PublicKey = m_model.createOntClass(NS + "PublicKey"); /** RSA public key class */ - public static final OntClass RSAPublicKey = m_model.createClass(NS + "RSAPublicKey"); + public static final Resource RSAPublicKey = m_model.createOntClass(NS + "RSAPublicKey"); /** Key property */ - public static final ObjectProperty key = m_model.createObjectProperty( NS + "key" ); + public static final Property key = m_model.createObjectProperty( NS + "key" ); /** Modulus property */ - public static final DatatypeProperty modulus = m_model.createDatatypeProperty( NS + "modulus" ); + public static final Property modulus = m_model.createDataProperty( NS + "modulus" ); /** Exponent property */ - public static final DatatypeProperty exponent = m_model.createDatatypeProperty( NS + "exponent" ); + public static final Property exponent = m_model.createDataProperty( NS + "exponent" ); } \ No newline at end of file diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/DH.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/DH.java index 7ea41b3058..30e4b486ff 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/DH.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/DH.java @@ -15,12 +15,11 @@ */ package com.atomgraph.linkeddatahub.vocabulary; -import org.apache.jena.ontology.DatatypeProperty; -import org.apache.jena.ontology.ObjectProperty; -import org.apache.jena.ontology.OntClass; -import org.apache.jena.ontology.OntModel; -import org.apache.jena.ontology.OntModelSpec; -import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.ontapi.OntModelFactory; +import org.apache.jena.ontapi.OntSpecification; +import org.apache.jena.ontapi.model.OntModel; +import org.apache.jena.rdf.model.Property; + import org.apache.jena.rdf.model.Resource; /** @@ -31,8 +30,13 @@ public class DH { + static + { + org.apache.jena.sys.JenaSystem.init(); // ensure Jena (RDFS vocab) is initialized before ontapi touches it + } + /**

The RDF model that holds the vocabulary terms

*/ - private static OntModel m_model = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM, null); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM); /**

The namespace of the vocabulary as a string

*/ public static final String NS = "https://www.w3.org/ns/ldt/document-hierarchy#"; @@ -49,15 +53,15 @@ public static String getURI() public static final Resource NAMESPACE = m_model.createResource( NS ); /** Document class */ - public static final OntClass Document = m_model.createClass( NS + "Document" ); + public static final Resource Document = m_model.createOntClass( NS + "Document" ); /** Container class */ - public static final OntClass Container = m_model.createClass( NS + "Container" ); + public static final Resource Container = m_model.createOntClass( NS + "Container" ); /** Item class */ - public static final OntClass Item = m_model.createClass( NS + "Item" ); + public static final Resource Item = m_model.createOntClass( NS + "Item" ); /** Slug property */ - public static final DatatypeProperty slug = m_model.createDatatypeProperty( NS + "slug" ); + public static final Property slug = m_model.createDataProperty( NS + "slug" ); } diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/Default.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/Default.java index 03d36f4293..41182134b3 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/Default.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/Default.java @@ -16,10 +16,10 @@ */ package com.atomgraph.linkeddatahub.vocabulary; -import org.apache.jena.ontology.OntClass; -import org.apache.jena.ontology.OntModel; -import org.apache.jena.ontology.OntModelSpec; -import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.ontapi.OntModelFactory; +import org.apache.jena.ontapi.OntSpecification; +import org.apache.jena.ontapi.model.OntModel; + import org.apache.jena.rdf.model.Resource; /** @@ -29,8 +29,13 @@ */ public class Default { + + static + { + org.apache.jena.sys.JenaSystem.init(); // ensure Jena (RDFS vocab) is initialized before ontapi touches it + } /** The RDF model that holds the vocabulary terms */ - private static OntModel m_model = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM, null); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM); /** The namespace of the vocabulary as a string */ public static final String NS = "https://w3id.org/atomgraph/linkeddatahub/default#"; @@ -50,6 +55,6 @@ public static String getURI() // DOMAIN /** Root document class */ - public static final OntClass Root = m_model.createClass(NS + "Root"); + public static final Resource Root = m_model.createOntClass(NS + "Root"); } diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/FOAF.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/FOAF.java index a71875fe53..b816439510 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/FOAF.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/FOAF.java @@ -16,12 +16,11 @@ */ package com.atomgraph.linkeddatahub.vocabulary; -import org.apache.jena.ontology.DatatypeProperty; -import org.apache.jena.ontology.ObjectProperty; -import org.apache.jena.ontology.OntClass; -import org.apache.jena.ontology.OntModel; -import org.apache.jena.ontology.OntModelSpec; -import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.ontapi.OntModelFactory; +import org.apache.jena.ontapi.OntSpecification; +import org.apache.jena.ontapi.model.OntModel; +import org.apache.jena.rdf.model.Property; + import org.apache.jena.rdf.model.Resource; /** @@ -31,8 +30,13 @@ */ public class FOAF { + + static + { + org.apache.jena.sys.JenaSystem.init(); // ensure Jena (RDFS vocab) is initialized before ontapi touches it + } /** The RDF model that holds the vocabulary terms */ - private static OntModel m_model = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM, null); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM); /** The namespace of the vocabulary as a string */ public static final String NS = "http://xmlns.com/foaf/0.1/"; @@ -52,45 +56,45 @@ public static String getURI() public static final Resource NAMESPACE = m_model.createResource( NS ); /** Agent class */ - public static final OntClass Agent = m_model.createClass( NS + "Agent" ); + public static final Resource Agent = m_model.createOntClass( NS + "Agent" ); /** Person class */ - public static final OntClass Person = m_model.createClass( NS + "Person" ); + public static final Resource Person = m_model.createOntClass( NS + "Person" ); /** Document class */ - public static final OntClass Document = m_model.createClass( NS + "Document" ); + public static final Resource Document = m_model.createOntClass( NS + "Document" ); /** Name property */ - public static final DatatypeProperty name = m_model.createDatatypeProperty( NS + "name" ); + public static final Property name = m_model.createDataProperty( NS + "name" ); /** Given name property */ - public static final DatatypeProperty givenName = m_model.createDatatypeProperty( NS + "givenName" ); + public static final Property givenName = m_model.createDataProperty( NS + "givenName" ); /** Family name property */ - public static final DatatypeProperty familyName = m_model.createDatatypeProperty( NS + "familyName" ); + public static final Property familyName = m_model.createDataProperty( NS + "familyName" ); /** Mailbox property */ - public static final ObjectProperty mbox = m_model.createObjectProperty( NS + "mbox" ); + public static final Property mbox = m_model.createObjectProperty( NS + "mbox" ); /** Based near property */ - public static final ObjectProperty based_near = m_model.createObjectProperty( NS + "based_near" ); + public static final Property based_near = m_model.createObjectProperty( NS + "based_near" ); /** Member property */ - public static final ObjectProperty member = m_model.createObjectProperty( NS + "member" ); + public static final Property member = m_model.createObjectProperty( NS + "member" ); /** Primary topic property */ - public static final ObjectProperty primaryTopic = m_model.createObjectProperty( NS + "primaryTopic" ); + public static final Property primaryTopic = m_model.createObjectProperty( NS + "primaryTopic" ); /** Is primary topic of property */ - public static final ObjectProperty isPrimaryTopicOf = m_model.createObjectProperty( NS + "isPrimaryTopicOf" ); + public static final Property isPrimaryTopicOf = m_model.createObjectProperty( NS + "isPrimaryTopicOf" ); /** Maker property */ - public static final ObjectProperty maker = m_model.createObjectProperty( NS + "maker" ); + public static final Property maker = m_model.createObjectProperty( NS + "maker" ); /** Account property */ - public static final ObjectProperty account = m_model.createObjectProperty( NS + "account" ); + public static final Property account = m_model.createObjectProperty( NS + "account" ); /** Image property */ - public static final ObjectProperty img = m_model.createObjectProperty( NS + "img" ); + public static final Property img = m_model.createObjectProperty( NS + "img" ); } diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/Google.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/Google.java index cd68c45f5e..a1ed9a6c64 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/Google.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/Google.java @@ -1,9 +1,10 @@ package com.atomgraph.linkeddatahub.vocabulary; -import org.apache.jena.ontology.DatatypeProperty; -import org.apache.jena.ontology.OntModel; -import org.apache.jena.ontology.OntModelSpec; -import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.ontapi.OntModelFactory; +import org.apache.jena.ontapi.OntSpecification; +import org.apache.jena.ontapi.model.OntModel; +import org.apache.jena.rdf.model.Property; + import org.apache.jena.rdf.model.Resource; /** @@ -14,8 +15,13 @@ public class Google { + static + { + org.apache.jena.sys.JenaSystem.init(); // ensure Jena (RDFS vocab) is initialized before ontapi touches it + } + /** The RDF model that holds the vocabulary terms */ - private static OntModel m_model = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM, null); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM); /** The namespace of the vocabulary as a string */ public static final String NS = "https://w3id.org/atomgraph/linkeddatahub/services/google#"; @@ -35,9 +41,9 @@ public static String getURI() public static final Resource NAMESPACE = m_model.createResource( NS ); /** Client ID property */ - public static final DatatypeProperty clientID = m_model.createDatatypeProperty( NS + "clientID" ); + public static final Property clientID = m_model.createDataProperty( NS + "clientID" ); /** Client secret property */ - public static final DatatypeProperty clientSecret = m_model.createDatatypeProperty( NS + "clientSecret" ); + public static final Property clientSecret = m_model.createDataProperty( NS + "clientSecret" ); } diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LACL.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LACL.java index fd2af9e58f..5ced24b0c2 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LACL.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LACL.java @@ -16,12 +16,11 @@ */ package com.atomgraph.linkeddatahub.vocabulary; -import org.apache.jena.ontology.DatatypeProperty; -import org.apache.jena.ontology.ObjectProperty; -import org.apache.jena.ontology.OntClass; -import org.apache.jena.ontology.OntModel; -import org.apache.jena.ontology.OntModelSpec; -import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.ontapi.OntModelFactory; +import org.apache.jena.ontapi.OntSpecification; +import org.apache.jena.ontapi.model.OntModel; +import org.apache.jena.rdf.model.Property; + import org.apache.jena.rdf.model.Resource; /** @@ -31,8 +30,13 @@ */ public class LACL { + + static + { + org.apache.jena.sys.JenaSystem.init(); // ensure Jena (RDFS vocab) is initialized before ontapi touches it + } /** The RDF model that holds the vocabulary terms */ - private static OntModel m_model = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM, null); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM); /** The namespace of the vocabulary as a string */ public static final String NS = "https://w3id.org/atomgraph/linkeddatahub/admin/acl#"; @@ -52,33 +56,33 @@ public static String getURI() public static final Resource NAMESPACE = m_model.createResource( NS ); /** Authorization request class */ - public static final OntClass AuthorizationRequest = m_model.createClass( NS + "AuthorizationRequest" ); + public static final Resource AuthorizationRequest = m_model.createOntClass( NS + "AuthorizationRequest" ); /** Authorization request class */ - public static final OntClass OwnerAuthorization = m_model.createClass( NS + "OwnerAuthorization" ); + public static final Resource OwnerAuthorization = m_model.createOntClass( NS + "OwnerAuthorization" ); /** Password property */ - public static final DatatypeProperty password = m_model.createDatatypeProperty( NS + "password" ); + public static final Property password = m_model.createDataProperty( NS + "password" ); /** Issuer property */ - public static final DatatypeProperty issuer = m_model.createDatatypeProperty( NS + "issuer" ); + public static final Property issuer = m_model.createDataProperty( NS + "issuer" ); /** Request agent property **/ - public static final ObjectProperty requestMode = m_model.createObjectProperty( NS + "requestMode" ); + public static final Property requestMode = m_model.createObjectProperty( NS + "requestMode" ); /** Request agent property **/ - public static final ObjectProperty requestAgent = m_model.createObjectProperty( NS + "requestAgent" ); + public static final Property requestAgent = m_model.createObjectProperty( NS + "requestAgent" ); /** Request agent group property **/ - public static final ObjectProperty requestAgentGroup = m_model.createObjectProperty( NS + "requestAgentGroup" ); + public static final Property requestAgentGroup = m_model.createObjectProperty( NS + "requestAgentGroup" ); /** Request access to property */ - public static final ObjectProperty requestAccessTo = m_model.createObjectProperty( NS + "requestAccessTo" ); + public static final Property requestAccessTo = m_model.createObjectProperty( NS + "requestAccessTo" ); /** Request access to class property */ - public static final ObjectProperty requestAccessToClass = m_model.createObjectProperty( NS + "requestAccessToClass" ); + public static final Property requestAccessToClass = m_model.createObjectProperty( NS + "requestAccessToClass" ); /** Request access property */ - public static final ObjectProperty requestAccess = m_model.createObjectProperty( NS + "requestAccess" ); + public static final Property requestAccess = m_model.createObjectProperty( NS + "requestAccess" ); } diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LAPP.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LAPP.java index 7ef0a28f12..13daf790cd 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LAPP.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LAPP.java @@ -16,12 +16,11 @@ */ package com.atomgraph.linkeddatahub.vocabulary; -import org.apache.jena.ontology.DatatypeProperty; -import org.apache.jena.ontology.ObjectProperty; -import org.apache.jena.ontology.OntClass; -import org.apache.jena.ontology.OntModel; -import org.apache.jena.ontology.OntModelSpec; -import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.ontapi.OntModelFactory; +import org.apache.jena.ontapi.OntSpecification; +import org.apache.jena.ontapi.model.OntModel; +import org.apache.jena.rdf.model.Property; + import org.apache.jena.rdf.model.Resource; /** @@ -32,8 +31,13 @@ public class LAPP { + static + { + org.apache.jena.sys.JenaSystem.init(); // ensure Jena (RDFS vocab) is initialized before ontapi touches it + } + /** The RDF model that holds the vocabulary terms */ - private static OntModel m_model = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM, null); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM); /** The namespace of the vocabulary as a string */ public static final String NS = "https://w3id.org/atomgraph/linkeddatahub/apps#"; @@ -53,45 +57,45 @@ public static String getURI() public static final Resource NAMESPACE = m_model.createResource( NS ); /** Application class */ - public static final OntClass Context = m_model.createClass( NS + "Context" ); + public static final Resource Context = m_model.createOntClass( NS + "Context" ); /** Dataset class */ - public static final OntClass Dataset = m_model.createClass( NS + "Dataset" ); + public static final Resource Dataset = m_model.createOntClass( NS + "Dataset" ); /** Application class */ - public static final OntClass Application = m_model.createClass( NS + "Application" ); + public static final Resource Application = m_model.createOntClass( NS + "Application" ); /** Admin application class */ - public static final OntClass AdminApplication = m_model.createClass( NS + "AdminApplication" ); + public static final Resource AdminApplication = m_model.createOntClass( NS + "AdminApplication" ); /** End-user application class */ - public static final OntClass EndUserApplication = m_model.createClass( NS + "EndUserApplication" ); + public static final Resource EndUserApplication = m_model.createOntClass( NS + "EndUserApplication" ); /** Package class */ - public static final OntClass Package = m_model.createClass( NS + "Package" ); + public static final Resource Package = m_model.createOntClass( NS + "Package" ); /** Admin application class */ -// public static final ObjectProperty adminApplication = m_model.createObjectProperty( NS + "adminApplication" ); +// public static final Property adminApplication = m_model.createObjectProperty( NS + "adminApplication" ); // // /** End-user application class */ -// public static final ObjectProperty endUserApplication = m_model.createObjectProperty( NS + "endUserApplication" ); +// public static final Property endUserApplication = m_model.createObjectProperty( NS + "endUserApplication" ); /** Frontend proxy property */ - public static final ObjectProperty frontendProxy = m_model.createObjectProperty( NS + "frontendProxy" ); + public static final Property frontendProxy = m_model.createObjectProperty( NS + "frontendProxy" ); /** Backend proxy property */ - public static final ObjectProperty backendProxy = m_model.createObjectProperty( NS + "backendProxy" ); + public static final Property backendProxy = m_model.createObjectProperty( NS + "backendProxy" ); /** Prefix property */ - public static final ObjectProperty prefix = m_model.createObjectProperty( NS + "prefix" ); + public static final Property prefix = m_model.createObjectProperty( NS + "prefix" ); /** Read-only property */ - public static final DatatypeProperty allowRead = m_model.createDatatypeProperty( NS + "allowRead" ); + public static final Property allowRead = m_model.createDataProperty( NS + "allowRead" ); /** Origin property for subdomain-based application matching */ - public static final ObjectProperty origin = m_model.createObjectProperty(NS + "origin"); + public static final Property origin = m_model.createObjectProperty(NS + "origin"); /** Application property (for Link header rel) */ - public static final ObjectProperty application = m_model.createObjectProperty( NS + "application" ); + public static final Property application = m_model.createObjectProperty( NS + "application" ); } diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LDH.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LDH.java index 629fe4d4af..dff4f44de3 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LDH.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LDH.java @@ -16,12 +16,11 @@ */ package com.atomgraph.linkeddatahub.vocabulary; -import org.apache.jena.ontology.DatatypeProperty; -import org.apache.jena.ontology.ObjectProperty; -import org.apache.jena.ontology.OntClass; -import org.apache.jena.ontology.OntModel; -import org.apache.jena.ontology.OntModelSpec; -import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.ontapi.OntModelFactory; +import org.apache.jena.ontapi.OntSpecification; +import org.apache.jena.ontapi.model.OntModel; +import org.apache.jena.rdf.model.Property; + import org.apache.jena.rdf.model.Resource; /** @@ -31,8 +30,13 @@ */ public class LDH { + + static + { + org.apache.jena.sys.JenaSystem.init(); // ensure Jena (RDFS vocab) is initialized before ontapi touches it + } /** The RDF model that holds the vocabulary terms */ - private static OntModel m_model = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM, null); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM); /** The namespace of the vocabulary as a string */ public static final String NS = "https://w3id.org/atomgraph/linkeddatahub#"; @@ -51,62 +55,62 @@ public static String getURI() public static final Resource NAMESPACE = m_model.createResource( NS ); /** Dataset class */ - public static final OntClass Dataset = m_model.createClass(NS + "Dataset"); + public static final Resource Dataset = m_model.createOntClass(NS + "Dataset"); /** Generic service class */ - public static final OntClass GenericService = m_model.createClass(NS + "GenericService"); + public static final Resource GenericService = m_model.createOntClass(NS + "GenericService"); /** Import class */ - public static final OntClass Import = m_model.createClass(NS + "Import"); + public static final Resource Import = m_model.createOntClass(NS + "Import"); /** CSV import class */ - public static final OntClass CSVImport = m_model.createClass(NS + "CSVImport"); + public static final Resource CSVImport = m_model.createOntClass(NS + "CSVImport"); /** RDF import class */ - public static final OntClass RDFImport = m_model.createClass(NS + "RDFImport"); + public static final Resource RDFImport = m_model.createOntClass(NS + "RDFImport"); /** File class */ - public static final OntClass File = m_model.createClass(NS + "File"); + public static final Resource File = m_model.createOntClass(NS + "File"); /** Object class */ - public static final OntClass Object = m_model.createClass(NS + "Object"); + public static final Resource Object = m_model.createOntClass(NS + "Object"); /** View class */ - public static final OntClass View = m_model.createClass(NS + "View"); + public static final Resource View = m_model.createOntClass(NS + "View"); /** URI syntax violation class */ - public static final OntClass URISyntaxViolation = m_model.createClass(NS + "URISyntaxViolation"); + public static final Resource URISyntaxViolation = m_model.createOntClass(NS + "URISyntaxViolation"); /** Base property */ - public static final ObjectProperty base = m_model.createObjectProperty( NS + "base" ); + public static final Property base = m_model.createObjectProperty( NS + "base" ); /** File property */ - public static final ObjectProperty file = m_model.createObjectProperty( NS + "file" ); + public static final Property file = m_model.createObjectProperty( NS + "file" ); /** Action property */ - public static final ObjectProperty action = m_model.createObjectProperty( NS + "action" ); + public static final Property action = m_model.createObjectProperty( NS + "action" ); /** Delimiter property */ - public static final DatatypeProperty delimiter = m_model.createDatatypeProperty( NS + "delimiter" ); + public static final Property delimiter = m_model.createDataProperty( NS + "delimiter" ); /** Violation value property */ - public static final DatatypeProperty violationValue = m_model.createDatatypeProperty( NS + "violationValue" ); + public static final Property violationValue = m_model.createDataProperty( NS + "violationValue" ); /** Request URI property */ - public static final ObjectProperty requestUri = m_model.createObjectProperty(NS + "requestUri"); + public static final Property requestUri = m_model.createObjectProperty(NS + "requestUri"); /** HTTP headers property */ - public static final ObjectProperty httpHeaders = m_model.createObjectProperty(NS + "httpHeaders"); + public static final Property httpHeaders = m_model.createObjectProperty(NS + "httpHeaders"); /** Service property */ - public static final ObjectProperty service = m_model.createObjectProperty( NS + "service" ); + public static final Property service = m_model.createObjectProperty( NS + "service" ); /** * For shape property */ - public static final ObjectProperty forShape = m_model.createObjectProperty( NS + "forShape" ); + public static final Property forShape = m_model.createObjectProperty( NS + "forShape" ); /** * Import property - used to import packages into an application */ - public static final ObjectProperty importPackage = m_model.createObjectProperty( NS + "import" ); + public static final Property importPackage = m_model.createObjectProperty( NS + "import" ); } diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LDHC.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LDHC.java index 5daf5c3899..7c972af205 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LDHC.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LDHC.java @@ -16,11 +16,11 @@ */ package com.atomgraph.linkeddatahub.vocabulary; -import org.apache.jena.ontology.DatatypeProperty; -import org.apache.jena.ontology.ObjectProperty; -import org.apache.jena.ontology.OntModel; -import org.apache.jena.ontology.OntModelSpec; -import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.ontapi.OntModelFactory; +import org.apache.jena.ontapi.OntSpecification; +import org.apache.jena.ontapi.model.OntModel; +import org.apache.jena.rdf.model.Property; + import org.apache.jena.rdf.model.Resource; /** @@ -30,8 +30,13 @@ */ public class LDHC { + + static + { + org.apache.jena.sys.JenaSystem.init(); // ensure Jena (RDFS vocab) is initialized before ontapi touches it + } /** The RDF model that holds the vocabulary terms */ - private static OntModel m_model = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM, null); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM); /** The namespace of the vocabulary as a string */ public static final String NS = "https://w3id.org/atomgraph/linkeddatahub/config#"; @@ -51,138 +56,138 @@ public static String getURI() public static final Resource NAMESPACE = m_model.createResource( NS ); /** Base URI property */ - public static final ObjectProperty baseUri = m_model.createObjectProperty( NS + "baseUri" ); + public static final Property baseUri = m_model.createObjectProperty( NS + "baseUri" ); /** Proxy scheme property */ - public static final DatatypeProperty proxyScheme = m_model.createDatatypeProperty( NS + "proxyScheme" ); + public static final Property proxyScheme = m_model.createDataProperty( NS + "proxyScheme" ); /** Proxy host property */ - public static final DatatypeProperty proxyHost = m_model.createDatatypeProperty( NS + "proxyHost" ); + public static final Property proxyHost = m_model.createDataProperty( NS + "proxyHost" ); /** Proxy port property */ - public static final DatatypeProperty proxyPort = m_model.createDatatypeProperty( NS + "proxyPort" ); + public static final Property proxyPort = m_model.createDataProperty( NS + "proxyPort" ); /** Document type query property */ - public static final DatatypeProperty documentTypeQuery = m_model.createDatatypeProperty( NS + "documentTypeQuery" ); + public static final Property documentTypeQuery = m_model.createDataProperty( NS + "documentTypeQuery" ); /** Document owner query property */ - public static final DatatypeProperty documentOwnerQuery = m_model.createDatatypeProperty( NS + "documentOwnerQuery" ); + public static final Property documentOwnerQuery = m_model.createDataProperty( NS + "documentOwnerQuery" ); /** ACL query property */ - public static final DatatypeProperty aclQuery = m_model.createDatatypeProperty( NS + "aclQuery" ); + public static final Property aclQuery = m_model.createDataProperty( NS + "aclQuery" ); /** Owner's ACL property */ - public static final DatatypeProperty ownerAclQuery = m_model.createDatatypeProperty( NS + "ownerAclQuery" ); + public static final Property ownerAclQuery = m_model.createDataProperty( NS + "ownerAclQuery" ); /** WebID query property */ - public static final DatatypeProperty webIDQuery = m_model.createDatatypeProperty( NS + "webIDQuery" ); + public static final Property webIDQuery = m_model.createDataProperty( NS + "webIDQuery" ); /** Agent query property */ - public static final DatatypeProperty agentQuery = m_model.createDatatypeProperty( NS + "agentQuery" ); + public static final Property agentQuery = m_model.createDataProperty( NS + "agentQuery" ); /** User account query property */ - public static final DatatypeProperty userAccountQuery = m_model.createDatatypeProperty( NS + "userAccountQuery" ); + public static final Property userAccountQuery = m_model.createDataProperty( NS + "userAccountQuery" ); /** Ontology query property */ - public static final DatatypeProperty ontologyQuery = m_model.createDatatypeProperty( NS + "ontologyQuery" ); + public static final Property ontologyQuery = m_model.createDataProperty( NS + "ontologyQuery" ); /** Upload root property */ - public static final ObjectProperty uploadRoot = m_model.createObjectProperty( NS + "uploadRoot" ); + public static final Property uploadRoot = m_model.createObjectProperty( NS + "uploadRoot" ); /** Invalidate cache property */ - public static final DatatypeProperty invalidateCache = m_model.createDatatypeProperty( NS + "invalidateCache" ); + public static final Property invalidateCache = m_model.createDataProperty( NS + "invalidateCache" ); /** Cookie max age property */ - public static final DatatypeProperty cookieMaxAge = m_model.createDatatypeProperty( NS + "cookieMaxAge" ); + public static final Property cookieMaxAge = m_model.createDataProperty( NS + "cookieMaxAge" ); /** Client keystore property */ - public static final ObjectProperty clientKeyStore = m_model.createObjectProperty( NS + "clientKeyStore" ); + public static final Property clientKeyStore = m_model.createObjectProperty( NS + "clientKeyStore" ); /** Client keystore password property */ - public static final DatatypeProperty clientKeyStorePassword = m_model.createDatatypeProperty( NS + "clientKeyStorePassword" ); + public static final Property clientKeyStorePassword = m_model.createDataProperty( NS + "clientKeyStorePassword" ); /** Secretary cert alias property */ - public static final DatatypeProperty secretaryCertAlias = m_model.createDatatypeProperty( NS + "secretaryCertAlias" ); + public static final Property secretaryCertAlias = m_model.createDataProperty( NS + "secretaryCertAlias" ); /** Client truststore property */ - public static final ObjectProperty clientTrustStore = m_model.createObjectProperty( NS + "clientTrustStore" ); + public static final Property clientTrustStore = m_model.createObjectProperty( NS + "clientTrustStore" ); /** Client truststore password property */ - public static final DatatypeProperty clientTrustStorePassword = m_model.createDatatypeProperty( NS + "clientTrustStorePassword" ); + public static final Property clientTrustStorePassword = m_model.createDataProperty( NS + "clientTrustStorePassword" ); /** Signup email subject property */ - public static final DatatypeProperty signUpEMailSubject = m_model.createDatatypeProperty( NS + "signUpEMailSubject" ); + public static final Property signUpEMailSubject = m_model.createDataProperty( NS + "signUpEMailSubject" ); /** WebID signup email text property */ - public static final DatatypeProperty webIDSignUpEMailText = m_model.createDatatypeProperty( NS + "webIDSignUpEMailText" ); + public static final Property webIDSignUpEMailText = m_model.createDataProperty( NS + "webIDSignUpEMailText" ); /** OAuth signup email text property */ - public static final DatatypeProperty oAuthSignUpEMailText = m_model.createDatatypeProperty( NS + "oAuthSignUpEMailText" ); + public static final Property oAuthSignUpEMailText = m_model.createDataProperty( NS + "oAuthSignUpEMailText" ); /** Notification address property */ - public static final DatatypeProperty notificationAddress = m_model.createDatatypeProperty( NS + "notificationAddress" ); + public static final Property notificationAddress = m_model.createDataProperty( NS + "notificationAddress" ); /** Request access email subject property */ - public static final DatatypeProperty requestAccessEMailSubject = m_model.createDatatypeProperty( NS + "requestAccessEMailSubject" ); + public static final Property requestAccessEMailSubject = m_model.createDataProperty( NS + "requestAccessEMailSubject" ); /** Request access email text property */ - public static final DatatypeProperty requestAccessEMailText = m_model.createDatatypeProperty( NS + "requestAccessEMailText" ); + public static final Property requestAccessEMailText = m_model.createDataProperty( NS + "requestAccessEMailText" ); /** Authorization email subject property */ - public static final DatatypeProperty authorizationEMailSubject = m_model.createDatatypeProperty( NS + "authorizationEMailSubject" ); + public static final Property authorizationEMailSubject = m_model.createDataProperty( NS + "authorizationEMailSubject" ); /** Authorization email text property */ - public static final DatatypeProperty authorizationEMailText = m_model.createDatatypeProperty( NS + "authorizationEMailText" ); + public static final Property authorizationEMailText = m_model.createDataProperty( NS + "authorizationEMailText" ); /** Signup cert validity property */ - public static final DatatypeProperty signUpCertValidity = m_model.createDatatypeProperty( NS + "signUpCertValidity" ); + public static final Property signUpCertValidity = m_model.createDataProperty( NS + "signUpCertValidity" ); /** Context dataset property */ - public static final ObjectProperty contextDataset = m_model.createObjectProperty( NS + "contextDataset" ); + public static final Property contextDataset = m_model.createObjectProperty( NS + "contextDataset" ); /** Max connections per route property */ - public static final DatatypeProperty maxConnPerRoute = m_model.createDatatypeProperty( NS + "maxConnPerRoute" ); + public static final Property maxConnPerRoute = m_model.createDataProperty( NS + "maxConnPerRoute" ); /** Max total connections property */ - public static final DatatypeProperty maxTotalConn = m_model.createDatatypeProperty( NS + "maxTotalConn" ); + public static final Property maxTotalConn = m_model.createDataProperty( NS + "maxTotalConn" ); /** Import keep-alive property */ - public static final DatatypeProperty importKeepAlive = m_model.createDatatypeProperty( NS + "importKeepAlive" ); + public static final Property importKeepAlive = m_model.createDataProperty( NS + "importKeepAlive" ); /** HTTP client request retry count */ - public static final DatatypeProperty maxRequestRetries = m_model.createDatatypeProperty( NS + "maxRequestRetries" ); + public static final Property maxRequestRetries = m_model.createDataProperty( NS + "maxRequestRetries" ); /** Timeout in milliseconds waiting for a connection from the HTTP client pool */ - public static final DatatypeProperty connectionRequestTimeout = m_model.createDatatypeProperty( NS + "connectionRequestTimeout" ); + public static final Property connectionRequestTimeout = m_model.createDataProperty( NS + "connectionRequestTimeout" ); /** Max content length property */ - public static final DatatypeProperty maxContentLength = m_model.createDatatypeProperty( NS + "maxContentLength" ); + public static final Property maxContentLength = m_model.createDataProperty( NS + "maxContentLength" ); /** Support languages property */ - public static final DatatypeProperty supportedLanguages = m_model.createDatatypeProperty( NS + "supportedLanguages" ); + public static final Property supportedLanguages = m_model.createDataProperty( NS + "supportedLanguages" ); /** Max import threads property */ - public static final DatatypeProperty maxImportThreads = m_model.createDatatypeProperty( NS + "maxImportThreads" ); + public static final Property maxImportThreads = m_model.createDataProperty( NS + "maxImportThreads" ); /** Enable WebID signup property **/ - public static final DatatypeProperty enableWebIDSignUp = m_model.createDatatypeProperty( NS + "enableWebIDSignUp" ); + public static final Property enableWebIDSignUp = m_model.createDataProperty( NS + "enableWebIDSignUp" ); /** Enable Linked Data proxy property */ - public static final DatatypeProperty enableLinkedDataProxy = m_model.createDatatypeProperty( NS + "enableLinkedDataProxy" ); + public static final Property enableLinkedDataProxy = m_model.createDataProperty( NS + "enableLinkedDataProxy" ); /** Allow internal URLs property */ - public static final DatatypeProperty allowInternalUrls = m_model.createDatatypeProperty( NS + "allowInternalUrls" ); + public static final Property allowInternalUrls = m_model.createDataProperty( NS + "allowInternalUrls" ); /** OIDC refresh token properties property */ - public static final DatatypeProperty oidcRefreshTokens = m_model.createDatatypeProperty( NS + "oidcRefreshTokens" ); + public static final Property oidcRefreshTokens = m_model.createDataProperty( NS + "oidcRefreshTokens" ); /** Frontend proxy URI property (Varnish frontend cache, used for cache invalidation) */ - public static final ObjectProperty frontendProxy = m_model.createObjectProperty( NS + "frontendProxy" ); + public static final Property frontendProxy = m_model.createObjectProperty( NS + "frontendProxy" ); /** Backend proxy URI for the admin SPARQL service (used for cache invalidation and endpoint URI rewriting) */ - public static final ObjectProperty backendProxyAdmin = m_model.createObjectProperty( NS + "backendProxyAdmin" ); + public static final Property backendProxyAdmin = m_model.createObjectProperty( NS + "backendProxyAdmin" ); /** Backend proxy URI for the end-user SPARQL service (used for cache invalidation and endpoint URI rewriting) */ - public static final ObjectProperty backendProxyEndUser = m_model.createObjectProperty( NS + "backendProxyEndUser" ); + public static final Property backendProxyEndUser = m_model.createObjectProperty( NS + "backendProxyEndUser" ); } diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LDHT.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LDHT.java index e43185459d..7741c9e8d3 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LDHT.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LDHT.java @@ -16,11 +16,11 @@ */ package com.atomgraph.linkeddatahub.vocabulary; -import org.apache.jena.ontology.DatatypeProperty; -import org.apache.jena.ontology.ObjectProperty; -import org.apache.jena.ontology.OntModel; -import org.apache.jena.ontology.OntModelSpec; -import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.ontapi.OntModelFactory; +import org.apache.jena.ontapi.OntSpecification; +import org.apache.jena.ontapi.model.OntModel; +import org.apache.jena.rdf.model.Property; + import org.apache.jena.rdf.model.Resource; /** @@ -31,8 +31,13 @@ @Deprecated public class LDHT { + + static + { + org.apache.jena.sys.JenaSystem.init(); // ensure Jena (RDFS vocab) is initialized before ontapi touches it + } /** The RDF model that holds the vocabulary terms */ - private static OntModel m_model = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM, null); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM); /** The namespace of the vocabulary as a string */ public static final String NS = "https://w3id.org/atomgraph/linkeddatahub/templates#"; @@ -52,9 +57,9 @@ public static String getURI() public static final Resource NAMESPACE = m_model.createResource( NS ); /** For class property */ - //public static final ObjectProperty forClass = m_model.createObjectProperty( NS + "forClass" ); + //public static final Property forClass = m_model.createObjectProperty( NS + "forClass" ); /** Ban property */ - public static final DatatypeProperty ban = m_model.createDatatypeProperty( NS + "ban" ); + public static final Property ban = m_model.createDataProperty( NS + "ban" ); } diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/NFO.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/NFO.java index 6aba1781b3..5c5e79d738 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/NFO.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/NFO.java @@ -16,11 +16,11 @@ */ package com.atomgraph.linkeddatahub.vocabulary; -import org.apache.jena.ontology.DatatypeProperty; -import org.apache.jena.ontology.OntClass; -import org.apache.jena.ontology.OntModel; -import org.apache.jena.ontology.OntModelSpec; -import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.ontapi.OntModelFactory; +import org.apache.jena.ontapi.OntSpecification; +import org.apache.jena.ontapi.model.OntModel; +import org.apache.jena.rdf.model.Property; + import org.apache.jena.rdf.model.Resource; /** @@ -30,8 +30,13 @@ */ public class NFO { + + static + { + org.apache.jena.sys.JenaSystem.init(); // ensure Jena (RDFS vocab) is initialized before ontapi touches it + } /** The RDF model that holds the vocabulary terms */ - private static OntModel m_model = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM, null); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM); /** The namespace of the vocabulary as a string */ public static final String NS = "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#"; @@ -51,9 +56,9 @@ public static String getURI() public static final Resource NAMESPACE = m_model.createResource( NS ); /** File data object class */ - public static final OntClass FileDataObject = m_model.createClass(NS + "FileDataObject"); + public static final Resource FileDataObject = m_model.createOntClass(NS + "FileDataObject"); /** Filename property */ - public static final DatatypeProperty fileName = m_model.createDatatypeProperty( NS + "fileName" ); + public static final Property fileName = m_model.createDataProperty( NS + "fileName" ); } diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/ORCID.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/ORCID.java index 7998fc9073..42c21d60d9 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/ORCID.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/ORCID.java @@ -1,9 +1,10 @@ package com.atomgraph.linkeddatahub.vocabulary; -import org.apache.jena.ontology.DatatypeProperty; -import org.apache.jena.ontology.OntModel; -import org.apache.jena.ontology.OntModelSpec; -import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.ontapi.OntModelFactory; +import org.apache.jena.ontapi.OntSpecification; +import org.apache.jena.ontapi.model.OntModel; +import org.apache.jena.rdf.model.Property; + import org.apache.jena.rdf.model.Resource; /** @@ -14,8 +15,13 @@ public class ORCID { + static + { + org.apache.jena.sys.JenaSystem.init(); // ensure Jena (RDFS vocab) is initialized before ontapi touches it + } + /** The RDF model that holds the vocabulary terms */ - private static OntModel m_model = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM, null); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM); /** The namespace of the vocabulary as a string */ public static final String NS = "https://w3id.org/atomgraph/linkeddatahub/services/orcid#"; @@ -35,9 +41,9 @@ public static String getURI() public static final Resource NAMESPACE = m_model.createResource( NS ); /** Client ID property */ - public static final DatatypeProperty clientID = m_model.createDatatypeProperty( NS + "clientID" ); + public static final Property clientID = m_model.createDataProperty( NS + "clientID" ); /** Client secret property */ - public static final DatatypeProperty clientSecret = m_model.createDatatypeProperty( NS + "clientSecret" ); + public static final Property clientSecret = m_model.createDataProperty( NS + "clientSecret" ); } diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/PROV.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/PROV.java index efd61c6693..e536478aa2 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/PROV.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/PROV.java @@ -16,12 +16,11 @@ */ package com.atomgraph.linkeddatahub.vocabulary; -import org.apache.jena.ontology.DatatypeProperty; -import org.apache.jena.ontology.ObjectProperty; -import org.apache.jena.ontology.OntClass; -import org.apache.jena.ontology.OntModel; -import org.apache.jena.ontology.OntModelSpec; -import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.ontapi.OntModelFactory; +import org.apache.jena.ontapi.OntSpecification; +import org.apache.jena.ontapi.model.OntModel; +import org.apache.jena.rdf.model.Property; + import org.apache.jena.rdf.model.Resource; /** @@ -30,9 +29,14 @@ * @author Martynas Jusevičius {@literal } */ public class PROV { + + static + { + org.apache.jena.sys.JenaSystem.init(); // ensure Jena (RDFS vocab) is initialized before ontapi touches it + } /** The RDF model that holds the vocabulary terms */ - private static OntModel m_model = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM, null); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM); /** The namespace of the vocabulary as a string */ public static final String NS = "http://www.w3.org/ns/prov#"; @@ -52,33 +56,33 @@ public static String getURI() public static final Resource NAMESPACE = m_model.createResource( NS ); /** Entity class */ - public static final OntClass Entity = m_model.createClass( NS + "Entity" ); + public static final Resource Entity = m_model.createOntClass( NS + "Entity" ); /** Activity class */ - public static final OntClass Activity = m_model.createClass( NS + "Activity" ); + public static final Resource Activity = m_model.createOntClass( NS + "Activity" ); /** Agent class */ - public static final OntClass Agent = m_model.createClass( NS + "Agent" ); + public static final Resource Agent = m_model.createOntClass( NS + "Agent" ); /** Was attributed to property */ - public static final ObjectProperty wasAttributedTo = m_model.createObjectProperty( NS + "wasAttributedTo" ); + public static final Property wasAttributedTo = m_model.createObjectProperty( NS + "wasAttributedTo" ); /** Was derived from property */ - public static final ObjectProperty wasDerivedFrom = m_model.createObjectProperty( NS + "wasDerivedFrom" ); + public static final Property wasDerivedFrom = m_model.createObjectProperty( NS + "wasDerivedFrom" ); /** Was generated by property */ - public static final ObjectProperty wasGeneratedBy = m_model.createObjectProperty( NS + "wasGeneratedBy" ); + public static final Property wasGeneratedBy = m_model.createObjectProperty( NS + "wasGeneratedBy" ); /** Was started by property */ - public static final ObjectProperty wasStartedBy = m_model.createObjectProperty( NS + "wasStartedBy" ); + public static final Property wasStartedBy = m_model.createObjectProperty( NS + "wasStartedBy" ); /** Started at time property */ - public static final DatatypeProperty startedAtTime = m_model.createDatatypeProperty( NS + "startedAtTime" ); + public static final Property startedAtTime = m_model.createDataProperty( NS + "startedAtTime" ); /** Ended at time property */ - public static final DatatypeProperty endedAtTime = m_model.createDatatypeProperty( NS + "endedAtTime" ); + public static final Property endedAtTime = m_model.createDataProperty( NS + "endedAtTime" ); /** Generated at time property */ - public static final DatatypeProperty generatedAtTime = m_model.createDatatypeProperty( NS + "generatedAtTime" ); + public static final Property generatedAtTime = m_model.createDataProperty( NS + "generatedAtTime" ); } diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/SIOC.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/SIOC.java index 302ef9f397..4a19b93cc4 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/SIOC.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/SIOC.java @@ -16,8 +16,11 @@ */ package com.atomgraph.linkeddatahub.vocabulary; -import org.apache.jena.ontology.*; -import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.ontapi.OntModelFactory; +import org.apache.jena.ontapi.OntSpecification; +import org.apache.jena.ontapi.model.OntModel; +import org.apache.jena.rdf.model.Property; + import org.apache.jena.rdf.model.Resource; /** @@ -27,12 +30,17 @@ */ public class SIOC { + + static + { + org.apache.jena.sys.JenaSystem.init(); // ensure Jena (RDFS vocab) is initialized before ontapi touches it + } /** *

* The ontology model that holds the vocabulary terms *

*/ - private static OntModel m_model = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM, null); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM); /** *

@@ -74,7 +82,7 @@ public static String getURI() { * etc. *

*/ - public static final ObjectProperty ABOUT = m_model + public static final Property ABOUT = m_model .createObjectProperty("http://rdfs.org/sioc/ns#about"); /** @@ -82,7 +90,7 @@ public static String getURI() { * Refers to the foaf:Agent or foaf:Person who owns this sioc:User online account. *

*/ - public static final ObjectProperty ACCOUNT_OF = m_model + public static final Property ACCOUNT_OF = m_model .createObjectProperty("http://rdfs.org/sioc/ns#account_of"); /** @@ -90,7 +98,7 @@ public static String getURI() { * A Site that the User is an administrator of. *

*/ - public static final ObjectProperty ADMINISTRATOR_OF = m_model + public static final Property ADMINISTRATOR_OF = m_model .createObjectProperty("http://rdfs.org/sioc/ns#administrator_of"); /** @@ -98,7 +106,7 @@ public static String getURI() { * The URI of a file attached to an Item. *

*/ - public static final ObjectProperty ATTACHMENT = m_model + public static final Property ATTACHMENT = m_model .createObjectProperty("http://rdfs.org/sioc/ns#attachment"); /** @@ -106,7 +114,7 @@ public static String getURI() { * An image or depiction used to represent this User. *

*/ - public static final ObjectProperty AVATAR = m_model + public static final Property AVATAR = m_model .createObjectProperty("http://rdfs.org/sioc/ns#avatar"); /** @@ -114,7 +122,7 @@ public static String getURI() { * An Item that this Container contains. *

*/ - public static final ObjectProperty CONTAINER_OF = m_model + public static final Property CONTAINER_OF = m_model .createObjectProperty("http://rdfs.org/sioc/ns#container_of"); /** @@ -122,7 +130,7 @@ public static String getURI() { * A resource that the User is a creator of. *

*/ - public static final ObjectProperty CREATOR_OF = m_model + public static final Property CREATOR_OF = m_model .createObjectProperty("http://rdfs.org/sioc/ns#creator_of"); /** @@ -130,7 +138,7 @@ public static String getURI() { * An electronic mail address of the User. *

*/ - public static final ObjectProperty EMAIL = m_model + public static final Property EMAIL = m_model .createObjectProperty("http://rdfs.org/sioc/ns#email"); /** @@ -138,7 +146,7 @@ public static String getURI() { * A feed (e.g. RSS, Atom, etc.) pertaining to this resource (e.g. for a Forum, Site, User, etc.). *

*/ - public static final ObjectProperty FEED = m_model + public static final Property FEED = m_model .createObjectProperty("http://rdfs.org/sioc/ns#feed"); /** @@ -147,7 +155,7 @@ public static String getURI() { * updates). *

*/ - public static final ObjectProperty FOLLOWS = m_model + public static final Property FOLLOWS = m_model .createObjectProperty("http://rdfs.org/sioc/ns#follows"); /** @@ -155,13 +163,13 @@ public static String getURI() { * A User who has this Role. *

*/ - public static final ObjectProperty FUNCTION_OF = m_model + public static final Property FUNCTION_OF = m_model .createObjectProperty("http://rdfs.org/sioc/ns#function_of"); /** * This property has been renamed. Use sioc:sioc:usergroup_of instead. */ - public static final ObjectProperty GROUP_OF = m_model + public static final Property GROUP_OF = m_model .createObjectProperty("http://rdfs.org/sioc/ns#group_of"); /** @@ -169,7 +177,7 @@ public static String getURI() { * A User who is an administrator of this Site. *

*/ - public static final ObjectProperty HAS_ADMINISTRATOR = m_model + public static final Property HAS_ADMINISTRATOR = m_model .createObjectProperty("http://rdfs.org/sioc/ns#has_administrator"); /** @@ -177,7 +185,7 @@ public static String getURI() { * The Container to which this Item belongs. *

*/ - public static final ObjectProperty HAS_CONTAINER = m_model + public static final Property HAS_CONTAINER = m_model .createObjectProperty("http://rdfs.org/sioc/ns#has_container"); /** @@ -185,7 +193,7 @@ public static String getURI() { * This is the User who made this resource. *

*/ - public static final ObjectProperty HAS_CREATOR = m_model + public static final Property HAS_CREATOR = m_model .createObjectProperty("http://rdfs.org/sioc/ns#has_creator"); /** @@ -193,7 +201,7 @@ public static String getURI() { * The discussion that is related to this Item. *

*/ - public static final ObjectProperty HAS_DISCUSSION = m_model + public static final Property HAS_DISCUSSION = m_model .createObjectProperty("http://rdfs.org/sioc/ns#has_discussion"); /** @@ -201,13 +209,13 @@ public static String getURI() { * A Role that this User has. *

*/ - public static final ObjectProperty HAS_FUNCTION = m_model + public static final Property HAS_FUNCTION = m_model .createObjectProperty("http://rdfs.org/sioc/ns#has_function"); /** * This property has been renamed. Use sioc:has_usergroup instead. */ - public static final ObjectProperty HAS_GROUP = m_model + public static final Property HAS_GROUP = m_model .createObjectProperty("http://rdfs.org/sioc/ns#has_group"); /** @@ -215,7 +223,7 @@ public static String getURI() { * The Site that hosts this Forum. *

*/ - public static final ObjectProperty HAS_HOST = m_model + public static final Property HAS_HOST = m_model .createObjectProperty("http://rdfs.org/sioc/ns#has_host"); /** @@ -223,7 +231,7 @@ public static String getURI() { * A User who is a member of this Usergroup. *

*/ - public static final ObjectProperty HAS_MEMBER = m_model + public static final Property HAS_MEMBER = m_model .createObjectProperty("http://rdfs.org/sioc/ns#has_member"); /** @@ -231,7 +239,7 @@ public static String getURI() { * A User who is a moderator of this Forum. *

*/ - public static final ObjectProperty HAS_MODERATOR = m_model + public static final Property HAS_MODERATOR = m_model .createObjectProperty("http://rdfs.org/sioc/ns#has_moderator"); /** @@ -239,7 +247,7 @@ public static String getURI() { * A User who modified this Item. *

*/ - public static final ObjectProperty HAS_MODIFIER = m_model + public static final Property HAS_MODIFIER = m_model .createObjectProperty("http://rdfs.org/sioc/ns#has_modifier"); /** @@ -247,7 +255,7 @@ public static String getURI() { * A User that this resource is owned by. *

*/ - public static final ObjectProperty HAS_OWNER = m_model + public static final Property HAS_OWNER = m_model .createObjectProperty("http://rdfs.org/sioc/ns#has_owner"); /** @@ -255,7 +263,7 @@ public static String getURI() { * A Container or Forum that this Container or Forum is a child of. *

*/ - public static final ObjectProperty HAS_PARENT = m_model + public static final Property HAS_PARENT = m_model .createObjectProperty("http://rdfs.org/sioc/ns#has_parent"); /** @@ -263,7 +271,7 @@ public static String getURI() { * An resource that is a part of this subject. *

*/ - public static final ObjectProperty HAS_PART = m_model + public static final Property HAS_PART = m_model .createObjectProperty("http://rdfs.org/sioc/ns#has_part"); /** @@ -271,7 +279,7 @@ public static String getURI() { * Points to an Item or Post that is a reply or response to this Item or Post. *

*/ - public static final ObjectProperty HAS_REPLY = m_model + public static final Property HAS_REPLY = m_model .createObjectProperty("http://rdfs.org/sioc/ns#has_reply"); /** @@ -279,7 +287,7 @@ public static String getURI() { * A resource that this Role applies to. *

*/ - public static final ObjectProperty HAS_SCOPE = m_model + public static final Property HAS_SCOPE = m_model .createObjectProperty("http://rdfs.org/sioc/ns#has_scope"); /** @@ -287,7 +295,7 @@ public static String getURI() { * A data Space which this resource is a part of. *

*/ - public static final ObjectProperty HAS_SPACE = m_model + public static final Property HAS_SPACE = m_model .createObjectProperty("http://rdfs.org/sioc/ns#has_space"); /** @@ -295,7 +303,7 @@ public static String getURI() { * A User who is subscribed to this Container. *

*/ - public static final ObjectProperty HAS_SUBSCRIBER = m_model + public static final Property HAS_SUBSCRIBER = m_model .createObjectProperty("http://rdfs.org/sioc/ns#has_subscriber"); /** @@ -303,7 +311,7 @@ public static String getURI() { * Points to a Usergroup that has certain access to this Space. *

*/ - public static final ObjectProperty HAS_USERGROUP = m_model + public static final Property HAS_USERGROUP = m_model .createObjectProperty("http://rdfs.org/sioc/ns#has_usergroup"); /** @@ -311,7 +319,7 @@ public static String getURI() { * A Forum that is hosted on this Site. *

*/ - public static final ObjectProperty HOST_OF = m_model + public static final Property HOST_OF = m_model .createObjectProperty("http://rdfs.org/sioc/ns#host_of"); /** @@ -319,7 +327,7 @@ public static String getURI() { * Links to the latest revision of this Item or Post. *

*/ - public static final ObjectProperty LATEST_VERSION = m_model + public static final Property LATEST_VERSION = m_model .createObjectProperty("http://rdfs.org/sioc/ns#latest_version"); /** @@ -327,7 +335,7 @@ public static String getURI() { * A URI of a document which contains this SIOC object. *

*/ - public static final ObjectProperty LINK = m_model + public static final Property LINK = m_model .createObjectProperty("http://rdfs.org/sioc/ns#link"); /** @@ -335,7 +343,7 @@ public static String getURI() { * Links extracted from hyperlinks within a SIOC concept, e.g. Post or Site. *

*/ - public static final ObjectProperty LINKS_TO = m_model + public static final Property LINKS_TO = m_model .createObjectProperty("http://rdfs.org/sioc/ns#links_to"); /** @@ -343,7 +351,7 @@ public static String getURI() { * A Usergroup that this User is a member of. *

*/ - public static final ObjectProperty MEMBER_OF = m_model + public static final Property MEMBER_OF = m_model .createObjectProperty("http://rdfs.org/sioc/ns#member_of"); /** @@ -351,7 +359,7 @@ public static String getURI() { * A Forum that User is a moderator of. *

*/ - public static final ObjectProperty MODERATOR_OF = m_model + public static final Property MODERATOR_OF = m_model .createObjectProperty("http://rdfs.org/sioc/ns#moderator_of"); /** @@ -359,7 +367,7 @@ public static String getURI() { * An Item that this User has modified. *

*/ - public static final ObjectProperty MODIFIER_OF = m_model + public static final Property MODIFIER_OF = m_model .createObjectProperty("http://rdfs.org/sioc/ns#modifier_of"); /** @@ -367,7 +375,7 @@ public static String getURI() { * Next Item or Post in a given Container sorted by date. *

*/ - public static final ObjectProperty NEXT_BY_DATE = m_model + public static final Property NEXT_BY_DATE = m_model .createObjectProperty("http://rdfs.org/sioc/ns#next_by_date"); /** @@ -375,7 +383,7 @@ public static String getURI() { * Links to the next revision of this Item or Post. *

*/ - public static final ObjectProperty NEXT_VERSION = m_model + public static final Property NEXT_VERSION = m_model .createObjectProperty("http://rdfs.org/sioc/ns#next_version"); /** @@ -383,7 +391,7 @@ public static String getURI() { * A resource owned by a particular User, for example, a weblog or image gallery. *

*/ - public static final ObjectProperty OWNER_OF = m_model + public static final Property OWNER_OF = m_model .createObjectProperty("http://rdfs.org/sioc/ns#owner_of"); /** @@ -391,7 +399,7 @@ public static String getURI() { * A child Container or Forum that this Container or Forum is a parent of. *

*/ - public static final ObjectProperty PARENT_OF = m_model + public static final Property PARENT_OF = m_model .createObjectProperty("http://rdfs.org/sioc/ns#parent_of"); /** @@ -399,7 +407,7 @@ public static String getURI() { * A resource that the subject is a part of. *

*/ - public static final ObjectProperty PART_OF = m_model + public static final Property PART_OF = m_model .createObjectProperty("http://rdfs.org/sioc/ns#part_of"); /** @@ -407,7 +415,7 @@ public static String getURI() { * Previous Item or Post in a given Container sorted by date. *

*/ - public static final ObjectProperty PREVIOUS_BY_DATE = m_model + public static final Property PREVIOUS_BY_DATE = m_model .createObjectProperty("http://rdfs.org/sioc/ns#previous_by_date"); /** @@ -415,7 +423,7 @@ public static String getURI() { * Links to the previous revision of this Item or Post. *

*/ - public static final ObjectProperty PREVIOUS_VERSION = m_model + public static final Property PREVIOUS_VERSION = m_model .createObjectProperty("http://rdfs.org/sioc/ns#previous_version"); /** @@ -423,7 +431,7 @@ public static String getURI() { * Links either created explicitly or extracted implicitly on the HTML level from the Post. *

*/ - public static final ObjectProperty REFERENCE = m_model + public static final Property REFERENCE = m_model .createObjectProperty("http://rdfs.org/sioc/ns#reference"); /** @@ -431,7 +439,7 @@ public static String getURI() { * Related Posts for this Post, perhaps determined implicitly from topics or references. *

*/ - public static final ObjectProperty RELATED_TO = m_model + public static final Property RELATED_TO = m_model .createObjectProperty("http://rdfs.org/sioc/ns#related_to"); /** @@ -439,7 +447,7 @@ public static String getURI() { * Links to an Item or Post which this Item or Post is a reply to. *

*/ - public static final ObjectProperty REPLY_OF = m_model + public static final Property REPLY_OF = m_model .createObjectProperty("http://rdfs.org/sioc/ns#reply_of"); /** @@ -447,7 +455,7 @@ public static String getURI() { * A Role that has a scope of this resource. *

*/ - public static final ObjectProperty SCOPE_OF = m_model + public static final Property SCOPE_OF = m_model .createObjectProperty("http://rdfs.org/sioc/ns#scope_of"); /** @@ -455,7 +463,7 @@ public static String getURI() { * A resource which belongs to this data Space. *

*/ - public static final ObjectProperty SPACE_OF = m_model + public static final Property SPACE_OF = m_model .createObjectProperty("http://rdfs.org/sioc/ns#space_of"); /** @@ -463,7 +471,7 @@ public static String getURI() { * A Container that a User is subscribed to. *

*/ - public static final ObjectProperty SUBSCRIBER_OF = m_model + public static final Property SUBSCRIBER_OF = m_model .createObjectProperty("http://rdfs.org/sioc/ns#subscriber_of"); /** @@ -472,7 +480,7 @@ public static String getURI() { * SKOS category. *

*/ - public static final ObjectProperty TOPIC = m_model + public static final Property TOPIC = m_model .createObjectProperty("http://rdfs.org/sioc/ns#topic"); /** @@ -480,7 +488,7 @@ public static String getURI() { * A Space that the Usergroup has access to. *

*/ - public static final ObjectProperty USERGROUP_OF = m_model + public static final Property USERGROUP_OF = m_model .createObjectProperty("http://rdfs.org/sioc/ns#usergroup_of"); /** @@ -488,48 +496,48 @@ public static String getURI() { * The content of the Item in plain text format. *

*/ - public static final DatatypeProperty CONTENT = m_model - .createDatatypeProperty("http://rdfs.org/sioc/ns#content"); + public static final Property CONTENT = m_model + .createDataProperty("http://rdfs.org/sioc/ns#content"); /** *

* The encoded content of the Post, contained in CDATA areas. *

*/ - public static final DatatypeProperty CONTENT_ENCODED = m_model - .createDatatypeProperty("http://rdfs.org/sioc/ns#content_encoded"); + public static final Property CONTENT_ENCODED = m_model + .createDataProperty("http://rdfs.org/sioc/ns#content_encoded"); /** *

* When this was created, in ISO 8601 format. *

*/ - public static final DatatypeProperty CREATED_AT = m_model - .createDatatypeProperty("http://rdfs.org/sioc/ns#created_at"); + public static final Property CREATED_AT = m_model + .createDataProperty("http://rdfs.org/sioc/ns#created_at"); /** *

* The content of the Post. *

*/ - public static final DatatypeProperty DESCRIPTION = m_model - .createDatatypeProperty("http://rdfs.org/sioc/ns#description"); + public static final Property DESCRIPTION = m_model + .createDataProperty("http://rdfs.org/sioc/ns#description"); /** *

* An electronic mail address of the User, encoded using SHA1. *

*/ - public static final DatatypeProperty EMAIL_SHA1 = m_model - .createDatatypeProperty("http://rdfs.org/sioc/ns#email_sha1"); + public static final Property EMAIL_SHA1 = m_model + .createDataProperty("http://rdfs.org/sioc/ns#email_sha1"); /** *

* First (real) name of this User. Synonyms include given name or christian name. *

*/ - public static final DatatypeProperty FIRST_NAME = m_model - .createDatatypeProperty("http://rdfs.org/sioc/ns#first_name"); + public static final Property FIRST_NAME = m_model + .createDataProperty("http://rdfs.org/sioc/ns#first_name"); /** *

@@ -537,8 +545,8 @@ public static String getURI() { * of each type of SIOC concept within the same site. *

*/ - public static final DatatypeProperty ID = m_model - .createDatatypeProperty("http://rdfs.org/sioc/ns#id"); + public static final Property ID = m_model + .createDataProperty("http://rdfs.org/sioc/ns#id"); /** *

@@ -546,47 +554,47 @@ public static String getURI() { * articles list the IP addresses for the creator or modifiers when the usernames are absent. *

*/ - public static final DatatypeProperty IP_ADDRESS = m_model - .createDatatypeProperty("http://rdfs.org/sioc/ns#ip_address"); + public static final Property IP_ADDRESS = m_model + .createDataProperty("http://rdfs.org/sioc/ns#ip_address"); /** *

* Last (real) name of this user. Synonyms include surname or family name. *

*/ - public static final DatatypeProperty LAST_NAME = m_model - .createDatatypeProperty("http://rdfs.org/sioc/ns#last_name"); + public static final Property LAST_NAME = m_model + .createDataProperty("http://rdfs.org/sioc/ns#last_name"); /** *

* When this was modified, in ISO 8601 format. *

*/ - public static final DatatypeProperty MODIFIED_AT = m_model - .createDatatypeProperty("http://rdfs.org/sioc/ns#modified_at"); + public static final Property MODIFIED_AT = m_model + .createDataProperty("http://rdfs.org/sioc/ns#modified_at"); /** *

* The name of a SIOC instance, e.g. a username for a User, group name for a Usergroup, etc. *

*/ - public static final DatatypeProperty NAME = m_model - .createDatatypeProperty("http://rdfs.org/sioc/ns#name"); + public static final Property NAME = m_model + .createDataProperty("http://rdfs.org/sioc/ns#name"); /** *

* A note associated with this resource, for example, if it has been edited by a User. *

*/ - public static final DatatypeProperty NOTE = m_model - .createDatatypeProperty("http://rdfs.org/sioc/ns#note"); + public static final Property NOTE = m_model + .createDataProperty("http://rdfs.org/sioc/ns#note"); /** *

* The number of posts that this person has posted. *

*/ - public static final ObjectProperty NUM_POSTS = m_model + public static final Property NUM_POSTS = m_model .createObjectProperty("http://rdfs.org/sioc/ns#num_posts"); /** @@ -595,24 +603,24 @@ public static String getURI() { * structure is absent. *

*/ - public static final DatatypeProperty NUM_REPLIES = m_model - .createDatatypeProperty("http://rdfs.org/sioc/ns#num_replies"); + public static final Property NUM_REPLIES = m_model + .createDataProperty("http://rdfs.org/sioc/ns#num_replies"); /** *

* The number of times this Item, Thread, User profile, etc. has been viewed. *

*/ - public static final DatatypeProperty NUM_VIEWS = m_model - .createDatatypeProperty("http://rdfs.org/sioc/ns#num_views"); + public static final Property NUM_VIEWS = m_model + .createDataProperty("http://rdfs.org/sioc/ns#num_views"); /** *

* Keyword(s) describing subject of the Post. *

*/ - public static final DatatypeProperty SUBJECT = m_model - .createDatatypeProperty("http://rdfs.org/sioc/ns#subject"); + public static final Property SUBJECT = m_model + .createDataProperty("http://rdfs.org/sioc/ns#subject"); /** *

@@ -620,8 +628,8 @@ public static String getURI() { * that has no parents, it would detail the topic thread. *

*/ - public static final DatatypeProperty TITLE = m_model - .createDatatypeProperty("http://rdfs.org/sioc/ns#title"); + public static final Property TITLE = m_model + .createDataProperty("http://rdfs.org/sioc/ns#title"); // Vocabulary classes // ///////////////////////// @@ -631,42 +639,42 @@ public static String getURI() { * Community is a high-level concept that defines an online community and what it consists of. *

*/ - public static final OntClass COMMUNITY = m_model.createClass("http://rdfs.org/sioc/ns#Community"); + public static final Resource COMMUNITY = m_model.createOntClass("http://rdfs.org/sioc/ns#Community"); /** *

* An area in which content Items are contained. *

*/ - public static final OntClass CONTAINER = m_model.createClass("http://rdfs.org/sioc/ns#Container"); + public static final Resource CONTAINER = m_model.createOntClass("http://rdfs.org/sioc/ns#Container"); /** *

* A discussion area on which Posts or entries are made. *

*/ - public static final OntClass FORUM = m_model.createClass("http://rdfs.org/sioc/ns#Forum"); + public static final Resource FORUM = m_model.createOntClass("http://rdfs.org/sioc/ns#Forum"); /** *

* An Item is something which can be in a Container. *

*/ - public static final OntClass ITEM = m_model.createClass("http://rdfs.org/sioc/ns#Item"); + public static final Resource ITEM = m_model.createOntClass("http://rdfs.org/sioc/ns#Item"); /** *

* An article or message that can be posted to a Forum. *

*/ - public static final OntClass POST = m_model.createClass("http://rdfs.org/sioc/ns#Post"); + public static final Resource POST = m_model.createOntClass("http://rdfs.org/sioc/ns#Post"); /** *

* A Role is a function of a User within a scope of a particular Forum, Site, etc. *

*/ - public static final OntClass ROLE = m_model.createClass("http://rdfs.org/sioc/ns#Role"); + public static final Resource ROLE = m_model.createOntClass("http://rdfs.org/sioc/ns#Role"); /** *

@@ -675,28 +683,28 @@ public static String getURI() { * Space. *

*/ - public static final OntClass SITE = m_model.createClass("http://rdfs.org/sioc/ns#Site"); + public static final Resource SITE = m_model.createOntClass("http://rdfs.org/sioc/ns#Site"); /** *

* A Space is a place where data resides, e.g. on a website, desktop, fileshare, etc. *

*/ - public static final OntClass SPACE = m_model.createClass("http://rdfs.org/sioc/ns#Space"); + public static final Resource SPACE = m_model.createOntClass("http://rdfs.org/sioc/ns#Space"); /** *

* A container for a series of threaded discussion Posts or Items. *

*/ - public static final OntClass THREAD = m_model.createClass("http://rdfs.org/sioc/ns#Thread"); + public static final Resource THREAD = m_model.createOntClass("http://rdfs.org/sioc/ns#Thread"); /** *

* A User account in an online community site. *

*/ - public static final OntClass USER_ACCOUNT = m_model.createClass("http://rdfs.org/sioc/ns#UserAccount"); + public static final Resource USER_ACCOUNT = m_model.createOntClass("http://rdfs.org/sioc/ns#UserAccount"); /** *

@@ -704,7 +712,7 @@ public static String getURI() { * control purposes. *

*/ - public static final OntClass USERGROUP = m_model.createClass("http://rdfs.org/sioc/ns#Usergroup"); + public static final Resource USERGROUP = m_model.createOntClass("http://rdfs.org/sioc/ns#Usergroup"); // Vocabulary individuals // ///////////////////////// diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/VoID.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/VoID.java index 9d31bc7a97..f2f787b797 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/VoID.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/VoID.java @@ -16,12 +16,11 @@ */ package com.atomgraph.linkeddatahub.vocabulary; -import org.apache.jena.ontology.DatatypeProperty; -import org.apache.jena.ontology.ObjectProperty; -import org.apache.jena.ontology.OntClass; -import org.apache.jena.ontology.OntModel; -import org.apache.jena.ontology.OntModelSpec; -import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.ontapi.OntModelFactory; +import org.apache.jena.ontapi.OntSpecification; +import org.apache.jena.ontapi.model.OntModel; +import org.apache.jena.rdf.model.Property; + import org.apache.jena.rdf.model.Resource; /** @@ -30,9 +29,14 @@ * @author Martynas Jusevičius {@literal } */ public class VoID { + + static + { + org.apache.jena.sys.JenaSystem.init(); // ensure Jena (RDFS vocab) is initialized before ontapi touches it + } /** The RDF model that holds the vocabulary terms */ - private static OntModel m_model = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM, null); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM); /** The namespace of the vocabulary as a string */ public static final String NS = "http://rdfs.org/ns/void#"; @@ -52,18 +56,18 @@ public static String getURI() public static final Resource NAMESPACE = m_model.createResource( NS ); /** Dataset class */ - public static final OntClass Dataset = m_model.createClass( NS + "Dataset" ); + public static final Resource Dataset = m_model.createOntClass( NS + "Dataset" ); /** Triples property */ - public static final DatatypeProperty triples = m_model.createDatatypeProperty( NS + "triples" ); + public static final Property triples = m_model.createDataProperty( NS + "triples" ); /** Distinct subject property */ - public static final DatatypeProperty distinctSubjects = m_model.createDatatypeProperty( NS + "distinctSubjects" ); + public static final Property distinctSubjects = m_model.createDataProperty( NS + "distinctSubjects" ); /** In dataset property */ - public static final ObjectProperty inDataset = m_model.createObjectProperty( NS + "inDataset" ); + public static final Property inDataset = m_model.createObjectProperty( NS + "inDataset" ); /** Class property */ - public static final ObjectProperty _class = m_model.createObjectProperty( NS + "class" ); + public static final Property _class = m_model.createObjectProperty( NS + "class" ); } diff --git a/src/main/java/com/atomgraph/server/vocabulary/HTTP.java b/src/main/java/com/atomgraph/server/vocabulary/HTTP.java index e9316e1eec..77213e1cfb 100644 --- a/src/main/java/com/atomgraph/server/vocabulary/HTTP.java +++ b/src/main/java/com/atomgraph/server/vocabulary/HTTP.java @@ -16,12 +16,11 @@ package com.atomgraph.server.vocabulary; -import org.apache.jena.ontology.DatatypeProperty; -import org.apache.jena.ontology.ObjectProperty; -import org.apache.jena.ontology.OntClass; -import org.apache.jena.ontology.OntModel; -import org.apache.jena.ontology.OntModelSpec; -import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.ontapi.OntModelFactory; +import org.apache.jena.ontapi.OntSpecification; +import org.apache.jena.ontapi.model.OntModel; +import org.apache.jena.rdf.model.Property; + import org.apache.jena.rdf.model.Resource; /** @@ -33,8 +32,13 @@ public class HTTP { + static + { + org.apache.jena.sys.JenaSystem.init(); // ensure Jena (RDFS vocab) is initialized before ontapi touches it + } + /**

The RDF model that holds the vocabulary terms

*/ - private static OntModel m_model = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM, null); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM); /**

The namespace of the vocabulary as a string

*/ public static final String NS = "http://www.w3.org/2011/http#"; @@ -49,16 +53,16 @@ public static String getURI() /**

The namespace of the vocabulary as a resource

*/ public static final Resource NAMESPACE = m_model.createResource( NS ); - public static final OntClass Response = m_model.createClass( NS + "Response" ); + public static final Resource Response = m_model.createOntClass( NS + "Response" ); - public static final ObjectProperty sc = m_model.createObjectProperty( NS + "sc" ); + public static final Property sc = m_model.createObjectProperty( NS + "sc" ); - public static final DatatypeProperty statusCodeValue = m_model.createDatatypeProperty( NS + "statusCodeValue" ); + public static final Property statusCodeValue = m_model.createDataProperty( NS + "statusCodeValue" ); - public static final DatatypeProperty reasonPhrase = m_model.createDatatypeProperty( NS + "reasonPhrase" ); + public static final Property reasonPhrase = m_model.createDataProperty( NS + "reasonPhrase" ); - public static final DatatypeProperty absoluteURI = m_model.createDatatypeProperty( NS + "absoluteURI" ); + public static final Property absoluteURI = m_model.createDataProperty( NS + "absoluteURI" ); - public static final DatatypeProperty absolutePath = m_model.createDatatypeProperty( NS + "absolutePath" ); + public static final Property absolutePath = m_model.createDataProperty( NS + "absolutePath" ); } diff --git a/src/main/java/com/atomgraph/server/vocabulary/LDT.java b/src/main/java/com/atomgraph/server/vocabulary/LDT.java index 5b59ee7f8a..b48ad91fef 100644 --- a/src/main/java/com/atomgraph/server/vocabulary/LDT.java +++ b/src/main/java/com/atomgraph/server/vocabulary/LDT.java @@ -16,12 +16,11 @@ */ package com.atomgraph.server.vocabulary; -import org.apache.jena.ontology.DatatypeProperty; -import org.apache.jena.ontology.ObjectProperty; -import org.apache.jena.ontology.OntClass; -import org.apache.jena.ontology.OntModel; -import org.apache.jena.ontology.OntModelSpec; -import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.ontapi.OntModelFactory; +import org.apache.jena.ontapi.OntSpecification; +import org.apache.jena.ontapi.model.OntModel; +import org.apache.jena.rdf.model.Property; + import org.apache.jena.rdf.model.Resource; /** @@ -31,8 +30,13 @@ */ public final class LDT { + + static + { + org.apache.jena.sys.JenaSystem.init(); // ensure Jena (RDFS vocab) is initialized before ontapi touches it + } /**

The RDF model that holds the vocabulary terms

*/ - private static OntModel m_model = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM, null); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM); /**

The namespace of the vocabulary as a string

*/ public static final String NS = "https://www.w3.org/ns/ldt#"; @@ -47,51 +51,51 @@ public static String getURI() /**

The namespace of the vocabulary as a resource

*/ public static final Resource NAMESPACE = m_model.createResource( NS ); - public static final OntClass Application = m_model.createClass( NS + "Application" ); + public static final Resource Application = m_model.createOntClass( NS + "Application" ); - public static final OntClass Ontology = m_model.createClass( NS + "Ontology" ); + public static final Resource Ontology = m_model.createOntClass( NS + "Ontology" ); - public static final OntClass Template = m_model.createClass( NS + "Template" ); + public static final Resource Template = m_model.createOntClass( NS + "Template" ); - public static final OntClass Parameter = m_model.createClass( NS + "Parameter" ); + public static final Resource Parameter = m_model.createOntClass( NS + "Parameter" ); - public static final OntClass TemplateCall = m_model.createClass( NS + "TemplateCall" ); + public static final Resource TemplateCall = m_model.createOntClass( NS + "TemplateCall" ); - public static final OntClass Argument = m_model.createClass( NS + "Argument" ); + public static final Resource Argument = m_model.createOntClass( NS + "Argument" ); - public static final ObjectProperty base = m_model.createObjectProperty( NS + "base" ); + public static final Property base = m_model.createObjectProperty( NS + "base" ); - public static final ObjectProperty ontology = m_model.createObjectProperty( NS + "ontology" ); + public static final Property ontology = m_model.createObjectProperty( NS + "ontology" ); - public static final ObjectProperty service = m_model.createObjectProperty( NS + "service" ); + public static final Property service = m_model.createObjectProperty( NS + "service" ); - public static final ObjectProperty arg = m_model.createObjectProperty( NS + "arg" ); + public static final Property arg = m_model.createObjectProperty( NS + "arg" ); - public static final DatatypeProperty paramName = m_model.createDatatypeProperty( NS + "paramName" ); + public static final Property paramName = m_model.createDataProperty( NS + "paramName" ); // "extends" is a reserved keyword in Java, obviously - public static final ObjectProperty extends_ = m_model.createObjectProperty( NS + "extends" ); + public static final Property extends_ = m_model.createObjectProperty( NS + "extends" ); - public static final DatatypeProperty path = m_model.createDatatypeProperty( NS + "path" ); + public static final Property path = m_model.createDataProperty( NS + "path" ); - public static final ObjectProperty query = m_model.createObjectProperty( NS + "query" ); + public static final Property query = m_model.createObjectProperty( NS + "query" ); - public static final ObjectProperty update = m_model.createObjectProperty( NS + "update" ); + public static final Property update = m_model.createObjectProperty( NS + "update" ); - public static final DatatypeProperty match = m_model.createDatatypeProperty( NS + "match" ); + public static final Property match = m_model.createDataProperty( NS + "match" ); - public static final DatatypeProperty priority = m_model.createDatatypeProperty( NS + "priority" ); + public static final Property priority = m_model.createDataProperty( NS + "priority" ); - public static final DatatypeProperty fragment = m_model.createDatatypeProperty( NS + "fragment" ); + public static final Property fragment = m_model.createDataProperty( NS + "fragment" ); - public static final ObjectProperty param = m_model.createObjectProperty( NS + "param" ); + public static final Property param = m_model.createObjectProperty( NS + "param" ); - public static final ObjectProperty loadClass = m_model.createObjectProperty( NS + "loadClass" ); + public static final Property loadClass = m_model.createObjectProperty( NS + "loadClass" ); - public static final DatatypeProperty cacheControl = m_model.createDatatypeProperty( NS + "cacheControl" ); + public static final Property cacheControl = m_model.createDataProperty( NS + "cacheControl" ); - public static final ObjectProperty lang = m_model.createObjectProperty( NS + "lang" ); + public static final Property lang = m_model.createObjectProperty( NS + "lang" ); - public static final ObjectProperty template = m_model.createObjectProperty( NS + "template" ); + public static final Property template = m_model.createObjectProperty( NS + "template" ); } diff --git a/src/test/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyFilterTest.java b/src/test/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyFilterTest.java new file mode 100644 index 0000000000..76787051fc --- /dev/null +++ b/src/test/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyFilterTest.java @@ -0,0 +1,79 @@ +/** + * Copyright 2026 Martynas Jusevičius + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package com.atomgraph.linkeddatahub.server.filter.request; + +import org.apache.jena.ontology.OntDocumentManager; +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.ModelFactory; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +/** + * Characterization tests for {@link OntologyFilter#addDocumentModel}, which caches an + * imported ontology model under a SECONDARY key: the fragment-stripped document URI. + * + * This pins the current dual-key caching behavior so the migration to the new ontology + * API ({@code GraphRepository}) can be proven to retain it. + * + * @author Martynas Jusevičius {@literal } + */ +public class OntologyFilterTest +{ + + /** An import URI with a fragment is also cached under its fragment-stripped document URI. */ + @Test + public void testAddDocumentModelCachesUnderStrippedDocumentURI() + { + OntDocumentManager odm = new OntDocumentManager(); + String importURI = "http://example.org/onto#"; + String docURI = "http://example.org/onto"; + + Model imported = ModelFactory.createDefaultModel(); + imported.createResource(importURI + "Thing"); + odm.addModel(importURI, imported, true); + + // precondition: only the ontology (fragment) URI is known + assertNotNull(odm.getModel(importURI)); + + OntologyFilter.addDocumentModel(odm, importURI); + + // the same model is now retrievable under the fragment-stripped document URI + assertNotNull(odm.getModel(docURI), "import model should be cached under the document URI"); + assertSame(imported, odm.getModel(docURI)); + } + + /** If the document URI is mapped (mapURI != docURI), the secondary cache write is skipped. */ + @Test + public void testAddDocumentModelSkipsWhenDocumentURIMapped() + { + OntDocumentManager odm = new OntDocumentManager(); + String importURI = "http://example.org/mapped#"; + String docURI = "http://example.org/mapped"; + + // map the document URI to a different location -> guard mappedURI.equals(docURI) is false + odm.getFileManager().getLocationMapper().addAltEntry(docURI, "file:elsewhere.ttl"); + + Model imported = ModelFactory.createDefaultModel(); + odm.addModel(importURI, imported, true); + + OntologyFilter.addDocumentModel(odm, importURI); + + // no secondary cache entry created under the document URI + assertNull(odm.getModel(docURI), "mapped document URI should not be cached as a secondary key"); + } + +} diff --git a/src/test/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyImportsCharacterizationTest.java b/src/test/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyImportsCharacterizationTest.java new file mode 100644 index 0000000000..3009ef1693 --- /dev/null +++ b/src/test/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyImportsCharacterizationTest.java @@ -0,0 +1,96 @@ +/** + * Copyright 2026 Martynas Jusevičius + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package com.atomgraph.linkeddatahub.server.filter.request; + +import org.apache.jena.ontology.OntDocumentManager; +import org.apache.jena.ontology.OntModel; +import org.apache.jena.ontology.OntModelSpec; +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.rdf.model.Resource; +import org.apache.jena.rdf.model.ResourceFactory; +import org.apache.jena.vocabulary.OWL; +import org.apache.jena.vocabulary.RDF; +import org.apache.jena.vocabulary.RDFS; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +/** + * Oracle for the ontology-load mechanism used by {@link OntologyFilter}: build an + * OWL_MEM_RDFS_INF model over a base ontology that owl:imports another, then materialize + * the inferences into a plain OWL_MEM model that is cached. + * + * Pins (a) the owl:imports transitive closure, (b) RDFS inference, and (c) that the + * materialized model retains inferred triples but carries no reasoner — the three + * behaviors the {@code GraphRepository}/{@code OntSpecification} migration must reproduce. + * + * @author Martynas Jusevičius {@literal } + */ +public class OntologyImportsCharacterizationTest +{ + + private static final String BASE_URI = "http://example.org/base"; + private static final String IMPORT_URI = "http://example.org/imported"; + private static final String NS = "http://example.org/ns#"; + + @Test + public void testImportClosureRdfsInferenceAndMaterialization() + { + OntModelSpec spec = new OntModelSpec(OntModelSpec.OWL_MEM_RDFS_INF); + OntDocumentManager odm = new OntDocumentManager(); + spec.setDocumentManager(odm); + + // imported ontology: B rdfs:subClassOf A; individual x a B + Resource a = ResourceFactory.createResource(NS + "A"); + Resource b = ResourceFactory.createResource(NS + "B"); + Resource x = ResourceFactory.createResource(NS + "x"); + Model imported = ModelFactory.createDefaultModel(); + imported.add(imported.createResource(IMPORT_URI), RDF.type, OWL.Ontology); + imported.add(b, RDFS.subClassOf, a); + imported.add(x, RDF.type, b); + odm.addModel(IMPORT_URI, imported); + + // base ontology owl:imports the imported one + Model base = ModelFactory.createDefaultModel(); + base.add(base.createResource(BASE_URI), RDF.type, OWL.Ontology); + base.add(base.createResource(BASE_URI), OWL.imports, base.createResource(IMPORT_URI)); + + OntModel ontModel = ModelFactory.createOntologyModel(spec, base); + + // (a) transitive import closure includes the imported ontology + assertTrue(ontModel.listImportedOntologyURIs(true).contains(IMPORT_URI), "import closure should contain the imported ontology URI"); + // imported asserted triple is visible through the union + assertTrue(ontModel.contains(b, RDFS.subClassOf, a), "imported terms should be visible"); + // (b) RDFS inference: x a A is entailed from (x a B) + (B subClassOf A) + assertTrue(ontModel.contains(x, RDF.type, a), "RDFS reasoner should infer x a A"); + + // (c) materialize into a plain OWL_MEM model (no inference) + OntModel materialized = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM); + materialized.add(ontModel); + // inferred triple is now asserted in the materialized copy + assertTrue(materialized.contains(x, RDF.type, a), "materialized model retains the inferred triple"); + // and the materialized model carries no reasoner + assertNull(materialized.getSpecification().getReasonerFactory(), "OWL_MEM materialized model must have no reasoner factory"); + // proof it is plain: a fresh entailment is NOT auto-derived + Resource c = ResourceFactory.createResource(NS + "C"); + Resource y = ResourceFactory.createResource(NS + "y"); + materialized.add(c, RDFS.subClassOf, b); + materialized.add(y, RDF.type, c); + assertFalse(materialized.contains(y, RDF.type, a), "no reasoner: y a A must not be inferred"); + } + +} diff --git a/src/test/java/com/atomgraph/linkeddatahub/server/util/OntologyModelGetterTest.java b/src/test/java/com/atomgraph/linkeddatahub/server/util/OntologyModelGetterTest.java new file mode 100644 index 0000000000..4db93eac45 --- /dev/null +++ b/src/test/java/com/atomgraph/linkeddatahub/server/util/OntologyModelGetterTest.java @@ -0,0 +1,117 @@ +/** + * Copyright 2026 Martynas Jusevičius + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package com.atomgraph.linkeddatahub.server.util; + +import com.atomgraph.core.client.SPARQLClient; +import com.atomgraph.linkeddatahub.apps.model.AdminApplication; +import com.atomgraph.linkeddatahub.apps.model.EndUserApplication; +import com.atomgraph.linkeddatahub.model.Service; +import com.atomgraph.linkeddatahub.model.ServiceContext; +import jakarta.ws.rs.core.MultivaluedMap; +import jakarta.ws.rs.core.Response; +import org.apache.jena.ontology.OntModelSpec; +import org.apache.jena.query.Query; +import org.apache.jena.query.QueryFactory; +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.util.FileManager; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * Characterization tests for {@link OntologyModelGetter}, which resolves an ontology model + * SPARQL-first (admin endpoint CONSTRUCT) and falls back to the FileManager only when the + * SPARQL result is empty. + * + * Pins the current resolution order so the migration to a {@code GraphRepository.get(uri)} + * can be proven to retain it. + * + * @author Martynas Jusevičius {@literal } + */ +@ExtendWith(MockitoExtension.class) +public class OntologyModelGetterTest +{ + + private static final String ONTOLOGY_URI = "http://example.org/ontology#"; + private static final Query ONTOLOGY_QUERY = QueryFactory.create("CONSTRUCT { ?ontology ?p ?o } WHERE { ?ontology ?p ?o }"); + + @Mock com.atomgraph.linkeddatahub.Application system; + @Mock EndUserApplication app; + @Mock AdminApplication adminApp; + @Mock Service service; + @Mock ServiceContext serviceContext; + @Mock SPARQLClient sparqlClient; + @Mock Response response; + + private void stubSPARQLChain(Model sparqlResult) + { + when(app.getAdminApplication()).thenReturn(adminApp); + when(adminApp.getService()).thenReturn(service); + when(system.getServiceContext(service)).thenReturn(serviceContext); + when(serviceContext.getSPARQLClient()).thenReturn(sparqlClient); + when(sparqlClient.query(any(Query.class), eq(Model.class), any(MultivaluedMap.class), any(MultivaluedMap.class))).thenReturn(response); + when(response.readEntity(Model.class)).thenReturn(sparqlResult); + } + + /** A non-empty admin SPARQL CONSTRUCT result is returned directly; the FileManager is not consulted. */ + @Test + public void testSPARQLResultUsedWhenNonEmpty() + { + Model sparqlResult = ModelFactory.createDefaultModel(); + sparqlResult.createResource(ONTOLOGY_URI).addProperty(org.apache.jena.vocabulary.RDF.type, org.apache.jena.vocabulary.OWL.Ontology); + stubSPARQLChain(sparqlResult); + + OntModelSpec spec = new OntModelSpec(OntModelSpec.OWL_MEM); + FileManager fileManager = org.mockito.Mockito.mock(FileManager.class); + spec.getDocumentManager().setFileManager(fileManager); + + OntologyModelGetter getter = new OntologyModelGetter(app, system, spec, ONTOLOGY_QUERY); + Model result = getter.getModel(ONTOLOGY_URI); + + assertSame(sparqlResult, result); + verify(fileManager, never()).loadModel(any()); + } + + /** An empty SPARQL result falls back to FileManager.loadModel(uri). */ + @Test + public void testFallsBackToFileManagerWhenSPARQLEmpty() + { + Model empty = ModelFactory.createDefaultModel(); + stubSPARQLChain(empty); + + OntModelSpec spec = new OntModelSpec(OntModelSpec.OWL_MEM); + FileManager fileManager = org.mockito.Mockito.mock(FileManager.class); + spec.getDocumentManager().setFileManager(fileManager); + Model fallback = ModelFactory.createDefaultModel(); + fallback.createResource(ONTOLOGY_URI); + when(fileManager.loadModel(ONTOLOGY_URI)).thenReturn(fallback); + + OntologyModelGetter getter = new OntologyModelGetter(app, system, spec, ONTOLOGY_QUERY); + Model result = getter.getModel(ONTOLOGY_URI); + + assertSame(fallback, result); + } + +} diff --git a/src/test/java/com/atomgraph/linkeddatahub/vocabulary/VocabularyHolderTest.java b/src/test/java/com/atomgraph/linkeddatahub/vocabulary/VocabularyHolderTest.java new file mode 100644 index 0000000000..c416b712bb --- /dev/null +++ b/src/test/java/com/atomgraph/linkeddatahub/vocabulary/VocabularyHolderTest.java @@ -0,0 +1,63 @@ +/** + * Copyright 2026 Martynas Jusevičius + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package com.atomgraph.linkeddatahub.vocabulary; + +import org.apache.jena.rdf.model.Resource; +import org.apache.jena.vocabulary.OWL; +import org.apache.jena.vocabulary.RDF; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +/** + * Characterization snapshot for the vocabulary holders (representative: {@link DH}). + * + * All ~19 vocabulary classes follow the identical pattern — a static OntModel built with + * {@code ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM, null)} on which + * {@code createClass} / {@code createDatatypeProperty} mint typed terms. Phase A swaps the + * factory to {@code OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM)} and renames + * {@code createClass}→{@code createOntClass}, {@code createDatatypeProperty}→{@code createDataProperty}. + * + * Asserted via the migration-stable {@link Resource} interface: the term URIs and the + * {@code rdf:type owl:Class} / {@code rdf:type owl:DatatypeProperty} triples they produce + * must stay identical after the rename. + * + * @author Martynas Jusevičius {@literal } + */ +public class VocabularyHolderTest +{ + + @Test + public void testClassTermsHaveStableUriAndType() + { + Resource document = DH.Document; + assertEquals(DH.NS + "Document", document.getURI()); + assertTrue(document.hasProperty(RDF.type, OWL.Class), "DH.Document must be typed owl:Class"); + + Resource container = DH.Container; + assertEquals(DH.NS + "Container", container.getURI()); + assertTrue(container.hasProperty(RDF.type, OWL.Class), "DH.Container must be typed owl:Class"); + } + + @Test + public void testDatatypePropertyTermHasStableUriAndType() + { + Resource slug = DH.slug; + assertEquals(DH.NS + "slug", slug.getURI()); + assertTrue(slug.hasProperty(RDF.type, OWL.DatatypeProperty), "DH.slug must be typed owl:DatatypeProperty"); + } + +} From 2011ff9f1c9b2c4672cac76a7918970657a86e48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martynas=20Jusevi=C4=8Dius?= Date: Thu, 18 Jun 2026 15:04:04 +0200 Subject: [PATCH 02/14] Phase D: dissolve DataManager + migrate ontology pipeline to ontapi Completes the dissolution in LinkedDataHub (0 [removal] warnings, was ~90): - DataManagerImpl -> SameSiteSourceResolver (RDFSourceResolver subclass, same-site restriction) - OntologyModelGetter -> OntologyRepository (SPARQL-first PrefixGraphRepository subclass) - DataManagerFactory -> SourceResolverFactory (provides request-scoped RDFSourceResolver) - Application: per-app OntModelSpec/OntDocumentManager -> per-app OntologyRepository; global repository + resolver; remove PrefixMapper/LocationMapper/OntDocumentManager wiring - OntologyFilter/ClearOntology: ontapi OntModelFactory.createModel with OWL2_DL_MEM_RDFS_INF + materialize into OWL2_DL_MEM cached in the repository; dual-key import-closure caching preserved - request ontology property Optional -> Optional (ontapi) across OntologyFactory + ~8 consumers (.getOntModel() dropped) - Validator, ProxyRequestFilter, ValidatingModelProvider, Install/UninstallPackage: DataManager/OntModelSpec cache ops -> repository - characterization tests adapted to new API (OntologyRepositoryTest, OntologyFilterTest); 69 tests green Co-Authored-By: Claude Opus 4.8 (1M context) --- .../atomgraph/linkeddatahub/Application.java | 152 ++++++++---------- .../linkeddatahub/resource/Generate.java | 10 +- .../linkeddatahub/resource/Namespace.java | 18 +-- .../resource/admin/ClearOntology.java | 35 ++-- .../linkeddatahub/resource/admin/SignUp.java | 4 +- .../resource/admin/pkg/InstallPackage.java | 34 ++-- .../resource/admin/pkg/UninstallPackage.java | 25 +-- .../server/event/AuthorizationCreated.java | 1 - .../server/factory/OntologyFactory.java | 12 +- .../server/filter/request/OntologyFilter.java | 134 +++++++-------- .../filter/request/ProxyRequestFilter.java | 12 +- .../server/io/ValidatingModelProvider.java | 2 +- .../impl/DocumentHierarchyGraphStoreImpl.java | 8 +- ...delGetter.java => OntologyRepository.java} | 74 ++++----- .../linkeddatahub/writer/ModelXSLTWriter.java | 7 +- .../writer/ResultSetXSLTWriter.java | 7 +- .../linkeddatahub/writer/XSLTWriterBase.java | 38 ++--- ...actory.java => SourceResolverFactory.java} | 47 +++--- ...rImpl.java => SameSiteSourceResolver.java} | 72 +++------ .../server/io/ValidatingDatasetProvider.java | 10 +- .../server/io/ValidatingModelProvider.java | 10 +- .../com/atomgraph/server/util/Validator.java | 2 +- .../filter/request/OntologyFilterTest.java | 45 ++---- ...rTest.java => OntologyRepositoryTest.java} | 56 +++---- 24 files changed, 342 insertions(+), 473 deletions(-) rename src/main/java/com/atomgraph/linkeddatahub/server/util/{OntologyModelGetter.java => OntologyRepository.java} (64%) rename src/main/java/com/atomgraph/linkeddatahub/writer/factory/{DataManagerFactory.java => SourceResolverFactory.java} (70%) rename src/main/java/com/atomgraph/linkeddatahub/writer/impl/{DataManagerImpl.java => SameSiteSourceResolver.java} (59%) rename src/test/java/com/atomgraph/linkeddatahub/server/util/{OntologyModelGetterTest.java => OntologyRepositoryTest.java} (64%) diff --git a/src/main/java/com/atomgraph/linkeddatahub/Application.java b/src/main/java/com/atomgraph/linkeddatahub/Application.java index abe64f03d6..46bc8f8dc0 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/Application.java +++ b/src/main/java/com/atomgraph/linkeddatahub/Application.java @@ -16,6 +16,14 @@ */ package com.atomgraph.linkeddatahub; +import com.atomgraph.core.util.jena.PrefixGraphRepository; +import com.atomgraph.client.util.RDFSourceResolver; +import com.atomgraph.client.util.StylesheetResolver; +import com.atomgraph.linkeddatahub.writer.impl.SameSiteSourceResolver; +import com.atomgraph.linkeddatahub.server.util.OntologyRepository; +import org.apache.jena.riot.RDFParser; +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.ModelFactory; import com.atomgraph.linkeddatahub.server.mapper.HttpHostConnectExceptionMapper; import com.atomgraph.linkeddatahub.server.mapper.InternalURLExceptionMapper; import com.atomgraph.linkeddatahub.server.mapper.MessagingExceptionMapper; @@ -27,9 +35,6 @@ import com.atomgraph.linkeddatahub.server.mapper.auth.webid.WebIDCertificateExceptionMapper; import com.atomgraph.client.MediaTypes; import com.atomgraph.client.locator.PrefixMapper; -import org.apache.jena.ontology.OntDocumentManager; -import org.apache.jena.util.FileManager; -import org.apache.jena.util.LocationMapper; import jakarta.annotation.PostConstruct; import jakarta.servlet.ServletConfig; import jakarta.ws.rs.core.Context; @@ -37,8 +42,6 @@ import org.apache.jena.riot.RDFFormat; import org.apache.jena.riot.RDFWriterRegistry; import com.atomgraph.client.mapper.ClientErrorExceptionMapper; -import com.atomgraph.client.util.DataManager; -import com.atomgraph.client.util.DataManagerImpl; import com.atomgraph.client.vocabulary.AC; import com.atomgraph.client.writer.function.UUID; import com.atomgraph.core.exception.ConfigurationException; @@ -49,7 +52,7 @@ import com.atomgraph.core.io.UpdateRequestProvider; import com.atomgraph.core.mapper.BadGatewayExceptionMapper; import com.atomgraph.core.provider.QueryParamProvider; -import com.atomgraph.linkeddatahub.writer.factory.DataManagerFactory; +import com.atomgraph.linkeddatahub.writer.factory.SourceResolverFactory; import com.atomgraph.server.vocabulary.LDT; import com.atomgraph.server.mapper.NotFoundExceptionMapper; import com.atomgraph.core.riot.RDFLanguages; @@ -67,7 +70,6 @@ import com.atomgraph.linkeddatahub.model.Service; import com.atomgraph.linkeddatahub.writer.factory.xslt.XsltExecutableSupplier; import com.atomgraph.linkeddatahub.writer.factory.XsltExecutableSupplierFactory; -import com.atomgraph.client.util.XsltResolver; import com.atomgraph.linkeddatahub.client.GraphStoreClient; import com.atomgraph.linkeddatahub.client.filter.ClientUriRewriteFilter; import com.atomgraph.linkeddatahub.client.filter.JSONGRDDLFilter; @@ -135,7 +137,6 @@ import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; import org.apache.jena.enhanced.BuiltinPersonalities; -import org.apache.jena.ontology.OntModelSpec; import org.apache.jena.riot.RDFParserRegistry; import org.slf4j.Logger; import java.net.URI; @@ -164,7 +165,7 @@ import javax.net.ssl.TrustManagerFactory; import jakarta.servlet.ServletContext; import javax.xml.transform.Source; -import org.apache.jena.ontology.Ontology; +import org.apache.jena.ontapi.model.OntModel; import org.apache.jena.query.Dataset; import org.apache.jena.query.Query; import org.apache.jena.query.QueryExecution; @@ -268,8 +269,9 @@ public class Application extends ResourceConfig private final ExecutorService importThreadPool; private final ServletConfig servletConfig; private final EventBus eventBus = new EventBus(); - private final DataManager dataManager; - private final Map endUserOntModelSpecs; + private final PrefixGraphRepository repository; + private final SameSiteSourceResolver resolver; + private final Map endUserRepositories; private final MediaTypes mediaTypes; private final Client client, externalClient, importClient, noCertClient; private final Query documentTypeQuery, documentOwnerQuery, aclQuery, ownerAclQuery, webIDQuery, agentQuery, userAccountQuery, ontologyQuery; // no relative URIs @@ -278,7 +280,6 @@ public class Application extends ResourceConfig private final Processor xsltProc = new Processor(false); private final XsltCompiler xsltComp; private final XsltExecutable xsltExec; - private final OntModelSpec ontModelSpec; private final boolean cacheStylesheet; private final boolean resolvingUncached; private final URI baseURI, uploadRoot; @@ -324,7 +325,7 @@ public Application(@Context ServletConfig servletConfig) throws URISyntaxExcepti servletConfig.getServletContext().getInitParameter(A.maxGetRequestSize.getURI()) != null ? Integer.valueOf(servletConfig.getServletContext().getInitParameter(A.maxGetRequestSize.getURI())) : null, servletConfig.getServletContext().getInitParameter(A.cacheModelLoads.getURI()) != null ? Boolean.parseBoolean(servletConfig.getServletContext().getInitParameter(A.cacheModelLoads.getURI())) : true, servletConfig.getServletContext().getInitParameter(A.preemptiveAuth.getURI()) != null ? Boolean.parseBoolean(servletConfig.getServletContext().getInitParameter(A.preemptiveAuth.getURI())) : false, - new PrefixMapper(servletConfig.getServletContext().getInitParameter(AC.prefixMapping.getURI()) != null ? servletConfig.getServletContext().getInitParameter(AC.prefixMapping.getURI()) : null), + servletConfig.getServletContext().getInitParameter(AC.prefixMapping.getURI()), servletConfig.getServletContext().getInitParameter(LDHC.contextDataset.getURI()) != null ? servletConfig.getServletContext().getInitParameter(LDHC.contextDataset.getURI()) : null, com.atomgraph.client.Application.getSource(servletConfig.getServletContext(), servletConfig.getServletContext().getInitParameter(AC.stylesheet.getURI()) != null ? servletConfig.getServletContext().getInitParameter(AC.stylesheet.getURI()) : null), servletConfig.getServletContext().getInitParameter(AC.cacheStylesheet.getURI()) != null ? Boolean.parseBoolean(servletConfig.getServletContext().getInitParameter(AC.cacheStylesheet.getURI())) : false, @@ -435,7 +436,7 @@ public Application(@Context ServletConfig servletConfig) throws URISyntaxExcepti */ public Application(final ServletConfig servletConfig, final MediaTypes mediaTypes, final Integer maxGetRequestSize, final boolean cacheModelLoads, final boolean preemptiveAuth, - final LocationMapper locationMapper, final String contextDatasetURIString, + final String prefixMappingConfig, final String contextDatasetURIString, final Source stylesheet, final boolean cacheStylesheet, final boolean resolvingUncached, final String clientKeyStoreURIString, final String clientKeyStorePassword, final String secretaryCertAlias, @@ -682,12 +683,8 @@ public Application(final ServletConfig servletConfig, final MediaTypes mediaType } // register plain RDF/XML writer as default - RDFWriterRegistry.register(Lang.RDFXML, RDFFormat.RDFXML_PLAIN); + RDFWriterRegistry.register(Lang.RDFXML, RDFFormat.RDFXML_PLAIN); - // initialize mapping for locally stored vocabularies - LocationMapper.setGlobalLocationMapper(locationMapper); - if (log.isTraceEnabled()) log.trace("LocationMapper.get(): {}", locationMapper); - try { this.contextDataset = getDataset(servletConfig.getServletContext(), contextDatasetURI); @@ -792,14 +789,16 @@ else if (app.hasProperty(RDF.type, LAPP.EndUserApplication)) serviceIt.close(); } - // TO-DO: config property for cacheModelLoads - endUserOntModelSpecs = new HashMap<>(); - dataManager = new DataManagerImpl(locationMapper, new HashMap<>(), GraphStoreClient.create(client, mediaTypes), cacheModelLoads, preemptiveAuth, resolvingUncached); - ontModelSpec = OntModelSpec.OWL_MEM_RDFS_INF; - ontModelSpec.setImportModelGetter(dataManager); - OntDocumentManager.getInstance().setFileManager((FileManager)dataManager); - OntDocumentManager.getInstance().setCacheModels(true); // need to re-set after changing FileManager - ontModelSpec.setDocumentManager(OntDocumentManager.getInstance()); + endUserRepositories = new HashMap<>(); + // global graph repository: bundled vocabularies/ontologies mapped from the prefix-mapping config + repository = new PrefixGraphRepository(GraphStoreClient.create(client, mediaTypes)); + if (prefixMappingConfig != null) + { + Model prefixMappingModel = ModelFactory.createDefaultModel(); + RDFParser.create().source(prefixMappingConfig).streamManager(repository.getStreamManager()).build().parse(prefixMappingModel); + repository.processConfig(prefixMappingModel); + } + resolver = new SameSiteSourceResolver(repository, GraphStoreClient.create(client, mediaTypes), resolvingUncached, baseURI); if (mailUser != null && mailPassword != null) // enable SMTP authentication { @@ -836,19 +835,15 @@ protected PasswordAuthentication getPasswordAuthentication() xsltProc.registerExtensionFunction(new com.atomgraph.linkeddatahub.writer.function.URLDecode()); xsltProc.registerExtensionFunction(new com.atomgraph.linkeddatahub.writer.function.SendHTTPRequest(xsltProc, client)); - Model mappingModel = locationMapper.toModel(); - ResIterator prefixedMappings = mappingModel.listResourcesWithProperty(LocationMappingVocab.prefix); try { - while (prefixedMappings.hasNext()) + for (String prefix : getRepository().getPrefixMappings().keySet()) { - Resource prefixMapping = prefixedMappings.next(); - String prefix = prefixMapping.getRequiredProperty(LocationMappingVocab.prefix).getString(); // register mapped RDF documents in the XSLT processor so that document() returns them cached, throughout multiple transformations - TreeInfo doc = xsltProc.getUnderlyingConfiguration().buildDocumentTree(dataManager.resolve("", prefix)); + TreeInfo doc = xsltProc.getUnderlyingConfiguration().buildDocumentTree(getResolver().resolve("", prefix)); xsltProc.getUnderlyingConfiguration().getGlobalDocumentPool().add(doc, prefix); } - + // register HTTPS URL of translations.rdf so it doesn't have to be requested repeatedly try (InputStream translations = servletConfig.getServletContext().getResourceAsStream(XSLTWriterBase.TRANSLATIONS_PATH)) { @@ -861,14 +856,10 @@ protected PasswordAuthentication getPasswordAuthentication() if (log.isErrorEnabled()) log.error("Error reading mapped RDF document: {}", ex); throw new IllegalStateException(ex); } - finally - { - prefixedMappings.close(); - } xsltComp = xsltProc.newXsltCompiler(); xsltComp.setParameter(new QName("ldh", LDH.base.getNameSpace(), LDH.base.getLocalName()), new XdmAtomicValue(baseURI)); - xsltComp.setURIResolver(new XsltResolver(LocationMapper.get(), new HashMap<>(), GraphStoreClient.create(client, mediaTypes), false, false, true)); // default Xerces parser does not support HTTPS + xsltComp.setURIResolver(new StylesheetResolver(getRepository(), GraphStoreClient.create(client, mediaTypes))); // resolves xsl:import to raw stylesheet sources xsltExec = xsltComp.compile(stylesheet); } catch (FileNotFoundException ex) @@ -935,8 +926,8 @@ public void init() register(new QueryProvider()); register(new QueryParamProvider()); register(new UpdateRequestProvider()); - register(new ModelXSLTWriter(getXsltExecutable(), getOntModelSpec(), getDataManager(), getMessageDigest())); // writes (X)HTML responses - register(new ResultSetXSLTWriter(getXsltExecutable(), getOntModelSpec(), getDataManager(), getMessageDigest())); // writes (X)HTML responses + register(new ModelXSLTWriter(getXsltExecutable(), getResolver(), getMessageDigest())); // writes (X)HTML responses + register(new ResultSetXSLTWriter(getXsltExecutable(), getResolver(), getMessageDigest())); // writes (X)HTML responses final com.atomgraph.linkeddatahub.Application system = this; register(new AbstractBinder() @@ -1014,7 +1005,7 @@ protected void configure() @Override protected void configure() { - bindFactory(OntologyFactory.class).to(new TypeLiteral>() {}). + bindFactory(OntologyFactory.class).to(new TypeLiteral>() {}). in(RequestScoped.class); } }); @@ -1023,15 +1014,7 @@ protected void configure() @Override protected void configure() { - bindFactory(new com.atomgraph.core.factory.DataManagerFactory(getDataManager())).to(com.atomgraph.core.util.jena.DataManager.class); - } - }); - register(new AbstractBinder() - { - @Override - protected void configure() - { - bindFactory(DataManagerFactory.class).to(com.atomgraph.client.util.DataManager.class). + bindFactory(SourceResolverFactory.class).to(com.atomgraph.client.util.RDFSourceResolver.class). in(RequestScoped.class); } }); @@ -1756,43 +1739,54 @@ public EventBus getEventBus() } /** - * Gets Jena's DataManager implementation. - * - * @return data manager instance + * Returns the global graph repository (bundled vocabularies/ontologies + URI mapping + cache). + * + * @return graph repository + */ + public PrefixGraphRepository getRepository() + { + return repository; + } + + /** + * Returns the global XSLT source resolver. + * + * @return source resolver */ - public DataManager getDataManager() + public SameSiteSourceResolver getResolver() { - return dataManager; + return resolver; } - + /** - * Returns a map of application URIs to ontology specifications. - * - * @return URI to ontology specification map + * Returns a map of application URIs to ontology repositories. + * + * @return URI to ontology repository map */ - protected Map getEndUserOntModelSpecs() + protected Map getEndUserRepositories() { - return endUserOntModelSpecs; + return endUserRepositories; } /** - * Returns ontology specification for the specified end-user application. - * + * Returns the SPARQL-first ontology repository for the specified end-user application. + * * @param app end-user application resource - * @return ontology specification + * @return ontology repository */ - public OntModelSpec getOntModelSpec(EndUserApplication app) + public OntologyRepository getRepository(EndUserApplication app) { - if (!getEndUserOntModelSpecs().containsKey(app.getURI())) + if (!getEndUserRepositories().containsKey(app.getURI())) { - OntModelSpec appOntModelSpec = new OntModelSpec(OntModelSpec.OWL_MEM_RDFS_INF); - appOntModelSpec.setDocumentManager(new OntDocumentManager()); - appOntModelSpec.getDocumentManager().setFileManager(new DataManagerImpl(LocationMapper.get(), new HashMap<>(), GraphStoreClient.create(getClient(), getMediaTypes()), true, isPreemptiveAuth(), isResolvingUncached())); - - getEndUserOntModelSpecs().put(app.getURI(), appOntModelSpec); + OntologyRepository appRepository = new OntologyRepository(app, this, GraphStoreClient.create(getClient(), getMediaTypes()), getOntologyQuery()); + // seed bundled vocabulary/ontology mappings from the global repository + getRepository().getLocationMappings().forEach(appRepository::addLocationMapping); + getRepository().getPrefixMappings().forEach(appRepository::addPrefixMapping); + + getEndUserRepositories().put(app.getURI(), appRepository); } - - return getEndUserOntModelSpecs().get(app.getURI()); + + return getEndUserRepositories().get(app.getURI()); } /** @@ -2002,16 +1996,6 @@ public boolean isPreemptiveAuth() return preemptiveAuth; } - /** - * The default specification of ontology models. - * - * @return spec object - */ - public OntModelSpec getOntModelSpec() - { - return ontModelSpec; - } - /** * Returns Saxon's XSLT compiler. * diff --git a/src/main/java/com/atomgraph/linkeddatahub/resource/Generate.java b/src/main/java/com/atomgraph/linkeddatahub/resource/Generate.java index 34b68d81fd..27e2124ffc 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/resource/Generate.java +++ b/src/main/java/com/atomgraph/linkeddatahub/resource/Generate.java @@ -42,7 +42,7 @@ import jakarta.ws.rs.core.Response.Status; import jakarta.ws.rs.core.UriBuilder; import jakarta.ws.rs.core.UriInfo; -import org.apache.jena.ontology.Ontology; +import org.apache.jena.ontapi.model.OntModel; import org.apache.jena.query.ParameterizedSparqlString; import org.apache.jena.query.Query; import org.apache.jena.query.QueryFactory; @@ -69,7 +69,7 @@ public class Generate private final UriInfo uriInfo; private final MediaTypes mediaTypes; private final Application application; - private final Ontology ontology; + private final OntModel ontology; private final Optional agentContext; private final com.atomgraph.linkeddatahub.Application system; private final ResourceContext resourceContext; @@ -88,7 +88,7 @@ public class Generate */ @Inject public Generate(@Context Request request, @Context UriInfo uriInfo, MediaTypes mediaTypes, - com.atomgraph.linkeddatahub.apps.model.Application application, Optional ontology, Optional agentContext, + com.atomgraph.linkeddatahub.apps.model.Application application, Optional ontology, Optional agentContext, com.atomgraph.linkeddatahub.Application system, @Context ResourceContext resourceContext) { if (ontology.isEmpty()) throw new InternalServerErrorException("Ontology is not specified"); @@ -134,7 +134,7 @@ public Response post(Model model) if (queryRes == null) throw new BadRequestException("Container query string (spin:query) not provided"); // Lookup query in ontology - Resource queryResource = getOntology().getOntModel().getResource(queryRes.getURI()); + Resource queryResource = getOntology().getResource(queryRes.getURI()); if (queryResource == null || !queryResource.hasProperty(SP.text)) throw new BadRequestException("Query resource not found in ontology: " + queryRes.getURI()); @@ -265,7 +265,7 @@ public Application getApplication() * * @return the ontology */ - public Ontology getOntology() + public OntModel getOntology() { return ontology; } diff --git a/src/main/java/com/atomgraph/linkeddatahub/resource/Namespace.java b/src/main/java/com/atomgraph/linkeddatahub/resource/Namespace.java index 4ccefa319f..c055f957da 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/resource/Namespace.java +++ b/src/main/java/com/atomgraph/linkeddatahub/resource/Namespace.java @@ -31,7 +31,7 @@ import com.atomgraph.core.model.impl.dataset.ServiceImpl; import com.atomgraph.linkeddatahub.apps.model.Application; import com.atomgraph.linkeddatahub.apps.model.EndUserApplication; -import com.atomgraph.linkeddatahub.server.util.OntologyModelGetter; +import com.atomgraph.linkeddatahub.server.util.OntologyRepository; import java.net.URI; import java.util.List; import java.util.Optional; @@ -47,7 +47,7 @@ import jakarta.ws.rs.core.SecurityContext; import jakarta.ws.rs.core.UriInfo; import org.apache.jena.irix.IRIx; -import org.apache.jena.ontology.Ontology; +import org.apache.jena.ontapi.model.OntModel; import org.apache.jena.query.DatasetFactory; import org.apache.jena.query.Query; import org.apache.jena.query.QueryFactory; @@ -70,7 +70,7 @@ public class Namespace extends com.atomgraph.core.model.impl.SPARQLEndpointImpl private final URI uri; private final UriInfo uriInfo; private final Application application; - private final Ontology ontology; + private final OntModel ontology; private final com.atomgraph.linkeddatahub.Application system; /** @@ -86,10 +86,10 @@ public class Namespace extends com.atomgraph.core.model.impl.SPARQLEndpointImpl */ @Inject public Namespace(@Context Request request, @Context UriInfo uriInfo, - Application application, Optional ontology, MediaTypes mediaTypes, + Application application, Optional ontology, MediaTypes mediaTypes, @Context SecurityContext securityContext, com.atomgraph.linkeddatahub.Application system) { - super(request, new ServiceImpl(DatasetFactory.create(ontology.get().getOntModel()), mediaTypes), mediaTypes); + super(request, new ServiceImpl(DatasetFactory.create(ontology.get()), mediaTypes), mediaTypes); this.uri = uriInfo.getAbsolutePath(); this.uriInfo = uriInfo; this.application = application; @@ -128,7 +128,7 @@ public Response get(@QueryParam(QUERY) Query query, Model instances = ModelFactory.createDefaultModel(); forClasses.stream(). - map(forClass -> Optional.ofNullable(getOntology().getOntModel().getOntClass(checkURI(forClass).toString()))). + map(forClass -> Optional.ofNullable(getOntology().getOntClass(checkURI(forClass).toString()))). flatMap(Optional::stream). forEach(forClass -> new Constructor().construct(forClass, instances, getApplication().getBase().getURI())); @@ -141,8 +141,8 @@ public Response get(@QueryParam(QUERY) Query query, String ontologyURI = getApplication().getOntology().getURI(); if (log.isDebugEnabled()) log.debug("Returning namespace ontology from OntDocumentManager: {}", ontologyURI); // not returning the injected in-memory ontology because it has inferences applied to it - OntologyModelGetter modelGetter = new OntologyModelGetter(getApplication().as(EndUserApplication.class), getSystem(), getSystem().getOntModelSpec(), getSystem().getOntologyQuery()); - return getResponseBuilder(modelGetter.getModel(ontologyURI)).build(); + OntologyRepository repository = new OntologyRepository(getApplication().as(EndUserApplication.class), getSystem(), com.atomgraph.linkeddatahub.client.GraphStoreClient.create(getSystem().getClient(), getSystem().getMediaTypes()), getSystem().getOntologyQuery()); + return getResponseBuilder(org.apache.jena.rdf.model.ModelFactory.createModelForGraph(repository.get(ontologyURI))).build(); } else throw new BadRequestException("SPARQL query string not provided"); } @@ -223,7 +223,7 @@ public Application getApplication() * * @return application ontology */ - public Ontology getOntology() + public OntModel getOntology() { return ontology; } diff --git a/src/main/java/com/atomgraph/linkeddatahub/resource/admin/ClearOntology.java b/src/main/java/com/atomgraph/linkeddatahub/resource/admin/ClearOntology.java index 202f724291..5470d5ef97 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/resource/admin/ClearOntology.java +++ b/src/main/java/com/atomgraph/linkeddatahub/resource/admin/ClearOntology.java @@ -18,9 +18,8 @@ import com.atomgraph.linkeddatahub.apps.model.AdminApplication; import com.atomgraph.linkeddatahub.apps.model.EndUserApplication; -import static com.atomgraph.linkeddatahub.server.filter.request.OntologyFilter.addDocumentModel; import com.atomgraph.linkeddatahub.server.filter.response.CacheInvalidationFilter; -import com.atomgraph.linkeddatahub.server.util.OntologyModelGetter; +import com.atomgraph.linkeddatahub.server.util.OntologyRepository; import java.net.URI; import jakarta.inject.Inject; import jakarta.ws.rs.BadRequestException; @@ -31,8 +30,12 @@ import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.UriBuilder; -import org.apache.jena.ontology.OntModel; -import org.apache.jena.ontology.OntModelSpec; +import org.apache.jena.ontapi.OntModelFactory; +import org.apache.jena.ontapi.OntSpecification; +import org.apache.jena.ontapi.model.OntModel; +import org.apache.jena.graph.Graph; +import java.util.HashSet; +import com.atomgraph.linkeddatahub.server.filter.request.OntologyFilter; import org.apache.jena.rdf.model.Model; import org.apache.jena.rdf.model.ModelFactory; import org.apache.jena.rdf.model.Resource; @@ -82,11 +85,11 @@ public Response post(@FormParam("uri") String ontologyURI, @HeaderParam("Referer if (ontologyURI == null) throw new BadRequestException("Ontology URI not specified"); EndUserApplication endUserApp = getApplication().as(AdminApplication.class).getEndUserApplication(); // we're assuming the current app is admin - OntModelSpec ontModelSpec = new OntModelSpec(getSystem().getOntModelSpec(endUserApp)); - if (ontModelSpec.getDocumentManager().getFileManager().hasCachedModel(ontologyURI)) + OntologyRepository repository = getSystem().getRepository(endUserApp); + if (repository.isCached(ontologyURI)) { if (log.isDebugEnabled()) log.debug("Clearing ontology with URI '{}' from memory", ontologyURI); - ontModelSpec.getDocumentManager().getFileManager().removeCacheModel(ontologyURI); + repository.remove(ontologyURI); URI ontologyDocURI = UriBuilder.fromUri(ontologyURI).fragment(null).build(); // skip fragment from the ontology URI to get its graph URI // frontend proxy still uses URL-pattern BAN for direct document GETs (until Stage 3 brings xkey tagging to varnish-frontend). @@ -117,17 +120,15 @@ public Response post(@FormParam("uri") String ontologyURI, @HeaderParam("Referer // !!! we need to reload the ontology model before returning a response, to make sure the next request already gets the new version !!! // same logic as in OntologyFilter. TO-DO: encapsulate? - OntologyModelGetter modelGetter = new OntologyModelGetter(endUserApp, getSystem(), ontModelSpec, getSystem().getOntologyQuery()); - ontModelSpec.setImportModelGetter(modelGetter); if (log.isDebugEnabled()) log.debug("Started loading ontology with URI '{}' from the admin dataset", ontologyURI); - Model baseModel = modelGetter.getModel(ontologyURI); - OntModel ontModel = ModelFactory.createOntologyModel(ontModelSpec, baseModel); - // materialize OntModel inferences to avoid invoking rules engine on every request - OntModel materializedModel = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM); // no inference - materializedModel.add(ontModel); - ontModel.getDocumentManager().addModel(ontologyURI, materializedModel, true); // make immutable and add as OntModel so that imports do not need to be reloaded during retrieval - // make sure to cache imported models not only by ontology URI but also by document URI - ontModel.listImportedOntologyURIs(true).forEach((String importURI) -> addDocumentModel(ontModel.getDocumentManager(), importURI)); + Graph baseGraph = repository.get(ontologyURI); + OntModel inferred = OntModelFactory.createModel(baseGraph, OntSpecification.OWL2_DL_MEM_RDFS_INF, repository); + // materialize inferences to avoid invoking the rules engine on every request + OntModel materialized = OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM); + materialized.add(inferred); + repository.put(ontologyURI, materialized.getGraph()); + // cache imported graphs under their document URIs too + OntologyFilter.importClosure(inferred, new HashSet<>()).forEach(importURI -> OntologyFilter.addDocumentModel(repository, importURI)); if (log.isDebugEnabled()) log.debug("Finished loading ontology with URI '{}' from the admin dataset", ontologyURI); } diff --git a/src/main/java/com/atomgraph/linkeddatahub/resource/admin/SignUp.java b/src/main/java/com/atomgraph/linkeddatahub/resource/admin/SignUp.java index d71770a2cc..bc8467d0dd 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/resource/admin/SignUp.java +++ b/src/main/java/com/atomgraph/linkeddatahub/resource/admin/SignUp.java @@ -72,7 +72,7 @@ import jakarta.ws.rs.core.UriInfo; import jakarta.ws.rs.ext.Providers; import static org.apache.jena.datatypes.xsd.XSDDatatype.XSDhexBinary; -import org.apache.jena.ontology.Ontology; +import org.apache.jena.ontapi.model.OntModel; import org.apache.jena.query.ParameterizedSparqlString; import org.apache.jena.query.Query; import org.apache.jena.query.ResultSet; @@ -144,7 +144,7 @@ public class SignUp extends DocumentHierarchyGraphStoreImpl // TO-DO: move to AuthenticationExceptionMapper and handle as state instead of URI resource? @Inject public SignUp(@Context Request request, @Context UriInfo uriInfo, MediaTypes mediaTypes, - com.atomgraph.linkeddatahub.apps.model.Application application, Optional ontology, Optional service, + com.atomgraph.linkeddatahub.apps.model.Application application, Optional ontology, Optional service, @Context SecurityContext securityContext, Optional agentContext, @Context Providers providers, com.atomgraph.linkeddatahub.Application system, @Context ServletConfig servletConfig) { diff --git a/src/main/java/com/atomgraph/linkeddatahub/resource/admin/pkg/InstallPackage.java b/src/main/java/com/atomgraph/linkeddatahub/resource/admin/pkg/InstallPackage.java index 9e760cd0b1..e178f6dc67 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/resource/admin/pkg/InstallPackage.java +++ b/src/main/java/com/atomgraph/linkeddatahub/resource/admin/pkg/InstallPackage.java @@ -17,7 +17,6 @@ package com.atomgraph.linkeddatahub.resource.admin.pkg; import static com.atomgraph.client.MediaType.TEXT_XSL; -import com.atomgraph.client.util.DataManager; import com.atomgraph.linkeddatahub.apps.model.AdminApplication; import com.atomgraph.linkeddatahub.apps.model.EndUserApplication; import com.atomgraph.linkeddatahub.client.GraphStoreClient; @@ -60,7 +59,6 @@ import org.apache.jena.ontology.ConversionException; import org.apache.jena.update.UpdateFactory; import org.apache.jena.update.UpdateRequest; -import org.apache.jena.util.FileManager; import com.atomgraph.linkeddatahub.vocabulary.DH; import com.atomgraph.linkeddatahub.vocabulary.FOAF; import com.atomgraph.linkeddatahub.vocabulary.SIOC; @@ -88,7 +86,6 @@ public class InstallPackage private final com.atomgraph.linkeddatahub.apps.model.Application application; private final com.atomgraph.linkeddatahub.Application system; - private final DataManager dataManager; private final Optional agentContext; @Context ServletContext servletContext; @@ -105,12 +102,10 @@ public class InstallPackage @Inject public InstallPackage(com.atomgraph.linkeddatahub.apps.model.Application application, com.atomgraph.linkeddatahub.Application system, - DataManager dataManager, Optional agentContext) { this.application = application; this.system = system; - this.dataManager = dataManager; this.agentContext = agentContext; } @@ -242,12 +237,12 @@ private com.atomgraph.linkeddatahub.apps.model.Package getPackage(String package final Model model; // check if we have the model in the cache first and if yes, return it from there instead making an HTTP request - if (((FileManager)getDataManager()).hasCachedModel(packageURI) || - (getDataManager().isResolvingMapped() && getDataManager().isMapped(packageURI))) // read mapped URIs (such as system ontologies) from a file + if (getSystem().getRepository().isCached(packageURI) || + (getSystem().getRepository().isMapped(packageURI))) // read mapped URIs (such as system ontologies) from a file { - if (log.isDebugEnabled()) log.debug("hasCachedModel({}): {}", packageURI, ((FileManager)getDataManager()).hasCachedModel(packageURI)); - if (log.isDebugEnabled()) log.debug("isMapped({}): {}", packageURI, getDataManager().isMapped(packageURI)); - model = getDataManager().loadModel(packageURI); + if (log.isDebugEnabled()) log.debug("hasCachedModel({}): {}", packageURI, getSystem().getRepository().isCached(packageURI)); + if (log.isDebugEnabled()) log.debug("isMapped({}): {}", packageURI, getSystem().getRepository().isMapped(packageURI)); + model = ModelFactory.createModelForGraph(getSystem().getRepository().get(packageURI)); } else { @@ -284,12 +279,12 @@ private Model downloadOntology(String uri) if (log.isDebugEnabled()) log.debug("Downloading ontology from: {}", uri); // check if we have the model in the cache first and if yes, return it from there instead making an HTTP request - if (((FileManager)getDataManager()).hasCachedModel(uri) || - (getDataManager().isResolvingMapped() && getDataManager().isMapped(uri))) // read mapped URIs (such as system ontologies) from a file + if (getSystem().getRepository().isCached(uri) || + (getSystem().getRepository().isMapped(uri))) // read mapped URIs (such as system ontologies) from a file { - if (log.isDebugEnabled()) log.debug("hasCachedModel({}): {}", uri, ((FileManager)getDataManager()).hasCachedModel(uri)); - if (log.isDebugEnabled()) log.debug("isMapped({}): {}", uri, getDataManager().isMapped(uri)); - return getDataManager().loadModel(uri); + if (log.isDebugEnabled()) log.debug("hasCachedModel({}): {}", uri, getSystem().getRepository().isCached(uri)); + if (log.isDebugEnabled()) log.debug("isMapped({}): {}", uri, getSystem().getRepository().isMapped(uri)); + return ModelFactory.createModelForGraph(getSystem().getRepository().get(uri)); } else { @@ -522,15 +517,6 @@ public ServletContext getServletContext() return servletContext; } - /** - * Returns RDF data manager. - * - * @return RDF data manager - */ - public DataManager getDataManager() - { - return dataManager; - } /** * Returns JAX-RS resource context. diff --git a/src/main/java/com/atomgraph/linkeddatahub/resource/admin/pkg/UninstallPackage.java b/src/main/java/com/atomgraph/linkeddatahub/resource/admin/pkg/UninstallPackage.java index fb0cb5f3c7..28d37ca9fa 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/resource/admin/pkg/UninstallPackage.java +++ b/src/main/java/com/atomgraph/linkeddatahub/resource/admin/pkg/UninstallPackage.java @@ -16,7 +16,6 @@ */ package com.atomgraph.linkeddatahub.resource.admin.pkg; -import com.atomgraph.client.util.DataManager; import com.atomgraph.linkeddatahub.apps.model.AdminApplication; import com.atomgraph.linkeddatahub.apps.model.EndUserApplication; import com.atomgraph.linkeddatahub.client.GraphStoreClient; @@ -39,10 +38,10 @@ import jakarta.ws.rs.core.UriBuilder; import org.apache.commons.codec.binary.Hex; import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.ModelFactory; import org.apache.jena.rdf.model.Resource; import org.apache.jena.update.UpdateFactory; import org.apache.jena.update.UpdateRequest; -import org.apache.jena.util.FileManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; @@ -73,7 +72,6 @@ public class UninstallPackage private final com.atomgraph.linkeddatahub.apps.model.Application application; private final com.atomgraph.linkeddatahub.Application system; - private final DataManager dataManager; private final Optional agentContext; @Context ServletContext servletContext; @@ -90,12 +88,10 @@ public class UninstallPackage @Inject public UninstallPackage(com.atomgraph.linkeddatahub.apps.model.Application application, com.atomgraph.linkeddatahub.Application system, - DataManager dataManager, Optional agentContext) { this.application = application; this.system = system; - this.dataManager = dataManager; this.agentContext = agentContext; } @@ -317,12 +313,12 @@ private com.atomgraph.linkeddatahub.apps.model.Package getPackage(String package final Model model; // check if we have the model in the cache first and if yes, return it from there instead making an HTTP request - if (((FileManager)getDataManager()).hasCachedModel(packageURI) || - (getDataManager().isResolvingMapped() && getDataManager().isMapped(packageURI))) // read mapped URIs (such as system ontologies) from a file + if (getSystem().getRepository().isCached(packageURI) || + (getSystem().getRepository().isMapped(packageURI))) // read mapped URIs (such as system ontologies) from a file { - if (log.isDebugEnabled()) log.debug("hasCachedModel({}): {}", packageURI, ((FileManager)getDataManager()).hasCachedModel(packageURI)); - if (log.isDebugEnabled()) log.debug("isMapped({}): {}", packageURI, getDataManager().isMapped(packageURI)); - model = getDataManager().loadModel(packageURI); + if (log.isDebugEnabled()) log.debug("hasCachedModel({}): {}", packageURI, getSystem().getRepository().isCached(packageURI)); + if (log.isDebugEnabled()) log.debug("isMapped({}): {}", packageURI, getSystem().getRepository().isMapped(packageURI)); + model = ModelFactory.createModelForGraph(getSystem().getRepository().get(packageURI)); } else { @@ -350,15 +346,6 @@ public com.atomgraph.linkeddatahub.Application getSystem() return system; } - /** - * Returns RDF data manager. - * - * @return RDF data manager - */ - public DataManager getDataManager() - { - return dataManager; - } /** * Returns JAX-RS resource context. diff --git a/src/main/java/com/atomgraph/linkeddatahub/server/event/AuthorizationCreated.java b/src/main/java/com/atomgraph/linkeddatahub/server/event/AuthorizationCreated.java index 7353e02978..3b02124d24 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/server/event/AuthorizationCreated.java +++ b/src/main/java/com/atomgraph/linkeddatahub/server/event/AuthorizationCreated.java @@ -16,7 +16,6 @@ */ package com.atomgraph.linkeddatahub.server.event; -import com.atomgraph.core.util.jena.DataManager; import com.atomgraph.linkeddatahub.apps.model.Application; import com.atomgraph.linkeddatahub.client.GraphStoreClient; import org.apache.jena.rdf.model.Resource; diff --git a/src/main/java/com/atomgraph/linkeddatahub/server/factory/OntologyFactory.java b/src/main/java/com/atomgraph/linkeddatahub/server/factory/OntologyFactory.java index 39fe494b6f..8fc56c6a24 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/server/factory/OntologyFactory.java +++ b/src/main/java/com/atomgraph/linkeddatahub/server/factory/OntologyFactory.java @@ -20,7 +20,7 @@ import jakarta.ws.rs.container.ContainerRequestContext; import jakarta.ws.rs.core.Context; import jakarta.ws.rs.ext.Provider; -import org.apache.jena.ontology.Ontology; +import org.apache.jena.ontapi.model.OntModel; import org.apache.jena.vocabulary.OWL; import org.glassfish.hk2.api.Factory; import org.glassfish.hk2.api.ServiceLocator; @@ -31,19 +31,19 @@ * @author Martynas Jusevičius {@literal } */ @Provider -public class OntologyFactory implements Factory> +public class OntologyFactory implements Factory> { @Context private ServiceLocator serviceLocator; @Override - public Optional provide() + public Optional provide() { return getOntology(); } @Override - public void dispose(Optional t) + public void dispose(Optional t) { } @@ -52,9 +52,9 @@ public void dispose(Optional t) * * @return ontology */ - public Optional getOntology() + public Optional getOntology() { - return (Optional)getContainerRequestContext().getProperty(OWL.Ontology.getURI()); + return (Optional)getContainerRequestContext().getProperty(OWL.Ontology.getURI()); } /** diff --git a/src/main/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyFilter.java b/src/main/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyFilter.java index 03ef836621..a09b31bd07 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyFilter.java +++ b/src/main/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyFilter.java @@ -18,26 +18,25 @@ import com.atomgraph.linkeddatahub.apps.model.Application; import com.atomgraph.linkeddatahub.apps.model.EndUserApplication; +import com.atomgraph.core.util.jena.PrefixGraphRepository; import com.atomgraph.linkeddatahub.vocabulary.LAPP; import com.atomgraph.server.exception.OntologyException; -import com.atomgraph.linkeddatahub.server.util.OntologyModelGetter; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; +import java.util.HashSet; import java.util.Optional; +import java.util.Set; import jakarta.annotation.Priority; import jakarta.inject.Inject; -import jakarta.ws.rs.InternalServerErrorException; import jakarta.ws.rs.container.ContainerRequestContext; import jakarta.ws.rs.container.ContainerRequestFilter; import jakarta.ws.rs.container.PreMatching; -import org.apache.jena.ontology.OntDocumentManager; -import org.apache.jena.ontology.OntModel; -import org.apache.jena.ontology.OntModelSpec; -import org.apache.jena.ontology.Ontology; -import org.apache.jena.rdf.model.Model; -import org.apache.jena.rdf.model.ModelFactory; -import org.apache.jena.util.FileManager; +import org.apache.jena.graph.Graph; +import org.apache.jena.ontapi.OntModelFactory; +import org.apache.jena.ontapi.OntSpecification; +import org.apache.jena.ontapi.model.OntID; +import org.apache.jena.ontapi.model.OntModel; import org.apache.jena.vocabulary.OWL; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -100,7 +99,7 @@ public void filter(ContainerRequestContext crc) throws IOException * @param crc request context * @return optional ontology */ - public Optional getOntology(ContainerRequestContext crc) + public Optional getOntology(ContainerRequestContext crc) { Optional appOpt = getApplication(crc); @@ -115,99 +114,86 @@ public Optional getOntology(ContainerRequestContext crc) return Optional.empty(); } } - + /** * Gets ontology of the specified application. - * + * * @param app application resource - * @return ontology resource + * @return ontology model */ - public Ontology getOntology(Application app) + public OntModel getOntology(Application app) { if (app.getOntology() == null) return null; return getOntology(app, app.getOntology().getURI()); } - + /** - * Loads ontology using the specified ontology URI. - * + * Loads the ontology model for the specified ontology URI, building its owl:imports closure with + * RDFS inference and materializing the inferences into the repository cache. + * * @param app application resource * @param uri ontology URI - * @return ontology resource + * @return ontology model */ - public Ontology getOntology(Application app, String uri) + public OntModel getOntology(Application app, String uri) { - if (app == null) throw new IllegalArgumentException("Application string cannot be null"); - if (uri == null) throw new IllegalArgumentException("Ontology URI string cannot be null"); + if (app == null) throw new IllegalArgumentException("Application cannot be null"); + if (uri == null) throw new IllegalArgumentException("Ontology URI cannot be null"); - final OntModelSpec ontModelSpec; - if (app.canAs(EndUserApplication.class)) + final PrefixGraphRepository repository = app.canAs(EndUserApplication.class) ? + getSystem().getRepository(app.as(EndUserApplication.class)) : getSystem().getRepository(); + + // only build the inferred model if the ontology is not already cached + if (!repository.isCached(uri)) { - ontModelSpec = new OntModelSpec(getSystem().getOntModelSpec(app.as(EndUserApplication.class))); - // only create InfModel if ontology is not already cached - if (!ontModelSpec.getDocumentManager().getFileManager().hasCachedModel(uri)) - { - OntologyModelGetter modelGetter = new OntologyModelGetter(app.as(EndUserApplication.class), getSystem(), ontModelSpec, getSystem().getOntologyQuery()); - ontModelSpec.setImportModelGetter(modelGetter); - if (log.isDebugEnabled()) log.debug("Started loading ontology with URI '{}' from the admin dataset", uri); - Model baseModel = modelGetter.getModel(uri); - OntModel ontModel = ModelFactory.createOntologyModel(ontModelSpec, baseModel); - // materialize OntModel inferences to avoid invoking rules engine on every request - OntModel materializedModel = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM); // no inference - materializedModel.add(ontModel); - ontModel.getDocumentManager().addModel(uri, materializedModel, true); // make immutable and add as OntModel so that imports do not need to be reloaded during retrieval - // make sure to cache imported models not only by ontology URI but also by document URI - ontModel.listImportedOntologyURIs(true).forEach((String importURI) -> addDocumentModel(ontModel.getDocumentManager(), importURI)); - if (log.isDebugEnabled()) log.debug("Finished loading ontology with URI '{}' from the admin dataset", uri); - } + if (log.isDebugEnabled()) log.debug("Started loading ontology with URI '{}'", uri); + Graph baseGraph = repository.get(uri); // end-user: SPARQL-first; otherwise bundled mapping / HTTP + OntModel inferred = OntModelFactory.createModel(baseGraph, OntSpecification.OWL2_DL_MEM_RDFS_INF, repository); + // materialize inferences to avoid invoking the rules engine on every request + OntModel materialized = OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM); + materialized.add(inferred); + repository.put(uri, materialized.getGraph()); + // cache imported graphs under their (fragment-stripped) document URIs too + importClosure(inferred, new HashSet<>()).forEach(importURI -> addDocumentModel(repository, importURI)); + if (log.isDebugEnabled()) log.debug("Finished loading ontology with URI '{}'", uri); } - else + + return OntModelFactory.createModel(repository.get(uri), OntSpecification.OWL2_DL_MEM, repository); + } + + /** + * Collects the transitive owl:imports closure URIs of the given ontology model. + * + * @param model ontology model + * @param seen accumulator of already-visited import URIs + * @return the import closure URIs + */ + public static Set importClosure(OntModel model, Set seen) + { + model.imports().forEach(imp -> imp.id().map(OntID::getURI).ifPresent(importURI -> { - ontModelSpec = new OntModelSpec(getSystem().getOntModelSpec()); - FileManager fileManager = ontModelSpec.getDocumentManager().getFileManager(); - if (!fileManager.hasCachedModel(uri)) - { - try - { - URI ontologyURI = URI.create(uri); - // remove fragment and normalize - URI ontDocURI = new URI(ontologyURI.getScheme(), ontologyURI.getSchemeSpecificPart(), null).normalize(); - Model baseModel = fileManager.loadModel(ontDocURI.toString()); - OntModel ontModel = ModelFactory.createOntologyModel(ontModelSpec, baseModel); - ontModel.getDocumentManager().addModel(uri, ontModel, true); - } - catch (URISyntaxException ex) - { - if (log.isErrorEnabled()) log.error("Ontology URI syntax error: {}", ex.getInput()); - throw new InternalServerErrorException(ex); - } - } - } - return ontModelSpec.getDocumentManager().getOntology(uri, ontModelSpec).getOntology(uri); // reloads the imports using ModelGetter. TO-DO: optimize? + if (seen.add(importURI)) importClosure(imp, seen); + })); + return seen; } /** - * Extracts document URI from ontology import URI and uses it as a secondary cache key. - * - * @param odm document manager + * Caches an imported graph under its fragment-stripped document URI as a secondary cache key. + * + * @param repository graph repository * @param importURI ontology URI */ - public static void addDocumentModel(OntDocumentManager odm, String importURI) + public static void addDocumentModel(PrefixGraphRepository repository, String importURI) { try { URI ontologyURI = URI.create(importURI); // remove fragment and normalize URI docURI = new URI(ontologyURI.getScheme(), ontologyURI.getSchemeSpecificPart(), null).normalize(); - String mappedURI = odm.getFileManager().mapURI(docURI.toString()); - // only cache import document URI if it's not already cached or mapped - if (!odm.getFileManager().hasCachedModel(docURI.toString()) && mappedURI.equals(docURI.toString())) - { - Model importModel = odm.getModel(importURI); - if (importModel == null) throw new IllegalArgumentException("Import model is not cached"); - odm.addModel(docURI.toString(), importModel, true); - } + // only cache the document URI if it is not already cached or mapped to a different location + if (!repository.isCached(docURI.toString()) && repository.resolve(docURI.toString()).equals(docURI.toString())) + repository.put(docURI.toString(), repository.get(importURI)); } catch (URISyntaxException ex) { diff --git a/src/main/java/com/atomgraph/linkeddatahub/server/filter/request/ProxyRequestFilter.java b/src/main/java/com/atomgraph/linkeddatahub/server/filter/request/ProxyRequestFilter.java index 4eeb6599e6..9166b28ea9 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/server/filter/request/ProxyRequestFilter.java +++ b/src/main/java/com/atomgraph/linkeddatahub/server/filter/request/ProxyRequestFilter.java @@ -22,7 +22,7 @@ import com.atomgraph.core.exception.BadGatewayException; import com.atomgraph.core.util.ModelUtils; import com.atomgraph.linkeddatahub.apps.model.Dataset; -import org.apache.jena.ontology.Ontology; +import org.apache.jena.ontapi.model.OntModel; import com.atomgraph.linkeddatahub.client.GraphStoreClient; import com.atomgraph.linkeddatahub.client.filter.auth.IDTokenDelegationFilter; import com.atomgraph.linkeddatahub.client.filter.auth.WebIDDelegationFilter; @@ -133,7 +133,7 @@ public class ProxyRequestFilter implements ContainerRequestFilter "Age"); @Inject com.atomgraph.linkeddatahub.Application system; - @Inject jakarta.inject.Provider> ontology; + @Inject jakarta.inject.Provider> ontology; @Inject MediaTypes mediaTypes; @Context Request request; @@ -178,10 +178,10 @@ public void filter(ContainerRequestContext requestContext) throws IOException || "HEAD".equalsIgnoreCase(requestContext.getMethod()); // serve mapped URIs (e.g. system ontologies) directly from the DataManager cache - if (isSafeMethod && getSystem().getDataManager().isMapped(targetURI.toString())) + if (isSafeMethod && getSystem().getRepository().isMapped(targetURI.toString())) { if (log.isDebugEnabled()) log.debug("Serving mapped URI from DataManager cache: {}", targetURI); - Model model = getSystem().getDataManager().loadModel(targetURI.toString()); + Model model = org.apache.jena.rdf.model.ModelFactory.createModelForGraph(getSystem().getRepository().get(targetURI.toString())); requestContext.abortWith(getResponse(model, Response.Status.OK)); return; } @@ -195,7 +195,7 @@ public void filter(ContainerRequestContext requestContext) throws IOException ParameterizedSparqlString pss = new ParameterizedSparqlString( "DESCRIBE ?doc ?term WHERE { ?term ?p ?o FILTER(STRSTARTS(STR(?term), CONCAT(STR(?doc), \"#\"))) }"); pss.setIri("doc", targetURI.toString()); - try (QueryExecution qe = QueryExecution.create(pss.asQuery(), getOntology().get().getOntModel())) + try (QueryExecution qe = QueryExecution.create(pss.asQuery(), getOntology().get())) { Model description = qe.execDescribe(); if (!description.isEmpty()) @@ -486,7 +486,7 @@ public com.atomgraph.linkeddatahub.Application getSystem() * * @return optional ontology */ - public Optional getOntology() + public Optional getOntology() { return ontology.get(); } diff --git a/src/main/java/com/atomgraph/linkeddatahub/server/io/ValidatingModelProvider.java b/src/main/java/com/atomgraph/linkeddatahub/server/io/ValidatingModelProvider.java index 206228401f..018bca6c29 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/server/io/ValidatingModelProvider.java +++ b/src/main/java/com/atomgraph/linkeddatahub/server/io/ValidatingModelProvider.java @@ -240,7 +240,7 @@ public Resource processRead(Resource resource) // this logic really belongs in a if (getApplication().isPresent() && getApplication().get().canAs(AdminApplication.class) && resource.hasProperty(RDF.type, OWL.Ontology)) { // clear cached OntModel if ontology is updated. TO-DO: send event instead - getSystem().getOntModelSpec().getDocumentManager().getFileManager().removeCacheModel(resource.getURI()); + getSystem().getRepository().remove(resource.getURI()); } if (getApplication().isPresent() && resource.hasProperty(RDF.type, ACL.Authorization)) diff --git a/src/main/java/com/atomgraph/linkeddatahub/server/model/impl/DocumentHierarchyGraphStoreImpl.java b/src/main/java/com/atomgraph/linkeddatahub/server/model/impl/DocumentHierarchyGraphStoreImpl.java index 0b8b65674f..e8f1bcf8d0 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/server/model/impl/DocumentHierarchyGraphStoreImpl.java +++ b/src/main/java/com/atomgraph/linkeddatahub/server/model/impl/DocumentHierarchyGraphStoreImpl.java @@ -89,7 +89,7 @@ import org.apache.commons.lang3.StringUtils; import org.apache.jena.atlas.RuntimeIOException; import org.apache.jena.datatypes.xsd.XSDDateTime; -import org.apache.jena.ontology.Ontology; +import org.apache.jena.ontapi.model.OntModel; import org.apache.jena.query.Dataset; import org.apache.jena.query.DatasetFactory; import org.apache.jena.rdf.model.Model; @@ -132,7 +132,7 @@ public class DocumentHierarchyGraphStoreImpl extends com.atomgraph.core.model.im public static final String UPLOADS_PATH = "uploads"; private final com.atomgraph.linkeddatahub.apps.model.Application application; - private final Ontology ontology; + private final OntModel ontology; private final Service service; private final Providers providers; private final com.atomgraph.linkeddatahub.Application system; @@ -160,7 +160,7 @@ public class DocumentHierarchyGraphStoreImpl extends com.atomgraph.core.model.im */ @Inject public DocumentHierarchyGraphStoreImpl(@Context Request request, @Context UriInfo uriInfo, MediaTypes mediaTypes, - com.atomgraph.linkeddatahub.apps.model.Application application, Optional ontology, Optional service, + com.atomgraph.linkeddatahub.apps.model.Application application, Optional ontology, Optional service, @Context SecurityContext securityContext, Optional agentContext, @Context Providers providers, com.atomgraph.linkeddatahub.Application system) { @@ -1030,7 +1030,7 @@ public com.atomgraph.linkeddatahub.apps.model.Application getApplication() * * @return ontology resource */ - public Ontology getOntology() + public OntModel getOntology() { return ontology; } diff --git a/src/main/java/com/atomgraph/linkeddatahub/server/util/OntologyModelGetter.java b/src/main/java/com/atomgraph/linkeddatahub/server/util/OntologyRepository.java similarity index 64% rename from src/main/java/com/atomgraph/linkeddatahub/server/util/OntologyModelGetter.java rename to src/main/java/com/atomgraph/linkeddatahub/server/util/OntologyRepository.java index 63c8d659d7..2d6044a8e6 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/server/util/OntologyModelGetter.java +++ b/src/main/java/com/atomgraph/linkeddatahub/server/util/OntologyRepository.java @@ -17,56 +17,57 @@ package com.atomgraph.linkeddatahub.server.util; import com.atomgraph.client.vocabulary.LDT; +import com.atomgraph.core.client.GraphStoreClient; +import com.atomgraph.core.util.jena.PrefixGraphRepository; import com.atomgraph.linkeddatahub.apps.model.EndUserApplication; -import com.atomgraph.server.exception.OntologyException; import jakarta.ws.rs.core.MultivaluedHashMap; import jakarta.ws.rs.core.MultivaluedMap; import jakarta.ws.rs.core.Response; -import org.apache.jena.ontology.OntModelSpec; +import org.apache.jena.graph.Graph; import org.apache.jena.query.ParameterizedSparqlString; import org.apache.jena.query.Query; import org.apache.jena.rdf.model.Model; -import org.apache.jena.rdf.model.ModelFactory; -import org.apache.jena.rdf.model.ModelReader; -import org.apache.jena.util.FileManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; - /** - * Application's ontology model getter. - * Loads ontology model using the configured ontology query + * Ontology graph repository that resolves graphs SPARQL-first: it runs the configured ontology query + * against the admin endpoint and, only if that returns nothing, falls back to the bundled mappings / + * HTTP loading of the superclass. Replaces the legacy {@code ModelGetter} plugged into {@code OntModelSpec}. + * + * @author Martynas Jusevičius {@literal } */ -public class OntologyModelGetter implements org.apache.jena.ontology.models.ModelGetter +public class OntologyRepository extends PrefixGraphRepository { - private static final Logger log = LoggerFactory.getLogger(OntologyModelGetter.class); + private static final Logger log = LoggerFactory.getLogger(OntologyRepository.class); private final EndUserApplication app; private final com.atomgraph.linkeddatahub.Application system; - private final OntModelSpec ontModelSpec; private final Query ontologyQuery; /** - * Constructs ontology getter for application. + * Constructs the repository for an application. * * @param app end-user application resource * @param system system application - * @param ontModelSpec ontology specification + * @param gsc Graph Store client for HTTP fallback loading * @param ontologyQuery SPARQL query that loads ontology terms */ - public OntologyModelGetter(EndUserApplication app, com.atomgraph.linkeddatahub.Application system, OntModelSpec ontModelSpec, Query ontologyQuery) + public OntologyRepository(EndUserApplication app, com.atomgraph.linkeddatahub.Application system, GraphStoreClient gsc, Query ontologyQuery) { + super(gsc); this.app = app; this.system = system; - this.ontModelSpec = ontModelSpec; this.ontologyQuery = ontologyQuery; } @Override - public Model getModel(String uri) + public Graph get(String uri) { - // attempt to load ontology model from the admin endpoint. TO-DO: is that necessary if ontologies terms are now stored in a single graph? + if (isCached(uri)) return super.get(uri); + + // attempt to load the ontology from the admin endpoint ParameterizedSparqlString ontologyPss = new ParameterizedSparqlString(getOntologyQuery().toString()); ontologyPss.setIri(LDT.ontology.getLocalName(), uri); @@ -82,24 +83,15 @@ public Model getModel(String uri) model = cr.readEntity(Model.class); } - if (!model.isEmpty()) return model; - - // if SPARQL result model is empty, fallback to using FileManager - FileManager fileManager = getOntModelSpec().getDocumentManager().getFileManager(); - return fileManager.loadModel(uri); - } - - @Override - public Model getModel(String uri, ModelReader loadIfAbsent) - { - try - { - return getModel(uri); - } - catch (OntologyException ex) + if (!model.isEmpty()) { - return loadIfAbsent.readModel(ModelFactory.createDefaultModel(), uri); + Graph graph = model.getGraph(); + put(uri, graph); + return graph; } + + // if the SPARQL result is empty, fall back to bundled mappings / HTTP loading + return super.get(uri); } /** @@ -122,24 +114,14 @@ public com.atomgraph.linkeddatahub.Application getSystem() return system; } - /** - * Returns ontology specification. - * - * @return ontology specification - */ - public OntModelSpec getOntModelSpec() - { - return ontModelSpec; - } - /** * Returns the SPARQL query used to load ontology terms. - * + * * @return SPARQL query */ public Query getOntologyQuery() { return ontologyQuery; } - -} \ No newline at end of file + +} diff --git a/src/main/java/com/atomgraph/linkeddatahub/writer/ModelXSLTWriter.java b/src/main/java/com/atomgraph/linkeddatahub/writer/ModelXSLTWriter.java index bdf1ebb5c1..eb7b2daaf8 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/writer/ModelXSLTWriter.java +++ b/src/main/java/com/atomgraph/linkeddatahub/writer/ModelXSLTWriter.java @@ -15,7 +15,7 @@ */ package com.atomgraph.linkeddatahub.writer; -import com.atomgraph.client.util.DataManager; +import com.atomgraph.client.util.RDFSourceResolver; import com.atomgraph.linkeddatahub.apps.model.EndUserApplication; import com.atomgraph.linkeddatahub.model.auth.Agent; import com.atomgraph.linkeddatahub.server.io.ValidatingModelProvider; @@ -41,7 +41,6 @@ import net.sf.saxon.s9api.SaxonApiException; import net.sf.saxon.s9api.XsltExecutable; import org.apache.http.HttpHeaders; -import org.apache.jena.ontology.OntModelSpec; import org.apache.jena.rdf.model.Model; import org.apache.jena.riot.RDFFormat; import org.apache.jena.riot.RDFWriter; @@ -70,9 +69,9 @@ public class ModelXSLTWriter extends XSLTWriterBase implements MessageBodyWriter * @param dataManager RDF data manager * @param messageDigest message digest */ - public ModelXSLTWriter(XsltExecutable xsltExec, OntModelSpec ontModelSpec, DataManager dataManager, MessageDigest messageDigest) + public ModelXSLTWriter(XsltExecutable xsltExec, RDFSourceResolver resolver, MessageDigest messageDigest) { - super(xsltExec, ontModelSpec, dataManager, messageDigest); + super(xsltExec, resolver, messageDigest); } @Override diff --git a/src/main/java/com/atomgraph/linkeddatahub/writer/ResultSetXSLTWriter.java b/src/main/java/com/atomgraph/linkeddatahub/writer/ResultSetXSLTWriter.java index fd7b24cfe0..df0d42d64b 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/writer/ResultSetXSLTWriter.java +++ b/src/main/java/com/atomgraph/linkeddatahub/writer/ResultSetXSLTWriter.java @@ -15,7 +15,7 @@ */ package com.atomgraph.linkeddatahub.writer; -import com.atomgraph.client.util.DataManager; +import com.atomgraph.client.util.RDFSourceResolver; import com.atomgraph.linkeddatahub.model.auth.Agent; import jakarta.inject.Singleton; import jakarta.ws.rs.Produces; @@ -38,7 +38,6 @@ import net.sf.saxon.s9api.SaxonApiException; import net.sf.saxon.s9api.XsltExecutable; import org.apache.http.HttpHeaders; -import org.apache.jena.ontology.OntModelSpec; import org.apache.jena.query.ResultSetFormatter; import org.apache.jena.query.ResultSetRewindable; import org.slf4j.Logger; @@ -65,9 +64,9 @@ public class ResultSetXSLTWriter extends XSLTWriterBase implements MessageBodyWr * @param dataManager RDF data manager * @param messageDigest message digest */ - public ResultSetXSLTWriter(XsltExecutable xsltExec, OntModelSpec ontModelSpec, DataManager dataManager, MessageDigest messageDigest) + public ResultSetXSLTWriter(XsltExecutable xsltExec, RDFSourceResolver resolver, MessageDigest messageDigest) { - super(xsltExec, ontModelSpec, dataManager, messageDigest); + super(xsltExec, resolver, messageDigest); } @Override diff --git a/src/main/java/com/atomgraph/linkeddatahub/writer/XSLTWriterBase.java b/src/main/java/com/atomgraph/linkeddatahub/writer/XSLTWriterBase.java index 50fc80af8f..31eb596c4b 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/writer/XSLTWriterBase.java +++ b/src/main/java/com/atomgraph/linkeddatahub/writer/XSLTWriterBase.java @@ -15,7 +15,7 @@ */ package com.atomgraph.linkeddatahub.writer; -import com.atomgraph.client.util.DataManager; +import com.atomgraph.client.util.RDFSourceResolver; import com.atomgraph.client.vocabulary.AC; import com.atomgraph.linkeddatahub.writer.factory.xslt.XsltExecutableSupplier; import com.atomgraph.linkeddatahub.model.auth.Agent; @@ -58,8 +58,7 @@ import net.sf.saxon.s9api.XdmValue; import net.sf.saxon.s9api.XsltExecutable; import org.apache.http.HttpHeaders; -import org.apache.jena.ontology.ObjectProperty; -import org.apache.jena.ontology.OntModelSpec; +import org.apache.jena.rdf.model.Property; import org.apache.jena.rdf.model.Model; import org.apache.jena.riot.RDFLanguages; import org.slf4j.Logger; @@ -88,7 +87,7 @@ public abstract class XSLTWriterBase extends com.atomgraph.client.writer.XSLTWri @Inject com.atomgraph.linkeddatahub.Application system; @Inject jakarta.inject.Provider> application; - @Inject jakarta.inject.Provider dataManager; + @Inject jakarta.inject.Provider resolver; @Inject jakarta.inject.Provider xsltExecSupplier; @Inject jakarta.inject.Provider crc; @Inject jakarta.inject.Provider> authorizationContext; @@ -97,15 +96,14 @@ public abstract class XSLTWriterBase extends com.atomgraph.client.writer.XSLTWri /** * Constructs XSLT writer. - * + * * @param xsltExec compiled XSLT stylesheet - * @param ontModelSpec ontology specification - * @param dataManager RDF data manager + * @param resolver XSLT source resolver * @param messageDigest message digest */ - public XSLTWriterBase(XsltExecutable xsltExec, OntModelSpec ontModelSpec, DataManager dataManager, MessageDigest messageDigest) + public XSLTWriterBase(XsltExecutable xsltExec, RDFSourceResolver resolver, MessageDigest messageDigest) { - super(xsltExec, ontModelSpec, dataManager); // this DataManager will be unused as we override getDataManager() with the injected (subclassed) one + super(xsltExec, resolver); // this resolver is unused as we override getResolver() with the injected (request-scoped) one this.messageDigest = messageDigest; } @@ -189,7 +187,7 @@ public Map getParameters(MultivaluedMap headerMap, ObjectProperty property) + public URI getLinkURI(MultivaluedMap headerMap, Property property) { if (headerMap.get(jakarta.ws.rs.core.HttpHeaders.LINK) == null) return null; @@ -257,36 +255,30 @@ public com.atomgraph.linkeddatahub.Application getSystem() return system; } - @Override - public OntModelSpec getOntModelSpec() - { - return getSystem().getOntModelSpec(); - } - /** * Returns JAX-RS security context. - * + * * @return security context */ public SecurityContext getSecurityContext() { return securityContext; } - + @Override - public DataManager getDataManager() + public RDFSourceResolver getResolver() { - return getDataManagerProvider().get(); + return getResolverProvider().get(); } /** - * Returns a JAX-RS provider for the RDF data manager. + * Returns a JAX-RS provider for the request-scoped source resolver. * * @return provider */ - public jakarta.inject.Provider getDataManagerProvider() + public jakarta.inject.Provider getResolverProvider() { - return dataManager; + return resolver; } @Override diff --git a/src/main/java/com/atomgraph/linkeddatahub/writer/factory/DataManagerFactory.java b/src/main/java/com/atomgraph/linkeddatahub/writer/factory/SourceResolverFactory.java similarity index 70% rename from src/main/java/com/atomgraph/linkeddatahub/writer/factory/DataManagerFactory.java rename to src/main/java/com/atomgraph/linkeddatahub/writer/factory/SourceResolverFactory.java index a9774fd63a..a405a7ca23 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/writer/factory/DataManagerFactory.java +++ b/src/main/java/com/atomgraph/linkeddatahub/writer/factory/SourceResolverFactory.java @@ -16,18 +16,16 @@ */ package com.atomgraph.linkeddatahub.writer.factory; -import org.apache.jena.util.LocationMapper; import jakarta.ws.rs.core.Context; import jakarta.ws.rs.ext.Provider; -import com.atomgraph.client.util.DataManager; +import com.atomgraph.client.util.RDFSourceResolver; +import com.atomgraph.core.util.jena.PrefixGraphRepository; import com.atomgraph.linkeddatahub.apps.model.Application; import com.atomgraph.linkeddatahub.apps.model.EndUserApplication; import com.atomgraph.linkeddatahub.client.GraphStoreClient; import com.atomgraph.linkeddatahub.server.security.AgentContext; import com.atomgraph.linkeddatahub.vocabulary.LAPP; -import com.atomgraph.linkeddatahub.writer.impl.DataManagerImpl; -import java.net.URI; -import java.util.HashMap; +import com.atomgraph.linkeddatahub.writer.impl.SameSiteSourceResolver; import java.util.Optional; import jakarta.inject.Inject; import jakarta.servlet.http.HttpServletRequest; @@ -40,15 +38,15 @@ import org.slf4j.LoggerFactory; /** - * JAX-RS provider for DataManager. - * + * JAX-RS provider for the request-scoped XSLT source resolver. + * * @author Martynas Jusevičius {@literal } - * @see com.atomgraph.client.util.DataManager + * @see com.atomgraph.client.util.RDFSourceResolver */ @Provider -public class DataManagerFactory implements Factory +public class SourceResolverFactory implements Factory { - private static final Logger log = LoggerFactory.getLogger(DataManagerFactory.class); + private static final Logger log = LoggerFactory.getLogger(SourceResolverFactory.class); @Context UriInfo uriInfo; @Context HttpServletRequest httpServletRequest; @@ -58,40 +56,37 @@ public class DataManagerFactory implements Factory @Inject com.atomgraph.linkeddatahub.Application system; @Override - public DataManager provide() + public RDFSourceResolver provide() { - // Always return DataManager, falling back to system DataManager if no Application (e.g., for error responses) - return getDataManager(getApplication()); + // falls back to the global repository if there is no application (e.g. for error responses) + return getResolver(getApplication()); } @Override - public void dispose(DataManager t) + public void dispose(RDFSourceResolver t) { } /** - * Returns RDF data manager. + * Returns the request-scoped source resolver, backed by the application's ontology repository + * (or the global repository) and a delegating Graph Store client. * - * @param appOpt optional end-user application (if empty, system DataManager is used) - * @return data manager + * @param appOpt optional end-user application (if empty, the global repository is used) + * @return source resolver */ - public DataManager getDataManager(Optional appOpt) + public RDFSourceResolver getResolver(Optional appOpt) { - final com.atomgraph.core.util.jena.DataManager baseManager; + final PrefixGraphRepository repository; if (appOpt.isPresent() && appOpt.get().canAs(EndUserApplication.class)) - baseManager = (com.atomgraph.core.util.jena.DataManager)getSystem().getOntModelSpec(appOpt.get().as(EndUserApplication.class)).getDocumentManager().getFileManager(); + repository = getSystem().getRepository(appOpt.get().as(EndUserApplication.class)); else - baseManager = getSystem().getDataManager(); + repository = getSystem().getRepository(); GraphStoreClient gsc = GraphStoreClient.create(getSystem().getClient(), getSystem().getMediaTypes()). delegation(getUriInfo().getBaseUri(), getAgentContext()); - // copy cached models over from the app's FileManager - return new DataManagerImpl(LocationMapper.get(), new HashMap<>(baseManager.getModelCache()), - gsc, true, getSystem().isPreemptiveAuth(), getSystem().isResolvingUncached(), - getSystem().getBaseURI(), - getAgentContext()); + return new SameSiteSourceResolver(repository, gsc, getSystem().isResolvingUncached(), getSystem().getBaseURI()); } /** diff --git a/src/main/java/com/atomgraph/linkeddatahub/writer/impl/DataManagerImpl.java b/src/main/java/com/atomgraph/linkeddatahub/writer/impl/SameSiteSourceResolver.java similarity index 59% rename from src/main/java/com/atomgraph/linkeddatahub/writer/impl/DataManagerImpl.java rename to src/main/java/com/atomgraph/linkeddatahub/writer/impl/SameSiteSourceResolver.java index b62cfefad9..3449bd2b3e 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/writer/impl/DataManagerImpl.java +++ b/src/main/java/com/atomgraph/linkeddatahub/writer/impl/SameSiteSourceResolver.java @@ -16,67 +16,57 @@ */ package com.atomgraph.linkeddatahub.writer.impl; -import org.apache.jena.util.LocationMapper; -import java.net.URI; +import com.atomgraph.client.util.RDFSourceResolver; import com.atomgraph.core.client.GraphStoreClient; -import com.atomgraph.linkeddatahub.server.security.AgentContext; +import com.atomgraph.core.util.jena.PrefixGraphRepository; import com.google.common.net.InternetDomainName; -import java.util.Map; -import org.apache.jena.rdf.model.Model; +import java.net.URI; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * Manager for remote RDF dataset access. - * Documents can be mapped to local copies. - * + * XSLT source resolver that restricts dereferencing of uncached URIs to the same site as the + * application. Mapped (bundled) and same-site URIs resolve; arbitrary external URIs do not. + * Backed by a {@link PrefixGraphRepository}; authenticated delegation is handled by the supplied + * {@link GraphStoreClient}. + * * @author Martynas Jusevičius {@literal } */ -public class DataManagerImpl extends com.atomgraph.client.util.DataManagerImpl +public class SameSiteSourceResolver extends RDFSourceResolver { - private static final Logger log = LoggerFactory.getLogger(DataManagerImpl.class); - + private static final Logger log = LoggerFactory.getLogger(SameSiteSourceResolver.class); + private final URI rootContextURI; - private final AgentContext agentContext; /** - * Constructs RDF data manager. - * - * @param mapper location mapper - * @param modelCache model cache - * @param gsc Graph Store client - * @param cacheModelLoads true if loaded RDF models are cached - * @param preemptiveAuth true if HTTP basic auth is sent preemptively + * Constructs the resolver. + * + * @param repository graph repository (bundled/cached graphs + URI→location mapping) + * @param gsc Graph Store client (with delegation) * @param resolvingUncached true if uncached URLs are resolved * @param rootContextURI the root URI of the JAX-RS application - * @param agentContext agent context */ - public DataManagerImpl(LocationMapper mapper, Map modelCache, - GraphStoreClient gsc, - boolean cacheModelLoads, boolean preemptiveAuth, boolean resolvingUncached, - URI rootContextURI, - AgentContext agentContext) + public SameSiteSourceResolver(PrefixGraphRepository repository, GraphStoreClient gsc, boolean resolvingUncached, URI rootContextURI) { - super(mapper, modelCache, gsc, cacheModelLoads, preemptiveAuth, resolvingUncached); + super(repository, gsc, resolvingUncached); this.rootContextURI = rootContextURI; - this.agentContext = agentContext; } - + @Override - public boolean resolvingUncached(String filenameOrURI) + protected boolean resolvingUncached(String filenameOrURI) { - if (super.resolvingUncached(filenameOrURI) && !isMapped(filenameOrURI)) + if (super.resolvingUncached(filenameOrURI) && !getRepository().isMapped(filenameOrURI)) { // Allow resolving URIs from the same site (e.g., localhost:4443/static/..., admin.localhost:4443/ns) return isSameSite(getRootContextURI(), URI.create(filenameOrURI)); } - return false; // super.resolvingUncached(filenameOrURI); // configured in web.xml + return false; } /** * Checks if two URIs are from the same site (schemeful same-site). - * This allows subdomains like admin.localhost and localhost to be considered part of the same LinkedDataHub instance. + * Allows subdomains like admin.localhost and localhost to be considered part of the same instance. * Ports are ignored per the same-site definition. * * @param uri1 first URI @@ -99,12 +89,9 @@ private boolean isSameSite(URI uri1, URI uri2) InternetDomainName domain1 = InternetDomainName.from(host1); InternetDomainName domain2 = InternetDomainName.from(host2); - // For localhost domains, compare the full host (localhost == localhost, admin.localhost != localhost at domain level) - // But we want to treat them as same root domain, so just check if both end with "localhost" if (host1.equals("localhost") || host1.endsWith(".localhost")) return host2.equals("localhost") || host2.endsWith(".localhost"); - // For regular domains, compare top private domains if (domain1.isTopPrivateDomain() && domain2.isTopPrivateDomain()) return domain1.equals(domain2); if (domain1.hasPublicSuffix() && domain2.hasPublicSuffix()) @@ -114,7 +101,6 @@ private boolean isSameSite(URI uri1, URI uri2) } catch (IllegalArgumentException ex) { - // Invalid domain name, fall back to simple equality check if (log.isDebugEnabled()) log.debug("Could not parse domain names for comparison: {} and {}", host1, host2); return false; } @@ -122,7 +108,7 @@ private boolean isSameSite(URI uri1, URI uri2) /** * Returns the root URI of the JAX-RS application. - * + * * @return root URI */ public URI getRootContextURI() @@ -130,14 +116,4 @@ public URI getRootContextURI() return rootContextURI; } - /** - * Returns the agent context. - * - * @return agent context or null - */ - public AgentContext getAgentContext() - { - return agentContext; - } - -} \ No newline at end of file +} diff --git a/src/main/java/com/atomgraph/server/io/ValidatingDatasetProvider.java b/src/main/java/com/atomgraph/server/io/ValidatingDatasetProvider.java index 500fb2e5af..341e7c7eb3 100644 --- a/src/main/java/com/atomgraph/server/io/ValidatingDatasetProvider.java +++ b/src/main/java/com/atomgraph/server/io/ValidatingDatasetProvider.java @@ -30,7 +30,7 @@ import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.MultivaluedMap; import jakarta.ws.rs.ext.Providers; -import org.apache.jena.ontology.Ontology; +import org.apache.jena.ontapi.model.OntModel; import org.apache.jena.query.Dataset; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -52,7 +52,7 @@ public class ValidatingDatasetProvider extends DatasetProvider @Context private Providers providers; - @Inject jakarta.inject.Provider> ontology; + @Inject jakarta.inject.Provider> ontology; @Override public Dataset readFrom(Class type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders, InputStream entityStream) throws IOException @@ -70,7 +70,7 @@ public Dataset validate(Dataset dataset) if (getOntology().isPresent()) { // SPIN validation - Validator validator = new Validator(getOntology().get().getOntModel()); + Validator validator = new Validator(getOntology().get()); List cvs = validator.validate(dataset.getDefaultModel()); if (!cvs.isEmpty()) { @@ -79,7 +79,7 @@ public Dataset validate(Dataset dataset) } // SHACL validation - Shapes shapes = Shapes.parse(getOntology().get().getOntModel().getGraph()); + Shapes shapes = Shapes.parse(getOntology().get().getGraph()); ValidationReport report = ShaclValidator.get().validate(shapes, dataset.getDefaultModel().getGraph()); if (!report.conforms()) { @@ -113,7 +113,7 @@ public Dataset validate(Dataset dataset) return dataset; } - public Optional getOntology() + public Optional getOntology() { return ontology.get(); } diff --git a/src/main/java/com/atomgraph/server/io/ValidatingModelProvider.java b/src/main/java/com/atomgraph/server/io/ValidatingModelProvider.java index 416d8ada1c..1b348127b7 100644 --- a/src/main/java/com/atomgraph/server/io/ValidatingModelProvider.java +++ b/src/main/java/com/atomgraph/server/io/ValidatingModelProvider.java @@ -16,7 +16,7 @@ package com.atomgraph.server.io; -import org.apache.jena.ontology.Ontology; +import org.apache.jena.ontapi.model.OntModel; import org.apache.jena.rdf.model.Model; import java.io.IOException; import java.io.InputStream; @@ -51,7 +51,7 @@ public class ValidatingModelProvider extends BasedModelProvider @Context private Providers providers; - @Inject jakarta.inject.Provider> ontology; + @Inject jakarta.inject.Provider> ontology; @Override public Model readFrom(Class type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders, InputStream entityStream) throws IOException @@ -69,7 +69,7 @@ public Model validate(Model model) if (getOntology().isPresent()) { // SPIN validation - List cvs = new Validator(getOntology().get().getOntModel()).validate(model); + List cvs = new Validator(getOntology().get()).validate(model); if (!cvs.isEmpty()) { if (log.isDebugEnabled()) log.debug("SPIN constraint violations: {}", cvs); @@ -77,7 +77,7 @@ public Model validate(Model model) } // SHACL validation - Shapes shapes = Shapes.parse(getOntology().get().getOntModel().getGraph()); + Shapes shapes = Shapes.parse(getOntology().get().getGraph()); ValidationReport report = ShaclValidator.get().validate(shapes, model.getGraph()); if (!report.conforms()) { @@ -100,7 +100,7 @@ public Model processWrite(Model model) return model; } - public Optional getOntology() + public Optional getOntology() { return ontology.get(); } diff --git a/src/main/java/com/atomgraph/server/util/Validator.java b/src/main/java/com/atomgraph/server/util/Validator.java index fc66576e58..67a6afe98f 100644 --- a/src/main/java/com/atomgraph/server/util/Validator.java +++ b/src/main/java/com/atomgraph/server/util/Validator.java @@ -16,7 +16,7 @@ package com.atomgraph.server.util; -import org.apache.jena.ontology.OntModel; +import org.apache.jena.ontapi.model.OntModel; import org.apache.jena.rdf.model.Model; import java.util.List; import org.slf4j.Logger; diff --git a/src/test/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyFilterTest.java b/src/test/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyFilterTest.java index 76787051fc..aa6b7ac80a 100644 --- a/src/test/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyFilterTest.java +++ b/src/test/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyFilterTest.java @@ -16,18 +16,16 @@ */ package com.atomgraph.linkeddatahub.server.filter.request; -import org.apache.jena.ontology.OntDocumentManager; -import org.apache.jena.rdf.model.Model; +import com.atomgraph.core.util.jena.PrefixGraphRepository; +import org.apache.jena.graph.Graph; import org.apache.jena.rdf.model.ModelFactory; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** - * Characterization tests for {@link OntologyFilter#addDocumentModel}, which caches an - * imported ontology model under a SECONDARY key: the fragment-stripped document URI. - * - * This pins the current dual-key caching behavior so the migration to the new ontology - * API ({@code GraphRepository}) can be proven to retain it. + * Characterization tests for {@link OntologyFilter#addDocumentModel}, which caches an imported graph + * under a SECONDARY key: the fragment-stripped document URI. Pins the dual-key caching behavior + * retained from the legacy implementation. * * @author Martynas Jusevičius {@literal } */ @@ -38,42 +36,33 @@ public class OntologyFilterTest @Test public void testAddDocumentModelCachesUnderStrippedDocumentURI() { - OntDocumentManager odm = new OntDocumentManager(); + PrefixGraphRepository repository = new PrefixGraphRepository(null); String importURI = "http://example.org/onto#"; String docURI = "http://example.org/onto"; - Model imported = ModelFactory.createDefaultModel(); - imported.createResource(importURI + "Thing"); - odm.addModel(importURI, imported, true); - - // precondition: only the ontology (fragment) URI is known - assertNotNull(odm.getModel(importURI)); + Graph imported = ModelFactory.createDefaultModel().getGraph(); + repository.put(importURI, imported); - OntologyFilter.addDocumentModel(odm, importURI); + OntologyFilter.addDocumentModel(repository, importURI); - // the same model is now retrievable under the fragment-stripped document URI - assertNotNull(odm.getModel(docURI), "import model should be cached under the document URI"); - assertSame(imported, odm.getModel(docURI)); + assertTrue(repository.isCached(docURI), "import graph should be cached under the document URI"); + assertSame(imported, repository.get(docURI)); } - /** If the document URI is mapped (mapURI != docURI), the secondary cache write is skipped. */ + /** If the document URI is mapped to a different location, the secondary cache write is skipped. */ @Test public void testAddDocumentModelSkipsWhenDocumentURIMapped() { - OntDocumentManager odm = new OntDocumentManager(); + PrefixGraphRepository repository = new PrefixGraphRepository(null); String importURI = "http://example.org/mapped#"; String docURI = "http://example.org/mapped"; - // map the document URI to a different location -> guard mappedURI.equals(docURI) is false - odm.getFileManager().getLocationMapper().addAltEntry(docURI, "file:elsewhere.ttl"); - - Model imported = ModelFactory.createDefaultModel(); - odm.addModel(importURI, imported, true); + repository.addLocationMapping(docURI, "file:elsewhere.ttl"); // resolve(docURI) != docURI -> skip + repository.put(importURI, ModelFactory.createDefaultModel().getGraph()); - OntologyFilter.addDocumentModel(odm, importURI); + OntologyFilter.addDocumentModel(repository, importURI); - // no secondary cache entry created under the document URI - assertNull(odm.getModel(docURI), "mapped document URI should not be cached as a secondary key"); + assertFalse(repository.isCached(docURI), "mapped document URI should not be cached as a secondary key"); } } diff --git a/src/test/java/com/atomgraph/linkeddatahub/server/util/OntologyModelGetterTest.java b/src/test/java/com/atomgraph/linkeddatahub/server/util/OntologyRepositoryTest.java similarity index 64% rename from src/test/java/com/atomgraph/linkeddatahub/server/util/OntologyModelGetterTest.java rename to src/test/java/com/atomgraph/linkeddatahub/server/util/OntologyRepositoryTest.java index 4db93eac45..33b3183f25 100644 --- a/src/test/java/com/atomgraph/linkeddatahub/server/util/OntologyModelGetterTest.java +++ b/src/test/java/com/atomgraph/linkeddatahub/server/util/OntologyRepositoryTest.java @@ -16,19 +16,22 @@ */ package com.atomgraph.linkeddatahub.server.util; -import com.atomgraph.core.client.SPARQLClient; +import com.atomgraph.core.client.GraphStoreClient; import com.atomgraph.linkeddatahub.apps.model.AdminApplication; import com.atomgraph.linkeddatahub.apps.model.EndUserApplication; import com.atomgraph.linkeddatahub.model.Service; import com.atomgraph.linkeddatahub.model.ServiceContext; import jakarta.ws.rs.core.MultivaluedMap; import jakarta.ws.rs.core.Response; -import org.apache.jena.ontology.OntModelSpec; +import org.apache.jena.graph.Graph; +import org.apache.jena.graph.NodeFactory; import org.apache.jena.query.Query; import org.apache.jena.query.QueryFactory; import org.apache.jena.rdf.model.Model; import org.apache.jena.rdf.model.ModelFactory; -import org.apache.jena.util.FileManager; +import org.apache.jena.vocabulary.OWL; +import org.apache.jena.vocabulary.RDF; +import org.apache.jena.vocabulary.RDFS; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; @@ -41,17 +44,14 @@ import static org.mockito.Mockito.when; /** - * Characterization tests for {@link OntologyModelGetter}, which resolves an ontology model - * SPARQL-first (admin endpoint CONSTRUCT) and falls back to the FileManager only when the - * SPARQL result is empty. - * - * Pins the current resolution order so the migration to a {@code GraphRepository.get(uri)} - * can be proven to retain it. + * Characterization tests for {@link OntologyRepository}, which resolves an ontology graph + * SPARQL-first (admin endpoint CONSTRUCT) and falls back to the superclass (bundled mapping / HTTP) + * only when the SPARQL result is empty. * * @author Martynas Jusevičius {@literal } */ @ExtendWith(MockitoExtension.class) -public class OntologyModelGetterTest +public class OntologyRepositoryTest { private static final String ONTOLOGY_URI = "http://example.org/ontology#"; @@ -62,8 +62,9 @@ public class OntologyModelGetterTest @Mock AdminApplication adminApp; @Mock Service service; @Mock ServiceContext serviceContext; - @Mock SPARQLClient sparqlClient; + @Mock com.atomgraph.core.client.SPARQLClient sparqlClient; @Mock Response response; + @Mock GraphStoreClient gsc; private void stubSPARQLChain(Model sparqlResult) { @@ -75,43 +76,36 @@ private void stubSPARQLChain(Model sparqlResult) when(response.readEntity(Model.class)).thenReturn(sparqlResult); } - /** A non-empty admin SPARQL CONSTRUCT result is returned directly; the FileManager is not consulted. */ + /** A non-empty admin SPARQL CONSTRUCT result is returned directly; the HTTP fallback is not used. */ @Test public void testSPARQLResultUsedWhenNonEmpty() { Model sparqlResult = ModelFactory.createDefaultModel(); - sparqlResult.createResource(ONTOLOGY_URI).addProperty(org.apache.jena.vocabulary.RDF.type, org.apache.jena.vocabulary.OWL.Ontology); + sparqlResult.createResource(ONTOLOGY_URI).addProperty(RDF.type, OWL.Ontology); stubSPARQLChain(sparqlResult); - OntModelSpec spec = new OntModelSpec(OntModelSpec.OWL_MEM); - FileManager fileManager = org.mockito.Mockito.mock(FileManager.class); - spec.getDocumentManager().setFileManager(fileManager); - - OntologyModelGetter getter = new OntologyModelGetter(app, system, spec, ONTOLOGY_QUERY); - Model result = getter.getModel(ONTOLOGY_URI); + OntologyRepository repository = new OntologyRepository(app, system, gsc, ONTOLOGY_QUERY); + Graph result = repository.get(ONTOLOGY_URI); - assertSame(sparqlResult, result); - verify(fileManager, never()).loadModel(any()); + assertTrue(result.contains(NodeFactory.createURI(ONTOLOGY_URI), RDF.type.asNode(), OWL.Ontology.asNode())); + verify(gsc, never()).getModel(any()); } - /** An empty SPARQL result falls back to FileManager.loadModel(uri). */ + /** An empty SPARQL result falls back to the Graph Store client (HTTP) load. */ @Test - public void testFallsBackToFileManagerWhenSPARQLEmpty() + public void testFallsBackToHttpWhenSPARQLEmpty() { Model empty = ModelFactory.createDefaultModel(); stubSPARQLChain(empty); - OntModelSpec spec = new OntModelSpec(OntModelSpec.OWL_MEM); - FileManager fileManager = org.mockito.Mockito.mock(FileManager.class); - spec.getDocumentManager().setFileManager(fileManager); Model fallback = ModelFactory.createDefaultModel(); - fallback.createResource(ONTOLOGY_URI); - when(fileManager.loadModel(ONTOLOGY_URI)).thenReturn(fallback); + fallback.createResource(ONTOLOGY_URI).addProperty(RDFS.label, "fallback"); + when(gsc.getModel(ONTOLOGY_URI)).thenReturn(fallback); - OntologyModelGetter getter = new OntologyModelGetter(app, system, spec, ONTOLOGY_QUERY); - Model result = getter.getModel(ONTOLOGY_URI); + OntologyRepository repository = new OntologyRepository(app, system, gsc, ONTOLOGY_QUERY); + Graph result = repository.get(ONTOLOGY_URI); - assertSame(fallback, result); + assertTrue(result.contains(NodeFactory.createURI(ONTOLOGY_URI), RDFS.label.asNode(), NodeFactory.createLiteralString("fallback"))); } } From cae83a6da2bf4ca22665a477f483ef9fe36fbb88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martynas=20Jusevi=C4=8Dius?= Date: Thu, 18 Jun 2026 19:17:17 +0200 Subject: [PATCH 03/14] Fix OntologyFilter: flatten owl:imports closure manually, not via ontapi union-graph repository MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OntModelFactory.createModel(graph, spec, repository) registers graphs in an OntUnionGraphRepository keyed by ontology ID, which collided with the same graph already in our flat PrefixGraphRepository cache (OntJenaException: 'Another graph with name <...#> is already in the hierarchy', thrown at runtime on the first /ns request). Replace it with explicit owl:imports closure traversal (loadClosure) that unions the base + imports into one graph, runs RDFS inference over that, and materializes — so createModel is only ever called WITHOUT a repository (no union-hierarchy registration). Encapsulates the duplicated OntologyFilter/ClearOntology load logic into OntologyFilter.loadOntology. OntologyImportsCharacterizationTest now exercises loadOntology and asserts the materialized closure. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../resource/admin/ClearOntology.java | 17 +---- .../server/filter/request/OntologyFilter.java | 68 ++++++++++++------- .../OntologyImportsCharacterizationTest.java | 60 ++++++---------- 3 files changed, 64 insertions(+), 81 deletions(-) diff --git a/src/main/java/com/atomgraph/linkeddatahub/resource/admin/ClearOntology.java b/src/main/java/com/atomgraph/linkeddatahub/resource/admin/ClearOntology.java index 5470d5ef97..75ab778a90 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/resource/admin/ClearOntology.java +++ b/src/main/java/com/atomgraph/linkeddatahub/resource/admin/ClearOntology.java @@ -30,11 +30,6 @@ import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.UriBuilder; -import org.apache.jena.ontapi.OntModelFactory; -import org.apache.jena.ontapi.OntSpecification; -import org.apache.jena.ontapi.model.OntModel; -import org.apache.jena.graph.Graph; -import java.util.HashSet; import com.atomgraph.linkeddatahub.server.filter.request.OntologyFilter; import org.apache.jena.rdf.model.Model; import org.apache.jena.rdf.model.ModelFactory; @@ -119,17 +114,7 @@ public Response post(@FormParam("uri") String ontologyURI, @HeaderParam("Referer } // !!! we need to reload the ontology model before returning a response, to make sure the next request already gets the new version !!! - // same logic as in OntologyFilter. TO-DO: encapsulate? - if (log.isDebugEnabled()) log.debug("Started loading ontology with URI '{}' from the admin dataset", ontologyURI); - Graph baseGraph = repository.get(ontologyURI); - OntModel inferred = OntModelFactory.createModel(baseGraph, OntSpecification.OWL2_DL_MEM_RDFS_INF, repository); - // materialize inferences to avoid invoking the rules engine on every request - OntModel materialized = OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM); - materialized.add(inferred); - repository.put(ontologyURI, materialized.getGraph()); - // cache imported graphs under their document URIs too - OntologyFilter.importClosure(inferred, new HashSet<>()).forEach(importURI -> OntologyFilter.addDocumentModel(repository, importURI)); - if (log.isDebugEnabled()) log.debug("Finished loading ontology with URI '{}' from the admin dataset", ontologyURI); + OntologyFilter.loadOntology(repository, ontologyURI); } if (referer != null) return Response.seeOther(referer).build(); diff --git a/src/main/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyFilter.java b/src/main/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyFilter.java index a09b31bd07..cbf43d9e8e 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyFilter.java +++ b/src/main/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyFilter.java @@ -32,11 +32,11 @@ import jakarta.ws.rs.container.ContainerRequestContext; import jakarta.ws.rs.container.ContainerRequestFilter; import jakarta.ws.rs.container.PreMatching; -import org.apache.jena.graph.Graph; import org.apache.jena.ontapi.OntModelFactory; import org.apache.jena.ontapi.OntSpecification; -import org.apache.jena.ontapi.model.OntID; import org.apache.jena.ontapi.model.OntModel; +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.ModelFactory; import org.apache.jena.vocabulary.OWL; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -144,38 +144,54 @@ public OntModel getOntology(Application app, String uri) final PrefixGraphRepository repository = app.canAs(EndUserApplication.class) ? getSystem().getRepository(app.as(EndUserApplication.class)) : getSystem().getRepository(); - // only build the inferred model if the ontology is not already cached - if (!repository.isCached(uri)) - { - if (log.isDebugEnabled()) log.debug("Started loading ontology with URI '{}'", uri); - Graph baseGraph = repository.get(uri); // end-user: SPARQL-first; otherwise bundled mapping / HTTP - OntModel inferred = OntModelFactory.createModel(baseGraph, OntSpecification.OWL2_DL_MEM_RDFS_INF, repository); - // materialize inferences to avoid invoking the rules engine on every request - OntModel materialized = OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM); - materialized.add(inferred); - repository.put(uri, materialized.getGraph()); - // cache imported graphs under their (fragment-stripped) document URIs too - importClosure(inferred, new HashSet<>()).forEach(importURI -> addDocumentModel(repository, importURI)); - if (log.isDebugEnabled()) log.debug("Finished loading ontology with URI '{}'", uri); - } + // only build the materialized model if the ontology is not already cached + if (!repository.isCached(uri)) loadOntology(repository, uri); + + return OntModelFactory.createModel(repository.get(uri), OntSpecification.OWL2_DL_MEM); + } - return OntModelFactory.createModel(repository.get(uri), OntSpecification.OWL2_DL_MEM, repository); + /** + * Builds and caches the materialized ontology model. Assembles the owl:imports closure into a single + * graph (so ontapi never manages a union-graph hierarchy over the shared repository), applies RDFS + * inference over the flattened closure, and materializes the inferences into the repository cache so + * the rules engine is not invoked on every request. + * + * @param repository graph repository + * @param uri ontology URI + */ + public static void loadOntology(PrefixGraphRepository repository, String uri) + { + if (log.isDebugEnabled()) log.debug("Started loading ontology with URI '{}'", uri); + Model union = ModelFactory.createDefaultModel(); + Set closure = new HashSet<>(); + loadClosure(repository, uri, union, closure); + OntModel inferred = OntModelFactory.createModel(union.getGraph(), OntSpecification.OWL2_DL_MEM_RDFS_INF); + OntModel materialized = OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM); + materialized.add(inferred); + repository.put(uri, materialized.getGraph()); + // cache imported graphs under their fragment-stripped document URIs too + closure.stream().filter(closureURI -> !closureURI.equals(uri)).forEach(importURI -> addDocumentModel(repository, importURI)); + if (log.isDebugEnabled()) log.debug("Finished loading ontology with URI '{}'", uri); } /** - * Collects the transitive owl:imports closure URIs of the given ontology model. + * Recursively loads the transitive owl:imports closure of an ontology into a single union model, + * fetching each graph via the repository (SPARQL-first / bundled mappings). * - * @param model ontology model - * @param seen accumulator of already-visited import URIs - * @return the import closure URIs + * @param repository graph repository + * @param uri ontology URI + * @param union accumulator model + * @param seen accumulator of visited URIs (prevents cycles) */ - public static Set importClosure(OntModel model, Set seen) + public static void loadClosure(PrefixGraphRepository repository, String uri, Model union, Set seen) { - model.imports().forEach(imp -> imp.id().map(OntID::getURI).ifPresent(importURI -> + if (!seen.add(uri)) return; + Model model = ModelFactory.createModelForGraph(repository.get(uri)); + union.add(model); + model.listObjectsOfProperty(OWL.imports).toList().forEach(imp -> { - if (seen.add(importURI)) importClosure(imp, seen); - })); - return seen; + if (imp.isURIResource()) loadClosure(repository, imp.asResource().getURI(), union, seen); + }); } /** diff --git a/src/test/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyImportsCharacterizationTest.java b/src/test/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyImportsCharacterizationTest.java index 3009ef1693..ecfe40af08 100644 --- a/src/test/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyImportsCharacterizationTest.java +++ b/src/test/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyImportsCharacterizationTest.java @@ -16,9 +16,7 @@ */ package com.atomgraph.linkeddatahub.server.filter.request; -import org.apache.jena.ontology.OntDocumentManager; -import org.apache.jena.ontology.OntModel; -import org.apache.jena.ontology.OntModelSpec; +import com.atomgraph.core.util.jena.PrefixGraphRepository; import org.apache.jena.rdf.model.Model; import org.apache.jena.rdf.model.ModelFactory; import org.apache.jena.rdf.model.Resource; @@ -30,13 +28,9 @@ import static org.junit.jupiter.api.Assertions.*; /** - * Oracle for the ontology-load mechanism used by {@link OntologyFilter}: build an - * OWL_MEM_RDFS_INF model over a base ontology that owl:imports another, then materialize - * the inferences into a plain OWL_MEM model that is cached. - * - * Pins (a) the owl:imports transitive closure, (b) RDFS inference, and (c) that the - * materialized model retains inferred triples but carries no reasoner — the three - * behaviors the {@code GraphRepository}/{@code OntSpecification} migration must reproduce. + * Pins {@link OntologyFilter#loadOntology}: it flattens the owl:imports closure into one graph, + * applies RDFS inference, and materializes the inferences into the repository cache — without ontapi + * managing a union-graph hierarchy over the shared repository (which collides on duplicate ontology IDs). * * @author Martynas Jusevičius {@literal } */ @@ -48,49 +42,37 @@ public class OntologyImportsCharacterizationTest private static final String NS = "http://example.org/ns#"; @Test - public void testImportClosureRdfsInferenceAndMaterialization() + public void testLoadOntologyFlattensClosureWithMaterializedRDFSInference() { - OntModelSpec spec = new OntModelSpec(OntModelSpec.OWL_MEM_RDFS_INF); - OntDocumentManager odm = new OntDocumentManager(); - spec.setDocumentManager(odm); + PrefixGraphRepository repository = new PrefixGraphRepository(null); - // imported ontology: B rdfs:subClassOf A; individual x a B Resource a = ResourceFactory.createResource(NS + "A"); Resource b = ResourceFactory.createResource(NS + "B"); Resource x = ResourceFactory.createResource(NS + "x"); + + // imported ontology: B rdfs:subClassOf A; individual x a B Model imported = ModelFactory.createDefaultModel(); imported.add(imported.createResource(IMPORT_URI), RDF.type, OWL.Ontology); imported.add(b, RDFS.subClassOf, a); imported.add(x, RDF.type, b); - odm.addModel(IMPORT_URI, imported); + repository.put(IMPORT_URI, imported.getGraph()); // base ontology owl:imports the imported one Model base = ModelFactory.createDefaultModel(); - base.add(base.createResource(BASE_URI), RDF.type, OWL.Ontology); - base.add(base.createResource(BASE_URI), OWL.imports, base.createResource(IMPORT_URI)); - - OntModel ontModel = ModelFactory.createOntologyModel(spec, base); + Resource baseOnt = base.createResource(BASE_URI); + base.add(baseOnt, RDF.type, OWL.Ontology); + base.add(baseOnt, OWL.imports, base.createResource(IMPORT_URI)); + repository.put(BASE_URI, base.getGraph()); - // (a) transitive import closure includes the imported ontology - assertTrue(ontModel.listImportedOntologyURIs(true).contains(IMPORT_URI), "import closure should contain the imported ontology URI"); - // imported asserted triple is visible through the union - assertTrue(ontModel.contains(b, RDFS.subClassOf, a), "imported terms should be visible"); - // (b) RDFS inference: x a A is entailed from (x a B) + (B subClassOf A) - assertTrue(ontModel.contains(x, RDF.type, a), "RDFS reasoner should infer x a A"); + OntologyFilter.loadOntology(repository, BASE_URI); - // (c) materialize into a plain OWL_MEM model (no inference) - OntModel materialized = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM); - materialized.add(ontModel); - // inferred triple is now asserted in the materialized copy - assertTrue(materialized.contains(x, RDF.type, a), "materialized model retains the inferred triple"); - // and the materialized model carries no reasoner - assertNull(materialized.getSpecification().getReasonerFactory(), "OWL_MEM materialized model must have no reasoner factory"); - // proof it is plain: a fresh entailment is NOT auto-derived - Resource c = ResourceFactory.createResource(NS + "C"); - Resource y = ResourceFactory.createResource(NS + "y"); - materialized.add(c, RDFS.subClassOf, b); - materialized.add(y, RDF.type, c); - assertFalse(materialized.contains(y, RDF.type, a), "no reasoner: y a A must not be inferred"); + Model result = ModelFactory.createModelForGraph(repository.get(BASE_URI)); + // (a) imported terms flattened into the cached graph + assertTrue(result.contains(b, RDFS.subClassOf, a), "imported terms should be flattened in"); + // (b) RDFS inference materialized as a concrete triple: x a A + assertTrue(result.contains(x, RDF.type, a), "RDFS-inferred 'x a A' should be materialized in the cached graph"); + // (c) the import is also cached under its (fragment-stripped) document URI + assertTrue(repository.isCached(IMPORT_URI), "import should remain cached"); } } From 50480c8722a2bd6de3ff8ce7e49f580995e87816 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martynas=20Jusevi=C4=8Dius?= Date: Thu, 18 Jun 2026 23:05:22 +0200 Subject: [PATCH 04/14] Fix empty constructed response: use OWL1_FULL_MEM so rdfs:Class is recognized GET /ns?forClass=...#Item returned an empty graph: dh:Item is declared 'a rdfs:Class' (dh.ttl), and the ontapi OWL2_DL_MEM profile I'd used does not recognize rdfs:Class as an OntClass, so Namespace's getOntClass(uri) returned null and no SPIN constructor ran. Legacy OntModelSpec.OWL_MEM is OWL 1 Full (recognizes rdfs:Class); the correct ontapi mapping is OWL1_FULL_MEM, not OWL2_DL_MEM. Applied across OntologyFilter pipeline + all vocab holders. OntologyImportsCharacterizationTest now guards getOntClass(rdfs:Class) != null. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../server/filter/request/OntologyFilter.java | 6 +++--- .../com/atomgraph/linkeddatahub/vocabulary/ACL.java | 2 +- .../atomgraph/linkeddatahub/vocabulary/Admin.java | 2 +- .../com/atomgraph/linkeddatahub/vocabulary/Cert.java | 2 +- .../com/atomgraph/linkeddatahub/vocabulary/DH.java | 2 +- .../atomgraph/linkeddatahub/vocabulary/Default.java | 2 +- .../com/atomgraph/linkeddatahub/vocabulary/FOAF.java | 2 +- .../atomgraph/linkeddatahub/vocabulary/Google.java | 2 +- .../com/atomgraph/linkeddatahub/vocabulary/LACL.java | 2 +- .../com/atomgraph/linkeddatahub/vocabulary/LAPP.java | 2 +- .../com/atomgraph/linkeddatahub/vocabulary/LDH.java | 2 +- .../com/atomgraph/linkeddatahub/vocabulary/LDHC.java | 2 +- .../com/atomgraph/linkeddatahub/vocabulary/LDHT.java | 2 +- .../com/atomgraph/linkeddatahub/vocabulary/NFO.java | 2 +- .../atomgraph/linkeddatahub/vocabulary/ORCID.java | 2 +- .../com/atomgraph/linkeddatahub/vocabulary/PROV.java | 2 +- .../com/atomgraph/linkeddatahub/vocabulary/SIOC.java | 2 +- .../com/atomgraph/linkeddatahub/vocabulary/VoID.java | 2 +- .../java/com/atomgraph/server/vocabulary/HTTP.java | 2 +- .../java/com/atomgraph/server/vocabulary/LDT.java | 2 +- .../request/OntologyImportsCharacterizationTest.java | 12 +++++++++++- .../vocabulary/VocabularyHolderTest.java | 2 +- 22 files changed, 34 insertions(+), 24 deletions(-) diff --git a/src/main/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyFilter.java b/src/main/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyFilter.java index cbf43d9e8e..6b7e9fa788 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyFilter.java +++ b/src/main/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyFilter.java @@ -147,7 +147,7 @@ public OntModel getOntology(Application app, String uri) // only build the materialized model if the ontology is not already cached if (!repository.isCached(uri)) loadOntology(repository, uri); - return OntModelFactory.createModel(repository.get(uri), OntSpecification.OWL2_DL_MEM); + return OntModelFactory.createModel(repository.get(uri), OntSpecification.OWL1_FULL_MEM); } /** @@ -165,8 +165,8 @@ public static void loadOntology(PrefixGraphRepository repository, String uri) Model union = ModelFactory.createDefaultModel(); Set closure = new HashSet<>(); loadClosure(repository, uri, union, closure); - OntModel inferred = OntModelFactory.createModel(union.getGraph(), OntSpecification.OWL2_DL_MEM_RDFS_INF); - OntModel materialized = OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM); + OntModel inferred = OntModelFactory.createModel(union.getGraph(), OntSpecification.OWL1_FULL_MEM_RDFS_INF); + OntModel materialized = OntModelFactory.createModel(OntSpecification.OWL1_FULL_MEM); materialized.add(inferred); repository.put(uri, materialized.getGraph()); // cache imported graphs under their fragment-stripped document URIs too diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/ACL.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/ACL.java index ab8afcc1c0..d7197ac61c 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/ACL.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/ACL.java @@ -36,7 +36,7 @@ public class ACL org.apache.jena.sys.JenaSystem.init(); // ensure Jena (RDFS vocab) is initialized before ontapi touches it } /** The RDF model that holds the vocabulary terms */ - private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL1_FULL_MEM); /** The namespace of the vocabulary as a string */ public static final String NS = "http://www.w3.org/ns/auth/acl#"; diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/Admin.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/Admin.java index 8f8901c666..2ff7cb2223 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/Admin.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/Admin.java @@ -36,7 +36,7 @@ public class Admin } /** The RDF model that holds the vocabulary terms */ - private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL1_FULL_MEM); /** The namespace of the vocabulary as a string */ public static final String NS = "https://w3id.org/atomgraph/linkeddatahub/admin#"; diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/Cert.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/Cert.java index 655a2582d9..d27f9d8b6f 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/Cert.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/Cert.java @@ -37,7 +37,7 @@ public class Cert org.apache.jena.sys.JenaSystem.init(); // ensure Jena (RDFS vocab) is initialized before ontapi touches it } /** The RDF model that holds the vocabulary terms */ - private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL1_FULL_MEM); /** The namespace of the vocabulary as a string */ public static final String NS = "http://www.w3.org/ns/auth/cert#"; diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/DH.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/DH.java index 30e4b486ff..f59a8c2517 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/DH.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/DH.java @@ -36,7 +36,7 @@ public class DH } /**

The RDF model that holds the vocabulary terms

*/ - private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL1_FULL_MEM); /**

The namespace of the vocabulary as a string

*/ public static final String NS = "https://www.w3.org/ns/ldt/document-hierarchy#"; diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/Default.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/Default.java index 41182134b3..0cf4da59f8 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/Default.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/Default.java @@ -35,7 +35,7 @@ public class Default org.apache.jena.sys.JenaSystem.init(); // ensure Jena (RDFS vocab) is initialized before ontapi touches it } /** The RDF model that holds the vocabulary terms */ - private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL1_FULL_MEM); /** The namespace of the vocabulary as a string */ public static final String NS = "https://w3id.org/atomgraph/linkeddatahub/default#"; diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/FOAF.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/FOAF.java index b816439510..67607e16a2 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/FOAF.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/FOAF.java @@ -36,7 +36,7 @@ public class FOAF org.apache.jena.sys.JenaSystem.init(); // ensure Jena (RDFS vocab) is initialized before ontapi touches it } /** The RDF model that holds the vocabulary terms */ - private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL1_FULL_MEM); /** The namespace of the vocabulary as a string */ public static final String NS = "http://xmlns.com/foaf/0.1/"; diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/Google.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/Google.java index a1ed9a6c64..07726a01e2 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/Google.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/Google.java @@ -21,7 +21,7 @@ public class Google } /** The RDF model that holds the vocabulary terms */ - private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL1_FULL_MEM); /** The namespace of the vocabulary as a string */ public static final String NS = "https://w3id.org/atomgraph/linkeddatahub/services/google#"; diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LACL.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LACL.java index 5ced24b0c2..7a54880e17 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LACL.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LACL.java @@ -36,7 +36,7 @@ public class LACL org.apache.jena.sys.JenaSystem.init(); // ensure Jena (RDFS vocab) is initialized before ontapi touches it } /** The RDF model that holds the vocabulary terms */ - private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL1_FULL_MEM); /** The namespace of the vocabulary as a string */ public static final String NS = "https://w3id.org/atomgraph/linkeddatahub/admin/acl#"; diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LAPP.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LAPP.java index 13daf790cd..9ff8b6d81a 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LAPP.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LAPP.java @@ -37,7 +37,7 @@ public class LAPP } /** The RDF model that holds the vocabulary terms */ - private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL1_FULL_MEM); /** The namespace of the vocabulary as a string */ public static final String NS = "https://w3id.org/atomgraph/linkeddatahub/apps#"; diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LDH.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LDH.java index dff4f44de3..2e3b89a85c 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LDH.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LDH.java @@ -36,7 +36,7 @@ public class LDH org.apache.jena.sys.JenaSystem.init(); // ensure Jena (RDFS vocab) is initialized before ontapi touches it } /** The RDF model that holds the vocabulary terms */ - private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL1_FULL_MEM); /** The namespace of the vocabulary as a string */ public static final String NS = "https://w3id.org/atomgraph/linkeddatahub#"; diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LDHC.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LDHC.java index 7c972af205..0d48df8c81 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LDHC.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LDHC.java @@ -36,7 +36,7 @@ public class LDHC org.apache.jena.sys.JenaSystem.init(); // ensure Jena (RDFS vocab) is initialized before ontapi touches it } /** The RDF model that holds the vocabulary terms */ - private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL1_FULL_MEM); /** The namespace of the vocabulary as a string */ public static final String NS = "https://w3id.org/atomgraph/linkeddatahub/config#"; diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LDHT.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LDHT.java index 7741c9e8d3..54058c063b 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LDHT.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LDHT.java @@ -37,7 +37,7 @@ public class LDHT org.apache.jena.sys.JenaSystem.init(); // ensure Jena (RDFS vocab) is initialized before ontapi touches it } /** The RDF model that holds the vocabulary terms */ - private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL1_FULL_MEM); /** The namespace of the vocabulary as a string */ public static final String NS = "https://w3id.org/atomgraph/linkeddatahub/templates#"; diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/NFO.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/NFO.java index 5c5e79d738..bbdc8a21b0 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/NFO.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/NFO.java @@ -36,7 +36,7 @@ public class NFO org.apache.jena.sys.JenaSystem.init(); // ensure Jena (RDFS vocab) is initialized before ontapi touches it } /** The RDF model that holds the vocabulary terms */ - private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL1_FULL_MEM); /** The namespace of the vocabulary as a string */ public static final String NS = "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#"; diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/ORCID.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/ORCID.java index 42c21d60d9..9b6786a257 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/ORCID.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/ORCID.java @@ -21,7 +21,7 @@ public class ORCID } /** The RDF model that holds the vocabulary terms */ - private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL1_FULL_MEM); /** The namespace of the vocabulary as a string */ public static final String NS = "https://w3id.org/atomgraph/linkeddatahub/services/orcid#"; diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/PROV.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/PROV.java index e536478aa2..2934134988 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/PROV.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/PROV.java @@ -36,7 +36,7 @@ public class PROV { } /** The RDF model that holds the vocabulary terms */ - private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL1_FULL_MEM); /** The namespace of the vocabulary as a string */ public static final String NS = "http://www.w3.org/ns/prov#"; diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/SIOC.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/SIOC.java index 4a19b93cc4..db061d92a0 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/SIOC.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/SIOC.java @@ -40,7 +40,7 @@ public class SIOC { * The ontology model that holds the vocabulary terms *

*/ - private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL1_FULL_MEM); /** *

diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/VoID.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/VoID.java index f2f787b797..c8d52e3067 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/VoID.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/VoID.java @@ -36,7 +36,7 @@ public class VoID { } /** The RDF model that holds the vocabulary terms */ - private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL1_FULL_MEM); /** The namespace of the vocabulary as a string */ public static final String NS = "http://rdfs.org/ns/void#"; diff --git a/src/main/java/com/atomgraph/server/vocabulary/HTTP.java b/src/main/java/com/atomgraph/server/vocabulary/HTTP.java index 77213e1cfb..0cd17cee33 100644 --- a/src/main/java/com/atomgraph/server/vocabulary/HTTP.java +++ b/src/main/java/com/atomgraph/server/vocabulary/HTTP.java @@ -38,7 +38,7 @@ public class HTTP } /**

The RDF model that holds the vocabulary terms

*/ - private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL1_FULL_MEM); /**

The namespace of the vocabulary as a string

*/ public static final String NS = "http://www.w3.org/2011/http#"; diff --git a/src/main/java/com/atomgraph/server/vocabulary/LDT.java b/src/main/java/com/atomgraph/server/vocabulary/LDT.java index b48ad91fef..0b07c859db 100644 --- a/src/main/java/com/atomgraph/server/vocabulary/LDT.java +++ b/src/main/java/com/atomgraph/server/vocabulary/LDT.java @@ -36,7 +36,7 @@ public final class LDT org.apache.jena.sys.JenaSystem.init(); // ensure Jena (RDFS vocab) is initialized before ontapi touches it } /**

The RDF model that holds the vocabulary terms

*/ - private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL1_FULL_MEM); /**

The namespace of the vocabulary as a string

*/ public static final String NS = "https://www.w3.org/ns/ldt#"; diff --git a/src/test/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyImportsCharacterizationTest.java b/src/test/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyImportsCharacterizationTest.java index ecfe40af08..a4321e573d 100644 --- a/src/test/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyImportsCharacterizationTest.java +++ b/src/test/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyImportsCharacterizationTest.java @@ -17,6 +17,9 @@ package com.atomgraph.linkeddatahub.server.filter.request; import com.atomgraph.core.util.jena.PrefixGraphRepository; +import org.apache.jena.ontapi.OntModelFactory; +import org.apache.jena.ontapi.OntSpecification; +import org.apache.jena.ontapi.model.OntModel; import org.apache.jena.rdf.model.Model; import org.apache.jena.rdf.model.ModelFactory; import org.apache.jena.rdf.model.Resource; @@ -50,9 +53,11 @@ public void testLoadOntologyFlattensClosureWithMaterializedRDFSInference() Resource b = ResourceFactory.createResource(NS + "B"); Resource x = ResourceFactory.createResource(NS + "x"); - // imported ontology: B rdfs:subClassOf A; individual x a B + // imported ontology: A, B declared as rdfs:Class (as in dh.ttl, NOT owl:Class); B rdfs:subClassOf A; individual x a B Model imported = ModelFactory.createDefaultModel(); imported.add(imported.createResource(IMPORT_URI), RDF.type, OWL.Ontology); + imported.add(a, RDF.type, RDFS.Class); + imported.add(b, RDF.type, RDFS.Class); imported.add(b, RDFS.subClassOf, a); imported.add(x, RDF.type, b); repository.put(IMPORT_URI, imported.getGraph()); @@ -73,6 +78,11 @@ public void testLoadOntologyFlattensClosureWithMaterializedRDFSInference() assertTrue(result.contains(x, RDF.type, a), "RDFS-inferred 'x a A' should be materialized in the cached graph"); // (c) the import is also cached under its (fragment-stripped) document URI assertTrue(repository.isCached(IMPORT_URI), "import should remain cached"); + // (d) REGRESSION GUARD: an rdfs:Class must be recognized as an OntClass by the model the pipeline returns. + // Legacy OntModelSpec.OWL_MEM is OWL 1 Full and recognizes rdfs:Class; ontapi's OWL2_* profiles do NOT — so + // the earlier OWL2_DL_MEM made GET /ns?forClass=...#Item return an empty graph (getOntClass -> null -> no SPIN construct). + OntModel ontology = OntModelFactory.createModel(repository.get(BASE_URI), OntSpecification.OWL1_FULL_MEM); + assertNotNull(ontology.getOntClass(NS + "B"), "rdfs:Class must be recognized as an OntClass (OWL1_FULL_MEM == legacy OWL_MEM)"); } } diff --git a/src/test/java/com/atomgraph/linkeddatahub/vocabulary/VocabularyHolderTest.java b/src/test/java/com/atomgraph/linkeddatahub/vocabulary/VocabularyHolderTest.java index c416b712bb..1bdf967335 100644 --- a/src/test/java/com/atomgraph/linkeddatahub/vocabulary/VocabularyHolderTest.java +++ b/src/test/java/com/atomgraph/linkeddatahub/vocabulary/VocabularyHolderTest.java @@ -28,7 +28,7 @@ * All ~19 vocabulary classes follow the identical pattern — a static OntModel built with * {@code ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM, null)} on which * {@code createClass} / {@code createDatatypeProperty} mint typed terms. Phase A swaps the - * factory to {@code OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM)} and renames + * factory to {@code OntModelFactory.createModel(OntSpecification.OWL1_FULL_MEM)} and renames * {@code createClass}→{@code createOntClass}, {@code createDatatypeProperty}→{@code createDataProperty}. * * Asserted via the migration-stable {@link Resource} interface: the term URIs and the From 88fff4a51f2b036ee9425f4cbf206109c172980b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martynas=20Jusevi=C4=8Dius?= Date: Fri, 19 Jun 2026 10:58:45 +0200 Subject: [PATCH 05/14] Use twirl 1.2.0-SNAPSHOT; drop redundant SPIN personality registration twirl 1.2.0 re-bases the constraint model onto its own SPIN personality, so the ontapi ontology model can be passed straight to SPINConstraints.check; LDH no longer needs to register SPIN into the global personality. - twirl 1.1.0 -> 1.2.0-SNAPSHOT - Remove SP.init(BuiltinPersonalities.model) from Application (Web-Client Constructor reads sp:text directly; nothing else needed the global registration) - Add SPINConstraintValidationTest: guards the Validator path and the raw-ontapi-via-twirl-rebase path Co-Authored-By: Claude Opus 4.8 (1M context) --- pom.xml | 2 +- .../atomgraph/linkeddatahub/Application.java | 2 - .../util/SPINConstraintValidationTest.java | 98 +++++++++++++++++++ 3 files changed, 99 insertions(+), 3 deletions(-) create mode 100644 src/test/java/com/atomgraph/linkeddatahub/server/util/SPINConstraintValidationTest.java diff --git a/pom.xml b/pom.xml index fc68de8c63..2f436e4f87 100644 --- a/pom.xml +++ b/pom.xml @@ -146,7 +146,7 @@ ${project.groupId} twirl - 1.1.0 + 1.2.0-SNAPSHOT diff --git a/src/main/java/com/atomgraph/linkeddatahub/Application.java b/src/main/java/com/atomgraph/linkeddatahub/Application.java index 46bc8f8dc0..ba3efb8b56 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/Application.java +++ b/src/main/java/com/atomgraph/linkeddatahub/Application.java @@ -175,7 +175,6 @@ import org.slf4j.LoggerFactory; import com.atomgraph.server.mapper.SHACLConstraintViolationExceptionMapper; import com.atomgraph.server.mapper.SPINConstraintViolationExceptionMapper; -import com.atomgraph.spinrdf.vocabulary.SP; import com.apicatalog.jsonld.JsonLdError; import com.apicatalog.jsonld.JsonLdOptions; import java.io.FileOutputStream; @@ -744,7 +743,6 @@ public Application(final ServletConfig servletConfig, final MediaTypes mediaType throw new IllegalStateException(new CertificateException("Secretary certificate with alias " + secretaryCertAlias + " not a valid WebID sertificate (SNA URI is missing)")); } - SP.init(BuiltinPersonalities.model); BuiltinPersonalities.model.add(Authorization.class, AuthorizationImpl.factory); BuiltinPersonalities.model.add(Agent.class, AgentImpl.factory); BuiltinPersonalities.model.add(UserAccount.class, UserAccountImpl.factory); diff --git a/src/test/java/com/atomgraph/linkeddatahub/server/util/SPINConstraintValidationTest.java b/src/test/java/com/atomgraph/linkeddatahub/server/util/SPINConstraintValidationTest.java new file mode 100644 index 0000000000..ac274f4d55 --- /dev/null +++ b/src/test/java/com/atomgraph/linkeddatahub/server/util/SPINConstraintValidationTest.java @@ -0,0 +1,98 @@ +/** + * Copyright 2026 Martynas Jusevičius + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package com.atomgraph.linkeddatahub.server.util; + +import com.atomgraph.server.util.Validator; +import com.atomgraph.spinrdf.constraints.ConstraintViolation; +import com.atomgraph.spinrdf.constraints.SPINConstraints; +import java.util.List; +import org.apache.jena.ontapi.OntModelFactory; +import org.apache.jena.ontapi.OntSpecification; +import org.apache.jena.ontapi.model.OntModel; +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.riot.RDFDataMgr; +import org.apache.jena.vocabulary.RDF; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +/** + * Regression guard for SPIN constraint validation under the ontapi migration. + * + * {@code SPINConstraints} resolves a constraint through Jena enhanced-node polymorphism + * ({@code canAs(Query)}/{@code canAs(TemplateCall)}), which an ontapi {@link OntModel}'s profile-aware personality + * does not carry — so before the fix, validation silently fired nothing. twirl now re-bases the constraint model + * onto its SPIN personality internally (twirl {@code SPINConstraints.check}), so {@link Validator} can hand it the + * ontapi model directly. This pins that the real {@code dh:Item spin:constraint :MissingTitle} template constraint + * flags a {@code dh:Item} that lacks {@code dct:title}. + * + * @author Martynas Jusevičius {@literal } + */ +public class SPINConstraintValidationTest +{ + + static + { + org.apache.jena.sys.JenaSystem.init(); + } + + private static final String DH = "https://www.w3.org/ns/ldt/document-hierarchy#"; + + /** The full owl:imports closure the ontology pipeline assembles for the document-hierarchy ontology. */ + private OntModel loadOntology() + { + Model closure = ModelFactory.createDefaultModel(); + for (String classpath : new String[] { + "etc/sp.ttl", "etc/spl.spin.ttl", "etc/spin.ttl", + "com/atomgraph/linkeddatahub/dh.ttl", + "com/atomgraph/linkeddatahub/def.ttl", + "com/atomgraph/linkeddatahub/ldh.ttl" }) + RDFDataMgr.read(closure, classpath); + + // mirror OntologyFilter.loadOntology: RDFS-infer then materialize into a plain OWL1_FULL_MEM graph + OntModel inferred = OntModelFactory.createModel(closure.getGraph(), OntSpecification.OWL1_FULL_MEM_RDFS_INF); + OntModel materialized = OntModelFactory.createModel(OntSpecification.OWL1_FULL_MEM); + materialized.add(inferred); + return materialized; + } + + /** A dh:Item with NO dct:title — violates the MissingTitle constraint. */ + private Model violatingItem() + { + Model data = ModelFactory.createDefaultModel(); + data.createResource("http://example.org/doc").addProperty(RDF.type, data.createResource(DH + "Item")); + return data; + } + + @Test + public void testMissingTitleConstraintFiresThroughValidator() + { + List violations = new Validator(loadOntology()).validate(violatingItem()); + assertFalse(violations.isEmpty(), "MissingTitle SPIN constraint must fire on a dh:Item without dct:title"); + } + + @Test + public void testRawOntApiModelFiresViaTwirlRebase() + { + // The ontapi OntModel's profile-aware personality lacks the SPIN views, so canAs(...) is false on it. + // twirl's SPINConstraints.check re-bases the constraint model onto its own SPIN personality, so passing the + // ontapi model straight in still fires. Guards against twirl regressing that re-base. + List direct = SPINConstraints.check(violatingItem(), loadOntology()); + assertFalse(direct.isEmpty(), "SPINConstraints.check must fire on a raw ontapi OntModel (twirl re-bases internally)"); + } + +} From 11b9d210e2ca7634ca0b500ed4cf7be4cae46811 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martynas=20Jusevi=C4=8Dius?= Date: Fri, 19 Jun 2026 12:32:54 +0200 Subject: [PATCH 06/14] ci: re-run against deployed twirl/core snapshots From ae0c69ebda65bb285c65fd7d67843e180144bb00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martynas=20Jusevi=C4=8Dius?= Date: Fri, 19 Jun 2026 14:26:34 +0200 Subject: [PATCH 07/14] ci: re-run against deployed twirl/core snapshots From 4a2e7d3594069f3347c5fe9ed749034436fef0b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martynas=20Jusevi=C4=8Dius?= Date: Fri, 19 Jun 2026 15:07:42 +0200 Subject: [PATCH 08/14] Normalize ontologies to owl:Class; use OWL2_FULL_MEM profile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The document/LDT/ACL ontology classes were declared bare rdfs:Class, which no OWL2 ontapi profile recognizes as an OntClass — forcing OWL1_FULL_MEM (which in turn bans named individuals, breaking SD). Declare them owl:Class (dual with rdfs:Class) and switch all OntSpecification usages from OWL1_FULL_MEM to OWL2_FULL_MEM, so getOntClass/forClass works and named individuals are allowed. - dh.ttl/ldt.ttl/lacl.ttl: class declarations now 'a rdfs:Class, owl:Class' - vocab holders + OntologyFilter + ConstructForClass: OWL1_FULL_MEM -> OWL2_FULL_MEM - remove dead 'import com.atomgraph.client.locator.PrefixMapper' (deleted in Web-Client) - OntologyImportsCharacterizationTest: assert owl:Class (not rdfs:Class) is recognized Co-Authored-By: Claude Opus 4.8 (1M context) --- .../com/atomgraph/linkeddatahub/Application.java | 1 - .../server/filter/request/OntologyFilter.java | 6 +++--- .../atomgraph/linkeddatahub/vocabulary/ACL.java | 2 +- .../linkeddatahub/vocabulary/Admin.java | 2 +- .../atomgraph/linkeddatahub/vocabulary/Cert.java | 2 +- .../atomgraph/linkeddatahub/vocabulary/DH.java | 2 +- .../linkeddatahub/vocabulary/Default.java | 2 +- .../atomgraph/linkeddatahub/vocabulary/FOAF.java | 2 +- .../linkeddatahub/vocabulary/Google.java | 2 +- .../atomgraph/linkeddatahub/vocabulary/LACL.java | 2 +- .../atomgraph/linkeddatahub/vocabulary/LAPP.java | 2 +- .../atomgraph/linkeddatahub/vocabulary/LDH.java | 2 +- .../atomgraph/linkeddatahub/vocabulary/LDHC.java | 2 +- .../atomgraph/linkeddatahub/vocabulary/LDHT.java | 2 +- .../atomgraph/linkeddatahub/vocabulary/NFO.java | 2 +- .../linkeddatahub/vocabulary/ORCID.java | 2 +- .../atomgraph/linkeddatahub/vocabulary/PROV.java | 2 +- .../atomgraph/linkeddatahub/vocabulary/SIOC.java | 2 +- .../atomgraph/linkeddatahub/vocabulary/VoID.java | 2 +- .../com/atomgraph/server/vocabulary/HTTP.java | 2 +- .../com/atomgraph/server/vocabulary/LDT.java | 2 +- .../atomgraph/linkeddatahub/app/admin/lacl.ttl | 4 ++-- .../resources/com/atomgraph/linkeddatahub/dh.ttl | 6 +++--- .../com/atomgraph/linkeddatahub/ldt.ttl | 14 +++++++------- .../OntologyImportsCharacterizationTest.java | 16 ++++++++-------- .../util/SPINConstraintValidationTest.java | 6 +++--- .../vocabulary/VocabularyHolderTest.java | 2 +- 27 files changed, 46 insertions(+), 47 deletions(-) diff --git a/src/main/java/com/atomgraph/linkeddatahub/Application.java b/src/main/java/com/atomgraph/linkeddatahub/Application.java index ba3efb8b56..f294aea127 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/Application.java +++ b/src/main/java/com/atomgraph/linkeddatahub/Application.java @@ -34,7 +34,6 @@ import com.atomgraph.linkeddatahub.server.mapper.ForbiddenExceptionMapper; import com.atomgraph.linkeddatahub.server.mapper.auth.webid.WebIDCertificateExceptionMapper; import com.atomgraph.client.MediaTypes; -import com.atomgraph.client.locator.PrefixMapper; import jakarta.annotation.PostConstruct; import jakarta.servlet.ServletConfig; import jakarta.ws.rs.core.Context; diff --git a/src/main/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyFilter.java b/src/main/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyFilter.java index 6b7e9fa788..ea9befb2b6 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyFilter.java +++ b/src/main/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyFilter.java @@ -147,7 +147,7 @@ public OntModel getOntology(Application app, String uri) // only build the materialized model if the ontology is not already cached if (!repository.isCached(uri)) loadOntology(repository, uri); - return OntModelFactory.createModel(repository.get(uri), OntSpecification.OWL1_FULL_MEM); + return OntModelFactory.createModel(repository.get(uri), OntSpecification.OWL2_FULL_MEM); } /** @@ -165,8 +165,8 @@ public static void loadOntology(PrefixGraphRepository repository, String uri) Model union = ModelFactory.createDefaultModel(); Set closure = new HashSet<>(); loadClosure(repository, uri, union, closure); - OntModel inferred = OntModelFactory.createModel(union.getGraph(), OntSpecification.OWL1_FULL_MEM_RDFS_INF); - OntModel materialized = OntModelFactory.createModel(OntSpecification.OWL1_FULL_MEM); + OntModel inferred = OntModelFactory.createModel(union.getGraph(), OntSpecification.OWL2_FULL_MEM_RDFS_INF); + OntModel materialized = OntModelFactory.createModel(OntSpecification.OWL2_FULL_MEM); materialized.add(inferred); repository.put(uri, materialized.getGraph()); // cache imported graphs under their fragment-stripped document URIs too diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/ACL.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/ACL.java index d7197ac61c..c31bca2089 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/ACL.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/ACL.java @@ -36,7 +36,7 @@ public class ACL org.apache.jena.sys.JenaSystem.init(); // ensure Jena (RDFS vocab) is initialized before ontapi touches it } /** The RDF model that holds the vocabulary terms */ - private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL1_FULL_MEM); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_FULL_MEM); /** The namespace of the vocabulary as a string */ public static final String NS = "http://www.w3.org/ns/auth/acl#"; diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/Admin.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/Admin.java index 2ff7cb2223..8c463d9ca6 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/Admin.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/Admin.java @@ -36,7 +36,7 @@ public class Admin } /** The RDF model that holds the vocabulary terms */ - private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL1_FULL_MEM); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_FULL_MEM); /** The namespace of the vocabulary as a string */ public static final String NS = "https://w3id.org/atomgraph/linkeddatahub/admin#"; diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/Cert.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/Cert.java index d27f9d8b6f..ad869bae69 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/Cert.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/Cert.java @@ -37,7 +37,7 @@ public class Cert org.apache.jena.sys.JenaSystem.init(); // ensure Jena (RDFS vocab) is initialized before ontapi touches it } /** The RDF model that holds the vocabulary terms */ - private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL1_FULL_MEM); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_FULL_MEM); /** The namespace of the vocabulary as a string */ public static final String NS = "http://www.w3.org/ns/auth/cert#"; diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/DH.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/DH.java index f59a8c2517..140b2e511e 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/DH.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/DH.java @@ -36,7 +36,7 @@ public class DH } /**

The RDF model that holds the vocabulary terms

*/ - private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL1_FULL_MEM); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_FULL_MEM); /**

The namespace of the vocabulary as a string

*/ public static final String NS = "https://www.w3.org/ns/ldt/document-hierarchy#"; diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/Default.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/Default.java index 0cf4da59f8..47ba21532c 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/Default.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/Default.java @@ -35,7 +35,7 @@ public class Default org.apache.jena.sys.JenaSystem.init(); // ensure Jena (RDFS vocab) is initialized before ontapi touches it } /** The RDF model that holds the vocabulary terms */ - private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL1_FULL_MEM); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_FULL_MEM); /** The namespace of the vocabulary as a string */ public static final String NS = "https://w3id.org/atomgraph/linkeddatahub/default#"; diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/FOAF.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/FOAF.java index 67607e16a2..c2bfcc475e 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/FOAF.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/FOAF.java @@ -36,7 +36,7 @@ public class FOAF org.apache.jena.sys.JenaSystem.init(); // ensure Jena (RDFS vocab) is initialized before ontapi touches it } /** The RDF model that holds the vocabulary terms */ - private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL1_FULL_MEM); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_FULL_MEM); /** The namespace of the vocabulary as a string */ public static final String NS = "http://xmlns.com/foaf/0.1/"; diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/Google.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/Google.java index 07726a01e2..6a21142b4e 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/Google.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/Google.java @@ -21,7 +21,7 @@ public class Google } /** The RDF model that holds the vocabulary terms */ - private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL1_FULL_MEM); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_FULL_MEM); /** The namespace of the vocabulary as a string */ public static final String NS = "https://w3id.org/atomgraph/linkeddatahub/services/google#"; diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LACL.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LACL.java index 7a54880e17..bf4123de90 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LACL.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LACL.java @@ -36,7 +36,7 @@ public class LACL org.apache.jena.sys.JenaSystem.init(); // ensure Jena (RDFS vocab) is initialized before ontapi touches it } /** The RDF model that holds the vocabulary terms */ - private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL1_FULL_MEM); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_FULL_MEM); /** The namespace of the vocabulary as a string */ public static final String NS = "https://w3id.org/atomgraph/linkeddatahub/admin/acl#"; diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LAPP.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LAPP.java index 9ff8b6d81a..9e991e04fe 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LAPP.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LAPP.java @@ -37,7 +37,7 @@ public class LAPP } /** The RDF model that holds the vocabulary terms */ - private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL1_FULL_MEM); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_FULL_MEM); /** The namespace of the vocabulary as a string */ public static final String NS = "https://w3id.org/atomgraph/linkeddatahub/apps#"; diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LDH.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LDH.java index 2e3b89a85c..a55c1fbce7 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LDH.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LDH.java @@ -36,7 +36,7 @@ public class LDH org.apache.jena.sys.JenaSystem.init(); // ensure Jena (RDFS vocab) is initialized before ontapi touches it } /** The RDF model that holds the vocabulary terms */ - private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL1_FULL_MEM); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_FULL_MEM); /** The namespace of the vocabulary as a string */ public static final String NS = "https://w3id.org/atomgraph/linkeddatahub#"; diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LDHC.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LDHC.java index 0d48df8c81..e808098ae1 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LDHC.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LDHC.java @@ -36,7 +36,7 @@ public class LDHC org.apache.jena.sys.JenaSystem.init(); // ensure Jena (RDFS vocab) is initialized before ontapi touches it } /** The RDF model that holds the vocabulary terms */ - private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL1_FULL_MEM); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_FULL_MEM); /** The namespace of the vocabulary as a string */ public static final String NS = "https://w3id.org/atomgraph/linkeddatahub/config#"; diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LDHT.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LDHT.java index 54058c063b..e2fdfc4182 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LDHT.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/LDHT.java @@ -37,7 +37,7 @@ public class LDHT org.apache.jena.sys.JenaSystem.init(); // ensure Jena (RDFS vocab) is initialized before ontapi touches it } /** The RDF model that holds the vocabulary terms */ - private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL1_FULL_MEM); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_FULL_MEM); /** The namespace of the vocabulary as a string */ public static final String NS = "https://w3id.org/atomgraph/linkeddatahub/templates#"; diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/NFO.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/NFO.java index bbdc8a21b0..d828638b6b 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/NFO.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/NFO.java @@ -36,7 +36,7 @@ public class NFO org.apache.jena.sys.JenaSystem.init(); // ensure Jena (RDFS vocab) is initialized before ontapi touches it } /** The RDF model that holds the vocabulary terms */ - private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL1_FULL_MEM); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_FULL_MEM); /** The namespace of the vocabulary as a string */ public static final String NS = "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#"; diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/ORCID.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/ORCID.java index 9b6786a257..c64923c8e6 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/ORCID.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/ORCID.java @@ -21,7 +21,7 @@ public class ORCID } /** The RDF model that holds the vocabulary terms */ - private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL1_FULL_MEM); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_FULL_MEM); /** The namespace of the vocabulary as a string */ public static final String NS = "https://w3id.org/atomgraph/linkeddatahub/services/orcid#"; diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/PROV.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/PROV.java index 2934134988..fb4655c533 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/PROV.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/PROV.java @@ -36,7 +36,7 @@ public class PROV { } /** The RDF model that holds the vocabulary terms */ - private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL1_FULL_MEM); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_FULL_MEM); /** The namespace of the vocabulary as a string */ public static final String NS = "http://www.w3.org/ns/prov#"; diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/SIOC.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/SIOC.java index db061d92a0..6e2f11f0bc 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/SIOC.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/SIOC.java @@ -40,7 +40,7 @@ public class SIOC { * The ontology model that holds the vocabulary terms *

*/ - private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL1_FULL_MEM); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_FULL_MEM); /** *

diff --git a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/VoID.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/VoID.java index c8d52e3067..12d04d9168 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/vocabulary/VoID.java +++ b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/VoID.java @@ -36,7 +36,7 @@ public class VoID { } /** The RDF model that holds the vocabulary terms */ - private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL1_FULL_MEM); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_FULL_MEM); /** The namespace of the vocabulary as a string */ public static final String NS = "http://rdfs.org/ns/void#"; diff --git a/src/main/java/com/atomgraph/server/vocabulary/HTTP.java b/src/main/java/com/atomgraph/server/vocabulary/HTTP.java index 0cd17cee33..316d6e3b9d 100644 --- a/src/main/java/com/atomgraph/server/vocabulary/HTTP.java +++ b/src/main/java/com/atomgraph/server/vocabulary/HTTP.java @@ -38,7 +38,7 @@ public class HTTP } /**

The RDF model that holds the vocabulary terms

*/ - private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL1_FULL_MEM); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_FULL_MEM); /**

The namespace of the vocabulary as a string

*/ public static final String NS = "http://www.w3.org/2011/http#"; diff --git a/src/main/java/com/atomgraph/server/vocabulary/LDT.java b/src/main/java/com/atomgraph/server/vocabulary/LDT.java index 0b07c859db..28cb52bcf9 100644 --- a/src/main/java/com/atomgraph/server/vocabulary/LDT.java +++ b/src/main/java/com/atomgraph/server/vocabulary/LDT.java @@ -36,7 +36,7 @@ public final class LDT org.apache.jena.sys.JenaSystem.init(); // ensure Jena (RDFS vocab) is initialized before ontapi touches it } /**

The RDF model that holds the vocabulary terms

*/ - private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL1_FULL_MEM); + private static OntModel m_model = OntModelFactory.createModel(OntSpecification.OWL2_FULL_MEM); /**

The namespace of the vocabulary as a string

*/ public static final String NS = "https://www.w3.org/ns/ldt#"; diff --git a/src/main/resources/com/atomgraph/linkeddatahub/app/admin/lacl.ttl b/src/main/resources/com/atomgraph/linkeddatahub/app/admin/lacl.ttl index ae9068ccfd..a45a58977a 100644 --- a/src/main/resources/com/atomgraph/linkeddatahub/app/admin/lacl.ttl +++ b/src/main/resources/com/atomgraph/linkeddatahub/app/admin/lacl.ttl @@ -69,7 +69,7 @@ # authorization requests -:AuthorizationRequest a rdfs:Class ; +:AuthorizationRequest a rdfs:Class, owl:Class ; spin:constructor :AuthorizationRequestConstructor ; spin:constraint :MissingRequestMode, :MissingRequestAgent, :MissingRequestAccessTo ; rdfs:label "Authorization request" ; @@ -108,7 +108,7 @@ WHERE {}""" ; rdfs:isDefinedBy : . -:CreatorAuthorization a rdfs:Class ; +:CreatorAuthorization a rdfs:Class, owl:Class ; rdfs:subClassOf acl:Authorization ; rdfs:label "Creator access" ; rdfs:comment "Creators have full control of their created resources" ; diff --git a/src/main/resources/com/atomgraph/linkeddatahub/dh.ttl b/src/main/resources/com/atomgraph/linkeddatahub/dh.ttl index 2c80455940..a3dae50f24 100644 --- a/src/main/resources/com/atomgraph/linkeddatahub/dh.ttl +++ b/src/main/resources/com/atomgraph/linkeddatahub/dh.ttl @@ -34,21 +34,21 @@ # CLASSES -:Document a rdfs:Class ; +:Document a rdfs:Class, owl:Class ; rdfs:subClassOf foaf:Document ; # ldt:path "{slug}/" ; rdfs:label "Document" ; # spin:constructor :DocumentConstructor ; rdfs:isDefinedBy : . -:Container a rdfs:Class ; +:Container a rdfs:Class, owl:Class ; rdfs:subClassOf :Document, sioc:Container ; # spin:constructor :ContainerConstructor ; rdfs:label "Container" ; rdfs:comment "An area in which content Items are contained" ; rdfs:isDefinedBy : . -:Item a rdfs:Class ; +:Item a rdfs:Class, owl:Class ; rdfs:subClassOf :Document, sioc:Item ; # spin:constructor :ItemConstructor ; rdfs:label "Item" ; diff --git a/src/main/resources/com/atomgraph/linkeddatahub/ldt.ttl b/src/main/resources/com/atomgraph/linkeddatahub/ldt.ttl index 5c847a307a..120859f87f 100644 --- a/src/main/resources/com/atomgraph/linkeddatahub/ldt.ttl +++ b/src/main/resources/com/atomgraph/linkeddatahub/ldt.ttl @@ -155,29 +155,29 @@ # CLASSES -:InheritedProperty a rdfs:Class ; +:InheritedProperty a rdfs:Class, owl:Class ; rdfs:subClassOf owl:AnnotationProperty ; rdfs:label "Inherited property" ; rdfs:comment "Values of this property are inherited by subclasses that do not have this property" ; rdfs:isDefinedBy : . -:Application a rdfs:Class ; +:Application a rdfs:Class, owl:Class ; rdfs:label "Application" ; rdfs:comment "Declarative Linked Data application which structure is defined by its ontology and data is access via its SPARQL service" ; rdfs:isDefinedBy : . -:Ontology a rdfs:Class ; +:Ontology a rdfs:Class, owl:Class ; rdfs:subClassOf owl:Ontology ; rdfs:label "Ontology" ; rdfs:comment "Ontology annotated with Linked Data Templates" ; rdfs:isDefinedBy : . -:Template a rdfs:Class ; +:Template a rdfs:Class, owl:Class ; rdfs:label "Resource template" ; rdfs:comment "A class of RDF resources that share the same URI template and SPARQL query template" ; rdfs:isDefinedBy : . -:Query a rdfs:Class ; +:Query a rdfs:Class, owl:Class ; rdfs:subClassOf sp:Query, [ a owl:Class ; owl:unionOf (sp:Describe sp:Construct) ; rdfs:label "Graph query forms" @@ -185,12 +185,12 @@ rdfs:label "Query" ; rdfs:isDefinedBy : . -:Update a rdfs:Class ; +:Update a rdfs:Class, owl:Class ; rdfs:subClassOf sp:Update ; rdfs:label "Update" ; rdfs:isDefinedBy : . -:Parameter a rdfs:Class ; +:Parameter a rdfs:Class, owl:Class ; rdfs:subClassOf spl:Argument ; rdfs:label "Parameter" ; rdfs:comment "Represents a query parameter that has predicate, value type, default value etc." ; diff --git a/src/test/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyImportsCharacterizationTest.java b/src/test/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyImportsCharacterizationTest.java index a4321e573d..14afcb0cf6 100644 --- a/src/test/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyImportsCharacterizationTest.java +++ b/src/test/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyImportsCharacterizationTest.java @@ -53,11 +53,11 @@ public void testLoadOntologyFlattensClosureWithMaterializedRDFSInference() Resource b = ResourceFactory.createResource(NS + "B"); Resource x = ResourceFactory.createResource(NS + "x"); - // imported ontology: A, B declared as rdfs:Class (as in dh.ttl, NOT owl:Class); B rdfs:subClassOf A; individual x a B + // imported ontology: A, B declared as owl:Class (as in dh.ttl since the OWL2 normalization); B rdfs:subClassOf A; individual x a B Model imported = ModelFactory.createDefaultModel(); imported.add(imported.createResource(IMPORT_URI), RDF.type, OWL.Ontology); - imported.add(a, RDF.type, RDFS.Class); - imported.add(b, RDF.type, RDFS.Class); + imported.add(a, RDF.type, OWL.Class); + imported.add(b, RDF.type, OWL.Class); imported.add(b, RDFS.subClassOf, a); imported.add(x, RDF.type, b); repository.put(IMPORT_URI, imported.getGraph()); @@ -78,11 +78,11 @@ public void testLoadOntologyFlattensClosureWithMaterializedRDFSInference() assertTrue(result.contains(x, RDF.type, a), "RDFS-inferred 'x a A' should be materialized in the cached graph"); // (c) the import is also cached under its (fragment-stripped) document URI assertTrue(repository.isCached(IMPORT_URI), "import should remain cached"); - // (d) REGRESSION GUARD: an rdfs:Class must be recognized as an OntClass by the model the pipeline returns. - // Legacy OntModelSpec.OWL_MEM is OWL 1 Full and recognizes rdfs:Class; ontapi's OWL2_* profiles do NOT — so - // the earlier OWL2_DL_MEM made GET /ns?forClass=...#Item return an empty graph (getOntClass -> null -> no SPIN construct). - OntModel ontology = OntModelFactory.createModel(repository.get(BASE_URI), OntSpecification.OWL1_FULL_MEM); - assertNotNull(ontology.getOntClass(NS + "B"), "rdfs:Class must be recognized as an OntClass (OWL1_FULL_MEM == legacy OWL_MEM)"); + // (d) REGRESSION GUARD: an owl:Class must be recognized as an OntClass by the model the pipeline returns, so + // GET /ns?forClass=...#Item resolves the class and runs its SPIN constructor. The ontologies are normalized to + // owl:Class so OWL2_FULL_MEM recognizes them (bare rdfs:Class is NOT recognized by any OWL2 profile). + OntModel ontology = OntModelFactory.createModel(repository.get(BASE_URI), OntSpecification.OWL2_FULL_MEM); + assertNotNull(ontology.getOntClass(NS + "B"), "owl:Class must be recognized as an OntClass under OWL2_FULL_MEM"); } } diff --git a/src/test/java/com/atomgraph/linkeddatahub/server/util/SPINConstraintValidationTest.java b/src/test/java/com/atomgraph/linkeddatahub/server/util/SPINConstraintValidationTest.java index ac274f4d55..6d2062f9c1 100644 --- a/src/test/java/com/atomgraph/linkeddatahub/server/util/SPINConstraintValidationTest.java +++ b/src/test/java/com/atomgraph/linkeddatahub/server/util/SPINConstraintValidationTest.java @@ -63,9 +63,9 @@ private OntModel loadOntology() "com/atomgraph/linkeddatahub/ldh.ttl" }) RDFDataMgr.read(closure, classpath); - // mirror OntologyFilter.loadOntology: RDFS-infer then materialize into a plain OWL1_FULL_MEM graph - OntModel inferred = OntModelFactory.createModel(closure.getGraph(), OntSpecification.OWL1_FULL_MEM_RDFS_INF); - OntModel materialized = OntModelFactory.createModel(OntSpecification.OWL1_FULL_MEM); + // mirror OntologyFilter.loadOntology: RDFS-infer then materialize into a plain OWL2_FULL_MEM graph + OntModel inferred = OntModelFactory.createModel(closure.getGraph(), OntSpecification.OWL2_FULL_MEM_RDFS_INF); + OntModel materialized = OntModelFactory.createModel(OntSpecification.OWL2_FULL_MEM); materialized.add(inferred); return materialized; } diff --git a/src/test/java/com/atomgraph/linkeddatahub/vocabulary/VocabularyHolderTest.java b/src/test/java/com/atomgraph/linkeddatahub/vocabulary/VocabularyHolderTest.java index 1bdf967335..95f10ddc6d 100644 --- a/src/test/java/com/atomgraph/linkeddatahub/vocabulary/VocabularyHolderTest.java +++ b/src/test/java/com/atomgraph/linkeddatahub/vocabulary/VocabularyHolderTest.java @@ -28,7 +28,7 @@ * All ~19 vocabulary classes follow the identical pattern — a static OntModel built with * {@code ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM, null)} on which * {@code createClass} / {@code createDatatypeProperty} mint typed terms. Phase A swaps the - * factory to {@code OntModelFactory.createModel(OntSpecification.OWL1_FULL_MEM)} and renames + * factory to {@code OntModelFactory.createModel(OntSpecification.OWL2_FULL_MEM)} and renames * {@code createClass}→{@code createOntClass}, {@code createDatatypeProperty}→{@code createDataProperty}. * * Asserted via the migration-stable {@link Resource} interface: the term URIs and the From 4e52c177f2237f4e58501ab61e5cfb8b097728b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martynas=20Jusevi=C4=8Dius?= Date: Fri, 19 Jun 2026 21:14:56 +0200 Subject: [PATCH 09/14] Add debug output to add-property-constraint and GET-proxied-ontology-ns tests Co-Authored-By: Claude Opus 4.8 (1M context) --- .../admin/model/add-property-constraint.sh | 19 ++++++++++++++----- http-tests/proxy/GET-proxied-ontology-ns.sh | 15 ++++++++++----- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/http-tests/admin/model/add-property-constraint.sh b/http-tests/admin/model/add-property-constraint.sh index c5e179841f..0c5ef907ce 100755 --- a/http-tests/admin/model/add-property-constraint.sh +++ b/http-tests/admin/model/add-property-constraint.sh @@ -22,6 +22,7 @@ add-property-constraint.sh \ --label "New constraint" \ --property "http://rdfs.org/sioc/ns#content" \ "$ontology_doc" +echo "DEBUG: add-property-constraint.sh done" # create a class with the constraint @@ -34,21 +35,28 @@ add-class.sh \ --constraint "$constraint" \ --sub-class-of "https://www.w3.org/ns/ldt/document-hierarchy#Item" \ "$ontology_doc" +echo "DEBUG: add-class.sh done" # clear ontology from memory -clear-ontology.sh \ +clear_output=$(clear-ontology.sh \ -f "$OWNER_CERT_FILE" \ -p "$OWNER_CERT_PWD" \ -b "$ADMIN_BASE_URL" \ - --ontology "$namespace" + --ontology "$namespace" 2>&1) && clear_exit=0 || clear_exit=$? +echo "DEBUG: clear-ontology.sh exit=$clear_exit output=$clear_output" +[ "$clear_exit" -eq 0 ] || exit "$clear_exit" # check that the constraint is present in the ontology -curl -k -f -s -N \ +ns_body=$(curl -k -f -s -N \ -H "Accept: application/n-triples" \ - "$namespace_doc" \ -| grep "$constraint" > /dev/null + "$namespace_doc" 2>&1) && ns_exit=0 || ns_exit=$? +echo "DEBUG: namespace GET exit=$ns_exit" +echo "DEBUG: namespace body (first 500 chars): ${ns_body:0:500}" +[ "$ns_exit" -eq 0 ] || exit "$ns_exit" +echo "$ns_body" | grep "$constraint" > /dev/null +echo "DEBUG: constraint found in namespace doc" # check that creating an instance of the class without sioc:content returns 422 Unprocessable Entity due to missing sioc:content @@ -71,5 +79,6 @@ response=$(echo -e "$turtle" \ "$END_USER_BASE_URL" \ 2>&1) # redirect output from stderr to stdout +echo "DEBUG: PUT response (last 200 chars): ${response: -200}" echo "$response" \ | grep "HTTP/1.1 422" > /dev/null diff --git a/http-tests/proxy/GET-proxied-ontology-ns.sh b/http-tests/proxy/GET-proxied-ontology-ns.sh index c97f48505a..e7ed016fb9 100755 --- a/http-tests/proxy/GET-proxied-ontology-ns.sh +++ b/http-tests/proxy/GET-proxied-ontology-ns.sh @@ -42,11 +42,13 @@ add-class.sh \ # clear the in-memory ontology so the new classes are present on next request -clear-ontology.sh \ +clear_out=$(clear-ontology.sh \ -f "$OWNER_CERT_FILE" \ -p "$OWNER_CERT_PWD" \ -b "$ADMIN_BASE_URL" \ - --ontology "$namespace" + --ontology "$namespace" 2>&1) && clear_exit=0 || clear_exit=$? +echo "DEBUG: clear-ontology.sh exit=$clear_exit output=$clear_out" +[ "$clear_exit" -eq 0 ] || exit "$clear_exit" # request the namespace document URI (without fragment) via ?uri= proxy. # the namespace document is not DataManager-mapped and not a registered app, @@ -58,9 +60,12 @@ response=$(curl -k -f -s \ -E "$AGENT_CERT_FILE":"$AGENT_CERT_PWD" \ -H "Accept: application/n-triples" \ --data-urlencode "uri=${namespace_uri}" \ - "$END_USER_BASE_URL") + "$END_USER_BASE_URL" 2>&1) && proxy_exit=0 || proxy_exit=$? +echo "DEBUG: proxy GET exit=$proxy_exit" +echo "DEBUG: proxy response (first 500 chars): ${response:0:500}" +[ "$proxy_exit" -eq 0 ] || exit "$proxy_exit" # verify both class descriptions are present in the response -echo "$response" | grep -q "$class1" -echo "$response" | grep -q "$class2" +echo "$response" | grep -q "$class1" && echo "DEBUG: class1 found" || { echo "DEBUG: class1 NOT found"; exit 1; } +echo "$response" | grep -q "$class2" && echo "DEBUG: class2 found" || { echo "DEBUG: class2 NOT found"; exit 1; } From 3cb48a41264b391a954adc3ebf2d14d080771dc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martynas=20Jusevi=C4=8Dius?= Date: Fri, 19 Jun 2026 21:58:59 +0200 Subject: [PATCH 10/14] Removed debug output --- .../admin/model/add-property-constraint.sh | 19 +++++-------------- http-tests/proxy/GET-proxied-ontology-ns.sh | 15 +++++---------- 2 files changed, 10 insertions(+), 24 deletions(-) diff --git a/http-tests/admin/model/add-property-constraint.sh b/http-tests/admin/model/add-property-constraint.sh index 0c5ef907ce..c5e179841f 100755 --- a/http-tests/admin/model/add-property-constraint.sh +++ b/http-tests/admin/model/add-property-constraint.sh @@ -22,7 +22,6 @@ add-property-constraint.sh \ --label "New constraint" \ --property "http://rdfs.org/sioc/ns#content" \ "$ontology_doc" -echo "DEBUG: add-property-constraint.sh done" # create a class with the constraint @@ -35,28 +34,21 @@ add-class.sh \ --constraint "$constraint" \ --sub-class-of "https://www.w3.org/ns/ldt/document-hierarchy#Item" \ "$ontology_doc" -echo "DEBUG: add-class.sh done" # clear ontology from memory -clear_output=$(clear-ontology.sh \ +clear-ontology.sh \ -f "$OWNER_CERT_FILE" \ -p "$OWNER_CERT_PWD" \ -b "$ADMIN_BASE_URL" \ - --ontology "$namespace" 2>&1) && clear_exit=0 || clear_exit=$? -echo "DEBUG: clear-ontology.sh exit=$clear_exit output=$clear_output" -[ "$clear_exit" -eq 0 ] || exit "$clear_exit" + --ontology "$namespace" # check that the constraint is present in the ontology -ns_body=$(curl -k -f -s -N \ +curl -k -f -s -N \ -H "Accept: application/n-triples" \ - "$namespace_doc" 2>&1) && ns_exit=0 || ns_exit=$? -echo "DEBUG: namespace GET exit=$ns_exit" -echo "DEBUG: namespace body (first 500 chars): ${ns_body:0:500}" -[ "$ns_exit" -eq 0 ] || exit "$ns_exit" -echo "$ns_body" | grep "$constraint" > /dev/null -echo "DEBUG: constraint found in namespace doc" + "$namespace_doc" \ +| grep "$constraint" > /dev/null # check that creating an instance of the class without sioc:content returns 422 Unprocessable Entity due to missing sioc:content @@ -79,6 +71,5 @@ response=$(echo -e "$turtle" \ "$END_USER_BASE_URL" \ 2>&1) # redirect output from stderr to stdout -echo "DEBUG: PUT response (last 200 chars): ${response: -200}" echo "$response" \ | grep "HTTP/1.1 422" > /dev/null diff --git a/http-tests/proxy/GET-proxied-ontology-ns.sh b/http-tests/proxy/GET-proxied-ontology-ns.sh index e7ed016fb9..c97f48505a 100755 --- a/http-tests/proxy/GET-proxied-ontology-ns.sh +++ b/http-tests/proxy/GET-proxied-ontology-ns.sh @@ -42,13 +42,11 @@ add-class.sh \ # clear the in-memory ontology so the new classes are present on next request -clear_out=$(clear-ontology.sh \ +clear-ontology.sh \ -f "$OWNER_CERT_FILE" \ -p "$OWNER_CERT_PWD" \ -b "$ADMIN_BASE_URL" \ - --ontology "$namespace" 2>&1) && clear_exit=0 || clear_exit=$? -echo "DEBUG: clear-ontology.sh exit=$clear_exit output=$clear_out" -[ "$clear_exit" -eq 0 ] || exit "$clear_exit" + --ontology "$namespace" # request the namespace document URI (without fragment) via ?uri= proxy. # the namespace document is not DataManager-mapped and not a registered app, @@ -60,12 +58,9 @@ response=$(curl -k -f -s \ -E "$AGENT_CERT_FILE":"$AGENT_CERT_PWD" \ -H "Accept: application/n-triples" \ --data-urlencode "uri=${namespace_uri}" \ - "$END_USER_BASE_URL" 2>&1) && proxy_exit=0 || proxy_exit=$? -echo "DEBUG: proxy GET exit=$proxy_exit" -echo "DEBUG: proxy response (first 500 chars): ${response:0:500}" -[ "$proxy_exit" -eq 0 ] || exit "$proxy_exit" + "$END_USER_BASE_URL") # verify both class descriptions are present in the response -echo "$response" | grep -q "$class1" && echo "DEBUG: class1 found" || { echo "DEBUG: class1 NOT found"; exit 1; } -echo "$response" | grep -q "$class2" && echo "DEBUG: class2 found" || { echo "DEBUG: class2 NOT found"; exit 1; } +echo "$response" | grep -q "$class1" +echo "$response" | grep -q "$class2" From ee9b15b1ccbfc61a94a333315ae7ce1e258c8af8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martynas=20Jusevi=C4=8Dius?= Date: Fri, 19 Jun 2026 23:59:12 +0200 Subject: [PATCH 11/14] Tests for RDFS ontology imports --- .../GET-namespace-forClass-rdfs.sh | 22 +++++++++++++++++++ .../OntologyImportsCharacterizationTest.java | 15 ++++++++----- 2 files changed, 31 insertions(+), 6 deletions(-) create mode 100644 http-tests/document-hierarchy/GET-namespace-forClass-rdfs.sh diff --git a/http-tests/document-hierarchy/GET-namespace-forClass-rdfs.sh b/http-tests/document-hierarchy/GET-namespace-forClass-rdfs.sh new file mode 100644 index 0000000000..e363dfbcf4 --- /dev/null +++ b/http-tests/document-hierarchy/GET-namespace-forClass-rdfs.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +set -euo pipefail + +initialize_dataset "$END_USER_BASE_URL" "$TMP_END_USER_DATASET" "$END_USER_ENDPOINT_URL" +initialize_dataset "$ADMIN_BASE_URL" "$TMP_ADMIN_DATASET" "$ADMIN_ENDPOINT_URL" +purge_cache "$END_USER_VARNISH_SERVICE" +purge_cache "$ADMIN_VARNISH_SERVICE" +purge_cache "$FRONTEND_VARNISH_SERVICE" + +# sp:Describe is declared only as rdfs:Class (not owl:Class) in sp.ttl. +# OntologyFilter must promote rdfs:Class to owl:Class during materialization so +# that OWL2 profiles recognise third-party vocab terms and return their SPIN constructors. + +response=$(curl -k -f -s \ + -G \ + -E "$OWNER_CERT_FILE":"$OWNER_CERT_PWD" \ + -H "Accept: application/rdf+xml" \ + --data-urlencode "forClass=http://spinrdf.org/sp#Describe" \ + "${END_USER_BASE_URL}ns") + +# response must be non-empty: sp:Describe must be recognised as an OntClass +echo "$response" | grep -q "http://spinrdf.org/sp#Describe" diff --git a/src/test/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyImportsCharacterizationTest.java b/src/test/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyImportsCharacterizationTest.java index 14afcb0cf6..00e6f0c7ce 100644 --- a/src/test/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyImportsCharacterizationTest.java +++ b/src/test/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyImportsCharacterizationTest.java @@ -53,11 +53,12 @@ public void testLoadOntologyFlattensClosureWithMaterializedRDFSInference() Resource b = ResourceFactory.createResource(NS + "B"); Resource x = ResourceFactory.createResource(NS + "x"); - // imported ontology: A, B declared as owl:Class (as in dh.ttl since the OWL2 normalization); B rdfs:subClassOf A; individual x a B + // imported ontology: A declared as owl:Class only; B declared as rdfs:Class only (mimicking third-party vocabs like sp.ttl); + // B rdfs:subClassOf A; individual x a B Model imported = ModelFactory.createDefaultModel(); imported.add(imported.createResource(IMPORT_URI), RDF.type, OWL.Ontology); imported.add(a, RDF.type, OWL.Class); - imported.add(b, RDF.type, OWL.Class); + imported.add(b, RDF.type, RDFS.Class); imported.add(b, RDFS.subClassOf, a); imported.add(x, RDF.type, b); repository.put(IMPORT_URI, imported.getGraph()); @@ -78,11 +79,13 @@ public void testLoadOntologyFlattensClosureWithMaterializedRDFSInference() assertTrue(result.contains(x, RDF.type, a), "RDFS-inferred 'x a A' should be materialized in the cached graph"); // (c) the import is also cached under its (fragment-stripped) document URI assertTrue(repository.isCached(IMPORT_URI), "import should remain cached"); - // (d) REGRESSION GUARD: an owl:Class must be recognized as an OntClass by the model the pipeline returns, so - // GET /ns?forClass=...#Item resolves the class and runs its SPIN constructor. The ontologies are normalized to - // owl:Class so OWL2_FULL_MEM recognizes them (bare rdfs:Class is NOT recognized by any OWL2 profile). + // (d) REGRESSION GUARD: both owl:Class and rdfs:Class-only terms must be recognized as OntClasses by the returned + // model, so GET /ns?forClass= resolves the class and runs its SPIN constructor. + // OntologyFilter promotes all rdfs:Class subjects to owl:Class so OWL2 profiles (which do not recognize bare + // rdfs:Class) can find third-party vocab terms like sp:Describe. OntModel ontology = OntModelFactory.createModel(repository.get(BASE_URI), OntSpecification.OWL2_FULL_MEM); - assertNotNull(ontology.getOntClass(NS + "B"), "owl:Class must be recognized as an OntClass under OWL2_FULL_MEM"); + assertNotNull(ontology.getOntClass(NS + "A"), "owl:Class term must be recognized as an OntClass under OWL2_FULL_MEM"); + assertNotNull(ontology.getOntClass(NS + "B"), "rdfs:Class-only term must be recognized as an OntClass after promotion"); } } From f27c423e0b384a470bcd614d8c8ae5d7a7996d85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martynas=20Jusevi=C4=8Dius?= Date: Fri, 19 Jun 2026 23:59:37 +0200 Subject: [PATCH 12/14] Promote rdfs:Class to owl:Class during ontology materialization OWL2 profiles do not recognise bare rdfs:Class as OntClass, so third-party vocab terms declared only as rdfs:Class (e.g. sp:Describe in sp.ttl) were invisible to getOntClass() lookups, returning empty forClass constructor responses. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../linkeddatahub/server/filter/request/OntologyFilter.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyFilter.java b/src/main/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyFilter.java index ea9befb2b6..86a49bb048 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyFilter.java +++ b/src/main/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyFilter.java @@ -38,6 +38,8 @@ import org.apache.jena.rdf.model.Model; import org.apache.jena.rdf.model.ModelFactory; import org.apache.jena.vocabulary.OWL; +import org.apache.jena.vocabulary.RDF; +import org.apache.jena.vocabulary.RDFS; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -168,6 +170,8 @@ public static void loadOntology(PrefixGraphRepository repository, String uri) OntModel inferred = OntModelFactory.createModel(union.getGraph(), OntSpecification.OWL2_FULL_MEM_RDFS_INF); OntModel materialized = OntModelFactory.createModel(OntSpecification.OWL2_FULL_MEM); materialized.add(inferred); + // promote rdfs:Class to owl:Class so OWL2 profiles recognise third-party vocab terms (e.g. sp:Describe in sp.ttl) + inferred.listSubjectsWithProperty(RDF.type, RDFS.Class).forEach(r -> materialized.add(r, RDF.type, OWL.Class)); repository.put(uri, materialized.getGraph()); // cache imported graphs under their fragment-stripped document URIs too closure.stream().filter(closureURI -> !closureURI.equals(uri)).forEach(importURI -> addDocumentModel(repository, importURI)); From db8d966a1154bca8c975bb493d6bb683572adb4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martynas=20Jusevi=C4=8Dius?= Date: Sat, 20 Jun 2026 23:42:06 +0200 Subject: [PATCH 13/14] Repoint PrefixGraphRepository import to Web-Client; replace deprecated Model.write with RDFWriter - Update imports to com.atomgraph.client.util.jena.PrefixGraphRepository (moved out of Core): Application, OntologyFilter, OntologyRepository, SourceResolverFactory, SameSiteSourceResolver + tests - XSLTWriterBase: use RDFWriter instead of deprecated Model.write, keeping plain RDF/XML Co-Authored-By: Claude Opus 4.8 (1M context) --- .../java/com/atomgraph/linkeddatahub/Application.java | 2 +- .../server/filter/request/OntologyFilter.java | 2 +- .../linkeddatahub/server/util/OntologyRepository.java | 2 +- .../atomgraph/linkeddatahub/writer/XSLTWriterBase.java | 8 ++++++-- .../writer/factory/SourceResolverFactory.java | 2 +- .../linkeddatahub/writer/impl/SameSiteSourceResolver.java | 2 +- .../server/filter/request/OntologyFilterTest.java | 2 +- .../request/OntologyImportsCharacterizationTest.java | 2 +- 8 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/atomgraph/linkeddatahub/Application.java b/src/main/java/com/atomgraph/linkeddatahub/Application.java index f294aea127..d10215d954 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/Application.java +++ b/src/main/java/com/atomgraph/linkeddatahub/Application.java @@ -16,7 +16,7 @@ */ package com.atomgraph.linkeddatahub; -import com.atomgraph.core.util.jena.PrefixGraphRepository; +import com.atomgraph.client.util.jena.PrefixGraphRepository; import com.atomgraph.client.util.RDFSourceResolver; import com.atomgraph.client.util.StylesheetResolver; import com.atomgraph.linkeddatahub.writer.impl.SameSiteSourceResolver; diff --git a/src/main/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyFilter.java b/src/main/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyFilter.java index 86a49bb048..5312cc2e57 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyFilter.java +++ b/src/main/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyFilter.java @@ -18,7 +18,7 @@ import com.atomgraph.linkeddatahub.apps.model.Application; import com.atomgraph.linkeddatahub.apps.model.EndUserApplication; -import com.atomgraph.core.util.jena.PrefixGraphRepository; +import com.atomgraph.client.util.jena.PrefixGraphRepository; import com.atomgraph.linkeddatahub.vocabulary.LAPP; import com.atomgraph.server.exception.OntologyException; import java.io.IOException; diff --git a/src/main/java/com/atomgraph/linkeddatahub/server/util/OntologyRepository.java b/src/main/java/com/atomgraph/linkeddatahub/server/util/OntologyRepository.java index 2d6044a8e6..14a0ded159 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/server/util/OntologyRepository.java +++ b/src/main/java/com/atomgraph/linkeddatahub/server/util/OntologyRepository.java @@ -18,7 +18,7 @@ import com.atomgraph.client.vocabulary.LDT; import com.atomgraph.core.client.GraphStoreClient; -import com.atomgraph.core.util.jena.PrefixGraphRepository; +import com.atomgraph.client.util.jena.PrefixGraphRepository; import com.atomgraph.linkeddatahub.apps.model.EndUserApplication; import jakarta.ws.rs.core.MultivaluedHashMap; import jakarta.ws.rs.core.MultivaluedMap; diff --git a/src/main/java/com/atomgraph/linkeddatahub/writer/XSLTWriterBase.java b/src/main/java/com/atomgraph/linkeddatahub/writer/XSLTWriterBase.java index 31eb596c4b..bbdaf3442c 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/writer/XSLTWriterBase.java +++ b/src/main/java/com/atomgraph/linkeddatahub/writer/XSLTWriterBase.java @@ -60,7 +60,8 @@ import org.apache.http.HttpHeaders; import org.apache.jena.rdf.model.Property; import org.apache.jena.rdf.model.Model; -import org.apache.jena.riot.RDFLanguages; +import org.apache.jena.riot.RDFFormat; +import org.apache.jena.riot.RDFWriter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -229,7 +230,10 @@ public StreamSource getSource(Model model) throws IOException try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) { - model.write(stream, RDFLanguages.RDFXML.getName(), null); + RDFWriter.create(). + format(RDFFormat.RDFXML_PLAIN). + source(model). + output(stream); return new StreamSource(new ByteArrayInputStream(stream.toByteArray())); } } diff --git a/src/main/java/com/atomgraph/linkeddatahub/writer/factory/SourceResolverFactory.java b/src/main/java/com/atomgraph/linkeddatahub/writer/factory/SourceResolverFactory.java index a405a7ca23..daca9faeb5 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/writer/factory/SourceResolverFactory.java +++ b/src/main/java/com/atomgraph/linkeddatahub/writer/factory/SourceResolverFactory.java @@ -19,7 +19,7 @@ import jakarta.ws.rs.core.Context; import jakarta.ws.rs.ext.Provider; import com.atomgraph.client.util.RDFSourceResolver; -import com.atomgraph.core.util.jena.PrefixGraphRepository; +import com.atomgraph.client.util.jena.PrefixGraphRepository; import com.atomgraph.linkeddatahub.apps.model.Application; import com.atomgraph.linkeddatahub.apps.model.EndUserApplication; import com.atomgraph.linkeddatahub.client.GraphStoreClient; diff --git a/src/main/java/com/atomgraph/linkeddatahub/writer/impl/SameSiteSourceResolver.java b/src/main/java/com/atomgraph/linkeddatahub/writer/impl/SameSiteSourceResolver.java index 3449bd2b3e..72134d5afc 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/writer/impl/SameSiteSourceResolver.java +++ b/src/main/java/com/atomgraph/linkeddatahub/writer/impl/SameSiteSourceResolver.java @@ -18,7 +18,7 @@ import com.atomgraph.client.util.RDFSourceResolver; import com.atomgraph.core.client.GraphStoreClient; -import com.atomgraph.core.util.jena.PrefixGraphRepository; +import com.atomgraph.client.util.jena.PrefixGraphRepository; import com.google.common.net.InternetDomainName; import java.net.URI; import org.slf4j.Logger; diff --git a/src/test/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyFilterTest.java b/src/test/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyFilterTest.java index aa6b7ac80a..9478f75d2d 100644 --- a/src/test/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyFilterTest.java +++ b/src/test/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyFilterTest.java @@ -16,7 +16,7 @@ */ package com.atomgraph.linkeddatahub.server.filter.request; -import com.atomgraph.core.util.jena.PrefixGraphRepository; +import com.atomgraph.client.util.jena.PrefixGraphRepository; import org.apache.jena.graph.Graph; import org.apache.jena.rdf.model.ModelFactory; import org.junit.jupiter.api.Test; diff --git a/src/test/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyImportsCharacterizationTest.java b/src/test/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyImportsCharacterizationTest.java index 00e6f0c7ce..312a614151 100644 --- a/src/test/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyImportsCharacterizationTest.java +++ b/src/test/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyImportsCharacterizationTest.java @@ -16,7 +16,7 @@ */ package com.atomgraph.linkeddatahub.server.filter.request; -import com.atomgraph.core.util.jena.PrefixGraphRepository; +import com.atomgraph.client.util.jena.PrefixGraphRepository; import org.apache.jena.ontapi.OntModelFactory; import org.apache.jena.ontapi.OntSpecification; import org.apache.jena.ontapi.model.OntModel; From 9fc5bb321f532df6bf0e232cc05d7674a0203158 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martynas=20Jusevi=C4=8Dius?= Date: Sun, 21 Jun 2026 11:11:41 +0200 Subject: [PATCH 14/14] SNAPSHOT bump --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 2f436e4f87..f8fb120864 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ com.atomgraph linkeddatahub - 5.5.4-SNAPSHOT + 5.6.0-SNAPSHOT ${packaging.type} AtomGraph LinkedDataHub @@ -163,13 +163,13 @@ ${project.groupId} client - 4.3.1-SNAPSHOT + 4.4.0-SNAPSHOT classes ${project.groupId} client - 4.3.1-SNAPSHOT + 4.4.0-SNAPSHOT war