Skip to content

Commit d8b86f1

Browse files
authored
Merge pull request #13 from SCDH/sparql
sparql plugin
2 parents 993d52c + 29e195e commit d8b86f1

15 files changed

Lines changed: 829 additions & 0 deletions

File tree

api/src/main/resources/openapi/seed-xc-openapi.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,16 @@ components:
417417
type:
418418
type: string
419419
description: The local name of the parameter type. Write 'integer' instead of 'xs:integer'!
420+
context:
421+
description: Options of the JSON-LD context
422+
type: object
423+
required:
424+
- location
425+
properties:
426+
location:
427+
type: string
428+
format: uri
429+
description: The URI of the initial active context
420430
transformationInfo:
421431
description: Information about the transformation resource and its runtime parameters
422432
type: object
@@ -490,6 +500,8 @@ components:
490500
$ref: '#/components/schemas/parser'
491501
serializer:
492502
$ref: '#/components/schemas/serializer'
503+
context:
504+
$ref: '#/components/schemas/context'
493505
required:
494506
- class
495507
- description

dts/pom.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,22 @@
3333
<artifactId>seed-xc-transformations</artifactId>
3434
<version>${revision}${changelist}</version>
3535
</dependency>
36+
<!--
37+
Add plugins to the classpath
38+
important: scope should be runtime or test.
39+
-->
3640
<dependency>
3741
<groupId>${project.groupId}</groupId>
3842
<artifactId>seed-xc-saxon</artifactId>
3943
<version>${revision}${changelist}</version>
4044
<scope>${plugins.scope}</scope>
4145
</dependency>
46+
<dependency>
47+
<groupId>${project.groupId}</groupId>
48+
<artifactId>seed-xc-sparql</artifactId>
49+
<version>${revision}${changelist}</version>
50+
<scope>${plugins.scope}</scope>
51+
</dependency>
4252
<dependency>
4353
<groupId>${project.groupId}</groupId>
4454
<artifactId>seed-resource-providers</artifactId>

plugins/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
<modules>
1818
<module>resource-providers</module>
1919
<module>saxon</module>
20+
<module>seed-xc-sparql</module>
2021
</modules>
2122

2223
<build>

plugins/seed-xc-sparql/README.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# SPARQL Plugins
2+
3+
This module provides transformation plugins. It is based on
4+
[Apache Jena](https://jena.apache.org/) and uses
5+
[Titanium JSON-LD](https://github.com/filip26/titanium-json-ld)
6+
for JSON-LD framing.
7+
8+
For JSON-LD contexts used in framing, this plugin allows outbound
9+
URL connections passing by the `ResourceProvider`.
10+
11+
## Transformation Types
12+
13+
- `sparql-construct`
14+
15+
## Content Negotiation
16+
17+
- If `TransformationInfo.mediaType` is present, then and only then it determines the returned format.
18+
- If there's no content type on the transformation level and content negotiation by the request's `Accept` is used.
19+
- In case no content type is defined on the transformation level nor one is requested, the content tpye of input resource will be used
20+
- As a fallback, a default content type is used.
21+
22+
## Transformation Parameters
23+
24+
Request parameters are passed to the query with a mechanism by
25+
[Apache Jena](https://jena.apache.org/documentation/query/parameterized-sparql-strings.html).
26+
It replaces all occurrences of a SPARQL variable `?X` with a literal.
27+
28+
Parameter types should be declared with the parameter descripter as `xs:...` types.
29+
`xs:string` is used as default.
30+
31+
## Application Properties
32+
33+
- `url-connect-timeout`: time limit for establishing a connection to a remote JSON-LD context URL for framing, defaults to 10s
34+
- `url-read-timeout`: time limit for fetching a remote JSON-LD context for framing, defaults to 10s
35+
- `context-max-size`: size limit of JSON-LD context for framing, defaults to 1MB

plugins/seed-xc-sparql/pom.xml

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
3+
<modelVersion>4.0.0</modelVersion>
4+
<parent>
5+
<groupId>de.ulbms.scdh.seed.xc</groupId>
6+
<artifactId>seed-xc-plugins</artifactId>
7+
<version>${revision}${changelist}</version>
8+
</parent>
9+
10+
<artifactId>seed-xc-sparql</artifactId>
11+
<name>SEED XC SPARQL</name>
12+
<description>A plugin for transformations build from SPARQL queries</description>
13+
14+
<properties>
15+
<maven.compiler.source>21</maven.compiler.source>
16+
<maven.compiler.target>21</maven.compiler.target>
17+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
18+
<jena.version>6.0.0</jena.version>
19+
</properties>
20+
21+
<dependencies>
22+
<dependency>
23+
<groupId>${project.groupId}</groupId>
24+
<artifactId>seed-xc-api</artifactId>
25+
<version>${revision}${changelist}</version>
26+
</dependency>
27+
<dependency>
28+
<groupId>${project.groupId}</groupId>
29+
<artifactId>seed-resource-providers</artifactId>
30+
<version>${revision}${changelist}</version>
31+
<scope>test</scope>
32+
</dependency>
33+
<dependency>
34+
<groupId>org.apache.jena</groupId>
35+
<artifactId>jena-arq</artifactId>
36+
<version>${jena.version}</version>
37+
</dependency>
38+
<dependency>
39+
<groupId>org.slf4j</groupId>
40+
<artifactId>slf4j-api</artifactId>
41+
<version>${slf4j.version}</version>
42+
</dependency>
43+
<dependency>
44+
<groupId>io.quarkus</groupId>
45+
<artifactId>quarkus-junit5</artifactId>
46+
<scope>test</scope>
47+
</dependency>
48+
<dependency>
49+
<groupId>io.quarkus</groupId>
50+
<artifactId>quarkus-junit5-mockito</artifactId>
51+
<scope>test</scope>
52+
</dependency>
53+
</dependencies>
54+
55+
<build>
56+
<plugins>
57+
58+
<!-- index the beans in this module, see
59+
https://quarkus.io/guides/cdi-reference#how-to-generate-a-jandex-index
60+
-->
61+
<plugin>
62+
<groupId>io.smallrye</groupId>
63+
<artifactId>jandex-maven-plugin</artifactId>
64+
<version>${smallrye-jandex-plugin.version}</version>
65+
<executions>
66+
<execution>
67+
<id>make-index</id>
68+
<goals>
69+
<goal>jandex</goal>
70+
</goals>
71+
</execution>
72+
</executions>
73+
</plugin>
74+
75+
<plugin>
76+
<groupId>org.apache.maven.plugins</groupId>
77+
<artifactId>maven-compiler-plugin</artifactId>
78+
</plugin>
79+
80+
<!-- flatten the pom -->
81+
<plugin>
82+
<groupId>org.codehaus.mojo</groupId>
83+
<artifactId>flatten-maven-plugin</artifactId>
84+
<version>1.5.0</version>
85+
<configuration/>
86+
<executions>
87+
<!-- enable flattening -->
88+
<execution>
89+
<id>flatten</id>
90+
<goals>
91+
<goal>flatten</goal>
92+
</goals>
93+
<phase>process-resources</phase>
94+
</execution>
95+
<!-- ensure proper cleanup -->
96+
<execution>
97+
<id>flatten.clean</id>
98+
<goals>
99+
<goal>clean</goal>
100+
</goals>
101+
<phase>clean</phase>
102+
</execution>
103+
</executions>
104+
</plugin>
105+
106+
</plugins>
107+
</build>
108+
109+
</project>
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package de.ulbms.scdh.seed.xc.jena;
2+
3+
import de.ulbms.scdh.seed.xc.api.TransformationPreparationException;
4+
import jakarta.enterprise.context.ApplicationScoped;
5+
import java.text.ParseException;
6+
import java.text.SimpleDateFormat;
7+
import java.util.Calendar;
8+
import org.apache.jena.query.ParameterizedSparqlString;
9+
import org.slf4j.Logger;
10+
import org.slf4j.LoggerFactory;
11+
12+
/**
13+
* The {@link ParameterConverter} class casts strings to Java objects based on a xs-type.
14+
*/
15+
@ApplicationScoped
16+
public class ParameterConverter {
17+
18+
private static final Logger LOG = LoggerFactory.getLogger(ParameterConverter.class);
19+
20+
/**
21+
* Sets the variable with <code>name</code> in the supplied query.
22+
*
23+
* @param name - Name of the SPARQL variable
24+
* @param value - Value to be set
25+
* @param type - type information
26+
* @param query - the parametrized query
27+
* @throws TransformationPreparationException - on a cast failure
28+
*/
29+
public void setQueryParameter(String name, String value, String type, ParameterizedSparqlString query)
30+
throws TransformationPreparationException {
31+
switch (type) {
32+
case "xs:anyURI" -> query.setIri(name, value);
33+
case "xs:string" -> query.setLiteral(name, value);
34+
case "xs:integer" -> {
35+
try {
36+
query.setLiteral(name, Integer.parseInt(value));
37+
} catch (NumberFormatException e) {
38+
LOG.error("failed to cast '{}' value of parameter {} to integer", value, name);
39+
throw new TransformationPreparationException("failed to set parameter " + name, e);
40+
}
41+
}
42+
case "xs:long" -> {
43+
try {
44+
query.setLiteral(name, Long.parseLong(value));
45+
} catch (NumberFormatException e) {
46+
LOG.error("failed to cast '{}' value of parameter {} to long", value, name);
47+
throw new TransformationPreparationException("failed to set parameter " + name, e);
48+
}
49+
}
50+
case "xs:float" -> {
51+
try {
52+
query.setLiteral(name, Float.parseFloat(value));
53+
} catch (NumberFormatException e) {
54+
LOG.error("failed to cast '{}' value of parameter {} to float", value, name);
55+
throw new TransformationPreparationException("failed to set parameter " + name, e);
56+
}
57+
}
58+
case "xs:double" -> {
59+
try {
60+
query.setLiteral(name, Double.parseDouble(value));
61+
} catch (NumberFormatException e) {
62+
LOG.error("failed to cast '{}' value of parameter {} to double", value, name);
63+
throw new TransformationPreparationException("failed to set parameter " + name, e);
64+
}
65+
}
66+
case "xs:date" -> {
67+
try {
68+
Calendar calendar = Calendar.getInstance();
69+
SimpleDateFormat sdf = new SimpleDateFormat();
70+
calendar.setTime(sdf.parse(value));
71+
query.setLiteral(name, calendar);
72+
} catch (ParseException e) {
73+
LOG.error("failed to cast '{}' value of parameter {} to calendar", value, name);
74+
throw new TransformationPreparationException("failed to set parameter " + name, e);
75+
}
76+
}
77+
default -> {
78+
// defaults to string again
79+
LOG.error("no valid type information for parameter {}: {}. Using string", name, type);
80+
query.setLiteral(name, value);
81+
}
82+
}
83+
}
84+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package de.ulbms.scdh.seed.xc.jena;
2+
3+
import de.ulbms.scdh.seed.xc.api.TransformationPreparationException;
4+
import io.vertx.core.http.HttpHeaders;
5+
import io.vertx.core.http.HttpServerRequest;
6+
import jakarta.enterprise.context.ApplicationScoped;
7+
import org.apache.jena.riot.Lang;
8+
import org.apache.jena.riot.RDFFormat;
9+
import org.apache.jena.riot.RDFLanguages;
10+
11+
/**
12+
* The {@link Serializer} adds sufficient information for getting an RDF output format that works.
13+
* This includes choosing an encoding variant.
14+
*/
15+
@ApplicationScoped
16+
public class Serializer {
17+
18+
public static final Lang DEFAULT = Lang.N3;
19+
20+
/**
21+
* This implementation prefers the content type declared for the transformation (first argument)
22+
* over the accept header of the request. If none is given, the content type is guessed from the
23+
* processed file extension. <code>DEFAULT</code> is returned as fallback.
24+
*
25+
* @param transformationContentType - the content type declared for the transformation
26+
* @param systemId - the name of the request file
27+
* @param request - HTTP request with accept headers
28+
* @return the RDF content type
29+
*/
30+
public static RDFFormat getFormat(String transformationContentType, String systemId, HttpServerRequest request)
31+
throws TransformationPreparationException {
32+
try {
33+
if (transformationContentType != null) {
34+
Lang lang = RDFLanguages.contentTypeToLang(transformationContentType);
35+
return getFormatVariant(lang, "utf-8");
36+
} else if (request != null & request.getHeader(HttpHeaders.ACCEPT) != null) {
37+
Lang lang = RDFLanguages.contentTypeToLang(request.getHeader(HttpHeaders.ACCEPT));
38+
return getFormatVariant(lang, request.getHeader(HttpHeaders.ACCEPT_CHARSET));
39+
} else {
40+
return new RDFFormat(RDFLanguages.filenameToLang(systemId, DEFAULT));
41+
}
42+
} catch (Exception e) {
43+
throw new TransformationPreparationException(
44+
"unknown RDF format: " + transformationContentType + " " + request.getHeader(HttpHeaders.ACCEPT));
45+
}
46+
}
47+
48+
/**
49+
* This adds missing information to get a format variant.
50+
*
51+
* @param lang - the basic format as {@link Lang}
52+
* @param charset - the charset requested
53+
* @return - the fully specified format for which a formatter exists
54+
*/
55+
protected static RDFFormat getFormatVariant(Lang lang, String charset) {
56+
if (lang.equals(Lang.NTRIPLES)) {
57+
return new RDFFormat(lang, RDFFormat.UTF8);
58+
} else if (lang.equals(Lang.TTL)) {
59+
return new RDFFormat(lang, RDFFormat.PRETTY);
60+
} else if (lang.equals(Lang.RDFXML)) {
61+
return new RDFFormat(lang, RDFFormat.PLAIN);
62+
} else if (lang.equals(Lang.JSONLD)) {
63+
return RDFFormat.JSONLD11_PRETTY;
64+
} else {
65+
return new RDFFormat(lang);
66+
}
67+
}
68+
}

0 commit comments

Comments
 (0)