From 9b1d47ff4ec5cf2f17e801c967e3321c258e90f7 Mon Sep 17 00:00:00 2001 From: parthiv39731 <70740707+parthiv39731@users.noreply.github.com> Date: Thu, 24 Aug 2023 12:04:54 -0700 Subject: [PATCH] BAEL-6865, Aggregate Runtime Exceptions in Java Streams Changed method names --- core-java-modules/core-java-streams-5/pom.xml | 6 + .../baeldung/aggregateEx/CustomMapper.java | 19 ++ .../aggregateEx/ExceptionCollector.java | 41 ++++ .../entity/ExceptionAggregator.java | 27 +++ .../baeldung/aggregateEx/entity/Result.java | 27 +++ .../AggregateExceptionHandlerUnitTest.java | 180 ++++++++++++++++++ 6 files changed, 300 insertions(+) create mode 100644 core-java-modules/core-java-streams-5/src/main/java/com/baeldung/aggregateEx/CustomMapper.java create mode 100644 core-java-modules/core-java-streams-5/src/main/java/com/baeldung/aggregateEx/ExceptionCollector.java create mode 100644 core-java-modules/core-java-streams-5/src/main/java/com/baeldung/aggregateEx/entity/ExceptionAggregator.java create mode 100644 core-java-modules/core-java-streams-5/src/main/java/com/baeldung/aggregateEx/entity/Result.java create mode 100644 core-java-modules/core-java-streams-5/src/test/java/com/baeldung/aggregateEx/AggregateExceptionHandlerUnitTest.java diff --git a/core-java-modules/core-java-streams-5/pom.xml b/core-java-modules/core-java-streams-5/pom.xml index d1f8af6461..abbdb504c1 100644 --- a/core-java-modules/core-java-streams-5/pom.xml +++ b/core-java-modules/core-java-streams-5/pom.xml @@ -38,6 +38,11 @@ 3.12.0 test + + io.vavr + vavr + ${vavr.version} + @@ -67,6 +72,7 @@ 3.1 12 12 + 0.10.2 \ No newline at end of file diff --git a/core-java-modules/core-java-streams-5/src/main/java/com/baeldung/aggregateEx/CustomMapper.java b/core-java-modules/core-java-streams-5/src/main/java/com/baeldung/aggregateEx/CustomMapper.java new file mode 100644 index 0000000000..114a812fdc --- /dev/null +++ b/core-java-modules/core-java-streams-5/src/main/java/com/baeldung/aggregateEx/CustomMapper.java @@ -0,0 +1,19 @@ +package com.baeldung.aggregateEx; + +import com.baeldung.aggregateEx.entity.Result; + +import java.util.function.Function; + +public class CustomMapper { + public static Function> mapper(Function func) { + return arg -> { + Result result; + try { + result = new Result(func.apply(arg)); + } catch (Exception e) { + result = new Result(e); + } + return result; + }; + } +} diff --git a/core-java-modules/core-java-streams-5/src/main/java/com/baeldung/aggregateEx/ExceptionCollector.java b/core-java-modules/core-java-streams-5/src/main/java/com/baeldung/aggregateEx/ExceptionCollector.java new file mode 100644 index 0000000000..5472724b7c --- /dev/null +++ b/core-java-modules/core-java-streams-5/src/main/java/com/baeldung/aggregateEx/ExceptionCollector.java @@ -0,0 +1,41 @@ +package com.baeldung.aggregateEx; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; +import java.util.stream.Collector; + +public class ExceptionCollector { + private final List results = new ArrayList<>(); + private final List exceptions = new ArrayList<>(); + + private ExceptionCollector() { + } + + public static Collector> of(Function mapper) { + return Collector.of( + ExceptionCollector::new, + (collector, item) -> { + try { + R result = mapper.apply(item); + collector.results.add(result); + } catch (Exception e) { + collector.exceptions.add(e); + } + }, + (left, right) -> { + left.results.addAll(right.results); + left.exceptions.addAll(right.exceptions); + return left; + } + ); + } + + public List getResults() { + return results; + } + + public List getExceptions() { + return exceptions; + } +} diff --git a/core-java-modules/core-java-streams-5/src/main/java/com/baeldung/aggregateEx/entity/ExceptionAggregator.java b/core-java-modules/core-java-streams-5/src/main/java/com/baeldung/aggregateEx/entity/ExceptionAggregator.java new file mode 100644 index 0000000000..b4c38db2cf --- /dev/null +++ b/core-java-modules/core-java-streams-5/src/main/java/com/baeldung/aggregateEx/entity/ExceptionAggregator.java @@ -0,0 +1,27 @@ +package com.baeldung.aggregateEx.entity; + +import java.util.ArrayList; +import java.util.List; + +public class ExceptionAggregator extends RuntimeException { + private List exceptions; + + public ExceptionAggregator(String message) { + super(message); + exceptions = new ArrayList<>(); + } + + public List getExceptions() { + return exceptions; + } + + public Exception addException(Exception e) { + this.addSuppressed(e); + exceptions.add(e); + return e; + } + + public void addExceptions(List exceptions) { + exceptions.forEach(this::addException); + } +} diff --git a/core-java-modules/core-java-streams-5/src/main/java/com/baeldung/aggregateEx/entity/Result.java b/core-java-modules/core-java-streams-5/src/main/java/com/baeldung/aggregateEx/entity/Result.java new file mode 100644 index 0000000000..287cccd060 --- /dev/null +++ b/core-java-modules/core-java-streams-5/src/main/java/com/baeldung/aggregateEx/entity/Result.java @@ -0,0 +1,27 @@ +package com.baeldung.aggregateEx.entity; + +import java.util.Optional; + +public class Result { + private Optional result; + private Optional exception; + + public Result(R result) { + this.result = Optional.of(result); + this.exception = Optional.empty(); + } + + public Result(Exception exception) { + this.exception = Optional.of(exception); + this.result = Optional.empty(); + } + + public Optional getResult() { + return result; + } + + public Optional getException() { + return exception; + } + +} diff --git a/core-java-modules/core-java-streams-5/src/test/java/com/baeldung/aggregateEx/AggregateExceptionHandlerUnitTest.java b/core-java-modules/core-java-streams-5/src/test/java/com/baeldung/aggregateEx/AggregateExceptionHandlerUnitTest.java new file mode 100644 index 0000000000..952ec376ac --- /dev/null +++ b/core-java-modules/core-java-streams-5/src/test/java/com/baeldung/aggregateEx/AggregateExceptionHandlerUnitTest.java @@ -0,0 +1,180 @@ +package com.baeldung.aggregateEx; + +import com.baeldung.aggregateEx.entity.ExceptionAggregator; +import com.baeldung.aggregateEx.entity.Result; + +import static org.junit.Assert.*; + +import io.vavr.control.Either; +import io.vavr.control.Try; +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +public class AggregateExceptionHandlerUnitTest { + + private static RuntimeException process(String str) { + try { + Integer.parseInt(str); + return null; + } catch (NumberFormatException e) { + return new RuntimeException(e); + } + } + + private static Object transform(String str) { + try { + return (Integer.parseInt(str)); + } catch (NumberFormatException e) { + return new RuntimeException(e); + } + } + + @Test + public void givenExtractedMethod_whenFoundNonInt_thenAggregateException() { + String[] strings = {"1", "2", "3", "a", "b", "c"}; + RuntimeException runEx = Arrays.stream(strings) + .map(AggregateExceptionHandlerUnitTest::process) + .filter(Objects::nonNull) + .reduce(new RuntimeException("Errors Occurred"), (o1, o2) -> { + o1.addSuppressed(o2); + return o1; + }); + assertEquals("Errors Occurred", runEx.getMessage()); + assertEquals(3, runEx.getSuppressed().length); + } + + @Test + public void givenTryCatchInPipeline_whenFoundNonInts_thenAggregateException() { + String[] strings = {"1", "2", "3", "a", "b", "c"}; + RuntimeException runEx = Arrays.stream(strings) + .map(str -> { + try { + Integer.parseInt(str); + return null; + } catch (NumberFormatException e) { + return new RuntimeException(e); + } + }) + .filter(Objects::nonNull) + .reduce(new RuntimeException("Errors Occurred"), (o1, o2) -> { + o1.addSuppressed(o2); + return o1; + }); + assertEquals("Errors Occurred", runEx.getMessage()); + assertEquals(3, runEx.getSuppressed().length); + } + + @Test + public void whenFoundNonInts_thenAggregateExceptionAndReturnOutput() { + String[] strings = {"1", "2", "3", "a", "b", "c"}; + Map resultMap = Arrays.stream(strings) + .map(AggregateExceptionHandlerUnitTest::transform) + .collect(Collectors.partitioningBy(o -> o instanceof RuntimeException, Collectors.toList())); + RuntimeException ex = null; + if (resultMap.containsKey(Boolean.TRUE)) { + List exs = (List) resultMap.get(Boolean.TRUE); + ex = exs.stream() + .reduce( + new RuntimeException("Errors Occurred"), (o1, o2) -> { + o1.addSuppressed(o2); + return o1; + }); + } + assertEquals("Errors Occurred", ex.getMessage()); + assertEquals(3, ex.getSuppressed().length); + } + + @Test + public void givenWrapFunction_whenFoundNonInts_thenAggregateException() throws ExceptionAggregator { + String[] strings = {"1", "2", "3", "a", "b", "c"}; + Map>> resultmap = Arrays.stream(strings) + .map(CustomMapper.mapper(Integer::parseInt)) + .collect(Collectors.partitioningBy(r -> r.getException().isEmpty(), Collectors.toList())); + + if (resultmap.containsKey(Boolean.FALSE)) { + List> resultList = resultmap.get(Boolean.FALSE); + List exceptionList = resultList.stream() + .map(opex -> opex.getException().get()) + .collect(Collectors.toList()); + + assertThrows(ExceptionAggregator.class, () -> handleExceptions(exceptionList)); + } + } + + private void handleExceptions(List exceptions) throws ExceptionAggregator { + ExceptionAggregator exceptionAggregator = new ExceptionAggregator("Errors occurred"); + exceptionAggregator.addExceptions(exceptions); + throw exceptionAggregator; + } + + @Test + public void givenExCollector_whenFoundNonInts_thenAggregateException() throws ExceptionAggregator { + String[] strings = {"1", "2", "3", "a", "b", "c"}; + ExceptionCollector exCollector = Arrays.stream(strings) + .collect(ExceptionCollector.of(Integer::parseInt)); + List output = exCollector.getResults(); + List runEx = exCollector.getExceptions(); + assertEquals(3, runEx.size()); + } + + private static Either processAndReturnEither(String str) { + Either either = null; + try { + either = Either.right(Integer.parseInt(str)); + } catch (NumberFormatException e) { + either = Either.left(new RuntimeException(e)); + } + return either; + } + + @Test + public void givenVavrEither_whenFoundNonInts_thenAggregateException() { + List strings = List.of("1", "2", "3", "a", "b", "c"); + Map>> map = strings.stream() + .map(str -> processAndReturnEither(str)) + .collect(Collectors.partitioningBy((t) -> t.isLeft(), Collectors.toList())); + + RuntimeException runEx = map.get(Boolean.TRUE) + .stream().map(either -> either.getLeft()) + .reduce(new RuntimeException("Errors Occurred"), (o1, o2) -> { + o1.addSuppressed(o2); + return o1; + }); + assertEquals(3, runEx.getSuppressed().length); + } + + @Test + public void givenVavrTry_whenFoundNonInts_thenAggregateException() { + List strings = List.of("1", "2", "3", "a", "b", "c"); + Map>> map = strings.stream() + .map(str -> Try.of(() -> Integer.parseInt(str))) + .collect(Collectors.partitioningBy((t) -> t.isFailure(), Collectors.toList())); + Throwable runEx = map.get(Boolean.TRUE).stream() + .map(t -> t.getCause()) + .reduce(new RuntimeException("Errors Occurred"), (o1, o2) -> { + o1.addSuppressed(o2); + return o1; + }); + assertEquals(3, runEx.getSuppressed().length); + } + + @Test + public void givenVavrEitherAndTry_whenFoundNonInts_thenAggregateException() { + List strings = List.of("1", "2", "3", "a", "b", "c"); + Map>> map = strings.stream() + .map(str -> Try.of(() -> Integer.parseInt(str)).toEither()) + .collect(Collectors.partitioningBy(Either::isLeft, Collectors.toList())); + Throwable runEx = map.get(Boolean.TRUE).stream() + .map(either -> either.getLeft()) + .reduce(new RuntimeException("Errors Occurred"), (o1, o2) -> { + o1.addSuppressed(o2); + return o1; + }); + assertEquals(3, runEx.getSuppressed().length); + } +}