From 5de9b3fd2f51d1b0772e08c849c04e19681ea1ec Mon Sep 17 00:00:00 2001 From: scottf Date: Tue, 4 Aug 2026 20:42:49 -0400 Subject: [PATCH 1/3] ResourceUtils: fail with the file name when a resource is missing getResource(...) returns null for a missing resource, so the unguarded .getFile() threw a bare NPE with no indication of which file was absent. Route all three accessors through a single open() that throws FileNotFoundException naming the resource. Uses getResourceAsStream instead of new File(url.getFile()), matching what V3 already does. Also correct for resources inside a jar and for paths needing URL decoding. Drops the now-unnecessary DataFlowIssue suppression, which only existed to hide the unguarded deref. --- .../io/nats/client/utils/ResourceUtils.java | 42 ++++++++++++------- .../nats/client/utils/ResourceUtilsTests.java | 33 +++++++++++++++ 2 files changed, 61 insertions(+), 14 deletions(-) create mode 100644 src/test/java/io/nats/client/utils/ResourceUtilsTests.java diff --git a/src/test/java/io/nats/client/utils/ResourceUtils.java b/src/test/java/io/nats/client/utils/ResourceUtils.java index 440225009..1f770801c 100644 --- a/src/test/java/io/nats/client/utils/ResourceUtils.java +++ b/src/test/java/io/nats/client/utils/ResourceUtils.java @@ -4,9 +4,9 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; import java.util.List; -@SuppressWarnings("DataFlowIssue") public abstract class ResourceUtils { public static List dataAsLines(String fileName) { return resourceAsLines("data/" + fileName); @@ -21,37 +21,51 @@ public static InputStream dataAsInputStream(String fileName) { } public static List resourceAsLines(String fileName) { - try { - ClassLoader classLoader = ResourceUtils.class.getClassLoader(); - File file = new File(classLoader.getResource(fileName).getFile()); - return Files.readAllLines(file.toPath()); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(open(fileName), StandardCharsets.UTF_8))) { + List lines = new ArrayList<>(); + String line; + while ((line = reader.readLine()) != null) { + lines.add(line); + } + return lines; } - catch (Exception e) { + catch (IOException e) { throw new RuntimeException(e); } - } public static String resourceAsString(String fileName) { - try { - ClassLoader classLoader = ResourceUtils.class.getClassLoader(); - File file = new File(classLoader.getResource(fileName).getFile()); - return new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8); + try (InputStream in = open(fileName)) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + byte[] buffer = new byte[8192]; + int len; + while ((len = in.read(buffer)) != -1) { + out.write(buffer, 0, len); + } + return new String(out.toByteArray(), StandardCharsets.UTF_8); } - catch (Exception e) { + catch (IOException e) { throw new RuntimeException(e); } } public static InputStream resourceAsInputStream(String fileName) { try { - return ResourceUtils.class.getClassLoader().getResourceAsStream(fileName); + return open(fileName); } - catch (Exception e) { + catch (IOException e) { throw new RuntimeException(e); } } + private static InputStream open(String fileName) throws FileNotFoundException { + InputStream in = ResourceUtils.class.getClassLoader().getResourceAsStream(fileName); + if (in == null) { + throw new FileNotFoundException(fileName); + } + return in; + } + public static String createTempFile(String prefix, String suffix, String[] lines) throws IOException { File f = File.createTempFile(prefix, suffix); BufferedWriter writer = new BufferedWriter(new FileWriter(f)); diff --git a/src/test/java/io/nats/client/utils/ResourceUtilsTests.java b/src/test/java/io/nats/client/utils/ResourceUtilsTests.java new file mode 100644 index 000000000..0e7b2dddf --- /dev/null +++ b/src/test/java/io/nats/client/utils/ResourceUtilsTests.java @@ -0,0 +1,33 @@ +package io.nats.client.utils; + +import org.junit.jupiter.api.Test; + +import java.io.FileNotFoundException; + +import static io.nats.client.utils.ResourceUtils.*; +import static org.junit.jupiter.api.Assertions.*; + +public class ResourceUtilsTests { + + private static final String MISSING = "ThisResourceDoesNotExist.json"; + + @Test + public void testMissingResourceIdentifiesTheFile() { + assertMissing(assertThrows(RuntimeException.class, () -> dataAsString(MISSING))); + assertMissing(assertThrows(RuntimeException.class, () -> dataAsLines(MISSING))); + assertMissing(assertThrows(RuntimeException.class, () -> dataAsInputStream(MISSING))); + } + + private void assertMissing(RuntimeException e) { + Throwable cause = e.getCause(); + assertInstanceOf(FileNotFoundException.class, cause); + assertTrue(cause.getMessage().contains(MISSING)); + } + + @Test + public void testResourceStillLoads() { + assertTrue(dataAsString("StreamConfiguration.json").contains("retention")); + assertFalse(dataAsLines("StreamConfiguration.json").isEmpty()); + assertNotNull(dataAsInputStream("StreamConfiguration.json")); + } +} From 90eed680ad2771d8294dfaed952d24b69e875526 Mon Sep 17 00:00:00 2001 From: scottf Date: Tue, 4 Aug 2026 21:04:40 -0400 Subject: [PATCH 2/3] Match V3's resourceAsLines verbatim V3's form is fully Java 8 compatible, so there was no reason to reflow it. The only remaining difference between the two files is now resourceAsString, where V3's in.readAllBytes() is Java 9+ and this has to read through a buffer instead. --- src/test/java/io/nats/client/utils/ResourceUtils.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/test/java/io/nats/client/utils/ResourceUtils.java b/src/test/java/io/nats/client/utils/ResourceUtils.java index 1f770801c..1f8c2f573 100644 --- a/src/test/java/io/nats/client/utils/ResourceUtils.java +++ b/src/test/java/io/nats/client/utils/ResourceUtils.java @@ -21,12 +21,10 @@ public static InputStream dataAsInputStream(String fileName) { } public static List resourceAsLines(String fileName) { - try (BufferedReader reader = new BufferedReader(new InputStreamReader(open(fileName), StandardCharsets.UTF_8))) { + try (BufferedReader r = new BufferedReader( + new InputStreamReader(open(fileName), StandardCharsets.UTF_8))) { List lines = new ArrayList<>(); - String line; - while ((line = reader.readLine()) != null) { - lines.add(line); - } + for (String l; (l = r.readLine()) != null; ) lines.add(l); return lines; } catch (IOException e) { From 9fc1c8ef763f4638eb8c5aa47e319651e0ce590b Mon Sep 17 00:00:00 2001 From: scottf Date: Wed, 5 Aug 2026 11:58:20 -0400 Subject: [PATCH 3/3] fixed git config files --- .gitattributes | 17 ++++++++++++++++- .gitignore | 3 ++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/.gitattributes b/.gitattributes index bcaca0269..db61f6488 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,16 @@ -gradlew text eol=lf +# Normalize all text files to LF in the repository (checkout follows the platform). +* text=auto + +# Scripts that must keep a specific line ending regardless of platform. +gradlew text eol=lf +*.sh text eol=lf +*.bat text eol=crlf +*.cmd text eol=crlf + +# Binary assets — never touch line endings. +*.rar binary +*.jar binary +*.png binary +*.jpg binary +*.gif binary +*.ico binary diff --git a/.gitignore b/.gitignore index 7f8c9de71..b542a5c89 100644 --- a/.gitignore +++ b/.gitignore @@ -7,9 +7,10 @@ examples/**/Z*.java **/examples/z/** **/examples/testapp_mc/** **/Debug*.java +**/TestDebugger* .claude/** +.editorconfig env.bat -**/TestDebugger* # Compiled source # ###################