Skip to content

Commit

Permalink
Merge pull request #172 from kbss-cvut/rdf4j-update-module
Browse files Browse the repository at this point in the history
Rdf4j update module
  • Loading branch information
blcham authored Jul 23, 2023
2 parents d039b9a + 82fb232 commit 0e0aa90
Show file tree
Hide file tree
Showing 14 changed files with 577 additions and 2 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ SPipes loads pipelines by recursive traversal of configured directories, searchi
SPipes script construction, execution, and execution history tracking is explained
in [Hello world example](doc/examples/hello-world/hello-world.md).
Script debugging is explained in [skosify example](doc/examples/skosify/skosify.md).
Working with RDF4J repository is explained in [rdf4j example](doc/examples/rdf4j-update/rdf4j-update.md).
Constraint validation is described in [constraint validation example](doc/examples/constraint-validation/constraint-validation.md).


Expand Down
71 changes: 71 additions & 0 deletions doc/examples/rdf4j-update/rdf4j-update.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# RDF4J update example

RDF4J update example explains how to update RDF4J repository.

## Introduction

This example explains script [rdf4j-update.sms.ttl](./rdf4j-update.sms.ttl]) that contain one pipeline. It creates RDF4J repository and performs update query. For more details about script construction and execution you can see [hello-world-example](../hello-world/hello-world.md).

## Script structure

Script performs following steps:
1) Creates RDF4J repository using `kbss:rdf4j-create-repository` module. Repository is specified by parameters `rdf4j:p-rdf4j-server-url` and `rdf4j:p-rdf4j-repository-name`. Parameter `rdf4j:p-rdf4j-ignore-if-exists` defines behavior of the module in case when defined repository already exists:
in case if it is set to be "true", module will not do anything if repository with given ID already exists on the server.


:create-repository
a kbss:rdf4j-create-repository ;
rdf4j:p-rdf4j-server-url "http://localhost:8080/rdf4j-server/" ;
rdf4j:p-rdf4j-repository-name "test-update" ;
rdf4j:p-rdf4j-ignore-if-exists "true" ;
sm:next :update ;
.

2) Perform an update on repository using `kbss:rdf4j-update` module. Repository is defined in the same way as in `:create-repository`. Update query is set by string in `sp:text` section.


:update-repository
a kbss:rdf4j-update ;
sm:next :update-repository_Return ;
sml:updateQuery [
a sp:Update ;
sp:text """
PREFIX ex-people: <http://example.org/people/>
DELETE {
ex-people:john ex-people:age ?oldAge .
}
INSERT {
ex-people:john ex-people:age ?newAge .
} WHERE {
OPTIONAL {
ex-people:john ex-people:age ?oldAge .
}
BIND(COALESCE(?oldAge+1, 1) as ?newAge)
}
""" ;
];
rdf4j:p-rdf4j-server-url "http://localhost:8080/rdf4j-server/" ;
rdf4j:p-rdf4j-repository-name "test-update" ;
.

## Script execution

Let's assume that SPipes web application is running at `http://localhost:8080/s-pipes`. We can call the *pipeline* with:

http://localhost:8080/s-pipes/service?_pId=update-repository

Note, that rdf4j server should be running on the URL that is specified in script (`http://localhost:8080/rdf4j-server/` for example).
You can see following logs while execution:

[http-nio-8080-exec-7] INFO c.c.s.e.ExecutionEngineImpl - ##### create-repository
...
[http-nio-8080-exec-1] INFO c.c.s.m.Rdf4jUpdateModule - Server url: http://localhost:8080/rdf4j-server/, Repsitory name: test-update, Ignore if repository exist: true
[http-nio-8080-exec-6] INFO c.c.s.m.Rdf4jUpdateModule - Repository "test-update" already exists
...
[http-nio-8080-exec-7] INFO c.c.s.e.ExecutionEngineImpl - ##### Make insert update
...
[http-nio-8080-exec-7] DEBUG c.c.s.m.Rdf4jUpdateModule - Connected to test-update
[http-nio-8080-exec-7] DEBUG c.c.s.m.Rdf4jUpdateModule - Update successful

This log will occur when Ignore flag is set to true and repository already exists:
`[http-nio-8080-exec-6] INFO c.c.s.m.Rdf4jUpdateModule - Repository "test-update" already exists`
69 changes: 69 additions & 0 deletions doc/examples/rdf4j-update/rdf4j-update.sms.ttl
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# baseURI: http://onto.fel.cvut.cz/ontologies/s-pipes/rdf-update-example
# imports: http://onto.fel.cvut.cz/ontologies/s-pipes-lib

@prefix : <http://onto.fel.cvut.cz/ontologies/s-pipes/rdf-update-example/> .
@prefix doc: <http://onto.fel.cvut.cz/ontologies/documentation/> .
@prefix kbss: <http://onto.fel.cvut.cz/ontologies/lib/module/> .
@prefix km-sesame: <http://onto.fel.cvut.cz/ontologies/lib/module/sesame/> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix sm: <http://topbraid.org/sparqlmotion#> .
@prefix sml: <http://topbraid.org/sparqlmotionlib#> .
@prefix sp: <http://spinrdf.org/sp#> .
@prefix spif: <http://spinrdf.org/spif#> .
@prefix spin: <http://spinrdf.org/spin#> .
@prefix spl: <http://spinrdf.org/spl#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix rdf4j: <http://onto.fel.cvut.cz/ontologies/lib/module/rdf4j/> .

<http://onto.fel.cvut.cz/ontologies/s-pipes/rdf-update-example>
a owl:Ontology ;
owl:imports <http://onto.fel.cvut.cz/ontologies/s-pipes-lib> ;
.

:create-repository
a kbss:rdf4j-create-repository ;
rdf4j:p-rdf4j-server-url "http://localhost:8080/rdf4j-server/" ;
rdf4j:p-rdf4j-repository-name "test-update" ;
rdf4j:p-rdf4j-ignore-if-exists "true" ;
sm:next :update-repository ;
.

:update-repository
a kbss:rdf4j-update ;
sm:next :update-repository_Return ;
sml:updateQuery [
a sp:Update ;
sp:text """
PREFIX ex-people: <http://example.org/people/>
DELETE {
ex-people:john ex-people:age ?oldAge .
}
INSERT {
ex-people:john ex-people:age ?newAge .
} WHERE {
OPTIONAL {
ex-people:john ex-people:age ?oldAge .
}
BIND(COALESCE(?oldAge+1, 1) as ?newAge)
}
""" ;
];
kbss:has-max-iteration-count 5 ;
kbss:only-if-triple-count-changes false;
rdf4j:p-rdf4j-server-url "http://localhost:8080/rdf4j-server/" ;
rdf4j:p-rdf4j-repository-name "test-update" ;
.

:update-repository_Return
a sml:ReturnRDF ;
sml:serialization sml:JSONLD ;
rdfs:label "Update repository" ;
.

:update-repository
a sm:Function ;
sm:returnModule :update-repository_Return ;
rdfs:subClassOf sm:Functions ;
.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ protected static final Property property(String local )
public static final Property is_parse_text = property("is-parse-text");
public static final Property has_max_iteration_count = property("has-max-iteration-count");
public static final Property has_resource_uri = property("has-resource-uri");
public static final Property stop_iteration_on_stable_triple_count = property("stop-iteration-on-stable-triple-count");

/**
returns the URI for this schema
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ protected static final Property property(String local )
public static final Property constructQuery = property("constructQuery");
public static final Property value = property("value");
public static final Property selectQuery = property("selectQuery");
public static final Property updateQuery = property("updateQuery");
public static final Property sourceFilePath = property("sourceFilePath");
public static final Property url = property("url");
public static final Property targetFilePath = property("targetFilePath");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package cz.cvut.spipes.exception;

/**
* Indicate that SPipes Module was incorrectly configured.
*/
public class ModuleConfigurationInconsistentException extends RuntimeException {

public ModuleConfigurationInconsistentException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import org.apache.jena.ontology.OntModel;
import org.apache.jena.ontology.OntModelSpec;
import org.apache.jena.rdf.model.ModelFactory;
import org.apache.jena.shared.PrefixMapping;
import org.apache.jena.util.FileManager;
import org.apache.jena.util.FileUtils;
import org.apache.jena.vocabulary.OWL;
Expand Down Expand Up @@ -52,7 +51,7 @@ void setUp() {
}

@Test
void testExecuteSelf() {
void executeSelfReturnPrefixes() {
given(ontoDocManager.getRegisteredOntologyUris()).willReturn(uri2ontModel.keySet());
uri2ontModel.forEach((key, value) -> {
doReturn(value).when(ontoDocManager).getOntology(key);
Expand Down
10 changes: 10 additions & 0 deletions s-pipes-modules/module-rdf4j/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,16 @@
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>

<!-- TODO needed -->
<!--<dependency>-->
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package cz.cvut.spipes.exceptions;

public class RepositoryAccessException extends RuntimeException {

public RepositoryAccessException(String repositoryName, Throwable cause) {
super("Cannot connect to repository " + repositoryName + ".", cause);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package cz.cvut.spipes.exceptions;

public class RepositoryAlreadyExistsException extends RuntimeException {

public RepositoryAlreadyExistsException(String repositoryName) {
super("Repository " + repositoryName + " already exists.");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package cz.cvut.spipes.modules;

import cz.cvut.spipes.constants.KBSS_MODULE;
import cz.cvut.spipes.engine.ExecutionContext;
import cz.cvut.spipes.exceptions.RepositoryAlreadyExistsException;
import org.apache.jena.rdf.model.Property;
import org.apache.jena.rdf.model.ResourceFactory;
import org.eclipse.rdf4j.repository.config.RepositoryConfig;
import org.eclipse.rdf4j.repository.manager.RemoteRepositoryManager;
import org.eclipse.rdf4j.repository.manager.RepositoryManager;
import org.eclipse.rdf4j.repository.sail.config.SailRepositoryConfig;
import org.eclipse.rdf4j.sail.nativerdf.config.NativeStoreConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Objects;

/**
* Module creates native store rdf4j repository on the given server with the given name
*/
public class Rdf4jCreateRepositoryModule extends AbstractModule {
private static final Logger LOG = LoggerFactory.getLogger(Rdf4jUpdateModule.class.getName());
private static final String TYPE_URI = KBSS_MODULE.getURI() + "rdf4j-create-repository";
private static final String PROPERTY_PREFIX_URI = KBSS_MODULE.getURI() + "rdf4j";

/**
* URL of the Rdf4j server
*/
static final Property P_RDF4J_SERVER_URL = getParameter("p-rdf4j-server-url");
private String rdf4jServerURL;

/**
* Rdf4j repository ID
*/
static final Property P_RDF4J_REPOSITORY_NAME = getParameter("p-rdf4j-repository-name");
private String rdf4jRepositoryName;

/**
* Don't try to create new repository if it already exists (Default value is false)
*/
static final Property P_RDF4J_IGNORE_IF_EXISTS = getParameter("p-rdf4j-ignore-if-exists");
private boolean rdf4jIgnoreIfExists;

private RepositoryManager repositoryManager;

public String getRdf4jServerURL() {
return rdf4jServerURL;
}

public void setRdf4jServerURL(String rdf4jServerURL) {
this.rdf4jServerURL = rdf4jServerURL;
}

public String getRdf4jRepositoryName() {
return rdf4jRepositoryName;
}

public void setRdf4jRepositoryName(String rdf4jRepositoryName) {
this.rdf4jRepositoryName = rdf4jRepositoryName;
}

public boolean isRdf4jIgnoreIfExists() {
return rdf4jIgnoreIfExists;
}

public void setRdf4jIgnoreIfExists(boolean rdf4jIgnoreIfExists) {
this.rdf4jIgnoreIfExists = rdf4jIgnoreIfExists;
}

private static Property getParameter(final String name) {
return ResourceFactory.createProperty(PROPERTY_PREFIX_URI + "/" + name);
}

void setRepositoryManager(RepositoryManager repositoryManager) {
this.repositoryManager = repositoryManager;
}

@Override
ExecutionContext executeSelf() {
NativeStoreConfig nativeStoreConfig = new NativeStoreConfig();
SailRepositoryConfig sailRepositoryConfig = new SailRepositoryConfig(nativeStoreConfig);

repositoryManager.init();
LOG.info("Server url:{}, Repsitory name:{}, Ignore if repository exist:{}.",
rdf4jServerURL,
rdf4jRepositoryName,
rdf4jIgnoreIfExists);

if((!rdf4jIgnoreIfExists) && repositoryManager.hasRepositoryConfig(rdf4jRepositoryName)){

LOG.info("Repository \"{}\" already exists",
rdf4jRepositoryName);
throw new RepositoryAlreadyExistsException(rdf4jRepositoryName);
}

RepositoryConfig repositoryConfig = new RepositoryConfig(rdf4jRepositoryName,sailRepositoryConfig);
repositoryManager.addRepositoryConfig(repositoryConfig);
repositoryManager.getRepository(rdf4jRepositoryName).init();

return executionContext;
}

@Override
public String getTypeURI() {
return TYPE_URI;
}

@Override
public void loadConfiguration() {
rdf4jServerURL = getEffectiveValue(P_RDF4J_SERVER_URL).asLiteral().getString();
rdf4jRepositoryName = getEffectiveValue(P_RDF4J_REPOSITORY_NAME).asLiteral().getString();
try {
rdf4jIgnoreIfExists = (Objects.equals(getEffectiveValue(P_RDF4J_IGNORE_IF_EXISTS).asLiteral().getString(), "true"));
}
catch (NullPointerException e){
rdf4jIgnoreIfExists = false;
}
repositoryManager = new RemoteRepositoryManager(rdf4jServerURL);
}
}
Loading

0 comments on commit 0e0aa90

Please sign in to comment.