--- old/src/share/classes/java/util/stream/Streams.java 2013-07-10 14:11:50.563135496 -0700 +++ new/src/share/classes/java/util/stream/Streams.java 2013-07-10 14:11:50.379135500 -0700 @@ -25,6 +25,7 @@ package java.util.stream; import java.util.Comparator; +import java.util.MayHoldCloseableResource; import java.util.Spliterator; import java.util.function.Consumer; import java.util.function.DoubleConsumer; @@ -810,4 +811,61 @@ } } } + + /** + * Given two Runnables, return a Runnable that executes both in sequence, + * even if the first throws an exception, and if both throw exceptions, add + * any exceptions thrown by the second as suppressed exceptions of the first. + */ + static Runnable composeWithExceptions(Runnable a, Runnable b) { + return new Runnable() { + @Override + public void run() { + try { + a.run(); + } + catch (Error|RuntimeException e1) { + try { + b.run(); + } + catch (Error|RuntimeException e2) { + e1.addSuppressed(e2); + } + finally { + throw e1; + } + } + b.run(); + } + }; + } + + /** + * Given two MayHoldCloseableResource objects, return a Runnable that + * executes both of their close methods in sequence, + * even if the first throws an exception, and if both throw exceptions, add + * any exceptions thrown by the second as suppressed exceptions of the first. + */ + static Runnable composedClose(MayHoldCloseableResource a, MayHoldCloseableResource b) { + return new Runnable() { + @Override + public void run() { + try { + a.close(); + } + catch (Error|RuntimeException e1) { + try { + b.close(); + } + catch (Error|RuntimeException e2) { + e1.addSuppressed(e2); + } + finally { + throw e1; + } + } + b.close(); + } + }; + } }