From 9dfa544acc9f7a973d15b6030ffcc4736dc49d5e Mon Sep 17 00:00:00 2001 From: Manuel Fiorelli Date: Mon, 4 Aug 2025 01:52:17 +0200 Subject: [PATCH 1/4] GH-5393: prevent the configuration migration routine to be invoked with an already open input stream to config.ttl --- .../rdf4j/repository/manager/LocalRepositoryManager.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/core/repository/manager/src/main/java/org/eclipse/rdf4j/repository/manager/LocalRepositoryManager.java b/core/repository/manager/src/main/java/org/eclipse/rdf4j/repository/manager/LocalRepositoryManager.java index 20a278f6177..fff7303922b 100644 --- a/core/repository/manager/src/main/java/org/eclipse/rdf4j/repository/manager/LocalRepositoryManager.java +++ b/core/repository/manager/src/main/java/org/eclipse/rdf4j/repository/manager/LocalRepositoryManager.java @@ -261,8 +261,11 @@ public synchronized RepositoryConfig getRepositoryConfig(String id) { File dataDir = getRepositoryDir(id); if (new File(dataDir, CFG_FILE).exists()) { File configFile = new File(dataDir, CFG_FILE); - try (InputStream input = new FileInputStream(configFile)) { - Model model = Rio.parse(input, configFile.toURI().toString(), CONFIG_FORMAT); + try { + Model model; + try (InputStream input = new FileInputStream(configFile)) { + model = Rio.parse(input, configFile.toURI().toString(), CONFIG_FORMAT); + } Set repositoryIDs = RepositoryConfigUtil.getRepositoryIDs(model); if (repositoryIDs.isEmpty()) { From be76402334ce1bb58ddd074466278cac1ef9d8c7 Mon Sep 17 00:00:00 2001 From: Jerven Bolleman Date: Tue, 5 Aug 2025 21:28:11 +0200 Subject: [PATCH 2/4] GH-5393 document why the input is read in an inner try --- .../eclipse/rdf4j/repository/manager/LocalRepositoryManager.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/repository/manager/src/main/java/org/eclipse/rdf4j/repository/manager/LocalRepositoryManager.java b/core/repository/manager/src/main/java/org/eclipse/rdf4j/repository/manager/LocalRepositoryManager.java index fff7303922b..3aa3a4c459d 100644 --- a/core/repository/manager/src/main/java/org/eclipse/rdf4j/repository/manager/LocalRepositoryManager.java +++ b/core/repository/manager/src/main/java/org/eclipse/rdf4j/repository/manager/LocalRepositoryManager.java @@ -263,6 +263,7 @@ public synchronized RepositoryConfig getRepositoryConfig(String id) { File configFile = new File(dataDir, CFG_FILE); try { Model model; + // We read the input and close it so that it may be reopend by the migration process try (InputStream input = new FileInputStream(configFile)) { model = Rio.parse(input, configFile.toURI().toString(), CONFIG_FORMAT); } From c162d025fe1eb94bf813e120a84ed20fe08ce620 Mon Sep 17 00:00:00 2001 From: Kacper Grzymkowski Date: Wed, 6 Aug 2025 14:54:43 +0200 Subject: [PATCH 3/4] GH-5397 Fix incorrect SHACL format inference --- .../eclipse/rdf4j/console/command/Verify.java | 2 +- .../rdf4j/console/command/VerifyTest.java | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/tools/console/src/main/java/org/eclipse/rdf4j/console/command/Verify.java b/tools/console/src/main/java/org/eclipse/rdf4j/console/command/Verify.java index d94240d54d5..4233ab42906 100644 --- a/tools/console/src/main/java/org/eclipse/rdf4j/console/command/Verify.java +++ b/tools/console/src/main/java/org/eclipse/rdf4j/console/command/Verify.java @@ -186,7 +186,7 @@ private void shacl(String dataPath, String shaclPath, String reportFile) { writeln("Loading shapes from " + shaclPath); URL shaclURL = new URL(shaclPath); - RDFFormat format = Rio.getParserFormatForFileName(reportFile).orElse(RDFFormat.TURTLE); + RDFFormat format = Rio.getParserFormatForFileName(shaclPath).orElse(RDFFormat.TURTLE); try (SailRepositoryConnection conn = repo.getConnection()) { conn.begin(IsolationLevels.NONE, ShaclSail.TransactionSettings.ValidationApproach.Disabled); diff --git a/tools/console/src/test/java/org/eclipse/rdf4j/console/command/VerifyTest.java b/tools/console/src/test/java/org/eclipse/rdf4j/console/command/VerifyTest.java index 09e79f28d6e..0dfe1849f42 100644 --- a/tools/console/src/test/java/org/eclipse/rdf4j/console/command/VerifyTest.java +++ b/tools/console/src/test/java/org/eclipse/rdf4j/console/command/VerifyTest.java @@ -127,6 +127,25 @@ public final void testShaclValid() throws IOException { assertFalse(io.wasErrorWritten()); } + @Test + public final void testShaclInvalidFormat() throws IOException { + File report = new File(locationFile, "testShaclInvalid.nt"); + cmd.execute("verify", copyFromRes("ok.ttl"), copyFromRes("shacl_invalid.ttl"), report.toString()); + assertTrue(io.wasErrorWritten()); + assertTrue(Files.size(report.toPath()) > 0); + } + + @Test + public final void testShaclValidFormat() throws IOException { + File report = new File(locationFile, "testShaclValid.nt"); + assertTrue(report.createNewFile()); + cmd.execute("verify", copyFromRes("ok.ttl"), copyFromRes("shacl_valid.ttl"), report.toString()); + + verify(mockConsoleIO, never()).writeError(anyString()); + assertFalse(Files.size(report.toPath()) > 0); + assertFalse(io.wasErrorWritten()); + } + @Test public final void testShaclValidWorkDir() throws IOException { setWorkingDir(cmd); From d6ae748f3a9c03599894e5eded3a3b56c174ffde Mon Sep 17 00:00:00 2001 From: Kacper Grzymkowski Date: Wed, 6 Aug 2025 14:24:44 +0200 Subject: [PATCH 4/4] GH-5395 Change console convert, export, verify to use OutputStreams when writing --- .../rdf4j/console/command/Convert.java | 6 ++++-- .../eclipse/rdf4j/console/command/Export.java | 10 +++++----- .../eclipse/rdf4j/console/command/Verify.java | 5 +++-- .../rdf4j/console/command/ConvertTest.java | 8 ++++++++ .../rdf4j/console/command/ExportTest.java | 9 +++++++++ .../rdf4j/console/command/VerifyTest.java | 19 +++++++++++++++++++ 6 files changed, 48 insertions(+), 9 deletions(-) diff --git a/tools/console/src/main/java/org/eclipse/rdf4j/console/command/Convert.java b/tools/console/src/main/java/org/eclipse/rdf4j/console/command/Convert.java index 602cc55f132..28d1a27e723 100644 --- a/tools/console/src/main/java/org/eclipse/rdf4j/console/command/Convert.java +++ b/tools/console/src/main/java/org/eclipse/rdf4j/console/command/Convert.java @@ -11,7 +11,7 @@ package org.eclipse.rdf4j.console.command; import java.io.BufferedInputStream; -import java.io.BufferedWriter; +import java.io.BufferedOutputStream; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -132,7 +132,7 @@ private void convert(String fileFrom, String fileTo) { String baseURI = pathFrom.toUri().toString(); try (BufferedInputStream r = new BufferedInputStream(Files.newInputStream(pathFrom)); - BufferedWriter w = Files.newBufferedWriter(pathTo)) { + BufferedOutputStream w = new BufferedOutputStream(Files.newOutputStream(pathTo))) { RDFWriter writer = Rio.createWriter(fmtTo.get(), w); parser.setRDFHandler(writer); @@ -141,6 +141,8 @@ private void convert(String fileFrom, String fileTo) { parser.parse(r, baseURI); + w.flush(); + long diff = (System.nanoTime() - startTime) / 1_000_000; writeln("Data has been written to file (" + diff + " ms)"); } catch (IOException | RDFParseException | RDFHandlerException e) { diff --git a/tools/console/src/main/java/org/eclipse/rdf4j/console/command/Export.java b/tools/console/src/main/java/org/eclipse/rdf4j/console/command/Export.java index ec3351e4e6d..6352ca6f7db 100644 --- a/tools/console/src/main/java/org/eclipse/rdf4j/console/command/Export.java +++ b/tools/console/src/main/java/org/eclipse/rdf4j/console/command/Export.java @@ -10,9 +10,7 @@ *******************************************************************************/ package org.eclipse.rdf4j.console.command; -import java.io.IOException; -import java.io.Writer; -import java.nio.charset.StandardCharsets; +import java.io.*; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; @@ -127,8 +125,8 @@ private void export(Repository repository, String fileName, Resource... contexts } try (RepositoryConnection conn = repository.getConnection(); - Writer w = Files.newBufferedWriter(path, StandardCharsets.UTF_8, StandardOpenOption.CREATE, - StandardOpenOption.TRUNCATE_EXISTING)) { + BufferedOutputStream w = new BufferedOutputStream(Files.newOutputStream(path, StandardOpenOption.CREATE, + StandardOpenOption.TRUNCATE_EXISTING))) { RDFFormat fmt = Rio.getWriterFormatForFileName(fileName) .orElseThrow(() -> new UnsupportedRDFormatException("No RDF parser for " + fileName)); @@ -139,6 +137,8 @@ private void export(Repository repository, String fileName, Resource... contexts conn.export(writer, contexts); + w.flush(); + long diff = (System.nanoTime() - startTime) / 1_000_000; writeln("Data has been written to file (" + diff + " ms)"); } catch (IOException | UnsupportedRDFormatException e) { diff --git a/tools/console/src/main/java/org/eclipse/rdf4j/console/command/Verify.java b/tools/console/src/main/java/org/eclipse/rdf4j/console/command/Verify.java index 4233ab42906..375e37a3da5 100644 --- a/tools/console/src/main/java/org/eclipse/rdf4j/console/command/Verify.java +++ b/tools/console/src/main/java/org/eclipse/rdf4j/console/command/Verify.java @@ -10,9 +10,9 @@ *******************************************************************************/ package org.eclipse.rdf4j.console.command; +import java.io.BufferedOutputStream; import java.io.IOException; import java.io.InputStream; -import java.io.Writer; import java.net.MalformedURLException; import java.net.URL; import java.nio.file.Files; @@ -279,8 +279,9 @@ private void writeReport(Model model, String reportFile) { RDFFormat format = Rio.getParserFormatForFileName(reportFile).orElse(RDFFormat.TURTLE); - try (Writer w = Files.newBufferedWriter(Paths.get(reportFile))) { + try (BufferedOutputStream w = new BufferedOutputStream(Files.newOutputStream(Paths.get(reportFile)))) { Rio.write(model, w, format, cfg); + w.flush(); } catch (IOException ex) { writeError("Could not write report to " + reportFile, ex); } diff --git a/tools/console/src/test/java/org/eclipse/rdf4j/console/command/ConvertTest.java b/tools/console/src/test/java/org/eclipse/rdf4j/console/command/ConvertTest.java index cdd79de05d0..11ec5c80d84 100644 --- a/tools/console/src/test/java/org/eclipse/rdf4j/console/command/ConvertTest.java +++ b/tools/console/src/test/java/org/eclipse/rdf4j/console/command/ConvertTest.java @@ -66,6 +66,14 @@ public final void testConvert() { assertTrue(o != null, "Invalid JSON"); } + @Test + public final void testConvertToBinary() { + File binary = new File(locationFile, "alien.brf"); + cmd.execute("convert", from.getAbsolutePath(), binary.getAbsolutePath()); + + assertTrue(binary.length() > 0, "File is empty"); + } + @Test public final void testConvertWorkDir() { setWorkingDir(cmd); diff --git a/tools/console/src/test/java/org/eclipse/rdf4j/console/command/ExportTest.java b/tools/console/src/test/java/org/eclipse/rdf4j/console/command/ExportTest.java index 772f940c9a8..60dc8f05e23 100644 --- a/tools/console/src/test/java/org/eclipse/rdf4j/console/command/ExportTest.java +++ b/tools/console/src/test/java/org/eclipse/rdf4j/console/command/ExportTest.java @@ -68,6 +68,15 @@ public final void testExportAll() throws RepositoryException, IOException { nq.delete(); } + @Test + public final void testExportAllToBinary() throws RepositoryException { + File binary = new File(locationFile, "all.brf"); + cmd.execute("export", binary.getAbsolutePath()); + assertTrue(binary.length() > 0, "File is empty"); + + binary.delete(); + } + @Test public final void testExportWorkDir() throws RepositoryException, IOException { setWorkingDir(cmd); diff --git a/tools/console/src/test/java/org/eclipse/rdf4j/console/command/VerifyTest.java b/tools/console/src/test/java/org/eclipse/rdf4j/console/command/VerifyTest.java index 0dfe1849f42..f781db28f35 100644 --- a/tools/console/src/test/java/org/eclipse/rdf4j/console/command/VerifyTest.java +++ b/tools/console/src/test/java/org/eclipse/rdf4j/console/command/VerifyTest.java @@ -146,6 +146,25 @@ public final void testShaclValidFormat() throws IOException { assertFalse(io.wasErrorWritten()); } + @Test + public final void testShaclInvalidToBinary() throws IOException { + File report = new File(locationFile, "testShaclInvalid.brf"); + cmd.execute("verify", copyFromRes("ok.ttl"), copyFromRes("shacl_invalid.ttl"), report.toString()); + assertTrue(io.wasErrorWritten()); + assertTrue(Files.size(report.toPath()) > 0); + } + + @Test + public final void testShaclValidToBinary() throws IOException { + File report = new File(locationFile, "testShaclValid.brf"); + assertTrue(report.createNewFile()); + cmd.execute("verify", copyFromRes("ok.ttl"), copyFromRes("shacl_valid.ttl"), report.toString()); + + verify(mockConsoleIO, never()).writeError(anyString()); + assertFalse(Files.size(report.toPath()) > 0); + assertFalse(io.wasErrorWritten()); + } + @Test public final void testShaclValidWorkDir() throws IOException { setWorkingDir(cmd);