Skip to content

Commit 435d153

Browse files
committed
Add several ways for flexible configuration of RDF4J repositories.
1 parent df60ee0 commit 435d153

File tree

14 files changed

+446
-3
lines changed

14 files changed

+446
-3
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package io.github.linkedfactory.core.komma;
2+
3+
import io.github.linkedfactory.core.kvin.DelegatingKvin;
4+
import io.github.linkedfactory.core.kvin.Kvin;
5+
import io.github.linkedfactory.core.rdf4j.kvin.KvinSail;
6+
import net.enilink.composition.annotations.Iri;
7+
import net.enilink.komma.core.URI;
8+
import net.enilink.komma.core.URIs;
9+
import net.enilink.komma.model.MODELS;
10+
import net.enilink.komma.model.rdf4j.PersistentModelSetSupport;
11+
import org.eclipse.core.runtime.FileLocator;
12+
import org.eclipse.rdf4j.repository.Repository;
13+
import org.eclipse.rdf4j.repository.RepositoryException;
14+
import org.eclipse.rdf4j.repository.sail.SailRepository;
15+
import org.eclipse.rdf4j.sail.NotifyingSail;
16+
import org.eclipse.rdf4j.sail.Sail;
17+
import org.eclipse.rdf4j.sail.inferencer.fc.SchemaCachingRDFSInferencer;
18+
import org.eclipse.rdf4j.sail.nativerdf.NativeStore;
19+
import org.osgi.framework.BundleContext;
20+
import org.osgi.framework.FrameworkUtil;
21+
import org.osgi.framework.ServiceReference;
22+
23+
import java.io.File;
24+
import java.net.URL;
25+
import java.util.function.Supplier;
26+
27+
@Iri(MODELS.NAMESPACE + "KvinPersistentModelSet")
28+
public abstract class KvinPersistentModelSet extends PersistentModelSetSupport {
29+
static BundleContext bundleContext = FrameworkUtil.getBundle(KvinPersistentModelSet.class).getBundleContext();
30+
static Kvin kvin;
31+
32+
public Repository createRepository() throws RepositoryException {
33+
URI repo = getRepository();
34+
if (repo.scheme() == "workspace") {
35+
try {
36+
String instanceFilter = "(type=osgi.instance.area)";
37+
BundleContext context = FrameworkUtil.getBundle(PersistentModelSetSupport.class).getBundleContext();
38+
ServiceReference<?>[] refs = context
39+
.getServiceReferences("org.eclipse.osgi.service.datalocation.Location", instanceFilter);
40+
if (refs.length > 0) {
41+
Object location = context.getService(refs[0]);
42+
URL loc = (URL) location.getClass().getMethod("getURL").invoke(location);
43+
URI workspace = URIs.createURI(FileLocator.resolve(loc).toString());
44+
if (workspace.lastSegment() == "") {
45+
workspace = workspace.trimSegments(1);
46+
}
47+
repo = workspace.appendSegments(repo.segments());
48+
}
49+
} catch (Exception e) {
50+
throw new RepositoryException(e);
51+
}
52+
} else {
53+
throw new RepositoryException("Location service for workspace scheme not found");
54+
}
55+
56+
NotifyingSail store = new NativeStore(new File(repo.toFileString()));
57+
if (! Boolean.FALSE.equals(getInference())) {
58+
store = new SchemaCachingRDFSInferencer(store);
59+
}
60+
Supplier<Kvin> kvinSupplier = () -> {
61+
if (kvin != null) {
62+
return kvin;
63+
} else {
64+
return bundleContext.getService(bundleContext.getServiceReference(Kvin.class));
65+
}
66+
};
67+
Sail kvinSail = new KvinSail(new DelegatingKvin(kvinSupplier), store);
68+
SailRepository repository = new SailRepository(kvinSail);
69+
repository.init();
70+
addBasicKnowledge(repository);
71+
return repository;
72+
}
73+
74+
public static void setKvin(Kvin kvin) {
75+
KvinPersistentModelSet.kvin = kvin;
76+
}
77+
}

bundles/io.github.linkedfactory.core/src/main/java/io/github/linkedfactory/core/komma/ModelModule.java

+3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
public class ModelModule extends KommaModule {
66
{
77
addBehaviour(KvinMemoryModelSet.class);
8+
addBehaviour(KvinPersistentModelSet.class);
9+
addBehaviour(RepositoryManagerModelSet.class);
10+
addBehaviour(Rdf4jModelSet.class);
811
}
912

1013
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package io.github.linkedfactory.core.komma;
2+
3+
import net.enilink.commons.iterator.IExtendedIterator;
4+
import net.enilink.composition.annotations.Iri;
5+
import net.enilink.komma.core.*;
6+
import net.enilink.komma.model.MODELS;
7+
import net.enilink.komma.model.rdf4j.MemoryModelSetSupport;
8+
import net.enilink.komma.rdf4j.RDF4JValueConverter;
9+
import org.apache.http.auth.AUTH;
10+
import org.eclipse.rdf4j.model.Model;
11+
import org.eclipse.rdf4j.model.impl.LinkedHashModel;
12+
import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
13+
import org.eclipse.rdf4j.repository.Repository;
14+
import org.eclipse.rdf4j.repository.RepositoryException;
15+
import org.eclipse.rdf4j.repository.config.*;
16+
import org.eclipse.rdf4j.repository.manager.LocalRepositoryManager;
17+
18+
import java.io.File;
19+
import java.util.*;
20+
21+
@Iri(MODELS.NAMESPACE + "Rdf4jModelSet")
22+
public abstract class Rdf4jModelSet extends MemoryModelSetSupport {
23+
@Iri(MODELS.NAMESPACE + "repository")
24+
public abstract IReference getRepository();
25+
26+
public Repository createRepository() throws RepositoryException {
27+
IEntityManager manager = ((IEntity) getBehaviourDelegate()).getEntityManager();
28+
RDF4JValueConverter converter = new RDF4JValueConverter(SimpleValueFactory.getInstance());
29+
Model configModel = new LinkedHashModel();
30+
try (IExtendedIterator<IStatement> stmts = manager
31+
.createQuery("construct { ?s ?p ?o } where { ?repository (!<:>)* ?s . ?s ?p ?o }")
32+
.setParameter("repository", getRepository())
33+
.evaluateRestricted(IStatement.class)) {
34+
stmts.forEach(stmt -> {
35+
configModel.add(converter.toRdf4j(stmt));
36+
});
37+
}
38+
Set<String> repositoryIDs = RepositoryConfigUtil.getRepositoryIDs(configModel);
39+
if (repositoryIDs.isEmpty()) {
40+
throw new KommaException("No repository ID in configuration: " + getRepository());
41+
}
42+
if (repositoryIDs.size() != 1) {
43+
throw new KommaException("Multiple repository IDs in configuration: " + getRepository());
44+
}
45+
RepositoryConfig repoConfig = RepositoryConfigUtil.getRepositoryConfig(configModel,
46+
repositoryIDs.iterator().next());
47+
RepositoryImplConfig implConfig = repoConfig.getRepositoryImplConfig();
48+
if (implConfig == null) {
49+
throw new KommaException("No implementation config in configuration: " + getRepository());
50+
}
51+
RepositoryFactory factory = RepositoryRegistry.getInstance().get(implConfig.getType()).orElseThrow(() -> {
52+
return new RepositoryConfigException("Unsupported repository type: " + implConfig.getType());
53+
});
54+
Repository repository = factory.getRepository(implConfig);
55+
repository.init();
56+
addBasicKnowledge(repository);
57+
return repository;
58+
}
59+
60+
@Override
61+
public boolean isPersistent() {
62+
return true;
63+
}
64+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package io.github.linkedfactory.core.komma;
2+
3+
import net.enilink.composition.annotations.Iri;
4+
import net.enilink.komma.core.EntityVar;
5+
import net.enilink.komma.core.IReference;
6+
import net.enilink.komma.model.MODELS;
7+
import net.enilink.komma.model.rdf4j.MemoryModelSetSupport;
8+
import org.eclipse.rdf4j.repository.Repository;
9+
import org.eclipse.rdf4j.repository.RepositoryException;
10+
import org.eclipse.rdf4j.repository.manager.LocalRepositoryManager;
11+
12+
import java.io.File;
13+
import java.util.Collections;
14+
15+
@Iri(MODELS.NAMESPACE + "RepositoryManagerModelSet")
16+
public abstract class RepositoryManagerModelSet extends MemoryModelSetSupport {
17+
EntityVar<LocalRepositoryManager> manager;
18+
19+
@Iri(MODELS.NAMESPACE + "baseDir")
20+
public abstract Object getBaseDir();
21+
22+
@Iri(MODELS.NAMESPACE + "repositoryID")
23+
public abstract String getRepositoryID();
24+
25+
public Repository createRepository() throws RepositoryException {
26+
Object baseDir = getBaseDir();
27+
File file;
28+
if (baseDir instanceof IReference && (((IReference) baseDir)).getURI() != null
29+
&& (((IReference) baseDir)).getURI().isFile()) {
30+
file = new File((((IReference) baseDir)).getURI().toFileString());
31+
} else {
32+
file = new File(baseDir.toString());
33+
}
34+
LocalRepositoryManager repoManager = new LocalRepositoryManager(file) {
35+
@Override
36+
public void shutDown() {
37+
// prevent manager from shutting down the repositories as this already handled by KOMMA
38+
setInitializedRepositories(Collections.emptyMap());
39+
super.shutDown();
40+
}
41+
};
42+
repoManager.init();
43+
manager.set(repoManager);
44+
Repository repository = manager.get().getRepository(getRepositoryID());
45+
repository.init();
46+
addBasicKnowledge(repository);
47+
return repository;
48+
}
49+
50+
@Override
51+
public void dispose() {
52+
LocalRepositoryManager m = manager.get();
53+
if (m != null) {
54+
m.shutDown();
55+
}
56+
manager.remove();
57+
}
58+
59+
@Override
60+
public boolean isPersistent() {
61+
return true;
62+
}
63+
}

bundles/io.github.linkedfactory.core/src/main/java/io/github/linkedfactory/core/rdf4j/kvin/KvinConnection.java

+7-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import io.github.linkedfactory.core.kvin.KvinTuple;
55
import io.github.linkedfactory.core.kvin.Record;
66
import io.github.linkedfactory.core.rdf4j.common.HasValue;
7+
import net.enilink.commons.iterator.IExtendedIterator;
78
import net.enilink.commons.iterator.WrappedIterator;
89
import net.enilink.komma.core.ILiteral;
910
import net.enilink.komma.core.IReference;
@@ -76,7 +77,7 @@ public void flush() throws SailException {
7677

7778
private void createKvinTuples() {
7879
long currentTime = System.currentTimeMillis();
79-
kvinSail.getKvin().put(WrappedIterator.create(
80+
try (IExtendedIterator<KvinTuple> tuples = WrappedIterator.create(
8081
stmtsBySubject.entrySet().stream().filter(e -> e.getKey().isIRI())
8182
.flatMap(e -> {
8283
IRI item = (IRI) e.getKey();
@@ -85,7 +86,11 @@ private void createKvinTuples() {
8586
return toKvinTuple(item, predicate, stmt.getObject(), currentTime);
8687
});
8788
//System.out.println(tuple);
88-
}).iterator()));
89+
}).iterator())) {
90+
if (tuples.hasNext()) {
91+
kvinSail.getKvin().put();
92+
}
93+
}
8994
}
9095

9196
private KvinTuple toKvinTuple(IRI item, IRI predicate, Value rdfValue, long currentTime) {

bundles/io.github.linkedfactory.core/src/main/java/io/github/linkedfactory/core/rdf4j/kvin/KvinSail.java

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import net.enilink.komma.literals.LiteralConverter;
77
import net.enilink.komma.rdf4j.RDF4JValueConverter;
88

9+
import java.io.File;
910
import java.util.Locale;
1011

1112
import org.eclipse.rdf4j.model.ValueFactory;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package io.github.linkedfactory.core.rdf4j.kvin.config;
2+
3+
import org.eclipse.rdf4j.model.Model;
4+
import org.eclipse.rdf4j.model.Resource;
5+
import org.eclipse.rdf4j.model.ValueFactory;
6+
import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
7+
import org.eclipse.rdf4j.sail.base.config.BaseSailConfig;
8+
import org.eclipse.rdf4j.sail.config.AbstractDelegatingSailImplConfig;
9+
import org.eclipse.rdf4j.sail.config.SailConfigException;
10+
11+
public class KvinSailConfig extends AbstractDelegatingSailImplConfig {
12+
public KvinSailConfig() {
13+
super(KvinSailFactory.SAIL_TYPE);
14+
}
15+
16+
@Override
17+
public Resource export(Model m) {
18+
Resource implNode = super.export(m);
19+
// implement if required
20+
return implNode;
21+
}
22+
23+
@Override
24+
public void parse(Model m, Resource implNode) throws SailConfigException {
25+
super.parse(m, implNode);
26+
// implement if required
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package io.github.linkedfactory.core.rdf4j.kvin.config;
2+
3+
import io.github.linkedfactory.core.kvin.DelegatingKvin;
4+
import io.github.linkedfactory.core.kvin.Kvin;
5+
import io.github.linkedfactory.core.rdf4j.kvin.KvinSail;
6+
import org.eclipse.rdf4j.sail.Sail;
7+
import org.eclipse.rdf4j.sail.config.SailConfigException;
8+
import org.eclipse.rdf4j.sail.config.SailFactory;
9+
import org.eclipse.rdf4j.sail.config.SailImplConfig;
10+
import org.osgi.framework.Bundle;
11+
import org.osgi.framework.BundleContext;
12+
import org.osgi.framework.FrameworkUtil;
13+
import org.slf4j.Logger;
14+
import org.slf4j.LoggerFactory;
15+
16+
import java.util.function.Supplier;
17+
18+
/**
19+
* A {@link SailFactory} that creates {@link KvinSail}s based on RDF configuration data.
20+
*/
21+
public class KvinSailFactory implements SailFactory {
22+
/**
23+
* The type of repositories that are created by this factory.
24+
*
25+
* @see SailFactory#getSailType()
26+
*/
27+
public static final String SAIL_TYPE = "kvin:KvinSail";
28+
private static final Logger logger = LoggerFactory.getLogger(KvinSailFactory.class);
29+
30+
/**
31+
* Returns the Sail's type: <tt>kvin:KvinSail</tt>.
32+
*/
33+
@Override
34+
public String getSailType() {
35+
return SAIL_TYPE;
36+
}
37+
38+
@Override
39+
public SailImplConfig getConfig() {
40+
return new KvinSailConfig();
41+
}
42+
43+
@Override
44+
public Sail getSail(SailImplConfig config) throws SailConfigException {
45+
if (!SAIL_TYPE.equals(config.getType())) {
46+
throw new SailConfigException("Invalid Sail type: " + config.getType());
47+
}
48+
49+
Supplier<Kvin> kvinSupplier = () -> {
50+
Bundle bundle = FrameworkUtil.getBundle(KvinSailFactory.class);
51+
BundleContext bundleContext = bundle == null ? null : bundle.getBundleContext();
52+
return bundleContext != null ? bundleContext.getService(bundleContext.getServiceReference(Kvin.class)) : null;
53+
};
54+
55+
Sail sail = new KvinSail(new DelegatingKvin(kvinSupplier));
56+
if (config instanceof KvinSailConfig) {
57+
// set config values;
58+
} else {
59+
logger.warn("Config is instance of {} is not KvinSailConfig.", config.getClass().getName());
60+
}
61+
return sail;
62+
}
63+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
io.github.linkedfactory.core.rdf4j.kvin.config.KvinSailFactory
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package io.github.linkedfactory.core.rdf4j;
2+
3+
import com.google.inject.Guice;
4+
import net.enilink.komma.core.*;
5+
import net.enilink.komma.core.visitor.IDataVisitor;
6+
import net.enilink.komma.model.*;
7+
import net.enilink.vocab.owl.Restriction;
8+
import org.junit.Assert;
9+
import org.junit.Test;
10+
11+
public class Rdf4jModelSetTest {
12+
@Test
13+
public void testBasicConfig() {
14+
// create configuration and a model set factory
15+
KommaModule module = ModelPlugin.createModelSetModule(getClass().getClassLoader());
16+
IModelSetFactory factory = Guice.createInjector(new ModelSetModule(module)).getInstance(IModelSetFactory.class);
17+
18+
IGraph config = new LinkedHashGraph();
19+
ModelUtil.readData(getClass().getResourceAsStream("/rdf4j-modelset-config.ttl"), null,
20+
"text/turtle", new IDataVisitor<Object>() {
21+
@Override
22+
public Object visitBegin() {
23+
return null;
24+
}
25+
26+
@Override
27+
public Object visitEnd() {
28+
return null;
29+
}
30+
31+
@Override
32+
public Object visitStatement(IStatement stmt) {
33+
return config.add(stmt);
34+
}
35+
});
36+
37+
IModelSet modelSet = factory.createModelSet(URIs.createURI("urn:enilink:data"), config);
38+
Assert.assertTrue(modelSet.createModel(URIs.createURI("test:model"))
39+
.getManager().create(Restriction.class) instanceof Restriction);
40+
modelSet.dispose();
41+
}
42+
}

0 commit comments

Comments
 (0)