Skip to content

Commit 07ca160

Browse files
committed
GH-4952 - Introduce FedXConfig overrides for FedXRepositoryConfig
- Provides a subset of options
1 parent f8c5a1e commit 07ca160

6 files changed

Lines changed: 377 additions & 0 deletions

File tree

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2024 Eclipse RDF4J contributors.
3+
*
4+
* All rights reserved. This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Distribution License v1.0
6+
* which accompanies this distribution, and is available at
7+
* http://www.eclipse.org/org/documents/edl-v10.php.
8+
*
9+
* SPDX-License-Identifier: BSD-3-Clause
10+
*******************************************************************************/
11+
package org.eclipse.rdf4j.federated.repository;
12+
13+
import static org.eclipse.rdf4j.federated.repository.FedXRepositoryConfig.NAMESPACE;
14+
15+
import org.eclipse.rdf4j.federated.FedXConfig;
16+
import org.eclipse.rdf4j.model.BNode;
17+
import org.eclipse.rdf4j.model.IRI;
18+
import org.eclipse.rdf4j.model.Model;
19+
import org.eclipse.rdf4j.model.Resource;
20+
import org.eclipse.rdf4j.model.ValueFactory;
21+
import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
22+
import org.eclipse.rdf4j.model.util.Models;
23+
import org.eclipse.rdf4j.model.util.Values;
24+
import org.eclipse.rdf4j.repository.config.RepositoryConfigException;
25+
26+
/**
27+
* A parser & exporter of {@link FedXConfig} to fine-tune FedX repositories when configured via
28+
* {@link FedXRepositoryConfig}.
29+
*
30+
* @author Iotic Labs
31+
*/
32+
public class FedXConfigParser {
33+
34+
private static final ValueFactory vf = SimpleValueFactory.getInstance();
35+
36+
/**
37+
* IRI of the property populating {@link FedXConfig#getEnforceMaxQueryTime()}
38+
*/
39+
public static final IRI CONFIG_ENFORCE_MAX_QUERY_TIME = vf.createIRI(NAMESPACE, "enforceMaxQueryTime");
40+
41+
/**
42+
* IRI of the property populating {@link FedXConfig#isEnableMonitoring()}
43+
*/
44+
public static final IRI CONFIG_ENABLE_MONITORING = vf.createIRI(NAMESPACE, "enableMonitoring");
45+
46+
/**
47+
* IRI of the property populating {@link FedXConfig#isLogQueryPlan()}
48+
*/
49+
public static final IRI CONFIG_LOG_QUERY_PLAN = vf.createIRI(NAMESPACE, "logQueryPlan");
50+
51+
/**
52+
* IRI of the property populating {@link FedXConfig#isDebugQueryPlan()}
53+
*/
54+
public static final IRI CONFIG_DEBUG_QUERY_PLAN = vf.createIRI(NAMESPACE, "debugQueryPlan");
55+
56+
/**
57+
* IRI of the property populating {@link FedXConfig#isLogQueries()}
58+
*/
59+
public static final IRI CONFIG_LOG_QUERIES = vf.createIRI(NAMESPACE, "logQueries");
60+
61+
/**
62+
* IRI of the property populating {@link FedXConfig#getSourceSelectionCacheSpec()}
63+
*/
64+
public static final IRI CONFIG_SOURCE_SELECTION_CACHE_SPEC = vf.createIRI(NAMESPACE, "sourceSelectionCacheSpec");
65+
66+
private FedXConfigParser() {
67+
}
68+
69+
/**
70+
* Updates the provided {@link FedXConfig} with properties from the supplied model.
71+
*
72+
* @param config the configuration to be amended.
73+
* @param m the model from which to read configuration properties
74+
* @param confNode the subject against which to expect {@link FedXConfig} overrides.
75+
*
76+
* @return The updated {@link FedXConfig}
77+
*
78+
* @throws RepositoryConfigException if any of the overridden fields are deemed to be invalid
79+
*/
80+
public static FedXConfig parse(FedXConfig config, Model m, Resource confNode) throws RepositoryConfigException {
81+
Models.objectLiteral(m.getStatements(confNode, CONFIG_ENFORCE_MAX_QUERY_TIME, null))
82+
.ifPresent(value -> config.withEnforceMaxQueryTime(value.intValue()));
83+
84+
Models.objectLiteral(m.getStatements(confNode, CONFIG_ENABLE_MONITORING, null))
85+
.ifPresent(value -> config.withEnableMonitoring(value.booleanValue()));
86+
87+
Models.objectLiteral(m.getStatements(confNode, CONFIG_LOG_QUERY_PLAN, null))
88+
.ifPresent(value -> config.withLogQueryPlan(value.booleanValue()));
89+
90+
Models.objectLiteral(m.getStatements(confNode, CONFIG_DEBUG_QUERY_PLAN, null))
91+
.ifPresent(value -> config.withDebugQueryPlan(value.booleanValue()));
92+
93+
Models.objectLiteral(m.getStatements(confNode, CONFIG_LOG_QUERIES, null))
94+
.ifPresent(value -> config.withLogQueries(value.booleanValue()));
95+
96+
Models.objectLiteral(m.getStatements(confNode, CONFIG_SOURCE_SELECTION_CACHE_SPEC, null))
97+
.ifPresent(value -> config.withSourceSelectionCacheSpec(value.stringValue()));
98+
99+
return config;
100+
}
101+
102+
/**
103+
* Export the provided {@link FedXConfig} to its RDF representation.
104+
*
105+
* @param config the configuration to export
106+
* @param m the model to which to write configuration properties
107+
*
108+
* @return the node against which the configuration has been written
109+
*/
110+
public static Resource export(FedXConfig config, Model m) {
111+
BNode confNode = Values.bnode();
112+
113+
m.add(confNode, CONFIG_ENFORCE_MAX_QUERY_TIME, vf.createLiteral(config.getEnforceMaxQueryTime()));
114+
115+
m.add(confNode, CONFIG_ENABLE_MONITORING, vf.createLiteral(config.isEnableMonitoring()));
116+
117+
m.add(confNode, CONFIG_LOG_QUERY_PLAN, vf.createLiteral(config.isLogQueryPlan()));
118+
119+
m.add(confNode, CONFIG_DEBUG_QUERY_PLAN, vf.createLiteral(config.isDebugQueryPlan()));
120+
121+
m.add(confNode, CONFIG_LOG_QUERIES, vf.createLiteral(config.isLogQueries()));
122+
123+
if (config.getSourceSelectionCacheSpec() != null) {
124+
m.add(confNode, CONFIG_SOURCE_SELECTION_CACHE_SPEC, vf.createLiteral(config.getSourceSelectionCacheSpec()));
125+
}
126+
127+
return confNode;
128+
}
129+
}

tools/federation/src/main/java/org/eclipse/rdf4j/federated/repository/FedXRepositoryConfig.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@
6060
* # optionally define data config
6161
* #fedx:fedxConfig "fedxConfig.prop" ;
6262
* fedx:dataConfig "dataConfig.ttl" ;
63+
*
64+
* # optionally define FedXConfig overrides
65+
* fedx:config [
66+
* fedx:sourceSelectionCacheSpec "maximumSize=0" ;
67+
* fedx:enforceMaxQueryTime 30 ;
68+
* ]
6369
* ];
6470
* rep:repositoryID "fedx" ;
6571
* rdfs:label "FedX Federation" .
@@ -87,6 +93,11 @@ public class FedXRepositoryConfig extends AbstractRepositoryImplConfig {
8793
*/
8894
public static final IRI DATA_CONFIG = vf.createIRI(NAMESPACE, "dataConfig");
8995

96+
/**
97+
* IRI of the property pointing to the {@link FedXConfig}
98+
*/
99+
public static final IRI FEDX_CONFIG = vf.createIRI(NAMESPACE, "config");
100+
90101
/**
91102
* IRI of the property pointing to a federation member node
92103
*/
@@ -152,6 +163,11 @@ public Resource export(Model m) {
152163
m.add(implNode, DATA_CONFIG, vf.createLiteral(getDataConfig()));
153164
}
154165

166+
if (getConfig() != null) {
167+
Resource confNode = FedXConfigParser.export(getConfig(), m);
168+
m.add(implNode, FEDX_CONFIG, confNode);
169+
}
170+
155171
if (getMembers() != null) {
156172

157173
Model members = getMembers();
@@ -187,6 +203,14 @@ public void parse(Model m, Resource implNode) throws RepositoryConfigException {
187203
Models.objectLiteral(m.getStatements(implNode, DATA_CONFIG, null))
188204
.ifPresent(value -> setDataConfig(value.stringValue()));
189205

206+
Models.objectResource(m.getStatements(implNode, FEDX_CONFIG, null))
207+
.ifPresent(res -> {
208+
if (getConfig() == null) {
209+
setConfig(new FedXConfig());
210+
}
211+
setConfig(FedXConfigParser.parse(getConfig(), m, res));
212+
});
213+
190214
Set<Value> memberNodes = m.filter(implNode, MEMBER, null).objects();
191215
if (!memberNodes.isEmpty()) {
192216
Model members = new TreeModel();
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2024 Eclipse RDF4J contributors.
3+
*
4+
* All rights reserved. This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Distribution License v1.0
6+
* which accompanies this distribution, and is available at
7+
* http://www.eclipse.org/org/documents/edl-v10.php.
8+
*
9+
* SPDX-License-Identifier: BSD-3-Clause
10+
*******************************************************************************/
11+
package org.eclipse.rdf4j.federated.repository;
12+
13+
import static org.assertj.core.api.Assertions.assertThat;
14+
15+
import java.io.InputStream;
16+
17+
import org.eclipse.rdf4j.federated.FedXConfig;
18+
import org.eclipse.rdf4j.model.Model;
19+
import org.eclipse.rdf4j.model.Resource;
20+
import org.eclipse.rdf4j.model.impl.TreeModel;
21+
import org.eclipse.rdf4j.model.util.Models;
22+
import org.eclipse.rdf4j.model.util.Values;
23+
import org.eclipse.rdf4j.rio.RDFFormat;
24+
import org.eclipse.rdf4j.rio.Rio;
25+
import org.junit.jupiter.api.Test;
26+
27+
public class FedXConfigParserTest {
28+
29+
@Test
30+
public void testParse() throws Exception {
31+
Model model = readConfig("/tests/rdf4jserver/config-fedXConfig-only.ttl");
32+
33+
FedXConfig config = FedXConfigParser.parse(new FedXConfig(), model, Values.iri("http://example.org/conf"));
34+
35+
assertThat(config.getEnforceMaxQueryTime()).isEqualTo(1234);
36+
assertThat(config.isEnableMonitoring()).isTrue();
37+
assertThat(config.isLogQueryPlan()).isTrue();
38+
assertThat(config.isDebugQueryPlan()).isTrue();
39+
assertThat(config.isLogQueries()).isTrue();
40+
assertThat(config.getSourceSelectionCacheSpec()).isEqualTo("spec-goes-here");
41+
}
42+
43+
@Test
44+
public void testParseWithEmptyConfig() throws Exception {
45+
Model model = new TreeModel();
46+
47+
FedXConfig config = FedXConfigParser.parse(new FedXConfig(), model, Values.iri("http://example.org/conf"));
48+
49+
// expecting defaults
50+
assertThat(config.getEnforceMaxQueryTime()).isEqualTo(30);
51+
assertThat(config.isEnableMonitoring()).isFalse();
52+
assertThat(config.isLogQueryPlan()).isFalse();
53+
assertThat(config.isDebugQueryPlan()).isFalse();
54+
assertThat(config.isLogQueries()).isFalse();
55+
assertThat(config.getSourceSelectionCacheSpec()).isNull();
56+
}
57+
58+
@Test
59+
public void testExport() throws Exception {
60+
Model model = readConfig("/tests/rdf4jserver/config-fedXConfig-only.ttl");
61+
62+
FedXConfig config = FedXConfigParser.parse(new FedXConfig(), model, Values.iri("http://example.org/conf"));
63+
64+
Model export = new TreeModel();
65+
Resource configNode = FedXConfigParser.export(config, export);
66+
67+
assertThat(export.filter(configNode, null, null)).hasSize(6);
68+
69+
assertThat(
70+
Models.objectLiteral(
71+
export.getStatements(configNode, FedXConfigParser.CONFIG_ENFORCE_MAX_QUERY_TIME, null)))
72+
.hasValueSatisfying(v -> assertThat(v.intValue()).isEqualTo(1234));
73+
assertThat(
74+
Models.objectLiteral(
75+
export.getStatements(configNode, FedXConfigParser.CONFIG_ENABLE_MONITORING, null)))
76+
.hasValueSatisfying(v -> assertThat(v.booleanValue()).isTrue());
77+
assertThat(
78+
Models.objectLiteral(
79+
export.getStatements(configNode, FedXConfigParser.CONFIG_LOG_QUERY_PLAN, null)))
80+
.hasValueSatisfying(v -> assertThat(v.booleanValue()).isTrue());
81+
assertThat(
82+
Models.objectLiteral(
83+
export.getStatements(configNode, FedXConfigParser.CONFIG_DEBUG_QUERY_PLAN, null)))
84+
.hasValueSatisfying(v -> assertThat(v.booleanValue()).isTrue());
85+
assertThat(
86+
Models.objectLiteral(
87+
export.getStatements(configNode, FedXConfigParser.CONFIG_LOG_QUERIES, null)))
88+
.hasValueSatisfying(v -> assertThat(v.booleanValue()).isTrue());
89+
assertThat(
90+
Models.objectLiteral(
91+
export.getStatements(configNode, FedXConfigParser.CONFIG_SOURCE_SELECTION_CACHE_SPEC, null)))
92+
.hasValueSatisfying(v -> assertThat(v.stringValue()).isEqualTo("spec-goes-here"));
93+
}
94+
95+
@Test
96+
public void testExportWithEmptyConfig() throws Exception {
97+
Model export = new TreeModel();
98+
Resource configNode = FedXConfigParser.export(new FedXConfig(), export);
99+
100+
// Note: 5 instead of 6 since CONFIG_SOURCE_SELECTION_CACHE_SPEC is null and thus should not be populated
101+
assertThat(export.filter(configNode, null, null)).hasSize(5);
102+
103+
assertThat(
104+
Models.objectLiteral(
105+
export.getStatements(configNode, FedXConfigParser.CONFIG_ENFORCE_MAX_QUERY_TIME, null)))
106+
.hasValueSatisfying(v -> assertThat(v.intValue()).isEqualTo(30));
107+
assertThat(
108+
Models.objectLiteral(
109+
export.getStatements(configNode, FedXConfigParser.CONFIG_ENABLE_MONITORING, null)))
110+
.hasValueSatisfying(v -> assertThat(v.booleanValue()).isFalse());
111+
assertThat(
112+
Models.objectLiteral(
113+
export.getStatements(configNode, FedXConfigParser.CONFIG_LOG_QUERY_PLAN, null)))
114+
.hasValueSatisfying(v -> assertThat(v.booleanValue()).isFalse());
115+
assertThat(
116+
Models.objectLiteral(
117+
export.getStatements(configNode, FedXConfigParser.CONFIG_DEBUG_QUERY_PLAN, null)))
118+
.hasValueSatisfying(v -> assertThat(v.booleanValue()).isFalse());
119+
assertThat(
120+
Models.objectLiteral(
121+
export.getStatements(configNode, FedXConfigParser.CONFIG_LOG_QUERIES, null)))
122+
.hasValueSatisfying(v -> assertThat(v.booleanValue()).isFalse());
123+
}
124+
125+
protected Model readConfig(String configResource) throws Exception {
126+
try (InputStream in = FedXRepositoryConfigTest.class.getResourceAsStream(configResource)) {
127+
return Rio.parse(in, "http://example.org/", RDFFormat.TURTLE);
128+
}
129+
}
130+
}

0 commit comments

Comments
 (0)