Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: sef drop cert release
.PHONY: sef drop cert release tests

# Generate Saxon-JS SEF files for client-side XSLT transformations
sef:
Expand All @@ -10,8 +10,12 @@ drop:

# Generate server SSL certificate using the .env config
cert:
./bin/server-cert-gen.sh .env nginx ssl
server-cert-gen.sh .env nginx ssl

# Run the full Maven release process (prepare, deploy to Sonatype, merge to master/develop)
release:
./release.sh

# Run HTTP tests using owner and secretary certificates with passwords from secrets/
tests:
cd http-tests && ./run.sh ../ssl/owner/cert.pem $$(cat ../secrets/owner_cert_password.txt) ../ssl/secretary/cert.pem $$(cat ../secrets/secretary_cert_password.txt)
40 changes: 40 additions & 0 deletions http-tests/sparql-protocol/query/GET-ns-relative-uri.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#!/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"

# create class in the namespace ontology

namespace_doc="${END_USER_BASE_URL}ns"
namespace="${namespace_doc}#"
ontology_doc="${ADMIN_BASE_URL}ontologies/namespace/"
class="${namespace}NewClass"

add-class.sh \
-f "$OWNER_CERT_FILE" \
-p "$OWNER_CERT_PWD" \
-b "$ADMIN_BASE_URL" \
--uri "$class" \
--label "New class" \
"$ontology_doc"

# clear ontology from memory

clear-ontology.sh \
-f "$OWNER_CERT_FILE" \
-p "$OWNER_CERT_PWD" \
-b "$ADMIN_BASE_URL" \
--ontology "$namespace"

# query using relative URI - <#NewClass> should resolve to ${namespace}NewClass

curl -k -s -G \
-E "$OWNER_CERT_FILE":"$OWNER_CERT_PWD" \
-H "Accept: application/sparql-results+xml" \
"${namespace_doc}" \
--data-urlencode "query=SELECT * { <#NewClass> ?p ?o }" \
| grep '<literal>New class</literal>' > /dev/null
41 changes: 41 additions & 0 deletions http-tests/sparql-protocol/query/POST-ns-relative-uri.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/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"

# create class in the namespace ontology

namespace_doc="${END_USER_BASE_URL}ns"
namespace="${namespace_doc}#"
ontology_doc="${ADMIN_BASE_URL}ontologies/namespace/"
class="${namespace}NewClass"

add-class.sh \
-f "$OWNER_CERT_FILE" \
-p "$OWNER_CERT_PWD" \
-b "$ADMIN_BASE_URL" \
--uri "$class" \
--label "New class" \
"$ontology_doc"

# clear ontology from memory

clear-ontology.sh \
-f "$OWNER_CERT_FILE" \
-p "$OWNER_CERT_PWD" \
-b "$ADMIN_BASE_URL" \
--ontology "$namespace"

# query using relative URI - <#NewClass> should resolve to ${namespace}NewClass

curl -k -s -X POST \
-E "$OWNER_CERT_FILE":"$OWNER_CERT_PWD" \
-H "Accept: application/sparql-results+xml" \
-H "Content-Type: application/x-www-form-urlencoded" \
"${namespace_doc}" \
--data-urlencode "query=SELECT * { <#NewClass> ?p ?o }" \
| grep '<literal>New class</literal>' > /dev/null
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

<groupId>com.atomgraph</groupId>
<artifactId>linkeddatahub</artifactId>
<version>5.3.0</version>
<version>5.3.1-SNAPSHOT</version>
<packaging>${packaging.type}</packaging>

<name>AtomGraph LinkedDataHub</name>
Expand Down Expand Up @@ -46,7 +46,7 @@
<url>https://github.com/AtomGraph/LinkedDataHub</url>
<connection>scm:git:git://github.com/AtomGraph/LinkedDataHub.git</connection>
<developerConnection>scm:git:git@github.com:AtomGraph/LinkedDataHub.git</developerConnection>
<tag>linkeddatahub-5.3.0</tag>
<tag>linkeddatahub-2.1.1</tag>
</scm>

<repositories>
Expand Down
26 changes: 15 additions & 11 deletions src/main/java/com/atomgraph/linkeddatahub/Application.java
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@
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.grddl.YouTubeGRDDLFilter;
import com.atomgraph.linkeddatahub.client.filter.JSONGRDDLFilter;
import com.atomgraph.linkeddatahub.client.filter.JSONGRDDLFilterProvider;
import com.atomgraph.linkeddatahub.imports.ImportExecutor;
import com.atomgraph.linkeddatahub.io.HtmlJsonLDReaderFactory;
import com.atomgraph.linkeddatahub.io.JsonLDReader;
Expand Down Expand Up @@ -153,6 +154,7 @@
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.ServiceLoader;
import java.util.Map;
import java.util.Properties;
import jakarta.mail.Authenticator;
Expand Down Expand Up @@ -1168,17 +1170,19 @@ protected void registerExceptionMappers()
*/
protected void registerClientFilters()
{
try
{
// Register YouTube GRDDL filter
YouTubeGRDDLFilter youtubeFilter = new YouTubeGRDDLFilter(xsltComp);
client.register(youtubeFilter);
externalClient.register(youtubeFilter);
}
catch (SaxonApiException ex)
ServiceLoader.load(JSONGRDDLFilterProvider.class).forEach(provider ->
{
if (log.isErrorEnabled()) log.error("Failed to initialize GRDDL client filter");
}
try
{
JSONGRDDLFilter filter = provider.getFilter(xsltComp);
client.register(filter);
externalClient.register(filter);
}
catch (SaxonApiException ex)
{
if (log.isErrorEnabled()) log.error("Failed to initialize GRDDL client filter for {}", provider.getClass().getSimpleName());
}
});
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* Copyright 2025 Martynas Jusevičius <martynas@atomgraph.com>
*
* 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.client.filter;

import net.sf.saxon.s9api.SaxonApiException;
import net.sf.saxon.s9api.XsltCompiler;

/**
* SPI interface for providing {@link JSONGRDDLFilter} instances.
* Implementations are discovered via {@link java.util.ServiceLoader}.
*/
public interface JSONGRDDLFilterProvider
{

JSONGRDDLFilter getFilter(XsltCompiler xsltCompiler) throws SaxonApiException;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* Copyright 2025 Martynas Jusevičius <martynas@atomgraph.com>
*
* 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.client.filter.grddl;

import com.atomgraph.linkeddatahub.client.filter.JSONGRDDLFilter;
import com.atomgraph.linkeddatahub.client.filter.JSONGRDDLFilterProvider;
import net.sf.saxon.s9api.SaxonApiException;
import net.sf.saxon.s9api.XsltCompiler;

/**
* {@link JSONGRDDLFilterProvider} implementation for YouTube GRDDL transformation.
*/
public class YouTubeGRDDLFilterProvider implements JSONGRDDLFilterProvider
{

@Override
public JSONGRDDLFilter getFilter(XsltCompiler xsltCompiler) throws SaxonApiException
{
return new YouTubeGRDDLFilter(xsltCompiler);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
import org.apache.jena.ontology.Ontology;
import org.apache.jena.query.DatasetFactory;
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.riot.system.Checker;
Expand Down Expand Up @@ -148,6 +149,8 @@ public Response get(@QueryParam(QUERY) Query query,
else throw new BadRequestException("SPARQL query string not provided");
}

// re-parse query to set explicit fallback base URI
query = QueryFactory.create(query.toString(), getUriInfo().getAbsolutePath().toString());
return super.get(query, defaultGraphUris, namedGraphUris);
}

Expand All @@ -160,6 +163,8 @@ public Response post(@FormParam(QUERY) String queryString, @FormParam(UPDATE) St
{
if (updateString != null) throw new WebApplicationException("SPARQL updates are not allowed on the <ns> endpoint", Status.METHOD_NOT_ALLOWED);

// re-parse query to set explicit fallback base URI
queryString = QueryFactory.create(queryString, getUriInfo().getAbsolutePath().toString()).toString();
return super.post(queryString, updateString, defaultGraphUris, namedGraphUris, usingGraphUris, usingNamedGraphUris);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
com.atomgraph.linkeddatahub.client.filter.grddl.YouTubeGRDDLFilterProvider
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@ exclude-result-prefixes="#all">

<xsl:output method="xhtml" encoding="UTF-8" indent="yes" omit-xml-declaration="yes" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" media-type="application/xhtml+xml"/>

<!-- <xsl:param name="ldh:base" as="xs:anyURI" static="yes"/>-->
<xsl:param name="lapp:origin" as="xs:anyURI"/>
<xsl:param name="ldh:requestUri" as="xs:anyURI"/>
<xsl:param name="ac:endpoint" select="resolve-uri('sparql', $ldt:base)" as="xs:anyURI"/>
Expand Down
Loading