Skip to content

Commit 89b7fa6

Browse files
authored
Merge pull request #14653 from parthiv39731/PR-6865
BAEL 6865
2 parents 86f7514 + 9e0223b commit 89b7fa6

5 files changed

Lines changed: 237 additions & 0 deletions

File tree

core-java-modules/core-java-streams-5/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@
3838
<version>3.12.0</version>
3939
<scope>test</scope>
4040
</dependency>
41+
<dependency>
42+
<groupId>io.vavr</groupId>
43+
<artifactId>vavr</artifactId>
44+
<version>${vavr.version}</version>
45+
</dependency>
4146
</dependencies>
4247

4348
<build>
@@ -66,6 +71,7 @@
6671
<!-- testing -->
6772
<maven.compiler.source>12</maven.compiler.source>
6873
<maven.compiler.target>12</maven.compiler.target>
74+
<vavr.version>0.10.2</vavr.version>
6975
</properties>
7076

7177
</project>
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.baeldung.aggregateexception;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
import java.util.function.Function;
6+
import java.util.stream.Collector;
7+
8+
public class CustomCollector<T, R> {
9+
private final List<R> results = new ArrayList<>();
10+
private final List<Throwable> exceptions = new ArrayList<>();
11+
12+
public static <T, R> Collector<T, ?, CustomCollector<T, R>> of(Function<T, R> mapper) {
13+
return Collector.of(
14+
CustomCollector::new,
15+
(collector, item) -> {
16+
try {
17+
R result = mapper.apply(item);
18+
collector.results.add(result);
19+
} catch (Exception e) {
20+
collector.exceptions.add(e);
21+
}
22+
},
23+
(left, right) -> {
24+
left.results.addAll(right.results);
25+
left.exceptions.addAll(right.exceptions);
26+
return left;
27+
}
28+
);
29+
}
30+
31+
public List<R> getResults() {
32+
return results;
33+
}
34+
35+
public List<Throwable> getExceptions() {
36+
return exceptions;
37+
}
38+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.baeldung.aggregateexception;
2+
3+
import com.baeldung.aggregateexception.entity.Result;
4+
5+
import java.util.function.Function;
6+
7+
public class CustomMapper {
8+
public static <T, R> Function<T, Result<R, Throwable>> mapper(Function<T, R> func) {
9+
return arg -> {
10+
try {
11+
return new Result(func.apply(arg));
12+
} catch (Exception e) {
13+
return new Result(e);
14+
}
15+
};
16+
}
17+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.baeldung.aggregateexception.entity;
2+
3+
import java.util.Optional;
4+
5+
public class Result<R, E extends Throwable> {
6+
private Optional<R> result;
7+
private Optional<E> exception;
8+
9+
public Result(R result) {
10+
this.result = Optional.of(result);
11+
this.exception = Optional.empty();
12+
}
13+
14+
public Result(E exception) {
15+
this.exception = Optional.of(exception);
16+
this.result = Optional.empty();
17+
}
18+
19+
public Optional<R> getResult() {
20+
return result;
21+
}
22+
23+
public Optional<E> getException() {
24+
return exception;
25+
}
26+
}
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
package com.baeldung.aggregateexception;
2+
3+
import com.baeldung.aggregateexception.entity.Result;
4+
import io.vavr.control.Either;
5+
import io.vavr.control.Try;
6+
import org.junit.Test;
7+
import org.slf4j.Logger;
8+
import org.slf4j.LoggerFactory;
9+
10+
import java.util.Arrays;
11+
import java.util.List;
12+
import java.util.Map;
13+
import java.util.Objects;
14+
import java.util.stream.Collectors;
15+
16+
import static org.junit.Assert.assertEquals;
17+
18+
19+
public class AggregateExceptionHandlerUnitTest {
20+
private static final Logger logger = LoggerFactory.getLogger(AggregateExceptionHandlerUnitTest.class);
21+
22+
@Test
23+
public void givenExtractedMethod_whenFoundEx_thenSuppressExIntoRuntimeEx() {
24+
String[] strings = {"1", "2", "3", "a", "b", "c"};
25+
RuntimeException runEx = Arrays.stream(strings)
26+
.map(str -> callProcessThrowsExAndNoOutput(str))
27+
.filter(Objects::nonNull)
28+
.reduce(new RuntimeException("Errors Occurred"), (o1, o2) -> {
29+
o1.addSuppressed(o2);
30+
return o1;
31+
});
32+
processExceptions(runEx);
33+
assertEquals("Errors Occurred", runEx.getMessage());
34+
assertEquals(3, runEx.getSuppressed().length);
35+
}
36+
@Test
37+
public void givenTryCatchInPipeline_whenFoundEx_thenSuppressExIntoRuntimeEx() {
38+
String[] strings = {"1", "2", "3", "a", "b", "c"};
39+
RuntimeException runEx = Arrays.stream(strings).map(str -> {
40+
try {
41+
processThrowsExAndNoOutput(str);
42+
return null;
43+
} catch (RuntimeException e) {
44+
return e;
45+
}
46+
}).filter(Objects::nonNull)
47+
.collect(Collectors.collectingAndThen(Collectors.toList(), list -> {
48+
RuntimeException runtimeException = new RuntimeException("Errors Occurred");
49+
list.forEach(runtimeException::addSuppressed);
50+
return runtimeException;
51+
}));
52+
processExceptions(runEx);
53+
assertEquals("Errors Occurred", runEx.getMessage());
54+
assertEquals(3, runEx.getSuppressed().length);
55+
}
56+
57+
@Test
58+
public void givenProcessMethod_whenStreamResultHasExAndOutput_thenHandleExceptionListAndOutputList() {
59+
List<String> strings = List.of("1", "2", "3", "a", "b", "c");
60+
Map map = strings.stream()
61+
.map(s -> processReturnsExAndOutput(s))
62+
.collect(Collectors.partitioningBy(o -> o instanceof RuntimeException, Collectors.toList()));
63+
64+
List<RuntimeException> exceptions = (List<RuntimeException>)map.getOrDefault(Boolean.TRUE, List.of());
65+
List<Integer> results = (List<Integer>)map.getOrDefault(Boolean.FALSE, List.of());
66+
handleExceptionsAndOutputs(exceptions, results);
67+
}
68+
69+
@Test
70+
public void givenCustomMapper_whenStreamResultHasExAndSuccess_thenHandleExceptionListAndOutputList() {
71+
List<String> strings = List.of("1", "2", "3", "a", "b", "c");
72+
strings.stream()
73+
.map(CustomMapper.mapper(Integer::parseInt))
74+
.collect(Collectors.collectingAndThen(Collectors.toList(), list -> handleErrorsAndOutputForResult(list)));
75+
}
76+
77+
@Test
78+
public void givenCustomCollector_whenStreamResultHasExAndSuccess_thenHandleAggrExceptionAndResults() {
79+
String[] strings = {"1", "2", "3", "a", "b", "c"};
80+
Arrays.stream(strings)
81+
.collect(Collectors.collectingAndThen(CustomCollector.of(Integer::parseInt),
82+
col -> handleExAndResults(col.getExceptions(), col.getResults())));
83+
}
84+
85+
@Test
86+
public void givenVavrEitherAndTry_whenStreamResultHasExAndSuccess_thenHandleExceptionListAndOutputList() {
87+
List<String> strings = List.of("1", "2", "3", "a", "b", "c");
88+
strings.stream()
89+
.map(str -> Try.of(() -> Integer.parseInt(str)).toEither())
90+
.collect(Collectors.collectingAndThen(Collectors.partitioningBy(Either::isLeft, Collectors.toList()),
91+
map -> handleErrorsAndOutputForEither(map)));
92+
}
93+
94+
private static void processThrowsExAndNoOutput(String input) {
95+
//throw exception when input is "a", "b", "c"
96+
if (input.matches("[a-c]")) {
97+
throw new RuntimeException("Downstream method throws exception for " + input);
98+
}
99+
}
100+
private static RuntimeException callProcessThrowsExAndNoOutput(String input) {
101+
try {
102+
processThrowsExAndNoOutput(input);
103+
return null;
104+
} catch (RuntimeException e) {
105+
return e;
106+
}
107+
}
108+
109+
private static Object processReturnsExAndOutput(String input) {
110+
logger.info("call a downstream method that returns an Integer");
111+
try {
112+
return Integer.parseInt(input);
113+
} catch (Exception e) {
114+
return new RuntimeException("Exception in processWithReturnOutput for " + input, e);
115+
}
116+
}
117+
118+
private static void processExceptions(Throwable throwable) {
119+
logger.error("Process Exception" + throwable.getMessage());
120+
}
121+
122+
private static void handleExceptionsAndOutputs(List<RuntimeException> exs, List<Integer> output) {
123+
logger.info("number of exceptions " + exs.size() + " number of outputs " + output.size());
124+
}
125+
126+
private static String handleExAndResults(List<Throwable> ex, List<Integer> results ) {
127+
logger.info("handle aggregated exceptions and results" + ex.size() + " " + results.size());
128+
return "Exceptions and Results Handled";
129+
}
130+
131+
private static String handleErrorsAndOutputForEither(Map<Boolean, List<Either<Throwable, Integer>>> map) {
132+
logger.info("handle errors and output");
133+
map.getOrDefault(Boolean.TRUE, List.of()).forEach(either -> logger.error("Process Exception " + either.getLeft()));
134+
135+
map.getOrDefault(Boolean.FALSE, List.of()).forEach(either -> logger.info("Process Result " + either.get()));
136+
return "Errors and Output Handled";
137+
}
138+
139+
private static String handleErrorsAndOutputForResult(List<Result<Integer, Throwable>> successAndErrors) {
140+
logger.info("handle errors and output");
141+
successAndErrors.forEach(result -> {
142+
if (result.getException().isPresent()) {
143+
logger.error("Process Exception " + result.getException().get());
144+
} else {
145+
logger.info("Process Result" + result.getResult().get());
146+
}
147+
});
148+
return "Errors and Output Handled";
149+
}
150+
}

0 commit comments

Comments
 (0)