diff --git a/docker-compose.yml b/docker-compose.yml
index 02b5f52033..5f3522f1dc 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -384,28 +384,24 @@ configs:
return (pass);
}
- if (req.http.Client-Cert) {
- # Authenticated HTML is user-specific → never cache
- if (req.http.Accept ~ "text/html" ||
- req.http.Accept ~ "application/xhtml+xml") {
- return (pass);
- }
-
- # Conditional requests must reach backend for validation
- if (req.http.If-Match || req.http.If-None-Match ||
- req.http.If-Modified-Since || req.http.If-Unmodified-Since) {
- return (pass);
- }
-
- # /access endpoint returns agent-specific group memberships
- if (req.url ~ "^/access") {
- return (pass);
- }
+ # Delegated requests carry user identity in the On-Behalf-Of header.
+ # The backend response echoes that identity in the Link header
+ # (acl#agent). The cache key does not include the asserted identity,
+ # so caching would let a later anonymous request to the same
+ # URL+Accept read back the previous agent's WebID and ACL grant.
+ if (req.http.On-Behalf-Of) {
+ return (pass);
+ }
- # SPARQL referencing /acl/agents/ depends on agent identity → don't cache
- if (req.url ~ "%2Facl%2Fagents%2F") {
- return (pass);
- }
+ # Authenticated responses get acl#agent stamped into the Link header by
+ # the backend, regardless of representation (HTML, RDF/XML, Turtle,
+ # JSON-LD, SPARQL results, …). The URL-keyed cache slot ignores the
+ # asserting identity, so any such response would leak to anonymous
+ # readers of the same URL. /static/* is the only safely-shared path —
+ # it's served by Tomcat's default servlet (web.xml:365-371), bypasses
+ # Jersey, and carries no identity-bearing headers.
+ if (req.http.Client-Cert && req.url !~ "^/static/") {
+ return (pass);
}
if (req.http.Cookie) {
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/http-tests/document-hierarchy/PATCH-blank-node-skolemized.sh b/http-tests/document-hierarchy/PATCH-blank-node-skolemized.sh
new file mode 100755
index 0000000000..fec9f83436
--- /dev/null
+++ b/http-tests/document-hierarchy/PATCH-blank-node-skolemized.sh
@@ -0,0 +1,57 @@
+#!/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"
+
+# add agent to the writers group
+
+add-agent-to-group.sh \
+ -f "$OWNER_CERT_FILE" \
+ -p "$OWNER_CERT_PWD" \
+ --agent "$AGENT_URI" \
+ "${ADMIN_BASE_URL}acl/groups/writers/"
+
+# PATCH the root with an INSERT that introduces a blank node.
+# Use rdf:_99 to avoid colliding with existing rdf:_1..rdf:_8 in the test dataset.
+# Expected: 204 No Content; the blank node is skolemized to a hash URI before persisting.
+
+update=$(cat <
+PREFIX dct:
+
+INSERT
+{
+ <${END_USER_BASE_URL}> rdf:_99 _:bnode0 .
+ _:bnode0 dct:title "Blank node title"
+}
+WHERE
+{}
+EOF
+)
+
+curl -k -w "%{http_code}\n" -o /dev/null -f -s \
+ -E "$AGENT_CERT_FILE":"$AGENT_CERT_PWD" \
+ -X PATCH \
+ -H "Content-Type: application/sparql-update" \
+ "$END_USER_BASE_URL" \
+ --data-binary "$update" \
+| grep -q "$STATUS_NO_CONTENT"
+
+# fetch the persisted graph and verify the blank node was skolemized to a hash URI
+
+response=$(curl -k -f -s -G \
+ -E "$AGENT_CERT_FILE":"$AGENT_CERT_PWD" \
+ -H "Accept: application/n-triples" \
+ "$END_USER_BASE_URL")
+
+rdf_99_line=$(echo "$response" | grep -E "^<${END_USER_BASE_URL}> " || true)
+
+[ -n "$rdf_99_line" ] || exit 1
+
+# object of rdf:_99 must be a URI (<...>), not a blank node label (_:...)
+! echo "$rdf_99_line" | grep -qE '_:[A-Za-z0-9]+ \.$'
+echo "$rdf_99_line" | grep -qE '<\S+> \.$'
diff --git a/http-tests/proxy/GET-proxied-no-cache-poisoning.sh b/http-tests/proxy/GET-proxied-no-cache-poisoning.sh
new file mode 100755
index 0000000000..89337d2383
--- /dev/null
+++ b/http-tests/proxy/GET-proxied-no-cache-poisoning.sh
@@ -0,0 +1,78 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+# Regression: ProxyRequestFilter's server-side fetch attaches On-Behalf-Of
+# (via WebIDDelegationFilter), and the backend response carries the asserted
+# agent's WebID in the Link header (acl#agent). varnish-frontend must not
+# cache that response under a URL-keyed entry — otherwise a subsequent
+# anonymous request to the same URL+Accept replays the cached 200 and reads
+# back the previous agent's identity (and inherits whatever ACL grant they
+# had).
+
+purge_cache "$END_USER_VARNISH_SERVICE"
+purge_cache "$ADMIN_VARNISH_SERVICE"
+purge_cache "$FRONTEND_VARNISH_SERVICE"
+
+# Step A: authenticated owner fires a proxy request from the end-user
+# dataspace to the admin dataspace. This triggers WebIDDelegationFilter →
+# On-Behalf-Of on the server-side hop into varnish-frontend.
+
+curl -k -f -s -o /dev/null \
+ -E "$OWNER_CERT_FILE":"$OWNER_CERT_PWD" \
+ -G \
+ -H 'Accept: application/rdf+xml' \
+ --data-urlencode "uri=${ADMIN_BASE_URL}" \
+ "${END_USER_BASE_URL}"
+
+# Step B: anonymous direct request to the admin URL with the same Accept.
+# If the cache was poisoned in Step A, this returns 200 with the owner's
+# WebID in the Link header. Expected after the fix: varnish-frontend should
+# pass on On-Behalf-Of and store nothing, so this goes to the backend
+# anonymously and gets 403.
+
+response=$(curl -k -s -i -H 'Accept: application/rdf+xml' "${ADMIN_BASE_URL}")
+
+status=$(printf '%s\n' "$response" | awk 'NR==1{print $2}' | tr -d '\r')
+link_leak=$(printf '%s\n' "$response" | tr -d '\r' | grep -i '^link:' | grep -c 'acl#agent' || true)
+
+if [ "$status" != "$STATUS_FORBIDDEN" ]; then
+ echo "Step B: expected $STATUS_FORBIDDEN, got $status"
+ exit 1
+fi
+
+if [ "$link_leak" != "0" ]; then
+ echo "Step B: anonymous response leaks acl#agent (cache poisoning)"
+ exit 1
+fi
+
+# Step C: authenticated owner fetches the admin URL directly with cert at TLS,
+# Accept: application/rdf+xml. The Client-Cert header reaches varnish-frontend
+# (nginx-forwarded). The backend stamps acl#agent into the Link header for the
+# authenticated 200. varnish-frontend must NOT cache this response — its hash
+# key ignores identity, so a subsequent anonymous request would replay the 200.
+
+purge_cache "$FRONTEND_VARNISH_SERVICE"
+
+curl -k -f -s -o /dev/null \
+ -E "$OWNER_CERT_FILE":"$OWNER_CERT_PWD" \
+ -H 'Accept: application/rdf+xml' \
+ "${ADMIN_BASE_URL}"
+
+# Step D: anonymous direct fetch of the same URL. With the fix in place
+# (Client-Cert + non-/static/ path → pass in vcl_recv), Step C didn't store
+# anything, so this reaches the backend anonymously and gets 403.
+
+response=$(curl -k -s -i -H 'Accept: application/rdf+xml' "${ADMIN_BASE_URL}")
+
+status=$(printf '%s\n' "$response" | awk 'NR==1{print $2}' | tr -d '\r')
+link_leak=$(printf '%s\n' "$response" | tr -d '\r' | grep -i '^link:' | grep -c 'acl#agent' || true)
+
+if [ "$status" != "$STATUS_FORBIDDEN" ]; then
+ echo "Step D: expected $STATUS_FORBIDDEN, got $status"
+ exit 1
+fi
+
+if [ "$link_leak" != "0" ]; then
+ echo "Step D: anonymous response leaks acl#agent (cache poisoning)"
+ exit 1
+fi
diff --git a/pom.xml b/pom.xml
index df0f494983..f8fb120864 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,7 +3,7 @@
com.atomgraph
linkeddatahub
- 5.5.3
+ 5.6.0-SNAPSHOT
${packaging.type}
AtomGraph LinkedDataHub
@@ -46,7 +46,7 @@
https://github.com/AtomGraph/LinkedDataHub
scm:git:git://github.com/AtomGraph/LinkedDataHub.git
scm:git:git@github.com:AtomGraph/LinkedDataHub.git
- linkeddatahub-5.5.3
+ linkeddatahub-2.1.1
@@ -146,7 +146,7 @@
${project.groupId}
twirl
- 1.1.0
+ 1.2.0-SNAPSHOT
@@ -163,13 +163,13 @@
${project.groupId}
client
- 4.3.0
+ 4.4.0-SNAPSHOT
classes
${project.groupId}
client
- 4.3.0
+ 4.4.0-SNAPSHOT
war
diff --git a/src/main/java/com/atomgraph/linkeddatahub/Application.java b/src/main/java/com/atomgraph/linkeddatahub/Application.java
index abe64f03d6..d10215d954 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.client.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;
@@ -26,10 +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 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 +41,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 +51,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 +69,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 +136,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 +164,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;
@@ -174,7 +174,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;
@@ -268,8 +267,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 +278,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 +323,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 +434,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 +681,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);
@@ -747,7 +742,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);
@@ -792,14 +786,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 +832,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 +853,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 +923,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 +1002,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 +1011,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 +1736,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 +1993,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..75ab778a90 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,7 @@
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 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 +80,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).
@@ -116,19 +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?
- 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));
- 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/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..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,27 +18,28 @@
import com.atomgraph.linkeddatahub.apps.model.Application;
import com.atomgraph.linkeddatahub.apps.model.EndUserApplication;
+import com.atomgraph.client.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.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.util.FileManager;
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;
@@ -100,7 +101,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 +116,104 @@ 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))
- {
- 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);
- }
- }
- else
+ final PrefixGraphRepository repository = app.canAs(EndUserApplication.class) ?
+ getSystem().getRepository(app.as(EndUserApplication.class)) : getSystem().getRepository();
+
+ // 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_FULL_MEM);
+ }
+
+ /**
+ * 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_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));
+ if (log.isDebugEnabled()) log.debug("Finished loading ontology with URI '{}'", uri);
+ }
+
+ /**
+ * 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 repository graph repository
+ * @param uri ontology URI
+ * @param union accumulator model
+ * @param seen accumulator of visited URIs (prevents cycles)
+ */
+ public static void loadClosure(PrefixGraphRepository repository, String uri, Model union, Set seen)
+ {
+ if (!seen.add(uri)) return;
+ Model model = ModelFactory.createModelForGraph(repository.get(uri));
+ union.add(model);
+ model.listObjectsOfProperty(OWL.imports).toList().forEach(imp ->
{
- 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 (imp.isURIResource()) loadClosure(repository, imp.asResource().getURI(), union, 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 e42e43a937..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;
@@ -38,8 +38,8 @@
import java.util.List;
import java.util.Optional;
import java.util.Set;
+import org.apache.jena.query.ParameterizedSparqlString;
import org.apache.jena.query.QueryExecution;
-import org.apache.jena.query.QueryFactory;
import jakarta.annotation.Priority;
import jakarta.inject.Inject;
import jakarta.ws.rs.NotAllowedException;
@@ -50,6 +50,7 @@
import jakarta.ws.rs.client.WebTarget;
import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.ws.rs.container.ContainerRequestFilter;
+import jakarta.ws.rs.HttpMethod;
import jakarta.ws.rs.container.PreMatching;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.EntityTag;
@@ -132,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;
@@ -177,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;
}
@@ -191,9 +192,10 @@ public void filter(ContainerRequestContext requestContext) throws IOException
// ?term where STR(?term) starts with "#")
if (isSafeMethod && getOntology().isPresent())
{
- String describeQueryStr = "DESCRIBE <" + targetURI + "> ?term " +
- "WHERE { ?term ?p ?o FILTER(STRSTARTS(STR(?term), CONCAT(STR(<" + targetURI + ">), \"#\"))) }";
- try (QueryExecution qe = QueryExecution.create(QueryFactory.create(describeQueryStr), getOntology().get().getOntModel()))
+ 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()))
{
Model description = qe.execDescribe();
if (!description.isEmpty())
@@ -240,7 +242,7 @@ else if (agentContext instanceof IDTokenSecurityContext idTokenSecurityContext)
try (clientResponse)
{
- requestContext.abortWith(getResponse(clientResponse, targetURI));
+ requestContext.abortWith(getResponse(clientResponse, targetURI, requestContext.getMethod()));
}
}
catch (MessageBodyProviderNotFoundException ex)
@@ -274,9 +276,7 @@ protected Optional resolveTargetURI(ContainerRequestContext requestContext)
if (proxyTarget != null) return Optional.of(proxyTarget);
// Case 2: lapp:Dataset proxy
- @SuppressWarnings("unchecked")
- Optional datasetOpt =
- (Optional) requestContext.getProperty(LAPP.Dataset.getURI());
+ Optional datasetOpt = (Optional) requestContext.getProperty(LAPP.Dataset.getURI());
if (datasetOpt != null && datasetOpt.isPresent())
{
URI proxied = datasetOpt.get().getProxied(requestContext.getUriInfo().getAbsolutePath());
@@ -320,10 +320,23 @@ protected Optional resolveTargetURI(ContainerRequestContext requestContext)
*
* @param clientResponse response from the proxy target
* @param targetURI upstream URI (used as the parse base URI hint for {@code ModelProvider})
+ * @param method HTTP method
* @return JAX-RS response to return to the original caller
*/
- protected Response getResponse(Response clientResponse, URI targetURI)
+ protected Response getResponse(Response clientResponse, URI targetURI, String method)
{
+ // HEAD responses have no body by HTTP semantics. Routing them through
+ // the typed branches below would parse an empty entity into an empty
+ // Model/ResultSet, then re-stamp ETag/Last-Modified off that empty
+ // value — producing validators that disagree with the upstream GET.
+ // Forward the upstream headers (including ETag) verbatim instead.
+ if (HttpMethod.HEAD.equalsIgnoreCase(method))
+ {
+ Response.ResponseBuilder rb = Response.status(clientResponse.getStatus());
+ if (clientResponse.getMediaType() != null) rb.type(clientResponse.getMediaType());
+ return overlayHeaders(rb.build(), clientResponse, true);
+ }
+
if (clientResponse.getMediaType() == null)
{
Response.ResponseBuilder rb = Response.status(clientResponse.getStatus());
@@ -473,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..14a0ded159 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.client.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/vocabulary/ACL.java b/src/main/java/com/atomgraph/linkeddatahub/vocabulary/ACL.java
index 259d34a0ed..c31bca2089 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_FULL_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..8c463d9ca6 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_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 c0906f3767..ad869bae69 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_FULL_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..140b2e511e 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_FULL_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..47ba21532c 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_FULL_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..c2bfcc475e 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_FULL_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..6a21142b4e 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_FULL_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..bf4123de90 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_FULL_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..9e991e04fe 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_FULL_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..a55c1fbce7 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_FULL_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..e808098ae1 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_FULL_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..e2fdfc4182 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_FULL_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..d828638b6b 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_FULL_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..c64923c8e6 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_FULL_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..fb4655c533 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_FULL_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..6e2f11f0bc 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_FULL_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..12d04d9168 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_FULL_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/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..bbdaf3442c 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,10 +58,10 @@
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.apache.jena.riot.RDFFormat;
+import org.apache.jena.riot.RDFWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -88,7 +88,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 +97,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 +188,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;
@@ -231,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()));
}
}
@@ -257,36 +259,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..daca9faeb5 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.client.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..72134d5afc 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.client.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/main/java/com/atomgraph/server/vocabulary/HTTP.java b/src/main/java/com/atomgraph/server/vocabulary/HTTP.java
index e9316e1eec..316d6e3b9d 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_FULL_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..28cb52bcf9 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_FULL_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/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/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/client/graph3d.xsl b/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/client/graph3d.xsl
index bbfff9e59a..3ccab50832 100644
--- a/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/client/graph3d.xsl
+++ b/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/client/graph3d.xsl
@@ -122,6 +122,8 @@ WHERE
'graph-state': $graph-state
}" as="map(*)"/>
+
+
+
+
+
+
@@ -381,6 +387,8 @@ WHERE
+
+
diff --git a/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/client.xsl b/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/client.xsl
index ecf8eff43d..ddf605df99 100644
--- a/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/client.xsl
+++ b/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/client.xsl
@@ -559,8 +559,10 @@ WHERE
+
+
@@ -718,6 +720,7 @@ WHERE
+
diff --git a/src/test/java/com/atomgraph/linkeddatahub/imports/stream/StreamRDFOutputWriterTest.java b/src/test/java/com/atomgraph/linkeddatahub/imports/stream/StreamRDFOutputWriterTest.java
new file mode 100644
index 0000000000..0e3f2b6cb1
--- /dev/null
+++ b/src/test/java/com/atomgraph/linkeddatahub/imports/stream/StreamRDFOutputWriterTest.java
@@ -0,0 +1,100 @@
+/**
+ * Copyright 2025 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.imports.stream;
+
+import com.atomgraph.linkeddatahub.client.GraphStoreClient;
+import com.atomgraph.linkeddatahub.model.Service;
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import jakarta.ws.rs.BadRequestException;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import org.apache.jena.query.Query;
+import org.apache.jena.query.QueryFactory;
+import org.junit.jupiter.api.BeforeEach;
+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.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * Unit tests for {@link StreamRDFOutputWriter}.
+ *
+ * @author Martynas Jusevičius {@literal }
+ */
+@ExtendWith(MockitoExtension.class)
+public class StreamRDFOutputWriterTest
+{
+
+ @Mock private Service service;
+ @Mock private Service adminService;
+ @Mock private com.atomgraph.linkeddatahub.Application system;
+ @Mock private GraphStoreClient gsc;
+
+ private static final String BASE_URI = "http://localhost/";
+ private static final String GRAPH_URI = "http://localhost/graphs/import";
+
+ private Query query;
+ private StreamRDFOutputWriter writer;
+
+ @BeforeEach
+ public void setUp()
+ {
+ query = QueryFactory.create("CONSTRUCT WHERE { ?s ?p ?o }");
+ writer = new StreamRDFOutputWriter(service, adminService, system, gsc, BASE_URI, query, GRAPH_URI);
+ }
+
+ @Test
+ public void testGettersRoundTrip()
+ {
+ assertSame(service, writer.getService());
+ assertSame(adminService, writer.getAdminService());
+ assertSame(system, writer.getSystem());
+ assertSame(gsc, writer.getGraphStoreClient());
+ assertEquals(BASE_URI, writer.getBaseURI());
+ assertSame(query, writer.getQuery());
+ assertEquals(GRAPH_URI, writer.getGraphURI());
+ }
+
+ @Test
+ public void testApplyNullResponse()
+ {
+ assertThrows(IllegalArgumentException.class, () -> writer.apply(null));
+ }
+
+ /**
+ * A response whose {@code Content-Type} does not map to an RDF language must be rejected with
+ * {@code 400 Bad Request} — after the body has been buffered to a temp file but before any
+ * Graph Store write is attempted.
+ */
+ @Test
+ public void testApplyNonRdfMediaTypeThrowsBadRequest()
+ {
+ Response response = mock(Response.class);
+ lenient().when(response.readEntity(InputStream.class)).thenReturn((InputStream)new ByteArrayInputStream("not rdf".getBytes()));
+ when(response.getMediaType()).thenReturn(MediaType.valueOf("image/png"));
+
+ assertThrows(BadRequestException.class, () -> writer.apply(response));
+ }
+
+}
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..9478f75d2d
--- /dev/null
+++ b/src/test/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyFilterTest.java
@@ -0,0 +1,68 @@
+/**
+ * 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 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;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * 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 }
+ */
+public class OntologyFilterTest
+{
+
+ /** An import URI with a fragment is also cached under its fragment-stripped document URI. */
+ @Test
+ public void testAddDocumentModelCachesUnderStrippedDocumentURI()
+ {
+ PrefixGraphRepository repository = new PrefixGraphRepository(null);
+ String importURI = "http://example.org/onto#";
+ String docURI = "http://example.org/onto";
+
+ Graph imported = ModelFactory.createDefaultModel().getGraph();
+ repository.put(importURI, imported);
+
+ OntologyFilter.addDocumentModel(repository, importURI);
+
+ assertTrue(repository.isCached(docURI), "import graph should be cached under the document URI");
+ assertSame(imported, repository.get(docURI));
+ }
+
+ /** If the document URI is mapped to a different location, the secondary cache write is skipped. */
+ @Test
+ public void testAddDocumentModelSkipsWhenDocumentURIMapped()
+ {
+ PrefixGraphRepository repository = new PrefixGraphRepository(null);
+ String importURI = "http://example.org/mapped#";
+ String docURI = "http://example.org/mapped";
+
+ repository.addLocationMapping(docURI, "file:elsewhere.ttl"); // resolve(docURI) != docURI -> skip
+ repository.put(importURI, ModelFactory.createDefaultModel().getGraph());
+
+ OntologyFilter.addDocumentModel(repository, importURI);
+
+ 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/filter/request/OntologyImportsCharacterizationTest.java b/src/test/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyImportsCharacterizationTest.java
new file mode 100644
index 0000000000..312a614151
--- /dev/null
+++ b/src/test/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyImportsCharacterizationTest.java
@@ -0,0 +1,91 @@
+/**
+ * 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 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;
+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.*;
+
+/**
+ * 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 }
+ */
+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 testLoadOntologyFlattensClosureWithMaterializedRDFSInference()
+ {
+ PrefixGraphRepository repository = new PrefixGraphRepository(null);
+
+ Resource a = ResourceFactory.createResource(NS + "A");
+ Resource b = ResourceFactory.createResource(NS + "B");
+ Resource x = ResourceFactory.createResource(NS + "x");
+
+ // 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, RDFS.Class);
+ imported.add(b, RDFS.subClassOf, a);
+ imported.add(x, RDF.type, b);
+ repository.put(IMPORT_URI, imported.getGraph());
+
+ // base ontology owl:imports the imported one
+ Model base = ModelFactory.createDefaultModel();
+ 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());
+
+ OntologyFilter.loadOntology(repository, BASE_URI);
+
+ 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");
+ // (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 + "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");
+ }
+
+}
diff --git a/src/test/java/com/atomgraph/linkeddatahub/server/filter/request/auth/ProxiedWebIDFilterTest.java b/src/test/java/com/atomgraph/linkeddatahub/server/filter/request/auth/ProxiedWebIDFilterTest.java
new file mode 100644
index 0000000000..e23a32861d
--- /dev/null
+++ b/src/test/java/com/atomgraph/linkeddatahub/server/filter/request/auth/ProxiedWebIDFilterTest.java
@@ -0,0 +1,115 @@
+/**
+ * Copyright 2025 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.auth;
+
+import jakarta.ws.rs.container.ContainerRequestContext;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import org.junit.jupiter.api.BeforeEach;
+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.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.when;
+
+/**
+ * Unit tests for {@link ProxiedWebIDFilter}, which extracts the client certificate from the
+ * {@code Client-Cert} request header (URL-encoded PEM) instead of the TLS connection.
+ *
+ * @author Martynas Jusevičius {@literal }
+ */
+@ExtendWith(MockitoExtension.class)
+public class ProxiedWebIDFilterTest
+{
+
+ private static final String CLIENT_CERT_HEADER_NAME = "Client-Cert";
+
+ /** A self-signed X.509 certificate (CN=Test WebID, O=LinkedDataHub) in PEM form. */
+ private static final String CERT_PEM =
+ "-----BEGIN CERTIFICATE-----\n" +
+ "MIIC1jCCAb4CCQD35LqgLKP0ATANBgkqhkiG9w0BAQsFADAtMRMwEQYDVQQDDApU\n" +
+ "ZXN0IFdlYklEMRYwFAYDVQQKDA1MaW5rZWREYXRhSHViMB4XDTI2MDYxODA4Mzkz\n" +
+ "OFoXDTI2MDYxOTA4MzkzOFowLTETMBEGA1UEAwwKVGVzdCBXZWJJRDEWMBQGA1UE\n" +
+ "CgwNTGlua2VkRGF0YUh1YjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB\n" +
+ "AMRsMfh0IikmxDbZ0kca2FRI+WXelczdi+dU9ZC42cAZJM6pEu9icvCdTKBKipYE\n" +
+ "07PkfAgsmkS3qzly1iQzyXZFzopndg9FdFvWZyn8SdxNSKCcQt13NTp8sXflkdxK\n" +
+ "SfOseUx1cZ0T4ylGNwkqxcqZo5b06CJqiZqjgO4x7kYWWrli44AgzMkT3AgJqq5X\n" +
+ "iSo5j8gOjicR+ZywLAEvWH0ITja4sIgsQzZHxbquOuPEevnT+135M33wHxsY5MHJ\n" +
+ "Ykxid7C4ifVm4jXf81CmnoCifR9UeBnMZ0QBPBP/Exv+CpTgb3TBAfF1o1QsEuM3\n" +
+ "60fYmqLcwLiKgZqDJ7ZH80UCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAeYJGVnFq\n" +
+ "CARK15JQQk1YBAUPspkFWAeXH9UzYyxpqt0bLlYO9g4KExJVJvE9Qub2lHXBs36j\n" +
+ "/elRF+PR5Zt/6LD26OnSu+QWkFSqbO6Otul7g9ikMufuhNrZyyOOzidqFfcfkhWx\n" +
+ "FZh+yZhGoo2f+ddMuYbK3lKI+/DMswfdNN6VN++EOYskjWBB85GKUxEJTLEF2yE+\n" +
+ "yRtqnQfX3ucvO2Zd1XHsgknzoSfG8CXZF3GDcqzzEZ6Aa//xtwYRCmNmj9E9SdMY\n" +
+ "xuCHnQP3cV/vBBhxt1BWdIRtcU6xpasNMfWGgAxqrCTz+GnT7FExbe5qt6CgX7yl\n" +
+ "JLw8c9VNQzsM9g==\n" +
+ "-----END CERTIFICATE-----\n";
+
+ @Mock private ContainerRequestContext requestContext;
+
+ private ProxiedWebIDFilter filter;
+
+ @BeforeEach
+ public void setUp()
+ {
+ filter = new ProxiedWebIDFilter();
+ }
+
+ @Test
+ public void testCertificateFactoryIsX509()
+ {
+ assertNotNull(filter.getCertificateFactory());
+ assertEquals("X.509", filter.getCertificateFactory().getType());
+ }
+
+ /** No {@code Client-Cert} header — no certificate. */
+ @Test
+ public void testNoHeaderReturnsNull() throws Exception
+ {
+ when(requestContext.getHeaderString(CLIENT_CERT_HEADER_NAME)).thenReturn(null);
+ assertNull(filter.getWebIDCertificate(requestContext));
+ }
+
+ /** A URL-encoded PEM certificate in the header is decoded and parsed into an X509Certificate. */
+ @Test
+ public void testValidHeaderParsesCertificate() throws Exception
+ {
+ String encoded = URLEncoder.encode(CERT_PEM, StandardCharsets.UTF_8);
+ when(requestContext.getHeaderString(CLIENT_CERT_HEADER_NAME)).thenReturn(encoded);
+
+ X509Certificate cert = filter.getWebIDCertificate(requestContext);
+
+ assertNotNull(cert);
+ assertTrue(cert.getSubjectX500Principal().getName().contains("Test WebID"));
+ }
+
+ /** A header that is not a valid certificate must surface as a CertificateException. */
+ @Test
+ public void testMalformedHeaderThrows()
+ {
+ when(requestContext.getHeaderString(CLIENT_CERT_HEADER_NAME)).thenReturn("garbage-not-a-cert");
+ assertThrows(CertificateException.class, () -> filter.getWebIDCertificate(requestContext));
+ }
+
+}
diff --git a/src/test/java/com/atomgraph/linkeddatahub/server/model/impl/DocumentHierarchyGraphStoreImplTest.java b/src/test/java/com/atomgraph/linkeddatahub/server/model/impl/DocumentHierarchyGraphStoreImplTest.java
new file mode 100644
index 0000000000..de95fa2891
--- /dev/null
+++ b/src/test/java/com/atomgraph/linkeddatahub/server/model/impl/DocumentHierarchyGraphStoreImplTest.java
@@ -0,0 +1,274 @@
+/**
+ * Copyright 2025 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.model.impl;
+
+import com.atomgraph.linkeddatahub.vocabulary.SIOC;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.InputStream;
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.Set;
+import org.apache.jena.datatypes.xsd.XSDDateTime;
+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.vocabulary.DCTerms;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.CALLS_REAL_METHODS;
+import static org.mockito.Mockito.mock;
+
+/**
+ * Unit tests for the stateless logic of {@link DocumentHierarchyGraphStoreImpl}.
+ *
+ * The class has a single heavyweight {@code @Inject} constructor that wires the JAX-RS request
+ * context and an HTTP-backed {@code GraphStoreClient}, so it cannot be instantiated directly in a
+ * unit test. The methods exercised here ({@code getChangedResources}, {@code getLastModified}, and
+ * the {@code writeFile}/{@code writeFiles} guards and file I/O) read no instance state, so we obtain
+ * an instance via Mockito's {@code CALLS_REAL_METHODS} (which skips the constructor) and invoke the
+ * real method bodies.
+ *
+ * @author Martynas Jusevičius {@literal }
+ */
+public class DocumentHierarchyGraphStoreImplTest
+{
+
+ private final DocumentHierarchyGraphStoreImpl gs = mock(DocumentHierarchyGraphStoreImpl.class, CALLS_REAL_METHODS);
+
+ // getChangedResources()
+
+ @Test
+ public void testChangedResourcesNullBefore()
+ {
+ assertThrows(IllegalArgumentException.class, () -> gs.getChangedResources(null, ModelFactory.createDefaultModel()));
+ }
+
+ @Test
+ public void testChangedResourcesNullAfter()
+ {
+ assertThrows(IllegalArgumentException.class, () -> gs.getChangedResources(ModelFactory.createDefaultModel(), null));
+ }
+
+ @Test
+ public void testChangedResourcesDetectsAddedSubject()
+ {
+ Model before = ModelFactory.createDefaultModel();
+ Model after = ModelFactory.createDefaultModel();
+ Resource added = after.createResource("http://localhost/added");
+ added.addProperty(SIOC.HAS_PARENT, after.createResource("http://localhost/"));
+
+ Set changed = gs.getChangedResources(before, after);
+ assertTrue(changed.contains(added));
+ }
+
+ @Test
+ public void testChangedResourcesDetectsRemovedSubject()
+ {
+ Model before = ModelFactory.createDefaultModel();
+ Resource removed = before.createResource("http://localhost/removed");
+ removed.addProperty(SIOC.HAS_PARENT, before.createResource("http://localhost/"));
+ Model after = ModelFactory.createDefaultModel();
+
+ Set changed = gs.getChangedResources(before, after);
+ assertTrue(changed.contains(removed));
+ }
+
+ @Test
+ public void testChangedResourcesIdenticalModelsAreEmpty()
+ {
+ Model before = ModelFactory.createDefaultModel();
+ before.createResource("http://localhost/doc").addProperty(DCTerms.creator, before.createResource("http://localhost/agent"));
+ Model after = ModelFactory.createDefaultModel().add(before);
+
+ assertTrue(gs.getChangedResources(before, after).isEmpty());
+ }
+
+ // getLastModified(Resource)
+
+ @Test
+ public void testLastModifiedNullResource()
+ {
+ assertThrows(IllegalArgumentException.class, () -> gs.getLastModified((Resource)null));
+ }
+
+ @Test
+ public void testLastModifiedReturnsNullWhenNoDates()
+ {
+ Model model = ModelFactory.createDefaultModel();
+ Resource resource = model.createResource("http://localhost/doc");
+ assertNull(gs.getLastModified(resource));
+ }
+
+ @Test
+ public void testLastModifiedReturnsMaxOfCreatedAndModified()
+ {
+ Model model = ModelFactory.createDefaultModel();
+ Calendar createdCal = new GregorianCalendar(2020, Calendar.JANUARY, 1, 0, 0, 0);
+ Calendar modifiedCal = new GregorianCalendar(2021, Calendar.JANUARY, 1, 0, 0, 0);
+ Resource resource = model.createResource("http://localhost/doc").
+ addProperty(DCTerms.created, model.createTypedLiteral(createdCal)).
+ addProperty(DCTerms.modified, model.createTypedLiteral(modifiedCal));
+
+ // expected value is the later (modified) date, round-tripped through the same XSDDateTime path the method uses
+ Date expected = ((XSDDateTime)model.createTypedLiteral(modifiedCal).getValue()).asCalendar().getTime();
+ assertEquals(expected, gs.getLastModified(resource));
+ }
+
+ @Test
+ public void testLastModifiedIgnoresNonDateTimeLiterals()
+ {
+ Model model = ModelFactory.createDefaultModel();
+ Resource resource = model.createResource("http://localhost/doc").
+ addProperty(DCTerms.modified, "not a date"); // plain string literal, not xsd:dateTime
+ assertNull(gs.getLastModified(resource));
+ }
+
+ // getLastModified(Model, URI)
+
+ @Test
+ public void testLastModifiedByGraphURIReturnsNullForNullURI()
+ {
+ assertNull(gs.getLastModified(ModelFactory.createDefaultModel(), null));
+ }
+
+ @Test
+ public void testLastModifiedByGraphURI()
+ {
+ Model model = ModelFactory.createDefaultModel();
+ Calendar cal = new GregorianCalendar(2022, Calendar.MARCH, 15, 12, 0, 0);
+ URI graphURI = URI.create("http://localhost/doc");
+ model.createResource(graphURI.toString()).addProperty(DCTerms.modified, model.createTypedLiteral(cal));
+
+ Date expected = ((XSDDateTime)model.createTypedLiteral(cal).getValue()).asCalendar().getTime();
+ assertEquals(expected, gs.getLastModified(model, graphURI));
+ }
+
+ // writeFile(File, InputStream)
+
+ @Test
+ public void testWriteFileNullFile()
+ {
+ assertThrows(IllegalArgumentException.class, () -> gs.writeFile((File)null, new ByteArrayInputStream(new byte[0])));
+ }
+
+ @Test
+ public void testWriteFileNullInputStream(@TempDir Path tempDir)
+ {
+ File file = tempDir.resolve("out.bin").toFile();
+ assertThrows(IllegalArgumentException.class, () -> gs.writeFile(file, (InputStream)null));
+ }
+
+ @Test
+ public void testWriteFileWritesContent(@TempDir Path tempDir) throws Exception
+ {
+ File file = tempDir.resolve("out.bin").toFile();
+ byte[] data = "hello world".getBytes(StandardCharsets.UTF_8);
+
+ gs.writeFile(file, new ByteArrayInputStream(data));
+
+ assertTrue(file.exists());
+ assertArrayEquals(data, Files.readAllBytes(file.toPath()));
+ }
+
+ // writeFile(URI, URI, URI, InputStream)
+
+ @Test
+ public void testWriteFileByURINullURI(@TempDir Path tempDir)
+ {
+ assertThrows(IllegalArgumentException.class, () -> gs.writeFile(null, URI.create("http://localhost/"), tempDir.toUri(), new ByteArrayInputStream(new byte[0])));
+ }
+
+ @Test
+ public void testWriteFileByURIRelativeURIRejected(@TempDir Path tempDir)
+ {
+ assertThrows(IllegalArgumentException.class, () -> gs.writeFile(URI.create("relative/path"), URI.create("http://localhost/"), tempDir.toUri(), new ByteArrayInputStream(new byte[0])));
+ }
+
+ @Test
+ public void testWriteFileByURINullBase(@TempDir Path tempDir)
+ {
+ assertThrows(IllegalArgumentException.class, () -> gs.writeFile(URI.create("http://localhost/myfile"), null, tempDir.toUri(), new ByteArrayInputStream(new byte[0])));
+ }
+
+ @Test
+ public void testWriteFileByURINullUploadRoot()
+ {
+ assertThrows(IllegalArgumentException.class, () -> gs.writeFile(URI.create("http://localhost/myfile"), URI.create("http://localhost/"), null, new ByteArrayInputStream(new byte[0])));
+ }
+
+ @Test
+ public void testWriteFileByURIResolvesRelativePath(@TempDir Path tempDir) throws Exception
+ {
+ URI base = URI.create("http://localhost/");
+ URI uri = URI.create("http://localhost/myfile");
+ URI uploadRoot = tempDir.toUri(); // ends with '/'
+ byte[] data = "content-addressed".getBytes(StandardCharsets.UTF_8);
+
+ File written = gs.writeFile(uri, base, uploadRoot, new ByteArrayInputStream(data));
+
+ assertEquals(new File(uploadRoot.resolve("myfile")), written);
+ assertArrayEquals(data, Files.readAllBytes(written.toPath()));
+ }
+
+ // writeFiles(Model, Map)
+
+ @Test
+ public void testWriteFilesNullModel()
+ {
+ assertThrows(IllegalArgumentException.class, () -> gs.writeFiles(null, new HashMap<>()));
+ }
+
+ @Test
+ public void testWriteFilesNullMap()
+ {
+ assertThrows(IllegalArgumentException.class, () -> gs.writeFiles(ModelFactory.createDefaultModel(), null));
+ }
+
+ @Test
+ public void testWriteFilesNoFileResourcesWritesNothing()
+ {
+ Model model = ModelFactory.createDefaultModel();
+ model.createResource("http://localhost/doc").addProperty(DCTerms.creator, model.createResource("http://localhost/agent"));
+
+ assertEquals(0, gs.writeFiles(model, new HashMap<>()));
+ }
+
+ @Test
+ public void testChangedResourcesUnchangedDoesNotContainSubject()
+ {
+ Model before = ModelFactory.createDefaultModel();
+ Resource subject = before.createResource("http://localhost/doc");
+ subject.addProperty(DCTerms.creator, before.createResource("http://localhost/agent"));
+ Model after = ModelFactory.createDefaultModel().add(before);
+
+ assertFalse(gs.getChangedResources(before, after).contains(subject));
+ }
+
+}
diff --git a/src/test/java/com/atomgraph/linkeddatahub/server/util/OntologyRepositoryTest.java b/src/test/java/com/atomgraph/linkeddatahub/server/util/OntologyRepositoryTest.java
new file mode 100644
index 0000000000..33b3183f25
--- /dev/null
+++ b/src/test/java/com/atomgraph/linkeddatahub/server/util/OntologyRepositoryTest.java
@@ -0,0 +1,111 @@
+/**
+ * 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.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.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.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;
+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 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 OntologyRepositoryTest
+{
+
+ 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 com.atomgraph.core.client.SPARQLClient sparqlClient;
+ @Mock Response response;
+ @Mock GraphStoreClient gsc;
+
+ 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 HTTP fallback is not used. */
+ @Test
+ public void testSPARQLResultUsedWhenNonEmpty()
+ {
+ Model sparqlResult = ModelFactory.createDefaultModel();
+ sparqlResult.createResource(ONTOLOGY_URI).addProperty(RDF.type, OWL.Ontology);
+ stubSPARQLChain(sparqlResult);
+
+ OntologyRepository repository = new OntologyRepository(app, system, gsc, ONTOLOGY_QUERY);
+ Graph result = repository.get(ONTOLOGY_URI);
+
+ 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 the Graph Store client (HTTP) load. */
+ @Test
+ public void testFallsBackToHttpWhenSPARQLEmpty()
+ {
+ Model empty = ModelFactory.createDefaultModel();
+ stubSPARQLChain(empty);
+
+ Model fallback = ModelFactory.createDefaultModel();
+ fallback.createResource(ONTOLOGY_URI).addProperty(RDFS.label, "fallback");
+ when(gsc.getModel(ONTOLOGY_URI)).thenReturn(fallback);
+
+ OntologyRepository repository = new OntologyRepository(app, system, gsc, ONTOLOGY_QUERY);
+ Graph result = repository.get(ONTOLOGY_URI);
+
+ assertTrue(result.contains(NodeFactory.createURI(ONTOLOGY_URI), RDFS.label.asNode(), NodeFactory.createLiteralString("fallback")));
+ }
+
+}
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..6d2062f9c1
--- /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 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;
+ }
+
+ /** 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)");
+ }
+
+}
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..95f10ddc6d
--- /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_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
+ * {@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");
+ }
+
+}