TransactionalIsolationTest.java
/*******************************************************************************
* Copyright (c) 2019 Eclipse RDF4J contributors.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Distribution License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*******************************************************************************/
package org.eclipse.rdf4j.sail.shacl;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import java.io.IOException;
import java.io.StringReader;
import java.util.List;
import org.eclipse.rdf4j.common.iteration.Iterations;
import org.eclipse.rdf4j.common.transaction.IsolationLevels;
import org.eclipse.rdf4j.model.IRI;
import org.eclipse.rdf4j.model.Model;
import org.eclipse.rdf4j.model.Resource;
import org.eclipse.rdf4j.model.Statement;
import org.eclipse.rdf4j.model.util.Values;
import org.eclipse.rdf4j.model.vocabulary.RDF4J;
import org.eclipse.rdf4j.query.Update;
import org.eclipse.rdf4j.repository.RepositoryException;
import org.eclipse.rdf4j.repository.sail.SailRepository;
import org.eclipse.rdf4j.repository.sail.SailRepositoryConnection;
import org.eclipse.rdf4j.rio.RDFFormat;
import org.eclipse.rdf4j.rio.Rio;
import org.eclipse.rdf4j.sail.memory.MemoryStore;
import org.eclipse.rdf4j.sail.shacl.results.ValidationReport;
import org.junit.jupiter.api.Test;
public class TransactionalIsolationTest {
@Test
public void testUpdatingShapes() throws IOException {
ShaclSail shaclSail = new ShaclSail(new MemoryStore());
SailRepository sailRepository = new SailRepository(shaclSail);
sailRepository.init();
try (SailRepositoryConnection connection = sailRepository.getConnection()) {
connection.begin();
StringReader shaclRules = new StringReader(String.join("\n", "",
"@prefix ex: <http://example.com/ns#> .",
"@prefix sh: <http://www.w3.org/ns/shacl#> .",
"@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .",
"@prefix foaf: <http://xmlns.com/foaf/0.1/>.",
"ex:PersonShape",
" a sh:NodeShape ;",
" sh:targetClass ex:Person ;",
" sh:property [",
" sh:path ex:age ;",
" sh:datatype xsd:integer ;",
" ] ."));
connection.add(shaclRules, "", RDFFormat.TRIG, RDF4J.SHACL_SHAPE_GRAPH);
connection.commit();
add(connection, "ex:pete a ex:Person .");
StringReader extraShaclRules = new StringReader(String.join("\n", "",
"@prefix ex: <http://example.com/ns#> .",
"@prefix sh: <http://www.w3.org/ns/shacl#> .",
"@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .",
"@prefix foaf: <http://xmlns.com/foaf/0.1/>.",
"ex:PersonShape",
" a sh:NodeShape ;",
" sh:targetClass ex:Person ;",
" sh:property [",
" sh:path ex:birthyear ;",
" sh:datatype xsd:integer ;",
" ] ."));
connection.add(extraShaclRules, "", RDFFormat.TRIG, RDF4J.SHACL_SHAPE_GRAPH);
} finally {
sailRepository.shutDown();
}
}
@Test
public void testUpdatingShapesViolation() throws Throwable {
ShaclSail shaclSail = new ShaclSail(new MemoryStore());
SailRepository sailRepository = new SailRepository(shaclSail);
sailRepository.init();
try (SailRepositoryConnection connection = sailRepository.getConnection()) {
connection.begin();
StringReader shaclRules = new StringReader(String.join("\n", "",
"@prefix ex: <http://example.com/ns#> .",
"@prefix sh: <http://www.w3.org/ns/shacl#> .",
"@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .",
"@prefix foaf: <http://xmlns.com/foaf/0.1/>.",
"ex:PersonShape",
" a sh:NodeShape ;",
" sh:targetClass ex:Person ;",
" sh:property [",
" sh:path ex:age ;",
" sh:datatype xsd:integer ;",
" ] ."));
connection.add(shaclRules, "", RDFFormat.TRIG, RDF4J.SHACL_SHAPE_GRAPH);
connection.commit();
add(connection, "ex:pete a ex:Person .");
StringReader extraShaclRules = new StringReader(String.join("\n", "",
"@prefix ex: <http://example.com/ns#> .",
"@prefix sh: <http://www.w3.org/ns/shacl#> .",
"@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .",
"@prefix foaf: <http://xmlns.com/foaf/0.1/>.",
"ex:PersonShape",
" a sh:NodeShape ;",
" sh:targetClass ex:Person ;",
" sh:property [",
" sh:path ex:age ;",
" sh:minCount 1 ;",
" ] ."));
try {
connection.add(extraShaclRules, "", RDFFormat.TRIG, RDF4J.SHACL_SHAPE_GRAPH);
} catch (RepositoryException e) {
assertThat(e.getCause()).isInstanceOf(ShaclSailValidationException.class);
}
} finally {
sailRepository.shutDown();
}
}
@Test
public void testAddingShapesAfterData() throws Throwable {
ShaclSail shaclSail = new ShaclSail(new MemoryStore());
SailRepository sailRepository = new SailRepository(shaclSail);
try (SailRepositoryConnection connection = sailRepository.getConnection()) {
add(connection, "ex:pete a ex:Person .");
connection.begin();
StringReader shaclRules = new StringReader(String.join("\n", "",
"@prefix ex: <http://example.com/ns#> .",
"@prefix sh: <http://www.w3.org/ns/shacl#> .",
"@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .",
"@prefix foaf: <http://xmlns.com/foaf/0.1/>.",
"ex:PersonShape",
" a sh:NodeShape ;",
" sh:targetClass ex:Person ;",
" sh:property [",
" sh:path ex:age ;",
" sh:minCount 1 ;",
" ] ."));
connection.add(shaclRules, "", RDFFormat.TRIG, RDF4J.SHACL_SHAPE_GRAPH);
try {
connection.commit();
} catch (RepositoryException e) {
assertThat(e.getCause()).isInstanceOf(ShaclSailValidationException.class);
}
} finally {
sailRepository.shutDown();
}
}
@Test
public void testAddingShapesThatFails() throws Throwable {
ShaclSail shaclSail = new ShaclSail(new MemoryStore());
SailRepository sailRepository = new SailRepository(shaclSail);
sailRepository.init();
SailRepositoryConnection connection1 = sailRepository.getConnection();
SailRepositoryConnection connection2 = sailRepository.getConnection();
add(connection1, "ex:pete a ex:Person .");
connection2.begin();
StringReader shaclRules = new StringReader(String.join("\n", "",
"@prefix ex: <http://example.com/ns#> .",
"@prefix sh: <http://www.w3.org/ns/shacl#> .",
"@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .",
"@prefix foaf: <http://xmlns.com/foaf/0.1/>.",
"ex:PersonShape",
" a sh:NodeShape ;",
" sh:targetClass ex:Person ;",
" sh:property [",
" sh:path ex:age ;",
" sh:minCount 1 ;",
" ] ."));
connection2.add(shaclRules, "", RDFFormat.TRIG, RDF4J.SHACL_SHAPE_GRAPH);
try {
connection2.commit();
} catch (Throwable ignored) {
}
connection2.rollback();
add(connection1, "ex:steve a ex:Person .");
connection2.close();
connection1.close();
sailRepository.shutDown();
}
@Test
public void testViolation() throws Throwable {
ShaclSail shaclSail = new ShaclSail(new MemoryStore());
SailRepository sailRepository = new SailRepository(shaclSail);
sailRepository.init();
try (
SailRepositoryConnection connection1 = sailRepository.getConnection();
SailRepositoryConnection connection2 = sailRepository.getConnection()) {
connection2.begin();
StringReader shaclRules = new StringReader(String.join("\n", "",
"@prefix ex: <http://example.com/ns#> .",
"@prefix sh: <http://www.w3.org/ns/shacl#> .",
"@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .",
"@prefix foaf: <http://xmlns.com/foaf/0.1/>.",
"ex:PersonShape",
" a sh:NodeShape ;",
" sh:targetClass ex:Person ;",
" sh:property [",
" sh:path ex:age ;",
" sh:minCount 1 ;",
" ] ."));
connection2.add(shaclRules, "", RDFFormat.TRIG, RDF4J.SHACL_SHAPE_GRAPH);
connection2.commit();
try {
add(connection1, "ex:steve a ex:Person .");
fail();
} catch (RepositoryException e) {
assertThat(e.getCause()).isInstanceOf(ShaclSailValidationException.class);
}
} finally {
sailRepository.shutDown();
}
}
@Test
public void testIsolation() throws Throwable {
ShaclSail shaclSail = new ShaclSail(new MemoryStore());
shaclSail.setSerializableValidation(true);
SailRepository sailRepository = new SailRepository(shaclSail);
SailRepositoryConnection connection1 = sailRepository.getConnection();
SailRepositoryConnection connection2 = sailRepository.getConnection();
connection2.begin();
StringReader shaclRules = new StringReader(String.join("\n", "",
"@prefix ex: <http://example.com/ns#> .",
"@prefix sh: <http://www.w3.org/ns/shacl#> .",
"@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .",
"@prefix foaf: <http://xmlns.com/foaf/0.1/>.",
"ex:PersonShape",
" a sh:NodeShape ;",
" sh:targetClass ex:Person ;",
" sh:property [",
" sh:path ex:age ;",
" sh:minCount 1 ;",
" ] ."));
connection2.add(shaclRules, "", RDFFormat.TRIG, RDF4J.SHACL_SHAPE_GRAPH);
connection1.begin();
connection2.commit();
addInTransaction(connection1, "ex:steve a ex:Person .");
try {
connection1.commit();
} catch (Throwable ignored) {
}
connection2.close();
connection1.close();
try (SailRepositoryConnection connection = sailRepository.getConnection()) {
connection.begin();
ValidationReport validationReport = ((ShaclSailConnection) connection.getSailConnection()).revalidate();
assertTrue(validationReport.conforms());
connection.commit();
}
sailRepository.shutDown();
}
@Test
public void testIsolation_2() throws Throwable {
ShaclSail shaclSail = new ShaclSail(new MemoryStore());
shaclSail.setSerializableValidation(true);
SailRepository sailRepository = new SailRepository(shaclSail);
sailRepository.init();
SailRepositoryConnection connection1 = sailRepository.getConnection();
SailRepositoryConnection connection2 = sailRepository.getConnection();
connection2.begin(IsolationLevels.SERIALIZABLE);
StringReader shaclRules = new StringReader(String.join("\n", "",
"@prefix ex: <http://example.com/ns#> .",
"@prefix sh: <http://www.w3.org/ns/shacl#> .",
"@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .",
"@prefix foaf: <http://xmlns.com/foaf/0.1/>.",
"ex:PersonShape",
" a sh:NodeShape ;",
" sh:targetClass ex:Person ;",
" sh:property [",
" sh:path ex:age ;",
" sh:minCount 1 ;",
" ] ."));
connection2.add(shaclRules, "", RDFFormat.TRIG, RDF4J.SHACL_SHAPE_GRAPH);
connection1.begin(IsolationLevels.SERIALIZABLE);
connection2.commit();
addInTransaction(connection1, "ex:steve a ex:Person .");
try {
connection1.commit();
} catch (Throwable ignored) {
}
connection2.close();
connection1.close();
try (SailRepositoryConnection connection = sailRepository.getConnection()) {
connection.begin();
ValidationReport validationReport = ((ShaclSailConnection) connection.getSailConnection()).revalidate();
assertTrue(validationReport.conforms());
connection.commit();
}
sailRepository.shutDown();
}
@Test
public void testIsolation2() throws Throwable {
ShaclSail shaclSail = new ShaclSail(new MemoryStore());
shaclSail.setSerializableValidation(true);
SailRepository sailRepository = new SailRepository(shaclSail);
sailRepository.init();
SailRepositoryConnection connection1 = sailRepository.getConnection();
SailRepositoryConnection connection2 = sailRepository.getConnection();
connection1.begin(IsolationLevels.SNAPSHOT);
addInTransaction(connection1, "ex:steve a ex:Person .");
connection2.begin(IsolationLevels.SNAPSHOT);
StringReader shaclRules = new StringReader(String.join("\n", "",
"@prefix ex: <http://example.com/ns#> .",
"@prefix sh: <http://www.w3.org/ns/shacl#> .",
"@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .",
"@prefix foaf: <http://xmlns.com/foaf/0.1/>.",
"ex:PersonShape",
" a sh:NodeShape ;",
" sh:targetClass ex:Person ;",
" sh:property [",
" sh:path ex:age ;",
" sh:minCount 1 ;",
" ] ."));
connection2.add(shaclRules, "", RDFFormat.TRIG, RDF4J.SHACL_SHAPE_GRAPH);
connection1.commit();
try {
connection2.commit();
} catch (Throwable ignored) {
}
connection2.close();
connection1.close();
try (SailRepositoryConnection connection = sailRepository.getConnection()) {
connection.begin();
ValidationReport validationReport = ((ShaclSailConnection) connection.getSailConnection()).revalidate();
assertTrue(validationReport.conforms());
connection.commit();
}
sailRepository.shutDown();
}
@Test
public void testIsolation5() throws Throwable {
ShaclSail shaclSail = new ShaclSail(new MemoryStore());
shaclSail.setSerializableValidation(true);
SailRepository sailRepository = new SailRepository(shaclSail);
try (SailRepositoryConnection connection = sailRepository.getConnection()) {
StringReader shaclRules = new StringReader(String.join("\n", "",
"@prefix ex: <http://example.com/ns#> .",
"@prefix sh: <http://www.w3.org/ns/shacl#> .",
"@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .",
"@prefix foaf: <http://xmlns.com/foaf/0.1/>.",
"ex:PersonShape",
" a sh:NodeShape ;",
" sh:targetClass ex:Person ;",
" sh:property [",
" sh:path ex:age ;",
" sh:minCount 1 ;",
" ] ."));
connection.add(shaclRules, "", RDFFormat.TRIG, RDF4J.SHACL_SHAPE_GRAPH);
add(connection, "ex:steve ex:age 1 .");
}
SailRepositoryConnection connection1 = sailRepository.getConnection();
SailRepositoryConnection connection2 = sailRepository.getConnection();
connection1.begin(IsolationLevels.SNAPSHOT);
addInTransaction(connection1, "ex:steve a ex:Person .");
connection2.begin(IsolationLevels.SNAPSHOT);
removeInTransaction(connection2, "ex:steve ex:age 1 .");
connection1.commit();
try {
connection2.commit();
} catch (Throwable ignored) {
}
connection2.close();
connection1.close();
try (SailRepositoryConnection connection = sailRepository.getConnection()) {
connection.begin();
ValidationReport validationReport = ((ShaclSailConnection) connection.getSailConnection()).revalidate();
assertTrue(validationReport.conforms());
connection.commit();
}
sailRepository.shutDown();
}
@Test
public void testIsolation2_1() throws Throwable {
ShaclSail shaclSail = new ShaclSail(new MemoryStore());
shaclSail.setSerializableValidation(true);
SailRepository sailRepository = new SailRepository(shaclSail);
sailRepository.init();
SailRepositoryConnection connection1 = sailRepository.getConnection();
SailRepositoryConnection connection2 = sailRepository.getConnection();
connection1.begin(IsolationLevels.SNAPSHOT_READ);
addInTransaction(connection1, "ex:steve a ex:Person .");
connection2.begin(IsolationLevels.SNAPSHOT_READ);
StringReader shaclRules = new StringReader(String.join("\n", "",
"@prefix ex: <http://example.com/ns#> .",
"@prefix sh: <http://www.w3.org/ns/shacl#> .",
"@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .",
"@prefix foaf: <http://xmlns.com/foaf/0.1/>.",
"ex:PersonShape",
" a sh:NodeShape ;",
" sh:targetClass ex:Person ;",
" sh:property [",
" sh:path ex:age ;",
" sh:minCount 1 ;",
" ] ."));
connection2.add(shaclRules, "", RDFFormat.TRIG, RDF4J.SHACL_SHAPE_GRAPH);
connection1.commit();
try {
connection2.commit();
} catch (Throwable ignored) {
}
connection2.close();
connection1.close();
try (SailRepositoryConnection connection = sailRepository.getConnection()) {
connection.begin();
ValidationReport validationReport = ((ShaclSailConnection) connection.getSailConnection()).revalidate();
assertTrue(validationReport.conforms());
connection.commit();
}
sailRepository.shutDown();
}
@Test
public void testIsolation2_2() throws Throwable {
ShaclSail shaclSail = new ShaclSail(new MemoryStore());
shaclSail.setSerializableValidation(true);
SailRepository sailRepository = new SailRepository(shaclSail);
sailRepository.init();
SailRepositoryConnection connection1 = sailRepository.getConnection();
SailRepositoryConnection connection2 = sailRepository.getConnection();
connection1.begin(IsolationLevels.SERIALIZABLE);
addInTransaction(connection1, "ex:steve a ex:Person .");
connection2.begin(IsolationLevels.SERIALIZABLE);
StringReader shaclRules = new StringReader(String.join("\n", "",
"@prefix ex: <http://example.com/ns#> .",
"@prefix sh: <http://www.w3.org/ns/shacl#> .",
"@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .",
"@prefix foaf: <http://xmlns.com/foaf/0.1/>.",
"ex:PersonShape",
" a sh:NodeShape ;",
" sh:targetClass ex:Person ;",
" sh:property [",
" sh:path ex:age ;",
" sh:minCount 1 ;",
" ] ."));
connection2.add(shaclRules, "", RDFFormat.TRIG, RDF4J.SHACL_SHAPE_GRAPH);
connection1.commit();
try {
connection2.commit();
} catch (Throwable ignored) {
}
connection2.close();
connection1.close();
try (SailRepositoryConnection connection = sailRepository.getConnection()) {
connection.begin();
ValidationReport validationReport = ((ShaclSailConnection) connection.getSailConnection()).revalidate();
assertTrue(validationReport.conforms());
connection.commit();
}
sailRepository.shutDown();
}
@Test
public void testIsolation3() throws Throwable {
ShaclSail shaclSail = new ShaclSail(new MemoryStore());
shaclSail.setSerializableValidation(true);
SailRepository sailRepository = new SailRepository(shaclSail);
sailRepository.init();
SailRepositoryConnection connection1 = sailRepository.getConnection();
SailRepositoryConnection connection2 = sailRepository.getConnection();
connection1.begin();
addInTransaction(connection1, "ex:steve a ex:Person .");
connection2.begin();
connection1.commit();
StringReader shaclRules = new StringReader(String.join("\n", "",
"@prefix ex: <http://example.com/ns#> .",
"@prefix sh: <http://www.w3.org/ns/shacl#> .",
"@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .",
"@prefix foaf: <http://xmlns.com/foaf/0.1/>.",
"ex:PersonShape",
" a sh:NodeShape ;",
" sh:targetClass ex:Person ;",
" sh:property [",
" sh:path ex:age ;",
" sh:minCount 1 ;",
" ] ."));
connection2.add(shaclRules, "", RDFFormat.TRIG, RDF4J.SHACL_SHAPE_GRAPH);
try {
connection2.commit();
} catch (Throwable ignored) {
}
connection2.close();
connection1.close();
try (SailRepositoryConnection connection = sailRepository.getConnection()) {
connection.begin();
ValidationReport validationReport = ((ShaclSailConnection) connection.getSailConnection()).revalidate();
assertTrue(validationReport.conforms());
connection.commit();
}
sailRepository.shutDown();
}
@Test
public void testIsolation4() throws Throwable {
ShaclSail shaclSail = new ShaclSail(new MemoryStore());
shaclSail.setSerializableValidation(true);
SailRepository sailRepository = new SailRepository(shaclSail);
sailRepository.init();
SailRepositoryConnection connection1 = sailRepository.getConnection();
SailRepositoryConnection connection2 = sailRepository.getConnection();
connection2.begin();
StringReader shaclRules = new StringReader(String.join("\n", "",
"@prefix ex: <http://example.com/ns#> .",
"@prefix sh: <http://www.w3.org/ns/shacl#> .",
"@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .",
"@prefix foaf: <http://xmlns.com/foaf/0.1/>.",
"ex:PersonShape",
" a sh:NodeShape ;",
" sh:targetClass ex:Person ;",
" sh:property [",
" sh:path ex:age ;",
" sh:minCount 1 ;",
" ] ."));
connection2.add(shaclRules, "", RDFFormat.TRIG, RDF4J.SHACL_SHAPE_GRAPH);
connection1.begin();
addInTransaction(connection1, "ex:steve a ex:Person .");
connection2.commit();
try {
connection1.commit();
} catch (Throwable ignored) {
}
connection2.close();
connection1.close();
try (SailRepositoryConnection connection = sailRepository.getConnection()) {
connection.begin();
ValidationReport validationReport = ((ShaclSailConnection) connection.getSailConnection()).revalidate();
assertTrue(validationReport.conforms());
connection.commit();
}
sailRepository.shutDown();
}
@Test
public void testRollbak() throws Throwable {
ShaclSail shaclSail = new ShaclSail(new MemoryStore());
SailRepository sailRepository = new SailRepository(shaclSail);
sailRepository.init();
SailRepositoryConnection connection1 = sailRepository.getConnection();
SailRepositoryConnection connection2 = sailRepository.getConnection();
connection2.begin();
StringReader shaclRules = new StringReader(String.join("\n", "",
"@prefix ex: <http://example.com/ns#> .",
"@prefix sh: <http://www.w3.org/ns/shacl#> .",
"@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .",
"@prefix foaf: <http://xmlns.com/foaf/0.1/>.",
"ex:PersonShape",
" a sh:NodeShape ;",
" sh:targetClass ex:Person ;",
" sh:property [",
" sh:path ex:age ;",
" sh:minCount 1 ;",
" ] ."));
connection2.add(shaclRules, "", RDFFormat.TRIG, RDF4J.SHACL_SHAPE_GRAPH);
connection2.getSailConnection().prepare();
connection2.rollback();
add(connection1, "ex:steve a ex:Person .");
connection2.close();
connection1.close();
sailRepository.shutDown();
}
@Test()
public void checkForNoExceptionWithEmptyTransaction() {
SailRepository sailRepository = new SailRepository(new ShaclSail(new MemoryStore()));
sailRepository.init();
try (SailRepositoryConnection connection = sailRepository.getConnection()) {
connection.begin();
connection.commit();
}
sailRepository.shutDown();
}
@Test
public void testRemoveShapes() throws IOException {
ShaclSail shaclSail = new ShaclSail(new MemoryStore());
SailRepository sailRepository = new SailRepository(shaclSail);
sailRepository.init();
try (SailRepositoryConnection connection = sailRepository.getConnection()) {
connection.begin();
StringReader shaclRules = new StringReader(String.join("\n", "",
"@prefix ex: <http://example.com/ns#> .",
"@prefix sh: <http://www.w3.org/ns/shacl#> .",
"@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .",
"@prefix foaf: <http://xmlns.com/foaf/0.1/>.",
"ex:PersonShape",
" a sh:NodeShape ;",
" sh:targetClass ex:Person ;",
" sh:property [",
" sh:path ex:age ;",
" sh:minCount 1 ;",
" ] ."));
connection.add(shaclRules, "", RDFFormat.TRIG, RDF4J.SHACL_SHAPE_GRAPH);
connection.commit();
connection.begin();
connection.remove((Resource) null, null, null, RDF4J.SHACL_SHAPE_GRAPH);
connection.commit();
StringReader extraShaclRules = new StringReader(String.join("\n", "",
"@prefix ex: <http://example.com/ns#> .",
"@prefix sh: <http://www.w3.org/ns/shacl#> .",
"@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .",
"@prefix foaf: <http://xmlns.com/foaf/0.1/>.",
"ex:PersonShape",
" a sh:NodeShape ;",
" sh:targetClass ex:Person ;",
" sh:property [",
" sh:path ex:age ;",
" sh:datatype xsd:integer ;",
" ] ."));
connection.add(extraShaclRules, "", RDFFormat.TRIG, RDF4J.SHACL_SHAPE_GRAPH);
add(connection, "ex:pete a ex:Person .");
} finally {
sailRepository.shutDown();
}
}
@Test
public void testUpdateShapesSparql() throws Throwable {
ShaclSail shaclSail = new ShaclSail(new MemoryStore());
SailRepository sailRepository = new SailRepository(shaclSail);
IRI g1 = Values.iri("http://example.com/g1");
try (SailRepositoryConnection connection = sailRepository.getConnection()) {
connection.begin();
StringReader shaclRules = new StringReader(String.join("\n", "",
"@prefix ex: <http://example.com/ns#> .",
"@prefix sh: <http://www.w3.org/ns/shacl#> .",
"@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .",
"@prefix foaf: <http://xmlns.com/foaf/0.1/>.",
"ex:PersonShape",
" a sh:NodeShape ;",
" sh:targetClass ex:Person ;",
" sh:property [",
" sh:path ex:age ;",
" sh:minCount 1 ;",
" ] ."));
connection.add(shaclRules, "", RDFFormat.TURTLE, g1);
connection.commit();
connection.begin();
Update update = connection.prepareUpdate(String.join("\n", "",
"DELETE {",
" graph ?g {",
" ?a ?b ?c .",
" }",
"} WHERE {",
" graph ?g {",
" ?a ?b ?c .",
" }",
"}"));
update.execute();
connection.commit();
add(connection, "ex:pete a ex:Person .");
} finally {
sailRepository.shutDown();
}
}
@Test
public void testReadShapes() throws Throwable {
ShaclSail shaclSail = new ShaclSail(new MemoryStore());
SailRepository sailRepository = new SailRepository(shaclSail);
sailRepository.init();
try (SailRepositoryConnection connection = sailRepository.getConnection()) {
connection.begin();
StringReader shaclRules = new StringReader(String.join("\n", "",
"@prefix ex: <http://example.com/ns#> .",
"@prefix sh: <http://www.w3.org/ns/shacl#> .",
"@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .",
"@prefix foaf: <http://xmlns.com/foaf/0.1/>.",
"ex:PersonShape",
" a sh:NodeShape ;",
" sh:targetClass ex:Person ;",
" sh:property ex:prop .",
"",
"ex:prop sh:path ex:age ;",
" sh:minCount 1 ."));
connection.add(shaclRules, "", RDFFormat.TRIG, RDF4J.SHACL_SHAPE_GRAPH);
connection.commit();
List<Statement> shapesStatements = Iterations
.asList(connection.getStatements(null, null, null, false, RDF4J.SHACL_SHAPE_GRAPH));
List<Statement> shapesStatementsInferred = Iterations
.asList(connection.getStatements(null, null, null, true, RDF4J.SHACL_SHAPE_GRAPH));
List<Statement> allStatements = Iterations.asList(connection.getStatements(null, null, null));
boolean hasShapes = connection.hasStatement(null, null, null, false, RDF4J.SHACL_SHAPE_GRAPH);
boolean hasAnyStatements = connection.hasStatement(null, null, null, false);
assertEquals(5, shapesStatements.size());
assertEquals(5, shapesStatementsInferred.size());
assertEquals(0, allStatements.size());
assertTrue(hasShapes);
assertFalse(hasAnyStatements);
} finally {
sailRepository.shutDown();
}
}
@Test
public void testSerializableValidationAndTransactionalValidationLimit() throws Throwable {
ShaclSail shaclSail = new ShaclSail(new MemoryStore());
shaclSail.setSerializableValidation(true);
shaclSail.setTransactionalValidationLimit(0);
SailRepository sailRepository = new SailRepository(shaclSail);
SailRepositoryConnection connection1 = sailRepository.getConnection();
SailRepositoryConnection connection2 = sailRepository.getConnection();
connection2.begin(IsolationLevels.SNAPSHOT);
StringReader shaclRules = new StringReader(String.join("\n", "",
"@prefix ex: <http://example.com/ns#> .",
"@prefix sh: <http://www.w3.org/ns/shacl#> .",
"@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .",
"@prefix foaf: <http://xmlns.com/foaf/0.1/>.",
"ex:PersonShape",
" a sh:NodeShape ;",
" sh:targetClass ex:Person ;",
" sh:property [",
" sh:path ex:age ;",
" sh:minCount 1 ;",
" ] ."));
connection2.add(shaclRules, "", RDFFormat.TRIG, RDF4J.SHACL_SHAPE_GRAPH);
addInTransaction(connection2, "ex:steve ex:age 1 .");
connection1.begin(IsolationLevels.SNAPSHOT);
addInTransaction(connection1, "ex:steve a ex:Person .");
connection2.commit();
connection1.commit();
connection2.close();
connection1.close();
try (SailRepositoryConnection connection = sailRepository.getConnection()) {
connection.begin();
ValidationReport validationReport = ((ShaclSailConnection) connection.getSailConnection()).revalidate();
assertTrue(validationReport.conforms());
connection.commit();
}
sailRepository.shutDown();
}
private void add(SailRepositoryConnection connection, String data) throws IOException {
data = String.join("\n", "",
"@prefix ex: <http://example.com/ns#> .",
"@prefix foaf: <http://xmlns.com/foaf/0.1/>.",
"@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .",
data);
connection.begin();
StringReader stringReader = new StringReader(data);
connection.add(stringReader, "", RDFFormat.TRIG);
connection.commit();
}
private void addInTransaction(SailRepositoryConnection connection, String data) {
data = String.join("\n", "",
"@prefix ex: <http://example.com/ns#> .",
"@prefix foaf: <http://xmlns.com/foaf/0.1/>.",
"@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .",
data);
StringReader stringReader = new StringReader(data);
try {
connection.add(stringReader, "", RDFFormat.TRIG);
} catch (IOException e) {
throw new IllegalStateException();
}
}
private void removeInTransaction(SailRepositoryConnection connection, String data) {
data = String.join("\n", "",
"@prefix ex: <http://example.com/ns#> .",
"@prefix foaf: <http://xmlns.com/foaf/0.1/>.",
"@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .",
data);
StringReader stringReader = new StringReader(data);
try {
Model parse = Rio.parse(stringReader, RDFFormat.TRIG);
connection.remove(parse);
} catch (IOException e) {
throw new IllegalStateException();
}
}
}