Merge branch 'master' into release
diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index 4f627f7..dc3be14 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -21,6 +21,58 @@
 o IO-851:  FileSystemUtils no longer throws IllegalStateException. Thanks to Sebb, Gary Gregory. 
 o          Avoid possible NullPointerException in FileUtils.listAccumulate(File, IOFileFilter, IOFileFilter, FileVisitOption...). Thanks to Gary Gregory. 
 o IO-853:  BoundedInputStream.reset() not updating count. Thanks to Mike Drob, Gary Gregory. 
+o          ThresholdingOutputStream: a negative threshold should behave like a zero threshold and trigger the event on the first write #609. Thanks to rproserpio, Gary Gregory. 
+
+Changes
+-------
+
+o          Bump commons.bytebuddy.version from 1.14.12 to 1.14.13 #605. Thanks to Gary Gregory. 
+o          Bump org.apache.commons:commons-parent from 67 to 69 #608. Thanks to Gary Gregory, Dependabot. 
+
+
+Commons IO 2.7 and up requires Java 8 or above.
+Commons IO 2.6 requires Java 7 or above.
+Commons IO 2.3 through 2.5 requires Java 6 or above.
+Commons IO 2.2 requires Java 5 or above.
+Commons IO 1.4 requires Java 1.3 or above.
+
+Historical list of changes: https://commons.apache.org/proper/commons-io/changes-report.html
+
+For complete information on Apache Commons IO, including instructions on how to submit bug reports,
+patches, or suggestions for improvement, see the Apache Commons IO website:
+
+https://commons.apache.org/proper/commons-io/
+
+Download page: https://commons.apache.org/proper/commons-io/download_io.cgi
+
+Have fun!
+-Apache Commons Team
+
+------------------------------------------------------------------------------
+
+
+Apache Commons IO 2.16.1 Release Notes
+
+Introduction
+------------
+
+Commons IO is a package of Java utility classes like java.io.  
+Classes in this package are considered to be so standard and of such high 
+reuse as to justify existence in java.io.
+
+The Apache Commons IO library contains utility classes, stream implementations, file filters,
+file comparators, endian transformation classes, and much more.
+
+Java 8 is required.
+
+
+Fixed Bugs
+----------
+
+o          Reimplement FileSystemUtils using NIO. Thanks to Gary Gregory. 
+o IO-851:  FileSystemUtils no longer throws IllegalStateException. Thanks to Sebb, Gary Gregory. 
+o          Avoid possible NullPointerException in FileUtils.listAccumulate(File, IOFileFilter, IOFileFilter, FileVisitOption...). Thanks to Gary Gregory. 
+o IO-853:  BoundedInputStream.reset() not updating count. Thanks to Mike Drob, Gary Gregory. 
 
 Changes
 -------
diff --git a/pom.xml b/pom.xml
index 11cb55c..37b2226 100644
--- a/pom.xml
+++ b/pom.xml
@@ -124,7 +124,7 @@
     <maven.compiler.target>1.8</maven.compiler.target>
     <commons.componentid>io</commons.componentid>
     <commons.module.name>org.apache.commons.io</commons.module.name>
-    <commons.rc.version>RC1</commons.rc.version>
+    <commons.rc.version>RC2</commons.rc.version>
     <commons.bc.version>2.16.0</commons.bc.version>
     <commons.release.version>2.16.1</commons.release.version>
     <commons.release.next>2.16.2</commons.release.next>
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 300389d..5d17e4e 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -52,6 +52,7 @@
       <action dev="ggregory" type="fix" issue="IO-851" due-to="Sebb, Gary Gregory">FileSystemUtils no longer throws IllegalStateException.</action>
       <action dev="ggregory" type="fix"                due-to="Gary Gregory">Avoid possible NullPointerException in FileUtils.listAccumulate(File, IOFileFilter, IOFileFilter, FileVisitOption...).</action>
       <action dev="ggregory" type="fix" issue="IO-853" due-to="Mike Drob, Gary Gregory">BoundedInputStream.reset() not updating count.</action>
+      <action dev="ggregory" type="fix"                due-to="rproserpio, Gary Gregory">ThresholdingOutputStream: a negative threshold should behave like a zero threshold and trigger the event on the first write #609.</action>
       <!-- UPDATE -->
       <action dev="ggregory" type="update"             due-to="Gary Gregory">Bump commons.bytebuddy.version from 1.14.12 to 1.14.13 #605.</action>
       <action dev="ggregory" type="update"             due-to="Gary Gregory, Dependabot">Bump org.apache.commons:commons-parent from 67 to 69 #608.</action>
diff --git a/src/main/java/org/apache/commons/io/output/ThresholdingOutputStream.java b/src/main/java/org/apache/commons/io/output/ThresholdingOutputStream.java
index fd7a861..c0bf601 100644
--- a/src/main/java/org/apache/commons/io/output/ThresholdingOutputStream.java
+++ b/src/main/java/org/apache/commons/io/output/ThresholdingOutputStream.java
@@ -81,6 +81,7 @@
 
     /**
      * Constructs an instance of this class which will trigger an event at the specified threshold.
+     * A negative threshold has no meaning and will be treated as 0
      *
      * @param threshold The number of bytes at which to trigger an event.
      * @param thresholdConsumer Accepts reaching the threshold.
@@ -89,10 +90,9 @@
      */
     public ThresholdingOutputStream(final int threshold, final IOConsumer<ThresholdingOutputStream> thresholdConsumer,
         final IOFunction<ThresholdingOutputStream, OutputStream> outputStreamGetter) {
-        this.threshold = threshold;
+        this.threshold = threshold < 0 ? 0 : threshold;
         this.thresholdConsumer = thresholdConsumer == null ? IOConsumer.noop() : thresholdConsumer;
         this.outputStreamGetter = outputStreamGetter == null ? NOOP_OS_GETTER : outputStreamGetter;
-        this.thresholdExceeded = threshold < 0;
     }
 
     /**
diff --git a/src/test/java/org/apache/commons/io/output/DeferredFileOutputStreamTest.java b/src/test/java/org/apache/commons/io/output/DeferredFileOutputStreamTest.java
index 08e38c1..d412845 100644
--- a/src/test/java/org/apache/commons/io/output/DeferredFileOutputStreamTest.java
+++ b/src/test/java/org/apache/commons/io/output/DeferredFileOutputStreamTest.java
@@ -105,6 +105,28 @@
     }
 
     /**
+     * Tests the case where the threshold is negative, and therefore the data is always written to disk. The actual data
+     * written to disk is verified, as is the file itself.
+     */
+    @ParameterizedTest(name = "initialBufferSize = {0}")
+    @MethodSource("data")
+    public void testThresholdNegative(final int initialBufferSize) throws IOException {
+        final File testFile = Files.createTempFile(tempDirPath, "testThresholdNegative", "dat").toFile();
+        try (DeferredFileOutputStream dfos = DeferredFileOutputStream.builder()
+                .setThreshold(-1)
+                .setBufferSize(initialBufferSize)
+                .setOutputFile(testFile)
+                .get()) {
+            dfos.write(testBytes, 0, testBytes.length);
+            dfos.close();
+            assertFalse(dfos.isInMemory());
+            assertNull(dfos.getData());
+            assertEquals(testFile.length(), dfos.getByteCount());
+            verifyResultFile(testFile);
+        }
+    }
+
+    /**
      * Tests the case where the amount of data is exactly the same as the threshold. The behavior should be the same as
      * that for the amount of data being below (i.e. not exceeding) the threshold.
      */
diff --git a/src/test/java/org/apache/commons/io/output/ThresholdingOutputStreamTest.java b/src/test/java/org/apache/commons/io/output/ThresholdingOutputStreamTest.java
index cc03f8d..4391643 100644
--- a/src/test/java/org/apache/commons/io/output/ThresholdingOutputStreamTest.java
+++ b/src/test/java/org/apache/commons/io/output/ThresholdingOutputStreamTest.java
@@ -32,13 +32,47 @@
  */
 public class ThresholdingOutputStreamTest {
 
+    /**
+     * Tests the case where the threshold is negative.
+     * The threshold is not reached until something is written to the stream.
+     */
     @Test
     public void testThresholdLessThanZero() throws IOException {
-        try (final ThresholdingOutputStream out = new ThresholdingOutputStream(-1)) {
+        final AtomicBoolean reached = new AtomicBoolean();
+        try (final ThresholdingOutputStream out = new ThresholdingOutputStream(-1) {
+            @Override
+            protected void thresholdReached() throws IOException {
+                reached.set(true);
+            }
+        }) {
+            assertFalse(reached.get());
+            out.write(89);
+            assertTrue(reached.get());
             assertTrue(out.isThresholdExceeded());
         }
     }
 
+    /**
+     * Tests the case where no bytes are written.
+     * The threshold is not reached until something is written to the stream.
+     */
+    @Test
+    public void testThresholdZeroWrite() throws IOException {
+        final AtomicBoolean reached = new AtomicBoolean();
+        try (final ThresholdingOutputStream out = new ThresholdingOutputStream(7) {
+            @Override
+            protected void thresholdReached() throws IOException {
+                reached.set(true);
+            }
+        }) {
+            assertFalse(out.isThresholdExceeded());
+            assertFalse(reached.get());
+            out.write(new byte[0]);
+            assertFalse(out.isThresholdExceeded());
+            assertFalse(reached.get());
+        }
+    }
+
     @Test
     public void testThresholdZero() throws IOException {
         final AtomicBoolean reached = new AtomicBoolean();