diff --git a/sdks/java/core/src/main/java/org/apache/beam/sdk/io/FileSystems.java b/sdks/java/core/src/main/java/org/apache/beam/sdk/io/FileSystems.java index 10eab53842b7..1fad1cf8d319 100644 --- a/sdks/java/core/src/main/java/org/apache/beam/sdk/io/FileSystems.java +++ b/sdks/java/core/src/main/java/org/apache/beam/sdk/io/FileSystems.java @@ -195,8 +195,17 @@ public static Metadata matchSingleFileSpec(String spec) throws IOException { if (matchResult.status() == Status.NOT_FOUND) { throw new FileNotFoundException(String.format("File spec %s not found", spec)); } else if (matchResult.status() != Status.OK) { + // PROPOSED FIX: Extract the root cause by catching it from metadata() + IOException rootCause = null; + try { + matchResult.metadata(); + } catch (IOException e) { + rootCause = e; + } + // Chain the root cause into the new IOException throw new IOException( - String.format("Error matching file spec %s: status %s", spec, matchResult.status())); + String.format("Error matching file spec %s: status %s", spec, matchResult.status()), + rootCause); } else { List metadata = matchResult.metadata(); if (metadata.size() != 1) { diff --git a/sdks/java/core/src/test/java/org/apache/beam/sdk/io/FileSystemsTest.java b/sdks/java/core/src/test/java/org/apache/beam/sdk/io/FileSystemsTest.java index 34567309c7d0..83a679b60995 100644 --- a/sdks/java/core/src/test/java/org/apache/beam/sdk/io/FileSystemsTest.java +++ b/sdks/java/core/src/test/java/org/apache/beam/sdk/io/FileSystemsTest.java @@ -63,6 +63,38 @@ public class FileSystemsTest { @Rule public ExpectedException thrown = ExpectedException.none(); private LocalFileSystem localFileSystem = new LocalFileSystem(); + @Test + public void testMatchSingleFileSpecExceptionChaining() throws Exception { + java.io.IOException rootCause = new java.io.IOException("403 Forbidden: Fake GCS Error"); + MatchResult failedResult = MatchResult.create(MatchResult.Status.ERROR, rootCause); + + FileSystem mockFileSystem = mock(FileSystem.class); + when(mockFileSystem.match(org.mockito.ArgumentMatchers.anyList())) + .thenReturn(ImmutableList.of(failedResult)); + + // Use reflection to temporarily override the registered filesystems + java.lang.reflect.Field field = FileSystems.class.getDeclaredField("SCHEME_TO_FILESYSTEM"); + field.setAccessible(true); + @SuppressWarnings("unchecked") + java.util.concurrent.atomic.AtomicReference> ref = + (java.util.concurrent.atomic.AtomicReference>) + field.get(null); + + java.util.Map original = ref.get(); + try { + ref.set(com.google.common.collect.ImmutableMap.of("dummy", mockFileSystem)); + + thrown.expect(java.io.IOException.class); + thrown.expectMessage("Error matching file spec dummy://fake/path: status ERROR"); + thrown.expectCause(org.hamcrest.Matchers.is(rootCause)); + + FileSystems.matchSingleFileSpec("dummy://fake/path"); + } finally { + // Restore the original registry so we don't break other tests + ref.set(original); + } + } + @Test public void testGetLocalFileSystem() throws Exception { // TODO: Java core test failing on windows, https://github.com/apache/beam/issues/20484