This is an automated email from the ASF dual-hosted git repository. jochen pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-lang.git
The following commit(s) were added to refs/heads/master by this push: new 2ebc17b Added Functions.tryWithResources. 2ebc17b is described below commit 2ebc17ba3f244ae44aae46273aeef7e321f9542a Author: Jochen Wiedmann (jwi) <jochen.wiedm...@softwareag.com> AuthorDate: Tue Jan 29 11:42:07 2019 +0100 Added Functions.tryWithResources. --- .../java/org/apache/commons/lang3/Functions.java | 121 +++++++++++++++++++-- .../org/apache/commons/lang3/FunctionsTest.java | 60 ++++++++++ 2 files changed, 174 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/apache/commons/lang3/Functions.java b/src/main/java/org/apache/commons/lang3/Functions.java index 9b6168f..5eaed0c 100644 --- a/src/main/java/org/apache/commons/lang3/Functions.java +++ b/src/main/java/org/apache/commons/lang3/Functions.java @@ -18,11 +18,8 @@ package org.apache.commons.lang3; import java.io.IOException; import java.io.UncheckedIOException; -import java.lang.reflect.Method; import java.lang.reflect.UndeclaredThrowableException; -import java.util.ArrayList; -import org.apache.commons.lang3.exception.ExceptionUtils; /** This class provides utility functions, and classes for working with the * {@code java.util.function} package, or more generally, with Java 8 @@ -73,6 +70,14 @@ public class Functions { public interface FailableBiFunction<I1,I2,O,T extends Throwable> { public O apply(I1 pInput1, I2 pInput2) throws T; } + @FunctionalInterface + public interface FailablePredicate<O,T extends Throwable> { + public boolean test(O pObject) throws T; + } + @FunctionalInterface + public interface FailableBiPredicate<O1,O2,T extends Throwable> { + public boolean test(O1 pObject1, O2 pObject2) throws T; + } public static <T extends Throwable> void run(FailableRunnable<T> pRunnable) { try { @@ -122,6 +127,112 @@ public class Functions { } } + public static <O,T extends Throwable> boolean test(FailablePredicate<O,T> pPredicate, O pObject) { + try { + return pPredicate.test(pObject); + } catch (Throwable t) { + throw rethrow(t); + } + } + + public static <O1,O2,T extends Throwable> boolean test(FailableBiPredicate<O1,O2,T> pPredicate, O1 pObject1, O2 pObject2) { + try { + return pPredicate.test(pObject1, pObject2); + } catch (Throwable t) { + throw rethrow(t); + } + } + + /** + * A simple try-with-resources implementation, that can be used, if your + * objects do not implement the {@link AutoCloseable} interface. The method + * executes the {@code pAction}. The method guarantees, that <em>all</em> + * the {@code pResources} are being executed, in the given order, afterwards, + * and regardless of success, or failure. If either the original action, or + * any of the resource action fails, then the <em>first</em> failure (aka + * {@link Throwable} is rethrown. Example use: + * <pre> + * final FileInputStream fis = new FileInputStream("my.file"); + * Functions.tryWithResources(useInputStream(fis), null, () -> fis.close()); + * </pre> + * @param pAction The action to execute. This object <em>will</em> always + * be invoked. + * @param pErrorHandler An optional error handler, which will be invoked finally, + * if any error occurred. The error handler will receive the first + * error, aka {@link Throwable}. + * @param pResources The resource actions to execute. <em>All</em> resource + * actions will be invoked, in the given order. A resource action is an + * instance of {@link FailableRunnable}, which will be executed. + * @see #tryWithResources(FailableRunnable, FailableRunnable...) + */ + @SafeVarargs + public static <O> void tryWithResources(FailableRunnable<? extends Throwable> pAction, + FailableConsumer<Throwable,? extends Throwable> pErrorHandler, + FailableRunnable<? extends Throwable>... pResources) { + final FailableConsumer<Throwable,? extends Throwable> errorHandler; + if (pErrorHandler == null) { + errorHandler = (t) -> rethrow(t); + } else { + errorHandler = pErrorHandler; + } + if (pResources != null) { + for (FailableRunnable<? extends Throwable> runnable : pResources) { + if (runnable == null) { + throw new NullPointerException("A resource action must not be null."); + } + } + } + Throwable th = null; + try { + pAction.run(); + } catch (Throwable t) { + th = t; + } + if (pResources != null) { + for (FailableRunnable<? extends Object> runnable : pResources) { + try { + runnable.run(); + } catch (Throwable t) { + if (th == null) { + th = t; + } + } + } + } + if (th != null) { + try { + errorHandler.accept(th); + } catch (Throwable t) { + throw rethrow(t); + } + } + } + + /** + * A simple try-with-resources implementation, that can be used, if your + * objects do not implement the {@link AutoCloseable} interface. The method + * executes the {@code pAction}. The method guarantees, that <em>all</em> + * the {@code pResources} are being executed, in the given order, afterwards, + * and regardless of success, or failure. If either the original action, or + * any of the resource action fails, then the <em>first</em> failure (aka + * {@link Throwable} is rethrown. Example use: + * <pre> + * final FileInputStream fis = new FileInputStream("my.file"); + * Functions.tryWithResources(useInputStream(fis), () -> fis.close()); + * </pre> + * @param pAction The action to execute. This object <em>will</em> always + * be invoked. + * @param pResources The resource actions to execute. <em>All</em> resource + * actions will be invoked, in the given order. A resource action is an + * instance of {@link FailableRunnable}, which will be executed. + * @see #tryWithResources(FailableRunnable, FailableConsumer, FailableRunnable...) + */ + @SafeVarargs + public static <O> void tryWithResources(FailableRunnable<? extends Throwable> pAction, + FailableRunnable<? extends Throwable>... pResources) { + tryWithResources(pAction, null, pResources); + } + public static RuntimeException rethrow(Throwable pThrowable) { if (pThrowable == null) { throw new NullPointerException("The Throwable must not be null."); @@ -137,8 +248,4 @@ public class Functions { } } } - - public static void test() throws Exception { - run(() -> ArrayList.class.newInstance()); - } } diff --git a/src/test/java/org/apache/commons/lang3/FunctionsTest.java b/src/test/java/org/apache/commons/lang3/FunctionsTest.java index 4f62c53..baef2dd 100644 --- a/src/test/java/org/apache/commons/lang3/FunctionsTest.java +++ b/src/test/java/org/apache/commons/lang3/FunctionsTest.java @@ -6,6 +6,8 @@ import java.io.IOException; import java.io.UncheckedIOException; import java.lang.reflect.UndeclaredThrowableException; +import org.apache.commons.lang3.Functions.FailableBiConsumer; +import org.apache.commons.lang3.Functions.FailableConsumer; import org.junit.jupiter.api.Test; class FunctionsTest { @@ -71,6 +73,28 @@ class FunctionsTest { } } + public static class CloseableObject { + private boolean closed; + + public void run(Throwable pTh) throws Throwable { + if (pTh != null) { + throw pTh; + } + } + + public void reset() { + closed = false; + } + + public void close() { + closed = true; + } + + public boolean isClosed() { + return closed; + } + } + @Test void testRunnable() { FailureOnOddInvocations.invocation = 0; @@ -229,4 +253,40 @@ class FunctionsTest { assertNotNull(i); assertEquals(0, i.intValue()); } + + @Test + public void testTryWithResources() { + final CloseableObject co = new CloseableObject(); + final FailableConsumer<Throwable,? extends Throwable> consumer = (th) -> co.run(th); + final IllegalStateException ise = new IllegalStateException(); + try { + Functions.tryWithResources(() -> consumer.accept(ise), () -> co.close()); + fail("Expected Exception"); + } catch (IllegalStateException e) { + assertSame(ise, e); + } + assertTrue(co.isClosed()); + co.reset(); + final Error error = new OutOfMemoryError(); + try { + Functions.tryWithResources(() -> consumer.accept(error), () -> co.close()); + fail("Expected Exception"); + } catch (OutOfMemoryError e) { + assertSame(error, e); + } + assertTrue(co.isClosed()); + co.reset(); + final IOException ioe = new IOException("Unknown I/O error"); + try { + Functions.tryWithResources(() -> consumer.accept(ioe), () -> co.close()); + fail("Expected Exception"); + } catch (UncheckedIOException e) { + final IOException cause = e.getCause(); + assertSame(ioe, cause); + } + assertTrue(co.isClosed()); + co.reset(); + Functions.tryWithResources(() -> consumer.accept(null), () -> co.close()); + assertTrue(co.isClosed()); + } }