Skip to content

Commit 0d39fa7

Browse files
author
James Leigh
committed
Issue #859: Use pseudo SYSTEM repository for http-server to save configurations using RepositoryManager API
Signed-off-by: James Leigh <james.leigh@ontotext.com>
1 parent 80ff3a4 commit 0d39fa7

2 files changed

Lines changed: 374 additions & 1 deletion

File tree

Lines changed: 369 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,369 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2017 Eclipse RDF4J contributors, Aduna, and others.
3+
* All rights reserved. This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Distribution License v1.0
5+
* which accompanies this distribution, and is available at
6+
* http://www.eclipse.org/org/documents/edl-v10.php.
7+
*******************************************************************************/
8+
package org.eclipse.rdf4j.http.server.repository;
9+
10+
import java.io.File;
11+
import java.net.MalformedURLException;
12+
import java.net.URISyntaxException;
13+
import java.util.LinkedHashSet;
14+
import java.util.Optional;
15+
import java.util.Set;
16+
17+
import org.eclipse.rdf4j.common.iteration.CloseableIteration;
18+
import org.eclipse.rdf4j.common.iteration.CloseableIteratorIteration;
19+
import org.eclipse.rdf4j.http.protocol.Protocol;
20+
import org.eclipse.rdf4j.model.IRI;
21+
import org.eclipse.rdf4j.model.Model;
22+
import org.eclipse.rdf4j.model.Namespace;
23+
import org.eclipse.rdf4j.model.Resource;
24+
import org.eclipse.rdf4j.model.Statement;
25+
import org.eclipse.rdf4j.model.Value;
26+
import org.eclipse.rdf4j.model.ValueFactory;
27+
import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
28+
import org.eclipse.rdf4j.model.impl.TreeModel;
29+
import org.eclipse.rdf4j.query.BooleanQuery;
30+
import org.eclipse.rdf4j.query.GraphQuery;
31+
import org.eclipse.rdf4j.query.MalformedQueryException;
32+
import org.eclipse.rdf4j.query.Query;
33+
import org.eclipse.rdf4j.query.QueryLanguage;
34+
import org.eclipse.rdf4j.query.TupleQuery;
35+
import org.eclipse.rdf4j.query.Update;
36+
import org.eclipse.rdf4j.repository.Repository;
37+
import org.eclipse.rdf4j.repository.RepositoryConnection;
38+
import org.eclipse.rdf4j.repository.RepositoryException;
39+
import org.eclipse.rdf4j.repository.RepositoryResult;
40+
import org.eclipse.rdf4j.repository.UnknownTransactionStateException;
41+
import org.eclipse.rdf4j.repository.base.AbstractRepository;
42+
import org.eclipse.rdf4j.repository.base.AbstractRepositoryConnection;
43+
import org.eclipse.rdf4j.repository.config.RepositoryConfig;
44+
import org.eclipse.rdf4j.repository.config.RepositoryConfigUtil;
45+
import org.eclipse.rdf4j.repository.manager.RepositoryManager;
46+
import org.eclipse.rdf4j.rio.RDFHandler;
47+
import org.eclipse.rdf4j.rio.RDFHandlerException;
48+
49+
/**
50+
* {@link Repository} implementation that saves {@link RepositoryConfig} RDF to a {@link RepositoryManager}.
51+
*
52+
* @author James Leigh
53+
*/
54+
public class RepositoryConfigRepository extends AbstractRepository {
55+
56+
/**
57+
* The repository identifier for the system repository that contains the configuration data.
58+
*/
59+
public static final String ID = "SYSTEM";
60+
61+
private final RepositoryManager manager;
62+
63+
public RepositoryConfigRepository(RepositoryManager manager) {
64+
this.manager = manager;
65+
}
66+
67+
@Override
68+
public void setDataDir(File dataDir) {
69+
// no-op
70+
}
71+
72+
@Override
73+
public File getDataDir() {
74+
return null;
75+
}
76+
77+
@Override
78+
public boolean isWritable()
79+
throws RepositoryException
80+
{
81+
return true;
82+
}
83+
84+
@Override
85+
public ValueFactory getValueFactory() {
86+
return SimpleValueFactory.getInstance();
87+
}
88+
89+
@Override
90+
protected void initializeInternal()
91+
throws RepositoryException
92+
{
93+
}
94+
95+
@Override
96+
protected void shutDownInternal()
97+
throws RepositoryException
98+
{
99+
}
100+
101+
@Override
102+
public RepositoryConnection getConnection()
103+
throws RepositoryException
104+
{
105+
return new AbstractRepositoryConnection(this) {
106+
107+
private boolean active = false;
108+
109+
private Model committed = loadModel();
110+
111+
private Model added = new TreeModel();
112+
113+
private Model removed = new TreeModel();
114+
115+
@Override
116+
public RepositoryResult<Resource> getContextIDs()
117+
throws RepositoryException
118+
{
119+
Set<Resource> contextIDs = new LinkedHashSet<>();
120+
manager.getRepositoryIDs().forEach(id -> {
121+
contextIDs.add(getContext(id));
122+
});
123+
CloseableIteration<Resource, RepositoryException> iter;
124+
iter = new CloseableIteratorIteration<>(contextIDs.iterator());
125+
return new RepositoryResult<>(iter);
126+
}
127+
128+
@Override
129+
public RepositoryResult<Statement> getStatements(Resource subj, IRI pred, Value obj,
130+
boolean includeInferred, Resource... contexts)
131+
throws RepositoryException
132+
{
133+
Model model = committed.filter(subj, pred, obj, contexts);
134+
CloseableIteration<Statement, RepositoryException> iter;
135+
iter = new CloseableIteratorIteration<>(model.iterator());
136+
return new RepositoryResult<>(iter);
137+
}
138+
139+
@Override
140+
public void exportStatements(Resource subj, IRI pred, Value obj, boolean includeInferred,
141+
RDFHandler handler, Resource... contexts)
142+
throws RepositoryException,
143+
RDFHandlerException
144+
{
145+
Model model = committed.filter(subj, pred, obj, contexts);
146+
handler.startRDF();
147+
model.getNamespaces().forEach(ns -> {
148+
handler.handleNamespace(ns.getPrefix(), ns.getName());
149+
});
150+
model.forEach(st -> {
151+
handler.handleStatement(st);
152+
});
153+
handler.endRDF();
154+
}
155+
156+
@Override
157+
public long size(Resource... contexts)
158+
throws RepositoryException
159+
{
160+
return committed.filter(null, null, null, contexts).size();
161+
}
162+
163+
@Override
164+
public boolean isActive()
165+
throws UnknownTransactionStateException,
166+
RepositoryException
167+
{
168+
return active;
169+
}
170+
171+
@Override
172+
public void begin()
173+
throws RepositoryException
174+
{
175+
active = true;
176+
}
177+
178+
@Override
179+
public void commit()
180+
throws RepositoryException
181+
{
182+
Set<String> ids = new LinkedHashSet<>();
183+
ids.addAll(manager.getRepositoryIDs());
184+
ids.addAll(RepositoryConfigUtil.getRepositoryIDs(added));
185+
ids.forEach(id -> {
186+
Resource ctx = getContext(id);
187+
Model less = removed.filter(null, null, null, ctx);
188+
Model more = added.filter(null, null, null, ctx);
189+
Model alt = RepositoryConfigUtil.getRepositoryConfigModel(added, id);
190+
if (!less.isEmpty() || !more.isEmpty() || alt != null) {
191+
Model model = new TreeModel(committed.filter(null, null, null, getContext(id)));
192+
model.removeAll(less);
193+
removed.getNamespaces().forEach(ns -> {
194+
model.removeNamespace(ns.getPrefix());
195+
});
196+
added.getNamespaces().forEach(ns -> {
197+
model.setNamespace(ns);
198+
});
199+
model.addAll(more);
200+
if (alt != null) {
201+
model.addAll(alt);
202+
}
203+
if (model.isEmpty()) {
204+
manager.removeRepository(id);
205+
}
206+
else {
207+
manager.addRepositoryConfig(RepositoryConfigUtil.getRepositoryConfig(model, id));
208+
}
209+
}
210+
});
211+
committed = loadModel();
212+
rollback();
213+
}
214+
215+
@Override
216+
public void rollback()
217+
throws RepositoryException
218+
{
219+
added.clear();
220+
added.getNamespaces().clear();
221+
removed.clear();
222+
removed.getNamespaces().clear();
223+
active = false;
224+
}
225+
226+
@Override
227+
public RepositoryResult<Namespace> getNamespaces()
228+
throws RepositoryException
229+
{
230+
CloseableIteration<Namespace, RepositoryException> iter;
231+
iter = new CloseableIteratorIteration<>(committed.getNamespaces().iterator());
232+
return new RepositoryResult<>(iter);
233+
}
234+
235+
@Override
236+
public String getNamespace(String prefix)
237+
throws RepositoryException
238+
{
239+
Optional<Namespace> ns = committed.getNamespace(prefix);
240+
if (ns.isPresent()) {
241+
return ns.get().getName();
242+
}
243+
else {
244+
return null;
245+
}
246+
}
247+
248+
@Override
249+
public void setNamespace(String prefix, String name)
250+
throws RepositoryException
251+
{
252+
removed.removeNamespace(prefix);
253+
added.setNamespace(prefix, name);
254+
}
255+
256+
@Override
257+
public void removeNamespace(String prefix)
258+
throws RepositoryException
259+
{
260+
added.removeNamespace(prefix);
261+
Optional<Namespace> ns = committed.getNamespace(prefix);
262+
if (ns.isPresent()) {
263+
removed.setNamespace(ns.get());
264+
}
265+
}
266+
267+
@Override
268+
public void clearNamespaces()
269+
throws RepositoryException
270+
{
271+
added.getNamespaces().clear();
272+
committed.getNamespaces().forEach(ns -> {
273+
removed.setNamespace(ns);
274+
});
275+
}
276+
277+
@Override
278+
public Query prepareQuery(QueryLanguage ql, String query, String baseURI)
279+
throws RepositoryException,
280+
MalformedQueryException
281+
{
282+
throw unsupported();
283+
}
284+
285+
@Override
286+
public TupleQuery prepareTupleQuery(QueryLanguage ql, String query, String baseURI)
287+
throws RepositoryException,
288+
MalformedQueryException
289+
{
290+
throw unsupported();
291+
}
292+
293+
@Override
294+
public GraphQuery prepareGraphQuery(QueryLanguage ql, String query, String baseURI)
295+
throws RepositoryException,
296+
MalformedQueryException
297+
{
298+
throw unsupported();
299+
}
300+
301+
@Override
302+
public BooleanQuery prepareBooleanQuery(QueryLanguage ql, String query, String baseURI)
303+
throws RepositoryException,
304+
MalformedQueryException
305+
{
306+
throw unsupported();
307+
}
308+
309+
@Override
310+
public Update prepareUpdate(QueryLanguage ql, String update, String baseURI)
311+
throws RepositoryException,
312+
MalformedQueryException
313+
{
314+
throw unsupported();
315+
}
316+
317+
@Override
318+
protected void addWithoutCommit(Resource subj, IRI pred, Value obj, Resource... contexts)
319+
throws RepositoryException
320+
{
321+
added.add(subj, pred, obj, contexts);
322+
}
323+
324+
@Override
325+
protected void removeWithoutCommit(Resource subj, IRI pred, Value obj, Resource... contexts)
326+
throws RepositoryException
327+
{
328+
Model model = committed.filter(subj, pred, obj, contexts);
329+
removed.addAll(model);
330+
}
331+
332+
private Model loadModel() {
333+
Model model = new TreeModel();
334+
manager.getRepositoryIDs().forEach(id -> {
335+
Resource ctx = getContext(id);
336+
RepositoryConfig config = manager.getRepositoryConfig(id);
337+
Model cfg = new TreeModel();
338+
config.export(cfg, ctx);
339+
cfg.getNamespaces().forEach(ns -> {
340+
model.setNamespace(ns);
341+
});
342+
cfg.forEach(st -> {
343+
model.add(st.getSubject(), st.getPredicate(), st.getObject(), ctx);
344+
});
345+
});
346+
return model;
347+
}
348+
349+
private Resource getContext(String repositoryID) {
350+
String location;
351+
try {
352+
location = manager.getLocation().toURI().toString();
353+
}
354+
catch (MalformedURLException | URISyntaxException e) {
355+
assert false;
356+
location = "urn:" + repositoryID;
357+
}
358+
String url = Protocol.getRepositoryLocation(location, repositoryID);
359+
return getValueFactory().createIRI(url + "#" + repositoryID);
360+
}
361+
362+
private UnsupportedOperationException unsupported() {
363+
return new UnsupportedOperationException(
364+
"Query operations are not supported on the SYSTEM repository");
365+
}
366+
};
367+
}
368+
369+
}

core/http/server-spring/src/main/java/org/eclipse/rdf4j/http/server/repository/RepositoryInterceptor.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,11 @@ protected void setRequestAttributes(HttpServletRequest request)
102102
throws ClientHTTPException, ServerHTTPException
103103
{
104104
String nextRepositoryID = repositoryID;
105-
if (nextRepositoryID != null) {
105+
if (RepositoryConfigRepository.ID.equals(nextRepositoryID)) {
106+
request.setAttribute(REPOSITORY_ID_KEY, nextRepositoryID);
107+
request.setAttribute(REPOSITORY_KEY, new RepositoryConfigRepository(repositoryManager));
108+
}
109+
else if (nextRepositoryID != null) {
106110
try {
107111
Repository repository = repositoryManager.getRepository(nextRepositoryID);
108112

0 commit comments

Comments
 (0)