diff --git a/compliance/shacl/src/test/java/org/eclipse/rdf4j/sail/shacl/SHACLComplianceTest.java b/compliance/shacl/src/test/java/org/eclipse/rdf4j/sail/shacl/SHACLComplianceTest.java
index d3bd646f2..bb9971680 100644
--- a/compliance/shacl/src/test/java/org/eclipse/rdf4j/sail/shacl/SHACLComplianceTest.java
+++ b/compliance/shacl/src/test/java/org/eclipse/rdf4j/sail/shacl/SHACLComplianceTest.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2018 Eclipse RDF4J contributors, Aduna, and others.
+ * Copyright (c) 2018 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
@@ -8,11 +8,6 @@
package org.eclipse.rdf4j.sail.shacl;
import org.eclipse.rdf4j.model.Model;
-import org.eclipse.rdf4j.model.Resource;
-import org.eclipse.rdf4j.model.impl.LinkedHashModel;
-import org.eclipse.rdf4j.model.vocabulary.RDF;
-import org.eclipse.rdf4j.model.vocabulary.SHACL;
-import org.eclipse.rdf4j.repository.sail.SailRepository;
import org.eclipse.rdf4j.sail.NotifyingSail;
import org.eclipse.rdf4j.sail.Sail;
import org.eclipse.rdf4j.sail.memory.MemoryStore;
@@ -62,39 +57,9 @@ protected NotifyingSail newDataSail() {
return new MemoryStore();
}
- protected SailRepository createShapesRepository() {
- SailRepository repo = new SailRepository(new MemoryStore());
- repo.initialize();
- return repo;
- }
-
@Override
- protected Sail newSail(Model shapesGraph) {
- SailRepository shapesRep = createShapesRepository();
- if (shapesGraph != null) {
- try {
- upload(shapesRep, shapesGraph);
- }
- catch (Exception exc) {
- try {
- shapesRep.shutDown();
- shapesRep = null;
- }
- catch (Exception e2) {
- logger.error(e2.toString(), e2);
- }
- throw exc;
- }
- }
- Model infer = new LinkedHashModel();
- for (Resource subj : shapesGraph.filter(null, RDF.TYPE, SHACL.NODE_SHAPE).subjects()) {
- infer.add(subj, RDF.TYPE, SHACL.SHAPE);
- }
- for (Resource subj : shapesGraph.filter(null, RDF.TYPE, SHACL.PROPERTY_SHAPE).subjects()) {
- infer.add(subj, RDF.TYPE, SHACL.SHAPE);
- }
- upload(shapesRep, infer);
- return new ShaclSail(newDataSail(), shapesRep);
+ protected Sail newSail() {
+ return new ShaclSail(newDataSail());
}
-
+
}
diff --git a/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/NoShapesLoadedException.java b/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/NoShapesLoadedException.java
index eaf48baa9..33f99ca24 100644
--- a/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/NoShapesLoadedException.java
+++ b/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/NoShapesLoadedException.java
@@ -1,8 +1,13 @@
package org.eclipse.rdf4j.sail.shacl;
+import org.eclipse.rdf4j.model.vocabulary.RDF4J;
+
public class NoShapesLoadedException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+
public NoShapesLoadedException() {
- super("Load shapes by adding them to named graph <"+ShaclSail.SHAPE_GRAPH+"> in the first transaction after initialization!");
+ super("Load shapes by adding them to named graph <" + RDF4J.SHACL_SHAPE_GRAPH
+ + "> in the first transaction after initialization!");
}
}
diff --git a/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ShaclSail.java b/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ShaclSail.java
index 25d19c757..3c946d316 100644
--- a/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ShaclSail.java
+++ b/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ShaclSail.java
@@ -11,6 +11,7 @@
import org.apache.commons.io.IOUtils;
import org.eclipse.rdf4j.model.IRI;
import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
+import org.eclipse.rdf4j.model.vocabulary.RDF4J;
import org.eclipse.rdf4j.repository.Repository;
import org.eclipse.rdf4j.repository.sail.SailRepository;
import org.eclipse.rdf4j.repository.sail.SailRepositoryConnection;
@@ -29,7 +30,9 @@
import java.util.List;
/**
- * A {@link Sail} implementation that adds support for the Shapes Constraint Language (SHACL)
+ * A {@link Sail} implementation that adds support for the Shapes Constraint Language (SHACL).
+ *
+ * The ShaclSail looks for SHACL shape data in a special named graph {@link RDF4J#SHACL_SHAPE_GRAPH}.
*
* @author Heshan Jayasinghe
* @author HÃ¥vard Ottestad
@@ -37,17 +40,10 @@
*/
public class ShaclSail extends NotifyingSailWrapper {
- /**
- * The virtual context identifier for persisting the SHACL shapes information.
- */
- @SuppressWarnings("WeakerAccess")
- public final static IRI SHAPE_GRAPH = SimpleValueFactory
- .getInstance()
- .createIRI("http://rdf4j.org/schema/schacl#ShapeGraph");
-
private List nodeShapes;
boolean debugPrintPlans = false;
+
private boolean ignoreNoShapesLoadedException = false;
ShaclSailConfig config = new ShaclSailConfig();
@@ -64,18 +60,14 @@ public class ShaclSail extends NotifyingSailWrapper {
static {
try {
SH_OR_UPDATE_QUERY = IOUtils.toString(
- ShaclSail
- .class
- .getClassLoader()
- .getResourceAsStream("shacl-sparql-inference/sh_or.rq"),
- "UTF-8");
+ ShaclSail.class.getClassLoader().getResourceAsStream("shacl-sparql-inference/sh_or.rq"),
+ "UTF-8");
SH_OR_NODE_SHAPE_UPDATE_QUERY = IOUtils.toString(
- ShaclSail
- .class
- .getClassLoader()
- .getResourceAsStream("shacl-sparql-inference/sh_or_node_shape.rq"),
- "UTF-8");
- } catch (IOException e) {
+ ShaclSail.class.getClassLoader().getResourceAsStream(
+ "shacl-sparql-inference/sh_or_node_shape.rq"),
+ "UTF-8");
+ }
+ catch (IOException e) {
throw new IllegalStateException(e);
}
@@ -86,10 +78,12 @@ public ShaclSail(NotifyingSail baseSail) {
String path = null;
if (baseSail.getDataDir() != null) {
path = baseSail.getDataDir().getPath();
- } else {
+ }
+ else {
try {
path = Files.createTempDirectory("shacl-shapes").toString();
- } catch (IOException e) {
+ }
+ catch (IOException e) {
throw new SailConfigException(e);
}
}
@@ -118,7 +112,8 @@ void refreshShapes(SailRepositoryConnection shapesRepoConnection) throws SailExc
// Our inferencer both adds and removes statements.
// To support updates I recommend having two graphs, one raw one with the unmodified data.
// Then copy all that data into a new graph, run inferencing on that graph and use it to generate the java objects
- throw new IllegalStateException("ShaclSail does not support modifying shapes that are already loaded or loading more shapes");
+ throw new IllegalStateException(
+ "ShaclSail does not support modifying shapes that are already loaded or loading more shapes");
}
}
@@ -130,7 +125,8 @@ void refreshShapes(SailRepositoryConnection shapesRepoConnection) throws SailExc
public void shutDown() throws SailException {
try {
shapesRepo.shutDown();
- } finally {
+ }
+ finally {
shapesRepo = null;
}
super.shutDown();
@@ -138,7 +134,8 @@ public void shutDown() throws SailException {
@Override
public NotifyingSailConnection getConnection() throws SailException {
- return new ShaclSailConnection(this, super.getConnection(), super.getConnection(), shapesRepo.getConnection());
+ return new ShaclSailConnection(this, super.getConnection(), super.getConnection(),
+ shapesRepo.getConnection());
}
public void disableValidation() {
diff --git a/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ShaclSailConnection.java b/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ShaclSailConnection.java
index ace3b7021..3ce2f4422 100644
--- a/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ShaclSailConnection.java
+++ b/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ShaclSailConnection.java
@@ -8,6 +8,13 @@
package org.eclipse.rdf4j.sail.shacl;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
import org.eclipse.rdf4j.IsolationLevel;
import org.eclipse.rdf4j.IsolationLevels;
import org.eclipse.rdf4j.common.iteration.Iterations;
@@ -15,6 +22,7 @@
import org.eclipse.rdf4j.model.Resource;
import org.eclipse.rdf4j.model.Statement;
import org.eclipse.rdf4j.model.Value;
+import org.eclipse.rdf4j.model.vocabulary.RDF4J;
import org.eclipse.rdf4j.repository.Repository;
import org.eclipse.rdf4j.repository.RepositoryConnection;
import org.eclipse.rdf4j.repository.sail.SailRepository;
@@ -22,6 +30,7 @@
import org.eclipse.rdf4j.sail.NotifyingSailConnection;
import org.eclipse.rdf4j.sail.SailConnectionListener;
import org.eclipse.rdf4j.sail.SailException;
+import org.eclipse.rdf4j.sail.UpdateContext;
import org.eclipse.rdf4j.sail.helpers.NotifyingSailConnectionWrapper;
import org.eclipse.rdf4j.sail.memory.MemoryStore;
import org.eclipse.rdf4j.sail.shacl.AST.NodeShape;
@@ -30,14 +39,6 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
/**
* @author Heshan Jayasinghe
*/
@@ -64,7 +65,8 @@ public class ShaclSailConnection extends NotifyingSailConnectionWrapper {
private SailRepositoryConnection shapesConnection;
ShaclSailConnection(ShaclSail sail, NotifyingSailConnection connection,
- NotifyingSailConnection previousStateConnection, SailRepositoryConnection shapesConnection) {
+ NotifyingSailConnection previousStateConnection, SailRepositoryConnection shapesConnection)
+ {
super(connection);
this.previousStateConnection = previousStateConnection;
this.shapesConnection = shapesConnection;
@@ -74,23 +76,23 @@ public class ShaclSailConnection extends NotifyingSailConnectionWrapper {
addConnectionListener(new SailConnectionListener() {
- @Override
- public void statementAdded(Statement statement) {
- boolean add = addedStatementsSet.add(statement);
- if (!add) {
- removedStatementsSet.remove(statement);
- }
+ @Override
+ public void statementAdded(Statement statement) {
+ boolean add = addedStatementsSet.add(statement);
+ if (!add) {
+ removedStatementsSet.remove(statement);
+ }
- }
+ }
- @Override
- public void statementRemoved(Statement statement) {
- boolean add = removedStatementsSet.add(statement);
- if (!add) {
- addedStatementsSet.remove(statement);
- }
- }
- }
+ @Override
+ public void statementRemoved(Statement statement) {
+ boolean add = removedStatementsSet.add(statement);
+ if (!add) {
+ addedStatementsSet.remove(statement);
+ }
+ }
+ }
);
}
@@ -144,7 +146,10 @@ public void commit() throws SailException {
refreshShapes(shapesConnection);
- if (!sail.isIgnoreNoShapesLoadedException() && ((!addedStatementsSet.isEmpty() || !removedStatementsSet.isEmpty()) && sail.getNodeShapes().isEmpty())) {
+ if (!sail.isIgnoreNoShapesLoadedException()
+ && ((!addedStatementsSet.isEmpty() || !removedStatementsSet.isEmpty())
+ && sail.getNodeShapes().isEmpty()))
+ {
throw new NoShapesLoadedException();
}
@@ -157,33 +162,65 @@ public void commit() throws SailException {
rollback();
refreshShapes(shapesConnection);
throw new ShaclSailValidationException(invalidTuples);
- } else {
+ }
+ else {
shapesConnection.commit();
super.commit();
}
- } finally {
+ }
+ finally {
cleanup();
}
}
}
+ @Override
+ public void addStatement(UpdateContext modify, Resource subj, IRI pred, Value obj, Resource... contexts)
+ throws SailException
+ {
+ if (contexts.length == 1 && contexts[0].equals(RDF4J.SHACL_SHAPE_GRAPH)) {
+ shapesConnection.add(subj, pred, obj);
+ isShapeRefreshNeeded = true;
+ }
+ else {
+ super.addStatement(modify, subj, pred, obj, contexts);
+ }
+ }
+
+ @Override
+ public void removeStatement(UpdateContext modify, Resource subj, IRI pred, Value obj,
+ Resource... contexts)
+ throws SailException
+ {
+ if (contexts.length == 1 && contexts[0].equals(RDF4J.SHACL_SHAPE_GRAPH)) {
+ shapesConnection.remove(subj, pred, obj);
+ isShapeRefreshNeeded = true;
+ }
+ else {
+ super.removeStatement(modify, subj, pred, obj, contexts);
+ }
+ }
+
@Override
public void addStatement(Resource subj, IRI pred, Value obj, Resource... contexts) throws SailException {
- if (contexts.length == 1 && contexts[0].equals(ShaclSail.SHAPE_GRAPH)) {
+ if (contexts.length == 1 && contexts[0].equals(RDF4J.SHACL_SHAPE_GRAPH)) {
shapesConnection.add(subj, pred, obj);
isShapeRefreshNeeded = true;
- } else {
+ }
+ else {
super.addStatement(subj, pred, obj, contexts);
}
}
@Override
public void removeStatements(Resource subj, IRI pred, Value obj, Resource... contexts)
- throws SailException {
- if (contexts.length == 1 && contexts[0].equals(ShaclSail.SHAPE_GRAPH)) {
+ throws SailException
+ {
+ if (contexts.length == 1 && contexts[0].equals(RDF4J.SHACL_SHAPE_GRAPH)) {
shapesConnection.remove(subj, pred, obj);
isShapeRefreshNeeded = true;
- } else {
+ }
+ else {
super.removeStatements(subj, pred, obj, contexts);
}
}
@@ -243,12 +280,12 @@ private List validate() {
boolean valid = collect.size() == 0;
if (!valid) {
logger.warn(
- "SHACL not valid. The following experimental debug results were produced: \n\tNodeShape: {} \n\t\t{}",
- nodeShape.toString(),
- String.join("\n\t\t",
- collect.stream().map(
- a -> a.toString() + " -cause-> " + a.getCause()).collect(
- Collectors.toList())));
+ "SHACL not valid. The following experimental debug results were produced: \n\tNodeShape: {} \n\t\t{}",
+ nodeShape.toString(),
+ String.join("\n\t\t",
+ collect.stream().map(
+ a -> a.toString() + " -cause-> " + a.getCause()).collect(
+ Collectors.toList())));
}
}
}
@@ -268,14 +305,14 @@ void fillAddedAndRemovedStatementRepositories() {
try (RepositoryConnection connection = addedStatements.getConnection()) {
connection.begin(IsolationLevels.NONE);
addedStatementsSet.stream().filter(
- statement -> !removedStatementsSet.contains(statement)).forEach(connection::add);
+ statement -> !removedStatementsSet.contains(statement)).forEach(connection::add);
connection.commit();
}
try (RepositoryConnection connection = removedStatements.getConnection()) {
connection.begin(IsolationLevels.NONE);
removedStatementsSet.stream().filter(
- statement -> !addedStatementsSet.contains(statement)).forEach(connection::add);
+ statement -> !addedStatementsSet.contains(statement)).forEach(connection::add);
connection.commit();
}
}
diff --git a/shacl/src/test/java/org/eclipse/rdf4j/sail/shacl/RuntimeModifyShapesTest.java b/shacl/src/test/java/org/eclipse/rdf4j/sail/shacl/RuntimeModifyShapesTest.java
index a973ae279..debb7cdad 100644
--- a/shacl/src/test/java/org/eclipse/rdf4j/sail/shacl/RuntimeModifyShapesTest.java
+++ b/shacl/src/test/java/org/eclipse/rdf4j/sail/shacl/RuntimeModifyShapesTest.java
@@ -1,6 +1,7 @@
package org.eclipse.rdf4j.sail.shacl;
import org.eclipse.rdf4j.model.vocabulary.RDF;
+import org.eclipse.rdf4j.model.vocabulary.RDF4J;
import org.eclipse.rdf4j.model.vocabulary.RDFS;
import org.eclipse.rdf4j.repository.sail.SailRepository;
import org.eclipse.rdf4j.repository.sail.SailRepositoryConnection;
@@ -49,10 +50,10 @@ public void checkForExceptionWhenModifyingShapes() throws IOException {
try (SailRepositoryConnection connection = sailRepository.getConnection()) {
connection.begin();
- connection.add(RuntimeModifyShapesTest.class.getClassLoader().getResourceAsStream("shaclDatatype.ttl"), "http://example.com/", RDFFormat.TURTLE, ShaclSail.SHAPE_GRAPH);
+ connection.add(RuntimeModifyShapesTest.class.getClassLoader().getResourceAsStream("shaclDatatype.ttl"), "http://example.com/", RDFFormat.TURTLE, RDF4J.SHACL_SHAPE_GRAPH);
connection.commit();
connection.begin();
- connection.add(RuntimeModifyShapesTest.class.getClassLoader().getResourceAsStream("shacl.ttl"), "http://example.com/", RDFFormat.TURTLE, ShaclSail.SHAPE_GRAPH);
+ connection.add(RuntimeModifyShapesTest.class.getClassLoader().getResourceAsStream("shacl.ttl"), "http://example.com/", RDFFormat.TURTLE, RDF4J.SHACL_SHAPE_GRAPH);
connection.commit();
}
diff --git a/shacl/src/test/java/org/eclipse/rdf4j/sail/shacl/Utils.java b/shacl/src/test/java/org/eclipse/rdf4j/sail/shacl/Utils.java
index a95b78906..460701095 100644
--- a/shacl/src/test/java/org/eclipse/rdf4j/sail/shacl/Utils.java
+++ b/shacl/src/test/java/org/eclipse/rdf4j/sail/shacl/Utils.java
@@ -17,6 +17,7 @@
import org.eclipse.rdf4j.model.Model;
import org.eclipse.rdf4j.model.Statement;
import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
+import org.eclipse.rdf4j.model.vocabulary.RDF4J;
import org.eclipse.rdf4j.repository.RepositoryConnection;
import org.eclipse.rdf4j.repository.sail.SailRepository;
import org.eclipse.rdf4j.rio.RDFFormat;
@@ -34,11 +35,11 @@ public static void loadShapeData(ShaclSail sail, String resourceName)
throws RDF4JException, UnsupportedRDFormatException, IOException
{
InputStream shapesData = Utils.class.getResourceAsStream("/" + resourceName);
- Model shapes = Rio.parse(shapesData, "", RDFFormat.TURTLE, ShaclSail.SHAPE_GRAPH);
+ Model shapes = Rio.parse(shapesData, "", RDFFormat.TURTLE, RDF4J.SHACL_SHAPE_GRAPH);
try (SailConnection conn = sail.getConnection()) {
conn.begin();
for (Statement st : shapes) {
- conn.addStatement(st.getSubject(), st.getPredicate(), st.getObject(), ShaclSail.SHAPE_GRAPH);
+ conn.addStatement(st.getSubject(), st.getPredicate(), st.getObject(), RDF4J.SHACL_SHAPE_GRAPH);
}
conn.commit();
}
@@ -49,11 +50,11 @@ public static void loadShapeData(SailRepository repo, String resourceName)
throws RDF4JException, UnsupportedRDFormatException, IOException
{
InputStream shapesData = Utils.class.getResourceAsStream("/" + resourceName);
- Model shapes = Rio.parse(shapesData, "", RDFFormat.TURTLE, ShaclSail.SHAPE_GRAPH);
+ Model shapes = Rio.parse(shapesData, "", RDFFormat.TURTLE, RDF4J.SHACL_SHAPE_GRAPH);
try (RepositoryConnection conn = repo.getConnection()) {
conn.begin();
for (Statement st : shapes) {
- conn.add(st.getSubject(), st.getPredicate(), st.getObject(), ShaclSail.SHAPE_GRAPH);
+ conn.add(st.getSubject(), st.getPredicate(), st.getObject(), RDF4J.SHACL_SHAPE_GRAPH);
}
conn.commit();
}