BAEL-6865, implemented review comments

This commit is contained in:
parthiv39731 2023-08-28 10:40:06 -07:00
parent a60be2d0b7
commit 2b88d3e646
5 changed files with 117 additions and 143 deletions

View File

@ -1,31 +1,34 @@
package com.baeldung.aggregateEx; package com.baeldung.aggregateEx;
import com.baeldung.aggregateEx.entity.ExceptionAggregator;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collector; import java.util.stream.Collector;
public class ExceptionCollector<T, R> { public class CustomCollector<T, R> {
private final List<R> results = new ArrayList<>(); private final List<R> results = new ArrayList<>();
private final List<Exception> exceptions = new ArrayList<>(); private final List<Throwable> exceptions = new ArrayList<>();
private ExceptionCollector() { private final ExceptionAggregator exceptionAggregator = new ExceptionAggregator("Exceptions occurred");
}
public static <T, R> Collector<T, ?, ExceptionCollector<T, R>> of(Function<T, R> mapper) { public static <T, R> Collector<T, ?, CustomCollector<T, R>> of(Function<T, R> mapper) {
return Collector.of( return Collector.of(
ExceptionCollector::new, CustomCollector::new,
(collector, item) -> { (collector, item) -> {
try { try {
R result = mapper.apply(item); R result = mapper.apply(item);
collector.results.add(result); collector.results.add(result);
} catch (Exception e) { } catch (Exception e) {
collector.exceptions.add(e); collector.exceptions.add(e);
collector.exceptionAggregator.addException(e);
} }
}, },
(left, right) -> { (left, right) -> {
left.results.addAll(right.results); left.results.addAll(right.results);
left.exceptions.addAll(right.exceptions); left.exceptions.addAll(right.exceptions);
left.exceptionAggregator.addExceptions(right.exceptions);
return left; return left;
} }
); );
@ -35,7 +38,11 @@ public class ExceptionCollector<T, R> {
return results; return results;
} }
public List<Exception> getExceptions() { public List<Throwable> getExceptions() {
return exceptions; return exceptions;
} }
public ExceptionAggregator getExceptionAggregator() {
return exceptionAggregator;
}
} }

View File

@ -5,7 +5,7 @@ import com.baeldung.aggregateEx.entity.Result;
import java.util.function.Function; import java.util.function.Function;
public class CustomMapper { public class CustomMapper {
public static <T, R> Function<T, Result<R>> mapper(Function<T, R> func) { public static <T, R> Function<T, Result<R, Throwable>> mapper(Function<T, R> func) {
return arg -> { return arg -> {
try { try {
return new Result(func.apply(arg)); return new Result(func.apply(arg));

View File

@ -4,24 +4,24 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
public class ExceptionAggregator extends RuntimeException { public class ExceptionAggregator extends RuntimeException {
private List<Exception> exceptions; private List<Throwable> exceptions;
public ExceptionAggregator(String message) { public ExceptionAggregator(String message) {
super(message); super(message);
exceptions = new ArrayList<>(); exceptions = new ArrayList<>();
} }
public List<Exception> getExceptions() { public List<Throwable> getExceptions() {
return exceptions; return exceptions;
} }
public Exception addException(Exception e) { public Throwable addException(Throwable e) {
this.addSuppressed(e); this.addSuppressed(e);
exceptions.add(e); exceptions.add(e);
return e; return e;
} }
public void addExceptions(List<Exception> exceptions) { public void addExceptions(List<Throwable> exceptions) {
exceptions.forEach(this::addException); exceptions.forEach(this::addException);
} }
} }

View File

@ -2,16 +2,16 @@ package com.baeldung.aggregateEx.entity;
import java.util.Optional; import java.util.Optional;
public class Result<R> { public class Result<R, E extends Throwable> {
private Optional<R> result; private Optional<R> result;
private Optional<Exception> exception; private Optional<E> exception;
public Result(R result) { public Result(R result) {
this.result = Optional.of(result); this.result = Optional.of(result);
this.exception = Optional.empty(); this.exception = Optional.empty();
} }
public Result(Exception exception) { public Result(E exception) {
this.exception = Optional.of(exception); this.exception = Optional.of(exception);
this.result = Optional.empty(); this.result = Optional.empty();
} }
@ -20,8 +20,7 @@ public class Result<R> {
return result; return result;
} }
public Optional<Exception> getException() { public Optional<E> getException() {
return exception; return exception;
} }
} }

View File

@ -5,6 +5,8 @@ import com.baeldung.aggregateEx.entity.Result;
import io.vavr.control.Either; import io.vavr.control.Either;
import io.vavr.control.Try; import io.vavr.control.Try;
import org.junit.Test; import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
@ -12,172 +14,138 @@ import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.junit.Assert.*; import static org.junit.Assert.assertEquals;
public class AggregateExceptionHandlerUnitTest { public class AggregateExceptionHandlerUnitTest {
static Logger logger = LoggerFactory.getLogger(AggregateExceptionHandlerUnitTest.class);
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 @Test
public void givenExtractedMethod_whenFoundNonInt_thenSuppressExIntoRuntimeEx() { public void givenExtractedMethod_whenFoundEx_thenSuppressExIntoRuntimeEx() {
String[] strings = {"1", "2", "3", "a", "b", "c"}; String[] strings = {"1", "2", "3", "a", "b", "c"};
RuntimeException runEx = Arrays.stream(strings) Throwable runEx = Arrays.stream(strings)
.map(AggregateExceptionHandlerUnitTest::process) .map(str -> callProcessThrowsExAndNoOutput(str))
.filter(Objects::nonNull) .filter(Objects::nonNull)
.reduce(new RuntimeException("Errors Occurred"), (o1, o2) -> { .reduce(new RuntimeException("Errors Occurred"), (o1, o2) -> {
o1.addSuppressed(o2); o1.addSuppressed(o2);
return o1; return o1;
}); });
processExceptions(runEx);
assertEquals("Errors Occurred", runEx.getMessage()); assertEquals("Errors Occurred", runEx.getMessage());
assertEquals(3, runEx.getSuppressed().length); assertEquals(3, runEx.getSuppressed().length);
} }
@Test @Test
public void givenTryCatchInPipeline_whenFoundNonInts_thenSuppressExIntoRuntimeEx() { public void givenTryCatchInPipeline_whenFoundEx_thenSuppressExIntoRuntimeEx() {
String[] strings = {"1", "2", "3", "a", "b", "c"}; String[] strings = {"1", "2", "3", "a", "b", "c"};
RuntimeException runEx = Arrays.stream(strings) RuntimeException runEx = Arrays.stream(strings).map(str -> {
.map(str -> {
try { try {
Integer.parseInt(str); processThrowsExAndNoOutput(str);
return null; return null;
} catch (NumberFormatException e) { } catch (RuntimeException e) {
return new RuntimeException(e); return e;
} }
}) }).filter(Objects::nonNull)
.filter(Objects::nonNull) .collect(Collectors.collectingAndThen(Collectors.toList(), list -> {
.reduce(new RuntimeException("Errors Occurred"), (o1, o2) -> { RuntimeException runtimeException = new RuntimeException("Errors Occurred");
o1.addSuppressed(o2); list.forEach(runtimeException::addSuppressed);
return o1; return runtimeException;
}); }));
processExceptions(runEx);
assertEquals("Errors Occurred", runEx.getMessage()); assertEquals("Errors Occurred", runEx.getMessage());
assertEquals(3, runEx.getSuppressed().length); assertEquals(3, runEx.getSuppressed().length);
} }
@Test @Test
public void givenExtractedMethodReturnOutputAndEx_whenFoundNonInts_thenSuppressExIntoRuntimeEx() { public void givenProcessMethod_whenStreamResultHasExAndOutput_thenHandleExceptionListAndOutputList() {
String[] strings = {"1", "2", "3", "a", "b", "c"}; List<String> strings = List.of("1", "2", "3", "a", "b", "c");
Map resultMap = Arrays.stream(strings) Map map = strings.stream()
.map(AggregateExceptionHandlerUnitTest::transform) .map(s -> processReturnsExAndOutput(s))
.collect(Collectors.partitioningBy(o -> o instanceof RuntimeException, Collectors.toList())); .collect(Collectors.partitioningBy(o -> o instanceof RuntimeException, Collectors.toList()));
assert(map.containsKey(Boolean.TRUE) && map.containsKey(Boolean.FALSE));
RuntimeException ex = null; handleExceptionsAndOutputs((List<RuntimeException>) map.get(Boolean.TRUE), (List<Integer>)map.get(Boolean.FALSE));
assertTrue(resultMap.containsKey(Boolean.TRUE));
List<RuntimeException> exs = (List<RuntimeException>) 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 @Test
public void givenWrapFunction_whenFoundNonInts_thenUseExAggregator() throws ExceptionAggregator { public void givenCustomMapper_whenStreamResultHasExAndSuccess_thenHandleExceptionListAndOutputList() {
String[] strings = {"1", "2", "3", "a", "b", "c"}; List<String> strings = List.of("1", "2", "3", "a", "b", "c");
Map<Boolean, List<Result<Integer>>> resultmap = Arrays.stream(strings) strings.stream()
.map(CustomMapper.mapper(Integer::parseInt)) .map(CustomMapper.mapper(Integer::parseInt))
.collect(Collectors.partitioningBy(r -> r.getException().isEmpty(), Collectors.toList())); .collect(Collectors.collectingAndThen(Collectors.toList(), list -> handleErrorsAndOutPutForResult(list)));
assertTrue(resultmap.containsKey(Boolean.TRUE));
List<Result<Integer>> resultList = resultmap.get(Boolean.FALSE);
List<Exception> exceptionList = resultList.stream()
.map(opex -> opex.getException().get())
.collect(Collectors.toList());
assertThrows(ExceptionAggregator.class, () -> handleExceptions(exceptionList));
}
private void handleExceptions(List<Exception> exceptions) throws ExceptionAggregator {
ExceptionAggregator exceptionAggregator = new ExceptionAggregator("Errors occurred");
exceptionAggregator.addExceptions(exceptions);
throw exceptionAggregator;
} }
@Test @Test
public void givenExCollector_whenFoundNonInts_thenAggregateExInCustomCollector() throws ExceptionAggregator { public void givenCustomCollector_whenStreamResultHasExAndSuccess_thenHandleAggrExceptionAndResults() {
String[] strings = {"1", "2", "3", "a", "b", "c"}; String[] strings = {"1", "2", "3", "a", "b", "c"};
ExceptionCollector exCollector = Arrays.stream(strings) Arrays.stream(strings)
.collect(ExceptionCollector.of(Integer::parseInt)); .collect(Collectors.collectingAndThen(CustomCollector.of(Integer::parseInt), col -> {
List<Integer> output = exCollector.getResults(); handleExAndResults(col.getExceptionAggregator(), col.getResults());
List<RuntimeException> runEx = exCollector.getExceptions(); return col;
assertEquals(3, runEx.size()); }));
}
private static Either<RuntimeException, Integer> processAndReturnEither(String str) {
try {
return Either.right(Integer.parseInt(str));
} catch (NumberFormatException e) {
return Either.left(new RuntimeException(e));
}
} }
@Test @Test
public void givenVavrEither_whenFoundNonInts_thenSuppressExIntoRuntimeEx() { public void givenVavrEitherAndTry_whenStreamResultHasExAndSuccess_thenHandleExceptionListAndOutputList() {
List<String> strings = List.of("1", "2", "3", "a", "b", "c"); List<String> strings = List.of("1", "2", "3", "a", "b", "c");
Map<Boolean, List<Either<RuntimeException, Integer>>> map = strings.stream() strings.stream()
.map(str -> processAndReturnEither(str))
.collect(Collectors.partitioningBy(t -> t.isLeft(), Collectors.toList()));
assertTrue(map.containsKey(Boolean.TRUE));
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_thenSuppressExIntoRuntimeEx() {
List<String> strings = List.of("1", "2", "3", "a", "b", "c");
Map<Boolean, List<Try<Integer>>> 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_thenSuppressExIntoRuntimeEx() {
List<String> strings = List.of("1", "2", "3", "a", "b", "c");
Map<Boolean, List<Either<Throwable, Integer>>> map = strings.stream()
.map(str -> Try.of(() -> Integer.parseInt(str)).toEither()) .map(str -> Try.of(() -> Integer.parseInt(str)).toEither())
.collect(Collectors.partitioningBy(Either::isLeft, Collectors.toList())); .collect(Collectors.collectingAndThen(Collectors.partitioningBy(Either::isLeft, Collectors.toList()), map -> {
Throwable runEx = map.get(Boolean.TRUE).stream() handleErrorsAndOutPutForEither(map);
.map(either -> either.getLeft()) return map;
.reduce(new RuntimeException("Errors Occurred"), (o1, o2) -> { }));
o1.addSuppressed(o2); }
return o1;
private static void processThrowsExAndNoOutput(String input) {
//return exception when input is "a", "b", "c"
if (input.matches("[a-c]")) {
throw new RuntimeException("Downstream method throws exception for " + input);
}
}
private static Throwable callProcessThrowsExAndNoOutput(String input) {
try {
processThrowsExAndNoOutput(input);
return null;
} catch (RuntimeException e) {
return e;
}
}
private static Object processReturnsExAndOutput(String input) {
logger.info("call a downstream method that returns an Integer");
try {
return Integer.parseInt(input);
} catch (Exception e) {
return new RuntimeException("Exception in processWithReturnOutput for " + input, e);
}
}
private static void processExceptions(Throwable throwable) {
logger.error("Process Exception" + throwable.getMessage());
}
private static void handleExceptionsAndOutputs(List<RuntimeException> exs, List output) {
logger.info("handle exceptions and output");
}
private static void handleExAndResults(ExceptionAggregator exAgg, List<Integer> results ) {
logger.info("handle aggregated exceptions and results" + exAgg.getExceptions().size() + " " + results.size());
}
private static void handleErrorsAndOutPutForEither(Map<Boolean, List<Either<Throwable, Integer>>> map) {
logger.info("handle errors and output");
map.get(Boolean.TRUE).forEach(either -> logger.error("Process Exception " + either.getLeft()));
map.get(Boolean.FALSE).forEach(either -> logger.info("Process Result " + either.get()));
}
private static String handleErrorsAndOutPutForResult(List<Result<Integer, Throwable>> successAndErrors) {
logger.info("handle errors and output");
successAndErrors.forEach(result -> {
if (result.getException().isPresent()) {
logger.error("Process Exception " + result.getException().get());
} else {
logger.info("Process Result" + result.getResult());
}
}); });
assertEquals(3, runEx.getSuppressed().length); return "Errors and Output Handled";
} }
} }