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);
+ }
+}