Skip to content

Commit

Permalink
GH-4952 - Introduce FedXConfig overrides for FedXRepositoryConfig
Browse files Browse the repository at this point in the history
- Provides a subset of options
  • Loading branch information
vtermanis committed Apr 17, 2024
1 parent f8c5a1e commit 07ca160
Show file tree
Hide file tree
Showing 6 changed files with 377 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/*******************************************************************************
* Copyright (c) 2024 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.federated.repository;

import static org.eclipse.rdf4j.federated.repository.FedXRepositoryConfig.NAMESPACE;

import org.eclipse.rdf4j.federated.FedXConfig;
import org.eclipse.rdf4j.model.BNode;
import org.eclipse.rdf4j.model.IRI;
import org.eclipse.rdf4j.model.Model;
import org.eclipse.rdf4j.model.Resource;
import org.eclipse.rdf4j.model.ValueFactory;
import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
import org.eclipse.rdf4j.model.util.Models;
import org.eclipse.rdf4j.model.util.Values;
import org.eclipse.rdf4j.repository.config.RepositoryConfigException;

/**
* A parser & exporter of {@link FedXConfig} to fine-tune FedX repositories when configured via
* {@link FedXRepositoryConfig}.
*
* @author Iotic Labs
*/
public class FedXConfigParser {

private static final ValueFactory vf = SimpleValueFactory.getInstance();

/**
* IRI of the property populating {@link FedXConfig#getEnforceMaxQueryTime()}
*/
public static final IRI CONFIG_ENFORCE_MAX_QUERY_TIME = vf.createIRI(NAMESPACE, "enforceMaxQueryTime");

/**
* IRI of the property populating {@link FedXConfig#isEnableMonitoring()}
*/
public static final IRI CONFIG_ENABLE_MONITORING = vf.createIRI(NAMESPACE, "enableMonitoring");

/**
* IRI of the property populating {@link FedXConfig#isLogQueryPlan()}
*/
public static final IRI CONFIG_LOG_QUERY_PLAN = vf.createIRI(NAMESPACE, "logQueryPlan");

/**
* IRI of the property populating {@link FedXConfig#isDebugQueryPlan()}
*/
public static final IRI CONFIG_DEBUG_QUERY_PLAN = vf.createIRI(NAMESPACE, "debugQueryPlan");

/**
* IRI of the property populating {@link FedXConfig#isLogQueries()}
*/
public static final IRI CONFIG_LOG_QUERIES = vf.createIRI(NAMESPACE, "logQueries");

/**
* IRI of the property populating {@link FedXConfig#getSourceSelectionCacheSpec()}
*/
public static final IRI CONFIG_SOURCE_SELECTION_CACHE_SPEC = vf.createIRI(NAMESPACE, "sourceSelectionCacheSpec");

private FedXConfigParser() {
}

/**
* Updates the provided {@link FedXConfig} with properties from the supplied model.
*
* @param config the configuration to be amended.
* @param m the model from which to read configuration properties
* @param confNode the subject against which to expect {@link FedXConfig} overrides.
*
* @return The updated {@link FedXConfig}
*
* @throws RepositoryConfigException if any of the overridden fields are deemed to be invalid
*/
public static FedXConfig parse(FedXConfig config, Model m, Resource confNode) throws RepositoryConfigException {
Models.objectLiteral(m.getStatements(confNode, CONFIG_ENFORCE_MAX_QUERY_TIME, null))
.ifPresent(value -> config.withEnforceMaxQueryTime(value.intValue()));

Models.objectLiteral(m.getStatements(confNode, CONFIG_ENABLE_MONITORING, null))
.ifPresent(value -> config.withEnableMonitoring(value.booleanValue()));

Models.objectLiteral(m.getStatements(confNode, CONFIG_LOG_QUERY_PLAN, null))
.ifPresent(value -> config.withLogQueryPlan(value.booleanValue()));

Models.objectLiteral(m.getStatements(confNode, CONFIG_DEBUG_QUERY_PLAN, null))
.ifPresent(value -> config.withDebugQueryPlan(value.booleanValue()));

Models.objectLiteral(m.getStatements(confNode, CONFIG_LOG_QUERIES, null))
.ifPresent(value -> config.withLogQueries(value.booleanValue()));

Models.objectLiteral(m.getStatements(confNode, CONFIG_SOURCE_SELECTION_CACHE_SPEC, null))
.ifPresent(value -> config.withSourceSelectionCacheSpec(value.stringValue()));

return config;
}

/**
* Export the provided {@link FedXConfig} to its RDF representation.
*
* @param config the configuration to export
* @param m the model to which to write configuration properties
*
* @return the node against which the configuration has been written
*/
public static Resource export(FedXConfig config, Model m) {
BNode confNode = Values.bnode();

m.add(confNode, CONFIG_ENFORCE_MAX_QUERY_TIME, vf.createLiteral(config.getEnforceMaxQueryTime()));

m.add(confNode, CONFIG_ENABLE_MONITORING, vf.createLiteral(config.isEnableMonitoring()));

m.add(confNode, CONFIG_LOG_QUERY_PLAN, vf.createLiteral(config.isLogQueryPlan()));

m.add(confNode, CONFIG_DEBUG_QUERY_PLAN, vf.createLiteral(config.isDebugQueryPlan()));

m.add(confNode, CONFIG_LOG_QUERIES, vf.createLiteral(config.isLogQueries()));

if (config.getSourceSelectionCacheSpec() != null) {
m.add(confNode, CONFIG_SOURCE_SELECTION_CACHE_SPEC, vf.createLiteral(config.getSourceSelectionCacheSpec()));
}

return confNode;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@
* # optionally define data config
* #fedx:fedxConfig "fedxConfig.prop" ;
* fedx:dataConfig "dataConfig.ttl" ;
*
* # optionally define FedXConfig overrides
* fedx:config [
* fedx:sourceSelectionCacheSpec "maximumSize=0" ;
* fedx:enforceMaxQueryTime 30 ;
* ]
* ];
* rep:repositoryID "fedx" ;
* rdfs:label "FedX Federation" .
Expand Down Expand Up @@ -87,6 +93,11 @@ public class FedXRepositoryConfig extends AbstractRepositoryImplConfig {
*/
public static final IRI DATA_CONFIG = vf.createIRI(NAMESPACE, "dataConfig");

/**
* IRI of the property pointing to the {@link FedXConfig}
*/
public static final IRI FEDX_CONFIG = vf.createIRI(NAMESPACE, "config");

/**
* IRI of the property pointing to a federation member node
*/
Expand Down Expand Up @@ -152,6 +163,11 @@ public Resource export(Model m) {
m.add(implNode, DATA_CONFIG, vf.createLiteral(getDataConfig()));
}

if (getConfig() != null) {
Resource confNode = FedXConfigParser.export(getConfig(), m);
m.add(implNode, FEDX_CONFIG, confNode);
}

if (getMembers() != null) {

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

Models.objectResource(m.getStatements(implNode, FEDX_CONFIG, null))
.ifPresent(res -> {
if (getConfig() == null) {
setConfig(new FedXConfig());
}
setConfig(FedXConfigParser.parse(getConfig(), m, res));
});

Set<Value> memberNodes = m.filter(implNode, MEMBER, null).objects();
if (!memberNodes.isEmpty()) {
Model members = new TreeModel();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/*******************************************************************************
* Copyright (c) 2024 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.federated.repository;

import static org.assertj.core.api.Assertions.assertThat;

import java.io.InputStream;

import org.eclipse.rdf4j.federated.FedXConfig;
import org.eclipse.rdf4j.model.Model;
import org.eclipse.rdf4j.model.Resource;
import org.eclipse.rdf4j.model.impl.TreeModel;
import org.eclipse.rdf4j.model.util.Models;
import org.eclipse.rdf4j.model.util.Values;
import org.eclipse.rdf4j.rio.RDFFormat;
import org.eclipse.rdf4j.rio.Rio;
import org.junit.jupiter.api.Test;

public class FedXConfigParserTest {

@Test
public void testParse() throws Exception {
Model model = readConfig("/tests/rdf4jserver/config-fedXConfig-only.ttl");

FedXConfig config = FedXConfigParser.parse(new FedXConfig(), model, Values.iri("http://example.org/conf"));

assertThat(config.getEnforceMaxQueryTime()).isEqualTo(1234);
assertThat(config.isEnableMonitoring()).isTrue();
assertThat(config.isLogQueryPlan()).isTrue();
assertThat(config.isDebugQueryPlan()).isTrue();
assertThat(config.isLogQueries()).isTrue();
assertThat(config.getSourceSelectionCacheSpec()).isEqualTo("spec-goes-here");
}

@Test
public void testParseWithEmptyConfig() throws Exception {
Model model = new TreeModel();

FedXConfig config = FedXConfigParser.parse(new FedXConfig(), model, Values.iri("http://example.org/conf"));

// expecting defaults
assertThat(config.getEnforceMaxQueryTime()).isEqualTo(30);
assertThat(config.isEnableMonitoring()).isFalse();
assertThat(config.isLogQueryPlan()).isFalse();
assertThat(config.isDebugQueryPlan()).isFalse();
assertThat(config.isLogQueries()).isFalse();
assertThat(config.getSourceSelectionCacheSpec()).isNull();
}

@Test
public void testExport() throws Exception {
Model model = readConfig("/tests/rdf4jserver/config-fedXConfig-only.ttl");

FedXConfig config = FedXConfigParser.parse(new FedXConfig(), model, Values.iri("http://example.org/conf"));

Model export = new TreeModel();
Resource configNode = FedXConfigParser.export(config, export);

assertThat(export.filter(configNode, null, null)).hasSize(6);

assertThat(
Models.objectLiteral(
export.getStatements(configNode, FedXConfigParser.CONFIG_ENFORCE_MAX_QUERY_TIME, null)))
.hasValueSatisfying(v -> assertThat(v.intValue()).isEqualTo(1234));
assertThat(
Models.objectLiteral(
export.getStatements(configNode, FedXConfigParser.CONFIG_ENABLE_MONITORING, null)))
.hasValueSatisfying(v -> assertThat(v.booleanValue()).isTrue());
assertThat(
Models.objectLiteral(
export.getStatements(configNode, FedXConfigParser.CONFIG_LOG_QUERY_PLAN, null)))
.hasValueSatisfying(v -> assertThat(v.booleanValue()).isTrue());
assertThat(
Models.objectLiteral(
export.getStatements(configNode, FedXConfigParser.CONFIG_DEBUG_QUERY_PLAN, null)))
.hasValueSatisfying(v -> assertThat(v.booleanValue()).isTrue());
assertThat(
Models.objectLiteral(
export.getStatements(configNode, FedXConfigParser.CONFIG_LOG_QUERIES, null)))
.hasValueSatisfying(v -> assertThat(v.booleanValue()).isTrue());
assertThat(
Models.objectLiteral(
export.getStatements(configNode, FedXConfigParser.CONFIG_SOURCE_SELECTION_CACHE_SPEC, null)))
.hasValueSatisfying(v -> assertThat(v.stringValue()).isEqualTo("spec-goes-here"));
}

@Test
public void testExportWithEmptyConfig() throws Exception {
Model export = new TreeModel();
Resource configNode = FedXConfigParser.export(new FedXConfig(), export);

// Note: 5 instead of 6 since CONFIG_SOURCE_SELECTION_CACHE_SPEC is null and thus should not be populated
assertThat(export.filter(configNode, null, null)).hasSize(5);

assertThat(
Models.objectLiteral(
export.getStatements(configNode, FedXConfigParser.CONFIG_ENFORCE_MAX_QUERY_TIME, null)))
.hasValueSatisfying(v -> assertThat(v.intValue()).isEqualTo(30));
assertThat(
Models.objectLiteral(
export.getStatements(configNode, FedXConfigParser.CONFIG_ENABLE_MONITORING, null)))
.hasValueSatisfying(v -> assertThat(v.booleanValue()).isFalse());
assertThat(
Models.objectLiteral(
export.getStatements(configNode, FedXConfigParser.CONFIG_LOG_QUERY_PLAN, null)))
.hasValueSatisfying(v -> assertThat(v.booleanValue()).isFalse());
assertThat(
Models.objectLiteral(
export.getStatements(configNode, FedXConfigParser.CONFIG_DEBUG_QUERY_PLAN, null)))
.hasValueSatisfying(v -> assertThat(v.booleanValue()).isFalse());
assertThat(
Models.objectLiteral(
export.getStatements(configNode, FedXConfigParser.CONFIG_LOG_QUERIES, null)))
.hasValueSatisfying(v -> assertThat(v.booleanValue()).isFalse());
}

protected Model readConfig(String configResource) throws Exception {
try (InputStream in = FedXRepositoryConfigTest.class.getResourceAsStream(configResource)) {
return Rio.parse(in, "http://example.org/", RDFFormat.TURTLE);
}
}
}

0 comments on commit 07ca160

Please sign in to comment.