This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-io.git


The following commit(s) were added to refs/heads/master by this push:
     new 68be2fbbe Add and use IOUtils.closeQuietly(Closeable, Throwable) (#818)
68be2fbbe is described below

commit 68be2fbbe46d26a854d6a8af6e38a8f60a4b49a0
Author: Gary Gregory <[email protected]>
AuthorDate: Thu Dec 11 16:20:55 2025 -0500

    Add and use IOUtils.closeQuietly(Closeable, Throwable) (#818)
    
    * [IO-856] Try test on all OSs for GitHub CI
    
    * Add and use IOUtils.closeQuietly(Closeable, Throwable)
---
 src/main/java/org/apache/commons/io/FileUtils.java |  2 +-
 src/main/java/org/apache/commons/io/IOUtils.java   | 37 ++++++++++++++++++++--
 .../java/org/apache/commons/io/LineIterator.java   |  3 +-
 .../commons/io/output/FileWriterWithEncoding.java  |  6 +---
 .../java/org/apache/commons/io/IOUtilsTest.java    | 13 ++++++++
 5 files changed, 51 insertions(+), 10 deletions(-)

diff --git a/src/main/java/org/apache/commons/io/FileUtils.java 
b/src/main/java/org/apache/commons/io/FileUtils.java
index 523c2c1fd..a24ab813a 100644
--- a/src/main/java/org/apache/commons/io/FileUtils.java
+++ b/src/main/java/org/apache/commons/io/FileUtils.java
@@ -2290,7 +2290,7 @@ public static LineIterator lineIterator(final File file, 
final String charsetNam
             inputStream = Files.newInputStream(file.toPath());
             return IOUtils.lineIterator(inputStream, charsetName);
         } catch (final IOException | RuntimeException ex) {
-            IOUtils.closeQuietly(inputStream, ex::addSuppressed);
+            IOUtils.closeQuietly(inputStream, ex);
             throw ex;
         }
     }
diff --git a/src/main/java/org/apache/commons/io/IOUtils.java 
b/src/main/java/org/apache/commons/io/IOUtils.java
index 09a018b03..8a4057ae5 100644
--- a/src/main/java/org/apache/commons/io/IOUtils.java
+++ b/src/main/java/org/apache/commons/io/IOUtils.java
@@ -780,7 +780,7 @@ public static void close(final URLConnection conn) {
      * @param closeable the object to close, may be null.
      */
     private static void closeQ(final Closeable closeable) {
-        closeQuietly(closeable, null);
+        closeQuietly(closeable, (Consumer<Exception>) null);
     }
 
     /**
@@ -824,7 +824,40 @@ private static void closeQ(final Closeable closeable) {
      * @see Throwable#addSuppressed(Throwable)
      */
     public static void closeQuietly(final Closeable closeable) {
-        closeQuietly(closeable, null);
+        closeQuietly(closeable, (Consumer<Exception>) null);
+    }
+
+    /**
+     * Closes a {@link Closeable} unconditionally and adds any exception 
thrown by the {@code close()} to the given Throwable.
+     *
+     * <p>
+     * For example:
+     * </p>
+     *
+     * <pre>
+     * Closeable closeable = ...;
+     * try {
+     *     // process closeable
+     *     closeable.close();
+     * } catch (Exception e) {
+     *     // error handling
+     *     throw IOUtils.closeQuietly(closeable, e);
+     * }
+     * </pre>
+     * <p>
+     * Also consider using a try-with-resources statement where appropriate.
+     * </p>
+     *
+     * @param <T> The Throwable type.
+     * @param closeable The object to close, may be null or already closed.
+     * @param throwable Add the exception throw by the closeable to the given 
Throwable.
+     * @return The given Throwable.
+     * @since 2.22.0
+     * @see Throwable#addSuppressed(Throwable)
+     */
+    public static <T extends Throwable> T closeQuietly(final Closeable 
closeable, final T throwable) {
+        closeQuietly(closeable, (Consumer<Exception>) 
throwable::addSuppressed);
+        return throwable;
     }
 
     /**
diff --git a/src/main/java/org/apache/commons/io/LineIterator.java 
b/src/main/java/org/apache/commons/io/LineIterator.java
index 293847e7a..2e1bf0169 100644
--- a/src/main/java/org/apache/commons/io/LineIterator.java
+++ b/src/main/java/org/apache/commons/io/LineIterator.java
@@ -135,8 +135,7 @@ public boolean hasNext() {
                 }
             }
         } catch (final IOException ioe) {
-            IOUtils.closeQuietly(this, ioe::addSuppressed);
-            throw new IllegalStateException(ioe);
+            throw new IllegalStateException(IOUtils.closeQuietly(this, ioe));
         }
     }
 
diff --git 
a/src/main/java/org/apache/commons/io/output/FileWriterWithEncoding.java 
b/src/main/java/org/apache/commons/io/output/FileWriterWithEncoding.java
index 42d378cc0..1bb15a37a 100644
--- a/src/main/java/org/apache/commons/io/output/FileWriterWithEncoding.java
+++ b/src/main/java/org/apache/commons/io/output/FileWriterWithEncoding.java
@@ -188,11 +188,7 @@ private static OutputStreamWriter initWriter(final File 
file, final Object encod
             }
             return new OutputStreamWriter(outputStream, (String) encoding);
         } catch (final IOException | RuntimeException ex) {
-            try {
-                IOUtils.close(outputStream);
-            } catch (final IOException e) {
-                ex.addSuppressed(e);
-            }
+            IOUtils.closeQuietly(outputStream, ex);
             if (!fileExistedAlready) {
                 FileUtils.deleteQuietly(file);
             }
diff --git a/src/test/java/org/apache/commons/io/IOUtilsTest.java 
b/src/test/java/org/apache/commons/io/IOUtilsTest.java
index 033fdffdd..df90ab66e 100644
--- a/src/test/java/org/apache/commons/io/IOUtilsTest.java
+++ b/src/test/java/org/apache/commons/io/IOUtilsTest.java
@@ -20,6 +20,7 @@
 import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNotSame;
 import static org.junit.jupiter.api.Assertions.assertNull;
@@ -539,6 +540,18 @@ void testCloseQuietly_AllCloseableIOException() {
         assertDoesNotThrow(() -> IOUtils.closeQuietly((Iterable<Closeable>) 
null));
     }
 
+    @SuppressWarnings("resource")
+    @Test
+    void testCloseQuietly_CloseableIOExceptionAddSuppressed() {
+        final Throwable e = new Exception("test").fillInStackTrace();
+        assertEquals(0, e.getSuppressed().length);
+        assertSame(e, IOUtils.closeQuietly(new BrokenInputStream(new 
EOFException("Suppressed").fillInStackTrace()), e));
+        assertEquals(1, e.getSuppressed().length);
+        final Throwable suppressed0 = e.getSuppressed()[0];
+        assertInstanceOf(EOFException.class, suppressed0);
+        assertEquals("Suppressed", suppressed0.getMessage());
+    }
+
     @Test
     void testCloseQuietly_CloseableException() {
         // IOException

Reply via email to