diff --git a/core/collection-factory/mapdb3/pom.xml b/core/collection-factory/mapdb3/pom.xml index 7a5b287eb23..a00d4c8e811 100644 --- a/core/collection-factory/mapdb3/pom.xml +++ b/core/collection-factory/mapdb3/pom.xml @@ -27,7 +27,7 @@ org.mapdb mapdb - 3.0.9 + 3.1.0 net.jpountz.lz4 diff --git a/core/query/src/main/java/org/eclipse/rdf4j/query/impl/FallbackDataset.java b/core/query/src/main/java/org/eclipse/rdf4j/query/impl/FallbackDataset.java index 0eb7c1c72d5..7b4fe2cd2a6 100644 --- a/core/query/src/main/java/org/eclipse/rdf4j/query/impl/FallbackDataset.java +++ b/core/query/src/main/java/org/eclipse/rdf4j/query/impl/FallbackDataset.java @@ -113,4 +113,12 @@ private void appendURI(StringBuilder sb, IRI uri) { } } + public Dataset getPrimary() { + return primary; + } + + public Dataset getFallback() { + return fallback; + } + } diff --git a/core/queryresultio/api/src/main/java/org/eclipse/rdf4j/query/resultio/BasicQueryWriterSettings.java b/core/queryresultio/api/src/main/java/org/eclipse/rdf4j/query/resultio/BasicQueryWriterSettings.java index 15d68510c43..61b1b94b668 100644 --- a/core/queryresultio/api/src/main/java/org/eclipse/rdf4j/query/resultio/BasicQueryWriterSettings.java +++ b/core/queryresultio/api/src/main/java/org/eclipse/rdf4j/query/resultio/BasicQueryWriterSettings.java @@ -11,7 +11,9 @@ package org.eclipse.rdf4j.query.resultio; import org.eclipse.rdf4j.rio.RioSetting; +import org.eclipse.rdf4j.rio.helpers.BooleanRioSetting; import org.eclipse.rdf4j.rio.helpers.RioSettingImpl; +import org.eclipse.rdf4j.rio.helpers.StringRioSetting; /** * {@link RioSetting} constants to use with {@link QueryResultWriter}s. @@ -26,7 +28,7 @@ public class BasicQueryWriterSettings { *

* Defaults to false. */ - public final static RioSetting ADD_SESAME_QNAME = new RioSettingImpl<>( + public final static BooleanRioSetting ADD_SESAME_QNAME = new BooleanRioSetting( "org.eclipse.rdf4j.query.resultio.addsesameqname", "Add Sesame QName", false); /** @@ -35,7 +37,7 @@ public class BasicQueryWriterSettings { *

* Defaults to "sesamecallback". */ - public static final RioSetting JSONP_CALLBACK = new RioSettingImpl<>( + public static final StringRioSetting JSONP_CALLBACK = new StringRioSetting( "org.eclipse.rdf4j.query.resultio.jsonpcallback", "JSONP callback function", "sesamecallback"); /** diff --git a/core/repository/api/src/main/java/org/eclipse/rdf4j/repository/config/RepositoryConfig.java b/core/repository/api/src/main/java/org/eclipse/rdf4j/repository/config/RepositoryConfig.java index 42f5d33055b..85ba45642ae 100644 --- a/core/repository/api/src/main/java/org/eclipse/rdf4j/repository/config/RepositoryConfig.java +++ b/core/repository/api/src/main/java/org/eclipse/rdf4j/repository/config/RepositoryConfig.java @@ -156,6 +156,7 @@ private void exportLegacy(Model model, Resource repositoryNode) { model.setNamespace(RDFS.NS); model.setNamespace(XSD.NS); model.setNamespace("rep", RepositoryConfigSchema.NAMESPACE); + model.add(repositoryNode, RDF.TYPE, RepositoryConfigSchema.REPOSITORY); if (id != null) { model.add(repositoryNode, RepositoryConfigSchema.REPOSITORYID, literal(id)); diff --git a/core/repository/http/src/main/java/org/eclipse/rdf4j/repository/http/helpers/HTTPRepositorySettings.java b/core/repository/http/src/main/java/org/eclipse/rdf4j/repository/http/helpers/HTTPRepositorySettings.java index 193610d52a1..bb200e8a676 100644 --- a/core/repository/http/src/main/java/org/eclipse/rdf4j/repository/http/helpers/HTTPRepositorySettings.java +++ b/core/repository/http/src/main/java/org/eclipse/rdf4j/repository/http/helpers/HTTPRepositorySettings.java @@ -12,6 +12,7 @@ import org.eclipse.rdf4j.repository.http.HTTPRepository; import org.eclipse.rdf4j.rio.RioSetting; +import org.eclipse.rdf4j.rio.helpers.IntegerRioSetting; import org.eclipse.rdf4j.rio.helpers.RioSettingImpl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -31,7 +32,7 @@ public class HTTPRepositorySettings { * By default inner buffers within {@link org.eclipse.rdf4j.repository.http.HTTPRepositoryConnection} keep in memory * up to 200000 statement before they are flushed to the remote repository. */ - public static final RioSetting MAX_STATEMENT_BUFFER_SIZE = new RioSettingImpl<>( + public static final IntegerRioSetting MAX_STATEMENT_BUFFER_SIZE = new IntegerRioSetting( "org.eclipse.rdf4j.http.maxstatementbuffersize", "Maximum number of statement buffered in memory", 200000); } diff --git a/core/rio/api/pom.xml b/core/rio/api/pom.xml index 4e5df955433..3733b074423 100644 --- a/core/rio/api/pom.xml +++ b/core/rio/api/pom.xml @@ -80,6 +80,18 @@ + + com.fasterxml.jackson.core + jackson-annotations + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-databind + commons-io commons-io diff --git a/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/BasicParserSettings.java b/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/BasicParserSettings.java index bc363b70350..e6dc74eecee 100644 --- a/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/BasicParserSettings.java +++ b/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/BasicParserSettings.java @@ -46,7 +46,7 @@ public class BasicParserSettings { *

* Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.verify_datatype_values}. */ - public static final RioSetting VERIFY_DATATYPE_VALUES = new BooleanRioSetting( + public static final BooleanRioSetting VERIFY_DATATYPE_VALUES = new BooleanRioSetting( "org.eclipse.rdf4j.rio.verify_datatype_values", "Verify recognised datatype values", Boolean.FALSE); /** @@ -58,7 +58,7 @@ public class BasicParserSettings { *

* Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.fail_on_unknown_datatypes}. */ - public static final RioSetting FAIL_ON_UNKNOWN_DATATYPES = new BooleanRioSetting( + public static final BooleanRioSetting FAIL_ON_UNKNOWN_DATATYPES = new BooleanRioSetting( "org.eclipse.rdf4j.rio.fail_on_unknown_datatypes", "Fail on unknown datatypes", Boolean.FALSE); /** @@ -70,7 +70,7 @@ public class BasicParserSettings { *

* Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.normalize_datatype_values}. */ - public static final RioSetting NORMALIZE_DATATYPE_VALUES = new BooleanRioSetting( + public static final BooleanRioSetting NORMALIZE_DATATYPE_VALUES = new BooleanRioSetting( "org.eclipse.rdf4j.rio.normalize_datatype_values", "Normalize recognised datatype values", Boolean.FALSE); /** @@ -91,7 +91,7 @@ public class BasicParserSettings { *

* Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.fail_on_unknown_languages}. */ - public static final RioSetting FAIL_ON_UNKNOWN_LANGUAGES = new BooleanRioSetting( + public static final BooleanRioSetting FAIL_ON_UNKNOWN_LANGUAGES = new BooleanRioSetting( "org.eclipse.rdf4j.rio.fail_on_unknown_languages", "Fail on unknown languages", Boolean.FALSE); /** @@ -104,7 +104,7 @@ public class BasicParserSettings { *

* Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.verify_language_tags}. */ - public static final RioSetting VERIFY_LANGUAGE_TAGS = new BooleanRioSetting( + public static final BooleanRioSetting VERIFY_LANGUAGE_TAGS = new BooleanRioSetting( "org.eclipse.rdf4j.rio.verify_language_tags", "Verify language tags", Boolean.TRUE); /** @@ -116,7 +116,7 @@ public class BasicParserSettings { *

* Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.normalize_language_tags}. */ - public static final RioSetting NORMALIZE_LANGUAGE_TAGS = new BooleanRioSetting( + public static final BooleanRioSetting NORMALIZE_LANGUAGE_TAGS = new BooleanRioSetting( "org.eclipse.rdf4j.rio.normalize_language_tags", "Normalize recognised language tags", Boolean.FALSE); /** @@ -134,7 +134,7 @@ public class BasicParserSettings { *

* Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.verify_relative_uris}. */ - public static final RioSetting VERIFY_RELATIVE_URIS = new BooleanRioSetting( + public static final BooleanRioSetting VERIFY_RELATIVE_URIS = new BooleanRioSetting( "org.eclipse.rdf4j.rio.verify_relative_uris", "Verify relative URIs", Boolean.TRUE); /** @@ -145,7 +145,7 @@ public class BasicParserSettings { *

* Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.verify_uri_syntax}. */ - public static final RioSetting VERIFY_URI_SYNTAX = new BooleanRioSetting( + public static final BooleanRioSetting VERIFY_URI_SYNTAX = new BooleanRioSetting( "org.eclipse.rdf4j.rio.verify_uri_syntax", "Verify URI syntax", Boolean.TRUE); /** @@ -156,7 +156,7 @@ public class BasicParserSettings { *

* Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.preserve_bnode_ids}. */ - public static final RioSetting PRESERVE_BNODE_IDS = new BooleanRioSetting( + public static final BooleanRioSetting PRESERVE_BNODE_IDS = new BooleanRioSetting( "org.eclipse.rdf4j.rio.preserve_bnode_ids", "Preserve blank node identifiers", Boolean.FALSE); /** @@ -168,7 +168,7 @@ public class BasicParserSettings { *

* Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.skolem_origin}. */ - public static final RioSetting SKOLEMIZE_ORIGIN = new StringRioSetting( + public static final StringRioSetting SKOLEMIZE_ORIGIN = new StringRioSetting( "org.eclipse.rdf4j.rio.skolem_origin", "Replace blank nodes with well known genid IRIs using this scheme and authority", null); @@ -197,7 +197,7 @@ public class BasicParserSettings { *

* Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.large_literals_limit}. */ - public static final RioSetting LARGE_LITERALS_LIMIT = new LongRioSetting( + public static final LongRioSetting LARGE_LITERALS_LIMIT = new LongRioSetting( "org.eclipse.rdf4j.rio.large_literals_limit", "Size limit for large literals", 1048576L); /** @@ -227,7 +227,7 @@ public class BasicParserSettings { *

* Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.process_encoded_rdf_star}. */ - public static final RioSetting PROCESS_ENCODED_RDF_STAR = new BooleanRioSetting( + public static final BooleanRioSetting PROCESS_ENCODED_RDF_STAR = new BooleanRioSetting( "org.eclipse.rdf4j.rio.process_encoded_rdf_star", "Converts RDF-star triples encoded as RDF-compatible IRIs back to triple values", Boolean.TRUE); diff --git a/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/BasicWriterSettings.java b/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/BasicWriterSettings.java index b5dd467393e..87083c8b22b 100644 --- a/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/BasicWriterSettings.java +++ b/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/BasicWriterSettings.java @@ -26,7 +26,7 @@ public class BasicWriterSettings { *

* Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.pretty_print}. */ - public static final RioSetting PRETTY_PRINT = new BooleanRioSetting("org.eclipse.rdf4j.rio.pretty_print", + public static final BooleanRioSetting PRETTY_PRINT = new BooleanRioSetting("org.eclipse.rdf4j.rio.pretty_print", "Pretty print", Boolean.TRUE); /** @@ -42,7 +42,7 @@ public class BasicWriterSettings { * * @since 2.3 */ - public static final RioSetting INLINE_BLANK_NODES = new BooleanRioSetting( + public static final BooleanRioSetting INLINE_BLANK_NODES = new BooleanRioSetting( "org.eclipse.rdf4j.rio.inline_blank_nodes", "Use blank node property lists, collections, and anonymous nodes instead of blank node labels", Boolean.FALSE); @@ -57,7 +57,7 @@ public class BasicWriterSettings { *

* Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.rdf10_plain_literals}. */ - public static final RioSetting XSD_STRING_TO_PLAIN_LITERAL = new BooleanRioSetting( + public static final BooleanRioSetting XSD_STRING_TO_PLAIN_LITERAL = new BooleanRioSetting( "org.eclipse.rdf4j.rio.rdf10_plain_literals", "RDF-1.0 compatible Plain Literals", Boolean.TRUE); /** @@ -78,7 +78,7 @@ public class BasicWriterSettings { *

* Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.rdf10_language_literals}. */ - public static final RioSetting RDF_LANGSTRING_TO_LANG_LITERAL = new BooleanRioSetting( + public static final BooleanRioSetting RDF_LANGSTRING_TO_LANG_LITERAL = new BooleanRioSetting( "org.eclipse.rdf4j.rio.rdf10_language_literals", "RDF-1.0 compatible Language Literals", Boolean.TRUE); /** @@ -88,7 +88,7 @@ public class BasicWriterSettings { *

* Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.base_directive}. */ - public static final RioSetting BASE_DIRECTIVE = new BooleanRioSetting( + public static final BooleanRioSetting BASE_DIRECTIVE = new BooleanRioSetting( "org.eclipse.rdf4j.rio.base_directive", "Serialize base directive", Boolean.TRUE); /** @@ -99,7 +99,7 @@ public class BasicWriterSettings { *

* Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.convert_rdf_star}. */ - public static final RioSetting CONVERT_RDF_STAR_TO_REIFICATION = new BooleanRioSetting( + public static final BooleanRioSetting CONVERT_RDF_STAR_TO_REIFICATION = new BooleanRioSetting( "org.eclipse.rdf4j.rio.convert_rdf_star", "Convert RDF-star statements to RDF reification", Boolean.FALSE); /** @@ -113,7 +113,7 @@ public class BasicWriterSettings { *

* Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.encode_rdf_star}. */ - public static final RioSetting ENCODE_RDF_STAR = new BooleanRioSetting( + public static final BooleanRioSetting ENCODE_RDF_STAR = new BooleanRioSetting( "org.eclipse.rdf4j.rio.encode_rdf_star", "Encodes RDF-star triples to special IRIs for compatibility with RDF", Boolean.TRUE); diff --git a/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/BinaryRDFWriterSettings.java b/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/BinaryRDFWriterSettings.java index 9627c241331..1086040ec97 100644 --- a/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/BinaryRDFWriterSettings.java +++ b/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/BinaryRDFWriterSettings.java @@ -32,7 +32,7 @@ public class BinaryRDFWriterSettings { * @deprecated Use {@link org.eclipse.rdf4j.rio.binary.BinaryRDFWriterSettings#VERSION} instead. */ @Deprecated(since = "4.3.0", forRemoval = true) - public static final RioSetting VERSION = new LongRioSetting( + public static final LongRioSetting VERSION = new LongRioSetting( "org.eclipse.rdf4j.rio.binary.format_version", "Binary RDF format", 2L); /** @@ -46,7 +46,7 @@ public class BinaryRDFWriterSettings { * @deprecated Use {@link org.eclipse.rdf4j.rio.binary.BinaryRDFWriterSettings#BUFFER_SIZE} instead. */ @Deprecated(since = "4.3.0", forRemoval = true) - public static final RioSetting BUFFER_SIZE = new LongRioSetting( + public static final LongRioSetting BUFFER_SIZE = new LongRioSetting( "org.eclipse.rdf4j.rio.binary.buffer_size", "Buffer size", 8192L); /** @@ -60,7 +60,7 @@ public class BinaryRDFWriterSettings { * @deprecated Use {@link org.eclipse.rdf4j.rio.binary.BinaryRDFWriterSettings#CHARSET} instead. */ @Deprecated(since = "4.3.0", forRemoval = true) - public static final RioSetting CHARSET = new StringRioSetting( + public static final StringRioSetting CHARSET = new StringRioSetting( "org.eclipse.rdf4j.rio.binary.charset", "Charset", StandardCharsets.UTF_8.name()); /** @@ -81,7 +81,7 @@ public class BinaryRDFWriterSettings { * @deprecated Use {@link org.eclipse.rdf4j.rio.binary.BinaryRDFWriterSettings#RECYCLE_IDS} instead. */ @Deprecated(since = "4.3.0", forRemoval = true) - public static final RioSetting RECYCLE_IDS = new BooleanRioSetting( + public static final BooleanRioSetting RECYCLE_IDS = new BooleanRioSetting( "org.eclipse.rdf4j.rio.binary.recycle_ids", "Charset", true); /** diff --git a/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/IntegerRioSetting.java b/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/IntegerRioSetting.java new file mode 100644 index 00000000000..4edf145a99c --- /dev/null +++ b/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/IntegerRioSetting.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * 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.rio.helpers; + +import org.eclipse.rdf4j.rio.RioSetting; + +/** + * A {@link RioSetting} with a {@link Integer} value. The given default for the setting can be overridden by means of a + * system property with a name equal to the setting key. + * + * @author Håvard M. Ottestad + */ +public class IntegerRioSetting extends AbstractRioSetting { + + private static final long serialVersionUID = -5945095126593465950L; + + public IntegerRioSetting(String key, String description, Integer defaultValue) { + super(key, description, defaultValue); + } + + @Override + public Integer convert(String stringValue) { + try { + return Integer.parseInt(stringValue); + } catch (NumberFormatException e) { + throw new RioConfigurationException("Conversion error for setting: " + getKey(), e); + } + } + +} diff --git a/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/JSONLDSettings.java b/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/JSONLDSettings.java index fc4d5592ba3..70b37632553 100644 --- a/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/JSONLDSettings.java +++ b/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/JSONLDSettings.java @@ -10,7 +10,6 @@ *******************************************************************************/ package org.eclipse.rdf4j.rio.helpers; -import java.util.List; import java.util.Set; import org.eclipse.rdf4j.rio.RioSetting; @@ -38,7 +37,7 @@ public class JSONLDSettings { * @see JSONLD Data Structures * */ - public static final RioSetting COMPACT_ARRAYS = new BooleanRioSetting( + public static final BooleanRioSetting COMPACT_ARRAYS = new BooleanRioSetting( "org.eclipse.rdf4j.rio.jsonld.compact_arrays", "Compact arrays", Boolean.TRUE); /** @@ -69,7 +68,7 @@ public class JSONLDSettings { * The JSON-LD processor will throw an exception if a warning is encountered during processing. * */ - public static final RioSetting EXCEPTION_ON_WARNING = new ClassRioSetting<>( + public static final BooleanRioSetting EXCEPTION_ON_WARNING = new BooleanRioSetting( "org.eclipse.rdf4j.rio.jsonld.exception_on_warning", "Throw an exception when logging a warning.", Boolean.FALSE); @@ -86,7 +85,7 @@ public class JSONLDSettings { * @see JSONLD Data Structures * */ - public static final RioSetting OPTIMIZE = new BooleanRioSetting("org.eclipse.rdf4j.rio.jsonld.optimize", + public static final BooleanRioSetting OPTIMIZE = new BooleanRioSetting("org.eclipse.rdf4j.rio.jsonld.optimize", "Optimize output", Boolean.FALSE); /** @@ -102,7 +101,7 @@ public class JSONLDSettings { * @see JSONLD Data Structures * */ - public static final RioSetting PRODUCE_GENERALIZED_RDF = new BooleanRioSetting( + public static final BooleanRioSetting PRODUCE_GENERALIZED_RDF = new BooleanRioSetting( "org.eclipse.rdf4j.rio.jsonld.produce_generalized_rdf", "Produce generalized RDF", Boolean.FALSE); /** @@ -117,7 +116,7 @@ public class JSONLDSettings { * @see JSONLD Data Structures * */ - public static final RioSetting USE_NATIVE_TYPES = new BooleanRioSetting( + public static final BooleanRioSetting USE_NATIVE_TYPES = new BooleanRioSetting( "org.eclipse.rdf4j.rio.jsonld.use_native_types", "Use Native JSON Types", Boolean.FALSE); /** @@ -131,7 +130,7 @@ public class JSONLDSettings { * @see JSONLD Data Structures * */ - public static final RioSetting USE_RDF_TYPE = new BooleanRioSetting( + public static final BooleanRioSetting USE_RDF_TYPE = new BooleanRioSetting( "org.eclipse.rdf4j.rio.jsonld.use_rdf_type", "Use RDF Type", Boolean.FALSE); /** @@ -153,17 +152,19 @@ public class JSONLDSettings { * Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.jsonld.hierarchical_view}. * */ - public static final RioSetting HIERARCHICAL_VIEW = new BooleanRioSetting( + public static final BooleanRioSetting HIERARCHICAL_VIEW = new BooleanRioSetting( "org.eclipse.rdf4j.rio.jsonld.hierarchical_view", "Hierarchical representation of the JSON", Boolean.FALSE); /** - * Whitelist of remote/local resources that the JSON-LD parser can retrieve. Set of URIs as strings. + * Whitelist of remote/local resources that the JSON-LD parser can retrieve. Set of URIs as strings. This can be + * overridden by setting a system property with the key {@code org.eclipse.rdf4j.rio.jsonld_whitelist} and a JSON + * array of the desired values. *

* Default: * {@code Set.of("http://www.w3.org/ns/anno.jsonld", "http://www.w3.org/ns/activitystreams.jsonld", "http://www.w3.org/ns/ldp.jsonld", "http://www.w3.org/ns/oa.jsonld", "http://www.w3.org/ns/hydra/context.jsonld", "http://schema.org/", "https://w3id.org/security/v1", "https://w3c.github.io/json-ld-rc/context.jsonld", "https://www.w3.org/2018/credentials/v1", "https://health-lifesci.schema.org/", "https://auto.schema.org/", "https://bib.schema.org/", "http://xmlns.com/foaf/spec/index.jsonld", "https://pending.schema.org/", "https://schema.org/", "https://schema.org/docs/jsonldcontext.jsonld", "https://schema.org/version/latest/schemaorg-current-https.jsonld", "https://schema.org/version/latest/schemaorg-all-http.jsonld", "https://schema.org/version/latest/schemaorg-all-https.jsonld", "https://schema.org/version/latest/schemaorg-current-http.jsonld", "https://schema.org/version/latest/schemaorg-all.jsonld", "https://schema.org/version/latest/schemaorg-current.jsonld", "https://project-open-data.cio.gov/v1.1/schema/catalog.jsonld", "https://geojson.org/geojson-ld/geojson-context.jsonld", "https://www.w3.org/2019/wot/td/v1"); * */ - public static final RioSetting> WHITELIST = new RioSettingImpl<>( + public static final SetRioSetting WHITELIST = new SetRioSetting<>( "org.eclipse.rdf4j.rio.jsonld_whitelist", "Whitelist of remote/local resources that the JSON-LD parser can retrieve. Set of URIs as strings.", Set.of( @@ -195,11 +196,13 @@ public class JSONLDSettings { )); /** - * Secure mode only allows loading remote/local resources (ex. context from url) that are whitelisted. + * Secure mode only allows loading remote/local resources (ex. context from url) that are whitelisted. This can be + * overridden by setting a system property with the key {@code org.eclipse.rdf4j.rio.jsonld_secure_mode} and a + * boolean value. *

* Default: true */ - public static final RioSetting SECURE_MODE = new RioSettingImpl<>( + public static final BooleanRioSetting SECURE_MODE = new BooleanRioSetting( "org.eclipse.rdf4j.rio.jsonld_secure_mode", "Secure mode only allows loading remote/local resources (ex. context from url) that are whitelisted.", Boolean.TRUE); @@ -211,7 +214,7 @@ public class JSONLDSettings { *

* Default: true */ - public static final RioSetting DOCUMENT_LOADER_CACHE = new RioSettingImpl<>( + public static final BooleanRioSetting DOCUMENT_LOADER_CACHE = new BooleanRioSetting( "org.eclipse.rdf4j.rio.jsonld_document_loader_cache", "The document loader cache is enabled by default. All loaded documents, such as remote contexts, are cached for 1 hour, or until the cache is full. The cache holds up to 1000 documents. The cache is shared between all JSONLDParsers. The cache can be disabled by setting this value to false.", Boolean.TRUE); diff --git a/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/JSONSettings.java b/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/JSONSettings.java index 5d5c9a4d33b..0ac1ebaca5d 100644 --- a/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/JSONSettings.java +++ b/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/JSONSettings.java @@ -29,7 +29,7 @@ public class JSONSettings { * Can be overridden by setting system property * {@code org.eclipse.rdf4j.rio.json.allow_backslash_escaping_any_character}. */ - public static final RioSetting ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER = new BooleanRioSetting( + public static final BooleanRioSetting ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER = new BooleanRioSetting( "org.eclipse.rdf4j.rio.json.allow_backslash_escaping_any_character", "Allow backslash escaping any character", Boolean.FALSE); @@ -40,7 +40,7 @@ public class JSONSettings { *

* Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.json.allow_comments}. */ - public static final RioSetting ALLOW_COMMENTS = new BooleanRioSetting( + public static final BooleanRioSetting ALLOW_COMMENTS = new BooleanRioSetting( "org.eclipse.rdf4j.rio.json.allow_comments", "Allow comments", Boolean.FALSE); /** @@ -50,7 +50,7 @@ public class JSONSettings { *

* Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.json.allow_non_numeric_numbers}. */ - public static final RioSetting ALLOW_NON_NUMERIC_NUMBERS = new BooleanRioSetting( + public static final BooleanRioSetting ALLOW_NON_NUMERIC_NUMBERS = new BooleanRioSetting( "org.eclipse.rdf4j.rio.json.allow_non_numeric_numbers", "Allow non-numeric numbers", Boolean.FALSE); /** @@ -60,7 +60,7 @@ public class JSONSettings { *

* Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.json.allow_numeric_leading_zeros}. */ - public static final RioSetting ALLOW_NUMERIC_LEADING_ZEROS = new BooleanRioSetting( + public static final BooleanRioSetting ALLOW_NUMERIC_LEADING_ZEROS = new BooleanRioSetting( "org.eclipse.rdf4j.rio.json.allow_numeric_leading_zeros", "Allow numeric leading zeros", Boolean.FALSE); /** @@ -70,7 +70,7 @@ public class JSONSettings { *

* Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.json.allow_single_quotes}. */ - public static final RioSetting ALLOW_SINGLE_QUOTES = new BooleanRioSetting( + public static final BooleanRioSetting ALLOW_SINGLE_QUOTES = new BooleanRioSetting( "org.eclipse.rdf4j.rio.json.allow_single_quotes", "Allow single quotes", Boolean.FALSE); /** @@ -80,7 +80,7 @@ public class JSONSettings { *

* Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.json.allow_unquoted_control_chars}. */ - public static final RioSetting ALLOW_UNQUOTED_CONTROL_CHARS = new BooleanRioSetting( + public static final BooleanRioSetting ALLOW_UNQUOTED_CONTROL_CHARS = new BooleanRioSetting( "org.eclipse.rdf4j.rio.json.allow_unquoted_control_chars", "Allow unquoted control chars", Boolean.FALSE); /** @@ -90,7 +90,7 @@ public class JSONSettings { *

* Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.json.allow_unquoted_field_names}. */ - public static final RioSetting ALLOW_UNQUOTED_FIELD_NAMES = new BooleanRioSetting( + public static final BooleanRioSetting ALLOW_UNQUOTED_FIELD_NAMES = new BooleanRioSetting( "org.eclipse.rdf4j.rio.json.allow_unquoted_field_names", "Allow unquoted field names", Boolean.FALSE); /** @@ -100,7 +100,7 @@ public class JSONSettings { *

* Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.json.allow_yaml_comments}. */ - public static final RioSetting ALLOW_YAML_COMMENTS = new BooleanRioSetting( + public static final BooleanRioSetting ALLOW_YAML_COMMENTS = new BooleanRioSetting( "org.eclipse.rdf4j.rio.json.allow_yaml_comments", "Allow YAML comments", Boolean.FALSE); /** @@ -110,7 +110,7 @@ public class JSONSettings { *

* Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.json.allow_trailing_comma}. */ - public static final RioSetting ALLOW_TRAILING_COMMA = new BooleanRioSetting( + public static final BooleanRioSetting ALLOW_TRAILING_COMMA = new BooleanRioSetting( "org.eclipse.rdf4j.rio.json.allow_trailing_comma", "Allow trailing comma", Boolean.FALSE); /** @@ -120,7 +120,7 @@ public class JSONSettings { *

* Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.json.include_source_in_location}. */ - public static final RioSetting INCLUDE_SOURCE_IN_LOCATION = new BooleanRioSetting( + public static final BooleanRioSetting INCLUDE_SOURCE_IN_LOCATION = new BooleanRioSetting( "org.eclipse.rdf4j.rio.json.include_source_in_location", "Include Source in Location", Boolean.TRUE); /** @@ -131,7 +131,7 @@ public class JSONSettings { *

* Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.json.strict_duplicate_detection}. */ - public static final RioSetting STRICT_DUPLICATE_DETECTION = new BooleanRioSetting( + public static final BooleanRioSetting STRICT_DUPLICATE_DETECTION = new BooleanRioSetting( "org.eclipse.rdf4j.rio.json.strict_duplicate_detection", "Strict duplicate detection", Boolean.FALSE); /** diff --git a/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/NTriplesParserSettings.java b/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/NTriplesParserSettings.java index 253a8854a7a..7ee7adebde4 100644 --- a/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/NTriplesParserSettings.java +++ b/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/NTriplesParserSettings.java @@ -32,7 +32,7 @@ public class NTriplesParserSettings { * @deprecated Use {@link org.eclipse.rdf4j.rio.ntriples.NTriplesParserSettings#FAIL_ON_INVALID_LINES} instead. */ @Deprecated(since = "4.3.0", forRemoval = true) - public static final RioSetting FAIL_ON_INVALID_LINES = new BooleanRioSetting( + public static final BooleanRioSetting FAIL_ON_INVALID_LINES = new BooleanRioSetting( "org.eclipse.rdf4j.rio.ntriples.fail_on_invalid_lines", "Fail on N-Triples invalid lines", Boolean.TRUE); /** diff --git a/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/NTriplesWriterSettings.java b/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/NTriplesWriterSettings.java index c7ea6af467b..0708d789bdb 100644 --- a/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/NTriplesWriterSettings.java +++ b/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/NTriplesWriterSettings.java @@ -29,7 +29,7 @@ public class NTriplesWriterSettings { * @deprecated Use {@link org.eclipse.rdf4j.rio.ntriples.NTriplesWriterSettings#ESCAPE_UNICODE} instead. */ @Deprecated(since = "4.3.0", forRemoval = true) - public static final RioSetting ESCAPE_UNICODE = new BooleanRioSetting( + public static final BooleanRioSetting ESCAPE_UNICODE = new BooleanRioSetting( "org.eclipse.rdf4j.rio.ntriples.escape_unicode", "Escape Unicode characters", Boolean.FALSE); /** diff --git a/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/RDFJSONParserSettings.java b/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/RDFJSONParserSettings.java index 33187eca512..6f216a66250 100644 --- a/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/RDFJSONParserSettings.java +++ b/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/RDFJSONParserSettings.java @@ -34,7 +34,7 @@ public class RDFJSONParserSettings { * instead. */ @Deprecated(since = "4.3.0", forRemoval = true) - public static final RioSetting FAIL_ON_MULTIPLE_OBJECT_VALUES = new BooleanRioSetting( + public static final BooleanRioSetting FAIL_ON_MULTIPLE_OBJECT_VALUES = new BooleanRioSetting( "org.eclipse.rdf4j.rio.rdfjson.fail_on_multiple_object_values", "Fail on multiple object values", Boolean.TRUE); @@ -50,7 +50,7 @@ public class RDFJSONParserSettings { * instead. */ @Deprecated(since = "4.3.0", forRemoval = true) - public static final RioSetting FAIL_ON_MULTIPLE_OBJECT_TYPES = new BooleanRioSetting( + public static final BooleanRioSetting FAIL_ON_MULTIPLE_OBJECT_TYPES = new BooleanRioSetting( "org.eclipse.rdf4j.rio.rdfjson.fail_on_multiple_object_types", "Fail on multiple object types", Boolean.TRUE); @@ -67,7 +67,7 @@ public class RDFJSONParserSettings { * instead. */ @Deprecated(since = "4.3.0", forRemoval = true) - public static final RioSetting FAIL_ON_MULTIPLE_OBJECT_LANGUAGES = new BooleanRioSetting( + public static final BooleanRioSetting FAIL_ON_MULTIPLE_OBJECT_LANGUAGES = new BooleanRioSetting( "org.eclipse.rdf4j.rio.rdfjson.fail_on_multiple_object_languages", "Fail on multiple object languages", Boolean.TRUE); @@ -84,7 +84,7 @@ public class RDFJSONParserSettings { * instead. */ @Deprecated(since = "4.3.0", forRemoval = true) - public static final RioSetting FAIL_ON_MULTIPLE_OBJECT_DATATYPES = new BooleanRioSetting( + public static final BooleanRioSetting FAIL_ON_MULTIPLE_OBJECT_DATATYPES = new BooleanRioSetting( "org.eclipse.rdf4j.rio.rdfjson.fail_on_multiple_object_datatypes", "Fail on multiple object datatypes", Boolean.TRUE); @@ -99,7 +99,7 @@ public class RDFJSONParserSettings { * @deprecated Use {@link org.eclipse.rdf4j.rio.rdfjson.RDFJSONParserSettings#FAIL_ON_UNKNOWN_PROPERTY} instead. */ @Deprecated(since = "4.3.0", forRemoval = true) - public static final RioSetting FAIL_ON_UNKNOWN_PROPERTY = new BooleanRioSetting( + public static final BooleanRioSetting FAIL_ON_UNKNOWN_PROPERTY = new BooleanRioSetting( "org.eclipse.rdf4j.rio.rdfjson.fail_on_unknown_property", "Fail on unknown property", Boolean.TRUE); /** @@ -113,7 +113,7 @@ public class RDFJSONParserSettings { * @deprecated Use {@link org.eclipse.rdf4j.rio.rdfjson.RDFJSONParserSettings#SUPPORT_GRAPHS_EXTENSION} instead. */ @Deprecated(since = "4.3.0", forRemoval = true) - public static final RioSetting SUPPORT_GRAPHS_EXTENSION = new BooleanRioSetting( + public static final BooleanRioSetting SUPPORT_GRAPHS_EXTENSION = new BooleanRioSetting( "org.eclipse.rdf4j.rio.rdfjson.support_graphs_extension", "SUPPORT_GRAPHS_EXTENSION", Boolean.TRUE); /** diff --git a/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/RDFJSONWriterSettings.java b/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/RDFJSONWriterSettings.java index 006cf4eeb83..a99f97163ba 100644 --- a/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/RDFJSONWriterSettings.java +++ b/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/RDFJSONWriterSettings.java @@ -32,7 +32,7 @@ public class RDFJSONWriterSettings { * @deprecated Use {@link org.eclipse.rdf4j.rio.rdfjson.RDFJSONWriterSettings#ALLOW_MULTIPLE_OBJECT_VALUES} instead. */ @Deprecated(since = "4.3.0", forRemoval = true) - public static final RioSetting ALLOW_MULTIPLE_OBJECT_VALUES = new BooleanRioSetting( + public static final BooleanRioSetting ALLOW_MULTIPLE_OBJECT_VALUES = new BooleanRioSetting( "org.eclipse.rdf4j.rio.rdfjson.allow_multiple_object_values", "Allow multiple object values", Boolean.FALSE); diff --git a/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/RDFaParserSettings.java b/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/RDFaParserSettings.java index ce3d2499199..76d7ead5af1 100644 --- a/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/RDFaParserSettings.java +++ b/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/RDFaParserSettings.java @@ -44,7 +44,7 @@ public class RDFaParserSettings { * @see RDFa Vocabulary Expansion */ @Deprecated(since = "4.3.0", forRemoval = true) - public static final RioSetting VOCAB_EXPANSION_ENABLED = new BooleanRioSetting( + public static final BooleanRioSetting VOCAB_EXPANSION_ENABLED = new BooleanRioSetting( "org.eclipse.rdf4j.rio.rdfa.vocab_expansion", "Vocabulary Expansion", Boolean.FALSE); /** @@ -58,7 +58,7 @@ public class RDFaParserSettings { * @deprecated Use {@link BasicParserSettings#NAMESPACES} */ @Deprecated(forRemoval = true) - public static final RioSetting FAIL_ON_RDFA_UNDEFINED_PREFIXES = new BooleanRioSetting( + public static final BooleanRioSetting FAIL_ON_RDFA_UNDEFINED_PREFIXES = new BooleanRioSetting( "org.eclipse.rdf4j.rio.rdfa.allow_undefined_prefixes", "Allow RDFa Undefined Prefixes", Boolean.FALSE); /** diff --git a/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/SetRioSetting.java b/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/SetRioSetting.java new file mode 100644 index 00000000000..236db8ee782 --- /dev/null +++ b/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/SetRioSetting.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * 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.rio.helpers; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.eclipse.rdf4j.rio.RioSetting; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * A {@link RioSetting} with a {@link Set} value. The given default for the setting can be overridden by means of a + * System property with a name equal to the setting key, and a string value of a JSON array of the desired values. + * + * @param the type of the elements in the set + */ +public final class SetRioSetting extends AbstractRioSetting> { + + private static final long serialVersionUID = 142127221198985291L; + + public SetRioSetting(String key, String description, Set defaultValue) { + super(key, description, defaultValue); + } + + @Override + public Set convert(String stringRepresentation) { + ObjectMapper objectMapper = new ObjectMapper(); + try { + return new HashSet<>(objectMapper.readValue(stringRepresentation, new TypeReference>() { + })); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/TriXParserSettings.java b/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/TriXParserSettings.java index bbdd993b1f2..d7ba8d8b936 100644 --- a/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/TriXParserSettings.java +++ b/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/TriXParserSettings.java @@ -31,7 +31,7 @@ public class TriXParserSettings { * @deprecated Use {@link org.eclipse.rdf4j.rio.trix.TriXParserSettings#FAIL_ON_MISSING_DATATYPE} instead. */ @Deprecated(since = "4.3.0", forRemoval = true) - public static final RioSetting FAIL_ON_MISSING_DATATYPE = new BooleanRioSetting( + public static final BooleanRioSetting FAIL_ON_MISSING_DATATYPE = new BooleanRioSetting( "org.eclipse.rdf4j.rio.trix.fail_on_missing_datatype", "Fail on TriX missing datatype", Boolean.TRUE); /** @@ -44,7 +44,7 @@ public class TriXParserSettings { * @deprecated Use {@link org.eclipse.rdf4j.rio.trix.TriXParserSettings#FAIL_ON_INVALID_STATEMENT} instead. */ @Deprecated(since = "4.3.0", forRemoval = true) - public static final RioSetting FAIL_ON_INVALID_STATEMENT = new BooleanRioSetting( + public static final BooleanRioSetting FAIL_ON_INVALID_STATEMENT = new BooleanRioSetting( "org.eclipse.rdf4j.rio.trix.fail_on_invalid_statement", "Fail on TriX invalid statement", Boolean.TRUE); /** diff --git a/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/TurtleParserSettings.java b/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/TurtleParserSettings.java index 80649c79c75..4aca2c8dc99 100644 --- a/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/TurtleParserSettings.java +++ b/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/TurtleParserSettings.java @@ -30,7 +30,7 @@ public class TurtleParserSettings { * @deprecated Use {@link org.eclipse.rdf4j.rio.turtle.TurtleParserSettings#CASE_INSENSITIVE_DIRECTIVES} instead. */ @Deprecated(since = "4.3.0", forRemoval = true) - public static final RioSetting CASE_INSENSITIVE_DIRECTIVES = new BooleanRioSetting( + public static final BooleanRioSetting CASE_INSENSITIVE_DIRECTIVES = new BooleanRioSetting( "org.eclipse.rdf4j.rio.turtle.case_insensitive_directives", "Allows case-insensitive directives to be recognised", Boolean.FALSE); @@ -46,7 +46,7 @@ public class TurtleParserSettings { * @deprecated Use {@link org.eclipse.rdf4j.rio.turtle.TurtleParserSettings#ACCEPT_TURTLESTAR} instead. */ @Deprecated(since = "4.3.0", forRemoval = true) - public static final RioSetting ACCEPT_TURTLESTAR = new BooleanRioSetting( + public static final BooleanRioSetting ACCEPT_TURTLESTAR = new BooleanRioSetting( "org.eclipse.rdf4j.rio.turtle.accept_turtlestar", "Allow processing of Turtle-star data by the standard Turtle parser", Boolean.TRUE); diff --git a/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/TurtleWriterSettings.java b/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/TurtleWriterSettings.java index 0d26dc4b7c2..e90c1505368 100644 --- a/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/TurtleWriterSettings.java +++ b/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/TurtleWriterSettings.java @@ -35,7 +35,7 @@ public class TurtleWriterSettings { * @deprecated Use {@link org.eclipse.rdf4j.rio.turtle.TurtleWriterSettings#ABBREVIATE_NUMBERS} instead. */ @Deprecated(since = "4.3.0", forRemoval = true) - public static final RioSetting ABBREVIATE_NUMBERS = new BooleanRioSetting( + public static final BooleanRioSetting ABBREVIATE_NUMBERS = new BooleanRioSetting( "org.eclipse.rdf4j.rio.turtle.abbreviate_numbers", "Abbreviate numbers", Boolean.TRUE); /** diff --git a/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/XMLParserSettings.java b/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/XMLParserSettings.java index 53aa9831bf0..c9bd41e5fd9 100644 --- a/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/XMLParserSettings.java +++ b/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/XMLParserSettings.java @@ -38,7 +38,7 @@ public final class XMLParserSettings { * @see * XMLConstants.FEATURE_SECURE_PROCESSING */ - public static final RioSetting SECURE_PROCESSING = new BooleanRioSetting( + public static final BooleanRioSetting SECURE_PROCESSING = new BooleanRioSetting( XMLConstants.FEATURE_SECURE_PROCESSING, "Secure processing feature of XMLConstants", true); /** @@ -52,7 +52,7 @@ public final class XMLParserSettings { * @see XXE Prevention * Cheat Sheet */ - public static final RioSetting DISALLOW_DOCTYPE_DECL = new BooleanRioSetting( + public static final BooleanRioSetting DISALLOW_DOCTYPE_DECL = new BooleanRioSetting( "http://apache.org/xml/features/disallow-doctype-decl", "Disallow DOCTYPE declaration in document", false); /** @@ -65,7 +65,7 @@ public final class XMLParserSettings { * * @see Apache XML Project - Features */ - public static final RioSetting LOAD_EXTERNAL_DTD = new BooleanRioSetting( + public static final BooleanRioSetting LOAD_EXTERNAL_DTD = new BooleanRioSetting( "http://apache.org/xml/features/nonvalidating/load-external-dtd", "Load External DTD", false); /** @@ -79,7 +79,7 @@ public final class XMLParserSettings { * @see XXE Prevention * Cheat Sheet */ - public static final RioSetting EXTERNAL_GENERAL_ENTITIES = new BooleanRioSetting( + public static final BooleanRioSetting EXTERNAL_GENERAL_ENTITIES = new BooleanRioSetting( "http://xml.org/sax/features/external-general-entities", "Include external general entities", false); /** @@ -93,7 +93,7 @@ public final class XMLParserSettings { * @see XXE Prevention * Cheat Sheet */ - public static final RioSetting EXTERNAL_PARAMETER_ENTITIES = new BooleanRioSetting( + public static final BooleanRioSetting EXTERNAL_PARAMETER_ENTITIES = new BooleanRioSetting( "http://xml.org/sax/features/external-parameter-entities", "Include external parameter entities", false); /** @@ -114,7 +114,7 @@ public final class XMLParserSettings { *

* Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.fail_on_sax_non_fatal_errors} */ - public static final RioSetting FAIL_ON_SAX_NON_FATAL_ERRORS = new BooleanRioSetting( + public static final BooleanRioSetting FAIL_ON_SAX_NON_FATAL_ERRORS = new BooleanRioSetting( "org.eclipse.rdf4j.rio.fail_on_sax_non_fatal_errors", "Fail on SAX non-fatal errors", true); /** @@ -124,7 +124,7 @@ public final class XMLParserSettings { *

* Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.fail_on_non_standard_attributes} */ - public static final RioSetting FAIL_ON_NON_STANDARD_ATTRIBUTES = new BooleanRioSetting( + public static final BooleanRioSetting FAIL_ON_NON_STANDARD_ATTRIBUTES = new BooleanRioSetting( "org.eclipse.rdf4j.rio.fail_on_non_standard_attributes", "Fail on non-standard attributes", true); /** @@ -134,7 +134,7 @@ public final class XMLParserSettings { *

* Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.fail_on_invalid_ncname} */ - public static final RioSetting FAIL_ON_INVALID_NCNAME = new BooleanRioSetting( + public static final BooleanRioSetting FAIL_ON_INVALID_NCNAME = new BooleanRioSetting( "org.eclipse.rdf4j.rio.fail_on_invalid_ncname", "Fail on invalid NCName", true); /** @@ -144,7 +144,7 @@ public final class XMLParserSettings { *

* Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.fail_on_duplicate_rdf_id} */ - public static final RioSetting FAIL_ON_DUPLICATE_RDF_ID = new BooleanRioSetting( + public static final BooleanRioSetting FAIL_ON_DUPLICATE_RDF_ID = new BooleanRioSetting( "org.eclipse.rdf4j.rio.fail_on_duplicate_rdf_id", "Fail on duplicate RDF ID", true); /** @@ -154,7 +154,7 @@ public final class XMLParserSettings { *

* Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.fail_on_invalid_qname} */ - public static final RioSetting FAIL_ON_INVALID_QNAME = new BooleanRioSetting( + public static final BooleanRioSetting FAIL_ON_INVALID_QNAME = new BooleanRioSetting( "org.eclipse.rdf4j.rio.fail_on_invalid_qname", "Fail on invalid QName", true); /** @@ -164,7 +164,7 @@ public final class XMLParserSettings { *

* Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.fail_on_mismatched_tags} */ - public static final RioSetting FAIL_ON_MISMATCHED_TAGS = new BooleanRioSetting( + public static final BooleanRioSetting FAIL_ON_MISMATCHED_TAGS = new BooleanRioSetting( "org.eclipse.rdf4j.rio.fail_on_mismatched_tags", "Fail on mismatched tags", true); /** @@ -175,7 +175,7 @@ public final class XMLParserSettings { *

* Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.parse_standalone_documents} */ - public static final RioSetting PARSE_STANDALONE_DOCUMENTS = new BooleanRioSetting( + public static final BooleanRioSetting PARSE_STANDALONE_DOCUMENTS = new BooleanRioSetting( "org.eclipse.rdf4j.rio.parse_standalone_documents", "Parse standalone documents", true); /** diff --git a/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/XMLWriterSettings.java b/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/XMLWriterSettings.java index 822a7fab862..5c644b639ae 100644 --- a/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/XMLWriterSettings.java +++ b/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/XMLWriterSettings.java @@ -32,7 +32,7 @@ public class XMLWriterSettings { * @see RDF/XML * specification */ - public static final RioSetting INCLUDE_XML_PI = new BooleanRioSetting( + public static final BooleanRioSetting INCLUDE_XML_PI = new BooleanRioSetting( "org.eclipse.rdf4j.rio.include_xml_pi", "Include XML Processing Instruction", Boolean.TRUE); /** @@ -46,7 +46,7 @@ public class XMLWriterSettings { * @see RDF/XML * specification */ - public static final RioSetting INCLUDE_ROOT_RDF_TAG = new BooleanRioSetting( + public static final BooleanRioSetting INCLUDE_ROOT_RDF_TAG = new BooleanRioSetting( "org.eclipse.rdf4j.rio.include_root_rdf_tag", "Include Root RDF Tag", Boolean.TRUE); /** @@ -57,7 +57,7 @@ public class XMLWriterSettings { *

* Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.use_single_quotes} */ - public static final RioSetting USE_SINGLE_QUOTES = new BooleanRioSetting( + public static final BooleanRioSetting USE_SINGLE_QUOTES = new BooleanRioSetting( "org.eclipse.rdf4j.rio.use_single_quotes", "Use single quotes", Boolean.FALSE); /** @@ -69,7 +69,7 @@ public class XMLWriterSettings { *

* Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.quotes_to_entities_in_text} */ - public static final RioSetting QUOTES_TO_ENTITIES_IN_TEXT = new BooleanRioSetting( + public static final BooleanRioSetting QUOTES_TO_ENTITIES_IN_TEXT = new BooleanRioSetting( "org.eclipse.rdf4j.rio.quotes_to_entities_in_text", "Use single quotes", Boolean.FALSE); /** diff --git a/core/rio/binary/src/main/java/org/eclipse/rdf4j/rio/binary/BinaryRDFWriterSettings.java b/core/rio/binary/src/main/java/org/eclipse/rdf4j/rio/binary/BinaryRDFWriterSettings.java index c8750b78d83..237391fecc0 100644 --- a/core/rio/binary/src/main/java/org/eclipse/rdf4j/rio/binary/BinaryRDFWriterSettings.java +++ b/core/rio/binary/src/main/java/org/eclipse/rdf4j/rio/binary/BinaryRDFWriterSettings.java @@ -34,7 +34,7 @@ public class BinaryRDFWriterSettings { *

* Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.binary.format_version} */ - public static final RioSetting VERSION = new LongRioSetting( + public static final LongRioSetting VERSION = new LongRioSetting( "org.eclipse.rdf4j.rio.binary.format_version", "Binary RDF format", 2L); /** @@ -45,7 +45,7 @@ public class BinaryRDFWriterSettings { *

* Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.binary.buffer_size} */ - public static final RioSetting BUFFER_SIZE = new LongRioSetting( + public static final LongRioSetting BUFFER_SIZE = new LongRioSetting( "org.eclipse.rdf4j.rio.binary.buffer_size", "Buffer size", 8192L); /** @@ -56,7 +56,7 @@ public class BinaryRDFWriterSettings { *

* Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.binary.charset} */ - public static final RioSetting CHARSET = new StringRioSetting( + public static final StringRioSetting CHARSET = new StringRioSetting( "org.eclipse.rdf4j.rio.binary.charset", "Charset", StandardCharsets.UTF_8.name()); /** @@ -74,7 +74,7 @@ public class BinaryRDFWriterSettings { *

* Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.binary.recycle_ids} */ - public static final RioSetting RECYCLE_IDS = new BooleanRioSetting( + public static final BooleanRioSetting RECYCLE_IDS = new BooleanRioSetting( "org.eclipse.rdf4j.rio.binary.recycle_ids", "Charset", true); /** diff --git a/core/rio/jsonld/src/main/java/org/eclipse/rdf4j/rio/jsonld/CachingDocumentLoader.java b/core/rio/jsonld/src/main/java/org/eclipse/rdf4j/rio/jsonld/CachingDocumentLoader.java index fc7074d12ce..c4363a1f4db 100644 --- a/core/rio/jsonld/src/main/java/org/eclipse/rdf4j/rio/jsonld/CachingDocumentLoader.java +++ b/core/rio/jsonld/src/main/java/org/eclipse/rdf4j/rio/jsonld/CachingDocumentLoader.java @@ -78,7 +78,7 @@ public Document loadDocument(URI uri, DocumentLoaderOptions options) { } } else { throw new RDFParseException("Could not load document from " + uri - + " because it is not whitelisted. See: JSONLDSettings.WHITELIST and JSONLDSettings.SECURE_MODE"); + + " because it is not whitelisted. See: JSONLDSettings.WHITELIST and JSONLDSettings.SECURE_MODE which can also be set as system properties."); } } catch (RDFParseException e) { logger.error(e.getMessage(), e); diff --git a/core/rio/jsonld/src/main/java/org/eclipse/rdf4j/rio/jsonld/JSONLDParser.java b/core/rio/jsonld/src/main/java/org/eclipse/rdf4j/rio/jsonld/JSONLDParser.java index 0c3561620c4..ab8eb983575 100644 --- a/core/rio/jsonld/src/main/java/org/eclipse/rdf4j/rio/jsonld/JSONLDParser.java +++ b/core/rio/jsonld/src/main/java/org/eclipse/rdf4j/rio/jsonld/JSONLDParser.java @@ -20,6 +20,8 @@ import java.net.URI; import java.net.URISyntaxException; import java.util.Collection; +import java.util.HashSet; +import java.util.List; import java.util.Optional; import java.util.Set; import java.util.function.BiConsumer; @@ -42,6 +44,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; + import jakarta.json.JsonObject; import jakarta.json.JsonString; import jakarta.json.JsonStructure; @@ -130,9 +135,8 @@ private void parse(InputStream in, Reader reader, String baseURI) } boolean secureMode = getParserConfig().get(SECURE_MODE); - boolean documentLoaderCache = getParserConfig().get(DOCUMENT_LOADER_CACHE); - Set whitelist = getParserConfig().get(WHITELIST); + boolean documentLoaderCache = getParserConfig().get(DOCUMENT_LOADER_CACHE); JsonLdOptions opts = new JsonLdOptions(); opts.setUriValidation(false); diff --git a/core/rio/jsonld/src/main/resources/META-INF/services/jakarta.json.spi.JsonProvider b/core/rio/jsonld/src/main/resources/META-INF/services/jakarta.json.spi.JsonProvider new file mode 100644 index 00000000000..52f1a6b2e45 --- /dev/null +++ b/core/rio/jsonld/src/main/resources/META-INF/services/jakarta.json.spi.JsonProvider @@ -0,0 +1 @@ +org.glassfish.json.JsonProviderImpl diff --git a/core/rio/jsonld/src/test/java/org/eclipse/rdf4j/rio/jsonld/JSONLDParserCustomTest.java b/core/rio/jsonld/src/test/java/org/eclipse/rdf4j/rio/jsonld/JSONLDParserCustomTest.java index 1e61a4423fe..f0fb28e05fe 100644 --- a/core/rio/jsonld/src/test/java/org/eclipse/rdf4j/rio/jsonld/JSONLDParserCustomTest.java +++ b/core/rio/jsonld/src/test/java/org/eclipse/rdf4j/rio/jsonld/JSONLDParserCustomTest.java @@ -14,13 +14,17 @@ import static org.eclipse.rdf4j.rio.helpers.JSONLDSettings.SECURE_MODE; import static org.eclipse.rdf4j.rio.helpers.JSONLDSettings.WHITELIST; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.io.StringReader; import java.net.URI; import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.ServiceLoader; import java.util.Set; +import java.util.stream.Collectors; import org.apache.commons.io.FileUtils; import org.eclipse.rdf4j.model.IRI; @@ -44,6 +48,7 @@ import org.junit.jupiter.api.RepeatedTest; import org.junit.jupiter.api.Test; +import jakarta.json.spi.JsonProvider; import no.hasmac.jsonld.document.Document; import no.hasmac.jsonld.document.JsonDocument; @@ -257,7 +262,7 @@ public void testLocalFileSecurity() throws Exception { }); Assertions.assertEquals("Could not load document from " + contextUri - + " because it is not whitelisted. See: JSONLDSettings.WHITELIST and JSONLDSettings.SECURE_MODE", + + " because it is not whitelisted. See: JSONLDSettings.WHITELIST and JSONLDSettings.SECURE_MODE which can also be set as system properties.", rdfParseException.getMessage()); } @@ -293,6 +298,26 @@ public void testLocalFileSecurityDisableSecurity() throws Exception { assertTrue(model.objects().contains(FOAF.PERSON)); } + @Test + public void testLocalFileSecurityDisableSecuritySystemProperty() throws Exception { + String jsonld = FileUtils.readFileToString(new File(JSONLDParserCustomTest.class.getClassLoader() + .getResource("testcases/jsonld/localFileContext/data.jsonld") + .getFile()), StandardCharsets.UTF_8); + jsonld = jsonld.replace("file:./context.jsonld", + JSONLDParserCustomTest.class.getClassLoader() + .getResource("testcases/jsonld/localFileContext/context.jsonld") + .toString()); + + try { + System.setProperty(SECURE_MODE.getKey(), "false"); + parser.parse(new StringReader(jsonld), ""); + assertTrue(model.objects().contains(FOAF.PERSON)); + } finally { + System.clearProperty(SECURE_MODE.getKey()); + } + + } + @RepeatedTest(10) public void testRemoteContext() throws Exception { String jsonld = FileUtils.readFileToString(new File(JSONLDParserCustomTest.class.getClassLoader() @@ -304,6 +329,22 @@ public void testRemoteContext() throws Exception { assertEquals(59, model.size()); } + @Test + public void testRemoteContextSystemProperty() throws Exception { + String jsonld = FileUtils.readFileToString(new File(JSONLDParserCustomTest.class.getClassLoader() + .getResource("testcases/jsonld/remoteContext/data.jsonld") + .getFile()), StandardCharsets.UTF_8); + + try { + System.setProperty(WHITELIST.getKey(), "[\"https://schema.org\"]"); + parser.parse(new StringReader(jsonld), ""); + assertEquals(59, model.size()); + } finally { + System.clearProperty(WHITELIST.getKey()); + } + + } + @Test public void testRemoteContextException() throws Exception { String jsonld = FileUtils.readFileToString(new File(JSONLDParserCustomTest.class.getClassLoader() @@ -318,4 +359,15 @@ public void testRemoteContextException() throws Exception { assertEquals("Could not load document from https://example.org/context.jsonld", rdfParseException.getMessage()); } + @Test + public void testSPI() { + ServiceLoader load = ServiceLoader.load(JsonProvider.class); + List collect = load.stream() + .map(ServiceLoader.Provider::get) + .map(t -> t.getClass().getName()) + .collect(Collectors.toList()); + assertFalse(collect.isEmpty()); + assertEquals("org.glassfish.json.JsonProviderImpl", collect.stream().findFirst().orElse("")); + } + } diff --git a/core/rio/ntriples/src/main/java/org/eclipse/rdf4j/rio/ntriples/NTriplesParserSettings.java b/core/rio/ntriples/src/main/java/org/eclipse/rdf4j/rio/ntriples/NTriplesParserSettings.java index 9c8c92788c2..2c35ca9fb0e 100644 --- a/core/rio/ntriples/src/main/java/org/eclipse/rdf4j/rio/ntriples/NTriplesParserSettings.java +++ b/core/rio/ntriples/src/main/java/org/eclipse/rdf4j/rio/ntriples/NTriplesParserSettings.java @@ -32,7 +32,7 @@ public class NTriplesParserSettings { *

* Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.ntriples.fail_on_invalid_lines} */ - public static final RioSetting FAIL_ON_INVALID_LINES = new BooleanRioSetting( + public static final BooleanRioSetting FAIL_ON_INVALID_LINES = new BooleanRioSetting( "org.eclipse.rdf4j.rio.ntriples.fail_on_invalid_lines", "Fail on N-Triples invalid lines", Boolean.TRUE); /** diff --git a/core/rio/ntriples/src/main/java/org/eclipse/rdf4j/rio/ntriples/NTriplesWriterSettings.java b/core/rio/ntriples/src/main/java/org/eclipse/rdf4j/rio/ntriples/NTriplesWriterSettings.java index fe077e8d2b2..1f10c0a4463 100644 --- a/core/rio/ntriples/src/main/java/org/eclipse/rdf4j/rio/ntriples/NTriplesWriterSettings.java +++ b/core/rio/ntriples/src/main/java/org/eclipse/rdf4j/rio/ntriples/NTriplesWriterSettings.java @@ -29,7 +29,7 @@ public class NTriplesWriterSettings { *

* Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.ntriples.escape_unicode} */ - public static final RioSetting ESCAPE_UNICODE = new BooleanRioSetting( + public static final BooleanRioSetting ESCAPE_UNICODE = new BooleanRioSetting( "org.eclipse.rdf4j.rio.ntriples.escape_unicode", "Escape Unicode characters", Boolean.FALSE); /** diff --git a/core/rio/rdfjson/src/main/java/org/eclipse/rdf4j/rio/rdfjson/RDFJSONWriterSettings.java b/core/rio/rdfjson/src/main/java/org/eclipse/rdf4j/rio/rdfjson/RDFJSONWriterSettings.java index baac68266e1..caa3268708b 100644 --- a/core/rio/rdfjson/src/main/java/org/eclipse/rdf4j/rio/rdfjson/RDFJSONWriterSettings.java +++ b/core/rio/rdfjson/src/main/java/org/eclipse/rdf4j/rio/rdfjson/RDFJSONWriterSettings.java @@ -32,7 +32,7 @@ public class RDFJSONWriterSettings { *

* Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.rdfjson.allow_multiple_object_values}. */ - public static final RioSetting ALLOW_MULTIPLE_OBJECT_VALUES = new BooleanRioSetting( + public static final BooleanRioSetting ALLOW_MULTIPLE_OBJECT_VALUES = new BooleanRioSetting( "org.eclipse.rdf4j.rio.rdfjson.allow_multiple_object_values", "Allow multiple object values", Boolean.FALSE); diff --git a/core/rio/trix/src/main/java/org/eclipse/rdf4j/rio/trix/TriXParserSettings.java b/core/rio/trix/src/main/java/org/eclipse/rdf4j/rio/trix/TriXParserSettings.java index 9fb876dcae2..41fe7288715 100644 --- a/core/rio/trix/src/main/java/org/eclipse/rdf4j/rio/trix/TriXParserSettings.java +++ b/core/rio/trix/src/main/java/org/eclipse/rdf4j/rio/trix/TriXParserSettings.java @@ -31,7 +31,7 @@ public class TriXParserSettings { *

* Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.trix.fail_on_missing_datatype}. */ - public static final RioSetting FAIL_ON_MISSING_DATATYPE = new BooleanRioSetting( + public static final BooleanRioSetting FAIL_ON_MISSING_DATATYPE = new BooleanRioSetting( "org.eclipse.rdf4j.rio.trix.fail_on_missing_datatype", "Fail on TriX missing datatype", Boolean.TRUE); /** @@ -41,7 +41,7 @@ public class TriXParserSettings { *

* Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.trix.fail_on_invalid_statement}. */ - public static final RioSetting FAIL_ON_INVALID_STATEMENT = new BooleanRioSetting( + public static final BooleanRioSetting FAIL_ON_INVALID_STATEMENT = new BooleanRioSetting( "org.eclipse.rdf4j.rio.trix.fail_on_invalid_statement", "Fail on TriX invalid statement", Boolean.TRUE); /** diff --git a/core/rio/turtle/src/main/java/org/eclipse/rdf4j/rio/turtle/TurtleParserSettings.java b/core/rio/turtle/src/main/java/org/eclipse/rdf4j/rio/turtle/TurtleParserSettings.java index 518fbb4542f..5f0c1583de0 100644 --- a/core/rio/turtle/src/main/java/org/eclipse/rdf4j/rio/turtle/TurtleParserSettings.java +++ b/core/rio/turtle/src/main/java/org/eclipse/rdf4j/rio/turtle/TurtleParserSettings.java @@ -30,7 +30,7 @@ public class TurtleParserSettings { *

* Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.turtle.case_insensitive_directives}. */ - public static final RioSetting CASE_INSENSITIVE_DIRECTIVES = new BooleanRioSetting( + public static final BooleanRioSetting CASE_INSENSITIVE_DIRECTIVES = new BooleanRioSetting( "org.eclipse.rdf4j.rio.turtle.case_insensitive_directives", "Allows case-insensitive directives to be recognised", Boolean.FALSE); @@ -43,7 +43,7 @@ public class TurtleParserSettings { *

* Can be overridden by setting system property {@code org.eclipse.rdf4j.rio.turtle.accept_turtlestar}. */ - public static final RioSetting ACCEPT_TURTLESTAR = new BooleanRioSetting( + public static final BooleanRioSetting ACCEPT_TURTLESTAR = new BooleanRioSetting( "org.eclipse.rdf4j.rio.turtle.accept_turtlestar", "Allow processing of Turtle-star data by the standard Turtle parser", Boolean.TRUE); diff --git a/core/rio/turtle/src/main/java/org/eclipse/rdf4j/rio/turtle/TurtleWriterSettings.java b/core/rio/turtle/src/main/java/org/eclipse/rdf4j/rio/turtle/TurtleWriterSettings.java index 408be720bcb..96c96880277 100644 --- a/core/rio/turtle/src/main/java/org/eclipse/rdf4j/rio/turtle/TurtleWriterSettings.java +++ b/core/rio/turtle/src/main/java/org/eclipse/rdf4j/rio/turtle/TurtleWriterSettings.java @@ -35,7 +35,7 @@ public class TurtleWriterSettings { * @see https://www.w3.org/TR/turtle/#abbrev * @since 3.7.0 */ - public static final RioSetting ABBREVIATE_NUMBERS = new BooleanRioSetting( + public static final BooleanRioSetting ABBREVIATE_NUMBERS = new BooleanRioSetting( "org.eclipse.rdf4j.rio.turtle.abbreviate_numbers", "Abbreviate numbers", Boolean.TRUE); /** diff --git a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/AbstractPairwisePlanNode.java b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/AbstractPairwisePlanNode.java index 75be9b1778d..cfac96a61c2 100644 --- a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/AbstractPairwisePlanNode.java +++ b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/AbstractPairwisePlanNode.java @@ -36,12 +36,16 @@ import org.eclipse.rdf4j.sail.shacl.ast.paths.Path; import org.eclipse.rdf4j.sail.shacl.ast.paths.SimplePath; import org.eclipse.rdf4j.sail.shacl.results.ValidationResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * @author Håvard Ottestad */ abstract class AbstractPairwisePlanNode implements PlanNode { + private static final Logger logger = LoggerFactory.getLogger(AbstractPairwisePlanNode.class); + private final SailConnection connection; private final Resource[] dataGraph; private final IRI predicate; diff --git a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/DatatypeFilter.java b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/DatatypeFilter.java index 47192d3c5d9..eba54296981 100644 --- a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/DatatypeFilter.java +++ b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/DatatypeFilter.java @@ -17,12 +17,16 @@ import org.eclipse.rdf4j.model.Literal; import org.eclipse.rdf4j.model.base.CoreDatatype; import org.eclipse.rdf4j.model.datatypes.XMLDatatypeUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * @author Håvard Ottestad */ public class DatatypeFilter extends FilterPlanNode { + private static final Logger logger = LoggerFactory.getLogger(DatatypeFilter.class); + private final IRI datatype; private final CoreDatatype.XSD xsdDatatype; private StackTraceElement[] stackTrace; @@ -37,17 +41,41 @@ public DatatypeFilter(PlanNode parent, IRI datatype) { @Override boolean checkTuple(Reference t) { if (!(t.get().getValue().isLiteral())) { + logger.debug("Tuple rejected because it's not a literal. Tuple: {}", t); return false; } Literal literal = (Literal) t.get().getValue(); if (xsdDatatype != null) { if (literal.getCoreDatatype() == xsdDatatype) { - return XMLDatatypeUtil.isValidValue(literal.stringValue(), xsdDatatype); + boolean isValid = XMLDatatypeUtil.isValidValue(literal.stringValue(), xsdDatatype); + if (isValid) { + logger.trace( + "Tuple accepted because its literal value is valid according to the rules for the datatype in the XSD spec. Actual datatype: {}, Expected datatype: {}, Tuple: {}", + literal.getDatatype(), xsdDatatype, t); + } else { + logger.debug( + "Tuple rejected because its literal value is invalid according to the rules for the datatype in the XSD spec. Actual datatype: {}, Expected datatype: {}, Tuple: {}", + literal.getDatatype(), xsdDatatype, t); + } + return isValid; } + logger.debug( + "Tuple rejected because literal's core datatype is not the expected datatype. Actual datatype: {}, Expected datatype: {}, Tuple: {}", + literal.getDatatype(), xsdDatatype, t); return false; } else { - return literal.getDatatype() == datatype || literal.getDatatype().equals(datatype); + boolean isEqual = literal.getDatatype() == datatype || literal.getDatatype().equals(datatype); + if (isEqual) { + logger.trace( + "Tuple accepted because literal's datatype is equal to the expected datatype. Actual datatype: {}, Expected datatype: {}, Tuple: {}", + literal.getDatatype(), datatype, t); + } else { + logger.debug( + "Tuple rejected because literal's datatype is not equal to the expected datatype. Actual datatype: {}, Expected datatype: {}, Tuple: {}", + literal.getDatatype(), datatype, t); + } + return isEqual; } } diff --git a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/ExternalFilterByQuery.java b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/ExternalFilterByQuery.java index 19bd9753394..9336f1d426c 100644 --- a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/ExternalFilterByQuery.java +++ b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/ExternalFilterByQuery.java @@ -91,10 +91,13 @@ boolean checkTuple(Reference t) { t.set(map.apply(t.get(), bindingSet.next())); } while (bindingSet.hasNext()); } + logger.trace("Tuple accepted because it matches the external query. Value: {}, Query: {}, Tuple: {}", + value, queryString, t); return true; } } - + logger.debug("Tuple rejected because it does not match the external query. Value: {}, Query: {}, Tuple: {}", + value, queryString, t); return false; } diff --git a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/GroupByCountFilter.java b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/GroupByCountFilter.java index 316c8394659..2d95a212338 100644 --- a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/GroupByCountFilter.java +++ b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/GroupByCountFilter.java @@ -16,12 +16,16 @@ import org.apache.commons.text.StringEscapeUtils; import org.eclipse.rdf4j.common.iteration.CloseableIteration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * @author Håvard Ottestad */ public class GroupByCountFilter implements PlanNode { + private static final Logger logger = LoggerFactory.getLogger(GroupByCountFilter.class); + private final Function filter; PlanNode parent; private boolean printed = false; @@ -74,7 +78,13 @@ private void calculateNext() { } if (!filter.apply(count)) { + logger.debug( + "Tuple rejected because its count does not pass the filter. Actual count: {}, Tuple: {}", + count, this.next); this.next = null; + } else { + logger.trace("Tuple accepted because its count passes the filter. Actual count: {}, Tuple: {}", + count, this.next); } } diff --git a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/LanguageInFilter.java b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/LanguageInFilter.java index ac2efb2d879..58736aee4c4 100644 --- a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/LanguageInFilter.java +++ b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/LanguageInFilter.java @@ -19,12 +19,16 @@ import org.eclipse.rdf4j.model.Literal; import org.eclipse.rdf4j.model.util.Literals; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * @author Håvard Ottestad */ public class LanguageInFilter extends FilterPlanNode { + private static final Logger logger = LoggerFactory.getLogger(LanguageInFilter.class); + private final List languageRanges; private final Set lowerCaseLanguageIn; @@ -37,17 +41,22 @@ public LanguageInFilter(PlanNode parent, Set lowerCaseLanguageIn, List language = ((Literal) t.get().getValue()).getLanguage(); - if (!language.isPresent()) { + if (language.isEmpty()) { + logger.debug("Tuple rejected because it does not have a language tag. Tuple: {}", t); return false; } // early matching boolean languageMatches = language.map(String::toLowerCase).filter(lowerCaseLanguageIn::contains).isPresent(); if (languageMatches) { + logger.trace( + "Tuple accepted because its language tag (toLowerCase()) is in the language set. Actual language: {}, Language set: {}, Tuple: {}", + language.get(), lowerCaseLanguageIn, t); return true; } @@ -56,6 +65,9 @@ boolean checkTuple(Reference t) { for (String languageRange : languageRanges) { if (Literals.langMatches(langTag, languageRange)) { + logger.trace( + "Tuple accepted because its language tag matches the language range (BCP47). Actual language: {}, Language range: {}, Tuple: {}", + langTag, languageRange, t); return true; } } diff --git a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/NodeKindFilter.java b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/NodeKindFilter.java index 9190ea83c6a..86526d93b9c 100644 --- a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/NodeKindFilter.java +++ b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/NodeKindFilter.java @@ -15,12 +15,16 @@ import org.eclipse.rdf4j.model.Value; import org.eclipse.rdf4j.sail.shacl.ast.constraintcomponents.NodeKindConstraintComponent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * @author Håvard Ottestad */ public class NodeKindFilter extends FilterPlanNode { + private static final Logger logger = LoggerFactory.getLogger(NodeKindFilter.class); + private final NodeKindConstraintComponent.NodeKind nodeKind; public NodeKindFilter(PlanNode parent, NodeKindConstraintComponent.NodeKind nodeKind) { @@ -32,28 +36,48 @@ public NodeKindFilter(PlanNode parent, NodeKindConstraintComponent.NodeKind node boolean checkTuple(Reference t) { Value value = t.get().getValue(); - /* - * BlankNode(SHACL.BLANK_NODE), IRI(SHACL.IRI), Literal(SHACL.LITERAL), BlankNodeOrIRI(SHACL.BLANK_NODE_OR_IRI), - * BlankNodeOrLiteral(SHACL.BLANK_NODE_OR_LITERAL), IRIOrLiteral(SHACL.IRI_OR_LITERAL), - */ switch (nodeKind) { case IRI: - return value.isIRI(); + if (value.isIRI()) { + logger.trace("Tuple accepted because its value is an IRI. Tuple: {}", t); + return true; + } + break; case Literal: - return value.isLiteral(); + if (value.isLiteral()) { + logger.trace("Tuple accepted because its value is a Literal. Tuple: {}", t); + return true; + } + break; case BlankNode: - return value.isBNode(); + if (value.isBNode()) { + logger.trace("Tuple accepted because its value is a BlankNode. Tuple: {}", t); + return true; + } + break; case IRIOrLiteral: - return value.isIRI() || value.isLiteral(); + if (value.isIRI() || value.isLiteral()) { + logger.trace("Tuple accepted because its value is an IRI or Literal. Tuple: {}", t); + return true; + } + break; case BlankNodeOrIRI: - return value.isBNode() || value.isIRI(); + if (value.isBNode() || value.isIRI()) { + logger.trace("Tuple accepted because its value is a BlankNode or IRI. Tuple: {}", t); + return true; + } + break; case BlankNodeOrLiteral: - return value.isBNode() || value.isLiteral(); + if (value.isBNode() || value.isLiteral()) { + logger.trace("Tuple accepted because its value is a BlankNode or Literal. Tuple: {}", t); + return true; + } + break; } - throw new IllegalStateException("Unknown nodeKind"); - + logger.debug("Tuple rejected because its value does not match the expected node kind. Tuple: {}", t); + return false; } @Override diff --git a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/PatternFilter.java b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/PatternFilter.java index be9b2abe695..ebef3b98ddc 100644 --- a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/PatternFilter.java +++ b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/PatternFilter.java @@ -15,55 +15,65 @@ import java.util.regex.Pattern; import org.eclipse.rdf4j.model.Value; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * @author Håvard Ottestad */ public class PatternFilter extends FilterPlanNode { + private static final Logger logger = LoggerFactory.getLogger(PatternFilter.class); + private final Pattern pattern; public PatternFilter(PlanNode parent, String pattern, String flags) { super(parent); if (flags != null && !flags.isEmpty()) { - int flag = 0b0; if (flags.contains("i")) { flag = flag | Pattern.CASE_INSENSITIVE; + logger.trace("PatternFilter constructed with case insensitive flag"); } if (flags.contains("d")) { flag = flag | Pattern.UNIX_LINES; + logger.trace("PatternFilter constructed with UNIX lines flag"); } if (flags.contains("m")) { flag = flag | Pattern.MULTILINE; + logger.trace("PatternFilter constructed with multiline flag"); } if (flags.contains("s")) { flag = flag | Pattern.DOTALL; + logger.trace("PatternFilter constructed with dotall flag"); } if (flags.contains("u")) { flag = flag | Pattern.UNICODE_CASE; + logger.trace("PatternFilter constructed with unicode case flag"); } if (flags.contains("x")) { flag = flag | Pattern.COMMENTS; + logger.trace("PatternFilter constructed with comments flag"); } if (flags.contains("U")) { flag = flag | Pattern.UNICODE_CHARACTER_CLASS; + logger.trace("PatternFilter constructed with unicode character class flag"); } this.pattern = Pattern.compile(pattern, flag); + logger.trace("PatternFilter constructed with pattern: {} and flags: {}", pattern, flags); } else { this.pattern = Pattern.compile(pattern); - + logger.trace("PatternFilter constructed with pattern: {} and no flags", pattern); } - } @Override diff --git a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/Unique.java b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/Unique.java index 5cdeac16758..4ba1984e1e6 100644 --- a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/Unique.java +++ b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/Unique.java @@ -61,6 +61,11 @@ public static PlanNode getInstance(PlanNode parent, boolean compress) { if (parent.isGuaranteedEmpty()) { return parent; } + + if (parent instanceof Unique && (!compress || ((Unique) parent).compress == compress)) { + return parent; + } + return new Unique(parent, compress); } diff --git a/docker/Dockerfile b/docker/Dockerfile index bab200d0725..800553f06f6 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -17,7 +17,7 @@ MAINTAINER Bart Hanssens (bart.hanssens@bosa.fgov.be) RUN apt-get clean && apt-get update && apt-get upgrade -y && apt-get clean ENV JAVA_OPTS="-Xmx2g" -ENV CATALINA_OPTS="-Dorg.eclipse.rdf4j.appdata.basedir=/var/rdf4j" +ENV CATALINA_OPTS="-Dorg.eclipse.rdf4j.appdata.basedir=/var/rdf4j -Dorg.eclipse.rdf4j.rio.jsonld_secure_mode=false" RUN adduser --system tomcat diff --git a/pom.xml b/pom.xml index 2552348b929..d905efa8377 100644 --- a/pom.xml +++ b/pom.xml @@ -622,6 +622,22 @@ hasmac-json-ld 0.9.0 + + + org.apache.kerby + kerb-core + 1.1.1 + + + org.apache.kerby + kerb-util + 1.1.1 + + + org.apache.kerby + kerby-pkix + 1.1.1 + diff --git a/spring-components/rdf4j-spring/src/main/java/org/eclipse/rdf4j/spring/dao/support/RelationMapBuilder.java b/spring-components/rdf4j-spring/src/main/java/org/eclipse/rdf4j/spring/dao/support/RelationMapBuilder.java index d4b982fbda8..24f60d596fb 100644 --- a/spring-components/rdf4j-spring/src/main/java/org/eclipse/rdf4j/spring/dao/support/RelationMapBuilder.java +++ b/spring-components/rdf4j-spring/src/main/java/org/eclipse/rdf4j/spring/dao/support/RelationMapBuilder.java @@ -146,14 +146,17 @@ private IRI getRelationValueOrNothing(BindingSet b) { private TupleQueryEvaluationBuilder makeTupleQueryBuilder() { return rdf4JTemplate - .tupleQuery( - Queries.SELECT(getProjection()) - .where(getWhereClause()) - .distinct() - .getQueryString()) + .tupleQuery(makeQueryString()) .withBindings(bindingsBuilder.build()); } + String makeQueryString() { + return Queries.SELECT(getProjection()) + .where(getWhereClause()) + .distinct() + .getQueryString(); + } + private Projectable[] getProjection() { if (this.isSubjectKeyed) { return new Projectable[] { @@ -168,14 +171,14 @@ private Projectable[] getProjection() { private GraphPattern[] getWhereClause() { TriplePattern tp = _relSubject.has(predicate, _relObject); + GraphPattern[] ret = new GraphPattern[constraints.length + 1]; if (this.isRelationOptional) { - GraphPattern[] ret = new GraphPattern[constraints.length + 1]; - ret[0] = tp.optional(); - System.arraycopy(constraints, 0, ret, 1, constraints.length); - return ret; + ret[constraints.length] = tp.optional(); } else { - return new GraphPattern[] { tp.and(constraints) }; + ret[constraints.length] = tp; } + System.arraycopy(constraints, 0, ret, 0, constraints.length); + return ret; } public RelationMapBuilder withBinding(Variable key, Value value) { diff --git a/spring-components/rdf4j-spring/src/main/java/org/eclipse/rdf4j/spring/dao/support/operation/TupleQueryResultConverter.java b/spring-components/rdf4j-spring/src/main/java/org/eclipse/rdf4j/spring/dao/support/operation/TupleQueryResultConverter.java index a65be43693d..96053acfd07 100644 --- a/spring-components/rdf4j-spring/src/main/java/org/eclipse/rdf4j/spring/dao/support/operation/TupleQueryResultConverter.java +++ b/spring-components/rdf4j-spring/src/main/java/org/eclipse/rdf4j/spring/dao/support/operation/TupleQueryResultConverter.java @@ -28,6 +28,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import org.eclipse.rdf4j.common.exception.RDF4JException; import org.eclipse.rdf4j.query.BindingSet; import org.eclipse.rdf4j.query.TupleQueryResult; import org.eclipse.rdf4j.spring.dao.support.BindingSetMapper; @@ -56,8 +57,10 @@ public void consumeResult(Consumer consumer) { try { consumer.accept(tupleQueryResult); } catch (Exception e) { - logger.debug("Caught execption while processing TupleQueryResult", e); - throw mapException("Error processing TupleQueryResult", e); + logger.debug("Caught execption while processing TupleQueryResult: {}", e.getMessage()); + RDF4JException mapped = mapException("Error processing TupleQueryResult", e); + logger.debug("Re-throwing as {} ", mapped.getClass().getSimpleName()); + throw mapped; } finally { tupleQueryResult.close(); tupleQueryResult = null; @@ -71,8 +74,10 @@ public T applyToResult(Function function) { try { return function.apply(tupleQueryResult); } catch (Exception e) { - logger.warn("Caught execption while processing TupleQueryResult", e); - throw mapException("Error processing TupleQueryResult", e); + logger.debug("Caught execption while processing TupleQueryResult: {}", e.getMessage()); + RDF4JException mapped = mapException("Error processing TupleQueryResult", e); + logger.debug("Re-throwing as {} ", mapped.getClass().getSimpleName()); + throw mapped; } finally { tupleQueryResult.close(); tupleQueryResult = null; diff --git a/spring-components/rdf4j-spring/src/test/java/org/eclipse/rdf4j/spring/dao/support/RelationMapBuilderTests.java b/spring-components/rdf4j-spring/src/test/java/org/eclipse/rdf4j/spring/dao/support/RelationMapBuilderTests.java new file mode 100644 index 00000000000..d1024c6ec5d --- /dev/null +++ b/spring-components/rdf4j-spring/src/test/java/org/eclipse/rdf4j/spring/dao/support/RelationMapBuilderTests.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * 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.spring.dao.support; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.eclipse.rdf4j.model.vocabulary.SKOS; +import org.eclipse.rdf4j.spring.RDF4JSpringTestBase; +import org.eclipse.rdf4j.spring.dao.support.RelationMapBuilder; +import org.eclipse.rdf4j.spring.domain.model.EX; +import org.eclipse.rdf4j.spring.support.RDF4JTemplate; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +public class RelationMapBuilderTests extends RDF4JSpringTestBase { + + @Autowired + RDF4JTemplate rdf4JTemplate; + + @Test + public void testRelationOnly() { + RelationMapBuilder rmb = new RelationMapBuilder(this.rdf4JTemplate, SKOS.BROADER); + assertEquals(normalize( + "SELECT DISTINCT ( ?rel_subject AS ?rel_key ) ( ?rel_object AS ?rel_value )\n" + + "WHERE { ?rel_subject ?rel_object . }"), + normalize(rmb.makeQueryString())); + } + + @Test + public void testRelationWithConstraints() { + RelationMapBuilder rmb = new RelationMapBuilder(this.rdf4JTemplate, EX.creatorOf) + .constraints(RelationMapBuilder._relSubject.isA( + EX.Artist)); + assertEquals(normalize("SELECT DISTINCT ( ?rel_subject AS ?rel_key ) ( ?rel_object AS ?rel_value )\n" + + "WHERE { ?rel_subject a .\n" + + "?rel_subject ?rel_object . }"), normalize(rmb.makeQueryString())); + } + + @Test + public void testOptionalRelationWithPattern() { + RelationMapBuilder rmb = new RelationMapBuilder(this.rdf4JTemplate, EX.creatorOf) + .constraints(RelationMapBuilder._relSubject.isA( + EX.Artist)) + .relationIsOptional(); + assertEquals(normalize( + "SELECT DISTINCT ( ?rel_subject AS ?rel_key ) ( ?rel_object AS ?rel_value )\n" + + "WHERE { ?rel_subject a .\n" + + "OPTIONAL { ?rel_subject ?rel_object . } }\n"), + normalize(rmb.makeQueryString())); + } + + private String normalize(String s) { + return s.replaceAll("\n", " ").replaceAll("\\s+", " ").trim(); + } +} diff --git a/tools/federation/src/main/java/org/eclipse/rdf4j/federated/evaluation/FederationEvalStrategy.java b/tools/federation/src/main/java/org/eclipse/rdf4j/federated/evaluation/FederationEvalStrategy.java index c4163320afc..a3e6237dac3 100644 --- a/tools/federation/src/main/java/org/eclipse/rdf4j/federated/evaluation/FederationEvalStrategy.java +++ b/tools/federation/src/main/java/org/eclipse/rdf4j/federated/evaluation/FederationEvalStrategy.java @@ -121,6 +121,7 @@ import org.eclipse.rdf4j.query.algebra.helpers.TupleExprs; import org.eclipse.rdf4j.query.algebra.helpers.collectors.VarNameCollector; import org.eclipse.rdf4j.query.impl.EmptyBindingSet; +import org.eclipse.rdf4j.query.impl.FallbackDataset; import org.eclipse.rdf4j.repository.RepositoryException; import org.eclipse.rdf4j.repository.sparql.federation.CollectionIteration; import org.eclipse.rdf4j.repository.sparql.federation.RepositoryFederatedService; @@ -179,19 +180,8 @@ public TupleExpr optimize(TupleExpr expr, EvaluationStatistics evaluationStatist FederationEvaluationStatistics stats = (FederationEvaluationStatistics) evaluationStatistics; QueryInfo queryInfo = stats.getQueryInfo(); - Dataset dataset = stats.getDataset(); - FederationContext federationContext = queryInfo.getFederationContext(); - List members; - if (dataset instanceof FedXDataset) { - // run the query against a selected set of endpoints - FedXDataset ds = (FedXDataset) dataset; - members = federationContext.getEndpointManager().getEndpoints(ds.getEndpoints()); - } else { - // evaluate against entire federation - FedX fed = federationContext.getFederation(); - members = fed.getMembers(); - } + List members = getMembersFromContext(stats); // Clone the tuple expression to allow for more aggressive optimizations TupleExpr clone = expr.clone(); @@ -270,6 +260,34 @@ public TupleExpr optimize(TupleExpr expr, EvaluationStatistics evaluationStatist return query; } + /** + * Returns the federation members that are active in the current federation. By default it is all federation + * members. If the passed {@link Dataset} is a {@link FedXDataset}, the defined {@link FedXDataset#getEndpoints()} + * are used. + * + * @param evaluationStatisticss to keep the current query context + * @return + */ + protected List getMembersFromContext(FederationEvaluationStatistics evaluationStatisticss) { + Dataset queryDataset = evaluationStatisticss.getDataset(); + + // if the query uses a FROM clause, the external dataset + // is wrapped as primary dataset in FallbackDataset + if (queryDataset instanceof FallbackDataset) { + queryDataset = ((FallbackDataset) queryDataset).getPrimary(); + } + + if (queryDataset instanceof FedXDataset) { + // run the query against a selected set of endpoints + FedXDataset ds = (FedXDataset) queryDataset; + return federationContext.getEndpointManager().getEndpoints(ds.getEndpoints()); + } else { + // evaluate against entire federation + FedX fed = federationContext.getFederation(); + return fed.getMembers(); + } + } + /** * Perform source selection for all statements of the query. As a result of this method all statement nodes are * annotated with their relevant sources. diff --git a/tools/federation/src/main/java/org/eclipse/rdf4j/federated/optimizer/LimitOptimizer.java b/tools/federation/src/main/java/org/eclipse/rdf4j/federated/optimizer/LimitOptimizer.java index 648397dc3fb..87132162e08 100644 --- a/tools/federation/src/main/java/org/eclipse/rdf4j/federated/optimizer/LimitOptimizer.java +++ b/tools/federation/src/main/java/org/eclipse/rdf4j/federated/optimizer/LimitOptimizer.java @@ -65,6 +65,18 @@ public void meet(Slice node) throws OptimizationException { applicableLimitInScope = node.getLimit(); } super.meet(node); + + TupleExpr expr = node.getArg(); + // if the top most element is a statement (e.g. for an ASK query with single statement pattern), + // i.e. no join, union or + // any other complex pattern, we can push the limit + // => this case typically represents a query with a single BGP + if (expr instanceof FedXStatementPattern) { + if (applicableLimitInScope > 0) { + pushLimit((FedXStatementPattern) expr, applicableLimitInScope); + } + } + applicableLimitInScope = -1; } diff --git a/tools/federation/src/test/java/org/eclipse/rdf4j/federated/BasicTests.java b/tools/federation/src/test/java/org/eclipse/rdf4j/federated/BasicTests.java index 98e554c6b58..962ded27832 100644 --- a/tools/federation/src/test/java/org/eclipse/rdf4j/federated/BasicTests.java +++ b/tools/federation/src/test/java/org/eclipse/rdf4j/federated/BasicTests.java @@ -24,6 +24,8 @@ import org.eclipse.rdf4j.model.Statement; import org.eclipse.rdf4j.model.util.Values; import org.eclipse.rdf4j.model.vocabulary.DCTERMS; +import org.eclipse.rdf4j.model.vocabulary.FOAF; +import org.eclipse.rdf4j.model.vocabulary.RDF; import org.eclipse.rdf4j.model.vocabulary.RDFS; import org.eclipse.rdf4j.query.AbstractTupleQueryResultHandler; import org.eclipse.rdf4j.query.BindingSet; @@ -35,6 +37,7 @@ import org.eclipse.rdf4j.query.TupleQueryResult; import org.eclipse.rdf4j.query.TupleQueryResultHandlerException; import org.eclipse.rdf4j.query.impl.SimpleDataset; +import org.eclipse.rdf4j.repository.Repository; import org.eclipse.rdf4j.repository.RepositoryConnection; import org.eclipse.rdf4j.repository.util.Repositories; import org.junit.jupiter.api.Assertions; @@ -575,4 +578,83 @@ public void test_EscapingQuotedLiteral() throws Exception { } } + + @Test + public void test_reduceFederation() throws Exception { + + List endpoints = prepareTest( + Arrays.asList("/tests/basic/data_emptyStore.ttl", "/tests/basic/data_emptyStore.ttl")); + + Repository repo1 = getRepository(1); + Repository repo2 = getRepository(2); + + String repo1Id = endpoints.get(0).getId(); + + IRI graph1 = Values.iri("http://example.org/graph1"); + IRI graph2 = Values.iri("http://example.org/graph2"); + + try (RepositoryConnection con = repo1.getConnection()) { + con.add(Values.iri("http://example.org/repo1/p1"), RDF.TYPE, FOAF.PERSON, graph1); + con.add(Values.iri("http://example.org/repo2/p2"), RDF.TYPE, FOAF.PERSON, graph2); + } + + try (RepositoryConnection con = repo2.getConnection()) { + con.add(Values.iri("http://example.org/repo2/p3"), RDF.TYPE, FOAF.PERSON, graph1); + } + + Repository fedxRepo = fedxRule.getRepository(); + + // 1: regular federation + try (RepositoryConnection con = fedxRepo.getConnection()) { + TupleQuery tupleQuery = con.prepareTupleQuery( + "PREFIX foaf: " + + "SELECT * WHERE { " + + " ?subClass a foaf:Person. " + + " } " + ); + + // expect from both repos + Assertions.assertEquals(3, QueryResults.asSet(tupleQuery.evaluate()).size()); + + // now we scope it additional to graph1 + tupleQuery = con.prepareTupleQuery( + "PREFIX foaf: " + + "SELECT * FROM WHERE { " + + " ?subClass a foaf:Person. " + + " } "); + + // expect results defined in graph1 (1 in repo1, 2 from repo2) + Assertions.assertEquals(2, QueryResults.asSet(tupleQuery.evaluate()).size()); + } + + // 2: reduce to federation member 1 id + // 2a: additionall restrict to named graph + FedXDataset fedXDataset = new FedXDataset(new SimpleDataset()); + fedXDataset.addEndpoint(repo1Id); + + try (RepositoryConnection con = fedxRepo.getConnection()) { + TupleQuery tupleQuery = con.prepareTupleQuery( + "PREFIX foaf: " + + "SELECT * WHERE { " + + " ?subClass a foaf:Person. " + + " } " + ); + tupleQuery.setDataset(fedXDataset); + + // expect result from repo 1 + Assertions.assertEquals(2, QueryResults.asSet(tupleQuery.evaluate()).size()); + + // now we scope it additional to graph1 + tupleQuery = con.prepareTupleQuery( + "PREFIX foaf: " + + "SELECT * FROM WHERE { " + + " ?subClass a foaf:Person. " + + " } "); + tupleQuery.setDataset(fedXDataset); + + // expect result from graph1 from repo1 + Assertions.assertEquals(1, QueryResults.asSet(tupleQuery.evaluate()).size()); + } + + } } diff --git a/tools/federation/src/test/java/org/eclipse/rdf4j/federated/LimitTests.java b/tools/federation/src/test/java/org/eclipse/rdf4j/federated/LimitTests.java new file mode 100644 index 00000000000..0fb9deaf664 --- /dev/null +++ b/tools/federation/src/test/java/org/eclipse/rdf4j/federated/LimitTests.java @@ -0,0 +1,69 @@ +/******************************************************************************* + * 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; + +import java.util.Arrays; + +import org.eclipse.rdf4j.model.vocabulary.FOAF; +import org.eclipse.rdf4j.query.QueryResults; +import org.eclipse.rdf4j.query.TupleQuery; +import org.eclipse.rdf4j.repository.Repository; +import org.eclipse.rdf4j.repository.RepositoryConnection; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class LimitTests extends SPARQLBaseTest { + + @Test + public void testLimitPushing_Select_SingleStatement() throws Exception { + + // datsets contain both instances of foaf:Person + prepareTest( + Arrays.asList("/tests/data/data1.ttl", "/tests/data/data2.ttl")); + + Repository fedxRepo = fedxRule.getRepository(); + + try (RepositoryConnection conn = fedxRepo.getConnection()) { + + String query = "SELECT * WHERE { ?person a <" + FOAF.PERSON.stringValue() + "> } LIMIT 2"; + TupleQuery tq = conn.prepareTupleQuery(query); + Assertions.assertEquals(2, QueryResults.asList(tq.evaluate()).size()); + + // check that the query plan contains information about limit + String queryPlan = fedxRule.getFederationContext().getQueryManager().getQueryPlan(query); + Assertions.assertTrue(queryPlan.contains("Upper Limit: 2")); + } + } + + @Test + public void testLimitPushing_Ask_SingleStatement() throws Exception { + + // datsets contain both instances of foaf:Person + prepareTest( + Arrays.asList("/tests/data/data1.ttl", "/tests/data/data2.ttl")); + + Repository fedxRepo = fedxRule.getRepository(); + + try (RepositoryConnection conn = fedxRepo.getConnection()) { + + String query = "ASK { ?person a <" + FOAF.PERSON.stringValue() + "> }"; + Assertions.assertTrue(conn.prepareBooleanQuery(query).evaluate()); + + // check that the query plan contains information about limit + String queryPlan = fedxRule.getFederationContext().getQueryManager().getQueryPlan(query); + Assertions.assertTrue(queryPlan.contains("Upper Limit: 1")); + + // also run a query with no backing data + query = "ASK { ?organization a <" + FOAF.ORGANIZATION.stringValue() + "> }"; + Assertions.assertFalse(conn.prepareBooleanQuery(query).evaluate()); + } + } +}