Skip to content

Commit bc39167

Browse files
committed
Release version 5.3.1
2 parents 8f8a65c + b7215b6 commit bc39167

File tree

20 files changed

+421
-154
lines changed

20 files changed

+421
-154
lines changed

CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
1+
## [5.3.1] - 2026-03-29
2+
### Added
3+
- Namespace endpoint handles queries with relative URIs, resolved against the endpoint URL (#276 related)
4+
- GRDDL filters loaded using `ServiceLoader` for better extensibility
5+
- HTTP tests for namespace endpoint relative URI queries
6+
- HTTP test for package stylesheet deduplication
7+
8+
### Changed
9+
- `XSLTMasterUpdater` refactored: `regenerateMasterStylesheet()` replaced with `addPackageImport()`/`removePackageImport()` methods that preserve existing stylesheet content (#275)
10+
- Resolving relative URLs in XHTML literals against the document's base URI (#276)
11+
- Removed unused XSLT params
12+
- `make tests` command added to Makefile
13+
14+
### Fixed
15+
- Fixed `XSLTMasterUpdater` to avoid duplicate `xsl:import` statements when installing packages (#275)
16+
117
## [5.3.0] - 2026-03-05
218
### Added
319
- Class-based navigation (#270)

Makefile

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.PHONY: sef drop cert release
1+
.PHONY: sef drop cert release tests
22

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

1111
# Generate server SSL certificate using the .env config
1212
cert:
13-
./bin/server-cert-gen.sh .env nginx ssl
13+
server-cert-gen.sh .env nginx ssl
1414

1515
# Run the full Maven release process (prepare, deploy to Sonatype, merge to master/develop)
1616
release:
1717
./release.sh
18+
19+
# Run HTTP tests using owner and secretary certificates with passwords from secrets/
20+
tests:
21+
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)

http-tests/admin/packages/install-package-422.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ curl -k -w "%{http_code}\n" -o /dev/null -s \
1313
-E "$OWNER_CERT_FILE":"$OWNER_CERT_PWD" \
1414
-X POST \
1515
-H "Content-Type: application/x-www-form-urlencoded" \
16-
--data-urlencode "package-uri=https://packages.linkeddatahub.com/nonexistent/#package" \
16+
--data-urlencode "package-uri=${END_USER_BASE_URL}static/nonexistent/#package" \
1717
"${ADMIN_BASE_URL}packages/install" \
1818
| grep -q "$STATUS_UNPROCESSABLE_ENTITY"
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
initialize_dataset "$END_USER_BASE_URL" "$TMP_END_USER_DATASET" "$END_USER_ENDPOINT_URL"
5+
initialize_dataset "$ADMIN_BASE_URL" "$TMP_ADMIN_DATASET" "$ADMIN_ENDPOINT_URL"
6+
purge_cache "$END_USER_VARNISH_SERVICE"
7+
purge_cache "$ADMIN_VARNISH_SERVICE"
8+
purge_cache "$FRONTEND_VARNISH_SERVICE"
9+
10+
# Clean up any leftover package stylesheet files from previous test runs
11+
docker compose exec -T linkeddatahub rm -rf /usr/local/tomcat/webapps/ROOT/static/com/linkeddatahub/packages/skos 2>/dev/null || true
12+
docker compose exec -T linkeddatahub sed -i '/linkeddatahub\/packages\/skos\/layout.xsl/d' /usr/local/tomcat/webapps/ROOT/static/xsl/layout.xsl 2>/dev/null || true
13+
14+
# Tomcat caches static files with default cacheTtl=5000ms (5 seconds)
15+
# See: https://tomcat.apache.org/tomcat-10.1-doc/config/resources.html#Attributes
16+
default_ttl=5
17+
18+
# test package URI (SKOS package)
19+
package_uri="https://packages.linkeddatahub.com/skos/#this"
20+
21+
# first install
22+
install-package.sh \
23+
-b "$END_USER_BASE_URL" \
24+
-f "$OWNER_CERT_FILE" \
25+
-p "$OWNER_CERT_PWD" \
26+
--package "$package_uri"
27+
28+
# Wait for Tomcat's static resource cache to expire
29+
sleep $default_ttl
30+
31+
# verify exactly one import after first install
32+
import_count=$(curl -k -s "${END_USER_BASE_URL}static/xsl/layout.xsl" \
33+
| grep -c "com/linkeddatahub/packages/skos/layout.xsl" || true)
34+
if [ "$import_count" -ne 1 ]; then
35+
exit 1
36+
fi
37+
38+
# second install (same package)
39+
install-package.sh \
40+
-b "$END_USER_BASE_URL" \
41+
-f "$OWNER_CERT_FILE" \
42+
-p "$OWNER_CERT_PWD" \
43+
--package "$package_uri"
44+
45+
# Wait for Tomcat's static resource cache to expire
46+
sleep $default_ttl
47+
48+
# verify still exactly one import after second install (deduplication guard)
49+
import_count=$(curl -k -s "${END_USER_BASE_URL}static/xsl/layout.xsl" \
50+
| grep -c "com/linkeddatahub/packages/skos/layout.xsl" || true)
51+
if [ "$import_count" -ne 1 ]; then
52+
exit 1
53+
fi
54+
55+
# cleanup
56+
uninstall-package.sh \
57+
-b "$END_USER_BASE_URL" \
58+
-f "$OWNER_CERT_FILE" \
59+
-p "$OWNER_CERT_PWD" \
60+
--package "$package_uri"

http-tests/run.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ if [ "$#" -ne 4 ]; then
77
exit 1
88
fi
99

10-
export OWNER_CERT_FILE="$1"
10+
export OWNER_CERT_FILE="$(realpath "$1")"
1111
export OWNER_CERT_PWD="$2"
12-
export SECRETARY_CERT_FILE="$3"
12+
export SECRETARY_CERT_FILE="$(realpath "$3")"
1313
export SECRETARY_CERT_PWD="$4"
1414

1515
export STATUS_OK=200
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
initialize_dataset "$END_USER_BASE_URL" "$TMP_END_USER_DATASET" "$END_USER_ENDPOINT_URL"
5+
initialize_dataset "$ADMIN_BASE_URL" "$TMP_ADMIN_DATASET" "$ADMIN_ENDPOINT_URL"
6+
purge_cache "$END_USER_VARNISH_SERVICE"
7+
purge_cache "$ADMIN_VARNISH_SERVICE"
8+
purge_cache "$FRONTEND_VARNISH_SERVICE"
9+
10+
# create class in the namespace ontology
11+
12+
namespace_doc="${END_USER_BASE_URL}ns"
13+
namespace="${namespace_doc}#"
14+
ontology_doc="${ADMIN_BASE_URL}ontologies/namespace/"
15+
class="${namespace}NewClass"
16+
17+
add-class.sh \
18+
-f "$OWNER_CERT_FILE" \
19+
-p "$OWNER_CERT_PWD" \
20+
-b "$ADMIN_BASE_URL" \
21+
--uri "$class" \
22+
--label "New class" \
23+
"$ontology_doc"
24+
25+
# clear ontology from memory
26+
27+
clear-ontology.sh \
28+
-f "$OWNER_CERT_FILE" \
29+
-p "$OWNER_CERT_PWD" \
30+
-b "$ADMIN_BASE_URL" \
31+
--ontology "$namespace"
32+
33+
# query using relative URI - <#NewClass> should resolve to ${namespace}NewClass
34+
35+
curl -k -s -G \
36+
-E "$OWNER_CERT_FILE":"$OWNER_CERT_PWD" \
37+
-H "Accept: application/sparql-results+xml" \
38+
"${namespace_doc}" \
39+
--data-urlencode "query=SELECT * { <#NewClass> ?p ?o }" \
40+
| grep '<literal>New class</literal>' > /dev/null
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
initialize_dataset "$END_USER_BASE_URL" "$TMP_END_USER_DATASET" "$END_USER_ENDPOINT_URL"
5+
initialize_dataset "$ADMIN_BASE_URL" "$TMP_ADMIN_DATASET" "$ADMIN_ENDPOINT_URL"
6+
purge_cache "$END_USER_VARNISH_SERVICE"
7+
purge_cache "$ADMIN_VARNISH_SERVICE"
8+
purge_cache "$FRONTEND_VARNISH_SERVICE"
9+
10+
# create class in the namespace ontology
11+
12+
namespace_doc="${END_USER_BASE_URL}ns"
13+
namespace="${namespace_doc}#"
14+
ontology_doc="${ADMIN_BASE_URL}ontologies/namespace/"
15+
class="${namespace}NewClass"
16+
17+
add-class.sh \
18+
-f "$OWNER_CERT_FILE" \
19+
-p "$OWNER_CERT_PWD" \
20+
-b "$ADMIN_BASE_URL" \
21+
--uri "$class" \
22+
--label "New class" \
23+
"$ontology_doc"
24+
25+
# clear ontology from memory
26+
27+
clear-ontology.sh \
28+
-f "$OWNER_CERT_FILE" \
29+
-p "$OWNER_CERT_PWD" \
30+
-b "$ADMIN_BASE_URL" \
31+
--ontology "$namespace"
32+
33+
# query using relative URI - <#NewClass> should resolve to ${namespace}NewClass
34+
35+
curl -k -s -X POST \
36+
-E "$OWNER_CERT_FILE":"$OWNER_CERT_PWD" \
37+
-H "Accept: application/sparql-results+xml" \
38+
-H "Content-Type: application/x-www-form-urlencoded" \
39+
"${namespace_doc}" \
40+
--data-urlencode "query=SELECT * { <#NewClass> ?p ?o }" \
41+
| grep '<literal>New class</literal>' > /dev/null

pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
<groupId>com.atomgraph</groupId>
55
<artifactId>linkeddatahub</artifactId>
6-
<version>5.3.0</version>
6+
<version>5.3.1</version>
77
<packaging>${packaging.type}</packaging>
88

99
<name>AtomGraph LinkedDataHub</name>
@@ -46,7 +46,7 @@
4646
<url>https://github.com/AtomGraph/LinkedDataHub</url>
4747
<connection>scm:git:git://github.com/AtomGraph/LinkedDataHub.git</connection>
4848
<developerConnection>scm:git:git@github.com:AtomGraph/LinkedDataHub.git</developerConnection>
49-
<tag>linkeddatahub-5.3.0</tag>
49+
<tag>linkeddatahub-5.3.1</tag>
5050
</scm>
5151

5252
<repositories>

src/main/java/com/atomgraph/linkeddatahub/Application.java

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,8 @@
7070
import com.atomgraph.client.util.XsltResolver;
7171
import com.atomgraph.linkeddatahub.client.GraphStoreClient;
7272
import com.atomgraph.linkeddatahub.client.filter.ClientUriRewriteFilter;
73-
import com.atomgraph.linkeddatahub.client.filter.grddl.YouTubeGRDDLFilter;
73+
import com.atomgraph.linkeddatahub.client.filter.JSONGRDDLFilter;
74+
import com.atomgraph.linkeddatahub.client.filter.JSONGRDDLFilterProvider;
7475
import com.atomgraph.linkeddatahub.imports.ImportExecutor;
7576
import com.atomgraph.linkeddatahub.io.HtmlJsonLDReaderFactory;
7677
import com.atomgraph.linkeddatahub.io.JsonLDReader;
@@ -153,6 +154,7 @@
153154
import java.security.cert.CertificateException;
154155
import java.security.cert.X509Certificate;
155156
import java.util.HashMap;
157+
import java.util.ServiceLoader;
156158
import java.util.Map;
157159
import java.util.Properties;
158160
import jakarta.mail.Authenticator;
@@ -1168,17 +1170,19 @@ protected void registerExceptionMappers()
11681170
*/
11691171
protected void registerClientFilters()
11701172
{
1171-
try
1172-
{
1173-
// Register YouTube GRDDL filter
1174-
YouTubeGRDDLFilter youtubeFilter = new YouTubeGRDDLFilter(xsltComp);
1175-
client.register(youtubeFilter);
1176-
externalClient.register(youtubeFilter);
1177-
}
1178-
catch (SaxonApiException ex)
1173+
ServiceLoader.load(JSONGRDDLFilterProvider.class).forEach(provider ->
11791174
{
1180-
if (log.isErrorEnabled()) log.error("Failed to initialize GRDDL client filter");
1181-
}
1175+
try
1176+
{
1177+
JSONGRDDLFilter filter = provider.getFilter(xsltComp);
1178+
client.register(filter);
1179+
externalClient.register(filter);
1180+
}
1181+
catch (SaxonApiException ex)
1182+
{
1183+
if (log.isErrorEnabled()) log.error("Failed to initialize GRDDL client filter for {}", provider.getClass().getSimpleName());
1184+
}
1185+
});
11821186
}
11831187

11841188
/**
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/**
2+
* Copyright 2025 Martynas Jusevičius <martynas@atomgraph.com>
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
package com.atomgraph.linkeddatahub.client.filter;
18+
19+
import net.sf.saxon.s9api.SaxonApiException;
20+
import net.sf.saxon.s9api.XsltCompiler;
21+
22+
/**
23+
* SPI interface for providing {@link JSONGRDDLFilter} instances.
24+
* Implementations are discovered via {@link java.util.ServiceLoader}.
25+
*/
26+
public interface JSONGRDDLFilterProvider
27+
{
28+
29+
JSONGRDDLFilter getFilter(XsltCompiler xsltCompiler) throws SaxonApiException;
30+
31+
}

0 commit comments

Comments
 (0)